Skip to content

Commit 53a10ea

Browse files
authored
[wasm] [jiterp] Use wasm if opcode for null checks and conditional branches (#88114)
Use wasm if opcode for null checks and conditional branches where necessary; use if and br_if directly where possible Refactor branching implementation and move more into Cfg
1 parent ab7c452 commit 53a10ea

File tree

2 files changed

+80
-55
lines changed

2 files changed

+80
-55
lines changed

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

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,11 +1120,18 @@ type CfgBranch = {
11201120
from: MintOpcodePtr;
11211121
target: MintOpcodePtr;
11221122
isBackward: boolean; // FIXME: This should be inferred automatically
1123-
isConditional: boolean;
1123+
branchType: CfgBranchType;
11241124
}
11251125

11261126
type CfgSegment = CfgBlob | CfgBranchBlockHeader | CfgBranch;
11271127

1128+
export const enum CfgBranchType {
1129+
Unconditional,
1130+
Conditional,
1131+
SafepointUnconditional,
1132+
SafepointConditional,
1133+
}
1134+
11281135
class Cfg {
11291136
builder: WasmBuilder;
11301137
startOfBody!: MintOpcodePtr;
@@ -1204,15 +1211,15 @@ class Cfg {
12041211
this.overheadBytes += 1; // each branch block just costs us an end
12051212
}
12061213

1207-
branch(target: MintOpcodePtr, isBackward: boolean, isConditional: boolean) {
1214+
branch(target: MintOpcodePtr, isBackward: boolean, branchType: CfgBranchType) {
12081215
this.observedBranchTargets.add(target);
12091216
this.appendBlob();
12101217
this.segments.push({
12111218
type: "branch",
12121219
from: this.ip,
12131220
target,
12141221
isBackward,
1215-
isConditional,
1222+
branchType: branchType,
12161223
});
12171224
// some branches will generate bailouts instead so we allocate 4 bytes per branch
12181225
// to try and balance this out and avoid underestimating too much
@@ -1225,6 +1232,14 @@ class Cfg {
12251232
// set_local <disp>
12261233
this.overheadBytes += 11;
12271234
}
1235+
1236+
// Account for the size of the safepoint
1237+
if (
1238+
(branchType === CfgBranchType.SafepointConditional) ||
1239+
(branchType === CfgBranchType.SafepointUnconditional)
1240+
) {
1241+
this.overheadBytes += 17;
1242+
}
12281243
}
12291244

12301245
emitBlob(segment: CfgBlob, source: Uint8Array) {
@@ -1387,10 +1402,32 @@ class Cfg {
13871402
}
13881403

13891404
if ((indexInStack >= 0) || successfulBackBranch) {
1390-
// Conditional branches are nested in an extra block, so the depth is +1
1391-
const offset = segment.isConditional ? 1 : 0;
1392-
this.builder.appendU8(WasmOpcode.br);
1405+
let offset = 0;
1406+
switch (segment.branchType) {
1407+
case CfgBranchType.SafepointUnconditional:
1408+
append_safepoint(this.builder, segment.from);
1409+
this.builder.appendU8(WasmOpcode.br);
1410+
break;
1411+
case CfgBranchType.SafepointConditional:
1412+
// Wrap the safepoint + branch in an if
1413+
this.builder.block(WasmValtype.void, WasmOpcode.if_);
1414+
append_safepoint(this.builder, segment.from);
1415+
this.builder.appendU8(WasmOpcode.br);
1416+
offset = 1;
1417+
break;
1418+
case CfgBranchType.Unconditional:
1419+
this.builder.appendU8(WasmOpcode.br);
1420+
break;
1421+
case CfgBranchType.Conditional:
1422+
this.builder.appendU8(WasmOpcode.br_if);
1423+
break;
1424+
default:
1425+
throw new Error("Unimplemented branch type");
1426+
}
1427+
13931428
this.builder.appendULeb(offset + indexInStack);
1429+
if (offset) // close the if
1430+
this.builder.endBlock();
13941431
if (this.trace > 1)
13951432
mono_log_info(`br from ${(<any>segment.from).toString(16)} to ${(<any>segment.target).toString(16)} breaking out ${offset + indexInStack + 1} level(s)`);
13961433
} else {
@@ -1401,7 +1438,14 @@ class Cfg {
14011438
else if (this.trace > 1)
14021439
mono_log_info(`br from ${(<any>segment.from).toString(16)} to ${(<any>segment.target).toString(16)} failed (outside of trace 0x${base.toString(16)} - 0x${(<any>this.exitIp).toString(16)})`);
14031440
}
1441+
1442+
const isConditional = (segment.branchType === CfgBranchType.Conditional) ||
1443+
(segment.branchType === CfgBranchType.SafepointConditional);
1444+
if (isConditional)
1445+
this.builder.block(WasmValtype.void, WasmOpcode.if_);
14041446
append_bailout(this.builder, segment.target, BailoutReason.Branch);
1447+
if (isConditional)
1448+
this.builder.endBlock();
14051449
}
14061450
break;
14071451
}
@@ -1475,6 +1519,20 @@ export const _now = (globalThis.performance && globalThis.performance.now)
14751519

14761520
let scratchBuffer: NativePointer = <any>0;
14771521

1522+
export function append_safepoint(builder: WasmBuilder, ip: MintOpcodePtr) {
1523+
// Check whether a safepoint is required
1524+
builder.ptr_const(cwraps.mono_jiterp_get_polling_required_address());
1525+
builder.appendU8(WasmOpcode.i32_load);
1526+
builder.appendMemarg(0, 2);
1527+
// If the polling flag is set we call mono_jiterp_do_safepoint()
1528+
builder.block(WasmValtype.void, WasmOpcode.if_);
1529+
builder.local("frame");
1530+
// Not ip_const, because we can't pass relative IP to do_safepoint
1531+
builder.i32_const(ip);
1532+
builder.callImport("safepoint");
1533+
builder.endBlock();
1534+
}
1535+
14781536
export function append_bailout(builder: WasmBuilder, ip: MintOpcodePtr, reason: BailoutReason) {
14791537
builder.ip_const(ip);
14801538
if (builder.options.countBailouts) {

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

Lines changed: 16 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
append_memmove_dest_src, try_append_memset_fast,
2323
try_append_memmove_fast, counters, getOpcodeTableValue,
2424
getMemberOffset, JiterpMember, BailoutReason,
25-
isZeroPageReserved
25+
isZeroPageReserved, CfgBranchType, append_safepoint
2626
} from "./jiterpreter-support";
2727
import { compileSimdFeatureDetect } from "./jiterpreter-feature-detect";
2828
import {
@@ -1271,9 +1271,7 @@ export function generateWasmBody(
12711271
builder.local("index");
12721272
builder.ptr_const(ra);
12731273
builder.appendU8(WasmOpcode.i32_eq);
1274-
builder.block(WasmValtype.void, WasmOpcode.if_);
1275-
builder.cfg.branch(ra, ra < ip, true);
1276-
builder.endBlock();
1274+
builder.cfg.branch(ra, ra < ip, CfgBranchType.Conditional);
12771275
}
12781276
// If none of the comparisons succeeded we won't have branched anywhere, so bail out
12791277
// This shouldn't happen during non-exception-handling execution unless the trace doesn't
@@ -1889,11 +1887,10 @@ function append_ldloc_cknull(builder: WasmBuilder, localOffset: number, ip: Mint
18891887
return;
18901888
}
18911889

1892-
builder.block();
18931890
append_ldloc(builder, localOffset, WasmOpcode.i32_load);
18941891
builder.local("cknull_ptr", WasmOpcode.tee_local);
1895-
builder.appendU8(WasmOpcode.br_if);
1896-
builder.appendULeb(0);
1892+
builder.appendU8(WasmOpcode.i32_eqz);
1893+
builder.block(WasmValtype.void, WasmOpcode.if_);
18971894
append_bailout(builder, ip, BailoutReason.NullCheck);
18981895
builder.endBlock();
18991896
if (leaveOnStack)
@@ -2697,7 +2694,7 @@ function emit_branch(
26972694
mono_log_info(`performing backward branch to 0x${destination.toString(16)}`);
26982695
if (isCallHandler)
26992696
append_call_handler_store_ret_ip(builder, ip, frame, opcode);
2700-
builder.cfg.branch(destination, true, false);
2697+
builder.cfg.branch(destination, true, CfgBranchType.Unconditional);
27012698
counters.backBranchesEmitted++;
27022699
return true;
27032700
} else {
@@ -2722,7 +2719,7 @@ function emit_branch(
27222719
builder.branchTargets.add(destination);
27232720
if (isCallHandler)
27242721
append_call_handler_store_ret_ip(builder, ip, frame, opcode);
2725-
builder.cfg.branch(destination, false, false);
2722+
builder.cfg.branch(destination, false, CfgBranchType.Unconditional);
27262723
return true;
27272724
}
27282725
}
@@ -2735,20 +2732,19 @@ function emit_branch(
27352732
case MintOpcode.MINT_BRFALSE_I8_S: {
27362733
const is64 = (opcode === MintOpcode.MINT_BRTRUE_I8_S) ||
27372734
(opcode === MintOpcode.MINT_BRFALSE_I8_S);
2738-
// Wrap the conditional branch in a block so we can skip the
2739-
// actual branch at the end of it
2740-
builder.block();
2735+
2736+
// Load the condition
27412737

27422738
displacement = getArgI16(ip, 2);
27432739
append_ldloc(builder, getArgU16(ip, 1), is64 ? WasmOpcode.i64_load : WasmOpcode.i32_load);
27442740
if (
2745-
(opcode === MintOpcode.MINT_BRTRUE_I4_S) ||
2746-
(opcode === MintOpcode.MINT_BRTRUE_I4_SP)
2741+
(opcode === MintOpcode.MINT_BRFALSE_I4_S) ||
2742+
(opcode === MintOpcode.MINT_BRFALSE_I4_SP)
27472743
)
27482744
builder.appendU8(WasmOpcode.i32_eqz);
2749-
else if (opcode === MintOpcode.MINT_BRTRUE_I8_S)
2750-
builder.appendU8(WasmOpcode.i64_eqz);
27512745
else if (opcode === MintOpcode.MINT_BRFALSE_I8_S) {
2746+
builder.appendU8(WasmOpcode.i64_eqz);
2747+
} else if (opcode === MintOpcode.MINT_BRTRUE_I8_S) {
27522748
// do (i64 == 0) == 0 because br_if can only branch on an i32 operand
27532749
builder.appendU8(WasmOpcode.i64_eqz);
27542750
builder.appendU8(WasmOpcode.i32_eqz);
@@ -2766,7 +2762,6 @@ function emit_branch(
27662762
if (cwraps.mono_jiterp_get_opcode_info(opcode, OpcodeInfoType.Length) !== 4)
27672763
throw new Error(`Unsupported long branch opcode: ${getOpcodeName(opcode)}`);
27682764

2769-
builder.appendU8(WasmOpcode.i32_eqz);
27702765
break;
27712766
}
27722767
}
@@ -2778,21 +2773,13 @@ function emit_branch(
27782773

27792774
const destination = <any>ip + (displacement * 2);
27802775

2781-
// We generate a conditional branch that will skip past the rest of this
2782-
// tiny branch dispatch block to avoid performing the branch
2783-
builder.appendU8(WasmOpcode.br_if);
2784-
builder.appendULeb(0);
2785-
27862776
if (displacement < 0) {
2787-
if (isSafepoint)
2788-
append_safepoint(builder, ip);
2789-
27902777
if (builder.backBranchOffsets.indexOf(destination) >= 0) {
27912778
// We found a backwards branch target we can reach via our outer trace loop, so
27922779
// we update eip and branch out to the top of the loop block
27932780
if (traceBackBranches > 1)
27942781
mono_log_info(`performing conditional backward branch to 0x${destination.toString(16)}`);
2795-
builder.cfg.branch(destination, true, true);
2782+
builder.cfg.branch(destination, true, isSafepoint ? CfgBranchType.SafepointConditional : CfgBranchType.Conditional);
27962783
counters.backBranchesEmitted++;
27972784
} else {
27982785
if (destination < builder.cfg.entryIp) {
@@ -2804,19 +2791,17 @@ function emit_branch(
28042791
);
28052792
// We didn't find a loop to branch to, so bail out
28062793
cwraps.mono_jiterp_boost_back_branch_target(destination);
2794+
builder.block(WasmValtype.void, WasmOpcode.if_);
28072795
append_bailout(builder, destination, BailoutReason.BackwardBranch);
2796+
builder.endBlock();
28082797
counters.backBranchesNotEmitted++;
28092798
}
28102799
} else {
2811-
// Do a safepoint *before* changing our IP, if necessary
2812-
if (isSafepoint)
2813-
append_safepoint(builder, ip);
28142800
// Branching is enabled, so set eip and exit the current branch block
28152801
builder.branchTargets.add(destination);
2816-
builder.cfg.branch(destination, false, true);
2802+
builder.cfg.branch(destination, false, isSafepoint ? CfgBranchType.SafepointConditional : CfgBranchType.Conditional);
28172803
}
28182804

2819-
builder.endBlock();
28202805
return true;
28212806
}
28222807

@@ -2838,10 +2823,6 @@ function emit_relop_branch(
28382823
if (!relopInfo && !intrinsicFpBinop)
28392824
return false;
28402825

2841-
// We have to wrap the computation of the branch condition inside the
2842-
// branch block because opening blocks destroys the contents of the
2843-
// wasm execution stack for some reason
2844-
builder.block();
28452826
const displacement = getArgI16(ip, 3);
28462827
if (traceBranchDisplacements)
28472828
mono_log_info(`relop @${ip} displacement=${displacement}`);
@@ -3812,17 +3793,3 @@ function emit_simd_4(builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrins
38123793
return false;
38133794
}
38143795
}
3815-
3816-
function append_safepoint(builder: WasmBuilder, ip: MintOpcodePtr) {
3817-
// Check whether a safepoint is required
3818-
builder.ptr_const(cwraps.mono_jiterp_get_polling_required_address());
3819-
builder.appendU8(WasmOpcode.i32_load);
3820-
builder.appendMemarg(0, 2);
3821-
// If the polling flag is set we call mono_jiterp_do_safepoint()
3822-
builder.block(WasmValtype.void, WasmOpcode.if_);
3823-
builder.local("frame");
3824-
// Not ip_const, because we can't pass relative IP to do_safepoint
3825-
builder.i32_const(ip);
3826-
builder.callImport("safepoint");
3827-
builder.endBlock();
3828-
}

0 commit comments

Comments
 (0)