Skip to content

Commit 5373c01

Browse files
authored
[wasm] Fix bugs related to jiterpreter heuristic (#99273)
* Fix bugs related to jiterpreter heuristic: - VALUE_ABORT_OUTSIDE_BRANCH_BLOCK_NONE was ignored, causing the heuristic to reject good traces - Instruction counting was using basic blocks' in-count instead of their actual instruction count - When two basic blocks were merged, their jiterpreter data (backwards branch target and contains call flags) were not merged * Fix jiterpreter opcode insertion not updating the estimated native offset of opcodes (this would result in weird asserts randomly occurring in sufficiently large methods) * Fix jiterpreter being unable to compile a backward branch that targeted the trace's prepare/enter opcode, since it was not "inside" the trace * Enable more jiterpreter diagnostics when a method is verbose according to the interpreter * Add better diagnostic messages for a few failure scenarios
1 parent d88ed80 commit 5373c01

File tree

7 files changed

+87
-33
lines changed

7 files changed

+87
-33
lines changed

src/mono/browser/runtime/jiterpreter-support.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { localHeapViewU8, localHeapViewU32 } from "./memory";
1212
import { utf8ToString } from "./strings";
1313
import {
1414
JiterpNumberMode, BailoutReason, JiterpreterTable,
15-
JiterpCounter, JiterpMember
15+
JiterpCounter, JiterpMember, OpcodeInfoType
1616
} from "./jiterpreter-enums";
1717

1818
export const maxFailures = 2,
@@ -104,6 +104,7 @@ export class WasmBuilder {
104104
backBranchOffsets: Array<MintOpcodePtr> = [];
105105
callHandlerReturnAddresses: Array<MintOpcodePtr> = [];
106106
nextConstantSlot = 0;
107+
backBranchTraceLevel = 0;
107108

108109
compressImportNames = false;
109110
lockImports = false;
@@ -1139,8 +1140,11 @@ class Cfg {
11391140
backBranchTargets: Uint16Array | null = null;
11401141
base!: MintOpcodePtr;
11411142
ip!: MintOpcodePtr;
1143+
// The address of the prepare point
11421144
entryIp!: MintOpcodePtr;
11431145
exitIp!: MintOpcodePtr;
1146+
// The address of the first actual opcode in the trace
1147+
firstOpcodeIp!: MintOpcodePtr;
11441148
lastSegmentStartIp!: MintOpcodePtr;
11451149
lastSegmentEnd = 0;
11461150
overheadBytes = 0;
@@ -1161,7 +1165,7 @@ class Cfg {
11611165
this.startOfBody = startOfBody;
11621166
this.backBranchTargets = backBranchTargets;
11631167
this.base = this.builder.base;
1164-
this.ip = this.lastSegmentStartIp = this.builder.base;
1168+
this.ip = this.lastSegmentStartIp = this.firstOpcodeIp = this.builder.base;
11651169
this.lastSegmentEnd = 0;
11661170
this.overheadBytes = 10; // epilogue
11671171
this.dispatchTable.clear();
@@ -1173,6 +1177,9 @@ class Cfg {
11731177
// We have a header containing the table of locals and we need to preserve it
11741178
entry(ip: MintOpcodePtr) {
11751179
this.entryIp = ip;
1180+
// Skip over the enter opcode
1181+
const enterSizeU16 = cwraps.mono_jiterp_get_opcode_info(MintOpcode.MINT_TIER_ENTER_JITERPRETER, OpcodeInfoType.Length);
1182+
this.firstOpcodeIp = ip + <any>(enterSizeU16 * 2);
11761183
this.appendBlob();
11771184
mono_assert(this.segments.length === 1, "expected 1 segment");
11781185
mono_assert(this.segments[0].type === "blob", "expected blob");
@@ -1183,6 +1190,7 @@ class Cfg {
11831190
this.overheadBytes += 20; // some extra padding for the dispatch br_table
11841191
this.overheadBytes += this.backBranchTargets.length; // one byte for each target in the table
11851192
}
1193+
return this.firstOpcodeIp;
11861194
}
11871195

11881196
appendBlob() {

src/mono/browser/runtime/jiterpreter-trace-generator.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
emitPadding, traceBranchDisplacements,
3838
traceEip, nullCheckValidation,
3939
traceNullCheckOptimizations,
40-
nullCheckCaching, traceBackBranches,
40+
nullCheckCaching, defaultTraceBackBranches,
4141
maxCallHandlerReturnAddresses,
4242

4343
mostRecentOptions,
@@ -219,14 +219,17 @@ export function generateWasmBody(
219219
let result = 0,
220220
prologueOpcodeCounter = 0,
221221
conditionalOpcodeCounter = 0;
222+
222223
eraseInferredState();
223224

224-
// Skip over the enter opcode
225-
const enterSizeU16 = cwraps.mono_jiterp_get_opcode_info(MintOpcode.MINT_TIER_ENTER_JITERPRETER, OpcodeInfoType.Length);
226-
ip += <any>(enterSizeU16 * 2);
227-
let rip = ip;
225+
// If a trace is instrumented, also activate back branch tracing
226+
builder.backBranchTraceLevel = instrumentedTraceId
227+
? 2
228+
: defaultTraceBackBranches;
228229

229-
builder.cfg.entry(ip);
230+
// Record the address of our prepare_jiterpreter opcode as the entry point, not the opcode after it.
231+
// Some back-branches will target prepare_jiterpreter directly, and we need them to work.
232+
let rip = builder.cfg.entry(ip);
230233

231234
while (ip) {
232235
// This means some code went 'ip = abort; continue'
@@ -301,7 +304,7 @@ export function generateWasmBody(
301304
// We record the offset of each backward branch we encounter, so that later branch
302305
// opcodes know that it's available by branching to the top of the dispatch loop
303306
if (isBackBranchTarget) {
304-
if (traceBackBranches > 1)
307+
if (builder.backBranchTraceLevel > 1)
305308
mono_log_info(`${traceName} recording back branch target 0x${(<any>ip).toString(16)}`);
306309
builder.backBranchOffsets.push(ip);
307310
}
@@ -2739,18 +2742,18 @@ function emit_branch(
27392742
// We found a backward branch target we can branch to, so we branch out
27402743
// to the top of the loop body
27412744
// append_safepoint(builder, ip);
2742-
if (traceBackBranches > 1)
2743-
mono_log_info(`performing backward branch to 0x${destination.toString(16)}`);
2745+
if (builder.backBranchTraceLevel > 1)
2746+
mono_log_info(`0x${(<any>ip).toString(16)} performing backward branch to 0x${destination.toString(16)}`);
27442747
if (isCallHandler)
27452748
append_call_handler_store_ret_ip(builder, ip, frame, opcode);
27462749
builder.cfg.branch(destination, true, CfgBranchType.Unconditional);
27472750
modifyCounter(JiterpCounter.BackBranchesEmitted, 1);
27482751
return true;
27492752
} else {
27502753
if (destination < builder.cfg.entryIp) {
2751-
if ((traceBackBranches > 1) || (builder.cfg.trace > 1))
2752-
mono_log_info(`${getOpcodeName(opcode)} target 0x${destination.toString(16)} before start of trace`);
2753-
} else if ((traceBackBranches > 0) || (builder.cfg.trace > 0))
2754+
if ((builder.backBranchTraceLevel > 1) || (builder.cfg.trace > 1))
2755+
mono_log_info(`0x${(<any>ip).toString(16)} ${getOpcodeName(opcode)} target 0x${destination.toString(16)} before start of trace`);
2756+
} else if ((builder.backBranchTraceLevel > 0) || (builder.cfg.trace > 0))
27542757
mono_log_info(`0x${(<any>ip).toString(16)} ${getOpcodeName(opcode)} target 0x${destination.toString(16)} not found in list ` +
27552758
builder.backBranchOffsets.map(bbo => "0x" + (<any>bbo).toString(16)).join(", ")
27562759
);
@@ -2820,15 +2823,15 @@ function emit_branch(
28202823
if (builder.backBranchOffsets.indexOf(destination) >= 0) {
28212824
// We found a backwards branch target we can reach via our outer trace loop, so
28222825
// we update eip and branch out to the top of the loop block
2823-
if (traceBackBranches > 1)
2824-
mono_log_info(`performing conditional backward branch to 0x${destination.toString(16)}`);
2826+
if (builder.backBranchTraceLevel > 1)
2827+
mono_log_info(`0x${(<any>ip).toString(16)} performing conditional backward branch to 0x${destination.toString(16)}`);
28252828
builder.cfg.branch(destination, true, isSafepoint ? CfgBranchType.SafepointConditional : CfgBranchType.Conditional);
28262829
modifyCounter(JiterpCounter.BackBranchesEmitted, 1);
28272830
} else {
28282831
if (destination < builder.cfg.entryIp) {
2829-
if ((traceBackBranches > 1) || (builder.cfg.trace > 1))
2830-
mono_log_info(`${getOpcodeName(opcode)} target 0x${destination.toString(16)} before start of trace`);
2831-
} else if ((traceBackBranches > 0) || (builder.cfg.trace > 0))
2832+
if ((builder.backBranchTraceLevel > 1) || (builder.cfg.trace > 1))
2833+
mono_log_info(`0x${(<any>ip).toString(16)} ${getOpcodeName(opcode)} target 0x${destination.toString(16)} before start of trace`);
2834+
} else if ((builder.backBranchTraceLevel > 0) || (builder.cfg.trace > 0))
28322835
mono_log_info(`0x${(<any>ip).toString(16)} ${getOpcodeName(opcode)} target 0x${destination.toString(16)} not found in list ` +
28332836
builder.backBranchOffsets.map(bbo => "0x" + (<any>bbo).toString(16)).join(", ")
28342837
);

src/mono/browser/runtime/jiterpreter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export const
5656
traceNullCheckOptimizations = false,
5757
// Print diagnostic information when generating backward branches
5858
// 1 = failures only, 2 = full detail
59-
traceBackBranches = 0,
59+
defaultTraceBackBranches = 0,
6060
// Enable generating conditional backward branches for ENDFINALLY opcodes if we saw some CALL_HANDLER
6161
// opcodes previously, up to this many potential return addresses. If a trace contains more potential
6262
// return addresses than this we will not emit code for the ENDFINALLY opcode
@@ -1032,7 +1032,7 @@ export function mono_interp_tier_prepare_jiterpreter(
10321032
const threshold = (<any>ip - <any>startOfBody) / 2;
10331033
let foundReachableBranchTarget = false;
10341034
for (let i = 0; i < backwardBranchTable.length; i++) {
1035-
if (backwardBranchTable[i] > threshold) {
1035+
if (backwardBranchTable[i] >= threshold) {
10361036
foundReachableBranchTarget = true;
10371037
break;
10381038
}

src/mono/mono/mini/interp/jiterpreter.c

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -617,16 +617,17 @@ jiterp_get_opcode_value (InterpInst *ins, gboolean *inside_branch_block)
617617
initialize_opcode_value_table ();
618618

619619
guint16 opcode = ins->opcode;
620-
g_assert(opcode < MINT_LASTOP);
620+
g_assert (opcode < MINT_LASTOP);
621621
int table_value = opcode_value_table[opcode];
622622

623-
if (table_value == VALUE_ABORT_OUTSIDE_BRANCH_BLOCK) {
624-
return *inside_branch_block ? VALUE_LOW : VALUE_ABORT;
625-
} else if (table_value == VALUE_ABORT_OUTSIDE_BRANCH_BLOCK) {
626-
return *inside_branch_block ? VALUE_NONE : VALUE_ABORT;
627-
} else if (table_value == VALUE_BEGIN_BRANCH_BLOCK) {
628-
*inside_branch_block = TRUE;
629-
return VALUE_NORMAL;
623+
switch (table_value) {
624+
case VALUE_ABORT_OUTSIDE_BRANCH_BLOCK:
625+
return *inside_branch_block ? VALUE_LOW : VALUE_ABORT;
626+
case VALUE_ABORT_OUTSIDE_BRANCH_BLOCK_NONE:
627+
return *inside_branch_block ? VALUE_NONE : VALUE_ABORT;
628+
case VALUE_BEGIN_BRANCH_BLOCK:
629+
*inside_branch_block = TRUE;
630+
return VALUE_NORMAL;
630631
}
631632

632633
switch (opcode) {
@@ -884,7 +885,10 @@ jiterp_insert_entry_points (void *_imethod, void *_td)
884885
// Increase the instruction counter. If we inserted an entry point at the top of this bb,
885886
// the new instruction counter will be the number of instructions in the block, so if
886887
// it's big enough we'll be able to insert another entry point right away.
887-
instruction_count += bb->in_count;
888+
for (InterpInst * ins = bb->first_ins; ins != NULL; ins = ins->next) {
889+
if (!MINT_IS_EMIT_NOP (ins->opcode))
890+
instruction_count++;
891+
}
888892

889893
build_address_taken_bitset (td, bb, bitset_size);
890894
}
@@ -1673,7 +1677,20 @@ mono_jiterp_tlqueue_clear (int queue) {
16731677

16741678
// HACK: fix C4206
16751679
EMSCRIPTEN_KEEPALIVE
1680+
#else
1681+
int
1682+
mono_jiterp_is_enabled (void);
16761683
#endif // HOST_BROWSER
16771684

1678-
void jiterp_preserve_module (void) {
1685+
int
1686+
mono_jiterp_is_enabled () {
1687+
#if HOST_BROWSER
1688+
return mono_opt_jiterpreter_traces_enabled;
1689+
#else
1690+
return 0;
1691+
#endif
1692+
}
1693+
1694+
void
1695+
jiterp_preserve_module (void) {
16791696
}

src/mono/mono/mini/interp/jiterpreter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,7 @@ mono_jiterp_tlqueue_purge_all (gpointer item);
239239

240240
#endif // HOST_BROWSER
241241

242+
int
243+
mono_jiterp_is_enabled ();
244+
242245
#endif // __MONO_MINI_JITERPRETER_H__

src/mono/mono/mini/interp/transform-opt.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,14 @@ interp_merge_bblocks (TransformData *td, InterpBasicBlock *bb, InterpBasicBlock
16251625
}
16261626
}
16271627

1628+
#if defined(TARGET_WASM)
1629+
// Copy jiterpreter data
1630+
if (bbadd->backwards_branch_target)
1631+
bb->backwards_branch_target = TRUE;
1632+
if (bbadd->contains_call_instruction)
1633+
bb->contains_call_instruction = TRUE;
1634+
#endif
1635+
16281636
mark_bb_as_dead (td, bbadd, bb);
16291637
}
16301638

src/mono/mono/mini/interp/transform.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8382,6 +8382,14 @@ interp_compute_native_offset_estimates (TransformData *td)
83828382
if (!td->optimized && bb->patchpoint_bb)
83838383
noe += 2;
83848384

8385+
#if HOST_BROWSER
8386+
// We don't know in advance whether a bb will have a trace entry point,
8387+
// but we know that it will only ever have one trace entry point, so
8388+
// reserve space for it so we can correctly insert one later
8389+
if (mono_jiterp_is_enabled ())
8390+
noe += 4;
8391+
#endif
8392+
83858393
for (ins = bb->first_ins; ins != NULL; ins = ins->next) {
83868394
int opcode = ins->opcode;
83878395
// Skip dummy opcodes for more precise offset computation
@@ -8532,8 +8540,15 @@ emit_compacted_instruction (TransformData *td, guint16* start_ip, InterpInst *in
85328540
gboolean is_short = interp_is_short_offset (br_offset, target_bb_estimated_offset);
85338541
if (is_short)
85348542
*start_ip = GINT_TO_OPCODE (get_short_brop (opcode));
8535-
else
8536-
g_assert (!MINT_IS_SUPER_BRANCH (opcode)); // FIXME missing handling for long branch
8543+
else if (MINT_IS_SUPER_BRANCH (opcode)) {
8544+
g_printf (
8545+
"long superbranch detected with opcode %d (%s) in method %s.%s\n",
8546+
opcode, mono_interp_opname (opcode),
8547+
m_class_get_name (td->method->klass), td->method->name
8548+
);
8549+
// FIXME missing handling for long branch
8550+
g_assert (FALSE);
8551+
}
85378552

85388553
// We don't know the in_offset of the target, add a reloc
85398554
Reloc *reloc = (Reloc*)mono_mempool_alloc0 (td->mempool, sizeof (Reloc));

0 commit comments

Comments
 (0)