From 0ba6b881fa7fbb5548fee3e2f5fa65530b4aef9f Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Mon, 6 Nov 2023 12:07:39 +0200 Subject: [PATCH 1/6] [mono][interp] Move optimization related code out of transform.c Use interp_ prefix for non-static methods within interpreter (mono_interp seems rather long) Use interp_ prefix, instead of .._interp_..., for consistency. Use ins everywhere in method names for consistency, instead of inst. --- src/mono/mono/mini/CMakeLists.txt | 1 + src/mono/mono/mini/interp/transform-opt.c | 2593 +++++++++++++++++++ src/mono/mono/mini/interp/transform.c | 2745 +-------------------- src/mono/mono/mini/interp/transform.h | 70 +- 4 files changed, 2742 insertions(+), 2667 deletions(-) create mode 100644 src/mono/mono/mini/interp/transform-opt.c diff --git a/src/mono/mono/mini/CMakeLists.txt b/src/mono/mono/mini/CMakeLists.txt index 3628dd3a19de26..5e6ebe9ce49dc4 100644 --- a/src/mono/mono/mini/CMakeLists.txt +++ b/src/mono/mono/mini/CMakeLists.txt @@ -314,6 +314,7 @@ set(interp_sources interp/mintops.h interp/mintops.c interp/transform.c + interp/transform-opt.c interp/tiering.h interp/tiering.c interp/jiterpreter.c diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c new file mode 100644 index 00000000000000..148b3c8a603ecb --- /dev/null +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -0,0 +1,2593 @@ +/* + * Optimizations for interpreter codegen + */ + +#include "mintops.h" +#include "transform.h" + +// Allocates var at the offset that tos points to, also updating it. +static int +alloc_var_offset (TransformData *td, int local, gint32 *ptos) +{ + int size, offset; + + offset = *ptos; + size = td->locals [local].size; + + if (td->locals [local].flags & INTERP_LOCAL_FLAG_SIMD) + offset = ALIGN_TO (offset, MINT_SIMD_ALIGNMENT); + + td->locals [local].offset = offset; + + *ptos = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); + + return td->locals [local].offset; +} + +int +interp_alloc_global_var_offset (TransformData *td, int var) +{ + return alloc_var_offset (td, var, &td->total_locals_size); +} + +static void +set_var_live_range (TransformData *td, int var, int ins_index) +{ + // We don't track liveness yet for global vars + if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) + return; + if (td->locals [var].live_start == -1) + td->locals [var].live_start = ins_index; + td->locals [var].live_end = ins_index; +} + +static void +set_var_live_range_cb (TransformData *td, int var, gpointer data) +{ + set_var_live_range (td, var, (int)(gsize)data); +} + +static void +initialize_global_var (TransformData *td, int var, int bb_index) +{ + // Check if already handled + if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) + return; + + if (td->locals [var].bb_index == -1) { + td->locals [var].bb_index = bb_index; + } else if (td->locals [var].bb_index != bb_index) { + // var used in multiple basic blocks + if (td->verbose_level) + g_print ("alloc global var %d to offset %d\n", var, td->total_locals_size); + interp_alloc_global_var_offset (td, var); + td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + } +} + +static void +initialize_global_var_cb (TransformData *td, int var, gpointer data) +{ + initialize_global_var (td, var, (int)(gsize)data); +} + +static void +initialize_global_vars (TransformData *td) +{ + InterpBasicBlock *bb; + + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + if (opcode == MINT_NOP) { + continue; + } else if (opcode == MINT_LDLOCA_S) { + int var = ins->sregs [0]; + // If global flag is set, it means its offset was already allocated + if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL)) { + if (td->verbose_level) + g_print ("alloc ldloca global var %d to offset %d\n", var, td->total_locals_size); + interp_alloc_global_var_offset (td, var); + td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + } + } + interp_foreach_local_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb); + } + } + td->total_locals_size = ALIGN_TO (td->total_locals_size, MINT_STACK_ALIGNMENT); +} + +// Data structure used for offset allocation of call args +typedef struct { + InterpInst **active_calls; + int active_calls_count; + int active_calls_capacity; + // A deferred call stack implemented as a linked list + GSList *deferred_calls; +} ActiveCalls; + +static void +init_active_calls (TransformData *td, ActiveCalls *ac) +{ + ac->active_calls_count = 0; + ac->active_calls_capacity = 5; + ac->active_calls = (InterpInst**)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (InterpInst*)); + ac->deferred_calls = NULL; +} + +static void +reinit_active_calls (TransformData *td, ActiveCalls *ac) +{ + ac->active_calls_count = 0; + ac->deferred_calls = NULL; +} + +static void +add_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) +{ + // Check if already added + if (call->flags & INTERP_INST_FLAG_ACTIVE_CALL) + return; + + if (ac->active_calls_count == ac->active_calls_capacity) { + InterpInst **old = ac->active_calls; + ac->active_calls_capacity *= 2; + ac->active_calls = (InterpInst**)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (InterpInst*)); + memcpy (ac->active_calls, old, ac->active_calls_count * sizeof (InterpInst*)); + } + ac->active_calls [ac->active_calls_count] = call; + ac->active_calls_count++; + + // Mark a flag on it so we don't have to lookup the array with every argument store. + call->flags |= INTERP_INST_FLAG_ACTIVE_CALL; +} + +/** + * Function allocates offsets of resolved calls following a constraint + * where the base offset of a call must be greater than the offset of any argument of other active call args. + * + * Function first removes the call from an array of active calls. If a match is found, + * the call is removed from the array by moving the last entry into its place. Otherwise, it is a call without arguments. + * + * If there are active calls, the call in question is push onto the stack as a deferred call. + * The call contains a list of other active calls on which it depends. Those calls need to be resolved first in order to determine optimal base offset for the call in question. + * Otherwise, if there are no active calls, function starts resolving the call in question and deferred calls from the stack. + * + * For each call, function computes the base offset, the offset of each call argument starting from a base offset, and stores the computed call offset into a InterpInst. + * The base offset is computed as max offset of all call offsets on which the call depends. + * Stack ensures that all call offsets on which the call depends are calculated before the call in question, by deferring calls from the last to the first one. + */ +static void +end_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) +{ + // Remove call from array + for (int i = 0; i < ac->active_calls_count; i++) { + if (ac->active_calls [i] == call) { + ac->active_calls_count--; + // Since this entry is removed, move the last entry into it + if (ac->active_calls_count > 0 && i < ac->active_calls_count) + ac->active_calls [i] = ac->active_calls [ac->active_calls_count]; + break; + } + } + + // Push active call that should be resolved onto the stack + call->info.call_info->call_deps = NULL; + if (ac->active_calls_count) { + for (int i = 0; i < ac->active_calls_count; i++) + call->info.call_info->call_deps = g_slist_prepend_mempool (td->mempool, call->info.call_info->call_deps, ac->active_calls [i]); + ac->deferred_calls = g_slist_prepend_mempool (td->mempool, ac->deferred_calls, call); + } else { + // If no other active calls, current active call and all deferred calls can be resolved from the stack + InterpInst *deferred_call = call; + while (deferred_call) { + // `base_offset` is a relative offset (to the start of the call args stack) where the args for this call reside. + // The deps for a call represent the list of active calls at the moment when the call ends. This means that all deps for a call end after the call in question. + // Given we iterate over the list of deferred calls from the last to the first one to end, all deps of a call are guaranteed to have been processed at this point. + int base_offset = 0; + for (GSList *list = deferred_call->info.call_info->call_deps; list; list = list->next) { + int end_offset = ((InterpInst*)list->data)->info.call_info->call_end_offset; + if (end_offset > base_offset) + base_offset = end_offset; + } + deferred_call->info.call_info->call_offset = base_offset; + // Compute to offset of each call argument + int *call_args = deferred_call->info.call_info->call_args; + if (call_args && (*call_args != -1)) { + int var = *call_args; + while (var != -1) { + alloc_var_offset (td, var, &base_offset); + call_args++; + var = *call_args; + } + } + deferred_call->info.call_info->call_end_offset = ALIGN_TO (base_offset, MINT_STACK_ALIGNMENT); + + if (ac->deferred_calls) { + deferred_call = (InterpInst*) ac->deferred_calls->data; + ac->deferred_calls = ac->deferred_calls->next; + } else + deferred_call = NULL; + } + } +} + +// Data structure used for offset allocation of local vars + +typedef struct { + int var; + gboolean is_alive; +} ActiveVar; + +typedef struct { + ActiveVar *active_vars; + int active_vars_count; + int active_vars_capacity; +} ActiveVars; + +static void +init_active_vars (TransformData *td, ActiveVars *av) +{ + av->active_vars_count = 0; + av->active_vars_capacity = MAX (td->locals_size / td->bb_count, 10); + av->active_vars = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVars)); +} + +static void +reinit_active_vars (TransformData *td, ActiveVars *av) +{ + av->active_vars_count = 0; +} + +static void +add_active_var (TransformData *td, ActiveVars *av, int var) +{ + if (av->active_vars_count == av->active_vars_capacity) { + av->active_vars_capacity *= 2; + ActiveVar *new_array = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVar)); + memcpy (new_array, av->active_vars, av->active_vars_count * sizeof (ActiveVar)); + av->active_vars = new_array; + } + av->active_vars [av->active_vars_count].var = var; + av->active_vars [av->active_vars_count].is_alive = TRUE; + av->active_vars_count++; +} + +static void +end_active_var (TransformData *td, ActiveVars *av, int var) +{ + // Iterate over active vars, set the entry associated with var as !is_alive + for (int i = 0; i < av->active_vars_count; i++) { + if (av->active_vars [i].var == var) { + av->active_vars [i].is_alive = FALSE; + return; + } + } +} + +static void +compact_active_vars (TransformData *td, ActiveVars *av, gint32 *current_offset) +{ + if (!av->active_vars_count) + return; + int i = av->active_vars_count - 1; + while (i >= 0 && !av->active_vars [i].is_alive) { + av->active_vars_count--; + *current_offset = td->locals [av->active_vars [i].var].offset; + i--; + } +} + +static void +dump_active_vars (TransformData *td, ActiveVars *av) +{ + if (td->verbose_level) { + g_print ("active :"); + for (int i = 0; i < av->active_vars_count; i++) { + if (av->active_vars [i].is_alive) + g_print (" %d (end %d),", av->active_vars [i].var, td->locals [av->active_vars [i].var].live_end); + } + g_print ("\n"); + } +} + +void +interp_alloc_offsets (TransformData *td) +{ + InterpBasicBlock *bb; + ActiveCalls ac; + ActiveVars av; + + if (td->verbose_level) + g_print ("\nvar offset allocator iteration\n"); + + initialize_global_vars (td); + + init_active_vars (td, &av); + init_active_calls (td, &ac); + + int final_total_locals_size = td->total_locals_size; + // We now have the top of stack offset. All local regs are allocated after this offset, with each basic block + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + int ins_index = 0; + if (td->verbose_level) + g_print ("BB%d\n", bb->index); + + reinit_active_calls (td, &ac); + reinit_active_vars (td, &av); + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + if (ins->opcode == MINT_NOP) + continue; + if (ins->opcode == MINT_NEWOBJ || ins->opcode == MINT_NEWOBJ_VT || + ins->opcode == MINT_NEWOBJ_SLOW || ins->opcode == MINT_NEWOBJ_STRING) { + // The offset allocator assumes that the liveness of destination var starts + // after the source vars, which means the destination var can be allocated + // at the same offset as some of the arguments. However, for newobj opcodes, + // the created object is set before the call is made. We solve this by making + // sure that the dreg is not allocated in the param area, so there is no + // risk of conflicts. + td->locals [ins->dreg].flags |= INTERP_LOCAL_FLAG_NO_CALL_ARGS; + } + if (ins->flags & INTERP_INST_FLAG_CALL) { + if (ins->info.call_info && ins->info.call_info->call_args) { + int *call_args = ins->info.call_info->call_args; + guint16 pair_sregs [MINT_MOV_PAIRS_MAX]; + guint16 pair_dregs [MINT_MOV_PAIRS_MAX]; + int num_pairs = 0; + int var = *call_args; + + while (var != -1) { + if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL || + !td->local_ref_count || td->local_ref_count [var] > 1 || + td->locals [var].flags & INTERP_LOCAL_FLAG_NO_CALL_ARGS) { + // Some vars can't be allocated on the call args stack, since the constraint is that + // call args vars die after the call. This isn't necessarily true for global vars or + // vars that are used by other instructions aside from the call. + // We need to copy the var into a new tmp var + int new_var = interp_create_local (td, td->locals [var].type); + td->locals [new_var].call = ins; + td->locals [new_var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + + int mt = mono_mint_type (td->locals [var].type); + if (mt != MINT_TYPE_VT && num_pairs < MINT_MOV_PAIRS_MAX && var <= G_MAXUINT16 && new_var <= G_MAXUINT16) { + // We store these in the instruction data slots so we do this optimizations only if they fit + pair_sregs [num_pairs] = (guint16)var; + pair_dregs [num_pairs] = (guint16)new_var; + num_pairs++; + // The arg of the call is no longer global + *call_args = new_var; + } else { + int opcode = interp_get_mov_for_type (mt, FALSE); + InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); + interp_ins_set_dreg (new_inst, new_var); + interp_ins_set_sreg (new_inst, var); + if (opcode == MINT_MOV_VT) + new_inst->data [0] = GINT_TO_UINT16 (td->locals [var].size); + // The arg of the call is no longer global + *call_args = new_var; + // Also update liveness for this instruction + interp_foreach_local_var (td, new_inst, (gpointer)(gsize)ins_index, set_var_live_range_cb); + ins_index++; + } + } else { + // Flag this var as it has special storage on the call args stack + td->locals [var].call = ins; + td->locals [var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + } + call_args++; + var = *call_args; + } + if (num_pairs > 0) { + int i; + for (i = 0; i < num_pairs; i++) { + set_var_live_range (td, pair_sregs [i], ins_index); + set_var_live_range (td, pair_dregs [i], ins_index); + } + if (num_pairs == 1) { + int mt = mono_mint_type (td->locals [pair_sregs [0]].type); + int opcode = interp_get_mov_for_type (mt, FALSE); + InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); + interp_ins_set_dreg (new_inst, pair_dregs [0]); + interp_ins_set_sreg (new_inst, pair_sregs [0]); + } else { + // Squash together multiple moves to the param area into a single opcode + int opcode = MINT_MOV_8_2 + num_pairs - 2; + InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); + int k = 0; + for (i = 0; i < num_pairs; i++) { + new_inst->data [k++] = pair_dregs [i]; + new_inst->data [k++] = pair_sregs [i]; + } + } + ins_index++; + } + } + } + // Set live_start and live_end for every referenced local that is not global + interp_foreach_local_var (td, ins, (gpointer)(gsize)ins_index, set_var_live_range_cb); + ins_index++; + } + gint32 current_offset = td->total_locals_size; + + ins_index = 0; + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + gboolean is_call = ins->flags & INTERP_INST_FLAG_CALL; + + if (opcode == MINT_NOP) + continue; + + if (td->verbose_level) { + g_print ("\tins_index %d\t", ins_index); + interp_dump_ins (ins, td->data_items); + } + + // Expire source vars. We first mark them as not alive and then compact the array + for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { + int var = ins->sregs [i]; + if (var == MINT_CALL_ARGS_SREG) + continue; + if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) { + g_assert (!(td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS)); + end_active_var (td, &av, var); + } + } + if (opcode >= MINT_MOV_8_2 && opcode <= MINT_MOV_8_4) { + // These opcodes have multiple dvars, which overcomplicate things, so they are + // marked as having no svars/dvars, for now. Special case it. + int num_pairs = 2 + opcode - MINT_MOV_8_2; + for (int i = 0; i < num_pairs; i++) { + int var = ins->data [2 * i + 1]; + if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) + end_active_var (td, &av, var); + } + } + + if (is_call) + end_active_call (td, &ac, ins); + + compact_active_vars (td, &av, ¤t_offset); + + // Alloc dreg local starting at the stack_offset + if (mono_interp_op_dregs [opcode]) { + int var = ins->dreg; + + if (td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { + add_active_call (td, &ac, td->locals [var].call); + } else if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].offset == -1) { + alloc_var_offset (td, var, ¤t_offset); + if (current_offset > final_total_locals_size) + final_total_locals_size = current_offset; + + if (td->verbose_level) + g_print ("alloc var %d to offset %d\n", var, td->locals [var].offset); + + if (td->locals [var].live_end > ins_index) { + // if dreg is still used in the basic block, add it to the active list + add_active_var (td, &av, var); + } else { + current_offset = td->locals [var].offset; + } + } + } + if (td->verbose_level) + dump_active_vars (td, &av); + ins_index++; + } + } + final_total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT); + + // Iterate over all call args locals, update their final offset (aka add td->total_locals_size to them) + // then also update td->total_locals_size to account for this space. + td->param_area_offset = final_total_locals_size; + for (unsigned int i = 0; i < td->locals_size; i++) { + // These are allocated separately at the end of the stack + if (td->locals [i].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { + td->locals [i].offset += td->param_area_offset; + final_total_locals_size = MAX (td->locals [i].offset + td->locals [i].size, final_total_locals_size); + } + } + td->total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT); +} + +static GString* +interp_get_bb_links (InterpBasicBlock *bb) +{ + GString *str = g_string_new (""); + + if (bb->in_count) { + g_string_append_printf (str, "IN (%d", bb->in_bb [0]->index); + for (int i = 1; i < bb->in_count; i++) + g_string_append_printf (str, " %d", bb->in_bb [i]->index); + g_string_append_printf (str, "), "); + } else { + g_string_append_printf (str, "IN (nil), "); + } + + if (bb->out_count) { + g_string_append_printf (str, "OUT (%d", bb->out_bb [0]->index); + for (int i = 1; i < bb->out_count; i++) + g_string_append_printf (str, " %d", bb->out_bb [i]->index); + g_string_append_printf (str, ")"); + } else { + g_string_append_printf (str, "OUT (nil)"); + } + + return str; +} + +static void +mark_bb_as_dead (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *replace_bb) +{ + // Update IL offset to bb mapping so that offset_to_bb doesn't point to dead + // bblocks. This mapping can still be needed when computing clause ranges. Since + // multiple IL offsets can end up pointing to same bblock after optimizations, + // make sure we update mapping for all of them + // + // To avoid scanning the entire offset_to_bb array, we scan only in the vicinity + // of the IL offset of bb. We can stop search when we encounter a different bblock. + g_assert (bb->il_offset >= 0); + for (int il_offset = bb->il_offset; il_offset >= 0; il_offset--) { + if (td->offset_to_bb [il_offset] == bb) + td->offset_to_bb [il_offset] = replace_bb; + else if (td->offset_to_bb [il_offset]) + break; + } + for (guint32 il_offset = bb->il_offset + 1; il_offset < td->header->code_size; il_offset++) { + if (td->offset_to_bb [il_offset] == bb) + td->offset_to_bb [il_offset] = replace_bb; + else if (td->offset_to_bb [il_offset]) + break; + } + + bb->dead = TRUE; + // bb should never be used/referenced after this +} + +/* Merges two consecutive bbs (in code order) into a single one */ +static void +interp_merge_bblocks (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *bbadd) +{ + g_assert (bbadd->in_count == 1 && bbadd->in_bb [0] == bb); + g_assert (bb->next_bb == bbadd); + + // Remove the branch instruction to the invalid bblock + if (bb->last_ins) { + InterpInst *last_ins = (bb->last_ins->opcode != MINT_NOP) ? bb->last_ins : interp_prev_ins (bb->last_ins); + if (last_ins) { + if (last_ins->opcode == MINT_BR) { + g_assert (last_ins->info.target_bb == bbadd); + interp_clear_ins (last_ins); + } else if (last_ins->opcode == MINT_SWITCH) { + // Weird corner case where empty switch can branch by default to next instruction + last_ins->opcode = MINT_NOP; + } + } + } + + // Append all instructions from bbadd to bb + if (bb->last_ins) { + if (bbadd->first_ins) { + bb->last_ins->next = bbadd->first_ins; + bbadd->first_ins->prev = bb->last_ins; + bb->last_ins = bbadd->last_ins; + } + } else { + bb->first_ins = bbadd->first_ins; + bb->last_ins = bbadd->last_ins; + } + bb->next_bb = bbadd->next_bb; + + // Fixup bb links + bb->out_count = bbadd->out_count; + bb->out_bb = bbadd->out_bb; + for (int i = 0; i < bbadd->out_count; i++) { + for (int j = 0; j < bbadd->out_bb [i]->in_count; j++) { + if (bbadd->out_bb [i]->in_bb [j] == bbadd) + bbadd->out_bb [i]->in_bb [j] = bb; + } + } + + mark_bb_as_dead (td, bbadd, bb); +} + +// array must contain ref +static void +remove_bblock_ref (InterpBasicBlock **array, InterpBasicBlock *ref, int len) +{ + int i = 0; + while (array [i] != ref) + i++; + i++; + while (i < len) { + array [i - 1] = array [i]; + i++; + } +} + +static void +interp_unlink_bblocks (InterpBasicBlock *from, InterpBasicBlock *to) +{ + remove_bblock_ref (from->out_bb, to, from->out_count); + from->out_count--; + remove_bblock_ref (to->in_bb, from, to->in_count); + to->in_count--; +} + +static gboolean +interp_remove_bblock (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *prev_bb) +{ + gboolean needs_cprop = FALSE; + + for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { + if (ins->opcode == MINT_LDLOCA_S) { + td->locals [ins->sregs [0]].indirects--; + if (!td->locals [ins->sregs [0]].indirects) { + // We can do cprop now through this local. Run cprop again. + needs_cprop = TRUE; + } + } + } + while (bb->in_count) + interp_unlink_bblocks (bb->in_bb [0], bb); + while (bb->out_count) + interp_unlink_bblocks (bb, bb->out_bb [0]); + prev_bb->next_bb = bb->next_bb; + mark_bb_as_dead (td, bb, bb->next_bb); + + return needs_cprop; +} + +void +interp_link_bblocks (TransformData *td, InterpBasicBlock *from, InterpBasicBlock *to) +{ + int i; + gboolean found = FALSE; + + for (i = 0; i < from->out_count; ++i) { + if (to == from->out_bb [i]) { + found = TRUE; + break; + } + } + if (!found) { + InterpBasicBlock **newa = (InterpBasicBlock**)mono_mempool_alloc (td->mempool, sizeof (InterpBasicBlock*) * (from->out_count + 1)); + for (i = 0; i < from->out_count; ++i) + newa [i] = from->out_bb [i]; + newa [i] = to; + from->out_count++; + from->out_bb = newa; + } + + found = FALSE; + for (i = 0; i < to->in_count; ++i) { + if (from == to->in_bb [i]) { + found = TRUE; + break; + } + } + if (!found) { + InterpBasicBlock **newa = (InterpBasicBlock**)mono_mempool_alloc (td->mempool, sizeof (InterpBasicBlock*) * (to->in_count + 1)); + for (i = 0; i < to->in_count; ++i) + newa [i] = to->in_bb [i]; + newa [i] = from; + to->in_count++; + to->in_bb = newa; + } +} + +static void +interp_mark_reachable_bblocks (TransformData *td) +{ + InterpBasicBlock **queue = mono_mempool_alloc0 (td->mempool, td->bb_count * sizeof (InterpBasicBlock*)); + InterpBasicBlock *current; + int cur_index = 0; + int next_position = 0; + + // FIXME There is no need to force eh bblocks to remain alive + current = td->entry_bb; + while (current != NULL) { + if (current->eh_block || current->patchpoint_data) { + queue [next_position++] = current; + current->reachable = TRUE; + } else { + current->reachable = FALSE; + } + current = current->next_bb; + } + + queue [next_position++] = td->entry_bb; + td->entry_bb->reachable = TRUE; + + // We have the roots, traverse everything else + while (cur_index < next_position) { + current = queue [cur_index++]; + for (int i = 0; i < current->out_count; i++) { + InterpBasicBlock *child = current->out_bb [i]; + if (!child->reachable) { + queue [next_position++] = child; + child->reachable = TRUE; + } + } + } +} + +/** + * Returns TRUE if instruction or previous instructions defines at least one of the variables, FALSE otherwise. + */ + +static gboolean +interp_prev_block_defines_var (InterpInst *ins, int var1, int var2) +{ + // Check max of 5 instructions + for (int i = 0; i < 5; i++) { + ins = interp_prev_ins (ins); + if (!ins) + return FALSE; + if (mono_interp_op_dregs [ins->opcode] && (ins->dreg == var1 || ins->dreg == var2)) + return TRUE; + } + return FALSE; +} + +/** + * Check if the given basic block has a known pattern for inlining into callers blocks, if so, return a pointer to the conditional branch instruction. + * + * The known patterns are: + * - `branch`: a conditional branch instruction. + * - `ldc; branch`: a load instruction followed by a binary conditional branch. + * - `ldc; compare; branch`: a load instruction followed by a compare instruction and a unary conditional branch. + */ +static InterpInst* +interp_inline_into_callers (InterpInst *first, int *lookup_var1, int *lookup_var2) { + // pattern `branch` + if (MINT_IS_CONDITIONAL_BRANCH (first->opcode)) { + *lookup_var1 = first->sregs [0]; + *lookup_var2 = (mono_interp_op_dregs [first->opcode] > 1) ? first->sregs [1] : -1; + return first; + } + + if (MINT_IS_LDC_I4 (first->opcode)) { + InterpInst *second = interp_next_ins (first); + if (!second) + return NULL; + *lookup_var2 = -1; + gboolean first_var_defined = first->dreg == second->sregs [0]; + gboolean second_var_defined = first->dreg == second->sregs [1]; + // pattern `ldc; binop conditional branch` + if (MINT_IS_BINOP_CONDITIONAL_BRANCH (second->opcode) && (first_var_defined || second_var_defined)) { + *lookup_var1 = first_var_defined ? second->sregs [1] : second->sregs [0]; + return second; + } + + InterpInst *third = interp_next_ins (second); + if (!third) + return NULL; + // pattern `ldc; compare; conditional branch` + if (MINT_IS_COMPARE (second->opcode) && (first_var_defined || second_var_defined) + && MINT_IS_UNOP_CONDITIONAL_BRANCH (third->opcode) && second->dreg == third->sregs [0]) { + *lookup_var1 = first_var_defined ? second->sregs [1] : second->sregs [0]; + return third; + } + } + + return NULL; +} + +InterpInst* +interp_first_ins (InterpBasicBlock *bb) +{ + InterpInst *ins = bb->first_ins; + if (!ins || !interp_ins_is_nop (ins)) + return ins; + while (ins && interp_ins_is_nop (ins)) + ins = ins->next; + return ins; +} + + +static InterpInst* +interp_last_ins (InterpBasicBlock *bb) +{ + InterpInst *ins = bb->last_ins; + if (!ins || !interp_ins_is_nop (ins)) + return ins; + return interp_prev_ins (ins); +} + +static void +interp_reorder_bblocks (TransformData *td) +{ + InterpBasicBlock *bb; + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + if (bb->eh_block) + continue; + InterpInst *first = interp_first_ins (bb); + if (!first) + continue; + int lookup_var1, lookup_var2; + InterpInst *cond_ins = interp_inline_into_callers (first, &lookup_var1, &lookup_var2); + if (cond_ins) { + // This means this bblock match a pattern for inlining into callers, with a conditional branch + int i = 0; + while (i < bb->in_count) { + InterpBasicBlock *in_bb = bb->in_bb [i]; + InterpInst *last_ins = interp_last_ins (in_bb); + if (last_ins && last_ins->opcode == MINT_BR && interp_prev_block_defines_var (last_ins, lookup_var1, lookup_var2)) { + // This bblock is reached unconditionally from one of its parents + // Move the conditional branch inside the parent to facilitate propagation + // of condition value. + InterpBasicBlock *cond_true_bb = cond_ins->info.target_bb; + InterpBasicBlock *next_bb = bb->next_bb; + + // Parent bb will do the conditional branch + interp_unlink_bblocks (in_bb, bb); + // Remove ending MINT_BR + interp_clear_ins (last_ins); + // Copy all instructions one by one, from interp_first_ins (bb) to the end of the in_bb + InterpInst *copy_ins = first; + while (copy_ins) { + InterpInst *new_ins = interp_insert_ins_bb (td, in_bb, in_bb->last_ins, copy_ins->opcode); + new_ins->dreg = copy_ins->dreg; + new_ins->sregs [0] = copy_ins->sregs [0]; + if (mono_interp_op_sregs [copy_ins->opcode] > 1) + new_ins->sregs [1] = copy_ins->sregs [1]; + + new_ins->data [0] = copy_ins->data [0]; + if (copy_ins->opcode == MINT_LDC_I4) + new_ins->data [1] = copy_ins->data [1]; + + copy_ins = interp_next_ins (copy_ins); + } + in_bb->last_ins->info.target_bb = cond_true_bb; + interp_link_bblocks (td, in_bb, cond_true_bb); + + // Create new fallthrough bb between in_bb and in_bb->next_bb + InterpBasicBlock *new_bb = interp_alloc_bb (td); + new_bb->next_bb = in_bb->next_bb; + in_bb->next_bb = new_bb; + new_bb->il_offset = in_bb->il_offset; + interp_link_bblocks (td, in_bb, new_bb); + + InterpInst *new_inst = interp_insert_ins_bb (td, new_bb, NULL, MINT_BR); + new_inst->info.target_bb = next_bb; + + interp_link_bblocks (td, new_bb, next_bb); + if (td->verbose_level) { + GString* bb_info = interp_get_bb_links (bb); + GString* in_bb_info = interp_get_bb_links (in_bb); + GString* new_bb_info = interp_get_bb_links (new_bb); + g_print ("Moved cond branch BB%d into BB%d, new BB%d\n", bb->index, in_bb->index, new_bb->index); + g_print ("\tBB%d: %s\n", bb->index, bb_info->str); + g_print ("\tBB%d: %s\n", in_bb->index, in_bb_info->str); + g_print ("\tBB%d: %s\n", new_bb->index, new_bb_info->str); + g_string_free (bb_info, TRUE); + g_string_free (in_bb_info, TRUE); + g_string_free (new_bb_info, TRUE); + } + // Since we changed links, in_bb might have changed, loop again from the start + i = 0; + } else { + i++; + } + } + } else if (first->opcode == MINT_BR) { + // All bblocks jumping into this bblock can jump directly into the br target since it is the single instruction of the bb + int i = 0; + while (i < bb->in_count) { + InterpBasicBlock *in_bb = bb->in_bb [i]; + InterpInst *last_ins = interp_last_ins (in_bb); + if (last_ins && (MINT_IS_CONDITIONAL_BRANCH (last_ins->opcode) || + MINT_IS_UNCONDITIONAL_BRANCH (last_ins->opcode)) && + last_ins->info.target_bb == bb) { + InterpBasicBlock *target_bb = first->info.target_bb; + last_ins->info.target_bb = target_bb; + interp_unlink_bblocks (in_bb, bb); + interp_link_bblocks (td, in_bb, target_bb); + if (td->verbose_level) { + GString* bb_info = interp_get_bb_links (bb); + GString* in_bb_info = interp_get_bb_links (in_bb); + GString* target_bb_info = interp_get_bb_links (target_bb); + g_print ("Propagated target bb BB%d into BB%d\n", target_bb->index, in_bb->index); + g_print ("\tBB%d: %s\n", bb->index, bb_info->str); + g_print ("\tBB%d: %s\n", in_bb->index, in_bb_info->str); + g_print ("\tBB%d: %s\n", target_bb->index, target_bb_info->str); + g_string_free (bb_info, TRUE); + g_string_free (in_bb_info, TRUE); + g_string_free (target_bb_info, TRUE); + } + i = 0; + } else { + i++; + } + } + } + } +} + +// Traverse the list of basic blocks and merge adjacent blocks +static gboolean +interp_optimize_bblocks (TransformData *td) +{ + InterpBasicBlock *bb = td->entry_bb; + gboolean needs_cprop = FALSE; + + interp_reorder_bblocks (td); + + interp_mark_reachable_bblocks (td); + + while (TRUE) { + InterpBasicBlock *next_bb = bb->next_bb; + if (!next_bb) + break; + if (!next_bb->reachable) { + if (td->verbose_level) + g_print ("Removed BB%d\n", next_bb->index); + needs_cprop |= interp_remove_bblock (td, next_bb, bb); + continue; + } else if (bb->out_count == 1 && bb->out_bb [0] == next_bb && next_bb->in_count == 1 && !next_bb->eh_block && !next_bb->patchpoint_data) { + g_assert (next_bb->in_bb [0] == bb); + interp_merge_bblocks (td, bb, next_bb); + if (td->verbose_level) + g_print ("Merged BB%d and BB%d\n", bb->index, next_bb->index); + needs_cprop = TRUE; + continue; + } + + bb = next_bb; + } + return needs_cprop; +} + +static gboolean +interp_local_deadce (TransformData *td) +{ + int *local_ref_count = td->local_ref_count; + gboolean needs_dce = FALSE; + gboolean needs_cprop = FALSE; + + for (unsigned int i = 0; i < td->locals_size; i++) { + g_assert (local_ref_count [i] >= 0); + g_assert (td->locals [i].indirects >= 0); + if (td->locals [i].indirects || (td->locals [i].flags & INTERP_LOCAL_FLAG_DEAD)) + continue; + if (!local_ref_count [i]) { + needs_dce = TRUE; + td->locals [i].flags |= INTERP_LOCAL_FLAG_DEAD; + } else if (!(td->locals [i].flags & INTERP_LOCAL_FLAG_UNKNOWN_USE)) { + if (!(td->locals [i].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) { + // The value of this var is not passed between multiple basic blocks + td->locals [i].flags |= INTERP_LOCAL_FLAG_LOCAL_ONLY; + if (td->verbose_level) + g_print ("Var %d is local only\n", i); + needs_cprop = TRUE; + } + } + td->locals [i].flags &= ~INTERP_LOCAL_FLAG_UNKNOWN_USE; + } + + // Return early if all locals are alive + if (!needs_dce) + return needs_cprop; + + // Kill instructions that don't use stack and are storing into dead locals + for (InterpBasicBlock *bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { + if (MINT_NO_SIDE_EFFECTS (ins->opcode) || + ins->opcode == MINT_LDLOCA_S) { + int dreg = ins->dreg; + if (td->locals [dreg].flags & INTERP_LOCAL_FLAG_DEAD) { + if (td->verbose_level) { + g_print ("kill dead ins:\n\t"); + interp_dump_ins (ins, td->data_items); + } + + if (ins->opcode == MINT_LDLOCA_S) { + mono_interp_stats.ldlocas_removed++; + td->locals [ins->sregs [0]].indirects--; + if (!td->locals [ins->sregs [0]].indirects) { + // We can do cprop now through this local. Run cprop again. + needs_cprop = TRUE; + } + } + interp_clear_ins (ins); + mono_interp_stats.killed_instructions++; + // FIXME This is lazy. We should update the ref count for the sregs and redo deadce. + needs_cprop = TRUE; + } + } + } + } + return needs_cprop; +} + +static InterpInst* +interp_inst_replace_with_i8_const (TransformData *td, InterpInst *ins, gint64 ct) +{ + int size = mono_interp_oplen [ins->opcode]; + int dreg = ins->dreg; + + if (size < 5) { + ins = interp_insert_ins (td, ins, MINT_LDC_I8); + interp_clear_ins (ins->prev); + } else { + ins->opcode = MINT_LDC_I8; + } + WRITE64_INS (ins, 0, &ct); + ins->dreg = dreg; + + return ins; +} + +static gint64 +interp_get_const_from_ldc_i8 (InterpInst *ins) +{ + switch (ins->opcode) { + case MINT_LDC_I8_0: return 0; + case MINT_LDC_I8_S: return (gint64)(gint16)ins->data [0]; + case MINT_LDC_I8: return READ64 (&ins->data [0]); + default: + g_assert_not_reached (); + } +} + +static int +interp_get_mt_for_ldind (int ldind_op) +{ + switch (ldind_op) { + case MINT_LDIND_I1: return MINT_TYPE_I1; + case MINT_LDIND_U1: return MINT_TYPE_U1; + case MINT_LDIND_I2: return MINT_TYPE_I2; + case MINT_LDIND_U2: return MINT_TYPE_U2; + case MINT_LDIND_I4: return MINT_TYPE_I4; + case MINT_LDIND_I8: return MINT_TYPE_I8; + case MINT_LDIND_R4: return MINT_TYPE_R4; + case MINT_LDIND_R8: return MINT_TYPE_R8; + default: + g_assert_not_reached (); + } + return -1; +} + +#define INTERP_FOLD_UNOP(opcode,val_type,field,op) \ + case opcode: \ + result.type = val_type; \ + result.field = op val->field; \ + break; + +#define INTERP_FOLD_CONV(opcode,val_type_dst,field_dst,val_type_src,field_src,cast_type) \ + case opcode: \ + result.type = val_type_dst; \ + result.field_dst = (cast_type)val->field_src; \ + break; + +#define INTERP_FOLD_CONV_FULL(opcode,val_type_dst,field_dst,val_type_src,field_src,cast_type,cond) \ + case opcode: \ + if (!(cond)) return ins; \ + result.type = val_type_dst; \ + result.field_dst = (cast_type)val->field_src; \ + break; + +static InterpInst* +interp_fold_unop (TransformData *td, LocalValue *local_defs, InterpInst *ins) +{ + int *local_ref_count = td->local_ref_count; + // ins should be an unop, therefore it should have a single dreg and a single sreg + int dreg = ins->dreg; + int sreg = ins->sregs [0]; + LocalValue *val = &local_defs [sreg]; + LocalValue result; + + if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8) + return ins; + + // Top of the stack is a constant + switch (ins->opcode) { + INTERP_FOLD_UNOP (MINT_ADD1_I4, LOCAL_VALUE_I4, i, 1+); + INTERP_FOLD_UNOP (MINT_ADD1_I8, LOCAL_VALUE_I8, l, 1+); + INTERP_FOLD_UNOP (MINT_SUB1_I4, LOCAL_VALUE_I4, i, -1+); + INTERP_FOLD_UNOP (MINT_SUB1_I8, LOCAL_VALUE_I8, l, -1+); + INTERP_FOLD_UNOP (MINT_NEG_I4, LOCAL_VALUE_I4, i, -); + INTERP_FOLD_UNOP (MINT_NEG_I8, LOCAL_VALUE_I8, l, -); + INTERP_FOLD_UNOP (MINT_NOT_I4, LOCAL_VALUE_I4, i, ~); + INTERP_FOLD_UNOP (MINT_NOT_I8, LOCAL_VALUE_I8, l, ~); + INTERP_FOLD_UNOP (MINT_CEQ0_I4, LOCAL_VALUE_I4, i, 0 ==); + + INTERP_FOLD_CONV (MINT_CONV_I1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8); + INTERP_FOLD_CONV (MINT_CONV_I1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8); + INTERP_FOLD_CONV (MINT_CONV_U1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint8); + INTERP_FOLD_CONV (MINT_CONV_U1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint8); + + INTERP_FOLD_CONV (MINT_CONV_I2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16); + INTERP_FOLD_CONV (MINT_CONV_I2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint16); + INTERP_FOLD_CONV (MINT_CONV_U2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint16); + INTERP_FOLD_CONV (MINT_CONV_U2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint16); + + INTERP_FOLD_CONV (MINT_CONV_I8_I4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, gint32); + INTERP_FOLD_CONV (MINT_CONV_I8_U4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, guint32); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8, val->i >= G_MININT8 && val->i <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8, val->l >= G_MININT8 && val->l <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8, val->i >= 0 && val->i <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8, val->l >= 0 && val->l <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint8, val->i >= 0 && val->i <= G_MAXUINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint8, val->l >= 0 && val->l <= G_MAXUINT8); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16, val->i >= G_MININT16 && val->i <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, i, gint16, val->l >= G_MININT16 && val->l <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16, val->i >= 0 && val->i <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint16, val->l >= 0 && val->l <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint16, val->i >= 0 && val->i <= G_MAXUINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint16, val->l >= 0 && val->l <= G_MAXUINT16); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint32, val->i >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint32, val->l >= G_MININT32 && val->l <= G_MAXINT32); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint32, val->l >= 0 && val->l <= G_MAXINT32); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint32, val->i >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint32, val->l >= 0 && val->l <= G_MAXINT32); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I8_U8, LOCAL_VALUE_I8, l, LOCAL_VALUE_I8, l, gint64, val->l >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, guint64, val->i >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I8, LOCAL_VALUE_I8, l, LOCAL_VALUE_I8, l, guint64, val->l >= 0); + + default: + return ins; + } + + // We were able to compute the result of the ins instruction. We replace the unop + // with a LDC of the constant. We leave alone the sregs of this instruction, for + // deadce to kill the instructions initializing them. + mono_interp_stats.constant_folds++; + + if (result.type == LOCAL_VALUE_I4) + ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); + else if (result.type == LOCAL_VALUE_I8) + ins = interp_inst_replace_with_i8_const (td, ins, result.l); + else + g_assert_not_reached (); + + if (td->verbose_level) { + g_print ("Fold unop :\n\t"); + interp_dump_ins (ins, td->data_items); + } + + local_ref_count [sreg]--; + result.ins = ins; + result.ref_count = 0; + local_defs [dreg] = result; + + return ins; +} + +#define INTERP_FOLD_UNOP_BR(_opcode,_cond) \ + case _opcode: \ + if (_cond) { \ + ins->opcode = MINT_BR; \ + if (cbb->next_bb != ins->info.target_bb) \ + interp_unlink_bblocks (cbb, cbb->next_bb); \ + for (InterpInst *it = ins->next; it != NULL; it = it->next) \ + interp_clear_ins (it); \ + } else { \ + interp_clear_ins (ins); \ + interp_unlink_bblocks (cbb, ins->info.target_bb); \ + } \ + break; + +static InterpInst* +interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) +{ + int *local_ref_count = td->local_ref_count; + // ins should be an unop conditional branch, therefore it should have a single sreg + int sreg = ins->sregs [0]; + LocalValue *val = &local_defs [sreg]; + + if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8 && val->type != LOCAL_VALUE_NON_NULL) + return ins; + + if (val->type == LOCAL_VALUE_NON_NULL) { + switch (ins->opcode) { + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, FALSE); + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, FALSE); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4, TRUE); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8, TRUE); + + default: + return ins; + } + } else { + // Top of the stack is a constant + switch (ins->opcode) { + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, val->i == 0); + INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, val->l == 0); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4, val->i != 0); + INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8, val->l != 0); + + default: + return ins; + } + } + + if (td->verbose_level) { + g_print ("Fold unop cond br :\n\t"); + interp_dump_ins (ins, td->data_items); + } + + mono_interp_stats.constant_folds++; + local_ref_count [sreg]--; + return ins; +} + +#define INTERP_FOLD_BINOP(opcode,local_type,field,op) \ + case opcode: \ + result.type = local_type; \ + result.field = val1->field op val2->field; \ + break; + +#define INTERP_FOLD_BINOP_FULL(opcode,local_type,field,op,cast_type,cond) \ + case opcode: \ + if (!(cond)) return ins; \ + result.type = local_type; \ + result.field = (cast_type)val1->field op (cast_type)val2->field; \ + break; + +#define INTERP_FOLD_SHIFTOP(opcode,local_type,field,shift_op,cast_type) \ + case opcode: \ + result.type = local_type; \ + result.field = (cast_type)val1->field shift_op val2->i; \ + break; + +#define INTERP_FOLD_RELOP(opcode,local_type,field,relop,cast_type) \ + case opcode: \ + result.type = LOCAL_VALUE_I4; \ + result.i = (cast_type) val1->field relop (cast_type) val2->field; \ + break; + + +static InterpInst* +interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins, gboolean *folded) +{ + int *local_ref_count = td->local_ref_count; + // ins should be a binop, therefore it should have a single dreg and two sregs + int dreg = ins->dreg; + int sreg1 = ins->sregs [0]; + int sreg2 = ins->sregs [1]; + LocalValue *val1 = &local_defs [sreg1]; + LocalValue *val2 = &local_defs [sreg2]; + LocalValue result; + + *folded = FALSE; + + if (val1->type != LOCAL_VALUE_I4 && val1->type != LOCAL_VALUE_I8) + return ins; + if (val2->type != LOCAL_VALUE_I4 && val2->type != LOCAL_VALUE_I8) + return ins; + + // Top two values of the stack are constants + switch (ins->opcode) { + INTERP_FOLD_BINOP (MINT_ADD_I4, LOCAL_VALUE_I4, i, +); + INTERP_FOLD_BINOP (MINT_ADD_I8, LOCAL_VALUE_I8, l, +); + INTERP_FOLD_BINOP (MINT_SUB_I4, LOCAL_VALUE_I4, i, -); + INTERP_FOLD_BINOP (MINT_SUB_I8, LOCAL_VALUE_I8, l, -); + INTERP_FOLD_BINOP (MINT_MUL_I4, LOCAL_VALUE_I4, i, *); + INTERP_FOLD_BINOP (MINT_MUL_I8, LOCAL_VALUE_I8, l, *); + + INTERP_FOLD_BINOP (MINT_AND_I4, LOCAL_VALUE_I4, i, &); + INTERP_FOLD_BINOP (MINT_AND_I8, LOCAL_VALUE_I8, l, &); + INTERP_FOLD_BINOP (MINT_OR_I4, LOCAL_VALUE_I4, i, |); + INTERP_FOLD_BINOP (MINT_OR_I8, LOCAL_VALUE_I8, l, |); + INTERP_FOLD_BINOP (MINT_XOR_I4, LOCAL_VALUE_I4, i, ^); + INTERP_FOLD_BINOP (MINT_XOR_I8, LOCAL_VALUE_I8, l, ^); + + INTERP_FOLD_SHIFTOP (MINT_SHL_I4, LOCAL_VALUE_I4, i, <<, gint32); + INTERP_FOLD_SHIFTOP (MINT_SHL_I8, LOCAL_VALUE_I8, l, <<, gint64); + INTERP_FOLD_SHIFTOP (MINT_SHR_I4, LOCAL_VALUE_I4, i, >>, gint32); + INTERP_FOLD_SHIFTOP (MINT_SHR_I8, LOCAL_VALUE_I8, l, >>, gint64); + INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4, LOCAL_VALUE_I4, i, >>, guint32); + INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8, LOCAL_VALUE_I8, l, >>, guint64); + + INTERP_FOLD_RELOP (MINT_CEQ_I4, LOCAL_VALUE_I4, i, ==, gint32); + INTERP_FOLD_RELOP (MINT_CEQ_I8, LOCAL_VALUE_I8, l, ==, gint64); + INTERP_FOLD_RELOP (MINT_CNE_I4, LOCAL_VALUE_I4, i, !=, gint32); + INTERP_FOLD_RELOP (MINT_CNE_I8, LOCAL_VALUE_I8, l, !=, gint64); + + INTERP_FOLD_RELOP (MINT_CGT_I4, LOCAL_VALUE_I4, i, >, gint32); + INTERP_FOLD_RELOP (MINT_CGT_I8, LOCAL_VALUE_I8, l, >, gint64); + INTERP_FOLD_RELOP (MINT_CGT_UN_I4, LOCAL_VALUE_I4, i, >, guint32); + INTERP_FOLD_RELOP (MINT_CGT_UN_I8, LOCAL_VALUE_I8, l, >, guint64); + + INTERP_FOLD_RELOP (MINT_CGE_I4, LOCAL_VALUE_I4, i, >=, gint32); + INTERP_FOLD_RELOP (MINT_CGE_I8, LOCAL_VALUE_I8, l, >=, gint64); + INTERP_FOLD_RELOP (MINT_CGE_UN_I4, LOCAL_VALUE_I4, i, >=, guint32); + INTERP_FOLD_RELOP (MINT_CGE_UN_I8, LOCAL_VALUE_I8, l, >=, guint64); + + INTERP_FOLD_RELOP (MINT_CLT_I4, LOCAL_VALUE_I4, i, <, gint32); + INTERP_FOLD_RELOP (MINT_CLT_I8, LOCAL_VALUE_I8, l, <, gint64); + INTERP_FOLD_RELOP (MINT_CLT_UN_I4, LOCAL_VALUE_I4, i, <, guint32); + INTERP_FOLD_RELOP (MINT_CLT_UN_I8, LOCAL_VALUE_I8, l, <, guint64); + + INTERP_FOLD_RELOP (MINT_CLE_I4, LOCAL_VALUE_I4, i, <=, gint32); + INTERP_FOLD_RELOP (MINT_CLE_I8, LOCAL_VALUE_I8, l, <=, gint64); + INTERP_FOLD_RELOP (MINT_CLE_UN_I4, LOCAL_VALUE_I4, i, <=, guint32); + INTERP_FOLD_RELOP (MINT_CLE_UN_I8, LOCAL_VALUE_I8, l, <=, guint64); + + INTERP_FOLD_BINOP_FULL (MINT_DIV_I4, LOCAL_VALUE_I4, i, /, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); + INTERP_FOLD_BINOP_FULL (MINT_DIV_I8, LOCAL_VALUE_I8, l, /, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); + INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4, LOCAL_VALUE_I4, i, /, guint32, val2->i != 0); + INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8, LOCAL_VALUE_I8, l, /, guint64, val2->l != 0); + + INTERP_FOLD_BINOP_FULL (MINT_REM_I4, LOCAL_VALUE_I4, i, %, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); + INTERP_FOLD_BINOP_FULL (MINT_REM_I8, LOCAL_VALUE_I8, l, %, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); + INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4, LOCAL_VALUE_I4, i, %, guint32, val2->i != 0); + INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8, LOCAL_VALUE_I8, l, %, guint64, val2->l != 0); + + default: + return ins; + } + + // We were able to compute the result of the ins instruction. We replace the binop + // with a LDC of the constant. We leave alone the sregs of this instruction, for + // deadce to kill the instructions initializing them. + mono_interp_stats.constant_folds++; + *folded = TRUE; + if (result.type == LOCAL_VALUE_I4) + ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); + else if (result.type == LOCAL_VALUE_I8) + ins = interp_inst_replace_with_i8_const (td, ins, result.l); + else + g_assert_not_reached (); + + if (td->verbose_level) { + g_print ("Fold binop :\n\t"); + interp_dump_ins (ins, td->data_items); + } + + local_ref_count [sreg1]--; + local_ref_count [sreg2]--; + result.ins = ins; + result.ref_count = 0; + local_defs [dreg] = result; + return ins; +} + +// Due to poor current design, the branch op might not be the last instruction in the bblock +// (in case we fallthrough and need to have the stack locals match the ones from next_bb, done +// in fixup_newbb_stack_locals). If that's the case, clear all these mov's. This helps bblock +// merging quickly find the MINT_BR opcode. +#define INTERP_FOLD_BINOP_BR(_opcode,_local_type,_cond) \ + case _opcode: \ + if (_cond) { \ + ins->opcode = MINT_BR; \ + if (cbb->next_bb != ins->info.target_bb) \ + interp_unlink_bblocks (cbb, cbb->next_bb); \ + for (InterpInst *it = ins->next; it != NULL; it = it->next) \ + interp_clear_ins (it); \ + } else { \ + interp_clear_ins (ins); \ + interp_unlink_bblocks (cbb, ins->info.target_bb); \ + } \ + break; + +static InterpInst* +interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) +{ + int *local_ref_count = td->local_ref_count; + // ins should be a conditional binop, therefore it should have only two sregs + int sreg1 = ins->sregs [0]; + int sreg2 = ins->sregs [1]; + LocalValue *val1 = &local_defs [sreg1]; + LocalValue *val2 = &local_defs [sreg2]; + + if (val1->type != LOCAL_VALUE_I4 && val1->type != LOCAL_VALUE_I8) + return ins; + if (val2->type != LOCAL_VALUE_I4 && val2->type != LOCAL_VALUE_I8) + return ins; + + switch (ins->opcode) { + INTERP_FOLD_BINOP_BR (MINT_BEQ_I4, LOCAL_VALUE_I4, val1->i == val2->i); + INTERP_FOLD_BINOP_BR (MINT_BEQ_I8, LOCAL_VALUE_I8, val1->l == val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGE_I4, LOCAL_VALUE_I4, val1->i >= val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGE_I8, LOCAL_VALUE_I8, val1->l >= val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGT_I4, LOCAL_VALUE_I4, val1->i > val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGT_I8, LOCAL_VALUE_I8, val1->l > val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLT_I4, LOCAL_VALUE_I4, val1->i < val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLT_I8, LOCAL_VALUE_I8, val1->l < val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLE_I4, LOCAL_VALUE_I4, val1->i <= val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLE_I8, LOCAL_VALUE_I8, val1->l <= val2->l); + + INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I4, LOCAL_VALUE_I4, val1->i != val2->i); + INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I8, LOCAL_VALUE_I8, val1->l != val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i >= (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l >= (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i > (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l > (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i <= (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l <= (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i < (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l < (guint64)val2->l); + + default: + return ins; + } + if (td->verbose_level) { + g_print ("Fold binop cond br :\n\t"); + interp_dump_ins (ins, td->data_items); + } + + mono_interp_stats.constant_folds++; + local_ref_count [sreg1]--; + local_ref_count [sreg2]--; + return ins; +} + +static void +write_v128_element (gpointer v128_addr, LocalValue *val, int index, int el_size) +{ + gpointer el_addr = (gint8*)v128_addr + index * el_size; + g_assert ((gint8*)el_addr < ((gint8*)v128_addr + 16)); + switch (el_size) { + case 1: *(gint8*)el_addr = (gint8)val->i; break; + case 2: *(gint16*)el_addr = (gint16)val->i; break; + case 4: *(gint32*)el_addr = val->i; break; // this also handles r4 + case 8: *(gint64*)el_addr = val->l; break; + default: + g_assert_not_reached (); + } +} + +static InterpInst* +interp_fold_simd_create (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) +{ + int *local_ref_count = td->local_ref_count; + + int *args = ins->info.call_info->call_args; + int index = 0; + int var = args [index]; + while (var != -1) { + LocalValue *val = &local_defs [var]; + if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8 && val->type != LOCAL_VALUE_R4) + return ins; + index++; + var = args [index]; + } + + // If we reached this point, it means that all args of the simd_create are constants + // We can replace the simd_create with simd_ldc + int el_size = 16 / index; + int dreg = ins->dreg; + + ins = interp_insert_ins (td, ins, MINT_SIMD_V128_LDC); + interp_clear_ins (ins->prev); + interp_ins_set_dreg (ins, dreg); + + gpointer v128_addr = &ins->data [0]; + + index = 0; + var = args [index]; + while (var != -1) { + LocalValue *val = &local_defs [var]; + write_v128_element (v128_addr, val, index, el_size); + val->ref_count--; + local_ref_count [var]--; + index++; + var = args [index]; + } + + if (td->verbose_level) { + g_print ("Fold simd create:\n\t"); + interp_dump_ins (ins, td->data_items); + } + + local_defs [dreg].ins = ins; + local_defs [dreg].type = LOCAL_VALUE_NONE; + + return ins; +} + +static void +cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, LocalValue *local_defs) +{ + int *local_ref_count = td->local_ref_count; + int sreg = *psreg; + + local_ref_count [sreg]++; + local_defs [sreg].ref_count++; + if (local_defs [sreg].type == LOCAL_VALUE_LOCAL) { + int cprop_local = local_defs [sreg].local; + + // We are trying to replace sregs [i] with its def local (cprop_local), but cprop_local has since been + // modified, so we can't use it. + if (local_defs [cprop_local].ins != NULL && local_defs [cprop_local].def_index > local_defs [sreg].def_index) + return; + + if (td->verbose_level) + g_print ("cprop %d -> %d:\n\t", sreg, cprop_local); + local_ref_count [sreg]--; + *psreg = cprop_local; + local_ref_count [cprop_local]++; + if (td->verbose_level) + interp_dump_ins (ins, td->data_items); + } else if (!local_defs [sreg].ins) { + td->locals [sreg].flags |= INTERP_LOCAL_FLAG_UNKNOWN_USE; + } +} + +static void +clear_local_defs (TransformData *td, int var, void *data) +{ + LocalValue *local_defs = (LocalValue*) data; + local_defs [var].type = LOCAL_VALUE_NONE; + local_defs [var].ins = NULL; + local_defs [var].ref_count = 0; +} + +static void +clear_unused_defs (TransformData *td, int var, void *data) +{ + if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) + return; + if (td->locals [var].indirects) + return; + + LocalValue *local_def = &((LocalValue*) data) [var]; + InterpInst *def_ins = local_def->ins; + if (!def_ins) + return; + if (local_def->ref_count) + return; + + // This is a local only var that is defined in this bblock and its value is not used + // at all in this bblock. Clear the definition + if (MINT_NO_SIDE_EFFECTS (def_ins->opcode)) { + for (int i = 0; i < mono_interp_op_sregs [def_ins->opcode]; i++) + td->local_ref_count [def_ins->sregs [i]]--; + if (td->verbose_level) { + g_print ("kill unused local def:\n\t"); + interp_dump_ins (def_ins, td->data_items); + } + interp_clear_ins (def_ins); + } +} + +static void +interp_cprop (TransformData *td) +{ + LocalValue *local_defs = (LocalValue*) g_malloc (td->locals_size * sizeof (LocalValue)); + int *local_ref_count = (int*) g_malloc (td->locals_size * sizeof (int)); + InterpBasicBlock *bb; + gboolean needs_retry; + int ins_index; + int iteration_count = 0; + + td->local_ref_count = local_ref_count; +retry: + needs_retry = FALSE; + memset (local_ref_count, 0, td->locals_size * sizeof (int)); + + if (td->verbose_level) + g_print ("\ncprop iteration %d\n", iteration_count++); + + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + ins_index = 0; + + // Set cbb since we do some instruction inserting below + td->cbb = bb; + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) + interp_foreach_local_var (td, ins, local_defs, clear_local_defs); + + if (td->verbose_level) { + GString* bb_info = interp_get_bb_links (bb); + g_print ("\nBB%d: %s\n", bb->index, bb_info->str); + g_string_free (bb_info, TRUE); + } + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + + if (opcode == MINT_NOP) + continue; + + int num_sregs = mono_interp_op_sregs [opcode]; + int num_dregs = mono_interp_op_dregs [opcode]; + gint32 *sregs = &ins->sregs [0]; + gint32 dreg = ins->dreg; + + if (td->verbose_level && ins->opcode != MINT_NOP && ins->opcode != MINT_IL_SEQ_POINT) + interp_dump_ins (ins, td->data_items); + + for (int i = 0; i < num_sregs; i++) { + if (sregs [i] == MINT_CALL_ARGS_SREG) { + if (ins->info.call_info && ins->info.call_info->call_args) { + int *call_args = ins->info.call_info->call_args; + while (*call_args != -1) { + cprop_sreg (td, ins, call_args, local_defs); + call_args++; + } + } + } else { + cprop_sreg (td, ins, &sregs [i], local_defs); + } + } + + if (num_dregs) { + // Check if the previous definition of this var was used at all. + // If it wasn't we can just clear the instruction + // + // MINT_MOV_DST_OFF doesn't fully write to the var, so we special case it here + if (local_defs [dreg].ins != NULL && + local_defs [dreg].ref_count == 0 && + !td->locals [dreg].indirects && + opcode != MINT_MOV_DST_OFF) { + InterpInst *prev_def = local_defs [dreg].ins; + if (MINT_NO_SIDE_EFFECTS (prev_def->opcode)) { + for (int i = 0; i < mono_interp_op_sregs [prev_def->opcode]; i++) + local_ref_count [prev_def->sregs [i]]--; + interp_clear_ins (prev_def); + } + } + local_defs [dreg].type = LOCAL_VALUE_NONE; + local_defs [dreg].ins = ins; + local_defs [dreg].def_index = ins_index; + } + + // We always store to the full i4, except as part of STIND opcodes. These opcodes can be + // applied to a local var only if that var has LDLOCA applied to it + if ((opcode >= MINT_MOV_I4_I1 && opcode <= MINT_MOV_I4_U2) && !td->locals [sregs [0]].indirects) { + ins->opcode = MINT_MOV_4; + opcode = MINT_MOV_4; + } + + if (opcode == MINT_MOV_4 || opcode == MINT_MOV_8 || opcode == MINT_MOV_VT) { + int sreg = sregs [0]; + if (dreg == sreg) { + if (td->verbose_level) + g_print ("clear redundant mov\n"); + interp_clear_ins (ins); + local_ref_count [sreg]--; + } else if (td->locals [sreg].indirects || td->locals [dreg].indirects) { + // Don't bother with indirect locals + } else if (local_defs [sreg].type == LOCAL_VALUE_I4 || local_defs [sreg].type == LOCAL_VALUE_I8) { + // Replace mov with ldc + gboolean is_i4 = local_defs [sreg].type == LOCAL_VALUE_I4; + g_assert (!td->locals [sreg].indirects); + local_defs [dreg].type = local_defs [sreg].type; + if (is_i4) { + int ct = local_defs [sreg].i; + ins = interp_get_ldc_i4_from_const (td, ins, ct, dreg); + local_defs [dreg].i = ct; + } else { + gint64 ct = local_defs [sreg].l; + ins = interp_inst_replace_with_i8_const (td, ins, ct); + local_defs [dreg].l = ct; + } + local_defs [dreg].ins = ins; + local_ref_count [sreg]--; + mono_interp_stats.copy_propagations++; + if (td->verbose_level) { + g_print ("cprop loc %d -> ct :\n\t", sreg); + interp_dump_ins (ins, td->data_items); + } + } else if (local_defs [sreg].ins != NULL && + (td->locals [sreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && + !(td->locals [dreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && + interp_prev_ins (ins) == local_defs [sreg].ins && + !(interp_prev_ins (ins)->flags & INTERP_INST_FLAG_PROTECTED_NEWOBJ)) { + // hackish temporary optimization that won't be necessary in the future + // We replace `local1 <- ?, local2 <- local1` with `local2 <- ?, local1 <- local2` + // if local1 is execution stack local and local2 is normal global local. This makes + // it more likely for `local1 <- local2` to be killed, while before we always needed + // to store to the global local, which is likely accessed by other instructions. + InterpInst *def = local_defs [sreg].ins; + int original_dreg = def->dreg; + + def->dreg = dreg; + ins->dreg = original_dreg; + sregs [0] = dreg; + + local_defs [dreg].type = LOCAL_VALUE_NONE; + local_defs [dreg].ins = def; + local_defs [dreg].def_index = local_defs [original_dreg].def_index; + local_defs [dreg].ref_count++; + local_defs [original_dreg].type = LOCAL_VALUE_LOCAL; + local_defs [original_dreg].ins = ins; + local_defs [original_dreg].local = dreg; + local_defs [original_dreg].def_index = ins_index; + local_defs [original_dreg].ref_count--; + + local_ref_count [original_dreg]--; + local_ref_count [dreg]++; + + if (td->verbose_level) { + g_print ("cprop dreg:\n\t"); + interp_dump_ins (def, td->data_items); + g_print ("\t"); + interp_dump_ins (ins, td->data_items); + } + } else { + if (td->verbose_level) + g_print ("local copy %d <- %d\n", dreg, sreg); + local_defs [dreg].type = LOCAL_VALUE_LOCAL; + local_defs [dreg].local = sreg; + } + } else if (opcode == MINT_LDLOCA_S) { + // The local that we are taking the address of is not a sreg but still referenced + local_ref_count [ins->sregs [0]]++; + } else if (MINT_IS_LDC_I4 (opcode)) { + local_defs [dreg].type = LOCAL_VALUE_I4; + local_defs [dreg].i = interp_get_const_from_ldc_i4 (ins); + } else if (MINT_IS_LDC_I8 (opcode)) { + local_defs [dreg].type = LOCAL_VALUE_I8; + local_defs [dreg].l = interp_get_const_from_ldc_i8 (ins); + } else if (opcode == MINT_LDC_R4) { + guint32 val_u = READ32 (&ins->data [0]); + float f = *(float*)(&val_u); + local_defs [dreg].type = LOCAL_VALUE_R4; + local_defs [dreg].f = f; + } else if (ins->opcode == MINT_LDPTR) { +#if SIZEOF_VOID_P == 8 + local_defs [dreg].type = LOCAL_VALUE_I8; + local_defs [dreg].l = (gint64)td->data_items [ins->data [0]]; +#else + local_defs [dreg].type = LOCAL_VALUE_I4; + local_defs [dreg].i = (gint32)td->data_items [ins->data [0]]; +#endif + } else if (MINT_IS_UNOP (opcode)) { + ins = interp_fold_unop (td, local_defs, ins); + } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode)) { + ins = interp_fold_unop_cond_br (td, bb, local_defs, ins); + } else if (MINT_IS_SIMD_CREATE (opcode)) { + ins = interp_fold_simd_create (td, bb, local_defs, ins); + } else if (MINT_IS_BINOP (opcode)) { + gboolean folded; + ins = interp_fold_binop (td, local_defs, ins, &folded); + if (!folded) { + int sreg = -1; + guint16 mov_op = 0; + if ((opcode == MINT_MUL_I4 || opcode == MINT_DIV_I4) && + local_defs [ins->sregs [1]].type == LOCAL_VALUE_I4 && + local_defs [ins->sregs [1]].i == 1) { + sreg = ins->sregs [0]; + mov_op = MINT_MOV_4; + } else if ((opcode == MINT_MUL_I8 || opcode == MINT_DIV_I8) && + local_defs [ins->sregs [1]].type == LOCAL_VALUE_I8 && + local_defs [ins->sregs [1]].l == 1) { + sreg = ins->sregs [0]; + mov_op = MINT_MOV_8; + } else if (opcode == MINT_MUL_I4 && + local_defs [ins->sregs [0]].type == LOCAL_VALUE_I4 && + local_defs [ins->sregs [0]].i == 1) { + sreg = ins->sregs [1]; + mov_op = MINT_MOV_4; + } else if (opcode == MINT_MUL_I8 && + local_defs [ins->sregs [0]].type == LOCAL_VALUE_I8 && + local_defs [ins->sregs [0]].l == 1) { + sreg = ins->sregs [1]; + mov_op = MINT_MOV_8; + } + if (sreg != -1) { + ins->opcode = mov_op; + ins->sregs [0] = sreg; + if (td->verbose_level) { + g_print ("Replace idempotent binop :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } + } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode)) { + ins = interp_fold_binop_cond_br (td, bb, local_defs, ins); + } else if (MINT_IS_LDIND (opcode)) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int local = ldloca->sregs [0]; + int mt = td->locals [local].mt; + if (mt != MINT_TYPE_VT) { + // LDIND cannot simply be replaced with a mov because it might also include a + // necessary conversion (especially when we do cprop and do moves between vars of + // different types). + int ldind_mt = interp_get_mt_for_ldind (opcode); + switch (ldind_mt) { + case MINT_TYPE_I1: ins->opcode = MINT_CONV_I1_I4; break; + case MINT_TYPE_U1: ins->opcode = MINT_CONV_U1_I4; break; + case MINT_TYPE_I2: ins->opcode = MINT_CONV_I2_I4; break; + case MINT_TYPE_U2: ins->opcode = MINT_CONV_U2_I4; break; + default: + ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (ldind_mt, FALSE)); + break; + } + local_ref_count [sregs [0]]--; + interp_ins_set_sreg (ins, local); + + if (td->verbose_level) { + g_print ("Replace ldloca/ldind pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } + } else if (MINT_IS_LDFLD (opcode)) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int mt = ins->opcode - MINT_LDFLD_I1; + int local = ldloca->sregs [0]; + // Allow ldloca instruction to be killed + local_ref_count [sregs [0]]--; + if (td->locals [local].mt == (ins->opcode - MINT_LDFLD_I1) && ins->data [0] == 0) { + // Replace LDLOCA + LDFLD with LDLOC, when the loading field represents + // the entire local. This is the case with loading the only field of an + // IntPtr. We don't handle value type loads. + ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, TRUE)); + // The dreg of the MOV is the same as the dreg of the LDFLD + sregs [0] = local; + } else { + // Add mov.src.off to load directly from the local var space without use of ldloca. + int foffset = ins->data [0]; + guint16 ldsize = 0; + if (mt == MINT_TYPE_VT) + ldsize = ins->data [1]; + + // This loads just a part of the local valuetype + ins = interp_insert_ins (td, ins, MINT_MOV_SRC_OFF); + interp_ins_set_dreg (ins, ins->prev->dreg); + interp_ins_set_sreg (ins, local); + ins->data [0] = GINT_TO_UINT16 (foffset); + ins->data [1] = GINT_TO_UINT16 (mt); + if (mt == MINT_TYPE_VT) + ins->data [2] = ldsize; + + interp_clear_ins (ins->prev); + } + + if (td->verbose_level) { + g_print ("Replace ldloca/ldfld pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } else if (opcode == MINT_INITOBJ) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int size = ins->data [0]; + int local = ldloca->sregs [0]; + // Replace LDLOCA + INITOBJ with or LDC + if (size <= 4) + ins->opcode = MINT_LDC_I4_0; + else if (size <= 8) + ins->opcode = MINT_LDC_I8_0; + else + ins->opcode = MINT_INITLOCAL; + local_ref_count [sregs [0]]--; + ins->dreg = local; + + if (td->verbose_level) { + g_print ("Replace ldloca/initobj pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } else if (opcode == MINT_LDOBJ_VT) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int ldsize = ins->data [0]; + int local = ldloca->sregs [0]; + local_ref_count [sregs [0]]--; + + if (ldsize == td->locals [local].size) { + // Replace LDLOCA + LDOBJ_VT with MOV_VT + ins->opcode = MINT_MOV_VT; + sregs [0] = local; + needs_retry = TRUE; + } else { + // This loads just a part of the local valuetype + ins = interp_insert_ins (td, ins, MINT_MOV_SRC_OFF); + interp_ins_set_dreg (ins, ins->prev->dreg); + interp_ins_set_sreg (ins, local); + ins->data [0] = 0; + ins->data [1] = MINT_TYPE_VT; + ins->data [2] = GINT_TO_UINT16 (ldsize); + + interp_clear_ins (ins->prev); + } + if (td->verbose_level) { + g_print ("Replace ldloca/ldobj_vt pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + } + } else if (opcode == MINT_STOBJ_VT || opcode == MINT_STOBJ_VT_NOREF) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int stsize = ins->data [0]; + int local = ldloca->sregs [0]; + + if (stsize == td->locals [local].size) { + // Replace LDLOCA + STOBJ_VT with MOV_VT + local_ref_count [sregs [0]]--; + ins->opcode = MINT_MOV_VT; + sregs [0] = sregs [1]; + ins->dreg = local; + needs_retry = TRUE; + + if (td->verbose_level) { + g_print ("Replace ldloca/stobj_vt pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + } + } + } else if (MINT_IS_STIND (opcode)) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int local = ldloca->sregs [0]; + int mt = td->locals [local].mt; + if (mt != MINT_TYPE_VT) { + // We have an 8 byte local, just replace the stind with a mov + local_ref_count [sregs [0]]--; + // We make the assumption that the STIND matches the local type + ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, TRUE)); + interp_ins_set_dreg (ins, local); + interp_ins_set_sreg (ins, sregs [1]); + + if (td->verbose_level) { + g_print ("Replace ldloca/stind pair :\n\t"); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } + } else if (MINT_IS_STFLD (opcode)) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int mt = ins->opcode - MINT_STFLD_I1; + int local = ldloca->sregs [0]; + local_ref_count [sregs [0]]--; + // Allow ldloca instruction to be killed + if (td->locals [local].mt == (ins->opcode - MINT_STFLD_I1) && ins->data [0] == 0) { + ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, FALSE)); + // The sreg of the MOV is the same as the second sreg of the STFLD + ins->dreg = local; + sregs [0] = sregs [1]; + } else { + // Add mov.dst.off to store directly int the local var space without use of ldloca. + int foffset = ins->data [0]; + guint16 vtsize = 0; + if (mt == MINT_TYPE_VT) { + vtsize = ins->data [1]; + } +#ifdef NO_UNALIGNED_ACCESS + else { + // As with normal loads/stores we use memcpy for unaligned 8 byte accesses + if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && foffset % SIZEOF_VOID_P != 0) + vtsize = 8; + } +#endif + + // This stores just to part of the dest valuetype + ins = interp_insert_ins (td, ins, MINT_MOV_DST_OFF); + interp_ins_set_dreg (ins, local); + interp_ins_set_sreg (ins, sregs [1]); + ins->data [0] = GINT_TO_UINT16 (foffset); + ins->data [1] = GINT_TO_UINT16 (mt); + ins->data [2] = vtsize; + + interp_clear_ins (ins->prev); + } + if (td->verbose_level) { + g_print ("Replace ldloca/stfld pair (off %p) :\n\t", (void *)(uintptr_t) ldloca->il_offset); + interp_dump_ins (ins, td->data_items); + } + needs_retry = TRUE; + } + } else if (opcode == MINT_GETITEM_SPAN) { + InterpInst *ldloca = local_defs [sregs [0]].ins; + if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { + int local = ldloca->sregs [0]; + // Allow ldloca instruction to be killed + local_ref_count [sregs [0]]--; + // Instead of loading from the indirect pointer pass directly the vt var + ins->opcode = MINT_GETITEM_LOCALSPAN; + sregs [0] = local; + needs_retry = TRUE; + } + } else if (opcode == MINT_CKNULL) { + InterpInst *def = local_defs [sregs [0]].ins; + if (def && def->opcode == MINT_LDLOCA_S) { + // CKNULL on LDLOCA is a NOP + ins->opcode = MINT_MOV_P; + needs_retry = TRUE; + } + } else if (opcode == MINT_BOX) { + // TODO Add more relevant opcodes + local_defs [dreg].type = LOCAL_VALUE_NON_NULL; + } + + ins_index++; + } + + for (ins = bb->first_ins; ins != NULL; ins = ins->next) + interp_foreach_local_var (td, ins, local_defs, clear_unused_defs); + } + + needs_retry |= interp_local_deadce (td); + if (mono_interp_opt & INTERP_OPT_BBLOCKS) + needs_retry |= interp_optimize_bblocks (td); + + if (needs_retry) + goto retry; + + g_free (local_defs); +} + +void +mono_test_interp_cprop (TransformData *td) +{ + interp_cprop (td); +} + +static gboolean +get_sreg_imm (TransformData *td, int sreg, gint16 *imm, int result_mt) +{ + InterpInst *def = td->locals [sreg].def; + if (def != NULL && td->local_ref_count [sreg] == 1) { + gint64 ct; + if (MINT_IS_LDC_I4 (def->opcode)) + ct = interp_get_const_from_ldc_i4 (def); + else if (MINT_IS_LDC_I8 (def->opcode)) + ct = interp_get_const_from_ldc_i8 (def); + else + return FALSE; + gint64 min_val, max_val; + // We only propagate the immediate only if it fits into the desired type, + // so we don't accidentaly handle conversions wrong + switch (result_mt) { + case MINT_TYPE_I1: + min_val = G_MININT8; + max_val = G_MAXINT8; + break; + case MINT_TYPE_I2: + min_val = G_MININT16; + max_val = G_MAXINT16; + break; + case MINT_TYPE_U1: + min_val = 0; + max_val = G_MAXUINT8; + break; + case MINT_TYPE_U2: + min_val = 0; + max_val = G_MAXINT16; + break; + default: + g_assert_not_reached (); + + } + if (ct >= min_val && ct <= max_val) { + *imm = (gint16)ct; + mono_interp_stats.super_instructions++; + return TRUE; + } + } + return FALSE; +} + +static int +get_binop_condbr_imm_sp (int opcode) +{ + switch (opcode) { + case MINT_BEQ_I4: return MINT_BEQ_I4_IMM_SP; + case MINT_BEQ_I8: return MINT_BEQ_I8_IMM_SP; + case MINT_BGE_I4: return MINT_BGE_I4_IMM_SP; + case MINT_BGE_I8: return MINT_BGE_I8_IMM_SP; + case MINT_BGT_I4: return MINT_BGT_I4_IMM_SP; + case MINT_BGT_I8: return MINT_BGT_I8_IMM_SP; + case MINT_BLT_I4: return MINT_BLT_I4_IMM_SP; + case MINT_BLT_I8: return MINT_BLT_I8_IMM_SP; + case MINT_BLE_I4: return MINT_BLE_I4_IMM_SP; + case MINT_BLE_I8: return MINT_BLE_I8_IMM_SP; + case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_IMM_SP; + case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_IMM_SP; + case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_IMM_SP; + case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_IMM_SP; + case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_IMM_SP; + case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_IMM_SP; + case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_IMM_SP; + case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_IMM_SP; + case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_IMM_SP; + case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_IMM_SP; + default: return MINT_NOP; + } +} + +static int +get_binop_condbr_sp (int opcode) +{ + switch (opcode) { + case MINT_BEQ_I4: return MINT_BEQ_I4_SP; + case MINT_BEQ_I8: return MINT_BEQ_I8_SP; + case MINT_BGE_I4: return MINT_BGE_I4_SP; + case MINT_BGE_I8: return MINT_BGE_I8_SP; + case MINT_BGT_I4: return MINT_BGT_I4_SP; + case MINT_BGT_I8: return MINT_BGT_I8_SP; + case MINT_BLT_I4: return MINT_BLT_I4_SP; + case MINT_BLT_I8: return MINT_BLT_I8_SP; + case MINT_BLE_I4: return MINT_BLE_I4_SP; + case MINT_BLE_I8: return MINT_BLE_I8_SP; + case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_SP; + case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_SP; + case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_SP; + case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_SP; + case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_SP; + case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_SP; + case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_SP; + case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_SP; + case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_SP; + case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_SP; + default: return MINT_NOP; + } +} + +static int +get_unop_condbr_sp (int opcode) +{ + switch (opcode) { + case MINT_BRFALSE_I4: return MINT_BRFALSE_I4_SP; + case MINT_BRFALSE_I8: return MINT_BRFALSE_I8_SP; + case MINT_BRTRUE_I4: return MINT_BRTRUE_I4_SP; + case MINT_BRTRUE_I8: return MINT_BRTRUE_I8_SP; + default: return MINT_NOP; + } +} + +static void +interp_super_instructions (TransformData *td) +{ + InterpBasicBlock *bb; + int *local_ref_count = td->local_ref_count; + + interp_compute_native_offset_estimates (td); + + // Add some actual super instructions + for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { + InterpInst *ins; + int noe; + + // Set cbb since we do some instruction inserting below + td->cbb = bb; + noe = bb->native_offset_estimate; + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { + int opcode = ins->opcode; + if (MINT_IS_NOP (opcode)) + continue; + if (mono_interp_op_dregs [opcode] && !(td->locals [ins->dreg].flags & INTERP_LOCAL_FLAG_GLOBAL)) + td->locals [ins->dreg].def = ins; + + if (opcode == MINT_RET || (opcode >= MINT_RET_I1 && opcode <= MINT_RET_U2)) { + // ldc + ret -> ret.imm + int sreg = ins->sregs [0]; + gint16 imm; + if (get_sreg_imm (td, sreg, &imm, (opcode == MINT_RET) ? MINT_TYPE_I2 : opcode - MINT_RET_I1)) { + InterpInst *def = td->locals [sreg].def; + int ret_op = MINT_IS_LDC_I4 (def->opcode) ? MINT_RET_I4_IMM : MINT_RET_I8_IMM; + InterpInst *new_inst = interp_insert_ins (td, ins, ret_op); + new_inst->data [0] = imm; + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg]--; + + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } else if (opcode == MINT_ADD_I4 || opcode == MINT_ADD_I8 || + opcode == MINT_MUL_I4 || opcode == MINT_MUL_I8) { + int sreg = -1; + int sreg_imm = -1; + gint16 imm; + if (get_sreg_imm (td, ins->sregs [0], &imm, MINT_TYPE_I2)) { + sreg = ins->sregs [1]; + sreg_imm = ins->sregs [0]; + } else if (get_sreg_imm (td, ins->sregs [1], &imm, MINT_TYPE_I2)) { + sreg = ins->sregs [0]; + sreg_imm = ins->sregs [1]; + } + if (sreg != -1) { + int binop; + switch (opcode) { + case MINT_ADD_I4: binop = MINT_ADD_I4_IMM; break; + case MINT_ADD_I8: binop = MINT_ADD_I8_IMM; break; + case MINT_MUL_I4: binop = MINT_MUL_I4_IMM; break; + case MINT_MUL_I8: binop = MINT_MUL_I8_IMM; break; + default: g_assert_not_reached (); + } + InterpInst *new_inst = interp_insert_ins (td, ins, binop); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = sreg; + new_inst->data [0] = imm; + interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } else if (opcode == MINT_SUB_I4 || opcode == MINT_SUB_I8) { + // ldc + sub -> add.-imm + gint16 imm; + int sreg_imm = ins->sregs [1]; + if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2) && imm != G_MININT16) { + int add_op = opcode == MINT_SUB_I4 ? MINT_ADD_I4_IMM : MINT_ADD_I8_IMM; + InterpInst *new_inst = interp_insert_ins (td, ins, add_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->data [0] = -imm; + interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } else if (opcode == MINT_MUL_I4_IMM || opcode == MINT_MUL_I8_IMM) { + int sreg = ins->sregs [0]; + InterpInst *def = td->locals [sreg].def; + if (def != NULL && td->local_ref_count [sreg] == 1) { + gboolean is_i4 = opcode == MINT_MUL_I4_IMM; + if ((is_i4 && def->opcode == MINT_ADD_I4_IMM) || + (!is_i4 && def->opcode == MINT_ADD_I8_IMM)) { + InterpInst *new_inst = interp_insert_ins (td, ins, is_i4 ? MINT_ADD_MUL_I4_IMM : MINT_ADD_MUL_I8_IMM); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = def->sregs [0]; + new_inst->data [0] = def->data [0]; + new_inst->data [1] = ins->data [0]; + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_BINOP_SHIFT (opcode)) { + gint16 imm; + int sreg_imm = ins->sregs [1]; + if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { + // ldc + sh -> sh.imm + int shift_op = MINT_SHR_UN_I4_IMM + (opcode - MINT_SHR_UN_I4); + InterpInst *new_inst = interp_insert_ins (td, ins, shift_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->data [0] = imm; + interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } else if (opcode == MINT_SHL_I4 || opcode == MINT_SHL_I8) { + int amount_var = ins->sregs [1]; + InterpInst *amount_def = td->locals [amount_var].def; + if (amount_def != NULL && td->local_ref_count [amount_var] == 1 && amount_def->opcode == MINT_AND_I4) { + int mask_var = amount_def->sregs [1]; + if (get_sreg_imm (td, mask_var, &imm, MINT_TYPE_I2)) { + // ldc + and + shl -> shl_and_imm + int new_opcode = -1; + if (opcode == MINT_SHL_I4 && imm == 31) + new_opcode = MINT_SHL_AND_I4; + else if (opcode == MINT_SHL_I8 && imm == 63) + new_opcode = MINT_SHL_AND_I8; + + if (new_opcode != -1) { + InterpInst *new_inst = interp_insert_ins (td, ins, new_opcode); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->sregs [1] = amount_def->sregs [0]; + + local_ref_count [amount_var]--; + local_ref_count [mask_var]--; + + interp_clear_ins (td->locals [mask_var].def); + interp_clear_ins (amount_def); + interp_clear_ins (ins); + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } + } + } else if (opcode == MINT_DIV_UN_I4 || opcode == MINT_DIV_UN_I8) { + // ldc + div.un -> shr.imm + int sreg_imm = ins->sregs [1]; + InterpInst *def = td->locals [sreg_imm].def; + if (def != NULL && td->local_ref_count [sreg_imm] == 1) { + int power2 = -1; + if (MINT_IS_LDC_I4 (def->opcode)) { + guint32 ct = interp_get_const_from_ldc_i4 (def); + power2 = mono_is_power_of_two ((guint32)ct); + } else if (MINT_IS_LDC_I8 (def->opcode)) { + guint64 ct = interp_get_const_from_ldc_i8 (def); + if (ct < G_MAXUINT32) + power2 = mono_is_power_of_two ((guint32)ct); + } + if (power2 > 0) { + InterpInst *new_inst; + if (opcode == MINT_DIV_UN_I4) + new_inst = interp_insert_ins (td, ins, MINT_SHR_UN_I4_IMM); + else + new_inst = interp_insert_ins (td, ins, MINT_SHR_UN_I8_IMM); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; + new_inst->data [0] = GINT_TO_UINT16 (power2); + + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("lower div.un: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_LDIND_INT (opcode)) { + int sreg_base = ins->sregs [0]; + InterpInst *def = td->locals [sreg_base].def; + if (def != NULL && td->local_ref_count [sreg_base] == 1) { + InterpInst *new_inst = NULL; + if (def->opcode == MINT_ADD_P) { + int ldind_offset_op = MINT_LDIND_OFFSET_I1 + (opcode - MINT_LDIND_I1); + new_inst = interp_insert_ins (td, ins, ldind_offset_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = def->sregs [0]; // base + new_inst->sregs [1] = def->sregs [1]; // off + } else if (def->opcode == MINT_ADD_P_IMM) { + int ldind_offset_imm_op = MINT_LDIND_OFFSET_IMM_I1 + (opcode - MINT_LDIND_I1); + new_inst = interp_insert_ins (td, ins, ldind_offset_imm_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = def->sregs [0]; // base + new_inst->data [0] = def->data [0]; // imm value + } + if (new_inst) { + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_base]--; + mono_interp_stats.super_instructions++; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_LDIND_OFFSET (opcode)) { + int sreg_off = ins->sregs [1]; + InterpInst *def = td->locals [sreg_off].def; + if (def != NULL && td->local_ref_count [sreg_off] == 1) { + if (def->opcode == MINT_MUL_P_IMM || def->opcode == MINT_ADD_P_IMM || def->opcode == MINT_ADD_MUL_P_IMM) { + int ldind_offset_op = MINT_LDIND_OFFSET_ADD_MUL_IMM_I1 + (opcode - MINT_LDIND_OFFSET_I1); + InterpInst *new_inst = interp_insert_ins (td, ins, ldind_offset_op); + new_inst->dreg = ins->dreg; + new_inst->sregs [0] = ins->sregs [0]; // base + new_inst->sregs [1] = def->sregs [0]; // off + + // set the add and mul immediates + switch (def->opcode) { + case MINT_ADD_P_IMM: + new_inst->data [0] = def->data [0]; + new_inst->data [1] = 1; + break; + case MINT_MUL_P_IMM: + new_inst->data [0] = 0; + new_inst->data [1] = def->data [0]; + break; + case MINT_ADD_MUL_P_IMM: + new_inst->data [0] = def->data [0]; + new_inst->data [1] = def->data [1]; + break; + } + + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_off]--; + mono_interp_stats.super_instructions++; + if (td->verbose_level) { + g_print ("method %s:%s, superins: ", m_class_get_name (td->method->klass), td->method->name); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_STIND_INT (opcode)) { + int sreg_base = ins->sregs [0]; + InterpInst *def = td->locals [sreg_base].def; + if (def != NULL && td->local_ref_count [sreg_base] == 1) { + InterpInst *new_inst = NULL; + if (def->opcode == MINT_ADD_P) { + int stind_offset_op = MINT_STIND_OFFSET_I1 + (opcode - MINT_STIND_I1); + new_inst = interp_insert_ins (td, ins, stind_offset_op); + new_inst->sregs [0] = def->sregs [0]; // base + new_inst->sregs [1] = def->sregs [1]; // off + new_inst->sregs [2] = ins->sregs [1]; // value + } else if (def->opcode == MINT_ADD_P_IMM) { + int stind_offset_imm_op = MINT_STIND_OFFSET_IMM_I1 + (opcode - MINT_STIND_I1); + new_inst = interp_insert_ins (td, ins, stind_offset_imm_op); + new_inst->sregs [0] = def->sregs [0]; // base + new_inst->sregs [1] = ins->sregs [1]; // value + new_inst->data [0] = def->data [0]; // imm value + } + if (new_inst) { + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_base]--; + mono_interp_stats.super_instructions++; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + } else if (MINT_IS_LDFLD (opcode)) { + // cknull + ldfld -> ldfld + // FIXME This optimization is very limited, it is meant mainly to remove cknull + // when inlining property accessors. We should have more advanced cknull removal + // optimzations, so we can catch cases where instructions are not next to each other. + int obj_sreg = ins->sregs [0]; + InterpInst *def = td->locals [obj_sreg].def; + if (def != NULL && def->opcode == MINT_CKNULL && interp_prev_ins (ins) == def && + def->dreg == obj_sreg && local_ref_count [obj_sreg] == 1) { + if (td->verbose_level) { + g_print ("remove redundant cknull (%s): ", td->method->name); + interp_dump_ins (def, td->data_items); + } + ins->sregs [0] = def->sregs [0]; + interp_clear_ins (def); + local_ref_count [obj_sreg]--; + mono_interp_stats.super_instructions++; + } + } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode) && interp_is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { + gint16 imm; + int sreg_imm = ins->sregs [1]; + if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { + int condbr_op = get_binop_condbr_imm_sp (opcode); + if (condbr_op != MINT_NOP) { + InterpInst *prev_ins = interp_prev_ins (ins); + // The new instruction does a safepoint + if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) + interp_clear_ins (prev_ins); + InterpInst *new_ins = interp_insert_ins (td, ins, condbr_op); + new_ins->sregs [0] = ins->sregs [0]; + new_ins->data [0] = imm; + new_ins->info.target_bb = ins->info.target_bb; + interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (ins); + local_ref_count [sreg_imm]--; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_ins, td->data_items); + } + } + } else { + InterpInst *prev_ins = interp_prev_ins (ins); + if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) { + int condbr_op = get_binop_condbr_sp (opcode); + if (condbr_op != MINT_NOP) { + interp_clear_ins (prev_ins); + ins->opcode = GINT_TO_OPCODE (condbr_op); + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (ins, td->data_items); + } + } + } + } + } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode) && interp_is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { + if (opcode == MINT_BRFALSE_I4 || opcode == MINT_BRTRUE_I4) { + gboolean negate = opcode == MINT_BRFALSE_I4; + int cond_sreg = ins->sregs [0]; + InterpInst *def = td->locals [cond_sreg].def; + if (def != NULL && local_ref_count [cond_sreg] == 1) { + int replace_opcode = -1; + switch (def->opcode) { + case MINT_CEQ_I4: replace_opcode = negate ? MINT_BNE_UN_I4 : MINT_BEQ_I4; break; + case MINT_CEQ_I8: replace_opcode = negate ? MINT_BNE_UN_I8 : MINT_BEQ_I8; break; + case MINT_CGT_I4: replace_opcode = negate ? MINT_BLE_I4 : MINT_BGT_I4; break; + case MINT_CGT_I8: replace_opcode = negate ? MINT_BLE_I8 : MINT_BGT_I8; break; + case MINT_CLT_I4: replace_opcode = negate ? MINT_BGE_I4 : MINT_BLT_I4; break; + case MINT_CLT_I8: replace_opcode = negate ? MINT_BGE_I8 : MINT_BLT_I8; break; + case MINT_CGT_UN_I4: replace_opcode = negate ? MINT_BLE_UN_I4 : MINT_BGT_UN_I4; break; + case MINT_CGT_UN_I8: replace_opcode = negate ? MINT_BLE_UN_I8 : MINT_BGT_UN_I8; break; + case MINT_CLT_UN_I4: replace_opcode = negate ? MINT_BGE_UN_I4 : MINT_BLT_UN_I4; break; + case MINT_CLT_UN_I8: replace_opcode = negate ? MINT_BGE_UN_I8 : MINT_BLT_UN_I8; break; + case MINT_CEQ_R4: replace_opcode = negate ? MINT_BNE_UN_R4 : MINT_BEQ_R4; break; + case MINT_CEQ_R8: replace_opcode = negate ? MINT_BNE_UN_R8 : MINT_BEQ_R8; break; + case MINT_CGT_R4: replace_opcode = negate ? MINT_BLE_UN_R4 : MINT_BGT_R4; break; + case MINT_CGT_R8: replace_opcode = negate ? MINT_BLE_UN_R8 : MINT_BGT_R8; break; + case MINT_CLT_R4: replace_opcode = negate ? MINT_BGE_UN_R4 : MINT_BLT_R4; break; + case MINT_CLT_R8: replace_opcode = negate ? MINT_BGE_UN_R8 : MINT_BLT_R8; break; + case MINT_CGT_UN_R4: replace_opcode = negate ? MINT_BLE_R4 : MINT_BGT_UN_R4; break; + case MINT_CGT_UN_R8: replace_opcode = negate ? MINT_BLE_R8 : MINT_BGT_UN_R8; break; + case MINT_CLT_UN_R4: replace_opcode = negate ? MINT_BGE_R4 : MINT_BLT_UN_R4; break; + case MINT_CLT_UN_R8: replace_opcode = negate ? MINT_BGE_R8 : MINT_BLT_UN_R8; break; + case MINT_CEQ0_I4: replace_opcode = negate ? MINT_BRTRUE_I4 : MINT_BRFALSE_I4; break; // If def->opcode is MINT_CEQ0_I4 ins->opcode is inverted + // Add more opcodes + default: + break; + } + if (replace_opcode != -1) { + ins->opcode = GINT_TO_UINT16 (replace_opcode); + ins->sregs [0] = def->sregs [0]; + if (def->opcode != MINT_CEQ0_I4) + ins->sregs [1] = def->sregs [1]; + interp_clear_ins (def); + local_ref_count [cond_sreg]--; + mono_interp_stats.super_instructions++; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (ins, td->data_items); + } + // The newly added opcode could be part of further superinstructions. Retry + ins = ins->prev; + continue; + } + } + } + InterpInst *prev_ins = interp_prev_ins (ins); + if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) { + int condbr_op = get_unop_condbr_sp (opcode); + if (condbr_op != MINT_NOP) { + interp_clear_ins (prev_ins); + ins->opcode = GINT_TO_OPCODE (condbr_op); + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (ins, td->data_items); + } + } + } + } else if (opcode == MINT_STOBJ_VT_NOREF) { + int sreg_src = ins->sregs [1]; + InterpInst *def = td->locals [sreg_src].def; + if (def != NULL && interp_prev_ins (ins) == def && def->opcode == MINT_LDOBJ_VT && ins->data [0] == def->data [0] && td->local_ref_count [sreg_src] == 1) { + InterpInst *new_inst = interp_insert_ins (td, ins, MINT_CPOBJ_VT_NOREF); + new_inst->sregs [0] = ins->sregs [0]; // dst + new_inst->sregs [1] = def->sregs [0]; // src + new_inst->data [0] = ins->data [0]; // size + + interp_clear_ins (def); + interp_clear_ins (ins); + local_ref_count [sreg_src]--; + mono_interp_stats.super_instructions++; + if (td->verbose_level) { + g_print ("superins: "); + interp_dump_ins (new_inst, td->data_items); + } + } + } + noe += interp_get_ins_length (ins); + } + } +} + +void +interp_optimize_code (TransformData *td) +{ + if (mono_interp_opt & INTERP_OPT_BBLOCKS) + interp_optimize_bblocks (td); + + if (mono_interp_opt & INTERP_OPT_CPROP) + MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td)); + + // After this point control optimizations on control flow can no longer happen, so we can determine + // which vars are global. This helps speed up the super instructions pass, which only operates on + // single def, single use local vars. + initialize_global_vars (td); + + if ((mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) && + (mono_interp_opt & INTERP_OPT_CPROP)) + MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td)); +} + diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 464ddebd676132..d7983ef88c106a 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -94,7 +94,7 @@ has_doesnotreturn_attribute (MonoMethod *method) return result; } -static InterpInst* +InterpInst* interp_new_ins (TransformData *td, int opcode, int len) { InterpInst *new_inst; @@ -127,7 +127,7 @@ interp_add_ins (TransformData *td, int opcode) return interp_add_ins_explicit (td, opcode, mono_interp_oplen [opcode]); } -static InterpInst* +InterpInst* interp_insert_ins_bb (TransformData *td, InterpBasicBlock *bb, InterpInst *prev_ins, int opcode) { InterpInst *new_inst = interp_new_ins (td, opcode, mono_interp_oplen [opcode]); @@ -152,13 +152,13 @@ interp_insert_ins_bb (TransformData *td, InterpBasicBlock *bb, InterpInst *prev_ } /* Inserts a new instruction after prev_ins. prev_ins must be in cbb */ -static InterpInst* +InterpInst* interp_insert_ins (TransformData *td, InterpInst *prev_ins, int opcode) { return interp_insert_ins_bb (td, td->cbb, prev_ins, opcode); } -static void +void interp_clear_ins (InterpInst *ins) { // Clearing instead of removing from the list makes everything easier. @@ -168,13 +168,13 @@ interp_clear_ins (InterpInst *ins) ins->opcode = MINT_NOP; } -static gboolean +gboolean interp_ins_is_nop (InterpInst *ins) { return ins->opcode == MINT_NOP || ins->opcode == MINT_IL_SEQ_POINT; } -static InterpInst* +InterpInst* interp_prev_ins (InterpInst *ins) { ins = ins->prev; @@ -183,7 +183,7 @@ interp_prev_ins (InterpInst *ins) return ins; } -static InterpInst* +InterpInst* interp_next_ins (InterpInst *ins) { ins = ins->next; @@ -203,26 +203,6 @@ check_stack_helper (TransformData *td, int n) return TRUE; } -static InterpInst* -interp_first_ins (InterpBasicBlock *bb) -{ - InterpInst *ins = bb->first_ins; - if (!ins || !interp_ins_is_nop (ins)) - return ins; - while (ins && interp_ins_is_nop (ins)) - ins = ins->next; - return ins; -} - -static InterpInst* -interp_last_ins (InterpBasicBlock *bb) -{ - InterpInst *ins = bb->last_ins; - if (!ins || !interp_ins_is_nop (ins)) - return ins; - return interp_prev_ins (ins); -} - #define CHECK_STACK(td, n) \ do { \ if (!check_stack_helper (td, n)) \ @@ -376,7 +356,7 @@ mono_mint_type (MonoType *type) * way, with an offset relative to the frame->locals. */ static int -create_interp_local_explicit (TransformData *td, MonoType *type, int size) +interp_create_local_explicit (TransformData *td, MonoType *type, int size) { if (td->locals_size == td->locals_capacity) { td->locals_capacity *= 2; @@ -405,10 +385,10 @@ create_interp_local_explicit (TransformData *td, MonoType *type, int size) } static void -create_interp_dummy_var (TransformData *td) +interp_create_dummy_var (TransformData *td) { g_assert (td->dummy_var < 0); - td->dummy_var = create_interp_local_explicit (td, m_class_get_byval_arg (mono_defaults.void_class), 8); + td->dummy_var = interp_create_local_explicit (td, m_class_get_byval_arg (mono_defaults.void_class), 8); td->locals [td->dummy_var].offset = 0; td->locals [td->dummy_var].flags = INTERP_LOCAL_FLAG_GLOBAL; } @@ -424,9 +404,9 @@ get_tos_offset (TransformData *td) // Create a local for sp static void -create_interp_stack_local (TransformData *td, StackInfo *sp, int type_size) +interp_create_stack_local (TransformData *td, StackInfo *sp, int type_size) { - int local = create_interp_local_explicit (td, get_type_from_stack (sp->type, sp->klass), type_size); + int local = interp_create_local_explicit (td, get_type_from_stack (sp->type, sp->klass), type_size); td->locals [local].flags |= INTERP_LOCAL_FLAG_EXECUTION_STACK; sp->local = local; @@ -452,7 +432,7 @@ push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) sp->klass = k; sp->flags = 0; sp->size = ALIGN_TO (type_size, MINT_STACK_SLOT_SIZE); - create_interp_stack_local (td, sp, type_size); + interp_create_stack_local (td, sp, type_size); if (!td->optimized) { sp->offset = get_tos_offset (td); if (td->locals [sp->local].flags & INTERP_LOCAL_FLAG_SIMD) @@ -502,7 +482,7 @@ static void set_type_and_local (TransformData *td, StackInfo *sp, int type, MonoClass *klass) { SET_TYPE (sp, type, klass); - create_interp_stack_local (td, sp, MINT_STACK_SLOT_SIZE); + interp_create_stack_local (td, sp, MINT_STACK_SLOT_SIZE); if (!td->optimized) td->locals [sp->local].stack_offset = sp->offset; } @@ -539,168 +519,8 @@ push_types (TransformData *td, StackInfo *types, int count) push_type_explicit (td, types [i].type, types [i].klass, types [i].size); } -static void -mark_bb_as_dead (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *replace_bb) -{ - // Update IL offset to bb mapping so that offset_to_bb doesn't point to dead - // bblocks. This mapping can still be needed when computing clause ranges. Since - // multiple IL offsets can end up pointing to same bblock after optimizations, - // make sure we update mapping for all of them - // - // To avoid scanning the entire offset_to_bb array, we scan only in the vicinity - // of the IL offset of bb. We can stop search when we encounter a different bblock. - g_assert (bb->il_offset >= 0); - for (int il_offset = bb->il_offset; il_offset >= 0; il_offset--) { - if (td->offset_to_bb [il_offset] == bb) - td->offset_to_bb [il_offset] = replace_bb; - else if (td->offset_to_bb [il_offset]) - break; - } - for (guint32 il_offset = bb->il_offset + 1; il_offset < td->header->code_size; il_offset++) { - if (td->offset_to_bb [il_offset] == bb) - td->offset_to_bb [il_offset] = replace_bb; - else if (td->offset_to_bb [il_offset]) - break; - } - - bb->dead = TRUE; - // bb should never be used/referenced after this -} - -/* Merges two consecutive bbs (in code order) into a single one */ -static void -interp_merge_bblocks (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *bbadd) -{ - g_assert (bbadd->in_count == 1 && bbadd->in_bb [0] == bb); - g_assert (bb->next_bb == bbadd); - - // Remove the branch instruction to the invalid bblock - if (bb->last_ins) { - InterpInst *last_ins = (bb->last_ins->opcode != MINT_NOP) ? bb->last_ins : interp_prev_ins (bb->last_ins); - if (last_ins) { - if (last_ins->opcode == MINT_BR) { - g_assert (last_ins->info.target_bb == bbadd); - interp_clear_ins (last_ins); - } else if (last_ins->opcode == MINT_SWITCH) { - // Weird corner case where empty switch can branch by default to next instruction - last_ins->opcode = MINT_NOP; - } - } - } - - // Append all instructions from bbadd to bb - if (bb->last_ins) { - if (bbadd->first_ins) { - bb->last_ins->next = bbadd->first_ins; - bbadd->first_ins->prev = bb->last_ins; - bb->last_ins = bbadd->last_ins; - } - } else { - bb->first_ins = bbadd->first_ins; - bb->last_ins = bbadd->last_ins; - } - bb->next_bb = bbadd->next_bb; - - // Fixup bb links - bb->out_count = bbadd->out_count; - bb->out_bb = bbadd->out_bb; - for (int i = 0; i < bbadd->out_count; i++) { - for (int j = 0; j < bbadd->out_bb [i]->in_count; j++) { - if (bbadd->out_bb [i]->in_bb [j] == bbadd) - bbadd->out_bb [i]->in_bb [j] = bb; - } - } - - mark_bb_as_dead (td, bbadd, bb); -} - -// array must contain ref -static void -remove_bblock_ref (InterpBasicBlock **array, InterpBasicBlock *ref, int len) -{ - int i = 0; - while (array [i] != ref) - i++; - i++; - while (i < len) { - array [i - 1] = array [i]; - i++; - } -} - -static void -interp_unlink_bblocks (InterpBasicBlock *from, InterpBasicBlock *to) -{ - remove_bblock_ref (from->out_bb, to, from->out_count); - from->out_count--; - remove_bblock_ref (to->in_bb, from, to->in_count); - to->in_count--; -} - -static gboolean -interp_remove_bblock (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock *prev_bb) -{ - gboolean needs_cprop = FALSE; - - for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { - if (ins->opcode == MINT_LDLOCA_S) { - td->locals [ins->sregs [0]].indirects--; - if (!td->locals [ins->sregs [0]].indirects) { - // We can do cprop now through this local. Run cprop again. - needs_cprop = TRUE; - } - } - } - while (bb->in_count) - interp_unlink_bblocks (bb->in_bb [0], bb); - while (bb->out_count) - interp_unlink_bblocks (bb, bb->out_bb [0]); - prev_bb->next_bb = bb->next_bb; - mark_bb_as_dead (td, bb, bb->next_bb); - - return needs_cprop; -} - -static void -interp_link_bblocks (TransformData *td, InterpBasicBlock *from, InterpBasicBlock *to) -{ - int i; - gboolean found = FALSE; - - for (i = 0; i < from->out_count; ++i) { - if (to == from->out_bb [i]) { - found = TRUE; - break; - } - } - if (!found) { - InterpBasicBlock **newa = (InterpBasicBlock**)mono_mempool_alloc (td->mempool, sizeof (InterpBasicBlock*) * (from->out_count + 1)); - for (i = 0; i < from->out_count; ++i) - newa [i] = from->out_bb [i]; - newa [i] = to; - from->out_count++; - from->out_bb = newa; - } - - found = FALSE; - for (i = 0; i < to->in_count; ++i) { - if (from == to->in_bb [i]) { - found = TRUE; - break; - } - } - if (!found) { - InterpBasicBlock **newa = (InterpBasicBlock**)mono_mempool_alloc (td->mempool, sizeof (InterpBasicBlock*) * (to->in_count + 1)); - for (i = 0; i < to->in_count; ++i) - newa [i] = to->in_bb [i]; - newa [i] = from; - to->in_count++; - to->in_bb = newa; - } -} - -static int -get_mov_for_type (int mt, gboolean needs_sext) +int +interp_get_mov_for_type (int mt, gboolean needs_sext) { switch (mt) { case MINT_TYPE_I1: @@ -772,10 +592,10 @@ fixup_newbb_stack_locals (TransformData *td, InterpBasicBlock *newbb) int dloc = newbb->stack_state [i].local; if (sloc != dloc) { int mt = td->locals [sloc].mt; - int mov_op = get_mov_for_type (mt, FALSE); + int mov_op = interp_get_mov_for_type (mt, FALSE); // FIXME can be hit in some IL cases. Should we merge the stack states ? (b41002.il) - // g_assert (mov_op == get_mov_for_type (td->locals [dloc].mt, FALSE)); + // g_assert (mov_op == interp_get_mov_for_type (td->locals [dloc].mt, FALSE)); interp_add_ins (td, mov_op); interp_ins_set_sreg (td->last_ins, td->stack [i].local); @@ -1057,7 +877,7 @@ load_arg(TransformData *td, int n) } push_type (td, stack_type [mt], klass); } - interp_add_ins (td, get_mov_for_type (mt, TRUE)); + interp_add_ins (td, interp_get_mov_for_type (mt, TRUE)); interp_ins_set_sreg (td->last_ins, n); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); if (mt == MINT_TYPE_VT) @@ -1083,7 +903,7 @@ store_arg(TransformData *td, int n) g_assert (size < G_MAXUINT16); } --td->sp; - interp_add_ins (td, get_mov_for_type (mt, FALSE)); + interp_add_ins (td, interp_get_mov_for_type (mt, FALSE)); interp_ins_set_sreg (td->last_ins, td->sp [0].local); interp_ins_set_dreg (td->last_ins, n); if (mt == MINT_TYPE_VT) @@ -1106,7 +926,7 @@ load_local (TransformData *td, int local) klass = mono_class_from_mono_type_internal (type); push_type (td, stack_type [mt], klass); } - interp_add_ins (td, get_mov_for_type (mt, TRUE)); + interp_add_ins (td, interp_get_mov_for_type (mt, TRUE)); interp_ins_set_sreg (td->last_ins, local); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); if (mt == MINT_TYPE_VT) @@ -1136,7 +956,7 @@ store_local (TransformData *td, int local) stack_type [mt], td->sp [-1].type); } --td->sp; - interp_add_ins (td, get_mov_for_type (mt, FALSE)); + interp_add_ins (td, interp_get_mov_for_type (mt, FALSE)); interp_ins_set_sreg (td->last_ins, td->sp [0].local); interp_ins_set_dreg (td->last_ins, local); if (mt == MINT_TYPE_VT) @@ -1349,40 +1169,15 @@ interp_generate_ipe_bad_fallthru (TransformData *td) } -static int -create_interp_local (TransformData *td, MonoType *type) +int +interp_create_local (TransformData *td, MonoType *type) { int size, align; size = mono_type_size (type, &align); g_assert (align <= MINT_STACK_SLOT_SIZE); - return create_interp_local_explicit (td, type, size); -} - -// Allocates var at the offset that tos points to, also updating it. -static int -alloc_var_offset (TransformData *td, int local, gint32 *ptos) -{ - int size, offset; - - offset = *ptos; - size = td->locals [local].size; - - if (td->locals [local].flags & INTERP_LOCAL_FLAG_SIMD) - offset = ALIGN_TO (offset, MINT_SIMD_ALIGNMENT); - - td->locals [local].offset = offset; - - *ptos = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); - - return td->locals [local].offset; -} - -static int -alloc_global_var_offset (TransformData *td, int var) -{ - return alloc_var_offset (td, var, &td->total_locals_size); + return interp_create_local_explicit (td, type, size); } /* @@ -1392,7 +1187,7 @@ alloc_global_var_offset (TransformData *td, int var) * ip is the address where the arguments of the instruction are located */ static char* -dump_interp_ins_data (InterpInst *ins, gint32 ins_offset, const guint16 *data, int opcode, gpointer *data_items) +interp_dump_ins_data (InterpInst *ins, gint32 ins_offset, const guint16 *data, int opcode, gpointer *data_items) { GString *str = g_string_new (""); int target; @@ -1509,7 +1304,7 @@ dump_interp_ins_data (InterpInst *ins, gint32 ins_offset, const guint16 *data, i } static void -dump_interp_compacted_ins (const guint16 *ip, const guint16 *start, gpointer *data_items) +interp_dump_compacted_ins (const guint16 *ip, const guint16 *start, gpointer *data_items) { int opcode = *ip; int ins_offset = GPTRDIFF_TO_INT (ip - start); @@ -1530,24 +1325,24 @@ dump_interp_compacted_ins (const guint16 *ip, const guint16 *start, gpointer *da } else { g_string_append_printf (str, " nil],"); } - char *ins_data = dump_interp_ins_data (NULL, ins_offset, ip, opcode, data_items); + char *ins_data = interp_dump_ins_data (NULL, ins_offset, ip, opcode, data_items); g_print ("%s%s\n", str->str, ins_data); g_string_free (str, TRUE); g_free (ins_data); } static void -dump_interp_code (const guint16 *start, const guint16* end, gpointer *data_items) +interp_dump_code (const guint16 *start, const guint16* end, gpointer *data_items) { const guint16 *p = start; while (p < end) { - dump_interp_compacted_ins (p, start, data_items); + interp_dump_compacted_ins (p, start, data_items); p = mono_interp_dis_mintop_len (p); } } -static void -dump_interp_inst (InterpInst *ins, gpointer *data_items) +void +interp_dump_ins (InterpInst *ins, gpointer *data_items) { int opcode = ins->opcode; GString *str = g_string_new (""); @@ -1582,7 +1377,7 @@ dump_interp_inst (InterpInst *ins, gpointer *data_items) // LDLOCA has special semantics, it has data in sregs [0], but it doesn't have any sregs g_string_append_printf (str, " %d", ins->sregs [0]); } else { - char *descr = dump_interp_ins_data (ins, ins->il_offset, &ins->data [0], ins->opcode, data_items); + char *descr = interp_dump_ins_data (ins, ins->il_offset, &ins->data [0], ins->opcode, data_items); g_string_append_printf (str, "%s", descr); g_free (descr); } @@ -1590,40 +1385,14 @@ dump_interp_inst (InterpInst *ins, gpointer *data_items) g_string_free (str, TRUE); } -static GString* -get_interp_bb_links (InterpBasicBlock *bb) -{ - GString *str = g_string_new (""); - - if (bb->in_count) { - g_string_append_printf (str, "IN (%d", bb->in_bb [0]->index); - for (int i = 1; i < bb->in_count; i++) - g_string_append_printf (str, " %d", bb->in_bb [i]->index); - g_string_append_printf (str, "), "); - } else { - g_string_append_printf (str, "IN (nil), "); - } - - if (bb->out_count) { - g_string_append_printf (str, "OUT (%d", bb->out_bb [0]->index); - for (int i = 1; i < bb->out_count; i++) - g_string_append_printf (str, " %d", bb->out_bb [i]->index); - g_string_append_printf (str, ")"); - } else { - g_string_append_printf (str, "OUT (nil)"); - } - - return str; -} - static void -dump_interp_bb (InterpBasicBlock *bb, gpointer *data_items) +interp_dump_bb (InterpBasicBlock *bb, gpointer *data_items) { g_print ("BB%d:\n", bb->index); for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { // Avoid some noise if (ins->opcode != MINT_NOP && ins->opcode != MINT_IL_SEQ_POINT) - dump_interp_inst (ins, data_items); + interp_dump_ins (ins, data_items); } } @@ -1643,7 +1412,7 @@ mono_interp_print_code (InterpMethod *imethod) g_free (name); start = (guint8*) jinfo->code_start; - dump_interp_code ((const guint16*)start, (const guint16*)(start + jinfo->code_size), imethod->data_items); + interp_dump_code ((const guint16*)start, (const guint16*)(start + jinfo->code_size), imethod->data_items); } /* For debug use */ @@ -1652,7 +1421,7 @@ mono_interp_print_td_code (TransformData *td) { g_print ("Unoptimized IR:\n"); for (InterpBasicBlock *bb = td->entry_bb; bb != NULL; bb = bb->next_bb) - dump_interp_bb (bb, td->data_items); + interp_dump_bb (bb, td->data_items); } @@ -1683,7 +1452,7 @@ interp_ins_is_ldc (InterpInst *ins) return ins->opcode >= MINT_LDC_I4_M1 && ins->opcode <= MINT_LDC_I8; } -static gint32 +gint32 interp_get_const_from_ldc_i4 (InterpInst *ins) { switch (ins->opcode) { @@ -1704,20 +1473,8 @@ interp_get_const_from_ldc_i4 (InterpInst *ins) } } -static gint64 -interp_get_const_from_ldc_i8 (InterpInst *ins) -{ - switch (ins->opcode) { - case MINT_LDC_I8_0: return 0; - case MINT_LDC_I8_S: return (gint64)(gint16)ins->data [0]; - case MINT_LDC_I8: return READ64 (&ins->data [0]); - default: - g_assert_not_reached (); - } -} - /* If ins is not null, it will replace it with the ldc */ -static InterpInst* +InterpInst* interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct, int dreg) { guint16 opcode; @@ -1763,24 +1520,6 @@ interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct, int return ins; } -static InterpInst* -interp_inst_replace_with_i8_const (TransformData *td, InterpInst *ins, gint64 ct) -{ - int size = mono_interp_oplen [ins->opcode]; - int dreg = ins->dreg; - - if (size < 5) { - ins = interp_insert_ins (td, ins, MINT_LDC_I8); - interp_clear_ins (ins->prev); - } else { - ins->opcode = MINT_LDC_I8; - } - WRITE64_INS (ins, 0, &ct); - ins->dreg = dreg; - - return ins; -} - static int interp_get_ldind_for_mt (int mt) { @@ -1800,24 +1539,6 @@ interp_get_ldind_for_mt (int mt) return -1; } -static int -interp_get_mt_for_ldind (int ldind_op) -{ - switch (ldind_op) { - case MINT_LDIND_I1: return MINT_TYPE_I1; - case MINT_LDIND_U1: return MINT_TYPE_U1; - case MINT_LDIND_I2: return MINT_TYPE_I2; - case MINT_LDIND_U2: return MINT_TYPE_U2; - case MINT_LDIND_I4: return MINT_TYPE_I4; - case MINT_LDIND_I8: return MINT_TYPE_I8; - case MINT_LDIND_R4: return MINT_TYPE_R4; - case MINT_LDIND_R8: return MINT_TYPE_R8; - default: - g_assert_not_reached (); - } - return -1; -} - static int interp_get_stind_for_mt (int mt) { @@ -2019,7 +1740,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas // the code is unsafe (like in some wrappers). In that case we assume the type // of the array and don't do any checks. - int local = create_interp_local (td, local_type); + int local = interp_create_local (td, local_type); store_local (td, local); interp_emit_ldelema (td, target_method->klass, value_class); @@ -2478,8 +2199,8 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas base_klass = mono_class_from_mono_type_internal (base_type); // Remove the boxing of valuetypes, by replacing them with moves - prev_prev_ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mono_mint_type (base_type), FALSE)); - td->last_ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mono_mint_type (base_type), FALSE)); + prev_prev_ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mono_mint_type (base_type), FALSE)); + td->last_ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mono_mint_type (base_type), FALSE)); intrinsify = TRUE; } else if (td->last_ins && td->last_ins->opcode == MINT_BOX && @@ -2493,7 +2214,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas int mt = mono_mint_type (m_class_get_byval_arg (base_klass)); // Remove boxing and load the value of this - td->last_ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mt, FALSE)); + td->last_ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, FALSE)); InterpInst *ins = interp_insert_ins (td, prev_prev_ins, interp_get_ldind_for_mt (mt)); interp_ins_set_sreg (ins, td->sp [-2].local); interp_ins_set_dreg (ins, td->sp [-2].local); @@ -2547,8 +2268,8 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas gboolean is_compareto = strcmp (tm, "EnumCompareTo") == 0; if (is_compareto) { int locala, localb; - locala = create_interp_local (td, t); - localb = create_interp_local (td, t); + locala = interp_create_local (td, t); + localb = interp_create_local (td, t); // Save arguments store_local (td, localb); @@ -3126,7 +2847,7 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi else vtsize = MINT_STACK_SLOT_SIZE; - dreg = create_interp_local (td, get_type_from_stack (stack_type [ret_mt], klass)); + dreg = interp_create_local (td, get_type_from_stack (stack_type [ret_mt], klass)); // For valuetypes, we need to control the lifetime of the valuetype. // MINT_NEWOBJ_VT_INLINED takes the address of this reg and we should keep @@ -3134,7 +2855,7 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi interp_add_ins (td, MINT_DEF); interp_ins_set_dreg (td->last_ins, dreg); } else { - dreg = create_interp_local (td, get_type_from_stack (stack_type [ret_mt], klass)); + dreg = interp_create_local (td, get_type_from_stack (stack_type [ret_mt], klass)); } // Allocate `this` pointer @@ -3427,11 +3148,11 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target *start = sp_fp; } else { int *arg_locals = mono_mempool_alloc0 (td->mempool, sizeof (int) * csignature->param_count); - int fp_local = create_interp_local (td, m_class_get_byval_arg (mono_defaults.int_class)); + int fp_local = interp_create_local (td, m_class_get_byval_arg (mono_defaults.int_class)); // Pop everything into locals. Push after into correct order store_local (td, fp_local); for (int i = csignature->param_count - 1; i >= 0; i--) { - arg_locals [i] = create_interp_local (td, csignature->params [i]); + arg_locals [i] = interp_create_local (td, csignature->params [i]); store_local (td, arg_locals [i]); } load_local (td, fp_local); @@ -3958,8 +3679,8 @@ interp_field_from_token (MonoMethod *method, guint32 token, MonoClass **klass, M return field; } -static InterpBasicBlock* -alloc_bb (TransformData *td) +InterpBasicBlock* +interp_alloc_bb (TransformData *td) { InterpBasicBlock *bb = (InterpBasicBlock*)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock)); bb->il_offset = -1; @@ -3977,7 +3698,7 @@ get_bb (TransformData *td, unsigned char *ip, gboolean make_list) InterpBasicBlock *bb = td->offset_to_bb [offset]; if (!bb) { - bb = alloc_bb (td); + bb = interp_alloc_bb (td); bb->il_offset = offset; td->offset_to_bb [offset] = bb; @@ -4436,9 +4157,9 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet imethod->clause_data_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32)); td->clause_vars = (int*)mono_mempool_alloc (td->mempool, sizeof (int) * header->num_clauses); for (guint i = 0; i < header->num_clauses; i++) { - int var = create_interp_local (td, mono_get_object_type ()); + int var = interp_create_local (td, mono_get_object_type ()); td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; - alloc_global_var_offset (td, var); + interp_alloc_global_var_offset (td, var); imethod->clause_data_offsets [i] = td->locals [var].offset; td->clause_vars [i] = var; } @@ -4953,7 +4674,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // first instruction in a vararg method needs to copy the variable arguments // into a special region so they can be accessed by MINT_ARGLIST. This region // is localloc'ed so we have compile time static offsets for all locals/stack. - arglist_local = create_interp_local (td, m_class_get_byval_arg (mono_defaults.int_class)); + arglist_local = interp_create_local (td, m_class_get_byval_arg (mono_defaults.int_class)); interp_add_ins (td, MINT_INIT_ARGLIST); interp_ins_set_dreg (td->last_ins, arglist_local); // This is the offset where the variable args are on stack. After this instruction @@ -5006,7 +4727,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, /* Allocate locals to store inlined method args from stack */ for (int i = signature->param_count - 1; i >= 0; i--) { MonoType *type = td->locals [td->sp [-1].local].type; - local = create_interp_local (td, type); + local = interp_create_local (td, type); arg_locals [i + !!signature->hasthis] = local; store_local (td, local); } @@ -5017,7 +4738,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, * Valuetype this local gets integer type MINT_TYPE_I. */ MonoType *type = td->locals [td->sp [-1].local].type; - local = create_interp_local (td, type); + local = interp_create_local (td, type); arg_locals [0] = local; store_local (td, local); } @@ -5025,7 +4746,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, local_locals = (guint32*) g_malloc (header->num_locals * sizeof (guint32)); /* Allocate locals to store inlined method args from stack */ for (int i = 0; i < header->num_locals; i++) - local_locals [i] = create_interp_local (td, header->locals [i]); + local_locals [i] = interp_create_local (td, header->locals [i]); } td->dont_inline = g_list_prepend (td->dont_inline, method); @@ -5362,7 +5083,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_ins_set_dreg (td->last_ins, td->sp [-1].local); td->last_ins->data [0] = GINT32_TO_UINT16 (size); } else { - interp_add_ins (td, get_mov_for_type (mt, FALSE)); + interp_add_ins (td, interp_get_mov_for_type (mt, FALSE)); interp_ins_set_sreg (td->last_ins, td->sp [-1].local); push_type (td, type, klass); interp_ins_set_dreg (td->last_ins, td->sp [-1].local); @@ -6269,7 +5990,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->sp -= csignature->param_count; // First arg is dummy var, it is null when passed to the ctor - call_args [0] = create_interp_local (td, get_type_from_stack (stack_type [ret_mt], NULL)); + call_args [0] = interp_create_local (td, get_type_from_stack (stack_type [ret_mt], NULL)); for (int i = 0; i < csignature->param_count; i++) { call_args [i + 1] = td->sp [i].local; } @@ -6501,7 +6222,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type * We create a local variable in the frame so that we can fetch its address. */ - int local = create_interp_local (td, m_class_get_byval_arg (klass)); + int local = interp_create_local (td, m_class_get_byval_arg (klass)); store_local (td, local); interp_add_ins (td, MINT_LDLOCA_S); @@ -6740,7 +6461,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, g_assert (!m_type_is_byref (ftype)); MonoClass *field_class = mono_class_from_mono_type_internal (ftype); MonoType *local_type = m_class_get_byval_arg (field_class); - int local = create_interp_local (td, local_type); + int local = interp_create_local (td, local_type); store_local (td, local); interp_emit_metadata_update_ldflda (td, field, error); goto_if_nok (error, exit); @@ -7692,7 +7413,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else size = mono_class_value_size (klass, NULL); - int local = create_interp_local_explicit (td, m_class_get_byval_arg (klass), size); + int local = interp_create_local_explicit (td, m_class_get_byval_arg (klass), size); interp_add_ins (td, MINT_MOV_VT); td->sp--; interp_ins_set_sreg (td->last_ins, td->sp [0].local); @@ -8347,8 +8068,8 @@ alloc_unopt_global_local (TransformData *td, int local, gpointer data) td->total_locals_size = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); } -static int -get_inst_length (InterpInst *ins) +int +interp_get_ins_length (InterpInst *ins) { if (ins->opcode == MINT_SWITCH) return MINT_SWITCH_LEN (READ32 (&ins->data [0])); @@ -8360,8 +8081,8 @@ get_inst_length (InterpInst *ins) return mono_interp_oplen [ins->opcode]; } -static void -foreach_local_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int, gpointer)) +void +interp_foreach_local_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int, gpointer)) { int opcode = ins->opcode; if (mono_interp_op_sregs [opcode]) { @@ -8388,8 +8109,8 @@ foreach_local_var (TransformData *td, InterpInst *ins, gpointer data, void (*cal callback (td, ins->dreg, data); } -static int -compute_native_offset_estimates (TransformData *td) +int +interp_compute_native_offset_estimates (TransformData *td) { InterpBasicBlock *bb; int noe = 0; @@ -8404,9 +8125,9 @@ compute_native_offset_estimates (TransformData *td) // Skip dummy opcodes for more precise offset computation if (MINT_IS_NOP (opcode)) continue; - noe += get_inst_length (ins); + noe += interp_get_ins_length (ins); if (!td->optimized) - foreach_local_var (td, ins, NULL, alloc_unopt_global_local); + interp_foreach_local_var (td, ins, NULL, alloc_unopt_global_local); } } @@ -8417,8 +8138,8 @@ compute_native_offset_estimates (TransformData *td) return noe; } -static gboolean -is_short_offset (int src_offset, int dest_offset) +gboolean +interp_is_short_offset (int src_offset, int dest_offset) { int diff = dest_offset - src_offset; if (diff >= G_MININT16 && diff <= G_MAXINT16) @@ -8531,7 +8252,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in if (ins->info.target_bb->native_offset >= 0) { int offset = ins->info.target_bb->native_offset - br_offset; // Backwards branch. We can already patch it. - if (is_short_offset (br_offset, ins->info.target_bb->native_offset)) { + if (interp_is_short_offset (br_offset, ins->info.target_bb->native_offset)) { // Replace the long opcode we added at the start *start_ip = GINT_TO_OPCODE (get_short_brop (opcode)); *ip++ = GINT_TO_UINT16 (ins->info.target_bb->native_offset - br_offset); @@ -8543,7 +8264,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in ip--; } else { // If the estimate offset is short, then surely the real offset is short - gboolean is_short = is_short_offset (br_offset, ins->info.target_bb->native_offset_estimate); + gboolean is_short = interp_is_short_offset (br_offset, ins->info.target_bb->native_offset_estimate); if (is_short) *start_ip = GINT_TO_OPCODE (get_short_brop (opcode)); @@ -8604,7 +8325,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in } else { if (opcode == MINT_MOV_SRC_OFF) { // Loading from field, always load full i4 - opcode = GINT_TO_OPCODE (get_mov_for_type (mt, TRUE)); + opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, TRUE)); } else { // Storing into field, copy exact size fsize = get_mint_type_size (mt); @@ -8678,7 +8399,7 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in *ip++ = GINT_TO_UINT16 (get_local_offset (td, ins->sregs [0])); } - int left = get_inst_length (ins) - GPTRDIFF_TO_INT(ip - start_ip); + int left = interp_get_ins_length (ins) - GPTRDIFF_TO_INT(ip - start_ip); // Emit the rest of the data for (int i = 0; i < left; i++) *ip++ = ins->data [i]; @@ -8717,7 +8438,7 @@ generate_compacted_code (InterpMethod *rtm, TransformData *td) // This iteration could be avoided at the cost of less precise size result, following // super instruction pass - size = compute_native_offset_estimates (td); + size = interp_compute_native_offset_estimates (td); // Generate the compacted stream of instructions td->new_code = ip = (guint16*)imethod_alloc0 (td, size * sizeof (guint16)); @@ -8789,2314 +8510,6 @@ generate_compacted_code (InterpMethod *rtm, TransformData *td) #endif } -static void -interp_mark_reachable_bblocks (TransformData *td) -{ - InterpBasicBlock **queue = mono_mempool_alloc0 (td->mempool, td->bb_count * sizeof (InterpBasicBlock*)); - InterpBasicBlock *current; - int cur_index = 0; - int next_position = 0; - - // FIXME There is no need to force eh bblocks to remain alive - current = td->entry_bb; - while (current != NULL) { - if (current->eh_block || current->patchpoint_data) { - queue [next_position++] = current; - current->reachable = TRUE; - } else { - current->reachable = FALSE; - } - current = current->next_bb; - } - - queue [next_position++] = td->entry_bb; - td->entry_bb->reachable = TRUE; - - // We have the roots, traverse everything else - while (cur_index < next_position) { - current = queue [cur_index++]; - for (int i = 0; i < current->out_count; i++) { - InterpBasicBlock *child = current->out_bb [i]; - if (!child->reachable) { - queue [next_position++] = child; - child->reachable = TRUE; - } - } - } -} - -/** - * Returns TRUE if instruction or previous instructions defines at least one of the variables, FALSE otherwise. - */ - -static gboolean -interp_prev_block_defines_var (InterpInst *ins, int var1, int var2) -{ - // Check max of 5 instructions - for (int i = 0; i < 5; i++) { - ins = interp_prev_ins (ins); - if (!ins) - return FALSE; - if (mono_interp_op_dregs [ins->opcode] && (ins->dreg == var1 || ins->dreg == var2)) - return TRUE; - } - return FALSE; -} - -/** - * Check if the given basic block has a known pattern for inlining into callers blocks, if so, return a pointer to the conditional branch instruction. - * - * The known patterns are: - * - `branch`: a conditional branch instruction. - * - `ldc; branch`: a load instruction followed by a binary conditional branch. - * - `ldc; compare; branch`: a load instruction followed by a compare instruction and a unary conditional branch. - */ -static InterpInst* -interp_inline_into_callers (InterpInst *first, int *lookup_var1, int *lookup_var2) { - // pattern `branch` - if (MINT_IS_CONDITIONAL_BRANCH (first->opcode)) { - *lookup_var1 = first->sregs [0]; - *lookup_var2 = (mono_interp_op_dregs [first->opcode] > 1) ? first->sregs [1] : -1; - return first; - } - - if (MINT_IS_LDC_I4 (first->opcode)) { - InterpInst *second = interp_next_ins (first); - if (!second) - return NULL; - *lookup_var2 = -1; - gboolean first_var_defined = first->dreg == second->sregs [0]; - gboolean second_var_defined = first->dreg == second->sregs [1]; - // pattern `ldc; binop conditional branch` - if (MINT_IS_BINOP_CONDITIONAL_BRANCH (second->opcode) && (first_var_defined || second_var_defined)) { - *lookup_var1 = first_var_defined ? second->sregs [1] : second->sregs [0]; - return second; - } - - InterpInst *third = interp_next_ins (second); - if (!third) - return NULL; - // pattern `ldc; compare; conditional branch` - if (MINT_IS_COMPARE (second->opcode) && (first_var_defined || second_var_defined) - && MINT_IS_UNOP_CONDITIONAL_BRANCH (third->opcode) && second->dreg == third->sregs [0]) { - *lookup_var1 = first_var_defined ? second->sregs [1] : second->sregs [0]; - return third; - } - } - - return NULL; -} - -static void -interp_reorder_bblocks (TransformData *td) -{ - InterpBasicBlock *bb; - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - if (bb->eh_block) - continue; - InterpInst *first = interp_first_ins (bb); - if (!first) - continue; - int lookup_var1, lookup_var2; - InterpInst *cond_ins = interp_inline_into_callers (first, &lookup_var1, &lookup_var2); - if (cond_ins) { - // This means this bblock match a pattern for inlining into callers, with a conditional branch - int i = 0; - while (i < bb->in_count) { - InterpBasicBlock *in_bb = bb->in_bb [i]; - InterpInst *last_ins = interp_last_ins (in_bb); - if (last_ins && last_ins->opcode == MINT_BR && interp_prev_block_defines_var (last_ins, lookup_var1, lookup_var2)) { - // This bblock is reached unconditionally from one of its parents - // Move the conditional branch inside the parent to facilitate propagation - // of condition value. - InterpBasicBlock *cond_true_bb = cond_ins->info.target_bb; - InterpBasicBlock *next_bb = bb->next_bb; - - // Parent bb will do the conditional branch - interp_unlink_bblocks (in_bb, bb); - // Remove ending MINT_BR - interp_clear_ins (last_ins); - // Copy all instructions one by one, from interp_first_ins (bb) to the end of the in_bb - InterpInst *copy_ins = first; - while (copy_ins) { - InterpInst *new_ins = interp_insert_ins_bb (td, in_bb, in_bb->last_ins, copy_ins->opcode); - new_ins->dreg = copy_ins->dreg; - new_ins->sregs [0] = copy_ins->sregs [0]; - if (mono_interp_op_sregs [copy_ins->opcode] > 1) - new_ins->sregs [1] = copy_ins->sregs [1]; - - new_ins->data [0] = copy_ins->data [0]; - if (copy_ins->opcode == MINT_LDC_I4) - new_ins->data [1] = copy_ins->data [1]; - - copy_ins = interp_next_ins (copy_ins); - } - in_bb->last_ins->info.target_bb = cond_true_bb; - interp_link_bblocks (td, in_bb, cond_true_bb); - - // Create new fallthrough bb between in_bb and in_bb->next_bb - InterpBasicBlock *new_bb = alloc_bb (td); - new_bb->next_bb = in_bb->next_bb; - in_bb->next_bb = new_bb; - new_bb->il_offset = in_bb->il_offset; - interp_link_bblocks (td, in_bb, new_bb); - - InterpInst *new_inst = interp_insert_ins_bb (td, new_bb, NULL, MINT_BR); - new_inst->info.target_bb = next_bb; - - interp_link_bblocks (td, new_bb, next_bb); - if (td->verbose_level) { - GString* bb_info = get_interp_bb_links (bb); - GString* in_bb_info = get_interp_bb_links (in_bb); - GString* new_bb_info = get_interp_bb_links (new_bb); - g_print ("Moved cond branch BB%d into BB%d, new BB%d\n", bb->index, in_bb->index, new_bb->index); - g_print ("\tBB%d: %s\n", bb->index, bb_info->str); - g_print ("\tBB%d: %s\n", in_bb->index, in_bb_info->str); - g_print ("\tBB%d: %s\n", new_bb->index, new_bb_info->str); - g_string_free (bb_info, TRUE); - g_string_free (in_bb_info, TRUE); - g_string_free (new_bb_info, TRUE); - } - // Since we changed links, in_bb might have changed, loop again from the start - i = 0; - } else { - i++; - } - } - } else if (first->opcode == MINT_BR) { - // All bblocks jumping into this bblock can jump directly into the br target since it is the single instruction of the bb - int i = 0; - while (i < bb->in_count) { - InterpBasicBlock *in_bb = bb->in_bb [i]; - InterpInst *last_ins = interp_last_ins (in_bb); - if (last_ins && (MINT_IS_CONDITIONAL_BRANCH (last_ins->opcode) || - MINT_IS_UNCONDITIONAL_BRANCH (last_ins->opcode)) && - last_ins->info.target_bb == bb) { - InterpBasicBlock *target_bb = first->info.target_bb; - last_ins->info.target_bb = target_bb; - interp_unlink_bblocks (in_bb, bb); - interp_link_bblocks (td, in_bb, target_bb); - if (td->verbose_level) { - GString* bb_info = get_interp_bb_links (bb); - GString* in_bb_info = get_interp_bb_links (in_bb); - GString* target_bb_info = get_interp_bb_links (target_bb); - g_print ("Propagated target bb BB%d into BB%d\n", target_bb->index, in_bb->index); - g_print ("\tBB%d: %s\n", bb->index, bb_info->str); - g_print ("\tBB%d: %s\n", in_bb->index, in_bb_info->str); - g_print ("\tBB%d: %s\n", target_bb->index, target_bb_info->str); - g_string_free (bb_info, TRUE); - g_string_free (in_bb_info, TRUE); - g_string_free (target_bb_info, TRUE); - } - i = 0; - } else { - i++; - } - } - } - } -} - -// Traverse the list of basic blocks and merge adjacent blocks -static gboolean -interp_optimize_bblocks (TransformData *td) -{ - InterpBasicBlock *bb = td->entry_bb; - gboolean needs_cprop = FALSE; - - interp_reorder_bblocks (td); - - interp_mark_reachable_bblocks (td); - - while (TRUE) { - InterpBasicBlock *next_bb = bb->next_bb; - if (!next_bb) - break; - if (!next_bb->reachable) { - if (td->verbose_level) - g_print ("Removed BB%d\n", next_bb->index); - needs_cprop |= interp_remove_bblock (td, next_bb, bb); - continue; - } else if (bb->out_count == 1 && bb->out_bb [0] == next_bb && next_bb->in_count == 1 && !next_bb->eh_block && !next_bb->patchpoint_data) { - g_assert (next_bb->in_bb [0] == bb); - interp_merge_bblocks (td, bb, next_bb); - if (td->verbose_level) - g_print ("Merged BB%d and BB%d\n", bb->index, next_bb->index); - needs_cprop = TRUE; - continue; - } - - bb = next_bb; - } - return needs_cprop; -} - -static gboolean -interp_local_deadce (TransformData *td) -{ - int *local_ref_count = td->local_ref_count; - gboolean needs_dce = FALSE; - gboolean needs_cprop = FALSE; - - for (unsigned int i = 0; i < td->locals_size; i++) { - g_assert (local_ref_count [i] >= 0); - g_assert (td->locals [i].indirects >= 0); - if (td->locals [i].indirects || (td->locals [i].flags & INTERP_LOCAL_FLAG_DEAD)) - continue; - if (!local_ref_count [i]) { - needs_dce = TRUE; - td->locals [i].flags |= INTERP_LOCAL_FLAG_DEAD; - } else if (!(td->locals [i].flags & INTERP_LOCAL_FLAG_UNKNOWN_USE)) { - if (!(td->locals [i].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) { - // The value of this var is not passed between multiple basic blocks - td->locals [i].flags |= INTERP_LOCAL_FLAG_LOCAL_ONLY; - if (td->verbose_level) - g_print ("Var %d is local only\n", i); - needs_cprop = TRUE; - } - } - td->locals [i].flags &= ~INTERP_LOCAL_FLAG_UNKNOWN_USE; - } - - // Return early if all locals are alive - if (!needs_dce) - return needs_cprop; - - // Kill instructions that don't use stack and are storing into dead locals - for (InterpBasicBlock *bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { - if (MINT_NO_SIDE_EFFECTS (ins->opcode) || - ins->opcode == MINT_LDLOCA_S) { - int dreg = ins->dreg; - if (td->locals [dreg].flags & INTERP_LOCAL_FLAG_DEAD) { - if (td->verbose_level) { - g_print ("kill dead ins:\n\t"); - dump_interp_inst (ins, td->data_items); - } - - if (ins->opcode == MINT_LDLOCA_S) { - mono_interp_stats.ldlocas_removed++; - td->locals [ins->sregs [0]].indirects--; - if (!td->locals [ins->sregs [0]].indirects) { - // We can do cprop now through this local. Run cprop again. - needs_cprop = TRUE; - } - } - interp_clear_ins (ins); - mono_interp_stats.killed_instructions++; - // FIXME This is lazy. We should update the ref count for the sregs and redo deadce. - needs_cprop = TRUE; - } - } - } - } - return needs_cprop; -} - -#define INTERP_FOLD_UNOP(opcode,val_type,field,op) \ - case opcode: \ - result.type = val_type; \ - result.field = op val->field; \ - break; - -#define INTERP_FOLD_CONV(opcode,val_type_dst,field_dst,val_type_src,field_src,cast_type) \ - case opcode: \ - result.type = val_type_dst; \ - result.field_dst = (cast_type)val->field_src; \ - break; - -#define INTERP_FOLD_CONV_FULL(opcode,val_type_dst,field_dst,val_type_src,field_src,cast_type,cond) \ - case opcode: \ - if (!(cond)) return ins; \ - result.type = val_type_dst; \ - result.field_dst = (cast_type)val->field_src; \ - break; - -static InterpInst* -interp_fold_unop (TransformData *td, LocalValue *local_defs, InterpInst *ins) -{ - int *local_ref_count = td->local_ref_count; - // ins should be an unop, therefore it should have a single dreg and a single sreg - int dreg = ins->dreg; - int sreg = ins->sregs [0]; - LocalValue *val = &local_defs [sreg]; - LocalValue result; - - if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8) - return ins; - - // Top of the stack is a constant - switch (ins->opcode) { - INTERP_FOLD_UNOP (MINT_ADD1_I4, LOCAL_VALUE_I4, i, 1+); - INTERP_FOLD_UNOP (MINT_ADD1_I8, LOCAL_VALUE_I8, l, 1+); - INTERP_FOLD_UNOP (MINT_SUB1_I4, LOCAL_VALUE_I4, i, -1+); - INTERP_FOLD_UNOP (MINT_SUB1_I8, LOCAL_VALUE_I8, l, -1+); - INTERP_FOLD_UNOP (MINT_NEG_I4, LOCAL_VALUE_I4, i, -); - INTERP_FOLD_UNOP (MINT_NEG_I8, LOCAL_VALUE_I8, l, -); - INTERP_FOLD_UNOP (MINT_NOT_I4, LOCAL_VALUE_I4, i, ~); - INTERP_FOLD_UNOP (MINT_NOT_I8, LOCAL_VALUE_I8, l, ~); - INTERP_FOLD_UNOP (MINT_CEQ0_I4, LOCAL_VALUE_I4, i, 0 ==); - - INTERP_FOLD_CONV (MINT_CONV_I1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8); - INTERP_FOLD_CONV (MINT_CONV_I1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8); - INTERP_FOLD_CONV (MINT_CONV_U1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint8); - INTERP_FOLD_CONV (MINT_CONV_U1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint8); - - INTERP_FOLD_CONV (MINT_CONV_I2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16); - INTERP_FOLD_CONV (MINT_CONV_I2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint16); - INTERP_FOLD_CONV (MINT_CONV_U2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint16); - INTERP_FOLD_CONV (MINT_CONV_U2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint16); - - INTERP_FOLD_CONV (MINT_CONV_I8_I4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, gint32); - INTERP_FOLD_CONV (MINT_CONV_I8_U4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, guint32); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8, val->i >= G_MININT8 && val->i <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8, val->l >= G_MININT8 && val->l <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8, val->i >= 0 && val->i <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8, val->l >= 0 && val->l <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint8, val->i >= 0 && val->i <= G_MAXUINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint8, val->l >= 0 && val->l <= G_MAXUINT8); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16, val->i >= G_MININT16 && val->i <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, i, gint16, val->l >= G_MININT16 && val->l <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16, val->i >= 0 && val->i <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint16, val->l >= 0 && val->l <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint16, val->i >= 0 && val->i <= G_MAXUINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint16, val->l >= 0 && val->l <= G_MAXUINT16); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint32, val->i >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint32, val->l >= G_MININT32 && val->l <= G_MAXINT32); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint32, val->l >= 0 && val->l <= G_MAXINT32); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint32, val->i >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint32, val->l >= 0 && val->l <= G_MAXINT32); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I8_U8, LOCAL_VALUE_I8, l, LOCAL_VALUE_I8, l, gint64, val->l >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, guint64, val->i >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I8, LOCAL_VALUE_I8, l, LOCAL_VALUE_I8, l, guint64, val->l >= 0); - - default: - return ins; - } - - // We were able to compute the result of the ins instruction. We replace the unop - // with a LDC of the constant. We leave alone the sregs of this instruction, for - // deadce to kill the instructions initializing them. - mono_interp_stats.constant_folds++; - - if (result.type == LOCAL_VALUE_I4) - ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); - else if (result.type == LOCAL_VALUE_I8) - ins = interp_inst_replace_with_i8_const (td, ins, result.l); - else - g_assert_not_reached (); - - if (td->verbose_level) { - g_print ("Fold unop :\n\t"); - dump_interp_inst (ins, td->data_items); - } - - local_ref_count [sreg]--; - result.ins = ins; - result.ref_count = 0; - local_defs [dreg] = result; - - return ins; -} - -#define INTERP_FOLD_UNOP_BR(_opcode,_cond) \ - case _opcode: \ - if (_cond) { \ - ins->opcode = MINT_BR; \ - if (cbb->next_bb != ins->info.target_bb) \ - interp_unlink_bblocks (cbb, cbb->next_bb); \ - for (InterpInst *it = ins->next; it != NULL; it = it->next) \ - interp_clear_ins (it); \ - } else { \ - interp_clear_ins (ins); \ - interp_unlink_bblocks (cbb, ins->info.target_bb); \ - } \ - break; - -static InterpInst* -interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) -{ - int *local_ref_count = td->local_ref_count; - // ins should be an unop conditional branch, therefore it should have a single sreg - int sreg = ins->sregs [0]; - LocalValue *val = &local_defs [sreg]; - - if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8 && val->type != LOCAL_VALUE_NON_NULL) - return ins; - - if (val->type == LOCAL_VALUE_NON_NULL) { - switch (ins->opcode) { - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, FALSE); - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, FALSE); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4, TRUE); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8, TRUE); - - default: - return ins; - } - } else { - // Top of the stack is a constant - switch (ins->opcode) { - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, val->i == 0); - INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, val->l == 0); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I4, val->i != 0); - INTERP_FOLD_UNOP_BR (MINT_BRTRUE_I8, val->l != 0); - - default: - return ins; - } - } - - if (td->verbose_level) { - g_print ("Fold unop cond br :\n\t"); - dump_interp_inst (ins, td->data_items); - } - - mono_interp_stats.constant_folds++; - local_ref_count [sreg]--; - return ins; -} - -#define INTERP_FOLD_BINOP(opcode,local_type,field,op) \ - case opcode: \ - result.type = local_type; \ - result.field = val1->field op val2->field; \ - break; - -#define INTERP_FOLD_BINOP_FULL(opcode,local_type,field,op,cast_type,cond) \ - case opcode: \ - if (!(cond)) return ins; \ - result.type = local_type; \ - result.field = (cast_type)val1->field op (cast_type)val2->field; \ - break; - -#define INTERP_FOLD_SHIFTOP(opcode,local_type,field,shift_op,cast_type) \ - case opcode: \ - result.type = local_type; \ - result.field = (cast_type)val1->field shift_op val2->i; \ - break; - -#define INTERP_FOLD_RELOP(opcode,local_type,field,relop,cast_type) \ - case opcode: \ - result.type = LOCAL_VALUE_I4; \ - result.i = (cast_type) val1->field relop (cast_type) val2->field; \ - break; - - -static InterpInst* -interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins, gboolean *folded) -{ - int *local_ref_count = td->local_ref_count; - // ins should be a binop, therefore it should have a single dreg and two sregs - int dreg = ins->dreg; - int sreg1 = ins->sregs [0]; - int sreg2 = ins->sregs [1]; - LocalValue *val1 = &local_defs [sreg1]; - LocalValue *val2 = &local_defs [sreg2]; - LocalValue result; - - *folded = FALSE; - - if (val1->type != LOCAL_VALUE_I4 && val1->type != LOCAL_VALUE_I8) - return ins; - if (val2->type != LOCAL_VALUE_I4 && val2->type != LOCAL_VALUE_I8) - return ins; - - // Top two values of the stack are constants - switch (ins->opcode) { - INTERP_FOLD_BINOP (MINT_ADD_I4, LOCAL_VALUE_I4, i, +); - INTERP_FOLD_BINOP (MINT_ADD_I8, LOCAL_VALUE_I8, l, +); - INTERP_FOLD_BINOP (MINT_SUB_I4, LOCAL_VALUE_I4, i, -); - INTERP_FOLD_BINOP (MINT_SUB_I8, LOCAL_VALUE_I8, l, -); - INTERP_FOLD_BINOP (MINT_MUL_I4, LOCAL_VALUE_I4, i, *); - INTERP_FOLD_BINOP (MINT_MUL_I8, LOCAL_VALUE_I8, l, *); - - INTERP_FOLD_BINOP (MINT_AND_I4, LOCAL_VALUE_I4, i, &); - INTERP_FOLD_BINOP (MINT_AND_I8, LOCAL_VALUE_I8, l, &); - INTERP_FOLD_BINOP (MINT_OR_I4, LOCAL_VALUE_I4, i, |); - INTERP_FOLD_BINOP (MINT_OR_I8, LOCAL_VALUE_I8, l, |); - INTERP_FOLD_BINOP (MINT_XOR_I4, LOCAL_VALUE_I4, i, ^); - INTERP_FOLD_BINOP (MINT_XOR_I8, LOCAL_VALUE_I8, l, ^); - - INTERP_FOLD_SHIFTOP (MINT_SHL_I4, LOCAL_VALUE_I4, i, <<, gint32); - INTERP_FOLD_SHIFTOP (MINT_SHL_I8, LOCAL_VALUE_I8, l, <<, gint64); - INTERP_FOLD_SHIFTOP (MINT_SHR_I4, LOCAL_VALUE_I4, i, >>, gint32); - INTERP_FOLD_SHIFTOP (MINT_SHR_I8, LOCAL_VALUE_I8, l, >>, gint64); - INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4, LOCAL_VALUE_I4, i, >>, guint32); - INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8, LOCAL_VALUE_I8, l, >>, guint64); - - INTERP_FOLD_RELOP (MINT_CEQ_I4, LOCAL_VALUE_I4, i, ==, gint32); - INTERP_FOLD_RELOP (MINT_CEQ_I8, LOCAL_VALUE_I8, l, ==, gint64); - INTERP_FOLD_RELOP (MINT_CNE_I4, LOCAL_VALUE_I4, i, !=, gint32); - INTERP_FOLD_RELOP (MINT_CNE_I8, LOCAL_VALUE_I8, l, !=, gint64); - - INTERP_FOLD_RELOP (MINT_CGT_I4, LOCAL_VALUE_I4, i, >, gint32); - INTERP_FOLD_RELOP (MINT_CGT_I8, LOCAL_VALUE_I8, l, >, gint64); - INTERP_FOLD_RELOP (MINT_CGT_UN_I4, LOCAL_VALUE_I4, i, >, guint32); - INTERP_FOLD_RELOP (MINT_CGT_UN_I8, LOCAL_VALUE_I8, l, >, guint64); - - INTERP_FOLD_RELOP (MINT_CGE_I4, LOCAL_VALUE_I4, i, >=, gint32); - INTERP_FOLD_RELOP (MINT_CGE_I8, LOCAL_VALUE_I8, l, >=, gint64); - INTERP_FOLD_RELOP (MINT_CGE_UN_I4, LOCAL_VALUE_I4, i, >=, guint32); - INTERP_FOLD_RELOP (MINT_CGE_UN_I8, LOCAL_VALUE_I8, l, >=, guint64); - - INTERP_FOLD_RELOP (MINT_CLT_I4, LOCAL_VALUE_I4, i, <, gint32); - INTERP_FOLD_RELOP (MINT_CLT_I8, LOCAL_VALUE_I8, l, <, gint64); - INTERP_FOLD_RELOP (MINT_CLT_UN_I4, LOCAL_VALUE_I4, i, <, guint32); - INTERP_FOLD_RELOP (MINT_CLT_UN_I8, LOCAL_VALUE_I8, l, <, guint64); - - INTERP_FOLD_RELOP (MINT_CLE_I4, LOCAL_VALUE_I4, i, <=, gint32); - INTERP_FOLD_RELOP (MINT_CLE_I8, LOCAL_VALUE_I8, l, <=, gint64); - INTERP_FOLD_RELOP (MINT_CLE_UN_I4, LOCAL_VALUE_I4, i, <=, guint32); - INTERP_FOLD_RELOP (MINT_CLE_UN_I8, LOCAL_VALUE_I8, l, <=, guint64); - - INTERP_FOLD_BINOP_FULL (MINT_DIV_I4, LOCAL_VALUE_I4, i, /, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); - INTERP_FOLD_BINOP_FULL (MINT_DIV_I8, LOCAL_VALUE_I8, l, /, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); - INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4, LOCAL_VALUE_I4, i, /, guint32, val2->i != 0); - INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8, LOCAL_VALUE_I8, l, /, guint64, val2->l != 0); - - INTERP_FOLD_BINOP_FULL (MINT_REM_I4, LOCAL_VALUE_I4, i, %, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); - INTERP_FOLD_BINOP_FULL (MINT_REM_I8, LOCAL_VALUE_I8, l, %, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); - INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4, LOCAL_VALUE_I4, i, %, guint32, val2->i != 0); - INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8, LOCAL_VALUE_I8, l, %, guint64, val2->l != 0); - - default: - return ins; - } - - // We were able to compute the result of the ins instruction. We replace the binop - // with a LDC of the constant. We leave alone the sregs of this instruction, for - // deadce to kill the instructions initializing them. - mono_interp_stats.constant_folds++; - *folded = TRUE; - if (result.type == LOCAL_VALUE_I4) - ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); - else if (result.type == LOCAL_VALUE_I8) - ins = interp_inst_replace_with_i8_const (td, ins, result.l); - else - g_assert_not_reached (); - - if (td->verbose_level) { - g_print ("Fold binop :\n\t"); - dump_interp_inst (ins, td->data_items); - } - - local_ref_count [sreg1]--; - local_ref_count [sreg2]--; - result.ins = ins; - result.ref_count = 0; - local_defs [dreg] = result; - return ins; -} - -// Due to poor current design, the branch op might not be the last instruction in the bblock -// (in case we fallthrough and need to have the stack locals match the ones from next_bb, done -// in fixup_newbb_stack_locals). If that's the case, clear all these mov's. This helps bblock -// merging quickly find the MINT_BR opcode. -#define INTERP_FOLD_BINOP_BR(_opcode,_local_type,_cond) \ - case _opcode: \ - if (_cond) { \ - ins->opcode = MINT_BR; \ - if (cbb->next_bb != ins->info.target_bb) \ - interp_unlink_bblocks (cbb, cbb->next_bb); \ - for (InterpInst *it = ins->next; it != NULL; it = it->next) \ - interp_clear_ins (it); \ - } else { \ - interp_clear_ins (ins); \ - interp_unlink_bblocks (cbb, ins->info.target_bb); \ - } \ - break; - -static InterpInst* -interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) -{ - int *local_ref_count = td->local_ref_count; - // ins should be a conditional binop, therefore it should have only two sregs - int sreg1 = ins->sregs [0]; - int sreg2 = ins->sregs [1]; - LocalValue *val1 = &local_defs [sreg1]; - LocalValue *val2 = &local_defs [sreg2]; - - if (val1->type != LOCAL_VALUE_I4 && val1->type != LOCAL_VALUE_I8) - return ins; - if (val2->type != LOCAL_VALUE_I4 && val2->type != LOCAL_VALUE_I8) - return ins; - - switch (ins->opcode) { - INTERP_FOLD_BINOP_BR (MINT_BEQ_I4, LOCAL_VALUE_I4, val1->i == val2->i); - INTERP_FOLD_BINOP_BR (MINT_BEQ_I8, LOCAL_VALUE_I8, val1->l == val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGE_I4, LOCAL_VALUE_I4, val1->i >= val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGE_I8, LOCAL_VALUE_I8, val1->l >= val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGT_I4, LOCAL_VALUE_I4, val1->i > val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGT_I8, LOCAL_VALUE_I8, val1->l > val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLT_I4, LOCAL_VALUE_I4, val1->i < val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLT_I8, LOCAL_VALUE_I8, val1->l < val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLE_I4, LOCAL_VALUE_I4, val1->i <= val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLE_I8, LOCAL_VALUE_I8, val1->l <= val2->l); - - INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I4, LOCAL_VALUE_I4, val1->i != val2->i); - INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I8, LOCAL_VALUE_I8, val1->l != val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i >= (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l >= (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i > (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l > (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i <= (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l <= (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i < (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l < (guint64)val2->l); - - default: - return ins; - } - if (td->verbose_level) { - g_print ("Fold binop cond br :\n\t"); - dump_interp_inst (ins, td->data_items); - } - - mono_interp_stats.constant_folds++; - local_ref_count [sreg1]--; - local_ref_count [sreg2]--; - return ins; -} - -static void -write_v128_element (gpointer v128_addr, LocalValue *val, int index, int el_size) -{ - gpointer el_addr = (gint8*)v128_addr + index * el_size; - g_assert ((gint8*)el_addr < ((gint8*)v128_addr + 16)); - switch (el_size) { - case 1: *(gint8*)el_addr = (gint8)val->i; break; - case 2: *(gint16*)el_addr = (gint16)val->i; break; - case 4: *(gint32*)el_addr = val->i; break; // this also handles r4 - case 8: *(gint64*)el_addr = val->l; break; - default: - g_assert_not_reached (); - } -} - -static InterpInst* -interp_fold_simd_create (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) -{ - int *local_ref_count = td->local_ref_count; - - int *args = ins->info.call_info->call_args; - int index = 0; - int var = args [index]; - while (var != -1) { - LocalValue *val = &local_defs [var]; - if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8 && val->type != LOCAL_VALUE_R4) - return ins; - index++; - var = args [index]; - } - - // If we reached this point, it means that all args of the simd_create are constants - // We can replace the simd_create with simd_ldc - int el_size = 16 / index; - int dreg = ins->dreg; - - ins = interp_insert_ins (td, ins, MINT_SIMD_V128_LDC); - interp_clear_ins (ins->prev); - interp_ins_set_dreg (ins, dreg); - - gpointer v128_addr = &ins->data [0]; - - index = 0; - var = args [index]; - while (var != -1) { - LocalValue *val = &local_defs [var]; - write_v128_element (v128_addr, val, index, el_size); - val->ref_count--; - local_ref_count [var]--; - index++; - var = args [index]; - } - - if (td->verbose_level) { - g_print ("Fold simd create:\n\t"); - dump_interp_inst (ins, td->data_items); - } - - local_defs [dreg].ins = ins; - local_defs [dreg].type = LOCAL_VALUE_NONE; - - return ins; -} - -static void -cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, LocalValue *local_defs) -{ - int *local_ref_count = td->local_ref_count; - int sreg = *psreg; - - local_ref_count [sreg]++; - local_defs [sreg].ref_count++; - if (local_defs [sreg].type == LOCAL_VALUE_LOCAL) { - int cprop_local = local_defs [sreg].local; - - // We are trying to replace sregs [i] with its def local (cprop_local), but cprop_local has since been - // modified, so we can't use it. - if (local_defs [cprop_local].ins != NULL && local_defs [cprop_local].def_index > local_defs [sreg].def_index) - return; - - if (td->verbose_level) - g_print ("cprop %d -> %d:\n\t", sreg, cprop_local); - local_ref_count [sreg]--; - *psreg = cprop_local; - local_ref_count [cprop_local]++; - if (td->verbose_level) - dump_interp_inst (ins, td->data_items); - } else if (!local_defs [sreg].ins) { - td->locals [sreg].flags |= INTERP_LOCAL_FLAG_UNKNOWN_USE; - } -} - -static void -clear_local_defs (TransformData *td, int var, void *data) -{ - LocalValue *local_defs = (LocalValue*) data; - local_defs [var].type = LOCAL_VALUE_NONE; - local_defs [var].ins = NULL; - local_defs [var].ref_count = 0; -} - -static void -clear_unused_defs (TransformData *td, int var, void *data) -{ - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) - return; - if (td->locals [var].indirects) - return; - - LocalValue *local_def = &((LocalValue*) data) [var]; - InterpInst *def_ins = local_def->ins; - if (!def_ins) - return; - if (local_def->ref_count) - return; - - // This is a local only var that is defined in this bblock and its value is not used - // at all in this bblock. Clear the definition - if (MINT_NO_SIDE_EFFECTS (def_ins->opcode)) { - for (int i = 0; i < mono_interp_op_sregs [def_ins->opcode]; i++) - td->local_ref_count [def_ins->sregs [i]]--; - if (td->verbose_level) { - g_print ("kill unused local def:\n\t"); - dump_interp_inst (def_ins, td->data_items); - } - interp_clear_ins (def_ins); - } -} - -static void -interp_cprop (TransformData *td) -{ - LocalValue *local_defs = (LocalValue*) g_malloc (td->locals_size * sizeof (LocalValue)); - int *local_ref_count = (int*) g_malloc (td->locals_size * sizeof (int)); - InterpBasicBlock *bb; - gboolean needs_retry; - int ins_index; - int iteration_count = 0; - - td->local_ref_count = local_ref_count; -retry: - needs_retry = FALSE; - memset (local_ref_count, 0, td->locals_size * sizeof (int)); - - if (td->verbose_level) - g_print ("\ncprop iteration %d\n", iteration_count++); - - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - InterpInst *ins; - ins_index = 0; - - // Set cbb since we do some instruction inserting below - td->cbb = bb; - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) - foreach_local_var (td, ins, local_defs, clear_local_defs); - - if (td->verbose_level) { - GString* bb_info = get_interp_bb_links (bb); - g_print ("\nBB%d: %s\n", bb->index, bb_info->str); - g_string_free (bb_info, TRUE); - } - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - int opcode = ins->opcode; - - if (opcode == MINT_NOP) - continue; - - int num_sregs = mono_interp_op_sregs [opcode]; - int num_dregs = mono_interp_op_dregs [opcode]; - gint32 *sregs = &ins->sregs [0]; - gint32 dreg = ins->dreg; - - if (td->verbose_level && ins->opcode != MINT_NOP && ins->opcode != MINT_IL_SEQ_POINT) - dump_interp_inst (ins, td->data_items); - - for (int i = 0; i < num_sregs; i++) { - if (sregs [i] == MINT_CALL_ARGS_SREG) { - if (ins->info.call_info && ins->info.call_info->call_args) { - int *call_args = ins->info.call_info->call_args; - while (*call_args != -1) { - cprop_sreg (td, ins, call_args, local_defs); - call_args++; - } - } - } else { - cprop_sreg (td, ins, &sregs [i], local_defs); - } - } - - if (num_dregs) { - // Check if the previous definition of this var was used at all. - // If it wasn't we can just clear the instruction - // - // MINT_MOV_DST_OFF doesn't fully write to the var, so we special case it here - if (local_defs [dreg].ins != NULL && - local_defs [dreg].ref_count == 0 && - !td->locals [dreg].indirects && - opcode != MINT_MOV_DST_OFF) { - InterpInst *prev_def = local_defs [dreg].ins; - if (MINT_NO_SIDE_EFFECTS (prev_def->opcode)) { - for (int i = 0; i < mono_interp_op_sregs [prev_def->opcode]; i++) - local_ref_count [prev_def->sregs [i]]--; - interp_clear_ins (prev_def); - } - } - local_defs [dreg].type = LOCAL_VALUE_NONE; - local_defs [dreg].ins = ins; - local_defs [dreg].def_index = ins_index; - } - - // We always store to the full i4, except as part of STIND opcodes. These opcodes can be - // applied to a local var only if that var has LDLOCA applied to it - if ((opcode >= MINT_MOV_I4_I1 && opcode <= MINT_MOV_I4_U2) && !td->locals [sregs [0]].indirects) { - ins->opcode = MINT_MOV_4; - opcode = MINT_MOV_4; - } - - if (opcode == MINT_MOV_4 || opcode == MINT_MOV_8 || opcode == MINT_MOV_VT) { - int sreg = sregs [0]; - if (dreg == sreg) { - if (td->verbose_level) - g_print ("clear redundant mov\n"); - interp_clear_ins (ins); - local_ref_count [sreg]--; - } else if (td->locals [sreg].indirects || td->locals [dreg].indirects) { - // Don't bother with indirect locals - } else if (local_defs [sreg].type == LOCAL_VALUE_I4 || local_defs [sreg].type == LOCAL_VALUE_I8) { - // Replace mov with ldc - gboolean is_i4 = local_defs [sreg].type == LOCAL_VALUE_I4; - g_assert (!td->locals [sreg].indirects); - local_defs [dreg].type = local_defs [sreg].type; - if (is_i4) { - int ct = local_defs [sreg].i; - ins = interp_get_ldc_i4_from_const (td, ins, ct, dreg); - local_defs [dreg].i = ct; - } else { - gint64 ct = local_defs [sreg].l; - ins = interp_inst_replace_with_i8_const (td, ins, ct); - local_defs [dreg].l = ct; - } - local_defs [dreg].ins = ins; - local_ref_count [sreg]--; - mono_interp_stats.copy_propagations++; - if (td->verbose_level) { - g_print ("cprop loc %d -> ct :\n\t", sreg); - dump_interp_inst (ins, td->data_items); - } - } else if (local_defs [sreg].ins != NULL && - (td->locals [sreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && - !(td->locals [dreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && - interp_prev_ins (ins) == local_defs [sreg].ins && - !(interp_prev_ins (ins)->flags & INTERP_INST_FLAG_PROTECTED_NEWOBJ)) { - // hackish temporary optimization that won't be necessary in the future - // We replace `local1 <- ?, local2 <- local1` with `local2 <- ?, local1 <- local2` - // if local1 is execution stack local and local2 is normal global local. This makes - // it more likely for `local1 <- local2` to be killed, while before we always needed - // to store to the global local, which is likely accessed by other instructions. - InterpInst *def = local_defs [sreg].ins; - int original_dreg = def->dreg; - - def->dreg = dreg; - ins->dreg = original_dreg; - sregs [0] = dreg; - - local_defs [dreg].type = LOCAL_VALUE_NONE; - local_defs [dreg].ins = def; - local_defs [dreg].def_index = local_defs [original_dreg].def_index; - local_defs [dreg].ref_count++; - local_defs [original_dreg].type = LOCAL_VALUE_LOCAL; - local_defs [original_dreg].ins = ins; - local_defs [original_dreg].local = dreg; - local_defs [original_dreg].def_index = ins_index; - local_defs [original_dreg].ref_count--; - - local_ref_count [original_dreg]--; - local_ref_count [dreg]++; - - if (td->verbose_level) { - g_print ("cprop dreg:\n\t"); - dump_interp_inst (def, td->data_items); - g_print ("\t"); - dump_interp_inst (ins, td->data_items); - } - } else { - if (td->verbose_level) - g_print ("local copy %d <- %d\n", dreg, sreg); - local_defs [dreg].type = LOCAL_VALUE_LOCAL; - local_defs [dreg].local = sreg; - } - } else if (opcode == MINT_LDLOCA_S) { - // The local that we are taking the address of is not a sreg but still referenced - local_ref_count [ins->sregs [0]]++; - } else if (MINT_IS_LDC_I4 (opcode)) { - local_defs [dreg].type = LOCAL_VALUE_I4; - local_defs [dreg].i = interp_get_const_from_ldc_i4 (ins); - } else if (MINT_IS_LDC_I8 (opcode)) { - local_defs [dreg].type = LOCAL_VALUE_I8; - local_defs [dreg].l = interp_get_const_from_ldc_i8 (ins); - } else if (opcode == MINT_LDC_R4) { - guint32 val_u = READ32 (&ins->data [0]); - float f = *(float*)(&val_u); - local_defs [dreg].type = LOCAL_VALUE_R4; - local_defs [dreg].f = f; - } else if (ins->opcode == MINT_LDPTR) { -#if SIZEOF_VOID_P == 8 - local_defs [dreg].type = LOCAL_VALUE_I8; - local_defs [dreg].l = (gint64)td->data_items [ins->data [0]]; -#else - local_defs [dreg].type = LOCAL_VALUE_I4; - local_defs [dreg].i = (gint32)td->data_items [ins->data [0]]; -#endif - } else if (MINT_IS_UNOP (opcode)) { - ins = interp_fold_unop (td, local_defs, ins); - } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode)) { - ins = interp_fold_unop_cond_br (td, bb, local_defs, ins); - } else if (MINT_IS_SIMD_CREATE (opcode)) { - ins = interp_fold_simd_create (td, bb, local_defs, ins); - } else if (MINT_IS_BINOP (opcode)) { - gboolean folded; - ins = interp_fold_binop (td, local_defs, ins, &folded); - if (!folded) { - int sreg = -1; - guint16 mov_op = 0; - if ((opcode == MINT_MUL_I4 || opcode == MINT_DIV_I4) && - local_defs [ins->sregs [1]].type == LOCAL_VALUE_I4 && - local_defs [ins->sregs [1]].i == 1) { - sreg = ins->sregs [0]; - mov_op = MINT_MOV_4; - } else if ((opcode == MINT_MUL_I8 || opcode == MINT_DIV_I8) && - local_defs [ins->sregs [1]].type == LOCAL_VALUE_I8 && - local_defs [ins->sregs [1]].l == 1) { - sreg = ins->sregs [0]; - mov_op = MINT_MOV_8; - } else if (opcode == MINT_MUL_I4 && - local_defs [ins->sregs [0]].type == LOCAL_VALUE_I4 && - local_defs [ins->sregs [0]].i == 1) { - sreg = ins->sregs [1]; - mov_op = MINT_MOV_4; - } else if (opcode == MINT_MUL_I8 && - local_defs [ins->sregs [0]].type == LOCAL_VALUE_I8 && - local_defs [ins->sregs [0]].l == 1) { - sreg = ins->sregs [1]; - mov_op = MINT_MOV_8; - } - if (sreg != -1) { - ins->opcode = mov_op; - ins->sregs [0] = sreg; - if (td->verbose_level) { - g_print ("Replace idempotent binop :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } - } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode)) { - ins = interp_fold_binop_cond_br (td, bb, local_defs, ins); - } else if (MINT_IS_LDIND (opcode)) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int local = ldloca->sregs [0]; - int mt = td->locals [local].mt; - if (mt != MINT_TYPE_VT) { - // LDIND cannot simply be replaced with a mov because it might also include a - // necessary conversion (especially when we do cprop and do moves between vars of - // different types). - int ldind_mt = interp_get_mt_for_ldind (opcode); - switch (ldind_mt) { - case MINT_TYPE_I1: ins->opcode = MINT_CONV_I1_I4; break; - case MINT_TYPE_U1: ins->opcode = MINT_CONV_U1_I4; break; - case MINT_TYPE_I2: ins->opcode = MINT_CONV_I2_I4; break; - case MINT_TYPE_U2: ins->opcode = MINT_CONV_U2_I4; break; - default: - ins->opcode = GINT_TO_OPCODE (get_mov_for_type (ldind_mt, FALSE)); - break; - } - local_ref_count [sregs [0]]--; - interp_ins_set_sreg (ins, local); - - if (td->verbose_level) { - g_print ("Replace ldloca/ldind pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } - } else if (MINT_IS_LDFLD (opcode)) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int mt = ins->opcode - MINT_LDFLD_I1; - int local = ldloca->sregs [0]; - // Allow ldloca instruction to be killed - local_ref_count [sregs [0]]--; - if (td->locals [local].mt == (ins->opcode - MINT_LDFLD_I1) && ins->data [0] == 0) { - // Replace LDLOCA + LDFLD with LDLOC, when the loading field represents - // the entire local. This is the case with loading the only field of an - // IntPtr. We don't handle value type loads. - ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mt, TRUE)); - // The dreg of the MOV is the same as the dreg of the LDFLD - sregs [0] = local; - } else { - // Add mov.src.off to load directly from the local var space without use of ldloca. - int foffset = ins->data [0]; - guint16 ldsize = 0; - if (mt == MINT_TYPE_VT) - ldsize = ins->data [1]; - - // This loads just a part of the local valuetype - ins = interp_insert_ins (td, ins, MINT_MOV_SRC_OFF); - interp_ins_set_dreg (ins, ins->prev->dreg); - interp_ins_set_sreg (ins, local); - ins->data [0] = GINT_TO_UINT16 (foffset); - ins->data [1] = GINT_TO_UINT16 (mt); - if (mt == MINT_TYPE_VT) - ins->data [2] = ldsize; - - interp_clear_ins (ins->prev); - } - - if (td->verbose_level) { - g_print ("Replace ldloca/ldfld pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } else if (opcode == MINT_INITOBJ) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int size = ins->data [0]; - int local = ldloca->sregs [0]; - // Replace LDLOCA + INITOBJ with or LDC - if (size <= 4) - ins->opcode = MINT_LDC_I4_0; - else if (size <= 8) - ins->opcode = MINT_LDC_I8_0; - else - ins->opcode = MINT_INITLOCAL; - local_ref_count [sregs [0]]--; - ins->dreg = local; - - if (td->verbose_level) { - g_print ("Replace ldloca/initobj pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } else if (opcode == MINT_LDOBJ_VT) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int ldsize = ins->data [0]; - int local = ldloca->sregs [0]; - local_ref_count [sregs [0]]--; - - if (ldsize == td->locals [local].size) { - // Replace LDLOCA + LDOBJ_VT with MOV_VT - ins->opcode = MINT_MOV_VT; - sregs [0] = local; - needs_retry = TRUE; - } else { - // This loads just a part of the local valuetype - ins = interp_insert_ins (td, ins, MINT_MOV_SRC_OFF); - interp_ins_set_dreg (ins, ins->prev->dreg); - interp_ins_set_sreg (ins, local); - ins->data [0] = 0; - ins->data [1] = MINT_TYPE_VT; - ins->data [2] = GINT_TO_UINT16 (ldsize); - - interp_clear_ins (ins->prev); - } - if (td->verbose_level) { - g_print ("Replace ldloca/ldobj_vt pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - } - } else if (opcode == MINT_STOBJ_VT || opcode == MINT_STOBJ_VT_NOREF) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int stsize = ins->data [0]; - int local = ldloca->sregs [0]; - - if (stsize == td->locals [local].size) { - // Replace LDLOCA + STOBJ_VT with MOV_VT - local_ref_count [sregs [0]]--; - ins->opcode = MINT_MOV_VT; - sregs [0] = sregs [1]; - ins->dreg = local; - needs_retry = TRUE; - - if (td->verbose_level) { - g_print ("Replace ldloca/stobj_vt pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - } - } - } else if (MINT_IS_STIND (opcode)) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int local = ldloca->sregs [0]; - int mt = td->locals [local].mt; - if (mt != MINT_TYPE_VT) { - // We have an 8 byte local, just replace the stind with a mov - local_ref_count [sregs [0]]--; - // We make the assumption that the STIND matches the local type - ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mt, TRUE)); - interp_ins_set_dreg (ins, local); - interp_ins_set_sreg (ins, sregs [1]); - - if (td->verbose_level) { - g_print ("Replace ldloca/stind pair :\n\t"); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } - } else if (MINT_IS_STFLD (opcode)) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int mt = ins->opcode - MINT_STFLD_I1; - int local = ldloca->sregs [0]; - local_ref_count [sregs [0]]--; - // Allow ldloca instruction to be killed - if (td->locals [local].mt == (ins->opcode - MINT_STFLD_I1) && ins->data [0] == 0) { - ins->opcode = GINT_TO_OPCODE (get_mov_for_type (mt, FALSE)); - // The sreg of the MOV is the same as the second sreg of the STFLD - ins->dreg = local; - sregs [0] = sregs [1]; - } else { - // Add mov.dst.off to store directly int the local var space without use of ldloca. - int foffset = ins->data [0]; - guint16 vtsize = 0; - if (mt == MINT_TYPE_VT) { - vtsize = ins->data [1]; - } -#ifdef NO_UNALIGNED_ACCESS - else { - // As with normal loads/stores we use memcpy for unaligned 8 byte accesses - if ((mt == MINT_TYPE_I8 || mt == MINT_TYPE_R8) && foffset % SIZEOF_VOID_P != 0) - vtsize = 8; - } -#endif - - // This stores just to part of the dest valuetype - ins = interp_insert_ins (td, ins, MINT_MOV_DST_OFF); - interp_ins_set_dreg (ins, local); - interp_ins_set_sreg (ins, sregs [1]); - ins->data [0] = GINT_TO_UINT16 (foffset); - ins->data [1] = GINT_TO_UINT16 (mt); - ins->data [2] = vtsize; - - interp_clear_ins (ins->prev); - } - if (td->verbose_level) { - g_print ("Replace ldloca/stfld pair (off %p) :\n\t", (void *)(uintptr_t) ldloca->il_offset); - dump_interp_inst (ins, td->data_items); - } - needs_retry = TRUE; - } - } else if (opcode == MINT_GETITEM_SPAN) { - InterpInst *ldloca = local_defs [sregs [0]].ins; - if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { - int local = ldloca->sregs [0]; - // Allow ldloca instruction to be killed - local_ref_count [sregs [0]]--; - // Instead of loading from the indirect pointer pass directly the vt var - ins->opcode = MINT_GETITEM_LOCALSPAN; - sregs [0] = local; - needs_retry = TRUE; - } - } else if (opcode == MINT_CKNULL) { - InterpInst *def = local_defs [sregs [0]].ins; - if (def && def->opcode == MINT_LDLOCA_S) { - // CKNULL on LDLOCA is a NOP - ins->opcode = MINT_MOV_P; - needs_retry = TRUE; - } - } else if (opcode == MINT_BOX) { - // TODO Add more relevant opcodes - local_defs [dreg].type = LOCAL_VALUE_NON_NULL; - } - - ins_index++; - } - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) - foreach_local_var (td, ins, local_defs, clear_unused_defs); - } - - needs_retry |= interp_local_deadce (td); - if (mono_interp_opt & INTERP_OPT_BBLOCKS) - needs_retry |= interp_optimize_bblocks (td); - - if (needs_retry) - goto retry; - - g_free (local_defs); -} - -void -mono_test_interp_cprop (TransformData *td) -{ - interp_cprop (td); -} - -static gboolean -get_sreg_imm (TransformData *td, int sreg, gint16 *imm, int result_mt) -{ - InterpInst *def = td->locals [sreg].def; - if (def != NULL && td->local_ref_count [sreg] == 1) { - gint64 ct; - if (MINT_IS_LDC_I4 (def->opcode)) - ct = interp_get_const_from_ldc_i4 (def); - else if (MINT_IS_LDC_I8 (def->opcode)) - ct = interp_get_const_from_ldc_i8 (def); - else - return FALSE; - gint64 min_val, max_val; - // We only propagate the immediate only if it fits into the desired type, - // so we don't accidentaly handle conversions wrong - switch (result_mt) { - case MINT_TYPE_I1: - min_val = G_MININT8; - max_val = G_MAXINT8; - break; - case MINT_TYPE_I2: - min_val = G_MININT16; - max_val = G_MAXINT16; - break; - case MINT_TYPE_U1: - min_val = 0; - max_val = G_MAXUINT8; - break; - case MINT_TYPE_U2: - min_val = 0; - max_val = G_MAXINT16; - break; - default: - g_assert_not_reached (); - - } - if (ct >= min_val && ct <= max_val) { - *imm = (gint16)ct; - mono_interp_stats.super_instructions++; - return TRUE; - } - } - return FALSE; -} - -static int -get_binop_condbr_imm_sp (int opcode) -{ - switch (opcode) { - case MINT_BEQ_I4: return MINT_BEQ_I4_IMM_SP; - case MINT_BEQ_I8: return MINT_BEQ_I8_IMM_SP; - case MINT_BGE_I4: return MINT_BGE_I4_IMM_SP; - case MINT_BGE_I8: return MINT_BGE_I8_IMM_SP; - case MINT_BGT_I4: return MINT_BGT_I4_IMM_SP; - case MINT_BGT_I8: return MINT_BGT_I8_IMM_SP; - case MINT_BLT_I4: return MINT_BLT_I4_IMM_SP; - case MINT_BLT_I8: return MINT_BLT_I8_IMM_SP; - case MINT_BLE_I4: return MINT_BLE_I4_IMM_SP; - case MINT_BLE_I8: return MINT_BLE_I8_IMM_SP; - case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_IMM_SP; - case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_IMM_SP; - case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_IMM_SP; - case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_IMM_SP; - case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_IMM_SP; - case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_IMM_SP; - case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_IMM_SP; - case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_IMM_SP; - case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_IMM_SP; - case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_IMM_SP; - default: return MINT_NOP; - } -} - -static int -get_binop_condbr_sp (int opcode) -{ - switch (opcode) { - case MINT_BEQ_I4: return MINT_BEQ_I4_SP; - case MINT_BEQ_I8: return MINT_BEQ_I8_SP; - case MINT_BGE_I4: return MINT_BGE_I4_SP; - case MINT_BGE_I8: return MINT_BGE_I8_SP; - case MINT_BGT_I4: return MINT_BGT_I4_SP; - case MINT_BGT_I8: return MINT_BGT_I8_SP; - case MINT_BLT_I4: return MINT_BLT_I4_SP; - case MINT_BLT_I8: return MINT_BLT_I8_SP; - case MINT_BLE_I4: return MINT_BLE_I4_SP; - case MINT_BLE_I8: return MINT_BLE_I8_SP; - case MINT_BNE_UN_I4: return MINT_BNE_UN_I4_SP; - case MINT_BNE_UN_I8: return MINT_BNE_UN_I8_SP; - case MINT_BGE_UN_I4: return MINT_BGE_UN_I4_SP; - case MINT_BGE_UN_I8: return MINT_BGE_UN_I8_SP; - case MINT_BGT_UN_I4: return MINT_BGT_UN_I4_SP; - case MINT_BGT_UN_I8: return MINT_BGT_UN_I8_SP; - case MINT_BLE_UN_I4: return MINT_BLE_UN_I4_SP; - case MINT_BLE_UN_I8: return MINT_BLE_UN_I8_SP; - case MINT_BLT_UN_I4: return MINT_BLT_UN_I4_SP; - case MINT_BLT_UN_I8: return MINT_BLT_UN_I8_SP; - default: return MINT_NOP; - } -} - -static int -get_unop_condbr_sp (int opcode) -{ - switch (opcode) { - case MINT_BRFALSE_I4: return MINT_BRFALSE_I4_SP; - case MINT_BRFALSE_I8: return MINT_BRFALSE_I8_SP; - case MINT_BRTRUE_I4: return MINT_BRTRUE_I4_SP; - case MINT_BRTRUE_I8: return MINT_BRTRUE_I8_SP; - default: return MINT_NOP; - } -} - -static void -interp_super_instructions (TransformData *td) -{ - InterpBasicBlock *bb; - int *local_ref_count = td->local_ref_count; - - compute_native_offset_estimates (td); - - // Add some actual super instructions - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - InterpInst *ins; - int noe; - - // Set cbb since we do some instruction inserting below - td->cbb = bb; - noe = bb->native_offset_estimate; - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - int opcode = ins->opcode; - if (MINT_IS_NOP (opcode)) - continue; - if (mono_interp_op_dregs [opcode] && !(td->locals [ins->dreg].flags & INTERP_LOCAL_FLAG_GLOBAL)) - td->locals [ins->dreg].def = ins; - - if (opcode == MINT_RET || (opcode >= MINT_RET_I1 && opcode <= MINT_RET_U2)) { - // ldc + ret -> ret.imm - int sreg = ins->sregs [0]; - gint16 imm; - if (get_sreg_imm (td, sreg, &imm, (opcode == MINT_RET) ? MINT_TYPE_I2 : opcode - MINT_RET_I1)) { - InterpInst *def = td->locals [sreg].def; - int ret_op = MINT_IS_LDC_I4 (def->opcode) ? MINT_RET_I4_IMM : MINT_RET_I8_IMM; - InterpInst *new_inst = interp_insert_ins (td, ins, ret_op); - new_inst->data [0] = imm; - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg]--; - - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } else if (opcode == MINT_ADD_I4 || opcode == MINT_ADD_I8 || - opcode == MINT_MUL_I4 || opcode == MINT_MUL_I8) { - int sreg = -1; - int sreg_imm = -1; - gint16 imm; - if (get_sreg_imm (td, ins->sregs [0], &imm, MINT_TYPE_I2)) { - sreg = ins->sregs [1]; - sreg_imm = ins->sregs [0]; - } else if (get_sreg_imm (td, ins->sregs [1], &imm, MINT_TYPE_I2)) { - sreg = ins->sregs [0]; - sreg_imm = ins->sregs [1]; - } - if (sreg != -1) { - int binop; - switch (opcode) { - case MINT_ADD_I4: binop = MINT_ADD_I4_IMM; break; - case MINT_ADD_I8: binop = MINT_ADD_I8_IMM; break; - case MINT_MUL_I4: binop = MINT_MUL_I4_IMM; break; - case MINT_MUL_I8: binop = MINT_MUL_I8_IMM; break; - default: g_assert_not_reached (); - } - InterpInst *new_inst = interp_insert_ins (td, ins, binop); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = sreg; - new_inst->data [0] = imm; - interp_clear_ins (td->locals [sreg_imm].def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } else if (opcode == MINT_SUB_I4 || opcode == MINT_SUB_I8) { - // ldc + sub -> add.-imm - gint16 imm; - int sreg_imm = ins->sregs [1]; - if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2) && imm != G_MININT16) { - int add_op = opcode == MINT_SUB_I4 ? MINT_ADD_I4_IMM : MINT_ADD_I8_IMM; - InterpInst *new_inst = interp_insert_ins (td, ins, add_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; - new_inst->data [0] = -imm; - interp_clear_ins (td->locals [sreg_imm].def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } else if (opcode == MINT_MUL_I4_IMM || opcode == MINT_MUL_I8_IMM) { - int sreg = ins->sregs [0]; - InterpInst *def = td->locals [sreg].def; - if (def != NULL && td->local_ref_count [sreg] == 1) { - gboolean is_i4 = opcode == MINT_MUL_I4_IMM; - if ((is_i4 && def->opcode == MINT_ADD_I4_IMM) || - (!is_i4 && def->opcode == MINT_ADD_I8_IMM)) { - InterpInst *new_inst = interp_insert_ins (td, ins, is_i4 ? MINT_ADD_MUL_I4_IMM : MINT_ADD_MUL_I8_IMM); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = def->sregs [0]; - new_inst->data [0] = def->data [0]; - new_inst->data [1] = ins->data [0]; - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_BINOP_SHIFT (opcode)) { - gint16 imm; - int sreg_imm = ins->sregs [1]; - if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { - // ldc + sh -> sh.imm - int shift_op = MINT_SHR_UN_I4_IMM + (opcode - MINT_SHR_UN_I4); - InterpInst *new_inst = interp_insert_ins (td, ins, shift_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; - new_inst->data [0] = imm; - interp_clear_ins (td->locals [sreg_imm].def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } else if (opcode == MINT_SHL_I4 || opcode == MINT_SHL_I8) { - int amount_var = ins->sregs [1]; - InterpInst *amount_def = td->locals [amount_var].def; - if (amount_def != NULL && td->local_ref_count [amount_var] == 1 && amount_def->opcode == MINT_AND_I4) { - int mask_var = amount_def->sregs [1]; - if (get_sreg_imm (td, mask_var, &imm, MINT_TYPE_I2)) { - // ldc + and + shl -> shl_and_imm - int new_opcode = -1; - if (opcode == MINT_SHL_I4 && imm == 31) - new_opcode = MINT_SHL_AND_I4; - else if (opcode == MINT_SHL_I8 && imm == 63) - new_opcode = MINT_SHL_AND_I8; - - if (new_opcode != -1) { - InterpInst *new_inst = interp_insert_ins (td, ins, new_opcode); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; - new_inst->sregs [1] = amount_def->sregs [0]; - - local_ref_count [amount_var]--; - local_ref_count [mask_var]--; - - interp_clear_ins (td->locals [mask_var].def); - interp_clear_ins (amount_def); - interp_clear_ins (ins); - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } - } - } else if (opcode == MINT_DIV_UN_I4 || opcode == MINT_DIV_UN_I8) { - // ldc + div.un -> shr.imm - int sreg_imm = ins->sregs [1]; - InterpInst *def = td->locals [sreg_imm].def; - if (def != NULL && td->local_ref_count [sreg_imm] == 1) { - int power2 = -1; - if (MINT_IS_LDC_I4 (def->opcode)) { - guint32 ct = interp_get_const_from_ldc_i4 (def); - power2 = mono_is_power_of_two ((guint32)ct); - } else if (MINT_IS_LDC_I8 (def->opcode)) { - guint64 ct = interp_get_const_from_ldc_i8 (def); - if (ct < G_MAXUINT32) - power2 = mono_is_power_of_two ((guint32)ct); - } - if (power2 > 0) { - InterpInst *new_inst; - if (opcode == MINT_DIV_UN_I4) - new_inst = interp_insert_ins (td, ins, MINT_SHR_UN_I4_IMM); - else - new_inst = interp_insert_ins (td, ins, MINT_SHR_UN_I8_IMM); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; - new_inst->data [0] = GINT_TO_UINT16 (power2); - - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("lower div.un: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_LDIND_INT (opcode)) { - int sreg_base = ins->sregs [0]; - InterpInst *def = td->locals [sreg_base].def; - if (def != NULL && td->local_ref_count [sreg_base] == 1) { - InterpInst *new_inst = NULL; - if (def->opcode == MINT_ADD_P) { - int ldind_offset_op = MINT_LDIND_OFFSET_I1 + (opcode - MINT_LDIND_I1); - new_inst = interp_insert_ins (td, ins, ldind_offset_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = def->sregs [0]; // base - new_inst->sregs [1] = def->sregs [1]; // off - } else if (def->opcode == MINT_ADD_P_IMM) { - int ldind_offset_imm_op = MINT_LDIND_OFFSET_IMM_I1 + (opcode - MINT_LDIND_I1); - new_inst = interp_insert_ins (td, ins, ldind_offset_imm_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = def->sregs [0]; // base - new_inst->data [0] = def->data [0]; // imm value - } - if (new_inst) { - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_base]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_LDIND_OFFSET (opcode)) { - int sreg_off = ins->sregs [1]; - InterpInst *def = td->locals [sreg_off].def; - if (def != NULL && td->local_ref_count [sreg_off] == 1) { - if (def->opcode == MINT_MUL_P_IMM || def->opcode == MINT_ADD_P_IMM || def->opcode == MINT_ADD_MUL_P_IMM) { - int ldind_offset_op = MINT_LDIND_OFFSET_ADD_MUL_IMM_I1 + (opcode - MINT_LDIND_OFFSET_I1); - InterpInst *new_inst = interp_insert_ins (td, ins, ldind_offset_op); - new_inst->dreg = ins->dreg; - new_inst->sregs [0] = ins->sregs [0]; // base - new_inst->sregs [1] = def->sregs [0]; // off - - // set the add and mul immediates - switch (def->opcode) { - case MINT_ADD_P_IMM: - new_inst->data [0] = def->data [0]; - new_inst->data [1] = 1; - break; - case MINT_MUL_P_IMM: - new_inst->data [0] = 0; - new_inst->data [1] = def->data [0]; - break; - case MINT_ADD_MUL_P_IMM: - new_inst->data [0] = def->data [0]; - new_inst->data [1] = def->data [1]; - break; - } - - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_off]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("method %s:%s, superins: ", m_class_get_name (td->method->klass), td->method->name); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_STIND_INT (opcode)) { - int sreg_base = ins->sregs [0]; - InterpInst *def = td->locals [sreg_base].def; - if (def != NULL && td->local_ref_count [sreg_base] == 1) { - InterpInst *new_inst = NULL; - if (def->opcode == MINT_ADD_P) { - int stind_offset_op = MINT_STIND_OFFSET_I1 + (opcode - MINT_STIND_I1); - new_inst = interp_insert_ins (td, ins, stind_offset_op); - new_inst->sregs [0] = def->sregs [0]; // base - new_inst->sregs [1] = def->sregs [1]; // off - new_inst->sregs [2] = ins->sregs [1]; // value - } else if (def->opcode == MINT_ADD_P_IMM) { - int stind_offset_imm_op = MINT_STIND_OFFSET_IMM_I1 + (opcode - MINT_STIND_I1); - new_inst = interp_insert_ins (td, ins, stind_offset_imm_op); - new_inst->sregs [0] = def->sregs [0]; // base - new_inst->sregs [1] = ins->sregs [1]; // value - new_inst->data [0] = def->data [0]; // imm value - } - if (new_inst) { - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_base]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - } else if (MINT_IS_LDFLD (opcode)) { - // cknull + ldfld -> ldfld - // FIXME This optimization is very limited, it is meant mainly to remove cknull - // when inlining property accessors. We should have more advanced cknull removal - // optimzations, so we can catch cases where instructions are not next to each other. - int obj_sreg = ins->sregs [0]; - InterpInst *def = td->locals [obj_sreg].def; - if (def != NULL && def->opcode == MINT_CKNULL && interp_prev_ins (ins) == def && - def->dreg == obj_sreg && local_ref_count [obj_sreg] == 1) { - if (td->verbose_level) { - g_print ("remove redundant cknull (%s): ", td->method->name); - dump_interp_inst (def, td->data_items); - } - ins->sregs [0] = def->sregs [0]; - interp_clear_ins (def); - local_ref_count [obj_sreg]--; - mono_interp_stats.super_instructions++; - } - } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode) && is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { - gint16 imm; - int sreg_imm = ins->sregs [1]; - if (get_sreg_imm (td, sreg_imm, &imm, MINT_TYPE_I2)) { - int condbr_op = get_binop_condbr_imm_sp (opcode); - if (condbr_op != MINT_NOP) { - InterpInst *prev_ins = interp_prev_ins (ins); - // The new instruction does a safepoint - if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) - interp_clear_ins (prev_ins); - InterpInst *new_ins = interp_insert_ins (td, ins, condbr_op); - new_ins->sregs [0] = ins->sregs [0]; - new_ins->data [0] = imm; - new_ins->info.target_bb = ins->info.target_bb; - interp_clear_ins (td->locals [sreg_imm].def); - interp_clear_ins (ins); - local_ref_count [sreg_imm]--; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_ins, td->data_items); - } - } - } else { - InterpInst *prev_ins = interp_prev_ins (ins); - if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) { - int condbr_op = get_binop_condbr_sp (opcode); - if (condbr_op != MINT_NOP) { - interp_clear_ins (prev_ins); - ins->opcode = GINT_TO_OPCODE (condbr_op); - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (ins, td->data_items); - } - } - } - } - } else if (MINT_IS_UNOP_CONDITIONAL_BRANCH (opcode) && is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { - if (opcode == MINT_BRFALSE_I4 || opcode == MINT_BRTRUE_I4) { - gboolean negate = opcode == MINT_BRFALSE_I4; - int cond_sreg = ins->sregs [0]; - InterpInst *def = td->locals [cond_sreg].def; - if (def != NULL && local_ref_count [cond_sreg] == 1) { - int replace_opcode = -1; - switch (def->opcode) { - case MINT_CEQ_I4: replace_opcode = negate ? MINT_BNE_UN_I4 : MINT_BEQ_I4; break; - case MINT_CEQ_I8: replace_opcode = negate ? MINT_BNE_UN_I8 : MINT_BEQ_I8; break; - case MINT_CGT_I4: replace_opcode = negate ? MINT_BLE_I4 : MINT_BGT_I4; break; - case MINT_CGT_I8: replace_opcode = negate ? MINT_BLE_I8 : MINT_BGT_I8; break; - case MINT_CLT_I4: replace_opcode = negate ? MINT_BGE_I4 : MINT_BLT_I4; break; - case MINT_CLT_I8: replace_opcode = negate ? MINT_BGE_I8 : MINT_BLT_I8; break; - case MINT_CGT_UN_I4: replace_opcode = negate ? MINT_BLE_UN_I4 : MINT_BGT_UN_I4; break; - case MINT_CGT_UN_I8: replace_opcode = negate ? MINT_BLE_UN_I8 : MINT_BGT_UN_I8; break; - case MINT_CLT_UN_I4: replace_opcode = negate ? MINT_BGE_UN_I4 : MINT_BLT_UN_I4; break; - case MINT_CLT_UN_I8: replace_opcode = negate ? MINT_BGE_UN_I8 : MINT_BLT_UN_I8; break; - case MINT_CEQ_R4: replace_opcode = negate ? MINT_BNE_UN_R4 : MINT_BEQ_R4; break; - case MINT_CEQ_R8: replace_opcode = negate ? MINT_BNE_UN_R8 : MINT_BEQ_R8; break; - case MINT_CGT_R4: replace_opcode = negate ? MINT_BLE_UN_R4 : MINT_BGT_R4; break; - case MINT_CGT_R8: replace_opcode = negate ? MINT_BLE_UN_R8 : MINT_BGT_R8; break; - case MINT_CLT_R4: replace_opcode = negate ? MINT_BGE_UN_R4 : MINT_BLT_R4; break; - case MINT_CLT_R8: replace_opcode = negate ? MINT_BGE_UN_R8 : MINT_BLT_R8; break; - case MINT_CGT_UN_R4: replace_opcode = negate ? MINT_BLE_R4 : MINT_BGT_UN_R4; break; - case MINT_CGT_UN_R8: replace_opcode = negate ? MINT_BLE_R8 : MINT_BGT_UN_R8; break; - case MINT_CLT_UN_R4: replace_opcode = negate ? MINT_BGE_R4 : MINT_BLT_UN_R4; break; - case MINT_CLT_UN_R8: replace_opcode = negate ? MINT_BGE_R8 : MINT_BLT_UN_R8; break; - case MINT_CEQ0_I4: replace_opcode = negate ? MINT_BRTRUE_I4 : MINT_BRFALSE_I4; break; // If def->opcode is MINT_CEQ0_I4 ins->opcode is inverted - // Add more opcodes - default: - break; - } - if (replace_opcode != -1) { - ins->opcode = GINT_TO_UINT16 (replace_opcode); - ins->sregs [0] = def->sregs [0]; - if (def->opcode != MINT_CEQ0_I4) - ins->sregs [1] = def->sregs [1]; - interp_clear_ins (def); - local_ref_count [cond_sreg]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (ins, td->data_items); - } - // The newly added opcode could be part of further superinstructions. Retry - ins = ins->prev; - continue; - } - } - } - InterpInst *prev_ins = interp_prev_ins (ins); - if (prev_ins && prev_ins->opcode == MINT_SAFEPOINT) { - int condbr_op = get_unop_condbr_sp (opcode); - if (condbr_op != MINT_NOP) { - interp_clear_ins (prev_ins); - ins->opcode = GINT_TO_OPCODE (condbr_op); - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (ins, td->data_items); - } - } - } - } else if (opcode == MINT_STOBJ_VT_NOREF) { - int sreg_src = ins->sregs [1]; - InterpInst *def = td->locals [sreg_src].def; - if (def != NULL && interp_prev_ins (ins) == def && def->opcode == MINT_LDOBJ_VT && ins->data [0] == def->data [0] && td->local_ref_count [sreg_src] == 1) { - InterpInst *new_inst = interp_insert_ins (td, ins, MINT_CPOBJ_VT_NOREF); - new_inst->sregs [0] = ins->sregs [0]; // dst - new_inst->sregs [1] = def->sregs [0]; // src - new_inst->data [0] = ins->data [0]; // size - - interp_clear_ins (def); - interp_clear_ins (ins); - local_ref_count [sreg_src]--; - mono_interp_stats.super_instructions++; - if (td->verbose_level) { - g_print ("superins: "); - dump_interp_inst (new_inst, td->data_items); - } - } - } - noe += get_inst_length (ins); - } - } -} - -static void initialize_global_vars (TransformData *td); - -static void -interp_optimize_code (TransformData *td) -{ - if (mono_interp_opt & INTERP_OPT_BBLOCKS) - interp_optimize_bblocks (td); - - if (mono_interp_opt & INTERP_OPT_CPROP) - MONO_TIME_TRACK (mono_interp_stats.cprop_time, interp_cprop (td)); - - // After this point control optimizations on control flow can no longer happen, so we can determine - // which vars are global. This helps speed up the super instructions pass, which only operates on - // single def, single use local vars. - initialize_global_vars (td); - - if ((mono_interp_opt & INTERP_OPT_SUPER_INSTRUCTIONS) && - (mono_interp_opt & INTERP_OPT_CPROP)) - MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td)); -} - -static void -set_var_live_range (TransformData *td, int var, int ins_index) -{ - // We don't track liveness yet for global vars - if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) - return; - if (td->locals [var].live_start == -1) - td->locals [var].live_start = ins_index; - td->locals [var].live_end = ins_index; -} - -static void -set_var_live_range_cb (TransformData *td, int var, gpointer data) -{ - set_var_live_range (td, var, (int)(gsize)data); -} - -static void -initialize_global_var (TransformData *td, int var, int bb_index) -{ - // Check if already handled - if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) - return; - - if (td->locals [var].bb_index == -1) { - td->locals [var].bb_index = bb_index; - } else if (td->locals [var].bb_index != bb_index) { - // var used in multiple basic blocks - if (td->verbose_level) - g_print ("alloc global var %d to offset %d\n", var, td->total_locals_size); - alloc_global_var_offset (td, var); - td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; - } -} - -static void -initialize_global_var_cb (TransformData *td, int var, gpointer data) -{ - initialize_global_var (td, var, (int)(gsize)data); -} - -static void -initialize_global_vars (TransformData *td) -{ - InterpBasicBlock *bb; - - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - InterpInst *ins; - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - int opcode = ins->opcode; - if (opcode == MINT_NOP) { - continue; - } else if (opcode == MINT_LDLOCA_S) { - int var = ins->sregs [0]; - // If global flag is set, it means its offset was already allocated - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL)) { - if (td->verbose_level) - g_print ("alloc ldloca global var %d to offset %d\n", var, td->total_locals_size); - alloc_global_var_offset (td, var); - td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; - } - } - foreach_local_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb); - } - } - td->total_locals_size = ALIGN_TO (td->total_locals_size, MINT_STACK_ALIGNMENT); -} - -// Data structure used for offset allocation of call args -typedef struct { - InterpInst **active_calls; - int active_calls_count; - int active_calls_capacity; - // A deferred call stack implemented as a linked list - GSList *deferred_calls; -} ActiveCalls; - -static void -init_active_calls (TransformData *td, ActiveCalls *ac) -{ - ac->active_calls_count = 0; - ac->active_calls_capacity = 5; - ac->active_calls = (InterpInst**)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (InterpInst*)); - ac->deferred_calls = NULL; -} - -static void -reinit_active_calls (TransformData *td, ActiveCalls *ac) -{ - ac->active_calls_count = 0; - ac->deferred_calls = NULL; -} - -static void -add_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) -{ - // Check if already added - if (call->flags & INTERP_INST_FLAG_ACTIVE_CALL) - return; - - if (ac->active_calls_count == ac->active_calls_capacity) { - InterpInst **old = ac->active_calls; - ac->active_calls_capacity *= 2; - ac->active_calls = (InterpInst**)mono_mempool_alloc (td->mempool, ac->active_calls_capacity * sizeof (InterpInst*)); - memcpy (ac->active_calls, old, ac->active_calls_count * sizeof (InterpInst*)); - } - ac->active_calls [ac->active_calls_count] = call; - ac->active_calls_count++; - - // Mark a flag on it so we don't have to lookup the array with every argument store. - call->flags |= INTERP_INST_FLAG_ACTIVE_CALL; -} - -/** - * Function allocates offsets of resolved calls following a constraint - * where the base offset of a call must be greater than the offset of any argument of other active call args. - * - * Function first removes the call from an array of active calls. If a match is found, - * the call is removed from the array by moving the last entry into its place. Otherwise, it is a call without arguments. - * - * If there are active calls, the call in question is push onto the stack as a deferred call. - * The call contains a list of other active calls on which it depends. Those calls need to be resolved first in order to determine optimal base offset for the call in question. - * Otherwise, if there are no active calls, function starts resolving the call in question and deferred calls from the stack. - * - * For each call, function computes the base offset, the offset of each call argument starting from a base offset, and stores the computed call offset into a InterpInst. - * The base offset is computed as max offset of all call offsets on which the call depends. - * Stack ensures that all call offsets on which the call depends are calculated before the call in question, by deferring calls from the last to the first one. - */ -static void -end_active_call (TransformData *td, ActiveCalls *ac, InterpInst *call) -{ - // Remove call from array - for (int i = 0; i < ac->active_calls_count; i++) { - if (ac->active_calls [i] == call) { - ac->active_calls_count--; - // Since this entry is removed, move the last entry into it - if (ac->active_calls_count > 0 && i < ac->active_calls_count) - ac->active_calls [i] = ac->active_calls [ac->active_calls_count]; - break; - } - } - - // Push active call that should be resolved onto the stack - call->info.call_info->call_deps = NULL; - if (ac->active_calls_count) { - for (int i = 0; i < ac->active_calls_count; i++) - call->info.call_info->call_deps = g_slist_prepend_mempool (td->mempool, call->info.call_info->call_deps, ac->active_calls [i]); - ac->deferred_calls = g_slist_prepend_mempool (td->mempool, ac->deferred_calls, call); - } else { - // If no other active calls, current active call and all deferred calls can be resolved from the stack - InterpInst *deferred_call = call; - while (deferred_call) { - // `base_offset` is a relative offset (to the start of the call args stack) where the args for this call reside. - // The deps for a call represent the list of active calls at the moment when the call ends. This means that all deps for a call end after the call in question. - // Given we iterate over the list of deferred calls from the last to the first one to end, all deps of a call are guaranteed to have been processed at this point. - int base_offset = 0; - for (GSList *list = deferred_call->info.call_info->call_deps; list; list = list->next) { - int end_offset = ((InterpInst*)list->data)->info.call_info->call_end_offset; - if (end_offset > base_offset) - base_offset = end_offset; - } - deferred_call->info.call_info->call_offset = base_offset; - // Compute to offset of each call argument - int *call_args = deferred_call->info.call_info->call_args; - if (call_args && (*call_args != -1)) { - int var = *call_args; - while (var != -1) { - alloc_var_offset (td, var, &base_offset); - call_args++; - var = *call_args; - } - } - deferred_call->info.call_info->call_end_offset = ALIGN_TO (base_offset, MINT_STACK_ALIGNMENT); - - if (ac->deferred_calls) { - deferred_call = (InterpInst*) ac->deferred_calls->data; - ac->deferred_calls = ac->deferred_calls->next; - } else - deferred_call = NULL; - } - } -} - -// Data structure used for offset allocation of local vars - -typedef struct { - int var; - gboolean is_alive; -} ActiveVar; - -typedef struct { - ActiveVar *active_vars; - int active_vars_count; - int active_vars_capacity; -} ActiveVars; - -static void -init_active_vars (TransformData *td, ActiveVars *av) -{ - av->active_vars_count = 0; - av->active_vars_capacity = MAX (td->locals_size / td->bb_count, 10); - av->active_vars = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVars)); -} - -static void -reinit_active_vars (TransformData *td, ActiveVars *av) -{ - av->active_vars_count = 0; -} - -static void -add_active_var (TransformData *td, ActiveVars *av, int var) -{ - if (av->active_vars_count == av->active_vars_capacity) { - av->active_vars_capacity *= 2; - ActiveVar *new_array = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVar)); - memcpy (new_array, av->active_vars, av->active_vars_count * sizeof (ActiveVar)); - av->active_vars = new_array; - } - av->active_vars [av->active_vars_count].var = var; - av->active_vars [av->active_vars_count].is_alive = TRUE; - av->active_vars_count++; -} - -static void -end_active_var (TransformData *td, ActiveVars *av, int var) -{ - // Iterate over active vars, set the entry associated with var as !is_alive - for (int i = 0; i < av->active_vars_count; i++) { - if (av->active_vars [i].var == var) { - av->active_vars [i].is_alive = FALSE; - return; - } - } -} - -static void -compact_active_vars (TransformData *td, ActiveVars *av, gint32 *current_offset) -{ - if (!av->active_vars_count) - return; - int i = av->active_vars_count - 1; - while (i >= 0 && !av->active_vars [i].is_alive) { - av->active_vars_count--; - *current_offset = td->locals [av->active_vars [i].var].offset; - i--; - } -} - -static void -dump_active_vars (TransformData *td, ActiveVars *av) -{ - if (td->verbose_level) { - g_print ("active :"); - for (int i = 0; i < av->active_vars_count; i++) { - if (av->active_vars [i].is_alive) - g_print (" %d (end %d),", av->active_vars [i].var, td->locals [av->active_vars [i].var].live_end); - } - g_print ("\n"); - } -} - -static void -interp_alloc_offsets (TransformData *td) -{ - InterpBasicBlock *bb; - ActiveCalls ac; - ActiveVars av; - - if (td->verbose_level) - g_print ("\nvar offset allocator iteration\n"); - - initialize_global_vars (td); - - init_active_vars (td, &av); - init_active_calls (td, &ac); - - int final_total_locals_size = td->total_locals_size; - // We now have the top of stack offset. All local regs are allocated after this offset, with each basic block - for (bb = td->entry_bb; bb != NULL; bb = bb->next_bb) { - InterpInst *ins; - int ins_index = 0; - if (td->verbose_level) - g_print ("BB%d\n", bb->index); - - reinit_active_calls (td, &ac); - reinit_active_vars (td, &av); - - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - if (ins->opcode == MINT_NOP) - continue; - if (ins->opcode == MINT_NEWOBJ || ins->opcode == MINT_NEWOBJ_VT || - ins->opcode == MINT_NEWOBJ_SLOW || ins->opcode == MINT_NEWOBJ_STRING) { - // The offset allocator assumes that the liveness of destination var starts - // after the source vars, which means the destination var can be allocated - // at the same offset as some of the arguments. However, for newobj opcodes, - // the created object is set before the call is made. We solve this by making - // sure that the dreg is not allocated in the param area, so there is no - // risk of conflicts. - td->locals [ins->dreg].flags |= INTERP_LOCAL_FLAG_NO_CALL_ARGS; - } - if (ins->flags & INTERP_INST_FLAG_CALL) { - if (ins->info.call_info && ins->info.call_info->call_args) { - int *call_args = ins->info.call_info->call_args; - guint16 pair_sregs [MINT_MOV_PAIRS_MAX]; - guint16 pair_dregs [MINT_MOV_PAIRS_MAX]; - int num_pairs = 0; - int var = *call_args; - - while (var != -1) { - if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL || - !td->local_ref_count || td->local_ref_count [var] > 1 || - td->locals [var].flags & INTERP_LOCAL_FLAG_NO_CALL_ARGS) { - // Some vars can't be allocated on the call args stack, since the constraint is that - // call args vars die after the call. This isn't necessarily true for global vars or - // vars that are used by other instructions aside from the call. - // We need to copy the var into a new tmp var - int new_var = create_interp_local (td, td->locals [var].type); - td->locals [new_var].call = ins; - td->locals [new_var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; - - int mt = mono_mint_type (td->locals [var].type); - if (mt != MINT_TYPE_VT && num_pairs < MINT_MOV_PAIRS_MAX && var <= G_MAXUINT16 && new_var <= G_MAXUINT16) { - // We store these in the instruction data slots so we do this optimizations only if they fit - pair_sregs [num_pairs] = (guint16)var; - pair_dregs [num_pairs] = (guint16)new_var; - num_pairs++; - // The arg of the call is no longer global - *call_args = new_var; - } else { - int opcode = get_mov_for_type (mt, FALSE); - InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); - interp_ins_set_dreg (new_inst, new_var); - interp_ins_set_sreg (new_inst, var); - if (opcode == MINT_MOV_VT) - new_inst->data [0] = GINT_TO_UINT16 (td->locals [var].size); - // The arg of the call is no longer global - *call_args = new_var; - // Also update liveness for this instruction - foreach_local_var (td, new_inst, (gpointer)(gsize)ins_index, set_var_live_range_cb); - ins_index++; - } - } else { - // Flag this var as it has special storage on the call args stack - td->locals [var].call = ins; - td->locals [var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; - } - call_args++; - var = *call_args; - } - if (num_pairs > 0) { - int i; - for (i = 0; i < num_pairs; i++) { - set_var_live_range (td, pair_sregs [i], ins_index); - set_var_live_range (td, pair_dregs [i], ins_index); - } - if (num_pairs == 1) { - int mt = mono_mint_type (td->locals [pair_sregs [0]].type); - int opcode = get_mov_for_type (mt, FALSE); - InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); - interp_ins_set_dreg (new_inst, pair_dregs [0]); - interp_ins_set_sreg (new_inst, pair_sregs [0]); - } else { - // Squash together multiple moves to the param area into a single opcode - int opcode = MINT_MOV_8_2 + num_pairs - 2; - InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); - int k = 0; - for (i = 0; i < num_pairs; i++) { - new_inst->data [k++] = pair_dregs [i]; - new_inst->data [k++] = pair_sregs [i]; - } - } - ins_index++; - } - } - } - // Set live_start and live_end for every referenced local that is not global - foreach_local_var (td, ins, (gpointer)(gsize)ins_index, set_var_live_range_cb); - ins_index++; - } - gint32 current_offset = td->total_locals_size; - - ins_index = 0; - for (ins = bb->first_ins; ins != NULL; ins = ins->next) { - int opcode = ins->opcode; - gboolean is_call = ins->flags & INTERP_INST_FLAG_CALL; - - if (opcode == MINT_NOP) - continue; - - if (td->verbose_level) { - g_print ("\tins_index %d\t", ins_index); - dump_interp_inst (ins, td->data_items); - } - - // Expire source vars. We first mark them as not alive and then compact the array - for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { - int var = ins->sregs [i]; - if (var == MINT_CALL_ARGS_SREG) - continue; - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) { - g_assert (!(td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS)); - end_active_var (td, &av, var); - } - } - if (opcode >= MINT_MOV_8_2 && opcode <= MINT_MOV_8_4) { - // These opcodes have multiple dvars, which overcomplicate things, so they are - // marked as having no svars/dvars, for now. Special case it. - int num_pairs = 2 + opcode - MINT_MOV_8_2; - for (int i = 0; i < num_pairs; i++) { - int var = ins->data [2 * i + 1]; - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) - end_active_var (td, &av, var); - } - } - - if (is_call) - end_active_call (td, &ac, ins); - - compact_active_vars (td, &av, ¤t_offset); - - // Alloc dreg local starting at the stack_offset - if (mono_interp_op_dregs [opcode]) { - int var = ins->dreg; - - if (td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { - add_active_call (td, &ac, td->locals [var].call); - } else if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].offset == -1) { - alloc_var_offset (td, var, ¤t_offset); - if (current_offset > final_total_locals_size) - final_total_locals_size = current_offset; - - if (td->verbose_level) - g_print ("alloc var %d to offset %d\n", var, td->locals [var].offset); - - if (td->locals [var].live_end > ins_index) { - // if dreg is still used in the basic block, add it to the active list - add_active_var (td, &av, var); - } else { - current_offset = td->locals [var].offset; - } - } - } - if (td->verbose_level) - dump_active_vars (td, &av); - ins_index++; - } - } - final_total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT); - - // Iterate over all call args locals, update their final offset (aka add td->total_locals_size to them) - // then also update td->total_locals_size to account for this space. - td->param_area_offset = final_total_locals_size; - for (unsigned int i = 0; i < td->locals_size; i++) { - // These are allocated separately at the end of the stack - if (td->locals [i].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { - td->locals [i].offset += td->param_area_offset; - final_total_locals_size = MAX (td->locals [i].offset + td->locals [i].size, final_total_locals_size); - } - } - td->total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT); -} - /* * Very few methods have localloc. Handle it separately to not impact performance * of other methods. We replace the normal return opcodes with opcodes that also @@ -11288,7 +8701,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG g_print ("Runtime method: %s %p\n", mono_method_full_name (method, TRUE), rtm); g_print ("Locals size %d\n", td->total_locals_size); g_print ("Calculated stack height: %d, stated height: %d\n", td->max_stack_height, header->max_stack); - dump_interp_code (td->new_code, td->new_code_end, td->data_items); + interp_dump_code (td->new_code, td->new_code_end, td->data_items); } /* Check if we use excessive stack space */ diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 2bfbe42390510e..2f66bc3c61c912 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -289,7 +289,7 @@ typedef struct #define interp_ins_set_dummy_dreg(ins,td) do { \ if (td->dummy_var < 0) \ - create_interp_dummy_var (td); \ + interp_create_dummy_var (td); \ ins->dreg = td->dummy_var; \ } while (0) @@ -381,6 +381,74 @@ mono_jiterp_insert_ins (TransformData *td, InterpInst *prev_ins, int opcode); void mono_interp_print_td_code (TransformData *td); +/* Compilation internal methods */ + +InterpInst* +interp_new_ins (TransformData *td, int opcode, int len); + +InterpInst* +interp_insert_ins_bb (TransformData *td, InterpBasicBlock *bb, InterpInst *prev_ins, int opcode); + +InterpInst* +interp_insert_ins (TransformData *td, InterpInst *prev_ins, int opcode); + +InterpInst* +interp_first_ins (InterpBasicBlock *bb); + +InterpInst* +interp_next_ins (InterpInst *ins); + +InterpInst* +interp_prev_ins (InterpInst *ins); + +void +interp_clear_ins (InterpInst *ins); + +gboolean +interp_ins_is_nop (InterpInst *ins); + +int +interp_get_ins_length (InterpInst *ins); + +void +interp_dump_ins (InterpInst *ins, gpointer *data_items); + +InterpInst* +interp_get_ldc_i4_from_const (TransformData *td, InterpInst *ins, gint32 ct, int dreg); + +gint32 +interp_get_const_from_ldc_i4 (InterpInst *ins); + +int +interp_get_mov_for_type (int mt, gboolean needs_sext); + +gboolean +interp_is_short_offset (int src_offset, int dest_offset); + +InterpBasicBlock* +interp_alloc_bb (TransformData *td); + +void +interp_link_bblocks (TransformData *td, InterpBasicBlock *from, InterpBasicBlock *to); + +int +interp_compute_native_offset_estimates (TransformData *td); + +void +interp_optimize_code (TransformData *td); + +void +interp_alloc_offsets (TransformData *td); + +int +interp_alloc_global_var_offset (TransformData *td, int var); + +int +interp_create_local (TransformData *td, MonoType *type); + +void +interp_foreach_local_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int, gpointer)); + /* Forward definitions for simd methods */ static gboolean interp_emit_simd_intrinsics (TransformData *td, MonoMethod *cmethod, MonoMethodSignature *csignature, gboolean newobj); From d44646523607770adf5f3657c88f8b43527740fc Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Wed, 11 Oct 2023 10:56:10 +0300 Subject: [PATCH 2/6] [mono][interp] Pass ref to var storage in interp inst In order to facilitate overwritting of sregs/dreg during instruction iterations. Also enable iterating only on sregs. --- src/mono/mono/mini/interp/transform-opt.c | 24 ++++++++++--------- src/mono/mono/mini/interp/transform.c | 28 +++++++++++++---------- src/mono/mono/mini/interp/transform.h | 6 ++++- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index 148b3c8a603ecb..922b742c2142e8 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -42,9 +42,9 @@ set_var_live_range (TransformData *td, int var, int ins_index) } static void -set_var_live_range_cb (TransformData *td, int var, gpointer data) +set_var_live_range_cb (TransformData *td, int *pvar, gpointer data) { - set_var_live_range (td, var, (int)(gsize)data); + set_var_live_range (td, *pvar, (int)(gsize)data); } static void @@ -66,9 +66,9 @@ initialize_global_var (TransformData *td, int var, int bb_index) } static void -initialize_global_var_cb (TransformData *td, int var, gpointer data) +initialize_global_var_cb (TransformData *td, int *pvar, gpointer data) { - initialize_global_var (td, var, (int)(gsize)data); + initialize_global_var (td, *pvar, (int)(gsize)data); } static void @@ -93,7 +93,7 @@ initialize_global_vars (TransformData *td) td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; } } - interp_foreach_local_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb); + interp_foreach_ins_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb); } } td->total_locals_size = ALIGN_TO (td->total_locals_size, MINT_STACK_ALIGNMENT); @@ -370,7 +370,7 @@ interp_alloc_offsets (TransformData *td) // The arg of the call is no longer global *call_args = new_var; // Also update liveness for this instruction - interp_foreach_local_var (td, new_inst, (gpointer)(gsize)ins_index, set_var_live_range_cb); + interp_foreach_ins_var (td, new_inst, (gpointer)(gsize)ins_index, set_var_live_range_cb); ins_index++; } } else { @@ -408,7 +408,7 @@ interp_alloc_offsets (TransformData *td) } } // Set live_start and live_end for every referenced local that is not global - interp_foreach_local_var (td, ins, (gpointer)(gsize)ins_index, set_var_live_range_cb); + interp_foreach_ins_var (td, ins, (gpointer)(gsize)ins_index, set_var_live_range_cb); ins_index++; } gint32 current_offset = td->total_locals_size; @@ -1516,8 +1516,9 @@ cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, LocalValue *local_de } static void -clear_local_defs (TransformData *td, int var, void *data) +clear_local_defs (TransformData *td, int *pvar, void *data) { + int var = *pvar; LocalValue *local_defs = (LocalValue*) data; local_defs [var].type = LOCAL_VALUE_NONE; local_defs [var].ins = NULL; @@ -1525,8 +1526,9 @@ clear_local_defs (TransformData *td, int var, void *data) } static void -clear_unused_defs (TransformData *td, int var, void *data) +clear_unused_defs (TransformData *td, int *pvar, void *data) { + int var = *pvar; if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) return; if (td->locals [var].indirects) @@ -1578,7 +1580,7 @@ interp_cprop (TransformData *td) td->cbb = bb; for (ins = bb->first_ins; ins != NULL; ins = ins->next) - interp_foreach_local_var (td, ins, local_defs, clear_local_defs); + interp_foreach_ins_var (td, ins, local_defs, clear_local_defs); if (td->verbose_level) { GString* bb_info = interp_get_bb_links (bb); @@ -2008,7 +2010,7 @@ interp_cprop (TransformData *td) } for (ins = bb->first_ins; ins != NULL; ins = ins->next) - interp_foreach_local_var (td, ins, local_defs, clear_unused_defs); + interp_foreach_ins_var (td, ins, local_defs, clear_unused_defs); } needs_retry |= interp_local_deadce (td); diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index d7983ef88c106a..3e5cef8c8fe7eb 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -8052,8 +8052,9 @@ handle_relocations (TransformData *td) } static void -alloc_unopt_global_local (TransformData *td, int local, gpointer data) +alloc_unopt_global_local (TransformData *td, int *plocal, gpointer data) { + int local = *plocal; // Execution stack locals are resolved when we emit the instruction in the code stream, // once all global locals have their offset resolved if (td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) @@ -8082,31 +8083,34 @@ interp_get_ins_length (InterpInst *ins) } void -interp_foreach_local_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int, gpointer)) +interp_foreach_ins_svar (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int*, gpointer)) { int opcode = ins->opcode; if (mono_interp_op_sregs [opcode]) { for (int i = 0; i < mono_interp_op_sregs [opcode]; i++) { - int sreg = ins->sregs [i]; - - if (sreg == MINT_CALL_ARGS_SREG) { + if (ins->sregs [i] == MINT_CALL_ARGS_SREG) { if (ins->info.call_info && ins->info.call_info->call_args) { int *call_args = ins->info.call_info->call_args; - int var = *call_args; - while (var != -1) { - callback (td, var, data); + while (*call_args != -1) { + callback (td, call_args, data); call_args++; - var = *call_args; } } } else { - callback (td, sreg, data); + callback (td, &ins->sregs [i], data); } } } +} +void +interp_foreach_ins_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int*, gpointer)) +{ + interp_foreach_ins_svar (td, ins, data, callback); + + int opcode = ins->opcode; if (mono_interp_op_dregs [opcode]) - callback (td, ins->dreg, data); + callback (td, &ins->dreg, data); } int @@ -8127,7 +8131,7 @@ interp_compute_native_offset_estimates (TransformData *td) continue; noe += interp_get_ins_length (ins); if (!td->optimized) - interp_foreach_local_var (td, ins, NULL, alloc_unopt_global_local); + interp_foreach_ins_var (td, ins, NULL, alloc_unopt_global_local); } } diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 2f66bc3c61c912..6662fe341dacc1 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -447,7 +447,11 @@ int interp_create_local (TransformData *td, MonoType *type); void -interp_foreach_local_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int, gpointer)); +interp_foreach_ins_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int*, gpointer)); + +void +interp_foreach_ins_svar (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int*, gpointer)); + /* Forward definitions for simd methods */ static gboolean From d30ee3f22032cdded1c5b8f416c61dbdbe3966d8 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Mon, 6 Nov 2023 13:28:18 +0200 Subject: [PATCH 3/6] [mono][interp] Print invalid il offset in aligned fashion --- src/mono/mono/mini/interp/transform.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 3e5cef8c8fe7eb..ad0450b87f6b4e 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -1346,7 +1346,10 @@ interp_dump_ins (InterpInst *ins, gpointer *data_items) { int opcode = ins->opcode; GString *str = g_string_new (""); - g_string_append_printf (str, "IL_%04x: %-14s", ins->il_offset, mono_interp_opname (opcode)); + if (ins->il_offset == -1) + g_string_append_printf (str, "IL_----: %-14s", mono_interp_opname (opcode)); + else + g_string_append_printf (str, "IL_%04x: %-14s", ins->il_offset, mono_interp_opname (opcode)); if (mono_interp_op_dregs [opcode] > 0) g_string_append_printf (str, " [%d <-", ins->dreg); From 51762ae9dcbf0a0bd6840ac902849b30ed97842e Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Mon, 6 Nov 2023 13:34:10 +0200 Subject: [PATCH 4/6] [mono][interp] Remove irrelevant stats --- src/mono/mono/mini/interp/interp-internals.h | 8 -------- src/mono/mono/mini/interp/interp.c | 8 -------- src/mono/mono/mini/interp/transform-opt.c | 15 --------------- src/mono/mono/mini/interp/transform.c | 1 - 4 files changed, 32 deletions(-) diff --git a/src/mono/mono/mini/interp/interp-internals.h b/src/mono/mono/mini/interp/interp-internals.h index 9be34b46d84570..72ef097eac22d7 100644 --- a/src/mono/mono/mini/interp/interp-internals.h +++ b/src/mono/mono/mini/interp/interp-internals.h @@ -277,15 +277,7 @@ typedef struct { gint64 methods_transformed; gint64 cprop_time; gint64 super_instructions_time; - gint32 stloc_nps; - gint32 movlocs; - gint32 copy_propagations; - gint32 constant_folds; - gint32 ldlocas_removed; - gint32 killed_instructions; gint32 emitted_instructions; - gint32 super_instructions; - gint32 added_pop_count; gint32 inlined_methods; gint32 inline_failures; } MonoInterpStats; diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index ad1c504ee1bd4e..23bf79620a73e2 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -8690,14 +8690,6 @@ register_interp_stats (void) mono_counters_register ("Methods transformed", MONO_COUNTER_INTERP | MONO_COUNTER_LONG, &mono_interp_stats.methods_transformed); mono_counters_register ("Total cprop time", MONO_COUNTER_INTERP | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_interp_stats.cprop_time); mono_counters_register ("Total super instructions time", MONO_COUNTER_INTERP | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_interp_stats.super_instructions_time); - mono_counters_register ("STLOC_NP count", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.stloc_nps); - mono_counters_register ("MOVLOC count", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.movlocs); - mono_counters_register ("Copy propagations", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.copy_propagations); - mono_counters_register ("Added pop count", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.added_pop_count); - mono_counters_register ("Constant folds", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.constant_folds); - mono_counters_register ("Ldlocas removed", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.ldlocas_removed); - mono_counters_register ("Super instructions", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.super_instructions); - mono_counters_register ("Killed instructions", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.killed_instructions); mono_counters_register ("Emitted instructions", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.emitted_instructions); mono_counters_register ("Methods inlined", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.inlined_methods); mono_counters_register ("Inline failures", MONO_COUNTER_INTERP | MONO_COUNTER_INT, &mono_interp_stats.inline_failures); diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index 922b742c2142e8..aabedc60c9cff0 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -987,7 +987,6 @@ interp_local_deadce (TransformData *td) } if (ins->opcode == MINT_LDLOCA_S) { - mono_interp_stats.ldlocas_removed++; td->locals [ins->sregs [0]].indirects--; if (!td->locals [ins->sregs [0]].indirects) { // We can do cprop now through this local. Run cprop again. @@ -995,7 +994,6 @@ interp_local_deadce (TransformData *td) } } interp_clear_ins (ins); - mono_interp_stats.killed_instructions++; // FIXME This is lazy. We should update the ref count for the sregs and redo deadce. needs_cprop = TRUE; } @@ -1141,8 +1139,6 @@ interp_fold_unop (TransformData *td, LocalValue *local_defs, InterpInst *ins) // We were able to compute the result of the ins instruction. We replace the unop // with a LDC of the constant. We leave alone the sregs of this instruction, for // deadce to kill the instructions initializing them. - mono_interp_stats.constant_folds++; - if (result.type == LOCAL_VALUE_I4) ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); else if (result.type == LOCAL_VALUE_I8) @@ -1216,7 +1212,6 @@ interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue * interp_dump_ins (ins, td->data_items); } - mono_interp_stats.constant_folds++; local_ref_count [sreg]--; return ins; } @@ -1331,7 +1326,6 @@ interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins, g // We were able to compute the result of the ins instruction. We replace the binop // with a LDC of the constant. We leave alone the sregs of this instruction, for // deadce to kill the instructions initializing them. - mono_interp_stats.constant_folds++; *folded = TRUE; if (result.type == LOCAL_VALUE_I4) ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); @@ -1417,7 +1411,6 @@ interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue interp_dump_ins (ins, td->data_items); } - mono_interp_stats.constant_folds++; local_ref_count [sreg1]--; local_ref_count [sreg2]--; return ins; @@ -1669,7 +1662,6 @@ interp_cprop (TransformData *td) } local_defs [dreg].ins = ins; local_ref_count [sreg]--; - mono_interp_stats.copy_propagations++; if (td->verbose_level) { g_print ("cprop loc %d -> ct :\n\t", sreg); interp_dump_ins (ins, td->data_items); @@ -2067,7 +2059,6 @@ get_sreg_imm (TransformData *td, int sreg, gint16 *imm, int result_mt) } if (ct >= min_val && ct <= max_val) { *imm = (gint16)ct; - mono_interp_stats.super_instructions++; return TRUE; } } @@ -2360,7 +2351,6 @@ interp_super_instructions (TransformData *td) interp_clear_ins (def); interp_clear_ins (ins); local_ref_count [sreg_base]--; - mono_interp_stats.super_instructions++; if (td->verbose_level) { g_print ("superins: "); interp_dump_ins (new_inst, td->data_items); @@ -2397,7 +2387,6 @@ interp_super_instructions (TransformData *td) interp_clear_ins (def); interp_clear_ins (ins); local_ref_count [sreg_off]--; - mono_interp_stats.super_instructions++; if (td->verbose_level) { g_print ("method %s:%s, superins: ", m_class_get_name (td->method->klass), td->method->name); interp_dump_ins (new_inst, td->data_items); @@ -2426,7 +2415,6 @@ interp_super_instructions (TransformData *td) interp_clear_ins (def); interp_clear_ins (ins); local_ref_count [sreg_base]--; - mono_interp_stats.super_instructions++; if (td->verbose_level) { g_print ("superins: "); interp_dump_ins (new_inst, td->data_items); @@ -2449,7 +2437,6 @@ interp_super_instructions (TransformData *td) ins->sregs [0] = def->sregs [0]; interp_clear_ins (def); local_ref_count [obj_sreg]--; - mono_interp_stats.super_instructions++; } } else if (MINT_IS_BINOP_CONDITIONAL_BRANCH (opcode) && interp_is_short_offset (noe, ins->info.target_bb->native_offset_estimate)) { gint16 imm; @@ -2527,7 +2514,6 @@ interp_super_instructions (TransformData *td) ins->sregs [1] = def->sregs [1]; interp_clear_ins (def); local_ref_count [cond_sreg]--; - mono_interp_stats.super_instructions++; if (td->verbose_level) { g_print ("superins: "); interp_dump_ins (ins, td->data_items); @@ -2562,7 +2548,6 @@ interp_super_instructions (TransformData *td) interp_clear_ins (def); interp_clear_ins (ins); local_ref_count [sreg_src]--; - mono_interp_stats.super_instructions++; if (td->verbose_level) { g_print ("superins: "); interp_dump_ins (new_inst, td->data_items); diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index ad0450b87f6b4e..9224dda5558cb0 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -1789,7 +1789,6 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas // Remove the ldlocas td->locals [ldloca1->sregs [0]].indirects--; td->locals [ldloca2->sregs [0]].indirects--; - mono_interp_stats.ldlocas_removed += 2; interp_clear_ins (ldloca1); interp_clear_ins (ldloca2); td->sp -= 2; From 26ab3b455e8c71f66210d1c866720dacf3426a6d Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Mon, 6 Nov 2023 13:48:42 +0200 Subject: [PATCH 5/6] [mono][interp] Renaming of local to var Local can have multiple meanings. Use it to refer to IL locals from now. All IL locals are vars. Vars can be local (single bblock use) or global. --- src/mono/mono/mini/interp/jiterpreter.c | 2 +- src/mono/mono/mini/interp/transform-opt.c | 534 ++++++++-------- src/mono/mono/mini/interp/transform-simd.c | 10 +- src/mono/mono/mini/interp/transform.c | 688 ++++++++++----------- src/mono/mono/mini/interp/transform.h | 37 +- 5 files changed, 637 insertions(+), 634 deletions(-) diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index ceb6a66679647e..0d4e17bf346f53 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -773,7 +773,7 @@ build_address_taken_bitset (TransformData *td, InterpBasicBlock *bb, guint32 bit for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { if (ins->opcode == MINT_LDLOCA_S) { InterpMethod *imethod = td->rtm; - InterpLocal *loc = &td->locals[ins->sregs[0]]; + InterpVar *loc = &td->vars [ins->sregs[0]]; // Allocate on demand so if a method contains no ldlocas we don't allocate the bitset if (!imethod->address_taken_bits) diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index aabedc60c9cff0..68f922216e24bf 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -12,16 +12,16 @@ alloc_var_offset (TransformData *td, int local, gint32 *ptos) int size, offset; offset = *ptos; - size = td->locals [local].size; + size = td->vars [local].size; - if (td->locals [local].flags & INTERP_LOCAL_FLAG_SIMD) + if (td->vars [local].flags & INTERP_LOCAL_FLAG_SIMD) offset = ALIGN_TO (offset, MINT_SIMD_ALIGNMENT); - td->locals [local].offset = offset; + td->vars [local].offset = offset; *ptos = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); - return td->locals [local].offset; + return td->vars [local].offset; } int @@ -34,11 +34,11 @@ static void set_var_live_range (TransformData *td, int var, int ins_index) { // We don't track liveness yet for global vars - if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) + if (td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) return; - if (td->locals [var].live_start == -1) - td->locals [var].live_start = ins_index; - td->locals [var].live_end = ins_index; + if (td->vars [var].live_start == -1) + td->vars [var].live_start = ins_index; + td->vars [var].live_end = ins_index; } static void @@ -51,17 +51,17 @@ static void initialize_global_var (TransformData *td, int var, int bb_index) { // Check if already handled - if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) + if (td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) return; - if (td->locals [var].bb_index == -1) { - td->locals [var].bb_index = bb_index; - } else if (td->locals [var].bb_index != bb_index) { + if (td->vars [var].bb_index == -1) { + td->vars [var].bb_index = bb_index; + } else if (td->vars [var].bb_index != bb_index) { // var used in multiple basic blocks if (td->verbose_level) g_print ("alloc global var %d to offset %d\n", var, td->total_locals_size); interp_alloc_global_var_offset (td, var); - td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + td->vars [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; } } @@ -86,11 +86,11 @@ initialize_global_vars (TransformData *td) } else if (opcode == MINT_LDLOCA_S) { int var = ins->sregs [0]; // If global flag is set, it means its offset was already allocated - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL)) { + if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL)) { if (td->verbose_level) g_print ("alloc ldloca global var %d to offset %d\n", var, td->total_locals_size); interp_alloc_global_var_offset (td, var); - td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + td->vars [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; } } interp_foreach_ins_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb); @@ -231,7 +231,7 @@ static void init_active_vars (TransformData *td, ActiveVars *av) { av->active_vars_count = 0; - av->active_vars_capacity = MAX (td->locals_size / td->bb_count, 10); + av->active_vars_capacity = MAX (td->vars_size / td->bb_count, 10); av->active_vars = (ActiveVar*)mono_mempool_alloc (td->mempool, av->active_vars_capacity * sizeof (ActiveVars)); } @@ -275,7 +275,7 @@ compact_active_vars (TransformData *td, ActiveVars *av, gint32 *current_offset) int i = av->active_vars_count - 1; while (i >= 0 && !av->active_vars [i].is_alive) { av->active_vars_count--; - *current_offset = td->locals [av->active_vars [i].var].offset; + *current_offset = td->vars [av->active_vars [i].var].offset; i--; } } @@ -287,7 +287,7 @@ dump_active_vars (TransformData *td, ActiveVars *av) g_print ("active :"); for (int i = 0; i < av->active_vars_count; i++) { if (av->active_vars [i].is_alive) - g_print (" %d (end %d),", av->active_vars [i].var, td->locals [av->active_vars [i].var].live_end); + g_print (" %d (end %d),", av->active_vars [i].var, td->vars [av->active_vars [i].var].live_end); } g_print ("\n"); } @@ -330,7 +330,7 @@ interp_alloc_offsets (TransformData *td) // the created object is set before the call is made. We solve this by making // sure that the dreg is not allocated in the param area, so there is no // risk of conflicts. - td->locals [ins->dreg].flags |= INTERP_LOCAL_FLAG_NO_CALL_ARGS; + td->vars [ins->dreg].flags |= INTERP_LOCAL_FLAG_NO_CALL_ARGS; } if (ins->flags & INTERP_INST_FLAG_CALL) { if (ins->info.call_info && ins->info.call_info->call_args) { @@ -341,18 +341,18 @@ interp_alloc_offsets (TransformData *td) int var = *call_args; while (var != -1) { - if (td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL || + if (td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL || !td->local_ref_count || td->local_ref_count [var] > 1 || - td->locals [var].flags & INTERP_LOCAL_FLAG_NO_CALL_ARGS) { + td->vars [var].flags & INTERP_LOCAL_FLAG_NO_CALL_ARGS) { // Some vars can't be allocated on the call args stack, since the constraint is that // call args vars die after the call. This isn't necessarily true for global vars or // vars that are used by other instructions aside from the call. // We need to copy the var into a new tmp var - int new_var = interp_create_local (td, td->locals [var].type); - td->locals [new_var].call = ins; - td->locals [new_var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + int new_var = interp_create_var (td, td->vars [var].type); + td->vars [new_var].call = ins; + td->vars [new_var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; - int mt = mono_mint_type (td->locals [var].type); + int mt = mono_mint_type (td->vars [var].type); if (mt != MINT_TYPE_VT && num_pairs < MINT_MOV_PAIRS_MAX && var <= G_MAXUINT16 && new_var <= G_MAXUINT16) { // We store these in the instruction data slots so we do this optimizations only if they fit pair_sregs [num_pairs] = (guint16)var; @@ -366,7 +366,7 @@ interp_alloc_offsets (TransformData *td) interp_ins_set_dreg (new_inst, new_var); interp_ins_set_sreg (new_inst, var); if (opcode == MINT_MOV_VT) - new_inst->data [0] = GINT_TO_UINT16 (td->locals [var].size); + new_inst->data [0] = GINT_TO_UINT16 (td->vars [var].size); // The arg of the call is no longer global *call_args = new_var; // Also update liveness for this instruction @@ -375,8 +375,8 @@ interp_alloc_offsets (TransformData *td) } } else { // Flag this var as it has special storage on the call args stack - td->locals [var].call = ins; - td->locals [var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + td->vars [var].call = ins; + td->vars [var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; } call_args++; var = *call_args; @@ -388,7 +388,7 @@ interp_alloc_offsets (TransformData *td) set_var_live_range (td, pair_dregs [i], ins_index); } if (num_pairs == 1) { - int mt = mono_mint_type (td->locals [pair_sregs [0]].type); + int mt = mono_mint_type (td->vars [pair_sregs [0]].type); int opcode = interp_get_mov_for_type (mt, FALSE); InterpInst *new_inst = interp_insert_ins_bb (td, bb, ins->prev, opcode); interp_ins_set_dreg (new_inst, pair_dregs [0]); @@ -431,8 +431,8 @@ interp_alloc_offsets (TransformData *td) int var = ins->sregs [i]; if (var == MINT_CALL_ARGS_SREG) continue; - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) { - g_assert (!(td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS)); + if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->vars [var].live_end == ins_index) { + g_assert (!(td->vars [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS)); end_active_var (td, &av, var); } } @@ -442,7 +442,7 @@ interp_alloc_offsets (TransformData *td) int num_pairs = 2 + opcode - MINT_MOV_8_2; for (int i = 0; i < num_pairs; i++) { int var = ins->data [2 * i + 1]; - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].live_end == ins_index) + if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->vars [var].live_end == ins_index) end_active_var (td, &av, var); } } @@ -456,21 +456,21 @@ interp_alloc_offsets (TransformData *td) if (mono_interp_op_dregs [opcode]) { int var = ins->dreg; - if (td->locals [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { - add_active_call (td, &ac, td->locals [var].call); - } else if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->locals [var].offset == -1) { + if (td->vars [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { + add_active_call (td, &ac, td->vars [var].call); + } else if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->vars [var].offset == -1) { alloc_var_offset (td, var, ¤t_offset); if (current_offset > final_total_locals_size) final_total_locals_size = current_offset; if (td->verbose_level) - g_print ("alloc var %d to offset %d\n", var, td->locals [var].offset); + g_print ("alloc var %d to offset %d\n", var, td->vars [var].offset); - if (td->locals [var].live_end > ins_index) { + if (td->vars [var].live_end > ins_index) { // if dreg is still used in the basic block, add it to the active list add_active_var (td, &av, var); } else { - current_offset = td->locals [var].offset; + current_offset = td->vars [var].offset; } } } @@ -484,11 +484,11 @@ interp_alloc_offsets (TransformData *td) // Iterate over all call args locals, update their final offset (aka add td->total_locals_size to them) // then also update td->total_locals_size to account for this space. td->param_area_offset = final_total_locals_size; - for (unsigned int i = 0; i < td->locals_size; i++) { + for (unsigned int i = 0; i < td->vars_size; i++) { // These are allocated separately at the end of the stack - if (td->locals [i].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { - td->locals [i].offset += td->param_area_offset; - final_total_locals_size = MAX (td->locals [i].offset + td->locals [i].size, final_total_locals_size); + if (td->vars [i].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { + td->vars [i].offset += td->param_area_offset; + final_total_locals_size = MAX (td->vars [i].offset + td->vars [i].size, final_total_locals_size); } } td->total_locals_size = ALIGN_TO (final_total_locals_size, MINT_STACK_ALIGNMENT); @@ -625,8 +625,8 @@ interp_remove_bblock (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { if (ins->opcode == MINT_LDLOCA_S) { - td->locals [ins->sregs [0]].indirects--; - if (!td->locals [ins->sregs [0]].indirects) { + td->vars [ins->sregs [0]].indirects--; + if (!td->vars [ins->sregs [0]].indirects) { // We can do cprop now through this local. Run cprop again. needs_cprop = TRUE; } @@ -950,24 +950,24 @@ interp_local_deadce (TransformData *td) gboolean needs_dce = FALSE; gboolean needs_cprop = FALSE; - for (unsigned int i = 0; i < td->locals_size; i++) { + for (unsigned int i = 0; i < td->vars_size; i++) { g_assert (local_ref_count [i] >= 0); - g_assert (td->locals [i].indirects >= 0); - if (td->locals [i].indirects || (td->locals [i].flags & INTERP_LOCAL_FLAG_DEAD)) + g_assert (td->vars [i].indirects >= 0); + if (td->vars [i].indirects || (td->vars [i].flags & INTERP_LOCAL_FLAG_DEAD)) continue; if (!local_ref_count [i]) { needs_dce = TRUE; - td->locals [i].flags |= INTERP_LOCAL_FLAG_DEAD; - } else if (!(td->locals [i].flags & INTERP_LOCAL_FLAG_UNKNOWN_USE)) { - if (!(td->locals [i].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) { + td->vars [i].flags |= INTERP_LOCAL_FLAG_DEAD; + } else if (!(td->vars [i].flags & INTERP_LOCAL_FLAG_UNKNOWN_USE)) { + if (!(td->vars [i].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) { // The value of this var is not passed between multiple basic blocks - td->locals [i].flags |= INTERP_LOCAL_FLAG_LOCAL_ONLY; + td->vars [i].flags |= INTERP_LOCAL_FLAG_LOCAL_ONLY; if (td->verbose_level) g_print ("Var %d is local only\n", i); needs_cprop = TRUE; } } - td->locals [i].flags &= ~INTERP_LOCAL_FLAG_UNKNOWN_USE; + td->vars [i].flags &= ~INTERP_LOCAL_FLAG_UNKNOWN_USE; } // Return early if all locals are alive @@ -980,15 +980,15 @@ interp_local_deadce (TransformData *td) if (MINT_NO_SIDE_EFFECTS (ins->opcode) || ins->opcode == MINT_LDLOCA_S) { int dreg = ins->dreg; - if (td->locals [dreg].flags & INTERP_LOCAL_FLAG_DEAD) { + if (td->vars [dreg].flags & INTERP_LOCAL_FLAG_DEAD) { if (td->verbose_level) { g_print ("kill dead ins:\n\t"); interp_dump_ins (ins, td->data_items); } if (ins->opcode == MINT_LDLOCA_S) { - td->locals [ins->sregs [0]].indirects--; - if (!td->locals [ins->sregs [0]].indirects) { + td->vars [ins->sregs [0]].indirects--; + if (!td->vars [ins->sregs [0]].indirects) { // We can do cprop now through this local. Run cprop again. needs_cprop = TRUE; } @@ -1071,66 +1071,66 @@ interp_get_mt_for_ldind (int ldind_op) break; static InterpInst* -interp_fold_unop (TransformData *td, LocalValue *local_defs, InterpInst *ins) +interp_fold_unop (TransformData *td, InterpVarValue *local_defs, InterpInst *ins) { int *local_ref_count = td->local_ref_count; // ins should be an unop, therefore it should have a single dreg and a single sreg int dreg = ins->dreg; int sreg = ins->sregs [0]; - LocalValue *val = &local_defs [sreg]; - LocalValue result; + InterpVarValue *val = &local_defs [sreg]; + InterpVarValue result; - if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8) + if (val->type != VAR_VALUE_I4 && val->type != VAR_VALUE_I8) return ins; // Top of the stack is a constant switch (ins->opcode) { - INTERP_FOLD_UNOP (MINT_ADD1_I4, LOCAL_VALUE_I4, i, 1+); - INTERP_FOLD_UNOP (MINT_ADD1_I8, LOCAL_VALUE_I8, l, 1+); - INTERP_FOLD_UNOP (MINT_SUB1_I4, LOCAL_VALUE_I4, i, -1+); - INTERP_FOLD_UNOP (MINT_SUB1_I8, LOCAL_VALUE_I8, l, -1+); - INTERP_FOLD_UNOP (MINT_NEG_I4, LOCAL_VALUE_I4, i, -); - INTERP_FOLD_UNOP (MINT_NEG_I8, LOCAL_VALUE_I8, l, -); - INTERP_FOLD_UNOP (MINT_NOT_I4, LOCAL_VALUE_I4, i, ~); - INTERP_FOLD_UNOP (MINT_NOT_I8, LOCAL_VALUE_I8, l, ~); - INTERP_FOLD_UNOP (MINT_CEQ0_I4, LOCAL_VALUE_I4, i, 0 ==); - - INTERP_FOLD_CONV (MINT_CONV_I1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8); - INTERP_FOLD_CONV (MINT_CONV_I1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8); - INTERP_FOLD_CONV (MINT_CONV_U1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint8); - INTERP_FOLD_CONV (MINT_CONV_U1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint8); - - INTERP_FOLD_CONV (MINT_CONV_I2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16); - INTERP_FOLD_CONV (MINT_CONV_I2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint16); - INTERP_FOLD_CONV (MINT_CONV_U2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint16); - INTERP_FOLD_CONV (MINT_CONV_U2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint16); - - INTERP_FOLD_CONV (MINT_CONV_I8_I4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, gint32); - INTERP_FOLD_CONV (MINT_CONV_I8_U4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, guint32); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8, val->i >= G_MININT8 && val->i <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8, val->l >= G_MININT8 && val->l <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint8, val->i >= 0 && val->i <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint8, val->l >= 0 && val->l <= G_MAXINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint8, val->i >= 0 && val->i <= G_MAXUINT8); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint8, val->l >= 0 && val->l <= G_MAXUINT8); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16, val->i >= G_MININT16 && val->i <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, i, gint16, val->l >= G_MININT16 && val->l <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint16, val->i >= 0 && val->i <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint16, val->l >= 0 && val->l <= G_MAXINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint16, val->i >= 0 && val->i <= G_MAXUINT16); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint16, val->l >= 0 && val->l <= G_MAXUINT16); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, gint32, val->i >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint32, val->l >= G_MININT32 && val->l <= G_MAXINT32); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, gint32, val->l >= 0 && val->l <= G_MAXINT32); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I4, LOCAL_VALUE_I4, i, LOCAL_VALUE_I4, i, guint32, val->i >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I8, LOCAL_VALUE_I4, i, LOCAL_VALUE_I8, l, guint32, val->l >= 0 && val->l <= G_MAXINT32); - - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I8_U8, LOCAL_VALUE_I8, l, LOCAL_VALUE_I8, l, gint64, val->l >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I4, LOCAL_VALUE_I8, l, LOCAL_VALUE_I4, i, guint64, val->i >= 0); - INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I8, LOCAL_VALUE_I8, l, LOCAL_VALUE_I8, l, guint64, val->l >= 0); + INTERP_FOLD_UNOP (MINT_ADD1_I4, VAR_VALUE_I4, i, 1+); + INTERP_FOLD_UNOP (MINT_ADD1_I8, VAR_VALUE_I8, l, 1+); + INTERP_FOLD_UNOP (MINT_SUB1_I4, VAR_VALUE_I4, i, -1+); + INTERP_FOLD_UNOP (MINT_SUB1_I8, VAR_VALUE_I8, l, -1+); + INTERP_FOLD_UNOP (MINT_NEG_I4, VAR_VALUE_I4, i, -); + INTERP_FOLD_UNOP (MINT_NEG_I8, VAR_VALUE_I8, l, -); + INTERP_FOLD_UNOP (MINT_NOT_I4, VAR_VALUE_I4, i, ~); + INTERP_FOLD_UNOP (MINT_NOT_I8, VAR_VALUE_I8, l, ~); + INTERP_FOLD_UNOP (MINT_CEQ0_I4, VAR_VALUE_I4, i, 0 ==); + + INTERP_FOLD_CONV (MINT_CONV_I1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint8); + INTERP_FOLD_CONV (MINT_CONV_I1_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint8); + INTERP_FOLD_CONV (MINT_CONV_U1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint8); + INTERP_FOLD_CONV (MINT_CONV_U1_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint8); + + INTERP_FOLD_CONV (MINT_CONV_I2_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint16); + INTERP_FOLD_CONV (MINT_CONV_I2_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint16); + INTERP_FOLD_CONV (MINT_CONV_U2_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint16); + INTERP_FOLD_CONV (MINT_CONV_U2_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint16); + + INTERP_FOLD_CONV (MINT_CONV_I8_I4, VAR_VALUE_I8, l, VAR_VALUE_I4, i, gint32); + INTERP_FOLD_CONV (MINT_CONV_I8_U4, VAR_VALUE_I8, l, VAR_VALUE_I4, i, guint32); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint8, val->i >= G_MININT8 && val->i <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint8, val->l >= G_MININT8 && val->l <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint8, val->i >= 0 && val->i <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I1_U8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint8, val->l >= 0 && val->l <= G_MAXINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint8, val->i >= 0 && val->i <= G_MAXUINT8); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U1_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint8, val->l >= 0 && val->l <= G_MAXUINT8); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint16, val->i >= G_MININT16 && val->i <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, i, gint16, val->l >= G_MININT16 && val->l <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint16, val->i >= 0 && val->i <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I2_U8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint16, val->l >= 0 && val->l <= G_MAXINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint16, val->i >= 0 && val->i <= G_MAXUINT16); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U2_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint16, val->l >= 0 && val->l <= G_MAXUINT16); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, gint32, val->i >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint32, val->l >= G_MININT32 && val->l <= G_MAXINT32); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I4_U8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, gint32, val->l >= 0 && val->l <= G_MAXINT32); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I4, VAR_VALUE_I4, i, VAR_VALUE_I4, i, guint32, val->i >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U4_I8, VAR_VALUE_I4, i, VAR_VALUE_I8, l, guint32, val->l >= 0 && val->l <= G_MAXINT32); + + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_I8_U8, VAR_VALUE_I8, l, VAR_VALUE_I8, l, gint64, val->l >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I4, VAR_VALUE_I8, l, VAR_VALUE_I4, i, guint64, val->i >= 0); + INTERP_FOLD_CONV_FULL (MINT_CONV_OVF_U8_I8, VAR_VALUE_I8, l, VAR_VALUE_I8, l, guint64, val->l >= 0); default: return ins; @@ -1139,9 +1139,9 @@ interp_fold_unop (TransformData *td, LocalValue *local_defs, InterpInst *ins) // We were able to compute the result of the ins instruction. We replace the unop // with a LDC of the constant. We leave alone the sregs of this instruction, for // deadce to kill the instructions initializing them. - if (result.type == LOCAL_VALUE_I4) + if (result.type == VAR_VALUE_I4) ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); - else if (result.type == LOCAL_VALUE_I8) + else if (result.type == VAR_VALUE_I8) ins = interp_inst_replace_with_i8_const (td, ins, result.l); else g_assert_not_reached (); @@ -1174,17 +1174,17 @@ interp_fold_unop (TransformData *td, LocalValue *local_defs, InterpInst *ins) break; static InterpInst* -interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) +interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, InterpVarValue *local_defs, InterpInst *ins) { int *local_ref_count = td->local_ref_count; // ins should be an unop conditional branch, therefore it should have a single sreg int sreg = ins->sregs [0]; - LocalValue *val = &local_defs [sreg]; + InterpVarValue *val = &local_defs [sreg]; - if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8 && val->type != LOCAL_VALUE_NON_NULL) + if (val->type != VAR_VALUE_I4 && val->type != VAR_VALUE_I8 && val->type != VAR_VALUE_NON_NULL) return ins; - if (val->type == LOCAL_VALUE_NON_NULL) { + if (val->type == VAR_VALUE_NON_NULL) { switch (ins->opcode) { INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I4, FALSE); INTERP_FOLD_UNOP_BR (MINT_BRFALSE_I8, FALSE); @@ -1237,87 +1237,87 @@ interp_fold_unop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue * #define INTERP_FOLD_RELOP(opcode,local_type,field,relop,cast_type) \ case opcode: \ - result.type = LOCAL_VALUE_I4; \ + result.type = VAR_VALUE_I4; \ result.i = (cast_type) val1->field relop (cast_type) val2->field; \ break; static InterpInst* -interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins, gboolean *folded) +interp_fold_binop (TransformData *td, InterpVarValue *local_defs, InterpInst *ins, gboolean *folded) { int *local_ref_count = td->local_ref_count; // ins should be a binop, therefore it should have a single dreg and two sregs int dreg = ins->dreg; int sreg1 = ins->sregs [0]; int sreg2 = ins->sregs [1]; - LocalValue *val1 = &local_defs [sreg1]; - LocalValue *val2 = &local_defs [sreg2]; - LocalValue result; + InterpVarValue *val1 = &local_defs [sreg1]; + InterpVarValue *val2 = &local_defs [sreg2]; + InterpVarValue result; *folded = FALSE; - if (val1->type != LOCAL_VALUE_I4 && val1->type != LOCAL_VALUE_I8) + if (val1->type != VAR_VALUE_I4 && val1->type != VAR_VALUE_I8) return ins; - if (val2->type != LOCAL_VALUE_I4 && val2->type != LOCAL_VALUE_I8) + if (val2->type != VAR_VALUE_I4 && val2->type != VAR_VALUE_I8) return ins; // Top two values of the stack are constants switch (ins->opcode) { - INTERP_FOLD_BINOP (MINT_ADD_I4, LOCAL_VALUE_I4, i, +); - INTERP_FOLD_BINOP (MINT_ADD_I8, LOCAL_VALUE_I8, l, +); - INTERP_FOLD_BINOP (MINT_SUB_I4, LOCAL_VALUE_I4, i, -); - INTERP_FOLD_BINOP (MINT_SUB_I8, LOCAL_VALUE_I8, l, -); - INTERP_FOLD_BINOP (MINT_MUL_I4, LOCAL_VALUE_I4, i, *); - INTERP_FOLD_BINOP (MINT_MUL_I8, LOCAL_VALUE_I8, l, *); - - INTERP_FOLD_BINOP (MINT_AND_I4, LOCAL_VALUE_I4, i, &); - INTERP_FOLD_BINOP (MINT_AND_I8, LOCAL_VALUE_I8, l, &); - INTERP_FOLD_BINOP (MINT_OR_I4, LOCAL_VALUE_I4, i, |); - INTERP_FOLD_BINOP (MINT_OR_I8, LOCAL_VALUE_I8, l, |); - INTERP_FOLD_BINOP (MINT_XOR_I4, LOCAL_VALUE_I4, i, ^); - INTERP_FOLD_BINOP (MINT_XOR_I8, LOCAL_VALUE_I8, l, ^); - - INTERP_FOLD_SHIFTOP (MINT_SHL_I4, LOCAL_VALUE_I4, i, <<, gint32); - INTERP_FOLD_SHIFTOP (MINT_SHL_I8, LOCAL_VALUE_I8, l, <<, gint64); - INTERP_FOLD_SHIFTOP (MINT_SHR_I4, LOCAL_VALUE_I4, i, >>, gint32); - INTERP_FOLD_SHIFTOP (MINT_SHR_I8, LOCAL_VALUE_I8, l, >>, gint64); - INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4, LOCAL_VALUE_I4, i, >>, guint32); - INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8, LOCAL_VALUE_I8, l, >>, guint64); - - INTERP_FOLD_RELOP (MINT_CEQ_I4, LOCAL_VALUE_I4, i, ==, gint32); - INTERP_FOLD_RELOP (MINT_CEQ_I8, LOCAL_VALUE_I8, l, ==, gint64); - INTERP_FOLD_RELOP (MINT_CNE_I4, LOCAL_VALUE_I4, i, !=, gint32); - INTERP_FOLD_RELOP (MINT_CNE_I8, LOCAL_VALUE_I8, l, !=, gint64); - - INTERP_FOLD_RELOP (MINT_CGT_I4, LOCAL_VALUE_I4, i, >, gint32); - INTERP_FOLD_RELOP (MINT_CGT_I8, LOCAL_VALUE_I8, l, >, gint64); - INTERP_FOLD_RELOP (MINT_CGT_UN_I4, LOCAL_VALUE_I4, i, >, guint32); - INTERP_FOLD_RELOP (MINT_CGT_UN_I8, LOCAL_VALUE_I8, l, >, guint64); - - INTERP_FOLD_RELOP (MINT_CGE_I4, LOCAL_VALUE_I4, i, >=, gint32); - INTERP_FOLD_RELOP (MINT_CGE_I8, LOCAL_VALUE_I8, l, >=, gint64); - INTERP_FOLD_RELOP (MINT_CGE_UN_I4, LOCAL_VALUE_I4, i, >=, guint32); - INTERP_FOLD_RELOP (MINT_CGE_UN_I8, LOCAL_VALUE_I8, l, >=, guint64); - - INTERP_FOLD_RELOP (MINT_CLT_I4, LOCAL_VALUE_I4, i, <, gint32); - INTERP_FOLD_RELOP (MINT_CLT_I8, LOCAL_VALUE_I8, l, <, gint64); - INTERP_FOLD_RELOP (MINT_CLT_UN_I4, LOCAL_VALUE_I4, i, <, guint32); - INTERP_FOLD_RELOP (MINT_CLT_UN_I8, LOCAL_VALUE_I8, l, <, guint64); - - INTERP_FOLD_RELOP (MINT_CLE_I4, LOCAL_VALUE_I4, i, <=, gint32); - INTERP_FOLD_RELOP (MINT_CLE_I8, LOCAL_VALUE_I8, l, <=, gint64); - INTERP_FOLD_RELOP (MINT_CLE_UN_I4, LOCAL_VALUE_I4, i, <=, guint32); - INTERP_FOLD_RELOP (MINT_CLE_UN_I8, LOCAL_VALUE_I8, l, <=, guint64); - - INTERP_FOLD_BINOP_FULL (MINT_DIV_I4, LOCAL_VALUE_I4, i, /, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); - INTERP_FOLD_BINOP_FULL (MINT_DIV_I8, LOCAL_VALUE_I8, l, /, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); - INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4, LOCAL_VALUE_I4, i, /, guint32, val2->i != 0); - INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8, LOCAL_VALUE_I8, l, /, guint64, val2->l != 0); - - INTERP_FOLD_BINOP_FULL (MINT_REM_I4, LOCAL_VALUE_I4, i, %, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); - INTERP_FOLD_BINOP_FULL (MINT_REM_I8, LOCAL_VALUE_I8, l, %, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); - INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4, LOCAL_VALUE_I4, i, %, guint32, val2->i != 0); - INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8, LOCAL_VALUE_I8, l, %, guint64, val2->l != 0); + INTERP_FOLD_BINOP (MINT_ADD_I4, VAR_VALUE_I4, i, +); + INTERP_FOLD_BINOP (MINT_ADD_I8, VAR_VALUE_I8, l, +); + INTERP_FOLD_BINOP (MINT_SUB_I4, VAR_VALUE_I4, i, -); + INTERP_FOLD_BINOP (MINT_SUB_I8, VAR_VALUE_I8, l, -); + INTERP_FOLD_BINOP (MINT_MUL_I4, VAR_VALUE_I4, i, *); + INTERP_FOLD_BINOP (MINT_MUL_I8, VAR_VALUE_I8, l, *); + + INTERP_FOLD_BINOP (MINT_AND_I4, VAR_VALUE_I4, i, &); + INTERP_FOLD_BINOP (MINT_AND_I8, VAR_VALUE_I8, l, &); + INTERP_FOLD_BINOP (MINT_OR_I4, VAR_VALUE_I4, i, |); + INTERP_FOLD_BINOP (MINT_OR_I8, VAR_VALUE_I8, l, |); + INTERP_FOLD_BINOP (MINT_XOR_I4, VAR_VALUE_I4, i, ^); + INTERP_FOLD_BINOP (MINT_XOR_I8, VAR_VALUE_I8, l, ^); + + INTERP_FOLD_SHIFTOP (MINT_SHL_I4, VAR_VALUE_I4, i, <<, gint32); + INTERP_FOLD_SHIFTOP (MINT_SHL_I8, VAR_VALUE_I8, l, <<, gint64); + INTERP_FOLD_SHIFTOP (MINT_SHR_I4, VAR_VALUE_I4, i, >>, gint32); + INTERP_FOLD_SHIFTOP (MINT_SHR_I8, VAR_VALUE_I8, l, >>, gint64); + INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I4, VAR_VALUE_I4, i, >>, guint32); + INTERP_FOLD_SHIFTOP (MINT_SHR_UN_I8, VAR_VALUE_I8, l, >>, guint64); + + INTERP_FOLD_RELOP (MINT_CEQ_I4, VAR_VALUE_I4, i, ==, gint32); + INTERP_FOLD_RELOP (MINT_CEQ_I8, VAR_VALUE_I8, l, ==, gint64); + INTERP_FOLD_RELOP (MINT_CNE_I4, VAR_VALUE_I4, i, !=, gint32); + INTERP_FOLD_RELOP (MINT_CNE_I8, VAR_VALUE_I8, l, !=, gint64); + + INTERP_FOLD_RELOP (MINT_CGT_I4, VAR_VALUE_I4, i, >, gint32); + INTERP_FOLD_RELOP (MINT_CGT_I8, VAR_VALUE_I8, l, >, gint64); + INTERP_FOLD_RELOP (MINT_CGT_UN_I4, VAR_VALUE_I4, i, >, guint32); + INTERP_FOLD_RELOP (MINT_CGT_UN_I8, VAR_VALUE_I8, l, >, guint64); + + INTERP_FOLD_RELOP (MINT_CGE_I4, VAR_VALUE_I4, i, >=, gint32); + INTERP_FOLD_RELOP (MINT_CGE_I8, VAR_VALUE_I8, l, >=, gint64); + INTERP_FOLD_RELOP (MINT_CGE_UN_I4, VAR_VALUE_I4, i, >=, guint32); + INTERP_FOLD_RELOP (MINT_CGE_UN_I8, VAR_VALUE_I8, l, >=, guint64); + + INTERP_FOLD_RELOP (MINT_CLT_I4, VAR_VALUE_I4, i, <, gint32); + INTERP_FOLD_RELOP (MINT_CLT_I8, VAR_VALUE_I8, l, <, gint64); + INTERP_FOLD_RELOP (MINT_CLT_UN_I4, VAR_VALUE_I4, i, <, guint32); + INTERP_FOLD_RELOP (MINT_CLT_UN_I8, VAR_VALUE_I8, l, <, guint64); + + INTERP_FOLD_RELOP (MINT_CLE_I4, VAR_VALUE_I4, i, <=, gint32); + INTERP_FOLD_RELOP (MINT_CLE_I8, VAR_VALUE_I8, l, <=, gint64); + INTERP_FOLD_RELOP (MINT_CLE_UN_I4, VAR_VALUE_I4, i, <=, guint32); + INTERP_FOLD_RELOP (MINT_CLE_UN_I8, VAR_VALUE_I8, l, <=, guint64); + + INTERP_FOLD_BINOP_FULL (MINT_DIV_I4, VAR_VALUE_I4, i, /, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); + INTERP_FOLD_BINOP_FULL (MINT_DIV_I8, VAR_VALUE_I8, l, /, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); + INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I4, VAR_VALUE_I4, i, /, guint32, val2->i != 0); + INTERP_FOLD_BINOP_FULL (MINT_DIV_UN_I8, VAR_VALUE_I8, l, /, guint64, val2->l != 0); + + INTERP_FOLD_BINOP_FULL (MINT_REM_I4, VAR_VALUE_I4, i, %, gint32, val2->i != 0 && (val1->i != G_MININT32 || val2->i != -1)); + INTERP_FOLD_BINOP_FULL (MINT_REM_I8, VAR_VALUE_I8, l, %, gint64, val2->l != 0 && (val1->l != G_MININT64 || val2->l != -1)); + INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I4, VAR_VALUE_I4, i, %, guint32, val2->i != 0); + INTERP_FOLD_BINOP_FULL (MINT_REM_UN_I8, VAR_VALUE_I8, l, %, guint64, val2->l != 0); default: return ins; @@ -1327,9 +1327,9 @@ interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins, g // with a LDC of the constant. We leave alone the sregs of this instruction, for // deadce to kill the instructions initializing them. *folded = TRUE; - if (result.type == LOCAL_VALUE_I4) + if (result.type == VAR_VALUE_I4) ins = interp_get_ldc_i4_from_const (td, ins, result.i, dreg); - else if (result.type == LOCAL_VALUE_I8) + else if (result.type == VAR_VALUE_I8) ins = interp_inst_replace_with_i8_const (td, ins, result.l); else g_assert_not_reached (); @@ -1366,42 +1366,42 @@ interp_fold_binop (TransformData *td, LocalValue *local_defs, InterpInst *ins, g break; static InterpInst* -interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) +interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, InterpVarValue *local_defs, InterpInst *ins) { int *local_ref_count = td->local_ref_count; // ins should be a conditional binop, therefore it should have only two sregs int sreg1 = ins->sregs [0]; int sreg2 = ins->sregs [1]; - LocalValue *val1 = &local_defs [sreg1]; - LocalValue *val2 = &local_defs [sreg2]; + InterpVarValue *val1 = &local_defs [sreg1]; + InterpVarValue *val2 = &local_defs [sreg2]; - if (val1->type != LOCAL_VALUE_I4 && val1->type != LOCAL_VALUE_I8) + if (val1->type != VAR_VALUE_I4 && val1->type != VAR_VALUE_I8) return ins; - if (val2->type != LOCAL_VALUE_I4 && val2->type != LOCAL_VALUE_I8) + if (val2->type != VAR_VALUE_I4 && val2->type != VAR_VALUE_I8) return ins; switch (ins->opcode) { - INTERP_FOLD_BINOP_BR (MINT_BEQ_I4, LOCAL_VALUE_I4, val1->i == val2->i); - INTERP_FOLD_BINOP_BR (MINT_BEQ_I8, LOCAL_VALUE_I8, val1->l == val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGE_I4, LOCAL_VALUE_I4, val1->i >= val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGE_I8, LOCAL_VALUE_I8, val1->l >= val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGT_I4, LOCAL_VALUE_I4, val1->i > val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGT_I8, LOCAL_VALUE_I8, val1->l > val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLT_I4, LOCAL_VALUE_I4, val1->i < val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLT_I8, LOCAL_VALUE_I8, val1->l < val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLE_I4, LOCAL_VALUE_I4, val1->i <= val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLE_I8, LOCAL_VALUE_I8, val1->l <= val2->l); - - INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I4, LOCAL_VALUE_I4, val1->i != val2->i); - INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I8, LOCAL_VALUE_I8, val1->l != val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i >= (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l >= (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i > (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l > (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i <= (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l <= (guint64)val2->l); - INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I4, LOCAL_VALUE_I4, (guint32)val1->i < (guint32)val2->i); - INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I8, LOCAL_VALUE_I8, (guint64)val1->l < (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BEQ_I4, VAR_VALUE_I4, val1->i == val2->i); + INTERP_FOLD_BINOP_BR (MINT_BEQ_I8, VAR_VALUE_I8, val1->l == val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGE_I4, VAR_VALUE_I4, val1->i >= val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGE_I8, VAR_VALUE_I8, val1->l >= val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGT_I4, VAR_VALUE_I4, val1->i > val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGT_I8, VAR_VALUE_I8, val1->l > val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLT_I4, VAR_VALUE_I4, val1->i < val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLT_I8, VAR_VALUE_I8, val1->l < val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLE_I4, VAR_VALUE_I4, val1->i <= val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLE_I8, VAR_VALUE_I8, val1->l <= val2->l); + + INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I4, VAR_VALUE_I4, val1->i != val2->i); + INTERP_FOLD_BINOP_BR (MINT_BNE_UN_I8, VAR_VALUE_I8, val1->l != val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I4, VAR_VALUE_I4, (guint32)val1->i >= (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGE_UN_I8, VAR_VALUE_I8, (guint64)val1->l >= (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I4, VAR_VALUE_I4, (guint32)val1->i > (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BGT_UN_I8, VAR_VALUE_I8, (guint64)val1->l > (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I4, VAR_VALUE_I4, (guint32)val1->i <= (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLE_UN_I8, VAR_VALUE_I8, (guint64)val1->l <= (guint64)val2->l); + INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I4, VAR_VALUE_I4, (guint32)val1->i < (guint32)val2->i); + INTERP_FOLD_BINOP_BR (MINT_BLT_UN_I8, VAR_VALUE_I8, (guint64)val1->l < (guint64)val2->l); default: return ins; @@ -1417,7 +1417,7 @@ interp_fold_binop_cond_br (TransformData *td, InterpBasicBlock *cbb, LocalValue } static void -write_v128_element (gpointer v128_addr, LocalValue *val, int index, int el_size) +write_v128_element (gpointer v128_addr, InterpVarValue *val, int index, int el_size) { gpointer el_addr = (gint8*)v128_addr + index * el_size; g_assert ((gint8*)el_addr < ((gint8*)v128_addr + 16)); @@ -1432,7 +1432,7 @@ write_v128_element (gpointer v128_addr, LocalValue *val, int index, int el_size) } static InterpInst* -interp_fold_simd_create (TransformData *td, InterpBasicBlock *cbb, LocalValue *local_defs, InterpInst *ins) +interp_fold_simd_create (TransformData *td, InterpBasicBlock *cbb, InterpVarValue *local_defs, InterpInst *ins) { int *local_ref_count = td->local_ref_count; @@ -1440,8 +1440,8 @@ interp_fold_simd_create (TransformData *td, InterpBasicBlock *cbb, LocalValue *l int index = 0; int var = args [index]; while (var != -1) { - LocalValue *val = &local_defs [var]; - if (val->type != LOCAL_VALUE_I4 && val->type != LOCAL_VALUE_I8 && val->type != LOCAL_VALUE_R4) + InterpVarValue *val = &local_defs [var]; + if (val->type != VAR_VALUE_I4 && val->type != VAR_VALUE_I8 && val->type != VAR_VALUE_R4) return ins; index++; var = args [index]; @@ -1461,7 +1461,7 @@ interp_fold_simd_create (TransformData *td, InterpBasicBlock *cbb, LocalValue *l index = 0; var = args [index]; while (var != -1) { - LocalValue *val = &local_defs [var]; + InterpVarValue *val = &local_defs [var]; write_v128_element (v128_addr, val, index, el_size); val->ref_count--; local_ref_count [var]--; @@ -1475,21 +1475,21 @@ interp_fold_simd_create (TransformData *td, InterpBasicBlock *cbb, LocalValue *l } local_defs [dreg].ins = ins; - local_defs [dreg].type = LOCAL_VALUE_NONE; + local_defs [dreg].type = VAR_VALUE_NONE; return ins; } static void -cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, LocalValue *local_defs) +cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, InterpVarValue *local_defs) { int *local_ref_count = td->local_ref_count; int sreg = *psreg; local_ref_count [sreg]++; local_defs [sreg].ref_count++; - if (local_defs [sreg].type == LOCAL_VALUE_LOCAL) { - int cprop_local = local_defs [sreg].local; + if (local_defs [sreg].type == VAR_VALUE_OTHER_VAR) { + int cprop_local = local_defs [sreg].var; // We are trying to replace sregs [i] with its def local (cprop_local), but cprop_local has since been // modified, so we can't use it. @@ -1504,7 +1504,7 @@ cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, LocalValue *local_de if (td->verbose_level) interp_dump_ins (ins, td->data_items); } else if (!local_defs [sreg].ins) { - td->locals [sreg].flags |= INTERP_LOCAL_FLAG_UNKNOWN_USE; + td->vars [sreg].flags |= INTERP_LOCAL_FLAG_UNKNOWN_USE; } } @@ -1512,8 +1512,8 @@ static void clear_local_defs (TransformData *td, int *pvar, void *data) { int var = *pvar; - LocalValue *local_defs = (LocalValue*) data; - local_defs [var].type = LOCAL_VALUE_NONE; + InterpVarValue *local_defs = (InterpVarValue*) data; + local_defs [var].type = VAR_VALUE_NONE; local_defs [var].ins = NULL; local_defs [var].ref_count = 0; } @@ -1522,12 +1522,12 @@ static void clear_unused_defs (TransformData *td, int *pvar, void *data) { int var = *pvar; - if (!(td->locals [var].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) + if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) return; - if (td->locals [var].indirects) + if (td->vars [var].indirects) return; - LocalValue *local_def = &((LocalValue*) data) [var]; + InterpVarValue *local_def = &((InterpVarValue*) data) [var]; InterpInst *def_ins = local_def->ins; if (!def_ins) return; @@ -1550,8 +1550,8 @@ clear_unused_defs (TransformData *td, int *pvar, void *data) static void interp_cprop (TransformData *td) { - LocalValue *local_defs = (LocalValue*) g_malloc (td->locals_size * sizeof (LocalValue)); - int *local_ref_count = (int*) g_malloc (td->locals_size * sizeof (int)); + InterpVarValue *local_defs = (InterpVarValue*) g_malloc (td->vars_size * sizeof (InterpVarValue)); + int *local_ref_count = (int*) g_malloc (td->vars_size * sizeof (int)); InterpBasicBlock *bb; gboolean needs_retry; int ins_index; @@ -1560,7 +1560,7 @@ interp_cprop (TransformData *td) td->local_ref_count = local_ref_count; retry: needs_retry = FALSE; - memset (local_ref_count, 0, td->locals_size * sizeof (int)); + memset (local_ref_count, 0, td->vars_size * sizeof (int)); if (td->verbose_level) g_print ("\ncprop iteration %d\n", iteration_count++); @@ -1616,7 +1616,7 @@ interp_cprop (TransformData *td) // MINT_MOV_DST_OFF doesn't fully write to the var, so we special case it here if (local_defs [dreg].ins != NULL && local_defs [dreg].ref_count == 0 && - !td->locals [dreg].indirects && + !td->vars [dreg].indirects && opcode != MINT_MOV_DST_OFF) { InterpInst *prev_def = local_defs [dreg].ins; if (MINT_NO_SIDE_EFFECTS (prev_def->opcode)) { @@ -1625,14 +1625,14 @@ interp_cprop (TransformData *td) interp_clear_ins (prev_def); } } - local_defs [dreg].type = LOCAL_VALUE_NONE; + local_defs [dreg].type = VAR_VALUE_NONE; local_defs [dreg].ins = ins; local_defs [dreg].def_index = ins_index; } // We always store to the full i4, except as part of STIND opcodes. These opcodes can be // applied to a local var only if that var has LDLOCA applied to it - if ((opcode >= MINT_MOV_I4_I1 && opcode <= MINT_MOV_I4_U2) && !td->locals [sregs [0]].indirects) { + if ((opcode >= MINT_MOV_I4_I1 && opcode <= MINT_MOV_I4_U2) && !td->vars [sregs [0]].indirects) { ins->opcode = MINT_MOV_4; opcode = MINT_MOV_4; } @@ -1644,12 +1644,12 @@ interp_cprop (TransformData *td) g_print ("clear redundant mov\n"); interp_clear_ins (ins); local_ref_count [sreg]--; - } else if (td->locals [sreg].indirects || td->locals [dreg].indirects) { + } else if (td->vars [sreg].indirects || td->vars [dreg].indirects) { // Don't bother with indirect locals - } else if (local_defs [sreg].type == LOCAL_VALUE_I4 || local_defs [sreg].type == LOCAL_VALUE_I8) { + } else if (local_defs [sreg].type == VAR_VALUE_I4 || local_defs [sreg].type == VAR_VALUE_I8) { // Replace mov with ldc - gboolean is_i4 = local_defs [sreg].type == LOCAL_VALUE_I4; - g_assert (!td->locals [sreg].indirects); + gboolean is_i4 = local_defs [sreg].type == VAR_VALUE_I4; + g_assert (!td->vars [sreg].indirects); local_defs [dreg].type = local_defs [sreg].type; if (is_i4) { int ct = local_defs [sreg].i; @@ -1667,8 +1667,8 @@ interp_cprop (TransformData *td) interp_dump_ins (ins, td->data_items); } } else if (local_defs [sreg].ins != NULL && - (td->locals [sreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && - !(td->locals [dreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && + (td->vars [sreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && + !(td->vars [dreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && interp_prev_ins (ins) == local_defs [sreg].ins && !(interp_prev_ins (ins)->flags & INTERP_INST_FLAG_PROTECTED_NEWOBJ)) { // hackish temporary optimization that won't be necessary in the future @@ -1683,13 +1683,13 @@ interp_cprop (TransformData *td) ins->dreg = original_dreg; sregs [0] = dreg; - local_defs [dreg].type = LOCAL_VALUE_NONE; + local_defs [dreg].type = VAR_VALUE_NONE; local_defs [dreg].ins = def; local_defs [dreg].def_index = local_defs [original_dreg].def_index; local_defs [dreg].ref_count++; - local_defs [original_dreg].type = LOCAL_VALUE_LOCAL; + local_defs [original_dreg].type = VAR_VALUE_OTHER_VAR; local_defs [original_dreg].ins = ins; - local_defs [original_dreg].local = dreg; + local_defs [original_dreg].var = dreg; local_defs [original_dreg].def_index = ins_index; local_defs [original_dreg].ref_count--; @@ -1705,29 +1705,29 @@ interp_cprop (TransformData *td) } else { if (td->verbose_level) g_print ("local copy %d <- %d\n", dreg, sreg); - local_defs [dreg].type = LOCAL_VALUE_LOCAL; - local_defs [dreg].local = sreg; + local_defs [dreg].type = VAR_VALUE_OTHER_VAR; + local_defs [dreg].var = sreg; } } else if (opcode == MINT_LDLOCA_S) { // The local that we are taking the address of is not a sreg but still referenced local_ref_count [ins->sregs [0]]++; } else if (MINT_IS_LDC_I4 (opcode)) { - local_defs [dreg].type = LOCAL_VALUE_I4; + local_defs [dreg].type = VAR_VALUE_I4; local_defs [dreg].i = interp_get_const_from_ldc_i4 (ins); } else if (MINT_IS_LDC_I8 (opcode)) { - local_defs [dreg].type = LOCAL_VALUE_I8; + local_defs [dreg].type = VAR_VALUE_I8; local_defs [dreg].l = interp_get_const_from_ldc_i8 (ins); } else if (opcode == MINT_LDC_R4) { guint32 val_u = READ32 (&ins->data [0]); float f = *(float*)(&val_u); - local_defs [dreg].type = LOCAL_VALUE_R4; + local_defs [dreg].type = VAR_VALUE_R4; local_defs [dreg].f = f; } else if (ins->opcode == MINT_LDPTR) { #if SIZEOF_VOID_P == 8 - local_defs [dreg].type = LOCAL_VALUE_I8; + local_defs [dreg].type = VAR_VALUE_I8; local_defs [dreg].l = (gint64)td->data_items [ins->data [0]]; #else - local_defs [dreg].type = LOCAL_VALUE_I4; + local_defs [dreg].type = VAR_VALUE_I4; local_defs [dreg].i = (gint32)td->data_items [ins->data [0]]; #endif } else if (MINT_IS_UNOP (opcode)) { @@ -1743,22 +1743,22 @@ interp_cprop (TransformData *td) int sreg = -1; guint16 mov_op = 0; if ((opcode == MINT_MUL_I4 || opcode == MINT_DIV_I4) && - local_defs [ins->sregs [1]].type == LOCAL_VALUE_I4 && + local_defs [ins->sregs [1]].type == VAR_VALUE_I4 && local_defs [ins->sregs [1]].i == 1) { sreg = ins->sregs [0]; mov_op = MINT_MOV_4; } else if ((opcode == MINT_MUL_I8 || opcode == MINT_DIV_I8) && - local_defs [ins->sregs [1]].type == LOCAL_VALUE_I8 && + local_defs [ins->sregs [1]].type == VAR_VALUE_I8 && local_defs [ins->sregs [1]].l == 1) { sreg = ins->sregs [0]; mov_op = MINT_MOV_8; } else if (opcode == MINT_MUL_I4 && - local_defs [ins->sregs [0]].type == LOCAL_VALUE_I4 && + local_defs [ins->sregs [0]].type == VAR_VALUE_I4 && local_defs [ins->sregs [0]].i == 1) { sreg = ins->sregs [1]; mov_op = MINT_MOV_4; } else if (opcode == MINT_MUL_I8 && - local_defs [ins->sregs [0]].type == LOCAL_VALUE_I8 && + local_defs [ins->sregs [0]].type == VAR_VALUE_I8 && local_defs [ins->sregs [0]].l == 1) { sreg = ins->sregs [1]; mov_op = MINT_MOV_8; @@ -1779,7 +1779,7 @@ interp_cprop (TransformData *td) InterpInst *ldloca = local_defs [sregs [0]].ins; if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { int local = ldloca->sregs [0]; - int mt = td->locals [local].mt; + int mt = td->vars [local].mt; if (mt != MINT_TYPE_VT) { // LDIND cannot simply be replaced with a mov because it might also include a // necessary conversion (especially when we do cprop and do moves between vars of @@ -1811,7 +1811,7 @@ interp_cprop (TransformData *td) int local = ldloca->sregs [0]; // Allow ldloca instruction to be killed local_ref_count [sregs [0]]--; - if (td->locals [local].mt == (ins->opcode - MINT_LDFLD_I1) && ins->data [0] == 0) { + if (td->vars [local].mt == (ins->opcode - MINT_LDFLD_I1) && ins->data [0] == 0) { // Replace LDLOCA + LDFLD with LDLOC, when the loading field represents // the entire local. This is the case with loading the only field of an // IntPtr. We don't handle value type loads. @@ -1871,7 +1871,7 @@ interp_cprop (TransformData *td) int local = ldloca->sregs [0]; local_ref_count [sregs [0]]--; - if (ldsize == td->locals [local].size) { + if (ldsize == td->vars [local].size) { // Replace LDLOCA + LDOBJ_VT with MOV_VT ins->opcode = MINT_MOV_VT; sregs [0] = local; @@ -1898,7 +1898,7 @@ interp_cprop (TransformData *td) int stsize = ins->data [0]; int local = ldloca->sregs [0]; - if (stsize == td->locals [local].size) { + if (stsize == td->vars [local].size) { // Replace LDLOCA + STOBJ_VT with MOV_VT local_ref_count [sregs [0]]--; ins->opcode = MINT_MOV_VT; @@ -1916,7 +1916,7 @@ interp_cprop (TransformData *td) InterpInst *ldloca = local_defs [sregs [0]].ins; if (ldloca != NULL && ldloca->opcode == MINT_LDLOCA_S) { int local = ldloca->sregs [0]; - int mt = td->locals [local].mt; + int mt = td->vars [local].mt; if (mt != MINT_TYPE_VT) { // We have an 8 byte local, just replace the stind with a mov local_ref_count [sregs [0]]--; @@ -1939,7 +1939,7 @@ interp_cprop (TransformData *td) int local = ldloca->sregs [0]; local_ref_count [sregs [0]]--; // Allow ldloca instruction to be killed - if (td->locals [local].mt == (ins->opcode - MINT_STFLD_I1) && ins->data [0] == 0) { + if (td->vars [local].mt == (ins->opcode - MINT_STFLD_I1) && ins->data [0] == 0) { ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, FALSE)); // The sreg of the MOV is the same as the second sreg of the STFLD ins->dreg = local; @@ -1995,7 +1995,7 @@ interp_cprop (TransformData *td) } } else if (opcode == MINT_BOX) { // TODO Add more relevant opcodes - local_defs [dreg].type = LOCAL_VALUE_NON_NULL; + local_defs [dreg].type = VAR_VALUE_NON_NULL; } ins_index++; @@ -2024,7 +2024,7 @@ mono_test_interp_cprop (TransformData *td) static gboolean get_sreg_imm (TransformData *td, int sreg, gint16 *imm, int result_mt) { - InterpInst *def = td->locals [sreg].def; + InterpInst *def = td->vars [sreg].def; if (def != NULL && td->local_ref_count [sreg] == 1) { gint64 ct; if (MINT_IS_LDC_I4 (def->opcode)) @@ -2153,15 +2153,15 @@ interp_super_instructions (TransformData *td) int opcode = ins->opcode; if (MINT_IS_NOP (opcode)) continue; - if (mono_interp_op_dregs [opcode] && !(td->locals [ins->dreg].flags & INTERP_LOCAL_FLAG_GLOBAL)) - td->locals [ins->dreg].def = ins; + if (mono_interp_op_dregs [opcode] && !(td->vars [ins->dreg].flags & INTERP_LOCAL_FLAG_GLOBAL)) + td->vars [ins->dreg].def = ins; if (opcode == MINT_RET || (opcode >= MINT_RET_I1 && opcode <= MINT_RET_U2)) { // ldc + ret -> ret.imm int sreg = ins->sregs [0]; gint16 imm; if (get_sreg_imm (td, sreg, &imm, (opcode == MINT_RET) ? MINT_TYPE_I2 : opcode - MINT_RET_I1)) { - InterpInst *def = td->locals [sreg].def; + InterpInst *def = td->vars [sreg].def; int ret_op = MINT_IS_LDC_I4 (def->opcode) ? MINT_RET_I4_IMM : MINT_RET_I8_IMM; InterpInst *new_inst = interp_insert_ins (td, ins, ret_op); new_inst->data [0] = imm; @@ -2199,7 +2199,7 @@ interp_super_instructions (TransformData *td) new_inst->dreg = ins->dreg; new_inst->sregs [0] = sreg; new_inst->data [0] = imm; - interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (td->vars [sreg_imm].def); interp_clear_ins (ins); local_ref_count [sreg_imm]--; if (td->verbose_level) { @@ -2217,7 +2217,7 @@ interp_super_instructions (TransformData *td) new_inst->dreg = ins->dreg; new_inst->sregs [0] = ins->sregs [0]; new_inst->data [0] = -imm; - interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (td->vars [sreg_imm].def); interp_clear_ins (ins); local_ref_count [sreg_imm]--; if (td->verbose_level) { @@ -2227,7 +2227,7 @@ interp_super_instructions (TransformData *td) } } else if (opcode == MINT_MUL_I4_IMM || opcode == MINT_MUL_I8_IMM) { int sreg = ins->sregs [0]; - InterpInst *def = td->locals [sreg].def; + InterpInst *def = td->vars [sreg].def; if (def != NULL && td->local_ref_count [sreg] == 1) { gboolean is_i4 = opcode == MINT_MUL_I4_IMM; if ((is_i4 && def->opcode == MINT_ADD_I4_IMM) || @@ -2256,7 +2256,7 @@ interp_super_instructions (TransformData *td) new_inst->dreg = ins->dreg; new_inst->sregs [0] = ins->sregs [0]; new_inst->data [0] = imm; - interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (td->vars [sreg_imm].def); interp_clear_ins (ins); local_ref_count [sreg_imm]--; if (td->verbose_level) { @@ -2265,7 +2265,7 @@ interp_super_instructions (TransformData *td) } } else if (opcode == MINT_SHL_I4 || opcode == MINT_SHL_I8) { int amount_var = ins->sregs [1]; - InterpInst *amount_def = td->locals [amount_var].def; + InterpInst *amount_def = td->vars [amount_var].def; if (amount_def != NULL && td->local_ref_count [amount_var] == 1 && amount_def->opcode == MINT_AND_I4) { int mask_var = amount_def->sregs [1]; if (get_sreg_imm (td, mask_var, &imm, MINT_TYPE_I2)) { @@ -2285,7 +2285,7 @@ interp_super_instructions (TransformData *td) local_ref_count [amount_var]--; local_ref_count [mask_var]--; - interp_clear_ins (td->locals [mask_var].def); + interp_clear_ins (td->vars [mask_var].def); interp_clear_ins (amount_def); interp_clear_ins (ins); if (td->verbose_level) { @@ -2299,7 +2299,7 @@ interp_super_instructions (TransformData *td) } else if (opcode == MINT_DIV_UN_I4 || opcode == MINT_DIV_UN_I8) { // ldc + div.un -> shr.imm int sreg_imm = ins->sregs [1]; - InterpInst *def = td->locals [sreg_imm].def; + InterpInst *def = td->vars [sreg_imm].def; if (def != NULL && td->local_ref_count [sreg_imm] == 1) { int power2 = -1; if (MINT_IS_LDC_I4 (def->opcode)) { @@ -2331,7 +2331,7 @@ interp_super_instructions (TransformData *td) } } else if (MINT_IS_LDIND_INT (opcode)) { int sreg_base = ins->sregs [0]; - InterpInst *def = td->locals [sreg_base].def; + InterpInst *def = td->vars [sreg_base].def; if (def != NULL && td->local_ref_count [sreg_base] == 1) { InterpInst *new_inst = NULL; if (def->opcode == MINT_ADD_P) { @@ -2359,7 +2359,7 @@ interp_super_instructions (TransformData *td) } } else if (MINT_IS_LDIND_OFFSET (opcode)) { int sreg_off = ins->sregs [1]; - InterpInst *def = td->locals [sreg_off].def; + InterpInst *def = td->vars [sreg_off].def; if (def != NULL && td->local_ref_count [sreg_off] == 1) { if (def->opcode == MINT_MUL_P_IMM || def->opcode == MINT_ADD_P_IMM || def->opcode == MINT_ADD_MUL_P_IMM) { int ldind_offset_op = MINT_LDIND_OFFSET_ADD_MUL_IMM_I1 + (opcode - MINT_LDIND_OFFSET_I1); @@ -2395,7 +2395,7 @@ interp_super_instructions (TransformData *td) } } else if (MINT_IS_STIND_INT (opcode)) { int sreg_base = ins->sregs [0]; - InterpInst *def = td->locals [sreg_base].def; + InterpInst *def = td->vars [sreg_base].def; if (def != NULL && td->local_ref_count [sreg_base] == 1) { InterpInst *new_inst = NULL; if (def->opcode == MINT_ADD_P) { @@ -2427,7 +2427,7 @@ interp_super_instructions (TransformData *td) // when inlining property accessors. We should have more advanced cknull removal // optimzations, so we can catch cases where instructions are not next to each other. int obj_sreg = ins->sregs [0]; - InterpInst *def = td->locals [obj_sreg].def; + InterpInst *def = td->vars [obj_sreg].def; if (def != NULL && def->opcode == MINT_CKNULL && interp_prev_ins (ins) == def && def->dreg == obj_sreg && local_ref_count [obj_sreg] == 1) { if (td->verbose_level) { @@ -2452,7 +2452,7 @@ interp_super_instructions (TransformData *td) new_ins->sregs [0] = ins->sregs [0]; new_ins->data [0] = imm; new_ins->info.target_bb = ins->info.target_bb; - interp_clear_ins (td->locals [sreg_imm].def); + interp_clear_ins (td->vars [sreg_imm].def); interp_clear_ins (ins); local_ref_count [sreg_imm]--; if (td->verbose_level) { @@ -2478,7 +2478,7 @@ interp_super_instructions (TransformData *td) if (opcode == MINT_BRFALSE_I4 || opcode == MINT_BRTRUE_I4) { gboolean negate = opcode == MINT_BRFALSE_I4; int cond_sreg = ins->sregs [0]; - InterpInst *def = td->locals [cond_sreg].def; + InterpInst *def = td->vars [cond_sreg].def; if (def != NULL && local_ref_count [cond_sreg] == 1) { int replace_opcode = -1; switch (def->opcode) { @@ -2538,7 +2538,7 @@ interp_super_instructions (TransformData *td) } } else if (opcode == MINT_STOBJ_VT_NOREF) { int sreg_src = ins->sregs [1]; - InterpInst *def = td->locals [sreg_src].def; + InterpInst *def = td->vars [sreg_src].def; if (def != NULL && interp_prev_ins (ins) == def && def->opcode == MINT_LDOBJ_VT && ins->data [0] == def->data [0] && td->local_ref_count [sreg_src] == 1) { InterpInst *new_inst = interp_insert_ins (td, ins, MINT_CPOBJ_VT_NOREF); new_inst->sregs [0] = ins->sregs [0]; // dst diff --git a/src/mono/mono/mini/interp/transform-simd.c b/src/mono/mono/mini/interp/transform-simd.c index 45fdb8ed592046..ee0501db4658e2 100644 --- a/src/mono/mono/mini/interp/transform-simd.c +++ b/src/mono/mono/mini/interp/transform-simd.c @@ -315,7 +315,7 @@ emit_common_simd_epilogue (TransformData *td, MonoClass *vector_klass, MonoMetho { td->sp -= csignature->param_count; for (int i = 0; i < csignature->param_count; i++) - td->last_ins->sregs [i] = td->sp [i].local; + td->last_ins->sregs [i] = td->sp [i].var; int ret_mt = mono_mint_type (csignature->ret); if (csignature->ret->type == MONO_TYPE_VOID) { @@ -324,10 +324,10 @@ emit_common_simd_epilogue (TransformData *td, MonoClass *vector_klass, MonoMetho } else if (ret_mt == MINT_TYPE_VT) { // For these intrinsics, if we return a VT then it is a V128 push_type_vt (td, vector_klass, vector_size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { push_simple_type (td, stack_type [ret_mt]); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip += 5; } @@ -347,14 +347,14 @@ emit_vector_create (TransformData *td, MonoMethodSignature *csignature, MonoClas int *call_args = (int*)mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); td->sp -= csignature->param_count; for (int i = 0; i < num_args; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [num_args] = -1; init_last_ins_call (td); td->last_ins->info.call_info->call_args = call_args; if (!td->optimized) td->last_ins->info.call_info->call_offset = get_tos_offset (td); push_type_vt (td, vector_klass, vector_size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static gboolean diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 9224dda5558cb0..8bb7f1285bf826 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -267,7 +267,7 @@ get_stack_size (TransformData *td, StackInfo *sp, int count) int result = 0; for (int i = 0; i < count; i++) { result += sp [i].size; - if (td->locals [sp [i].local].flags & INTERP_LOCAL_FLAG_SIMD) + if (td->vars [sp [i].var].flags & INTERP_LOCAL_FLAG_SIMD) result = ALIGN_TO (result, MINT_SIMD_ALIGNMENT); } return result; @@ -356,16 +356,16 @@ mono_mint_type (MonoType *type) * way, with an offset relative to the frame->locals. */ static int -interp_create_local_explicit (TransformData *td, MonoType *type, int size) +interp_create_var_explicit (TransformData *td, MonoType *type, int size) { - if (td->locals_size == td->locals_capacity) { - td->locals_capacity *= 2; - if (td->locals_capacity == 0) - td->locals_capacity = 2; - td->locals = (InterpLocal*) g_realloc (td->locals, td->locals_capacity * sizeof (InterpLocal)); + if (td->vars_size == td->vars_capacity) { + td->vars_capacity *= 2; + if (td->vars_capacity == 0) + td->vars_capacity = 2; + td->vars = (InterpVar*) g_realloc (td->vars, td->vars_capacity * sizeof (InterpVar)); } int mt = mono_mint_type (type); - InterpLocal *local = &td->locals [td->locals_size]; + InterpVar *local = &td->vars [td->vars_size]; local->type = type; local->mt = mt; @@ -379,8 +379,8 @@ interp_create_local_explicit (TransformData *td, MonoType *type, int size) local->bb_index = -1; local->def = NULL; - td->locals_size++; - return td->locals_size - 1; + td->vars_size++; + return td->vars_size - 1; } @@ -388,9 +388,9 @@ static void interp_create_dummy_var (TransformData *td) { g_assert (td->dummy_var < 0); - td->dummy_var = interp_create_local_explicit (td, m_class_get_byval_arg (mono_defaults.void_class), 8); - td->locals [td->dummy_var].offset = 0; - td->locals [td->dummy_var].flags = INTERP_LOCAL_FLAG_GLOBAL; + td->dummy_var = interp_create_var_explicit (td, m_class_get_byval_arg (mono_defaults.void_class), 8); + td->vars [td->dummy_var].offset = 0; + td->vars [td->dummy_var].flags = INTERP_LOCAL_FLAG_GLOBAL; } static int @@ -404,12 +404,12 @@ get_tos_offset (TransformData *td) // Create a local for sp static void -interp_create_stack_local (TransformData *td, StackInfo *sp, int type_size) +interp_create_stack_var (TransformData *td, StackInfo *sp, int type_size) { - int local = interp_create_local_explicit (td, get_type_from_stack (sp->type, sp->klass), type_size); + int local = interp_create_var_explicit (td, get_type_from_stack (sp->type, sp->klass), type_size); - td->locals [local].flags |= INTERP_LOCAL_FLAG_EXECUTION_STACK; - sp->local = local; + td->vars [local].flags |= INTERP_LOCAL_FLAG_EXECUTION_STACK; + sp->var = local; } static void @@ -432,12 +432,12 @@ push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) sp->klass = k; sp->flags = 0; sp->size = ALIGN_TO (type_size, MINT_STACK_SLOT_SIZE); - interp_create_stack_local (td, sp, type_size); + interp_create_stack_var (td, sp, type_size); if (!td->optimized) { sp->offset = get_tos_offset (td); - if (td->locals [sp->local].flags & INTERP_LOCAL_FLAG_SIMD) + if (td->vars [sp->var].flags & INTERP_LOCAL_FLAG_SIMD) sp->offset = ALIGN_TO (sp->offset, MINT_SIMD_ALIGNMENT); - td->locals [sp->local].stack_offset = sp->offset; + td->vars [sp->var].stack_offset = sp->offset; // Additional space that is allocated for the frame, when we don't run the var offset allocator ENSURE_STACK_SIZE(td, sp->offset + sp->size); } @@ -447,13 +447,13 @@ push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) static void push_var (TransformData *td, int var_index) { - InterpLocal *var = &td->locals [var_index]; + InterpVar *var = &td->vars [var_index]; ensure_stack (td, 1); StackInfo *sp = td->sp; sp->type = GINT_TO_UINT8 (stack_type [var->mt]); sp->klass = mono_class_from_mono_type_internal (var->type); sp->flags = 0; - sp->local = var_index; + sp->var = var_index; sp->size = ALIGN_TO (var->size, MINT_STACK_SLOT_SIZE); td->sp++; } @@ -479,18 +479,18 @@ push_var (TransformData *td, int var_index) } while (0) static void -set_type_and_local (TransformData *td, StackInfo *sp, int type, MonoClass *klass) +set_type_and_var (TransformData *td, StackInfo *sp, int type, MonoClass *klass) { SET_TYPE (sp, type, klass); - interp_create_stack_local (td, sp, MINT_STACK_SLOT_SIZE); + interp_create_stack_var (td, sp, MINT_STACK_SLOT_SIZE); if (!td->optimized) - td->locals [sp->local].stack_offset = sp->offset; + td->vars [sp->var].stack_offset = sp->offset; } static void -set_simple_type_and_local (TransformData *td, StackInfo *sp, int type) +set_simple_type_and_var (TransformData *td, StackInfo *sp, int type) { - set_type_and_local (td, sp, type, NULL); + set_type_and_var (td, sp, type, NULL); } static void @@ -588,22 +588,22 @@ fixup_newbb_stack_locals (TransformData *td, InterpBasicBlock *newbb) return; for (int i = 0; i < newbb->stack_height; i++) { - int sloc = td->stack [i].local; - int dloc = newbb->stack_state [i].local; + int sloc = td->stack [i].var; + int dloc = newbb->stack_state [i].var; if (sloc != dloc) { - int mt = td->locals [sloc].mt; + int mt = td->vars [sloc].mt; int mov_op = interp_get_mov_for_type (mt, FALSE); // FIXME can be hit in some IL cases. Should we merge the stack states ? (b41002.il) - // g_assert (mov_op == interp_get_mov_for_type (td->locals [dloc].mt, FALSE)); + // g_assert (mov_op == interp_get_mov_for_type (td->vars [dloc].mt, FALSE)); interp_add_ins (td, mov_op); - interp_ins_set_sreg (td->last_ins, td->stack [i].local); - interp_ins_set_dreg (td->last_ins, newbb->stack_state [i].local); + interp_ins_set_sreg (td->last_ins, td->stack [i].var); + interp_ins_set_dreg (td->last_ins, newbb->stack_state [i].var); if (mt == MINT_TYPE_VT) { - g_assert (td->locals [sloc].size == td->locals [dloc].size); - td->last_ins->data [0] = GINT_TO_UINT16 (td->locals [sloc].size); + g_assert (td->vars [sloc].size == td->vars [dloc].size); + td->last_ins->data [0] = GINT_TO_UINT16 (td->vars [sloc].size); } } } @@ -688,7 +688,7 @@ one_arg_branch(TransformData *td, int mint_op, int offset, int inst_size) --td->sp; if (offset) { handle_branch (td, long_op, offset + inst_size); - interp_ins_set_sreg (td->last_ins, td->sp->local); + interp_ins_set_sreg (td->last_ins, td->sp->var); } else { interp_add_ins (td, MINT_NOP); } @@ -703,9 +703,9 @@ interp_add_conv (TransformData *td, StackInfo *sp, InterpInst *prev_ins, int typ else new_inst = interp_add_ins (td, conv_op); - interp_ins_set_sreg (new_inst, sp->local); - set_simple_type_and_local (td, sp, type); - interp_ins_set_dreg (new_inst, sp->local); + interp_ins_set_sreg (new_inst, sp->var); + set_simple_type_and_var (td, sp, type); + interp_ins_set_dreg (new_inst, sp->var); } static void @@ -736,7 +736,7 @@ two_arg_branch(TransformData *td, int mint_op, int offset, int inst_size) td->sp -= 2; if (offset) { handle_branch (td, long_op, offset + inst_size); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } else { interp_add_ins (td, MINT_NOP); } @@ -749,9 +749,9 @@ unary_arith_op(TransformData *td, int mint_op) int op = mint_op + td->sp [-1].type - STACK_TYPE_I4; td->sp--; interp_add_ins (td, op); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, td->sp [0].type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static void @@ -791,9 +791,9 @@ binary_arith_op(TransformData *td, int mint_op) op = mint_op + type1 - STACK_TYPE_I4; td->sp -= 2; interp_add_ins (td, op); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, type1); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static void @@ -808,9 +808,9 @@ shift_op(TransformData *td, int mint_op) } td->sp -= 2; interp_add_ins (td, op); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, td->sp [0].type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static int @@ -879,7 +879,7 @@ load_arg(TransformData *td, int n) } interp_add_ins (td, interp_get_mov_for_type (mt, TRUE)); interp_ins_set_sreg (td->last_ins, n); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); if (mt == MINT_TYPE_VT) td->last_ins->data [0] = GINT32_TO_UINT16 (size); } @@ -904,7 +904,7 @@ store_arg(TransformData *td, int n) } --td->sp; interp_add_ins (td, interp_get_mov_for_type (mt, FALSE)); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); interp_ins_set_dreg (td->last_ins, n); if (mt == MINT_TYPE_VT) td->last_ins->data [0] = GINT32_TO_UINT16 (size); @@ -913,9 +913,9 @@ store_arg(TransformData *td, int n) static void load_local (TransformData *td, int local) { - int mt = td->locals [local].mt; - gint32 size = td->locals [local].size; - MonoType *type = td->locals [local].type; + int mt = td->vars [local].mt; + gint32 size = td->vars [local].size; + MonoType *type = td->vars [local].type; if (mt == MINT_TYPE_VT) { MonoClass *klass = mono_class_from_mono_type_internal (type); @@ -928,7 +928,7 @@ load_local (TransformData *td, int local) } interp_add_ins (td, interp_get_mov_for_type (mt, TRUE)); interp_ins_set_sreg (td->last_ins, local); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); if (mt == MINT_TYPE_VT) td->last_ins->data [0] = GINT32_TO_UINT16 (size); } @@ -936,7 +936,7 @@ load_local (TransformData *td, int local) static void store_local (TransformData *td, int local) { - int mt = td->locals [local].mt; + int mt = td->vars [local].mt; CHECK_STACK_RET_VOID (td, 1); #if SIZEOF_VOID_P == 8 // nint and int32 can be used interchangeably. Add implicit conversions. @@ -957,10 +957,10 @@ store_local (TransformData *td, int local) } --td->sp; interp_add_ins (td, interp_get_mov_for_type (mt, FALSE)); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); interp_ins_set_dreg (td->last_ins, local); if (mt == MINT_TYPE_VT) - td->last_ins->data [0] = GINT32_TO_UINT16 (td->locals [local].size); + td->last_ins->data [0] = GINT32_TO_UINT16 (td->vars [local].size); } static void @@ -1089,7 +1089,7 @@ emit_ldptr (TransformData *td, gpointer data) { interp_add_ins (td, MINT_LDPTR); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, data); } @@ -1126,7 +1126,7 @@ interp_generate_icall_throw (TransformData *td, MonoJitICallInfo *icall_info, gp if (num_args) { int *call_args = (int*)mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); for (int i = 0; i < num_args; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [num_args] = -1; td->last_ins->info.call_info->call_args = call_args; } @@ -1170,14 +1170,14 @@ interp_generate_ipe_bad_fallthru (TransformData *td) int -interp_create_local (TransformData *td, MonoType *type) +interp_create_var (TransformData *td, MonoType *type) { int size, align; size = mono_type_size (type, &align); g_assert (align <= MINT_STACK_SLOT_SIZE); - return interp_create_local_explicit (td, type, size); + return interp_create_var_explicit (td, type, size); } /* @@ -1579,15 +1579,15 @@ interp_emit_ldobj (TransformData *td, MonoClass *klass) interp_add_ins (td, MINT_LDOBJ_VT); size = mono_class_value_size (klass, NULL); g_assert (size < G_MAXUINT16); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type_vt (td, klass, size); } else { int opcode = interp_get_ldind_for_mt (mt); interp_add_ins (td, opcode); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type (td, stack_type [mt], klass); } - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); if (mt == MINT_TYPE_VT) td->last_ins->data [0] = GINT32_TO_UINT16 (size); } @@ -1611,9 +1611,9 @@ interp_emit_stobj (TransformData *td, MonoClass *klass, gboolean reverse_order) } td->sp -= 2; if (reverse_order) - interp_ins_set_sregs2 (td->last_ins, td->sp [1].local, td->sp [0].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [1].var, td->sp [0].var); else - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } static void @@ -1630,7 +1630,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check if (!check_class || m_class_is_valuetype (element_class)) { if (rank == 1 && !bounded) { interp_add_ins (td, MINT_LDELEMA1); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); g_assert (size < G_MAXUINT16); td->last_ins->data [0] = GINT_TO_UINT16 (size); } else { @@ -1638,7 +1638,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); for (int i = 0; i < rank + 1; i++) { - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; } call_args [rank + 1] = -1; g_assert (rank < G_MAXUINT16 && size < G_MAXUINT16); @@ -1654,7 +1654,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); int *call_args = (int*)mono_mempool_alloc (td->mempool, (rank + 2) * sizeof (int)); for (int i = 0; i < rank + 1; i++) { - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; } call_args [rank + 1] = -1; td->last_ins->data [0] = get_data_item_index (td, check_class); @@ -1665,7 +1665,7 @@ interp_emit_ldelema (TransformData *td, MonoClass *array_class, MonoClass *check } push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } static void @@ -1681,9 +1681,9 @@ interp_emit_metadata_update_ldflda (TransformData *td, MonoClassField *field, Mo interp_add_ins (td, MINT_METADATA_UPDATE_LDFLDA); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, field_type); td->last_ins->data [1] = get_data_item_index (td, GUINT_TO_POINTER (field_token)); } @@ -1743,7 +1743,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas // the code is unsafe (like in some wrappers). In that case we assume the type // of the array and don't do any checks. - int local = interp_create_local (td, local_type); + int local = interp_create_var (td, local_type); store_local (td, local); interp_emit_ldelema (td, target_method->klass, value_class); @@ -1787,8 +1787,8 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas td->last_ins->dreg = ldloca2->sregs [0]; // Remove the ldlocas - td->locals [ldloca1->sregs [0]].indirects--; - td->locals [ldloca2->sregs [0]].indirects--; + td->vars [ldloca1->sregs [0]].indirects--; + td->vars [ldloca2->sregs [0]].indirects--; interp_clear_ins (ldloca1); interp_clear_ins (ldloca2); td->sp -= 2; @@ -1910,9 +1910,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas td->last_ins->data [0] = GINT_TO_UINT16 (size); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } @@ -1941,9 +1941,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_SUB_I8); #endif td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [1].local, td->sp [0].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [1].var, td->sp [0].var); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "Unbox")) { @@ -1957,9 +1957,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_UNBOX); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; @@ -1989,9 +1989,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas MonoClass *k = mono_defaults.boolean_class; interp_add_ins (td, MINT_CLT_UN_P); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_type (td, stack_type [mono_mint_type (m_class_get_byval_arg (k))], k); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "IsAddressGreaterThan")) { @@ -2002,9 +2002,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_CGT_UN_P); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "SizeOf")) { @@ -2018,7 +2018,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_LDC_I4); WRITE32_INS (td->last_ins, 0, &esize); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "SkipInit")) { @@ -2039,7 +2039,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_LDC_I4); WRITE32_INS (td->last_ins, 0, &offset); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } else if (!strcmp (tm, "GetHashCode") || !strcmp (tm, "InternalGetHashCode")) { @@ -2051,9 +2051,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas td->last_ins->data [0] = (gint16) MONO_ABI_SIZEOF (MonoObject); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; @@ -2092,7 +2092,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas *op = has_refs ? MINT_LDC_I4_1 : MINT_LDC_I4_0; } else if (!strcmp (tm, "CreateSpan") && csignature->param_count == 1 && - td->last_ins->opcode == MINT_LDPTR && td->last_ins->dreg == td->sp [-1].local) { + td->last_ins->opcode == MINT_LDPTR && td->last_ins->dreg == td->sp [-1].var) { MonoGenericContext* ctx = mono_method_get_context (target_method); g_assert (ctx); g_assert (ctx->method_inst); @@ -2114,16 +2114,16 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas // push the length of this span push_simple_type (td, STACK_TYPE_I4); - interp_get_ldc_i4_from_const (td, NULL, num_elements, td->sp [-1].local); + interp_get_ldc_i4_from_const (td, NULL, num_elements, td->sp [-1].var); // create span interp_add_ins (td, MINT_INTRINS_SPAN_CTOR); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); MonoClass *ret_class = mono_class_from_mono_type_internal (csignature->ret); push_type_vt (td, ret_class, mono_class_value_size (ret_class, NULL)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; @@ -2151,7 +2151,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas td->sp--; interp_add_ins (td, MINT_LDPTR); push_type (td, STACK_TYPE_O, mono_defaults.runtimetype_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, systype); td->ip += 5; @@ -2169,15 +2169,15 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, MINT_LDIND_I); } td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_O); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } interp_add_ins (td, MINT_INTRINS_GET_TYPE); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type (td, STACK_TYPE_O, mono_defaults.runtimetype_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); mono_class_init_internal (target_method->klass); @@ -2218,17 +2218,17 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas // Remove boxing and load the value of this td->last_ins->opcode = GINT_TO_OPCODE (interp_get_mov_for_type (mt, FALSE)); InterpInst *ins = interp_insert_ins (td, prev_prev_ins, interp_get_ldind_for_mt (mt)); - interp_ins_set_sreg (ins, td->sp [-2].local); - interp_ins_set_dreg (ins, td->sp [-2].local); + interp_ins_set_sreg (ins, td->sp [-2].var); + interp_ins_set_dreg (ins, td->sp [-2].var); intrinsify = TRUE; } if (intrinsify) { interp_add_ins (td, MINT_INTRINS_ENUM_HASFLAG); td->last_ins->data [0] = get_data_item_index (td, base_klass); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; } @@ -2270,8 +2270,8 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas gboolean is_compareto = strcmp (tm, "EnumCompareTo") == 0; if (is_compareto) { int locala, localb; - locala = interp_create_local (td, t); - localb = interp_create_local (td, t); + locala = interp_create_var (td, t); + localb = interp_create_var (td, t); // Save arguments store_local (td, localb); @@ -2283,9 +2283,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas { interp_add_ins (td, MINT_SUB_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { @@ -2295,9 +2295,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas else interp_add_ins (td, is_i8 ? MINT_CGT_I8 : MINT_CGT_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); // (a < b) load_local (td, locala); load_local (td, localb); @@ -2306,15 +2306,15 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas else interp_add_ins (td, is_i8 ? MINT_CLT_I8 : MINT_CLT_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); // (a > b) - (a < b) interp_add_ins (td, MINT_SUB_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip += 5; return TRUE; @@ -2384,9 +2384,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas interp_add_ins (td, opcode); td->last_ins->data [0] = ct & (is_i4 ? 31 : 63); td->sp -= 2; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, is_i4 ? STACK_TYPE_I4 : STACK_TYPE_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; return TRUE; @@ -2731,7 +2731,7 @@ interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHe td->inlined_method = target_method; prev_max_stack_height = td->max_stack_height; - prev_locals_size = td->locals_size; + prev_locals_size = td->vars_size; prev_n_data_items = td->n_data_items; prev_in_offsets = td->in_offsets; @@ -2762,7 +2762,7 @@ interp_inline_method (TransformData *td, MonoMethod *target_method, MonoMethodHe if (td->verbose_level) g_print ("Inline aborted method %s.%s\n", m_class_get_name (target_method->klass), target_method->name); td->max_stack_height = prev_max_stack_height; - td->locals_size = prev_locals_size; + td->vars_size = prev_locals_size; /* Remove any newly added items */ for (i = prev_n_data_items; i < td->n_data_items; i++) { @@ -2849,7 +2849,7 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi else vtsize = MINT_STACK_SLOT_SIZE; - dreg = interp_create_local (td, get_type_from_stack (stack_type [ret_mt], klass)); + dreg = interp_create_var (td, get_type_from_stack (stack_type [ret_mt], klass)); // For valuetypes, we need to control the lifetime of the valuetype. // MINT_NEWOBJ_VT_INLINED takes the address of this reg and we should keep @@ -2857,13 +2857,13 @@ interp_inline_newobj (TransformData *td, MonoMethod *target_method, MonoMethodSi interp_add_ins (td, MINT_DEF); interp_ins_set_dreg (td->last_ins, dreg); } else { - dreg = interp_create_local (td, get_type_from_stack (stack_type [ret_mt], klass)); + dreg = interp_create_var (td, get_type_from_stack (stack_type [ret_mt], klass)); } // Allocate `this` pointer if (is_vt) { push_simple_type (td, STACK_TYPE_I); - this_reg = td->sp [-1].local; + this_reg = td->sp [-1].var; } else { push_var (td, dreg); } @@ -2931,9 +2931,9 @@ interp_constrained_box (TransformData *td, MonoClass *constrained_class, MonoMet interp_add_ins (td, MINT_BOX_PTR); td->last_ins->data [0] = get_data_item_index (td, vtable); } - interp_ins_set_sreg (td->last_ins, sp->local); - set_simple_type_and_local (td, sp, STACK_TYPE_O); - interp_ins_set_dreg (td->last_ins, sp->local); + interp_ins_set_sreg (td->last_ins, sp->var); + set_simple_type_and_var (td, sp, STACK_TYPE_O); + interp_ins_set_dreg (td->last_ins, sp->var); } static MonoMethod* @@ -3037,7 +3037,7 @@ create_call_args (TransformData *td, int num_args) return NULL; int *call_args = (int*) mono_mempool_alloc (td->mempool, (num_args + 1) * sizeof (int)); for (int i = 0; i < num_args; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [num_args] = -1; return call_args; } @@ -3046,7 +3046,7 @@ static void interp_realign_simd_params (TransformData *td, StackInfo *sp_params, int num_args, int prev_offset) { for (int i = 1; i < num_args; i++) { - if (td->locals [sp_params [i].local].flags & INTERP_LOCAL_FLAG_SIMD) { + if (td->vars [sp_params [i].var].flags & INTERP_LOCAL_FLAG_SIMD) { gint16 offset_amount; // If the simd struct comes immediately after the previous argument we do upper align // otherwise we should lower align to preserve call convention @@ -3150,11 +3150,11 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target *start = sp_fp; } else { int *arg_locals = mono_mempool_alloc0 (td->mempool, sizeof (int) * csignature->param_count); - int fp_local = interp_create_local (td, m_class_get_byval_arg (mono_defaults.int_class)); + int fp_local = interp_create_var (td, m_class_get_byval_arg (mono_defaults.int_class)); // Pop everything into locals. Push after into correct order store_local (td, fp_local); for (int i = csignature->param_count - 1; i >= 0; i--) { - arg_locals [i] = interp_create_local (td, csignature->params [i]); + arg_locals [i] = interp_create_var (td, csignature->params [i]); store_local (td, arg_locals [i]); } load_local (td, fp_local); @@ -3240,9 +3240,9 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target StackInfo *sp = td->sp - 1 - csignature->param_count; /* managed pointer on the stack, we need to deref that puppy */ interp_add_ins (td, MINT_LDIND_I); - interp_ins_set_sreg (td->last_ins, sp->local); - set_simple_type_and_local (td, sp, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, sp->local); + interp_ins_set_sreg (td->last_ins, sp->var); + set_simple_type_and_var (td, sp, STACK_TYPE_I); + interp_ins_set_dreg (td->last_ins, sp->var); } else if (target_method->klass != constrained_class) { /* * The type parameter is instantiated as a valuetype, @@ -3306,9 +3306,9 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (is_virtual) { interp_add_ins (td, MINT_CKNULL); - interp_ins_set_sreg (td->last_ins, td->sp->local); - set_simple_type_and_local (td, td->sp, td->sp->type); - interp_ins_set_dreg (td->last_ins, td->sp->local); + interp_ins_set_sreg (td->last_ins, td->sp->var); + set_simple_type_and_var (td, td->sp, td->sp->type); + interp_ins_set_dreg (td->last_ins, td->sp->var); interp_add_ins (td, MINT_TAILCALL_VIRT); td->last_ins->data [2] = get_virt_method_slot (target_method); @@ -3358,9 +3358,9 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (need_null_check) { StackInfo *sp = td->sp - 1 - csignature->param_count; interp_add_ins (td, MINT_CKNULL); - interp_ins_set_sreg (td->last_ins, sp->local); - set_type_and_local (td, sp, sp->type, sp->klass); - interp_ins_set_dreg (td->last_ins, sp->local); + interp_ins_set_sreg (td->last_ins, sp->var); + set_type_and_var (td, sp, sp->type, sp->klass); + interp_ins_set_dreg (td->last_ins, sp->var); } /* Offset the function pointer when emitting convert instructions */ @@ -3421,7 +3421,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target /* Pop the function pointer */ if (calli) { --td->sp; - fp_sreg = td->sp [0].local; + fp_sreg = td->sp [0].var; } int param_end_offset = 0; @@ -3463,7 +3463,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (calli) { // fp_sreg is at the top of the stack, make sure it is not overwritten by MINT_CALL_ALIGN_STACK int offset = new_param_offset - param_offset; - td->locals [fp_sreg].stack_offset += offset; + td->vars [fp_sreg].stack_offset += offset; } } else { call_offset = ALIGN_TO (param_offset, MINT_STACK_ALIGNMENT); @@ -3474,7 +3474,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target // We overwrite it with the return local, save it for future use if (csignature->param_count || csignature->hasthis) - first_sreg = td->sp [0].local; + first_sreg = td->sp [0].var; /* need to handle typedbyref ... */ if (csignature->ret->type != MONO_TYPE_VOID) { @@ -3495,14 +3495,14 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target } else { push_type (td, stack_type[mt], klass); } - dreg = td->sp [-1].local; + dreg = td->sp [-1].var; } else { // Create a new dummy local to serve as the dreg of the call // FIXME Consider adding special dreg type (ex -1), that is // resolved to null offset. The opcode shouldn't really write to it push_simple_type (td, STACK_TYPE_I4); td->sp--; - dreg = td->sp [0].local; + dreg = td->sp [0].var; } if (op >= 0) { @@ -3516,9 +3516,9 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target if (num_sregs == 1) interp_ins_set_sreg (td->last_ins, first_sreg); else if (num_sregs == 2) - interp_ins_set_sregs2 (td->last_ins, first_sreg, sp_args [1].local); + interp_ins_set_sregs2 (td->last_ins, first_sreg, sp_args [1].var); else if (num_sregs == 3) - interp_ins_set_sregs3 (td->last_ins, first_sreg, sp_args [1].local, sp_args [2].local); + interp_ins_set_sregs3 (td->last_ins, first_sreg, sp_args [1].var, sp_args [2].var); else g_error ("Unsupported opcode"); } @@ -3545,7 +3545,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target // needs to be able to access the actual arguments to continue with the call so it // needs to know whether there is an empty stack slot between the delegate ptr and the // rest of the args - gboolean first_arg_is_simd = td->locals [sp_args [1].local].flags & INTERP_LOCAL_FLAG_SIMD; + gboolean first_arg_is_simd = td->vars [sp_args [1].var].flags & INTERP_LOCAL_FLAG_SIMD; td->last_ins->data [2] = first_arg_is_simd ? MINT_SIMD_ALIGNMENT : MINT_STACK_SLOT_SIZE; } } else if (calli) { @@ -4098,9 +4098,9 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet int num_locals = num_args + num_il_locals; imethod->local_offsets = (guint32*)g_malloc (num_il_locals * sizeof(guint32)); - td->locals = (InterpLocal*)g_malloc (num_locals * sizeof (InterpLocal)); - td->locals_size = num_locals; - td->locals_capacity = td->locals_size; + td->vars = (InterpVar*)g_malloc (num_locals * sizeof (InterpVar)); + td->vars_size = num_locals; + td->vars_capacity = td->vars_size; offset = 0; /* @@ -4115,15 +4115,15 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet else type = mono_method_signature_internal (td->method)->params [i - sig->hasthis]; int mt = mono_mint_type (type); - td->locals [i].type = type; - td->locals [i].flags = INTERP_LOCAL_FLAG_GLOBAL; - td->locals [i].indirects = 0; - td->locals [i].mt = mt; - td->locals [i].def = NULL; + td->vars [i].type = type; + td->vars [i].flags = INTERP_LOCAL_FLAG_GLOBAL; + td->vars [i].indirects = 0; + td->vars [i].mt = mt; + td->vars [i].def = NULL; size = mono_interp_type_size (type, mt, &align); - td->locals [i].size = size; + td->vars [i].size = size; offset = ALIGN_TO (offset, align); - td->locals [i].offset = offset; + td->vars [i].offset = offset; offset += size; } offset = ALIGN_TO (offset, MINT_STACK_ALIGNMENT); @@ -4141,13 +4141,13 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet } offset = ALIGN_TO (offset, align); imethod->local_offsets [i] = offset; - td->locals [index].type = header->locals [i]; - td->locals [index].offset = offset; - td->locals [index].flags = INTERP_LOCAL_FLAG_GLOBAL; - td->locals [index].indirects = 0; - td->locals [index].mt = mono_mint_type (header->locals [i]); - td->locals [index].def = NULL; - td->locals [index].size = size; + td->vars [index].type = header->locals [i]; + td->vars [index].offset = offset; + td->vars [index].flags = INTERP_LOCAL_FLAG_GLOBAL; + td->vars [index].indirects = 0; + td->vars [index].mt = mono_mint_type (header->locals [i]); + td->vars [index].def = NULL; + td->vars [index].size = size; // Every local takes a MINT_STACK_SLOT_SIZE so IL locals have same behavior as execution locals offset += size; } @@ -4159,10 +4159,10 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet imethod->clause_data_offsets = (guint32*)g_malloc (header->num_clauses * sizeof (guint32)); td->clause_vars = (int*)mono_mempool_alloc (td->mempool, sizeof (int) * header->num_clauses); for (guint i = 0; i < header->num_clauses; i++) { - int var = interp_create_local (td, mono_get_object_type ()); - td->locals [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + int var = interp_create_var (td, mono_get_object_type ()); + td->vars [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; interp_alloc_global_var_offset (td, var); - imethod->clause_data_offsets [i] = td->locals [var].offset; + imethod->clause_data_offsets [i] = td->vars [var].offset; td->clause_vars [i] = var; } } @@ -4223,12 +4223,12 @@ interp_handle_isinst (TransformData *td, MonoClass *klass, gboolean isinst_instr interp_add_ins (td, isinst_instr ? MINT_ISINST : MINT_CASTCLASS); } td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); if (isinst_instr) push_type (td, td->sp [0].type, td->sp [0].klass); else push_type (td, STACK_TYPE_O, klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; @@ -4248,11 +4248,11 @@ interp_emit_ldsflda (TransformData *td, MonoClassField *field, MonoError *error) g_assert (offset); interp_add_ins (td, MINT_LDTSFLDA); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE32_INS(td->last_ins, 0, &offset); } else { interp_add_ins (td, MINT_LDSFLDA); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, vtable); td->last_ins->data [1] = get_data_item_index (td, mono_static_field_get_addr (vtable, field)); } @@ -4283,21 +4283,21 @@ interp_emit_load_const (TransformData *td, gpointer field_addr, int mt) default: val = *(gint32*)field_addr; } - interp_get_ldc_i4_from_const (td, NULL, val, td->sp [-1].local); + interp_get_ldc_i4_from_const (td, NULL, val, td->sp [-1].var); } else if (mt == MINT_TYPE_I8) { gint64 val = *(gint64*)field_addr; interp_add_ins (td, MINT_LDC_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE64_INS (td->last_ins, 0, &val); } else if (mt == MINT_TYPE_R4) { float val = *(float*)field_addr; interp_add_ins (td, MINT_LDC_R4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE32_INS (td->last_ins, 0, &val); } else if (mt == MINT_TYPE_R8) { double val = *(double*)field_addr; interp_add_ins (td, MINT_LDC_R8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE64_INS (td->last_ins, 0, &val); } else { // Revert stack @@ -4328,7 +4328,7 @@ interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *fi // Load address of thread static field push_simple_type (td, STACK_TYPE_MP); interp_add_ins (td, MINT_LDTSFLDA); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE32_INS (td->last_ins, 0, &offset); // Do a load/store to this address @@ -4336,17 +4336,17 @@ interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *fi if (mt == MINT_TYPE_VT) { int field_size = mono_class_value_size (field_class, NULL); interp_add_ins (td, MINT_LDOBJ_VT); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); td->sp--; push_type_vt (td, field_class, field_size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = GINT_TO_UINT16 (field_size); } else { interp_add_ins (td, interp_get_ldind_for_mt (mt)); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); td->sp--; push_type (td, stack_type [mt], field_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } } else { interp_emit_stobj (td, field_class, TRUE); @@ -4381,14 +4381,14 @@ interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *fi interp_add_ins (td, MINT_LDSFLD_I1 + mt - MINT_TYPE_I1); push_type (td, stack_type [mt], field_class); } - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { if (G_LIKELY (!wide_data)) interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1)); else interp_add_ins (td, MINT_STSFLD_W); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); } if (G_LIKELY (!wide_data)) { @@ -4439,7 +4439,7 @@ initialize_clause_bblocks (TransformData *td) bb->stack_state [0].type = STACK_TYPE_O; bb->stack_state [0].klass = NULL; /*FIX*/ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE; - bb->stack_state [0].local = td->clause_vars [i]; + bb->stack_state [0].var = td->clause_vars [i]; } if (c->flags == MONO_EXCEPTION_CLAUSE_FILTER) { @@ -4451,7 +4451,7 @@ initialize_clause_bblocks (TransformData *td) bb->stack_state [0].type = STACK_TYPE_O; bb->stack_state [0].klass = NULL; /*FIX*/ bb->stack_state [0].size = MINT_STACK_SLOT_SIZE; - bb->stack_state [0].local = td->clause_vars [i]; + bb->stack_state [0].var = td->clause_vars [i]; } else if (c->flags == MONO_EXCEPTION_CLAUSE_NONE) { /* * JIT doesn't emit sdb seq intr point at the start of catch clause, probably @@ -4470,9 +4470,9 @@ handle_ldind (TransformData *td, int op, int type, gboolean *volatile_) CHECK_STACK_RET_VOID (td, 1); interp_add_ins (td, op); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); if (*volatile_) { interp_emit_memory_barrier (td, MONO_MEMORY_BARRIER_ACQ); @@ -4491,7 +4491,7 @@ handle_stind (TransformData *td, int op, gboolean *volatile_) } interp_add_ins (td, op); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); ++td->ip; } @@ -4503,9 +4503,9 @@ handle_ldelem (TransformData *td, int op, int type) ENSURE_I4 (td, 1); interp_add_ins (td, op); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; } @@ -4516,7 +4516,7 @@ handle_stelem (TransformData *td, int op) ENSURE_I4 (td, 2); interp_add_ins (td, op); td->sp -= 3; - interp_ins_set_sregs3 (td->last_ins, td->sp [0].local, td->sp [1].local, td->sp [2].local); + interp_ins_set_sregs3 (td->last_ins, td->sp [0].var, td->sp [1].var, td->sp [2].var); ++td->ip; } @@ -4676,7 +4676,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // first instruction in a vararg method needs to copy the variable arguments // into a special region so they can be accessed by MINT_ARGLIST. This region // is localloc'ed so we have compile time static offsets for all locals/stack. - arglist_local = interp_create_local (td, m_class_get_byval_arg (mono_defaults.int_class)); + arglist_local = interp_create_var (td, m_class_get_byval_arg (mono_defaults.int_class)); interp_add_ins (td, MINT_INIT_ARGLIST); interp_ins_set_dreg (td->last_ins, arglist_local); // This is the offset where the variable args are on stack. After this instruction @@ -4728,8 +4728,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, arg_locals = (guint32*) g_malloc ((!!signature->hasthis + signature->param_count) * sizeof (guint32)); /* Allocate locals to store inlined method args from stack */ for (int i = signature->param_count - 1; i >= 0; i--) { - MonoType *type = td->locals [td->sp [-1].local].type; - local = interp_create_local (td, type); + MonoType *type = td->vars [td->sp [-1].var].type; + local = interp_create_var (td, type); arg_locals [i + !!signature->hasthis] = local; store_local (td, local); } @@ -4739,8 +4739,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, * If this is value type, it is passed by address and not by value. * Valuetype this local gets integer type MINT_TYPE_I. */ - MonoType *type = td->locals [td->sp [-1].local].type; - local = interp_create_local (td, type); + MonoType *type = td->vars [td->sp [-1].var].type; + local = interp_create_var (td, type); arg_locals [0] = local; store_local (td, local); } @@ -4748,7 +4748,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, local_locals = (guint32*) g_malloc (header->num_locals * sizeof (guint32)); /* Allocate locals to store inlined method args from stack */ for (int i = 0; i < header->num_locals; i++) - local_locals [i] = interp_create_local (td, header->locals [i]); + local_locals [i] = interp_create_var (td, header->locals [i]); } td->dont_inline = g_list_prepend (td->dont_inline, method); @@ -4793,12 +4793,12 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, int index = td->clause_indexes [in_offset]; if (index != -1 && new_bb->stack_height == 1 && header->clauses [index].handler_offset == in_offset) { int exvar = td->clause_vars [index]; - g_assert (td->stack [0].local == exvar); + g_assert (td->stack [0].var == exvar); td->sp--; push_simple_type (td, STACK_TYPE_O); interp_add_ins (td, MINT_MOV_P); interp_ins_set_sreg (td->last_ins, exvar); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } } } @@ -4918,15 +4918,15 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (!inlining) { interp_add_ins (td, MINT_LDLOCA_S); interp_ins_set_sreg (td->last_ins, n); - td->locals [n].indirects++; + td->vars [n].indirects++; } else { int loc_n = arg_locals [n]; interp_add_ins (td, MINT_LDLOCA_S); interp_ins_set_sreg (td->last_ins, loc_n); - td->locals [loc_n].indirects++; + td->vars [loc_n].indirects++; } push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 2; break; } @@ -4956,9 +4956,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else loc_n = local_locals [loc_n]; interp_ins_set_sreg (td->last_ins, loc_n); - td->locals [loc_n].indirects++; + td->vars [loc_n].indirects++; push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 2; break; } @@ -4974,13 +4974,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_LDNULL: interp_add_ins (td, MINT_LDNULL); push_type (td, STACK_TYPE_O, NULL); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDC_I4_M1: interp_add_ins (td, MINT_LDC_I4_M1); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDC_I4_0: @@ -4988,14 +4988,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->sp > td->stack && td->sp [-1].type == STACK_TYPE_I4) { interp_add_ins (td, MINT_CEQ0_I4); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 3; } else { interp_add_ins (td, MINT_LDC_I4_0); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; } break; @@ -5004,14 +5004,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, (td->ip [1] == CEE_ADD || td->ip [1] == CEE_SUB) && td->sp [-1].type == STACK_TYPE_I4) { interp_add_ins (td, td->ip [1] == CEE_ADD ? MINT_ADD1_I4 : MINT_SUB1_I4); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 2; } else { interp_add_ins (td, MINT_LDC_I4_1); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; } break; @@ -5024,14 +5024,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_LDC_I4_8: interp_add_ins (td, (*td->ip - CEE_LDC_I4_0) + MINT_LDC_I4_0); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDC_I4_S: interp_add_ins (td, MINT_LDC_I4_S); td->last_ins->data [0] = ((gint8 *) td->ip) [1]; push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 2; break; case CEE_LDC_I4: @@ -5039,7 +5039,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I4); WRITE32_INS (td->last_ins, 0, &i32); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; case CEE_LDC_I8: { @@ -5047,7 +5047,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I8); WRITE64_INS (td->last_ins, 0, &val); push_simple_type (td, STACK_TYPE_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 9; break; } @@ -5057,7 +5057,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_R4); WRITE32_INS (td->last_ins, 0, &val); push_simple_type (td, STACK_TYPE_R4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; } @@ -5067,28 +5067,28 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_R8); WRITE64_INS (td->last_ins, 0, &val); push_simple_type (td, STACK_TYPE_R8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 9; break; } case CEE_DUP: { int type = td->sp [-1].type; klass = td->sp [-1].klass; - mt = td->locals [td->sp [-1].local].mt; + mt = td->vars [td->sp [-1].var].mt; if (mt == MINT_TYPE_VT) { gint32 size = mono_class_value_size (klass, NULL); g_assert (size < G_MAXUINT16); interp_add_ins (td, MINT_MOV_VT); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); push_type_vt (td, klass, size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = GINT32_TO_UINT16 (size); } else { interp_add_ins (td, interp_get_mov_for_type (mt, FALSE)); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); push_type (td, type, klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip++; break; @@ -5203,7 +5203,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, is_void ? MINT_PROF_EXIT_VOID : MINT_PROF_EXIT); td->last_ins->data [0] = exit_profiling; if (!is_void) { - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); WRITE32_INS (td->last_ins, 1, &vt_size); } } else { @@ -5221,12 +5221,12 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_RET_U2); else interp_add_ins (td, MINT_RET); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); } } else { interp_add_ins (td, MINT_RET_VT); g_assert (vt_size < G_MAXUINT16); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = GINT_TO_UINT16 (vt_size); } } @@ -5381,7 +5381,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 4; next_ip = td->ip + n * 4; --td->sp; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); InterpBasicBlock **target_bb_table = (InterpBasicBlock**)mono_mempool_alloc0 (td->mempool, sizeof (InterpBasicBlock*) * n); for (guint32 i = 0; i < n; i++) { int offset = read32 (td->ip); @@ -5739,7 +5739,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I8); td->sp--; push_simple_type (td, STACK_TYPE_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE64_INS (td->last_ins, 0, &ct); } else { interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I8, MINT_CONV_I8_I4); @@ -5810,7 +5810,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I8); td->sp--; push_simple_type (td, STACK_TYPE_I8); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); WRITE64_INS (td->last_ins, 0, &ct); } else { interp_add_conv (td, td->sp - 1, NULL, STACK_TYPE_I8, MINT_CONV_I8_U4); @@ -5853,17 +5853,17 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_CPOBJ_VT : MINT_CPOBJ); td->last_ins->data [0] = get_data_item_index (td, klass); } - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } else { td->sp--; interp_add_ins (td, MINT_LDIND_I); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->sp -= 2; interp_add_ins (td, MINT_STIND_REF); - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } td->ip += 5; break; @@ -5895,7 +5895,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, /* GC won't scan code stream, but reference is held by metadata * machinery so we are good here */ interp_add_ins (td, MINT_LDSTR); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, s); } else if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) { /* token is an index into the MonoDynamicMethod:method.method_data @@ -5905,14 +5905,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, * the actual data item is a managed MonoString from the managed DynamicMethod */ interp_add_ins (td, MINT_LDSTR_DYNAMIC); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, GUINT_TO_POINTER (token)); } else { /* the token is an index into MonoWrapperMethod:method_data that * stores a global or malloc'ed C string. defer MonoString * allocation to execution-time */ interp_add_ins (td, MINT_LDSTR_CSTR); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); const char *cstr = (const char*)mono_method_get_wrapper_data (method, token); td->last_ins->data [0] = get_data_item_index (td, (void*)cstr); } @@ -5961,7 +5961,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 1) * sizeof (int)); td->sp -= csignature->param_count; for (int i = 0; i < csignature->param_count; i++) { - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; } call_args [csignature->param_count] = -1; @@ -5972,7 +5972,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (!td->optimized) td->last_ins->info.call_info->call_offset = get_tos_offset (td); push_type (td, stack_type [ret_mt], klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); td->last_ins->info.call_info->call_args = call_args; } else if (klass == mono_defaults.string_class) { @@ -5986,15 +5986,15 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (m)); td->last_ins->data [1] = GUINT32_TO_UINT16 (params_stack_size); push_type (td, stack_type [ret_mt], klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { int *call_args = (int*)mono_mempool_alloc (td->mempool, (csignature->param_count + 2) * sizeof (int)); td->sp -= csignature->param_count; // First arg is dummy var, it is null when passed to the ctor - call_args [0] = interp_create_local (td, get_type_from_stack (stack_type [ret_mt], NULL)); + call_args [0] = interp_create_var (td, get_type_from_stack (stack_type [ret_mt], NULL)); for (int i = 0; i < csignature->param_count; i++) { - call_args [i + 1] = td->sp [i].local; + call_args [i + 1] = td->sp [i].var; } call_args [csignature->param_count + 1] = -1; @@ -6003,7 +6003,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->last_ins->data [0] = get_data_item_index_imethod (td, mono_interp_get_imethod (m)); push_type (td, stack_type [ret_mt], klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); init_last_ins_call (td); td->last_ins->info.call_info->call_args = call_args; @@ -6018,9 +6018,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, MONO_PROFILER_RAISE (inline_method, (td->rtm->method, m)); interp_add_ins (td, MINT_INTRINS_SPAN_CTOR); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_type_vt (td, klass, mono_class_value_size (klass, NULL)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else if (!td->optimized) { int tos = get_tos_offset (td); int param_offset, param_size; @@ -6056,13 +6056,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type (td, stack_type [ret_mt], klass); } - int dreg = td->sp [-1].local; + int dreg = td->sp [-1].var; int call_offset = get_tos_offset (td); call_offset = ALIGN_TO (call_offset, MINT_STACK_ALIGNMENT); if (param_count) { - if (td->locals [sp_params [0].local].flags & INTERP_LOCAL_FLAG_SIMD) { + if (td->vars [sp_params [0].var].flags & INTERP_LOCAL_FLAG_SIMD) { // if first arg is simd, move all args at next aligned offset after this ptr interp_add_ins (td, MINT_MOV_STACK_UNOPT); td->last_ins->data [0] = GINT_TO_UINT16 (param_offset); @@ -6133,7 +6133,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type (td, stack_type [ret_mt], klass); push_type (td, stack_type [ret_mt], klass); } - int dreg = td->sp [-2].local; + int dreg = td->sp [-2].var; // Push back the params to top of stack. The original vars are maintained. ensure_stack (td, csignature->param_count); @@ -6165,7 +6165,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // Save the arguments for the call int *call_args = (int*) mono_mempool_alloc (td->mempool, (csignature->param_count + 2) * sizeof (int)); for (int i = 0; i < csignature->param_count + 1; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [csignature->param_count + 1] = -1; td->last_ins->info.call_info->call_args = call_args; } @@ -6224,20 +6224,20 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, * CEE_UNBOX needs to push address of vtype while Nullable.Unbox returns the value type * We create a local variable in the frame so that we can fetch its address. */ - int local = interp_create_local (td, m_class_get_byval_arg (klass)); + int local = interp_create_var (td, m_class_get_byval_arg (klass)); store_local (td, local); interp_add_ins (td, MINT_LDLOCA_S); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_ins_set_sreg (td->last_ins, local); - td->locals [local].indirects++; + td->vars [local].indirects++; } else { interp_add_ins (td, MINT_UNBOX); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; } @@ -6264,7 +6264,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // FIXME do this somewhere else, maybe in super instruction pass, where we would check // instruction patterns // Restore the local that is on top of the stack - td->sp [-1].local = td->last_ins->sregs [0]; + td->sp [-1].var = td->last_ins->sregs [0]; td->ip += 5; break; } @@ -6284,9 +6284,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, } else { interp_add_ins (td, MINT_UNBOX); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); interp_emit_ldobj (td, klass); @@ -6300,7 +6300,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, INLINE_FAILURE; CHECK_STACK (td, 1); interp_add_ins (td, MINT_THROW); - interp_ins_set_sreg (td->last_ins, td->sp [-1].local); + interp_ins_set_sreg (td->last_ins, td->sp [-1].var); link_bblocks = FALSE; td->sp = td->stack; ++td->ip; @@ -6343,9 +6343,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_MOV_P); } } - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip += 5; } @@ -6384,7 +6384,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_MOV_SRC_OFF); g_assert (m_class_is_valuetype (klass)); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = GINT_TO_UINT16 (m_field_get_offset (field) - MONO_ABI_SIZEOF (MonoObject)); td->last_ins->data [1] = GINT_TO_UINT16 (mt); if (mt == MINT_TYPE_VT) @@ -6395,7 +6395,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type_vt (td, field_klass, field_size); else push_type (td, stack_type [mt], field_klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { if (G_UNLIKELY (m_field_is_from_update (field))) { g_assert (!m_type_is_byref (ftype)); @@ -6414,7 +6414,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, #endif interp_add_ins (td, opcode); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = GINT_TO_UINT16 (m_class_is_valuetype (klass) ? m_field_get_offset (field) - MONO_ABI_SIZEOF (MonoObject) : m_field_get_offset (field)); if (mt == MINT_TYPE_VT) { int size = mono_class_value_size (field_klass, NULL); @@ -6425,7 +6425,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, push_type_vt (td, field_klass, field_size); else push_type (td, stack_type [mt], field_klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } } td->ip += 5; @@ -6463,7 +6463,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, g_assert (!m_type_is_byref (ftype)); MonoClass *field_class = mono_class_from_mono_type_internal (ftype); MonoType *local_type = m_class_get_byval_arg (field_class); - int local = interp_create_local (td, local_type); + int local = interp_create_var (td, local_type); store_local (td, local); interp_emit_metadata_update_ldflda (td, field, error); goto_if_nok (error, exit); @@ -6479,7 +6479,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, #endif interp_add_ins (td, opcode); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); td->last_ins->data [0] = GINT_TO_UINT16 (m_class_is_valuetype (klass) ? m_field_get_offset (field) - MONO_ABI_SIZEOF (MonoObject) : m_field_get_offset (field)); if (mt == MINT_TYPE_VT) { /* the vtable of the field might not be initialized at this point */ @@ -6521,7 +6521,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, { interp_add_ins (td, (TARGET_BYTE_ORDER == G_LITTLE_ENDIAN) ? MINT_LDC_I4_1 : MINT_LDC_I4_0); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; } @@ -6649,10 +6649,10 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->sp--; interp_add_ins (td, vt ? MINT_BOX_VT : MINT_BOX); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = get_data_item_index (td, vtable); push_type (td, STACK_TYPE_O, klass); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; } @@ -6682,9 +6682,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, } td->sp--; interp_add_ins (td, MINT_NEWARR); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type (td, STACK_TYPE_O, array_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, vtable); td->ip += 5; break; @@ -6693,9 +6693,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, CHECK_STACK (td, 1); td->sp--; interp_add_ins (td, MINT_LDLEN); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDELEMA: { @@ -6721,23 +6721,23 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDELEMA_TC); td->sp -= 2; int *call_args = (int*)mono_mempool_alloc (td->mempool, 3 * sizeof (int)); - call_args [0] = td->sp [0].local; - call_args [1] = td->sp [1].local; + call_args [0] = td->sp [0].var; + call_args [1] = td->sp [1].var; call_args [2] = -1; init_last_ins_call (td); if (!td->optimized) td->last_ins->info.call_info->call_offset = get_tos_offset (td); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->last_ins->info.call_info->call_args = call_args; interp_ins_set_sreg (td->last_ins, MINT_CALL_ARGS_SREG); } else { interp_add_ins (td, MINT_LDELEMA1); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); mono_class_init_internal (klass); size = mono_class_array_element_size (klass); td->last_ins->data [0] = GINT32_TO_UINT16 (size); @@ -6821,9 +6821,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, ENSURE_I4 (td, 1); interp_add_ins (td, MINT_LDELEM_VT); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_type_vt (td, klass, size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = GINT_TO_UINT16 (size); ++td->ip; break; @@ -6925,9 +6925,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, g_error ("Invalid stack type"); } td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, ckfinite_stack_type); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; } @@ -6940,9 +6940,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_MKREFANY); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type_vt (td, mono_defaults.typed_reference_class, sizeof (MonoTypedRef)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; @@ -6956,9 +6956,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_REFANYVAL); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, klass); td->ip += 5; @@ -7203,7 +7203,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_LDC_I4_0); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip = next_next_ip + 5; break; } @@ -7212,13 +7212,13 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, gpointer systype = mono_type_get_object_checked ((MonoType*)handle, error); goto_if_nok (error, exit); push_type (td, STACK_TYPE_O, mono_defaults.runtimetype_class); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, systype); td->ip = next_ip + 5; } else { interp_add_ins (td, MINT_LDPTR); push_type_vt (td, klass, sizeof (gpointer)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, handle); td->ip += 5; } @@ -7319,7 +7319,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, CHECK_STACK (td, 1); interp_add_ins (td, MINT_MONO_RETHROW); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->sp = td->stack; ++td->ip; link_bblocks = FALSE; @@ -7329,19 +7329,19 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, --td->sp; td->ip += 1; interp_add_ins (td, MINT_LD_DELEGATE_METHOD_PTR); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); break; case CEE_MONO_CALLI_EXTRA_ARG: { - int saved_local = td->sp [-1].local; + int saved_local = td->sp [-1].var; /* Same as CEE_CALLI, except that we drop the extra arg required for llvm specific behaviour */ td->sp -= 2; StackInfo tos = td->sp [1]; // Push back to top of stack and fixup the local offset push_types (td, &tos, 1); - td->sp [-1].local = saved_local; + td->sp [-1].var = saved_local; if (!interp_transform_call (td, method, NULL, generic_context, NULL, FALSE, error, FALSE, FALSE, FALSE)) goto exit; @@ -7354,7 +7354,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDFTN_ADDR); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, (gpointer)func); break; } @@ -7368,19 +7368,19 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->sp -= info->sig->param_count; int *call_args = (int*)mono_mempool_alloc (td->mempool, (info->sig->param_count + 1) * sizeof (int)); for (int i = 0; i < info->sig->param_count; i++) - call_args [i] = td->sp [i].local; + call_args [i] = td->sp [i].var; call_args [info->sig->param_count] = -1; int param_offset = get_tos_offset (td); if (!MONO_TYPE_IS_VOID (info->sig->ret)) { mt = mono_mint_type (info->sig->ret); push_simple_type (td, stack_type [mt]); - dreg = td->sp [-1].local; + dreg = td->sp [-1].var; } else { // dummy dreg push_simple_type (td, STACK_TYPE_I4); td->sp--; - dreg = td->sp [0].local; + dreg = td->sp [0].var; } if (jit_icall_id == MONO_JIT_ICALL_mono_threads_attach_coop) { @@ -7415,18 +7415,18 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else size = mono_class_value_size (klass, NULL); - int local = interp_create_local_explicit (td, m_class_get_byval_arg (klass), size); + int local = interp_create_var_explicit (td, m_class_get_byval_arg (klass), size); interp_add_ins (td, MINT_MOV_VT); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); interp_ins_set_dreg (td->last_ins, local); td->last_ins->data [0] = GINT_TO_UINT16 (size); interp_add_ins (td, MINT_LDLOCA_S); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_ins_set_sreg (td->last_ins, local); - td->locals [local].indirects++; + td->vars [local].indirects++; ++td->ip; break; @@ -7438,7 +7438,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 5; interp_add_ins (td, MINT_LDPTR); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token)); break; case CEE_MONO_PINVOKE_ADDR_CACHE: { @@ -7447,7 +7447,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDPTR); g_assert (method->wrapper_type != MONO_WRAPPER_NONE); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); /* This is a memory slot used by the wrapper */ gpointer addr = imethod_alloc0 (td, sizeof (gpointer)); td->last_ins->data [0] = get_data_item_index (td, addr); @@ -7464,7 +7464,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 5; interp_add_ins (td, MINT_MONO_NEWOBJ); push_simple_type (td, STACK_TYPE_O); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, mono_method_get_wrapper_data (method, token)); break; case CEE_MONO_RETOBJ: @@ -7473,7 +7473,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, td->ip += 5; interp_add_ins (td, MINT_MONO_RETOBJ); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); klass = (MonoClass *)mono_method_get_wrapper_data (method, token); /*stackval_from_data (signature->ret, frame->retval, sp->data.vt, signature->pinvoke);*/ @@ -7490,9 +7490,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, int size = mono_class_native_size (klass, NULL); interp_add_ins (td, MINT_LDOBJ_VT); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type_vt (td, klass, size); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = GINT_TO_UINT16 (size); break; } @@ -7511,7 +7511,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_MONO_LDPTR_INT_REQ_FLAG: interp_add_ins (td, MINT_LDPTR); push_type (td, STACK_TYPE_MP, NULL); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->last_ins->data [0] = get_data_item_index (td, &mono_thread_interruption_request_flag); ++td->ip; break; @@ -7522,7 +7522,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, case CEE_MONO_LDDOMAIN: interp_add_ins (td, MINT_MONO_LDDOMAIN); push_simple_type (td, STACK_TYPE_I); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_MONO_SAVE_LAST_ERROR: @@ -7545,7 +7545,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, // Push a dummy coop gc var interp_add_ins (td, MINT_LDNULL); push_simple_type (td, STACK_TYPE_I); - td->last_ins->dreg = td->sp [-1].local; + td->last_ins->dreg = td->sp [-1].var; interp_add_ins (td, MINT_MONO_ENABLE_GCTRANS); } else { g_assert_checked (jit_icall_id == MONO_JIT_ICALL_mono_threads_exit_gc_safe_region_unbalanced); @@ -7592,9 +7592,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_CEQ_I4 + td->sp [-1].type - STACK_TYPE_I4); } td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_CGT: @@ -7604,9 +7604,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_CGT_I4 + td->sp [-1].type - STACK_TYPE_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_CGT_UN: @@ -7616,9 +7616,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_CGT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_CLT: @@ -7628,9 +7628,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_CLT_I4 + td->sp [-1].type - STACK_TYPE_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_CLT_UN: @@ -7640,9 +7640,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else interp_add_ins (td, MINT_CLT_UN_I4 + td->sp [-1].type - STACK_TYPE_I4); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; case CEE_LDVIRTFTN: /* fallthrough */ @@ -7675,7 +7675,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_generate_void_throw (td, MONO_JIT_ICALL_mono_throw_not_supported); interp_add_ins (td, MINT_LDNULL); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; } @@ -7708,7 +7708,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, mono_interp_error_cleanup (wrapper_error); interp_add_ins (td, MINT_LDNULL); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } else { /* push a pointer to a trampoline that calls m */ gpointer entry = mini_get_interp_callbacks ()->create_method_pointer (m, TRUE, error); @@ -7720,7 +7720,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, WRITE32_INS (td->last_ins, 0, &entry); #endif push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); } td->ip += 5; break; @@ -7732,14 +7732,14 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, CHECK_STACK (td, 1); --td->sp; interp_add_ins (td, MINT_LDVIRTFTN); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); td->last_ins->data [0] = index; } else { interp_add_ins (td, MINT_LDFTN); td->last_ins->data [0] = index; } push_simple_type (td, STACK_TYPE_F); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 5; break; @@ -7759,15 +7759,15 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (!inlining) { interp_add_ins (td, MINT_LDLOCA_S); interp_ins_set_sreg (td->last_ins, n); - td->locals [n].indirects++; + td->vars [n].indirects++; } else { int loc_n = arg_locals [n]; interp_add_ins (td, MINT_LDLOCA_S); interp_ins_set_sreg (td->last_ins, n); - td->locals [loc_n].indirects++; + td->vars [loc_n].indirects++; } push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 3; break; } @@ -7797,9 +7797,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, else loc_n = local_locals [loc_n]; interp_ins_set_sreg (td->last_ins, loc_n); - td->locals [loc_n].indirects++; + td->vars [loc_n].indirects++; push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->ip += 3; break; } @@ -7825,9 +7825,9 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, mono_error_set_generic_error (error, "System", "InvalidProgramException", ""); goto exit; } - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_simple_type (td, STACK_TYPE_MP); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); td->has_localloc = TRUE; ++td->ip; break; @@ -7842,7 +7842,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, } interp_add_ins (td, MINT_ENDFILTER); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); ++td->ip; link_bblocks = FALSE; break; @@ -7866,18 +7866,18 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, if (m_class_is_valuetype (klass)) { --td->sp; interp_add_ins (td, MINT_INITOBJ); - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); i32 = mono_class_value_size (klass, NULL); g_assert (i32 < G_MAXUINT16); td->last_ins->data [0] = GINT_TO_UINT16 (i32); } else { interp_add_ins (td, MINT_LDNULL); push_type (td, STACK_TYPE_O, NULL); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); interp_add_ins (td, MINT_STIND_REF); td->sp -= 2; - interp_ins_set_sregs2 (td->last_ins, td->sp [0].local, td->sp [1].local); + interp_ins_set_sregs2 (td->last_ins, td->sp [0].var, td->sp [1].var); } td->ip += 5; break; @@ -7888,7 +7888,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_MONO_MEMORY_BARRIER); interp_add_ins (td, MINT_CPBLK); td->sp -= 3; - interp_ins_set_sregs3 (td->last_ins, td->sp [0].local, td->sp [1].local, td->sp [2].local); + interp_ins_set_sregs3 (td->last_ins, td->sp [0].var, td->sp [1].var, td->sp [2].var); BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_SEQ); ++td->ip; break; @@ -7907,7 +7907,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, BARRIER_IF_VOLATILE (td, MONO_MEMORY_BARRIER_REL); interp_add_ins (td, MINT_INITBLK); td->sp -= 3; - interp_ins_set_sregs3 (td->last_ins, td->sp [0].local, td->sp [1].local, td->sp [2].local); + interp_ins_set_sregs3 (td->last_ins, td->sp [0].var, td->sp [1].var, td->sp [2].var); td->ip += 1; break; case CEE_NO_: @@ -7946,15 +7946,15 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, interp_add_ins (td, MINT_LDC_I4); WRITE32_INS (td->last_ins, 0, &size); push_simple_type (td, STACK_TYPE_I4); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); break; } case CEE_REFANYTYPE: interp_add_ins (td, MINT_REFANYTYPE); td->sp--; - interp_ins_set_sreg (td->last_ins, td->sp [0].local); + interp_ins_set_sreg (td->last_ins, td->sp [0].var); push_type_vt (td, mono_defaults.typehandle_class, mono_class_value_size (mono_defaults.typehandle_class, NULL)); - interp_ins_set_dreg (td->last_ins, td->sp [-1].local); + interp_ins_set_dreg (td->last_ins, td->sp [-1].var); ++td->ip; break; default: @@ -8059,15 +8059,15 @@ alloc_unopt_global_local (TransformData *td, int *plocal, gpointer data) int local = *plocal; // Execution stack locals are resolved when we emit the instruction in the code stream, // once all global locals have their offset resolved - if (td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) + if (td->vars [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) return; // Check if already resolved - if (td->locals [local].offset != -1) + if (td->vars [local].offset != -1) return; int offset = td->total_locals_size; - int size = td->locals [local].size; - td->locals [local].offset = offset; + int size = td->vars [local].size; + td->vars [local].offset = offset; td->total_locals_size = ALIGN_TO (offset + size, MINT_STACK_SLOT_SIZE); } @@ -8180,8 +8180,8 @@ get_short_brop (int opcode) static int get_local_offset (TransformData *td, int local) { - if (td->locals [local].offset != -1) - return td->locals [local].offset; + if (td->vars [local].offset != -1) + return td->vars [local].offset; // FIXME Some vars might end up with unitialized offset because they are not declared at all in the code. // This can happen if the bblock declaring the var gets removed, while other unreachable bblocks, that access @@ -8193,10 +8193,10 @@ get_local_offset (TransformData *td, int local) // If we use the optimized offset allocator, all locals should have had their offsets already allocated g_assert (!td->optimized); // The only remaining locals to allocate are the ones from the execution stack - g_assert (td->locals [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK); + g_assert (td->vars [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK); - td->locals [local].offset = td->total_locals_size + td->locals [local].stack_offset; - return td->locals [local].offset; + td->vars [local].offset = td->total_locals_size + td->vars [local].stack_offset; + return td->vars [local].offset; } static guint16* @@ -8790,7 +8790,7 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG g_free (td->clause_indexes); g_free (td->data_items); g_free (td->stack); - g_free (td->locals); + g_free (td->vars); g_free (td->local_ref_count); g_hash_table_destroy (td->data_hash); #ifdef ENABLE_EXPERIMENT_TIERED diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 6662fe341dacc1..2f54c647706453 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -23,7 +23,7 @@ #define INTERP_LOCAL_FLAG_UNKNOWN_USE 32 #define INTERP_LOCAL_FLAG_LOCAL_ONLY 64 -// We use this flag to avoid addition of align field in InterpLocal, for now +// We use this flag to avoid addition of align field in InterpVar, for now #define INTERP_LOCAL_FLAG_SIMD 128 typedef struct _InterpInst InterpInst; @@ -36,22 +36,22 @@ typedef struct unsigned char type; unsigned char flags; /* - * The local associated with the value of this stack entry. Every time we push on - * the stack a new local is created. + * The var associated with the value of this stack entry. Every time we push on + * the stack a new var is created. */ - int local; + int var; /* The offset from the execution stack start where this is stored. Used by the fast offset allocator */ int offset; /* Saves how much stack this is using. It is a multiple of MINT_STACK_SLOT_SIZE*/ int size; } StackInfo; -#define LOCAL_VALUE_NONE 0 -#define LOCAL_VALUE_LOCAL 1 -#define LOCAL_VALUE_I4 2 -#define LOCAL_VALUE_I8 3 -#define LOCAL_VALUE_R4 4 -#define LOCAL_VALUE_NON_NULL 5 +#define VAR_VALUE_NONE 0 +#define VAR_VALUE_OTHER_VAR 1 +#define VAR_VALUE_I4 2 +#define VAR_VALUE_I8 3 +#define VAR_VALUE_R4 4 +#define VAR_VALUE_NON_NULL 5 // LocalValue contains data to construct an InterpInst that is equivalent with the contents // of the stack slot / local / argument. @@ -60,7 +60,7 @@ typedef struct { int type; // Holds the local index or the actual constant value union { - int local; + int var; gint32 i; gint64 l; float f; @@ -70,7 +70,7 @@ typedef struct { int def_index; // ref count for ins->dreg int ref_count; -} LocalValue; +} InterpVarValue; struct _InterpInst { guint16 opcode; @@ -200,7 +200,7 @@ typedef struct { // Only used during super instruction pass. InterpInst *def; }; -} InterpLocal; +} InterpVar; typedef struct { @@ -225,13 +225,16 @@ typedef struct gint32 param_area_offset; gint32 total_locals_size; gint32 max_stack_size; - InterpLocal *locals; int dummy_var; int *local_ref_count; unsigned int il_locals_offset; unsigned int il_locals_size; - unsigned int locals_size; - unsigned int locals_capacity; + + // All vars, used in instructions + InterpVar *vars; + unsigned int vars_size; + unsigned int vars_capacity; + int n_data_items; int max_data_items; void **data_items; @@ -444,7 +447,7 @@ int interp_alloc_global_var_offset (TransformData *td, int var); int -interp_create_local (TransformData *td, MonoType *type); +interp_create_var (TransformData *td, MonoType *type); void interp_foreach_ins_var (TransformData *td, InterpInst *ins, gpointer data, void (*callback)(TransformData*, int*, gpointer)); From 2da1bdceaed78b219367837916ec0b98f0e085ee Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Mon, 6 Nov 2023 14:22:47 +0200 Subject: [PATCH 6/6] [mono][interp] Remove flags and use bit fields instead Makes the code clearer and it is easier to maintain. --- src/mono/mono/mini/interp/transform-opt.c | 58 +++++++++++------------ src/mono/mono/mini/interp/transform.c | 30 ++++++------ src/mono/mono/mini/interp/transform.h | 21 ++++---- 3 files changed, 53 insertions(+), 56 deletions(-) diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index 68f922216e24bf..c6fc3a069c045d 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -14,7 +14,7 @@ alloc_var_offset (TransformData *td, int local, gint32 *ptos) offset = *ptos; size = td->vars [local].size; - if (td->vars [local].flags & INTERP_LOCAL_FLAG_SIMD) + if (td->vars [local].simd) offset = ALIGN_TO (offset, MINT_SIMD_ALIGNMENT); td->vars [local].offset = offset; @@ -34,7 +34,7 @@ static void set_var_live_range (TransformData *td, int var, int ins_index) { // We don't track liveness yet for global vars - if (td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) + if (td->vars [var].global) return; if (td->vars [var].live_start == -1) td->vars [var].live_start = ins_index; @@ -51,7 +51,7 @@ static void initialize_global_var (TransformData *td, int var, int bb_index) { // Check if already handled - if (td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) + if (td->vars [var].global) return; if (td->vars [var].bb_index == -1) { @@ -61,7 +61,7 @@ initialize_global_var (TransformData *td, int var, int bb_index) if (td->verbose_level) g_print ("alloc global var %d to offset %d\n", var, td->total_locals_size); interp_alloc_global_var_offset (td, var); - td->vars [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + td->vars [var].global = TRUE; } } @@ -86,11 +86,11 @@ initialize_global_vars (TransformData *td) } else if (opcode == MINT_LDLOCA_S) { int var = ins->sregs [0]; // If global flag is set, it means its offset was already allocated - if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL)) { + if (!td->vars [var].global) { if (td->verbose_level) g_print ("alloc ldloca global var %d to offset %d\n", var, td->total_locals_size); interp_alloc_global_var_offset (td, var); - td->vars [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + td->vars [var].global = TRUE; } } interp_foreach_ins_var (td, ins, (gpointer)(gsize)bb->index, initialize_global_var_cb); @@ -330,7 +330,7 @@ interp_alloc_offsets (TransformData *td) // the created object is set before the call is made. We solve this by making // sure that the dreg is not allocated in the param area, so there is no // risk of conflicts. - td->vars [ins->dreg].flags |= INTERP_LOCAL_FLAG_NO_CALL_ARGS; + td->vars [ins->dreg].no_call_args = TRUE; } if (ins->flags & INTERP_INST_FLAG_CALL) { if (ins->info.call_info && ins->info.call_info->call_args) { @@ -341,16 +341,16 @@ interp_alloc_offsets (TransformData *td) int var = *call_args; while (var != -1) { - if (td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL || + if (td->vars [var].global || !td->local_ref_count || td->local_ref_count [var] > 1 || - td->vars [var].flags & INTERP_LOCAL_FLAG_NO_CALL_ARGS) { + td->vars [var].no_call_args) { // Some vars can't be allocated on the call args stack, since the constraint is that // call args vars die after the call. This isn't necessarily true for global vars or // vars that are used by other instructions aside from the call. // We need to copy the var into a new tmp var int new_var = interp_create_var (td, td->vars [var].type); td->vars [new_var].call = ins; - td->vars [new_var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + td->vars [new_var].call_args = TRUE; int mt = mono_mint_type (td->vars [var].type); if (mt != MINT_TYPE_VT && num_pairs < MINT_MOV_PAIRS_MAX && var <= G_MAXUINT16 && new_var <= G_MAXUINT16) { @@ -376,7 +376,7 @@ interp_alloc_offsets (TransformData *td) } else { // Flag this var as it has special storage on the call args stack td->vars [var].call = ins; - td->vars [var].flags |= INTERP_LOCAL_FLAG_CALL_ARGS; + td->vars [var].call_args = TRUE; } call_args++; var = *call_args; @@ -431,8 +431,8 @@ interp_alloc_offsets (TransformData *td) int var = ins->sregs [i]; if (var == MINT_CALL_ARGS_SREG) continue; - if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->vars [var].live_end == ins_index) { - g_assert (!(td->vars [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS)); + if (!td->vars [var].global && td->vars [var].live_end == ins_index) { + g_assert (!td->vars [var].call_args); end_active_var (td, &av, var); } } @@ -442,7 +442,7 @@ interp_alloc_offsets (TransformData *td) int num_pairs = 2 + opcode - MINT_MOV_8_2; for (int i = 0; i < num_pairs; i++) { int var = ins->data [2 * i + 1]; - if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->vars [var].live_end == ins_index) + if (!td->vars [var].global && td->vars [var].live_end == ins_index) end_active_var (td, &av, var); } } @@ -456,9 +456,9 @@ interp_alloc_offsets (TransformData *td) if (mono_interp_op_dregs [opcode]) { int var = ins->dreg; - if (td->vars [var].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { + if (td->vars [var].call_args) { add_active_call (td, &ac, td->vars [var].call); - } else if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_GLOBAL) && td->vars [var].offset == -1) { + } else if (!td->vars [var].global && td->vars [var].offset == -1) { alloc_var_offset (td, var, ¤t_offset); if (current_offset > final_total_locals_size) final_total_locals_size = current_offset; @@ -486,7 +486,7 @@ interp_alloc_offsets (TransformData *td) td->param_area_offset = final_total_locals_size; for (unsigned int i = 0; i < td->vars_size; i++) { // These are allocated separately at the end of the stack - if (td->vars [i].flags & INTERP_LOCAL_FLAG_CALL_ARGS) { + if (td->vars [i].call_args) { td->vars [i].offset += td->param_area_offset; final_total_locals_size = MAX (td->vars [i].offset + td->vars [i].size, final_total_locals_size); } @@ -953,21 +953,21 @@ interp_local_deadce (TransformData *td) for (unsigned int i = 0; i < td->vars_size; i++) { g_assert (local_ref_count [i] >= 0); g_assert (td->vars [i].indirects >= 0); - if (td->vars [i].indirects || (td->vars [i].flags & INTERP_LOCAL_FLAG_DEAD)) + if (td->vars [i].indirects || td->vars [i].dead) continue; if (!local_ref_count [i]) { needs_dce = TRUE; - td->vars [i].flags |= INTERP_LOCAL_FLAG_DEAD; - } else if (!(td->vars [i].flags & INTERP_LOCAL_FLAG_UNKNOWN_USE)) { - if (!(td->vars [i].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) { + td->vars [i].dead = TRUE; + } else if (!td->vars [i].unknown_use) { + if (!td->vars [i].local_only) { // The value of this var is not passed between multiple basic blocks - td->vars [i].flags |= INTERP_LOCAL_FLAG_LOCAL_ONLY; + td->vars [i].local_only = TRUE; if (td->verbose_level) g_print ("Var %d is local only\n", i); needs_cprop = TRUE; } } - td->vars [i].flags &= ~INTERP_LOCAL_FLAG_UNKNOWN_USE; + td->vars [i].unknown_use = FALSE; } // Return early if all locals are alive @@ -980,7 +980,7 @@ interp_local_deadce (TransformData *td) if (MINT_NO_SIDE_EFFECTS (ins->opcode) || ins->opcode == MINT_LDLOCA_S) { int dreg = ins->dreg; - if (td->vars [dreg].flags & INTERP_LOCAL_FLAG_DEAD) { + if (td->vars [dreg].dead) { if (td->verbose_level) { g_print ("kill dead ins:\n\t"); interp_dump_ins (ins, td->data_items); @@ -1504,7 +1504,7 @@ cprop_sreg (TransformData *td, InterpInst *ins, int *psreg, InterpVarValue *loca if (td->verbose_level) interp_dump_ins (ins, td->data_items); } else if (!local_defs [sreg].ins) { - td->vars [sreg].flags |= INTERP_LOCAL_FLAG_UNKNOWN_USE; + td->vars [sreg].unknown_use = TRUE; } } @@ -1522,7 +1522,7 @@ static void clear_unused_defs (TransformData *td, int *pvar, void *data) { int var = *pvar; - if (!(td->vars [var].flags & INTERP_LOCAL_FLAG_LOCAL_ONLY)) + if (!td->vars [var].local_only) return; if (td->vars [var].indirects) return; @@ -1667,8 +1667,8 @@ interp_cprop (TransformData *td) interp_dump_ins (ins, td->data_items); } } else if (local_defs [sreg].ins != NULL && - (td->vars [sreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && - !(td->vars [dreg].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) && + td->vars [sreg].execution_stack && + !td->vars [dreg].execution_stack && interp_prev_ins (ins) == local_defs [sreg].ins && !(interp_prev_ins (ins)->flags & INTERP_INST_FLAG_PROTECTED_NEWOBJ)) { // hackish temporary optimization that won't be necessary in the future @@ -2153,7 +2153,7 @@ interp_super_instructions (TransformData *td) int opcode = ins->opcode; if (MINT_IS_NOP (opcode)) continue; - if (mono_interp_op_dregs [opcode] && !(td->vars [ins->dreg].flags & INTERP_LOCAL_FLAG_GLOBAL)) + if (mono_interp_op_dregs [opcode] && !td->vars [ins->dreg].global) td->vars [ins->dreg].def = ins; if (opcode == MINT_RET || (opcode >= MINT_RET_I1 && opcode <= MINT_RET_U2)) { diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 8bb7f1285bf826..52dcf65ec48284 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -267,7 +267,7 @@ get_stack_size (TransformData *td, StackInfo *sp, int count) int result = 0; for (int i = 0; i < count; i++) { result += sp [i].size; - if (td->vars [sp [i].var].flags & INTERP_LOCAL_FLAG_SIMD) + if (td->vars [sp [i].var].simd) result = ALIGN_TO (result, MINT_SIMD_ALIGNMENT); } return result; @@ -366,12 +366,12 @@ interp_create_var_explicit (TransformData *td, MonoType *type, int size) } int mt = mono_mint_type (type); InterpVar *local = &td->vars [td->vars_size]; + memset (local, 0, sizeof (InterpVar)); local->type = type; local->mt = mt; - local->flags = 0; if (mt == MINT_TYPE_VT && m_class_is_simd_type (mono_class_from_mono_type_internal (type))) - local->flags |= INTERP_LOCAL_FLAG_SIMD; + local->simd = TRUE; local->indirects = 0; local->offset = -1; local->size = size; @@ -390,7 +390,7 @@ interp_create_dummy_var (TransformData *td) g_assert (td->dummy_var < 0); td->dummy_var = interp_create_var_explicit (td, m_class_get_byval_arg (mono_defaults.void_class), 8); td->vars [td->dummy_var].offset = 0; - td->vars [td->dummy_var].flags = INTERP_LOCAL_FLAG_GLOBAL; + td->vars [td->dummy_var].global = TRUE; } static int @@ -408,7 +408,7 @@ interp_create_stack_var (TransformData *td, StackInfo *sp, int type_size) { int local = interp_create_var_explicit (td, get_type_from_stack (sp->type, sp->klass), type_size); - td->vars [local].flags |= INTERP_LOCAL_FLAG_EXECUTION_STACK; + td->vars [local].execution_stack = TRUE; sp->var = local; } @@ -435,7 +435,7 @@ push_type_explicit (TransformData *td, int type, MonoClass *k, int type_size) interp_create_stack_var (td, sp, type_size); if (!td->optimized) { sp->offset = get_tos_offset (td); - if (td->vars [sp->var].flags & INTERP_LOCAL_FLAG_SIMD) + if (td->vars [sp->var].simd) sp->offset = ALIGN_TO (sp->offset, MINT_SIMD_ALIGNMENT); td->vars [sp->var].stack_offset = sp->offset; // Additional space that is allocated for the frame, when we don't run the var offset allocator @@ -3046,7 +3046,7 @@ static void interp_realign_simd_params (TransformData *td, StackInfo *sp_params, int num_args, int prev_offset) { for (int i = 1; i < num_args; i++) { - if (td->vars [sp_params [i].var].flags & INTERP_LOCAL_FLAG_SIMD) { + if (td->vars [sp_params [i].var].simd) { gint16 offset_amount; // If the simd struct comes immediately after the previous argument we do upper align // otherwise we should lower align to preserve call convention @@ -3545,7 +3545,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target // needs to be able to access the actual arguments to continue with the call so it // needs to know whether there is an empty stack slot between the delegate ptr and the // rest of the args - gboolean first_arg_is_simd = td->vars [sp_args [1].var].flags & INTERP_LOCAL_FLAG_SIMD; + gboolean first_arg_is_simd = td->vars [sp_args [1].var].simd; td->last_ins->data [2] = first_arg_is_simd ? MINT_SIMD_ALIGNMENT : MINT_STACK_SLOT_SIZE; } } else if (calli) { @@ -4098,7 +4098,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet int num_locals = num_args + num_il_locals; imethod->local_offsets = (guint32*)g_malloc (num_il_locals * sizeof(guint32)); - td->vars = (InterpVar*)g_malloc (num_locals * sizeof (InterpVar)); + td->vars = (InterpVar*)g_malloc0 (num_locals * sizeof (InterpVar)); td->vars_size = num_locals; td->vars_capacity = td->vars_size; offset = 0; @@ -4116,7 +4116,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet type = mono_method_signature_internal (td->method)->params [i - sig->hasthis]; int mt = mono_mint_type (type); td->vars [i].type = type; - td->vars [i].flags = INTERP_LOCAL_FLAG_GLOBAL; + td->vars [i].global = TRUE; td->vars [i].indirects = 0; td->vars [i].mt = mt; td->vars [i].def = NULL; @@ -4143,7 +4143,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet imethod->local_offsets [i] = offset; td->vars [index].type = header->locals [i]; td->vars [index].offset = offset; - td->vars [index].flags = INTERP_LOCAL_FLAG_GLOBAL; + td->vars [index].global = TRUE; td->vars [index].indirects = 0; td->vars [index].mt = mono_mint_type (header->locals [i]); td->vars [index].def = NULL; @@ -4160,7 +4160,7 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet td->clause_vars = (int*)mono_mempool_alloc (td->mempool, sizeof (int) * header->num_clauses); for (guint i = 0; i < header->num_clauses; i++) { int var = interp_create_var (td, mono_get_object_type ()); - td->vars [var].flags |= INTERP_LOCAL_FLAG_GLOBAL; + td->vars [var].global = TRUE; interp_alloc_global_var_offset (td, var); imethod->clause_data_offsets [i] = td->vars [var].offset; td->clause_vars [i] = var; @@ -6062,7 +6062,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header, call_offset = ALIGN_TO (call_offset, MINT_STACK_ALIGNMENT); if (param_count) { - if (td->vars [sp_params [0].var].flags & INTERP_LOCAL_FLAG_SIMD) { + if (td->vars [sp_params [0].var].simd) { // if first arg is simd, move all args at next aligned offset after this ptr interp_add_ins (td, MINT_MOV_STACK_UNOPT); td->last_ins->data [0] = GINT_TO_UINT16 (param_offset); @@ -8059,7 +8059,7 @@ alloc_unopt_global_local (TransformData *td, int *plocal, gpointer data) int local = *plocal; // Execution stack locals are resolved when we emit the instruction in the code stream, // once all global locals have their offset resolved - if (td->vars [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK) + if (td->vars [local].execution_stack) return; // Check if already resolved if (td->vars [local].offset != -1) @@ -8193,7 +8193,7 @@ get_local_offset (TransformData *td, int local) // If we use the optimized offset allocator, all locals should have had their offsets already allocated g_assert (!td->optimized); // The only remaining locals to allocate are the ones from the execution stack - g_assert (td->vars [local].flags & INTERP_LOCAL_FLAG_EXECUTION_STACK); + g_assert (td->vars [local].execution_stack); td->vars [local].offset = td->total_locals_size + td->vars [local].stack_offset; return td->vars [local].offset; diff --git a/src/mono/mono/mini/interp/transform.h b/src/mono/mono/mini/interp/transform.h index 2f54c647706453..4b2c5bb5992ade 100644 --- a/src/mono/mono/mini/interp/transform.h +++ b/src/mono/mono/mini/interp/transform.h @@ -15,17 +15,6 @@ // This instruction is protected by a clause #define INTERP_INST_FLAG_PROTECTED_NEWOBJ 128 -#define INTERP_LOCAL_FLAG_DEAD 1 -#define INTERP_LOCAL_FLAG_EXECUTION_STACK 2 -#define INTERP_LOCAL_FLAG_CALL_ARGS 4 -#define INTERP_LOCAL_FLAG_GLOBAL 8 -#define INTERP_LOCAL_FLAG_NO_CALL_ARGS 16 - -#define INTERP_LOCAL_FLAG_UNKNOWN_USE 32 -#define INTERP_LOCAL_FLAG_LOCAL_ONLY 64 -// We use this flag to avoid addition of align field in InterpVar, for now -#define INTERP_LOCAL_FLAG_SIMD 128 - typedef struct _InterpInst InterpInst; typedef struct _InterpBasicBlock InterpBasicBlock; typedef struct _InterpCallInfo InterpCallInfo; @@ -179,7 +168,6 @@ typedef struct { typedef struct { MonoType *type; int mt; - int flags; int indirects; int offset; int size; @@ -200,6 +188,15 @@ typedef struct { // Only used during super instruction pass. InterpInst *def; }; + + guint dead : 1; + guint execution_stack : 1; + guint call_args : 1; + guint global : 1; + guint no_call_args : 1; + guint unknown_use : 1; + guint local_only : 1; + guint simd : 1; // We use this flag to avoid addition of align field in InterpVar, for now } InterpVar; typedef struct