Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 64 additions & 6 deletions src/mono/wasm/runtime/jiterpreter-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1108,11 +1108,18 @@ type CfgBranch = {
from: MintOpcodePtr;
target: MintOpcodePtr;
isBackward: boolean; // FIXME: This should be inferred automatically
isConditional: boolean;
branchType: CfgBranchType;
}

type CfgSegment = CfgBlob | CfgBranchBlockHeader | CfgBranch;

export const enum CfgBranchType {
Unconditional,
Conditional,
SafepointUnconditional,
SafepointConditional,
}

class Cfg {
builder: WasmBuilder;
startOfBody!: MintOpcodePtr;
Expand Down Expand Up @@ -1192,15 +1199,15 @@ class Cfg {
this.overheadBytes += 1; // each branch block just costs us an end
}

branch(target: MintOpcodePtr, isBackward: boolean, isConditional: boolean) {
branch(target: MintOpcodePtr, isBackward: boolean, branchType: CfgBranchType) {
this.observedBranchTargets.add(target);
this.appendBlob();
this.segments.push({
type: "branch",
from: this.ip,
target,
isBackward,
isConditional,
branchType: branchType,
});
// some branches will generate bailouts instead so we allocate 4 bytes per branch
// to try and balance this out and avoid underestimating too much
Expand All @@ -1213,6 +1220,14 @@ class Cfg {
// set_local <disp>
this.overheadBytes += 11;
}

// Account for the size of the safepoint
if (
(branchType === CfgBranchType.SafepointConditional) ||
(branchType === CfgBranchType.SafepointUnconditional)
) {
this.overheadBytes += 17;
}
}

emitBlob(segment: CfgBlob, source: Uint8Array) {
Expand Down Expand Up @@ -1375,10 +1390,32 @@ class Cfg {
}

if ((indexInStack >= 0) || successfulBackBranch) {
// Conditional branches are nested in an extra block, so the depth is +1
const offset = segment.isConditional ? 1 : 0;
this.builder.appendU8(WasmOpcode.br);
let offset = 0;
switch (segment.branchType) {
case CfgBranchType.SafepointUnconditional:
append_safepoint(this.builder, segment.from);
this.builder.appendU8(WasmOpcode.br);
break;
case CfgBranchType.SafepointConditional:
// Wrap the safepoint + branch in an if
this.builder.block(WasmValtype.void, WasmOpcode.if_);
append_safepoint(this.builder, segment.from);
this.builder.appendU8(WasmOpcode.br);
offset = 1;
break;
case CfgBranchType.Unconditional:
this.builder.appendU8(WasmOpcode.br);
break;
case CfgBranchType.Conditional:
this.builder.appendU8(WasmOpcode.br_if);
break;
default:
throw new Error("Unimplemented branch type");
}

this.builder.appendULeb(offset + indexInStack);
if (offset) // close the if
this.builder.endBlock();
if (this.trace > 1)
mono_log_info(`br from ${(<any>segment.from).toString(16)} to ${(<any>segment.target).toString(16)} breaking out ${offset + indexInStack + 1} level(s)`);
} else {
Expand All @@ -1389,7 +1426,14 @@ class Cfg {
else if (this.trace > 1)
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)})`);
}

const isConditional = (segment.branchType === CfgBranchType.Conditional) ||
(segment.branchType === CfgBranchType.SafepointConditional);
if (isConditional)
this.builder.block(WasmValtype.void, WasmOpcode.if_);
append_bailout(this.builder, segment.target, BailoutReason.Branch);
if (isConditional)
this.builder.endBlock();
}
break;
}
Expand Down Expand Up @@ -1463,6 +1507,20 @@ export const _now = (globalThis.performance && globalThis.performance.now)

let scratchBuffer: NativePointer = <any>0;

export function append_safepoint(builder: WasmBuilder, ip: MintOpcodePtr) {
// Check whether a safepoint is required
builder.ptr_const(cwraps.mono_jiterp_get_polling_required_address());
builder.appendU8(WasmOpcode.i32_load);
builder.appendMemarg(0, 2);
// If the polling flag is set we call mono_jiterp_do_safepoint()
builder.block(WasmValtype.void, WasmOpcode.if_);
builder.local("frame");
// Not ip_const, because we can't pass relative IP to do_safepoint
builder.i32_const(ip);
builder.callImport("safepoint");
builder.endBlock();
}

export function append_bailout(builder: WasmBuilder, ip: MintOpcodePtr, reason: BailoutReason) {
builder.ip_const(ip);
if (builder.options.countBailouts) {
Expand Down
65 changes: 16 additions & 49 deletions src/mono/wasm/runtime/jiterpreter-trace-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
append_memmove_dest_src, try_append_memset_fast,
try_append_memmove_fast, counters, getOpcodeTableValue,
getMemberOffset, JiterpMember, BailoutReason,
isZeroPageReserved
isZeroPageReserved, CfgBranchType, append_safepoint
} from "./jiterpreter-support";
import { compileSimdFeatureDetect } from "./jiterpreter-feature-detect";
import {
Expand Down Expand Up @@ -1269,9 +1269,7 @@ export function generateWasmBody(
builder.local("index");
builder.ptr_const(ra);
builder.appendU8(WasmOpcode.i32_eq);
builder.block(WasmValtype.void, WasmOpcode.if_);
builder.cfg.branch(ra, ra < ip, true);
builder.endBlock();
builder.cfg.branch(ra, ra < ip, CfgBranchType.Conditional);
}
// If none of the comparisons succeeded we won't have branched anywhere, so bail out
// This shouldn't happen during non-exception-handling execution unless the trace doesn't
Expand Down Expand Up @@ -1887,11 +1885,10 @@ function append_ldloc_cknull(builder: WasmBuilder, localOffset: number, ip: Mint
return;
}

builder.block();
append_ldloc(builder, localOffset, WasmOpcode.i32_load);
builder.local("cknull_ptr", WasmOpcode.tee_local);
builder.appendU8(WasmOpcode.br_if);
builder.appendULeb(0);
builder.appendU8(WasmOpcode.i32_eqz);
builder.block(WasmValtype.void, WasmOpcode.if_);
append_bailout(builder, ip, BailoutReason.NullCheck);
builder.endBlock();
if (leaveOnStack)
Expand Down Expand Up @@ -2695,7 +2692,7 @@ function emit_branch(
mono_log_info(`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, false);
builder.cfg.branch(destination, true, CfgBranchType.Unconditional);
counters.backBranchesEmitted++;
return true;
} else {
Expand All @@ -2720,7 +2717,7 @@ function emit_branch(
builder.branchTargets.add(destination);
if (isCallHandler)
append_call_handler_store_ret_ip(builder, ip, frame, opcode);
builder.cfg.branch(destination, false, false);
builder.cfg.branch(destination, false, CfgBranchType.Unconditional);
return true;
}
}
Expand All @@ -2733,20 +2730,19 @@ function emit_branch(
case MintOpcode.MINT_BRFALSE_I8_S: {
const is64 = (opcode === MintOpcode.MINT_BRTRUE_I8_S) ||
(opcode === MintOpcode.MINT_BRFALSE_I8_S);
// Wrap the conditional branch in a block so we can skip the
// actual branch at the end of it
builder.block();

// Load the condition

displacement = getArgI16(ip, 2);
append_ldloc(builder, getArgU16(ip, 1), is64 ? WasmOpcode.i64_load : WasmOpcode.i32_load);
if (
(opcode === MintOpcode.MINT_BRTRUE_I4_S) ||
(opcode === MintOpcode.MINT_BRTRUE_I4_SP)
(opcode === MintOpcode.MINT_BRFALSE_I4_S) ||
(opcode === MintOpcode.MINT_BRFALSE_I4_SP)
)
builder.appendU8(WasmOpcode.i32_eqz);
else if (opcode === MintOpcode.MINT_BRTRUE_I8_S)
builder.appendU8(WasmOpcode.i64_eqz);
else if (opcode === MintOpcode.MINT_BRFALSE_I8_S) {
builder.appendU8(WasmOpcode.i64_eqz);
} else if (opcode === MintOpcode.MINT_BRTRUE_I8_S) {
// do (i64 == 0) == 0 because br_if can only branch on an i32 operand
builder.appendU8(WasmOpcode.i64_eqz);
builder.appendU8(WasmOpcode.i32_eqz);
Expand All @@ -2764,7 +2760,6 @@ function emit_branch(
if (cwraps.mono_jiterp_get_opcode_info(opcode, OpcodeInfoType.Length) !== 4)
throw new Error(`Unsupported long branch opcode: ${getOpcodeName(opcode)}`);

builder.appendU8(WasmOpcode.i32_eqz);
break;
}
}
Expand All @@ -2776,21 +2771,13 @@ function emit_branch(

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

// We generate a conditional branch that will skip past the rest of this
// tiny branch dispatch block to avoid performing the branch
builder.appendU8(WasmOpcode.br_if);
builder.appendULeb(0);

if (displacement < 0) {
if (isSafepoint)
append_safepoint(builder, ip);

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)}`);
builder.cfg.branch(destination, true, true);
builder.cfg.branch(destination, true, isSafepoint ? CfgBranchType.SafepointConditional : CfgBranchType.Conditional);
counters.backBranchesEmitted++;
} else {
if (destination < builder.cfg.entryIp) {
Expand All @@ -2802,19 +2789,17 @@ function emit_branch(
);
// We didn't find a loop to branch to, so bail out
cwraps.mono_jiterp_boost_back_branch_target(destination);
builder.block(WasmValtype.void, WasmOpcode.if_);
append_bailout(builder, destination, BailoutReason.BackwardBranch);
builder.endBlock();
counters.backBranchesNotEmitted++;
}
} else {
// Do a safepoint *before* changing our IP, if necessary
if (isSafepoint)
append_safepoint(builder, ip);
// Branching is enabled, so set eip and exit the current branch block
builder.branchTargets.add(destination);
builder.cfg.branch(destination, false, true);
builder.cfg.branch(destination, false, isSafepoint ? CfgBranchType.SafepointConditional : CfgBranchType.Conditional);
}

builder.endBlock();
return true;
}

Expand All @@ -2836,10 +2821,6 @@ function emit_relop_branch(
if (!relopInfo && !intrinsicFpBinop)
return false;

// We have to wrap the computation of the branch condition inside the
// branch block because opening blocks destroys the contents of the
// wasm execution stack for some reason
builder.block();
const displacement = getArgI16(ip, 3);
if (traceBranchDisplacements)
mono_log_info(`relop @${ip} displacement=${displacement}`);
Expand Down Expand Up @@ -3704,17 +3685,3 @@ function emit_simd_4(builder: WasmBuilder, ip: MintOpcodePtr, index: SimdIntrins
return false;
}
}

function append_safepoint(builder: WasmBuilder, ip: MintOpcodePtr) {
// Check whether a safepoint is required
builder.ptr_const(cwraps.mono_jiterp_get_polling_required_address());
builder.appendU8(WasmOpcode.i32_load);
builder.appendMemarg(0, 2);
// If the polling flag is set we call mono_jiterp_do_safepoint()
builder.block(WasmValtype.void, WasmOpcode.if_);
builder.local("frame");
// Not ip_const, because we can't pass relative IP to do_safepoint
builder.i32_const(ip);
builder.callImport("safepoint");
builder.endBlock();
}