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
14 changes: 7 additions & 7 deletions majit/majit-backend-wasm/js/jit_glue.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ export function jit_set_table(table) {
// Call trampoline — invoked by generated wasm when it encounters a CALL op.
// Reads func_ptr + args from the frame's call area, performs the call via
// the main module's indirect function table, writes result back.
function jitCallTrampoline(framePtr) {
function jitCallTrampoline(framePtr, callAreaOfs = CALL_RESULT_OFS) {
const view = new DataView(mainMemory.buffer);
const funcPtrLo = view.getUint32(framePtr + CALL_FUNC_OFS, true);
const numArgs = Number(view.getBigInt64(framePtr + CALL_NARGS_OFS, true));
const funcPtrLo = view.getUint32(framePtr + callAreaOfs + 8, true);
const numArgs = Number(view.getBigInt64(framePtr + callAreaOfs + 16, true));

// Read args from call area
const args = [];
for (let i = 0; i < numArgs; i++) {
// On wasm32, values are i32 (pointers/ints). Read low 32 bits of each i64 slot.
args.push(view.getInt32(framePtr + CALL_ARGS_OFS + i * 8, true));
args.push(view.getInt32(framePtr + callAreaOfs + 24 + i * 8, true));
}

// Call via the main module's function table
Expand All @@ -50,8 +50,8 @@ function jitCallTrampoline(framePtr) {
}

// Write result to call area (as i64: low 32 bits = result, high 32 bits = 0)
view.setInt32(framePtr + CALL_RESULT_OFS, result, true);
view.setInt32(framePtr + CALL_RESULT_OFS + 4, 0, true);
view.setInt32(framePtr + callAreaOfs, result, true);
view.setInt32(framePtr + callAreaOfs + 4, 0, true);
}

export function jit_compile_wasm(bytesPtr, bytesLen) {
Expand All @@ -69,7 +69,7 @@ export function jit_compile_wasm(bytesPtr, bytesLen) {
// chaining; the module imports it only when it has CALL ops. Extra
// entries in the import object are ignored when not declared.
const instance = new WebAssembly.Instance(module, {
env: { memory: mainMemory, jit_call: jitCallTrampoline, __indirect_function_table: mainTable }
env: { memory: mainMemory, jit_call: jitCallTrampoline, jit_call_compact: jitCallTrampoline, __indirect_function_table: mainTable }
});
return registerTrace(instance.exports.trace);
} catch (e) {
Expand Down
480 changes: 386 additions & 94 deletions majit/majit-backend-wasm/src/codegen.rs

Large diffs are not rendered by default.

34 changes: 7 additions & 27 deletions majit/majit-backend-wasm/src/failguard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,26 +85,12 @@ pub struct LabelTarget {
/// livelock advance-check applies; earlier labels execute the peeled
/// segment, which advances the state by itself.
pub is_last_label: bool,
/// Ref-home slot count of the owning loop, for the chain-soundness check:
/// a chained trace runs in the frame `execute_token` sized for the loop
/// the chain ENTERED through, so a tail-call may only target a loop whose
/// Ref-home region fits `max(source loop's homes, FRAME_REF_HOME_FLOOR)`
/// — inductively, every loop in a chain then fits the entry frame (which
/// is sized to at least the floor). Ref homes are the ONLY variable frame
/// requirement: value slots are bounded by `codegen`'s
/// `CALL_AREA_FIRST_SLOT` decline, below the constant
/// `MIN_FRAME_BYTES / 8` value region every host frame carries.
pub num_ref_homes: usize,
/// Frozen frame geometry of the target token. A tail-call can only reuse
/// a frame when its offsets agree exactly, not merely when its allocation
/// is large enough.
pub frame: crate::codegen::FrameGeometry,
}

/// Minimum Ref-home slot count `execute_token` sizes every host frame for
/// while bridge chaining is enabled (see `LabelTarget::num_ref_homes`).
/// Chains between traces whose home counts stay at or under this floor need
/// no source-vs-target comparison at all, which is what lifts most frame-fit
/// bridge declines. Costs `8` bytes + one GC-root registration per slot per
/// `execute_token` call.
pub const FRAME_REF_HOME_FLOOR: usize = 64;

/// Global `frame[0]` fail-index space.
///
/// Cross-trace chaining (`LABEL_TARGETS`) means the module that last wrote
Expand Down Expand Up @@ -230,6 +216,9 @@ pub struct CompiledWasmLoop {
/// region (`codegen::HOME_SLOT_BASE`). `execute_token` sizes the host
/// frame to include this region and registers each home slot as a GC root.
pub num_ref_homes: usize,
/// Geometry frozen when this token was first compiled. Every bridge
/// chained onto it is emitted against this exact layout.
pub frame: crate::codegen::FrameGeometry,
/// Base address (shared linear memory) of this loop's per-guard bridge-slot
/// cell array — one i32 per `fail_index`, `0` = no bridge. The trace's
/// epilogue reads `cells[fail_index]` and `compile_bridge` writes a bridge's
Expand Down Expand Up @@ -288,15 +277,6 @@ pub struct CompiledWasmLoop {
/// module lives as long as the source loop it attaches to, so its cells are
/// freed when this loop drops. Appended by `compile_bridge`.
pub _bridge_owned_cells: RefCell<Vec<Box<[u32]>>>,
/// Max `num_ref_homes` over the self-recursive `CallAssemblerR` bridges
/// (`PYRE_WASM_CA`) chained onto this loop, or 0 when there are none. Such a
/// bridge runs in the host entry frame `F0` for the outermost call, so
/// `execute_token` must size `F0` (and register its GC roots) for the LARGER
/// of the loop's own homes and this — the bridge's home writes would
/// otherwise overflow a loop-sized `F0`. Set by `compile_bridge` when it
/// accepts a CA bridge; `Cell` because the source token is shared (`&`) and
/// the wasm host is single-threaded.
pub ca_bridge_ref_homes: Cell<usize>,
/// Set when `compile_bridge` accepts a self-recursive `CallAssemblerR`
/// bridge (`PYRE_WASM_CA`) for this loop. While set, `compile_bridge`
/// declines chaining any FURTHER bridge into this recursion (the guard
Expand Down
Loading
Loading