[interp] Transform ldloc/stloc pairs to a movloc instruction (mono/mono#16546)
* [interp] Add stack information for each instruction
For each instruction PopN indicates how many values from the top of the stack are used by the instruction. PushN signals how many values are pushed back. These are also used to prevent values from the stack being optimized out, since they might still be used by an instruction. VarPop and VarPush mean that the number is not constant, so we need to inspect the data fields of the instruction (typically for an imethod or csignature) to figure out the stack usage.
* [interp] Convert ldloc/stloc pairs to movloc
The movloc instruction operates directly on locals. This is faster due to avoiding dispatch and stack indirection. More important is that it makes it easy to do copy propagation of locals and it has no stack usage, which means that it could even be killed if the destination local is no longer referenced (as well as STLOC_NP). In the future we could reuse this mechanism of tracking the value of a stack slot to do constant propagation and kill even more instructions (we might need to have _IMM versions of opcodes for this to work).
* [interp] Enable disabling of interp optimization
* [interp] Add time tracking stat for cprop optimizations
* [interp] Move stloc.np transformations to the cprop pass
It makes more sense to do such transformations here and we also don't want transformations in the generate phase prevent us from detecting alternative transformations when running the full pass.
* [interp] Add counter for instructions that were optimized away
* [interp] Make it explicit that we are dealing with IR code
It can be confusing in verbose output.
Commit migrated from https://github.com/mono/mono/commit/
930a5d7d440a735b8b50e57ed397a700e3ee27b5