Refactor optimization pass handling.

Refactor the optimization passes to explicitly use the passes. This commit just re-implements the same passes as were already being run.

It also adds an option (behind `-Z`) to run the LLVM lint pass on the unoptimized IR.

This should close #2812. It is also the first step towards #2396

----------------

This is pretty much just an initial "get it out there" PR. With finer control over the optimization passes coming later. I also just blindly copied the passes we were already doing, so there could almost certainly be some more work in paring it down.

The other thing is the addition of the `mergefunctions` pass, which is currently enabled at `--opt-level=3` and does have a small impact on the code size. However the fact that it is at the end of the optimization pipeline is probably not ideal, so some more experimentation is in order.