Skip to content

Commit

Permalink
[interp] Rework the allocation of offsets for variables (#49072)
Browse files Browse the repository at this point in the history
* [interp] Cleanup get_interp_local_offset

Use it just for allocating an offset for a var, at the top of the locals space.

* [interp] Save the args inside call instructions

This will aid later optimizations, in order to easily detect the call args for an opcode. This is stored as a -1 terminated array of var indexes. Also change the structure of newobj_reg_map array, so it can reuse this format (newobj_reg_map should be killed at some point anyway).

* [interp] Pass target_ip in a normal var to MINT_CALLI_NAT_FAST

Making it consistent with other calli opcodes and simplifies a little bit the code generation path.

* [interp] Add explicit return for CallArgs opcodes

Before this change, calls used to receive a single special dreg argument. This was resolved to an offset. At this offset, the call could find all the parameters and the return value was also written at the same offset. With this change we move towards having an explicit dreg return. For calls, the last sreg must be of the special type MINT_CALL_ARGS_SREG. The var offset allocator should ensure all call args are allocated one after the other and that this special reg type is resolved to the offset where these args reside.

* [interp] Remove call args flag from code generation / optimization phases

This flag should only be relevant to the var offset allocator

* [interp] Add new local offset allocator

This change aims to simplify the handling of vars during optimizations. Before this change we had different types of vars : managed locals, var residing on the execution stack, vars that are the argument of a call. Multiple restrictions applied to vars residing on the execution stack and to call args. Following this change, all vars share the same semantics during optimizations passes. At the very end, we allocate offsets for them and we will end up with 3 types of vars : global vars (used from multiple bblocks), local vars (used in a single bblock) and call arg vars. Call arg vars are always local.

The first step of the allocator is to detect all global vars and allocate offsets for them by doing a full iteration over the code. They will reside in the first section of the stack frame and they are allocated one after the other in the order they are detected. The param area (containing call arg vars) will have to be allocated after the local var space, otherwise a call would overwrite vars in the calling method. These vars are allocated for one basic block at a time.

For simple local vars we do an initial iteration over the bblock instructions and we set the liveness information for each referenced var (live_start and live_end). We will maintain a list of active vars and the current top of stack. As a var becomes alive we allocate it at the current offset and add it to the active_vars array. As a var becomes dead, we remove entries from the active_vars array and update the current top of stack, if space has been freed at the end of the stack.

For call args, because we must control the offset at which these vars are allocated, in the initial pass we generate MOVs from the var to a new local var, if the call arg was initially global. Afterwards, call arg vars are allocated in a similar manner to normal local vars. The space for them is tied to the param area of the call, so the entire space is allocated at once. A call become active when any of its args is first written. The liveness of the call ends when the actual call is done, at which point we resolve the offset of every arg relative to the start of the param area of the method. Once all normal local vars are allocated, we will compute the final offset of the call arg vars.

* [interp] Improve dumping for call instructions

* [interp] Fix var type of valuetype this

* [interp] Re-enable copy propagation

* [interp] Rename MINT_NEWOBJ opcodes

* [interp] Disable tracking of offsets on the execution stack during codegen

They are no longer needed. We generate offsets for every var at the very end.

* [interp] Remove memmove of args during newobj

The offset allocator is allocating the vars at the right offset in the param area. We also used `push_types` to add the arguments back on the stack, which was allocating new vars for each argument. We no longer do this, so newobj_reg_map is not needed anymore.

* [interp] Re-enable inlining of constructors

For object ctors, MINT_NEWOBJ_INLINED allocates an object which will be used both as a `this` arg to the ctor as well as the return var from the newobj operation.

For valuetype ctors, we need to first inform the var offset allocator that the valuetype exists before the MINT_NEWOBJ_VT_INLINED invocation, which will take its address, which will be used as `this` arg to the inline method. We also need to dummy use the valuetype, so it never dies before the ctor is inlined, otherwise `this` points to garbage. We use this def/dummy_use mechanism in order to avoid promoting the valuetype to a global var, as it happens with normal vars that have their address taken (via ldloca).

* [interp] Avoid optimization if newobj is guarded

MINT_NEWOBJ* should not store into a local if the ctor might throw, because we set the return value before the ctor starts executing, and a guarding clause can see this variable as being set.

* [interp] Refactor the active vars code a bit

* [interp] Add missing implicit conversion

When passing an argument or returning a value from a method. The stack contents are not necessarily matching the signature type, in which case we add conversions.

* [interp] Disable test using excessive stack space

This test was exceeding the stack limit even before the new offset allocator, it was just not reported.
  • Loading branch information
BrzVlad authored Mar 12, 2021
1 parent 6a95503 commit 686f752
Show file tree
Hide file tree
Showing 7 changed files with 1,094 additions and 487 deletions.
5 changes: 1 addition & 4 deletions src/mono/mono/mini/interp/interp-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,7 @@ struct InterpMethod {
MonoType **param_types;
MonoJitInfo *jinfo;

// This doesn't include the size of stack locals
guint32 total_locals_size;
// The size of locals that map to the execution stack
guint32 stack_size;
guint32 locals_size;
guint32 alloca_size;
int num_clauses; // clauses
int transformed; // boolean
Expand Down
Loading

0 comments on commit 686f752

Please sign in to comment.