diff --git a/src/mono/browser/runtime/jiterpreter-support.ts b/src/mono/browser/runtime/jiterpreter-support.ts index 6ca884459d9504..b1c81b257b5d8e 100644 --- a/src/mono/browser/runtime/jiterpreter-support.ts +++ b/src/mono/browser/runtime/jiterpreter-support.ts @@ -12,7 +12,7 @@ import { localHeapViewU8, localHeapViewU32 } from "./memory"; import { utf8ToString } from "./strings"; import { JiterpNumberMode, BailoutReason, JiterpreterTable, - JiterpCounter, JiterpMember + JiterpCounter, JiterpMember, OpcodeInfoType } from "./jiterpreter-enums"; export const maxFailures = 2, @@ -104,6 +104,7 @@ export class WasmBuilder { backBranchOffsets: Array = []; callHandlerReturnAddresses: Array = []; nextConstantSlot = 0; + backBranchTraceLevel = 0; compressImportNames = false; lockImports = false; @@ -1139,8 +1140,11 @@ class Cfg { backBranchTargets: Uint16Array | null = null; base!: MintOpcodePtr; ip!: MintOpcodePtr; + // The address of the prepare point entryIp!: MintOpcodePtr; exitIp!: MintOpcodePtr; + // The address of the first actual opcode in the trace + firstOpcodeIp!: MintOpcodePtr; lastSegmentStartIp!: MintOpcodePtr; lastSegmentEnd = 0; overheadBytes = 0; @@ -1161,7 +1165,7 @@ class Cfg { this.startOfBody = startOfBody; this.backBranchTargets = backBranchTargets; this.base = this.builder.base; - this.ip = this.lastSegmentStartIp = this.builder.base; + this.ip = this.lastSegmentStartIp = this.firstOpcodeIp = this.builder.base; this.lastSegmentEnd = 0; this.overheadBytes = 10; // epilogue this.dispatchTable.clear(); @@ -1173,6 +1177,9 @@ class Cfg { // We have a header containing the table of locals and we need to preserve it entry(ip: MintOpcodePtr) { this.entryIp = ip; + // Skip over the enter opcode + const enterSizeU16 = cwraps.mono_jiterp_get_opcode_info(MintOpcode.MINT_TIER_ENTER_JITERPRETER, OpcodeInfoType.Length); + this.firstOpcodeIp = ip + (enterSizeU16 * 2); this.appendBlob(); mono_assert(this.segments.length === 1, "expected 1 segment"); mono_assert(this.segments[0].type === "blob", "expected blob"); @@ -1183,6 +1190,7 @@ class Cfg { this.overheadBytes += 20; // some extra padding for the dispatch br_table this.overheadBytes += this.backBranchTargets.length; // one byte for each target in the table } + return this.firstOpcodeIp; } appendBlob() { diff --git a/src/mono/browser/runtime/jiterpreter-trace-generator.ts b/src/mono/browser/runtime/jiterpreter-trace-generator.ts index 9b3002eae107ab..dcfc788b8d14ba 100644 --- a/src/mono/browser/runtime/jiterpreter-trace-generator.ts +++ b/src/mono/browser/runtime/jiterpreter-trace-generator.ts @@ -37,7 +37,7 @@ import { emitPadding, traceBranchDisplacements, traceEip, nullCheckValidation, traceNullCheckOptimizations, - nullCheckCaching, traceBackBranches, + nullCheckCaching, defaultTraceBackBranches, maxCallHandlerReturnAddresses, mostRecentOptions, @@ -219,14 +219,17 @@ export function generateWasmBody( let result = 0, prologueOpcodeCounter = 0, conditionalOpcodeCounter = 0; + eraseInferredState(); - // Skip over the enter opcode - const enterSizeU16 = cwraps.mono_jiterp_get_opcode_info(MintOpcode.MINT_TIER_ENTER_JITERPRETER, OpcodeInfoType.Length); - ip += (enterSizeU16 * 2); - let rip = ip; + // If a trace is instrumented, also activate back branch tracing + builder.backBranchTraceLevel = instrumentedTraceId + ? 2 + : defaultTraceBackBranches; - builder.cfg.entry(ip); + // Record the address of our prepare_jiterpreter opcode as the entry point, not the opcode after it. + // Some back-branches will target prepare_jiterpreter directly, and we need them to work. + let rip = builder.cfg.entry(ip); while (ip) { // This means some code went 'ip = abort; continue' @@ -301,7 +304,7 @@ export function generateWasmBody( // We record the offset of each backward branch we encounter, so that later branch // opcodes know that it's available by branching to the top of the dispatch loop if (isBackBranchTarget) { - if (traceBackBranches > 1) + if (builder.backBranchTraceLevel > 1) mono_log_info(`${traceName} recording back branch target 0x${(ip).toString(16)}`); builder.backBranchOffsets.push(ip); } @@ -2723,8 +2726,8 @@ function emit_branch( // We found a backward branch target we can branch to, so we branch out // to the top of the loop body // append_safepoint(builder, ip); - if (traceBackBranches > 1) - mono_log_info(`performing backward branch to 0x${destination.toString(16)}`); + if (builder.backBranchTraceLevel > 1) + mono_log_info(`0x${(ip).toString(16)} performing backward branch to 0x${destination.toString(16)}`); if (isCallHandler) append_call_handler_store_ret_ip(builder, ip, frame, opcode); builder.cfg.branch(destination, true, CfgBranchType.Unconditional); @@ -2732,9 +2735,9 @@ function emit_branch( return true; } else { if (destination < builder.cfg.entryIp) { - if ((traceBackBranches > 1) || (builder.cfg.trace > 1)) - mono_log_info(`${getOpcodeName(opcode)} target 0x${destination.toString(16)} before start of trace`); - } else if ((traceBackBranches > 0) || (builder.cfg.trace > 0)) + if ((builder.backBranchTraceLevel > 1) || (builder.cfg.trace > 1)) + mono_log_info(`0x${(ip).toString(16)} ${getOpcodeName(opcode)} target 0x${destination.toString(16)} before start of trace`); + } else if ((builder.backBranchTraceLevel > 0) || (builder.cfg.trace > 0)) mono_log_info(`0x${(ip).toString(16)} ${getOpcodeName(opcode)} target 0x${destination.toString(16)} not found in list ` + builder.backBranchOffsets.map(bbo => "0x" + (bbo).toString(16)).join(", ") ); @@ -2804,15 +2807,15 @@ function emit_branch( if (builder.backBranchOffsets.indexOf(destination) >= 0) { // We found a backwards branch target we can reach via our outer trace loop, so // we update eip and branch out to the top of the loop block - if (traceBackBranches > 1) - mono_log_info(`performing conditional backward branch to 0x${destination.toString(16)}`); + if (builder.backBranchTraceLevel > 1) + mono_log_info(`0x${(ip).toString(16)} performing conditional backward branch to 0x${destination.toString(16)}`); builder.cfg.branch(destination, true, isSafepoint ? CfgBranchType.SafepointConditional : CfgBranchType.Conditional); modifyCounter(JiterpCounter.BackBranchesEmitted, 1); } else { if (destination < builder.cfg.entryIp) { - if ((traceBackBranches > 1) || (builder.cfg.trace > 1)) - mono_log_info(`${getOpcodeName(opcode)} target 0x${destination.toString(16)} before start of trace`); - } else if ((traceBackBranches > 0) || (builder.cfg.trace > 0)) + if ((builder.backBranchTraceLevel > 1) || (builder.cfg.trace > 1)) + mono_log_info(`0x${(ip).toString(16)} ${getOpcodeName(opcode)} target 0x${destination.toString(16)} before start of trace`); + } else if ((builder.backBranchTraceLevel > 0) || (builder.cfg.trace > 0)) mono_log_info(`0x${(ip).toString(16)} ${getOpcodeName(opcode)} target 0x${destination.toString(16)} not found in list ` + builder.backBranchOffsets.map(bbo => "0x" + (bbo).toString(16)).join(", ") ); diff --git a/src/mono/browser/runtime/jiterpreter.ts b/src/mono/browser/runtime/jiterpreter.ts index 5ef86e182905eb..8ed4c618f77313 100644 --- a/src/mono/browser/runtime/jiterpreter.ts +++ b/src/mono/browser/runtime/jiterpreter.ts @@ -56,7 +56,7 @@ export const traceNullCheckOptimizations = false, // Print diagnostic information when generating backward branches // 1 = failures only, 2 = full detail - traceBackBranches = 0, + defaultTraceBackBranches = 0, // Enable generating conditional backward branches for ENDFINALLY opcodes if we saw some CALL_HANDLER // opcodes previously, up to this many potential return addresses. If a trace contains more potential // return addresses than this we will not emit code for the ENDFINALLY opcode @@ -1032,7 +1032,7 @@ export function mono_interp_tier_prepare_jiterpreter( const threshold = (ip - startOfBody) / 2; let foundReachableBranchTarget = false; for (let i = 0; i < backwardBranchTable.length; i++) { - if (backwardBranchTable[i] > threshold) { + if (backwardBranchTable[i] >= threshold) { foundReachableBranchTarget = true; break; } diff --git a/src/mono/mono/mini/interp/jiterpreter.c b/src/mono/mono/mini/interp/jiterpreter.c index 52a6f74f47d631..3a7b54da33eb22 100644 --- a/src/mono/mono/mini/interp/jiterpreter.c +++ b/src/mono/mono/mini/interp/jiterpreter.c @@ -617,16 +617,17 @@ jiterp_get_opcode_value (InterpInst *ins, gboolean *inside_branch_block) initialize_opcode_value_table (); guint16 opcode = ins->opcode; - g_assert(opcode < MINT_LASTOP); + g_assert (opcode < MINT_LASTOP); int table_value = opcode_value_table[opcode]; - if (table_value == VALUE_ABORT_OUTSIDE_BRANCH_BLOCK) { - return *inside_branch_block ? VALUE_LOW : VALUE_ABORT; - } else if (table_value == VALUE_ABORT_OUTSIDE_BRANCH_BLOCK) { - return *inside_branch_block ? VALUE_NONE : VALUE_ABORT; - } else if (table_value == VALUE_BEGIN_BRANCH_BLOCK) { - *inside_branch_block = TRUE; - return VALUE_NORMAL; + switch (table_value) { + case VALUE_ABORT_OUTSIDE_BRANCH_BLOCK: + return *inside_branch_block ? VALUE_LOW : VALUE_ABORT; + case VALUE_ABORT_OUTSIDE_BRANCH_BLOCK_NONE: + return *inside_branch_block ? VALUE_NONE : VALUE_ABORT; + case VALUE_BEGIN_BRANCH_BLOCK: + *inside_branch_block = TRUE; + return VALUE_NORMAL; } switch (opcode) { @@ -884,7 +885,10 @@ jiterp_insert_entry_points (void *_imethod, void *_td) // Increase the instruction counter. If we inserted an entry point at the top of this bb, // the new instruction counter will be the number of instructions in the block, so if // it's big enough we'll be able to insert another entry point right away. - instruction_count += bb->in_count; + for (InterpInst * ins = bb->first_ins; ins != NULL; ins = ins->next) { + if (!MINT_IS_EMIT_NOP (ins->opcode)) + instruction_count++; + } build_address_taken_bitset (td, bb, bitset_size); } @@ -1673,7 +1677,20 @@ mono_jiterp_tlqueue_clear (int queue) { // HACK: fix C4206 EMSCRIPTEN_KEEPALIVE +#else +int +mono_jiterp_is_enabled (void); #endif // HOST_BROWSER -void jiterp_preserve_module (void) { +int +mono_jiterp_is_enabled () { +#if HOST_BROWSER + return mono_opt_jiterpreter_traces_enabled; +#else + return 0; +#endif +} + +void +jiterp_preserve_module (void) { } diff --git a/src/mono/mono/mini/interp/jiterpreter.h b/src/mono/mono/mini/interp/jiterpreter.h index 26b05f64a0c85f..7307e449c9c1a8 100644 --- a/src/mono/mono/mini/interp/jiterpreter.h +++ b/src/mono/mono/mini/interp/jiterpreter.h @@ -239,4 +239,7 @@ mono_jiterp_tlqueue_purge_all (gpointer item); #endif // HOST_BROWSER +int +mono_jiterp_is_enabled (); + #endif // __MONO_MINI_JITERPRETER_H__ diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index 1b5e2b7a026b8b..99b70646b76c03 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -1625,6 +1625,14 @@ interp_merge_bblocks (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock } } +#if defined(TARGET_WASM) + // Copy jiterpreter data + if (bbadd->backwards_branch_target) + bb->backwards_branch_target = TRUE; + if (bbadd->contains_call_instruction) + bb->contains_call_instruction = TRUE; +#endif + mark_bb_as_dead (td, bbadd, bb); } diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index d791ea9d579e4d..1dce210e812a44 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -8398,6 +8398,14 @@ interp_compute_native_offset_estimates (TransformData *td) if (!td->optimized && bb->patchpoint_bb) noe += 2; +#if HOST_BROWSER + // We don't know in advance whether a bb will have a trace entry point, + // but we know that it will only ever have one trace entry point, so + // reserve space for it so we can correctly insert one later + if (mono_jiterp_is_enabled ()) + noe += 4; +#endif + for (ins = bb->first_ins; ins != NULL; ins = ins->next) { int opcode = ins->opcode; // Skip dummy opcodes for more precise offset computation @@ -8548,8 +8556,15 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in gboolean is_short = interp_is_short_offset (br_offset, target_bb_estimated_offset); if (is_short) *start_ip = GINT_TO_OPCODE (get_short_brop (opcode)); - else - g_assert (!MINT_IS_SUPER_BRANCH (opcode)); // FIXME missing handling for long branch + else if (MINT_IS_SUPER_BRANCH (opcode)) { + g_printf ( + "long superbranch detected with opcode %d (%s) in method %s.%s\n", + opcode, mono_interp_opname (opcode), + m_class_get_name (td->method->klass), td->method->name + ); + // FIXME missing handling for long branch + g_assert (FALSE); + } // We don't know the in_offset of the target, add a reloc Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));