From 91e11e8b803e3f4b548a3d0e9817b4b271147c83 Mon Sep 17 00:00:00 2001 From: davidmaamoaix Date: Fri, 10 Apr 2026 17:24:02 -0400 Subject: [PATCH 1/4] Added `X86_64FrameHandler` for frame-slot accesses --- src/engine/x86-64/X86_64Stack.v3 | 201 +++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/src/engine/x86-64/X86_64Stack.v3 b/src/engine/x86-64/X86_64Stack.v3 index 1208c156e..272e021f9 100644 --- a/src/engine/x86-64/X86_64Stack.v3 +++ b/src/engine/x86-64/X86_64Stack.v3 @@ -938,6 +938,207 @@ layout X86_64InterpreterFrame { =104; } +// Lightweight, discriminated handler for interpreter and SPC frames. +// Unlike {X86_64FrameAccessor}, does not require heap-caching on the frame's accessor slot, +// and does not store a FuncDecl/stack/etc. Use this for operations that just need +// to read/write frame fields without full probe instrumentation support. +type X86_64FrameHandler { + case Interpreter(sfp_: Pointer) { + def sfp() -> Pointer { return sfp_; } + } + case Spc(sfp_: Pointer) { + def sfp() -> Pointer { return sfp_; } + } + def sfp() -> Pointer; + def srp() -> Pointer { return sfp() + -RETADDR_SIZE; } + + // Shared field getters (both frame types). + def wasm_func() -> WasmFunction { + return (sfp() + X86_64InterpreterFrame.wasm_func.offset).load(); + } + def mem0_base() -> Pointer { + return (sfp() + X86_64InterpreterFrame.mem0_base.offset).load(); + } + def vfp() -> Pointer { + return (sfp() + X86_64InterpreterFrame.vfp.offset).load(); + } + def vsp() -> Pointer { + return (sfp() + X86_64InterpreterFrame.vsp.offset).load(); + } + def instance() -> Instance { + return (sfp() + X86_64InterpreterFrame.instance.offset).load(); + } + def curpc() -> int { + return (sfp() + X86_64InterpreterFrame.curpc.offset).load(); + } + def accessor() -> X86_64FrameAccessor { + return (sfp() + X86_64InterpreterFrame.accessor.offset).load(); + } + + // Shared field setters. + def set_wasm_func(val: WasmFunction) { + (sfp() + X86_64InterpreterFrame.wasm_func.offset).store(val); + } + def set_vsp(val: Pointer) { + (sfp() + X86_64InterpreterFrame.vsp.offset).store(val); + } + def set_curpc(val: int) { + (sfp() + X86_64InterpreterFrame.curpc.offset).store(val); + } + def set_accessor(val: X86_64FrameAccessor) { + (sfp() + X86_64InterpreterFrame.accessor.offset).store(val); + } + + // Guards. + def isSpc() -> bool { return X86_64FrameHandler.Spc.?(this); } + def isInterpreter() -> bool { return X86_64FrameHandler.Interpreter.?(this); } + + def checkInterpreter() { if (!isInterpreter()) X86_64FrameHandlers.fatal("interpreter"); } + def checkSpc() { if (!isSpc()) X86_64FrameHandlers.fatal("spc"); } + + // Interpreter-only field getters (trap on Spc). + def sidetable() -> Array { + checkInterpreter(); + return (sfp() + X86_64InterpreterFrame.sidetable.offset).load>(); + } + def stp() -> Pointer { + checkInterpreter(); + return (sfp() + X86_64InterpreterFrame.stp.offset).load(); + } + def code() -> Array { + checkInterpreter(); + return (sfp() + X86_64InterpreterFrame.code.offset).load>(); + } + // XXX: rename into a non-ambiguous name with the return address returned by `X86_64FrameAccessor.readIp()`. + def ip() -> Pointer { + checkInterpreter(); + return (sfp() + X86_64InterpreterFrame.ip.offset).load(); + } + def eip() -> Pointer { + checkInterpreter(); + return (sfp() + X86_64InterpreterFrame.eip.offset).load(); + } + def func_decl() -> FuncDecl { + checkInterpreter(); + return (sfp() + X86_64InterpreterFrame.func_decl.offset).load(); + } + + // Interpreter-only field setters (trap on Spc). + def set_func_decl(val: FuncDecl) { + checkInterpreter(); + (sfp() + X86_64InterpreterFrame.func_decl.offset).store(val); + } + def set_code(val: Array) { + checkInterpreter(); + (sfp() + X86_64InterpreterFrame.code.offset).store>(val); + } + def set_ip(val: Pointer) { + checkInterpreter(); + (sfp() + X86_64InterpreterFrame.ip.offset).store(val); + } + def set_eip(val: Pointer) { + checkInterpreter(); + (sfp() + X86_64InterpreterFrame.eip.offset).store(val); + } + def set_stp(val: Pointer) { + checkInterpreter(); + (sfp() + X86_64InterpreterFrame.stp.offset).store(val); + } + def set_sidetable(val: Array) { + checkInterpreter(); + (sfp() + X86_64InterpreterFrame.sidetable.offset).store>(val); + } + + // SPC-only field getters (trap on Interpreter). + def inlined_instance() -> Instance { + checkSpc(); + return (sfp() + X86_64InterpreterFrame.stp.offset).load(); // +40, reuses stp slot + } + def inlined_mem0_base() -> Pointer { + checkSpc(); + return (sfp() + X86_64InterpreterFrame.ip.offset).load(); // +56, reuses ip slot + } + + // SPC-only field setters (trap on Interpreter). + def set_inlined_instance(val: Instance) { + checkSpc(); + (sfp() + X86_64InterpreterFrame.stp.offset).store(val); + } + def set_inlined_mem0_base(val: Pointer) { + checkSpc(); + (sfp() + X86_64InterpreterFrame.ip.offset).store(val); + } + + // Compound operations. + + // Compute the program counter for this frame. + def computePc() -> int { + var retip = srp().load(); + return computePcFromCode(RiRuntime.findUserCode(retip)); + } + // Compute the program counter using an already-resolved code object. + def computePcFromCode(code: RiUserCode) -> int { + var retip = srp().load(); + match (code) { + x: X86_64SpcModuleCode => return x.lookupTopPc(retip, true); + x: X86_64SpcInlinedFrame => return curpc(); + x: X86_64InterpreterCode => return X86_64Interpreter.computePCFromFrame(sfp()); + x: X86_64SpcTrapsStub => return curpc(); + _ => return -1; + } + } + + // Write all interpreter execution state fields for resuming at (func, new_pc, stp_val). + // Called on both frame types. For SPC frames, this overwrites SPC-only slots + // with interpreter state in preparation for an deopt reentry. + def writeInterpreterState(func: FuncDecl, new_pc: int, stp_val: int) { + var p = sfp(); + var bytecode = func.cur_bytecode; + (p + X86_64InterpreterFrame.func_decl.offset).store(func); + (p + X86_64InterpreterFrame.curpc.offset).store(new_pc); + (p + X86_64InterpreterFrame.code.offset).store>(bytecode); + (p + X86_64InterpreterFrame.ip.offset).store(Pointer.atElement(bytecode, new_pc)); + (p + X86_64InterpreterFrame.eip.offset).store(Pointer.atContents(bytecode) + bytecode.length); + var st_entries = func.sidetable.entries; + var st_ptr = if(stp_val == st_entries.length, Pointer.atContents(st_entries), Pointer.atElement(func.sidetable.entries, stp_val)); + (p + X86_64InterpreterFrame.stp.offset).store(st_ptr); + } + + // Redirect return address {*(%srp)} to an SPC exception/suspension handler stub. + def redirectToHandlerStub(func: FuncDecl, handler: ExHandler) { + var dest_id = func.handlers.handler_dest_map[handler.index]; + var dest = func.handlers.handler_dests[dest_id]; + var offset = X86_64MasmLabel.!(dest.stub_label).label.pos; + srp().store(func.target_code.spc_entry + offset); + } + + // Redirect return address to the interpreter's deopt reentry point. + def redirectToInterpreter() { + var ic = X86_64PreGenStubs.getInterpreterCode(); + srp().store(ic.start + ic.header.deoptReentryOffset); + } +} + +// Factory for constructing {X86_64FrameHandler} from a stack frame pointer. +component X86_64FrameHandlers { + // Construct from an sfp by inspecting the return address. + def from(sfp: Pointer) -> X86_64FrameHandler { + var retip = (sfp + -RETADDR_SIZE).load(); + var code = RiRuntime.findUserCode(retip); + return fromCode(sfp, code); + } + // Construct from an sfp and a code object. + def fromCode(sfp: Pointer, code: RiUserCode) -> X86_64FrameHandler { + if (X86_64InterpreterCode.?(code)) return X86_64FrameHandler.Interpreter(sfp); + if (X86_64SpcCode.?(code)) return X86_64FrameHandler.Spc(sfp); + System.error("X86_64FrameHandlerError", "unknown frame type"); + return X86_64FrameHandler.Interpreter(sfp); // unreachable + } + def fatal(ftype: string) { + System.error("X86_64FrameHandlerError", Strings.format1("field not available on frame type %s", ftype)); + } +} + // Native frame states used in the implementation of {FrameStateAccessor}. Since a frame // can be optimized or deoptimized in place, the frame state accessor has to check the // state for every call. From f3113ec2f1d4def3ea58c5c58b351c9a51f1ae10 Mon Sep 17 00:00:00 2001 From: davidmaamoaix Date: Sat, 11 Apr 2026 14:40:59 -0400 Subject: [PATCH 2/4] Refactored field accesses to use `X86_64FrameHandler` --- src/engine/x86-64/V3Offsets.v3 | 2 +- src/engine/x86-64/X86_64Interpreter.v3 | 5 +- src/engine/x86-64/X86_64SinglePassCompiler.v3 | 35 +-- src/engine/x86-64/X86_64Stack.v3 | 210 ++++++++---------- 4 files changed, 117 insertions(+), 135 deletions(-) diff --git a/src/engine/x86-64/V3Offsets.v3 b/src/engine/x86-64/V3Offsets.v3 index a7c7860f8..7881429bc 100644 --- a/src/engine/x86-64/V3Offsets.v3 +++ b/src/engine/x86-64/V3Offsets.v3 @@ -15,7 +15,7 @@ class V3Offsets { private def wf = WasmFunction.new(i, decl); private def mem = NativeWasmMemory.new(null); private def vs = X86_64Stack.new(2u * 4096u); - private def acc = X86_64FrameAccessor.new(vs, Pointer.NULL, decl); + private def acc = X86_64FrameAccessor.new(vs, X86_64FrameHandler.Interpreter(Pointer.NULL), decl); private def ha = HeapArray.new(null, []); private def cnt = CountProbe.new(); private def metric = Metric.new("", "", ""); diff --git a/src/engine/x86-64/X86_64Interpreter.v3 b/src/engine/x86-64/X86_64Interpreter.v3 index 29307ca98..ad92cf71c 100644 --- a/src/engine/x86-64/X86_64Interpreter.v3 +++ b/src/engine/x86-64/X86_64Interpreter.v3 @@ -65,8 +65,9 @@ class X86_64InterpreterCode extends RiUserCode { if (Debug.stack) X86_64Stacks.traceIpAndSp(ip, sp, out); var msg = "\tin [fast-int] "; out(msg); - var instance = (sp + X86_64InterpreterFrame.instance.offset).load(); - var func = (sp + X86_64InterpreterFrame.func_decl.offset).load(); + var h = X86_64FrameHandler.Interpreter(sp); + var instance = h.instance(); + var func = h.func_decl(); // TODO: lazy parse of names section may allocate; must avoid this in OOM situation var buf = X86_64Runtime.globalFrameDescriptionBuf; if (func == null) buf.puts("(func=null)"); diff --git a/src/engine/x86-64/X86_64SinglePassCompiler.v3 b/src/engine/x86-64/X86_64SinglePassCompiler.v3 index 0ab68d3bc..eb6f9292a 100644 --- a/src/engine/x86-64/X86_64SinglePassCompiler.v3 +++ b/src/engine/x86-64/X86_64SinglePassCompiler.v3 @@ -1159,8 +1159,9 @@ class X86_64SpcCode extends RiUserCode { out(msg); msg = "] "; out(msg); - var instance = (sp + X86_64InterpreterFrame.instance.offset).load(); - var wf = (sp + X86_64InterpreterFrame.wasm_func.offset).load(); + var h = X86_64FrameHandler.Spc(sp); + var instance = h.instance(); + var wf = h.wasm_func(); // TODO: lazy parse of names section may allocate; must avoid this in OOM situation var buf = X86_64Runtime.globalFrameDescriptionBuf; wf.decl.render(instance.module.names, buf); @@ -1176,11 +1177,11 @@ class X86_64SpcCode extends RiUserCode { // V3-runtime callback: when the garbage collector needs to scan a JIT stack frame. def scanFrame(ip: Pointer, sp: Pointer) { // Handle other roots in the frame - RiGc.rescanRoot(sp + X86_64InterpreterFrame.wasm_func.offset); - RiGc.rescanRoot(sp + X86_64InterpreterFrame.instance.offset); - RiGc.rescanRoot(sp + X86_64InterpreterFrame.accessor.offset); + RiGc.rescanRoot(sp + X86_64SpcFrame.wasm_func.offset); + RiGc.rescanRoot(sp + X86_64SpcFrame.instance.offset); + RiGc.rescanRoot(sp + X86_64SpcFrame.accessor.offset); if (SpcTuning.inlineWhammProbes && SpcTuning.intrinsifyWhammProbe) { - RiGc.rescanRoot(sp + X86_64InterpreterFrame.stp.offset); // TODO: define X86_64SpcFrame and use dedicated slot + RiGc.rescanRoot(sp + X86_64SpcFrame.inlined_instance.offset); } } } @@ -1241,10 +1242,11 @@ class X86_64SpcModuleCode extends X86_64SpcCode { // Update the current PC in the JIT frame, if it is accessible. var ip = p_rip.load(); var inline_ctx = lookupPc(ip, false); + var h = X86_64FrameHandler.Spc(p_rsp); if (inline_ctx == null) { - (p_rsp + X86_64InterpreterFrame.curpc.offset).store(-1); + h.set_curpc(-1); } else if (inline_ctx.tail == null) { - (p_rsp + X86_64InterpreterFrame.curpc.offset).store(inline_ctx.head.pc); + h.set_curpc(inline_ctx.head.pc); } else { p_rsp = reconstructInlinedFramesForTrap(p_rsp, inline_ctx); (ucontext + ucontext_rsp_offset).store(p_rsp); @@ -1262,11 +1264,10 @@ class X86_64SpcModuleCode extends X86_64SpcCode { def inlined = frames[0 ... (frames.length - 1)]; def count = inlined.length; - // set outermost pc in the real frame - (r_rsp + X86_64InterpreterFrame.curpc.offset).store(outer.pc); - - // Read instance from the real outer frame (shared across all inlined frames) - var instance = (r_rsp + X86_64InterpreterFrame.instance.offset).load(); + // set outermost pc in the real frame; read instance (shared across all inlined frames) + var outer_h = X86_64FrameHandler.Spc(r_rsp); + outer_h.set_curpc(outer.pc); + var instance = outer_h.instance(); // Push inlined frames for (i = count - 1; i >= 0; i--) { @@ -1277,11 +1278,13 @@ class X86_64SpcModuleCode extends X86_64SpcCode { r_rsp.store(INLINED_FRAME_STUB.start); r_rsp += -X86_64InterpreterFrame.size; // move rsp? + var h = X86_64FrameHandler.Spc(r_rsp); + // write func, pc, frame accessor var wasm_func = WasmFunction.!(instance.functions[fid]); - (r_rsp + X86_64InterpreterFrame.wasm_func.offset).store(wasm_func); - (r_rsp + X86_64InterpreterFrame.curpc.offset).store(pc); - (r_rsp + X86_64InterpreterFrame.accessor.offset).store(null); + h.set_wasm_func(wasm_func); + h.set_curpc(pc); + h.set_accessor(null); } return r_rsp; } diff --git a/src/engine/x86-64/X86_64Stack.v3 b/src/engine/x86-64/X86_64Stack.v3 index 272e021f9..883b60b03 100644 --- a/src/engine/x86-64/X86_64Stack.v3 +++ b/src/engine/x86-64/X86_64Stack.v3 @@ -188,10 +188,9 @@ class X86_64Stack extends WasmStack { Trace.OUT.put1("searching for handler on stack[rsp=0x%x]", rsp - Pointer.NULL).ln(); } // Increment by {Pointer.SIZE} to skip the return address pushed by {resume}. - var frame = TargetFrame(rsp + Pointer.SIZE); - var accessor = frame.getFrameAccessor(); - var func = accessor.func(); - var handler = func.decl.findSuspensionHandler(func.instance, tag, accessor.pc()); + var h = X86_64FrameHandlers.from(rsp + Pointer.SIZE); + var func = h.wasm_func(); + var handler = func.decl.findSuspensionHandler(func.instance, tag, h.computePc()); if (handler.handler_pc < 0) { // not found if (Trace.stack) Trace.OUT.puts(" suspension handler not found, continue to unwind").ln(); return handler; @@ -201,10 +200,11 @@ class X86_64Stack extends WasmStack { Trace.OUT.put1(" stp=%d", handler.sidetable_pos).ln(); } // Reset current stack to handler position. - accessor.setNewProgramLocation(func.decl, handler.handler_pc, handler.sidetable_pos); - if (accessor.isSpc()) { - accessor.resetToHandlerStub(func.decl, handler); + if (h.isSpc()) { + h.writeSpcState(handler.handler_pc); + h.redirectToHandlerStub(func.decl, handler); } else { + h.writeInterpreterState(func.decl, handler.handler_pc, handler.sidetable_pos); // Set top address to suspension entry stub. var ic = X86_64PreGenStubs.getInterpreterCode(); var stub = ic.start + ic.header.intSuspendEntryOffset; @@ -216,19 +216,18 @@ class X86_64Stack extends WasmStack { ).ln(); } // perform implicit popping for ctlxfer - var vfp: Pointer = accessor.vfp(); + var vfp = h.vfp(); var stack_top = handler.val_stack_top + func.decl.num_locals; var vsp = vfp + (stack_top * valuerep.slot_size); this.vsp = vsp; - accessor.set_vsp(this.vsp); + h.set_vsp(this.vsp); return handler; } def tryHandleSwitch(tag: Tag) -> ExHandler { // Increment by {Pointer.SIZE} to skip the return address. - var frame = TargetFrame(rsp + Pointer.SIZE); - var accessor = frame.getFrameAccessor(); - var func = accessor.func(); - var handler = func.decl.findSwitchHandler(func.instance, tag, accessor.pc()); + var h = X86_64FrameHandlers.from(rsp + Pointer.SIZE); + var func = h.wasm_func(); + var handler = func.decl.findSwitchHandler(func.instance, tag, h.computePc()); if (handler.handler_pc < 0) { // not found if (Trace.stack) Trace.OUT.puts(" switch handler not found, continue to unwind").ln(); return handler; @@ -239,19 +238,23 @@ class X86_64Stack extends WasmStack { private def findExHandler(ip: Pointer, code: RiUserCode, pos: StackFramePos, ex: Exception) -> bool { var stack = pos.stack; var frame = pos.frame; - var accessor = frame.getFrameAccessor(); // XXX: don't materialize accessor - var func = accessor.func(); - var pc = accessor.pc(); + var h = X86_64FrameHandlers.fromCode(frame.sfp, code); + var func = h.wasm_func(); + var pc = h.computePcFromCode(code); var handler = func.decl.findExHandler(func.instance, ex.tag, pc); if (handler.handler_pc >= 0) { if (Trace.exception) Trace.OUT.put2(" walk found handler: pc=%d, stp=%d", handler.handler_pc, handler.sidetable_pos).ln(); - if (accessor.isSpc()) { - accessor.setNewProgramLocation(func.decl, handler.handler_pc, handler.sidetable_pos); - accessor.resetToHandlerStub(func.decl, handler); + + // Write frame state and redirect the control flow. + if (h.isSpc()) { + h.writeSpcState(handler.handler_pc); + h.redirectToHandlerStub(func.decl, handler); } else { - accessor.deoptToInterpreter0(func.decl, handler.handler_pc, handler.sidetable_pos); + h.writeInterpreterState(func.decl, handler.handler_pc, handler.sidetable_pos); + h.redirectToInterpreter(); } - var vfp: Pointer = accessor.vfp(); + + var vfp = h.vfp(); var slot_num = (handler.val_stack_top + func.decl.num_slots()); var vsp = vfp + (slot_num * valuerep.slot_size); stack.vsp = vsp; @@ -266,15 +269,15 @@ class X86_64Stack extends WasmStack { if (Trace.exception) Trace.OUT.puts(" push_exnref").ln(); stack.push(Value.Ref(ex)); } - accessor.set_vsp(stack.vsp); + h.set_vsp(stack.vsp); return false; // terminate stackwalk } if (Trace.exception) Trace.OUT.puts(" walk found no handler").ln(); return true; } private def addFrameToTrace(ip: Pointer, code: RiUserCode, pos: StackFramePos, trace: Vector<(WasmFunction, int)>) -> bool { - var accessor = pos.frame.getFrameAccessor(); // XXX: don't materialize accessor - trace.put(accessor.func(), accessor.pc()); + var h = X86_64FrameHandlers.fromCode(pos.frame.sfp, code); + trace.put(h.wasm_func(), h.computePcFromCode(code)); return true; } private def unwind(p_retaddr: Pointer, new_sp: Pointer) { @@ -595,28 +598,29 @@ component X86_64Stacks { def getFrameAccessor(sfp: Pointer) -> X86_64FrameAccessor { var retip = (sfp + -RETADDR_SIZE).load(); var code = RiRuntime.findUserCode(retip); + var h: X86_64FrameHandler; + var decl: FuncDecl; match (code) { x: X86_64InterpreterCode => { - var prev = (sfp + X86_64InterpreterFrame.accessor.offset).load(); + h = X86_64FrameHandler.Interpreter(sfp); + var prev = h.accessor(); if (prev != null) return prev; // Interpreter frames store the {WasmFunction} _and_ {FuncDecl}. - var decl = (sfp + X86_64InterpreterFrame.func_decl.offset).load(); - var n = X86_64FrameAccessor.new(X86_64Runtime.curStack, sfp, decl); - (sfp + X86_64InterpreterFrame.accessor.offset).store(n); - return n; + decl = h.func_decl(); } x: X86_64SpcCode => { - var prev = (sfp + X86_64InterpreterFrame.accessor.offset).load(); + h = X86_64FrameHandler.Spc(sfp); + var prev = h.accessor(); if (prev != null) return prev; // SPC frames only store the {WasmFunction}. - var wf = (sfp + X86_64InterpreterFrame.wasm_func.offset).load(); // TODO: assert wf.decl == x.decl() - var n = X86_64FrameAccessor.new(X86_64Runtime.curStack, sfp, wf.decl); - (sfp + X86_64InterpreterFrame.accessor.offset).store(n); - return n; + decl = h.wasm_func().decl; } + _ => return null; } - return null; + var n = X86_64FrameAccessor.new(X86_64Runtime.curStack, h, decl); + h.set_accessor(n); + return n; } def traceIpAndSp(ip: Pointer, sfp: Pointer, out: Range -> void) { var buf = X86_64Runtime.globalFrameDescriptionBuf; @@ -938,6 +942,25 @@ layout X86_64InterpreterFrame { =104; } +// Layout of an SPC frame. Same 104-byte footprint as {X86_64InterpreterFrame}. +// Slots used by the interpreter but not by SPC are omitted. +layout X86_64SpcFrame { + +0 wasm_func : i64; // WasmFunction (shared with interpreter) + +8 mem0_base : i64; // Pointer (shared with interpreter) + +16 vfp : i64; // Pointer (shared with interpreter) + +24 vsp : i64; // Pointer (shared with interpreter) +// +32: unused + +40 inlined_instance : i64; // Instance — Whamm probe inlining (aliases interpreter stp slot) +// +48: unused + +56 inlined_mem0_base : i64; // Pointer — Whamm probe inlining (aliases interpreter ip slot) +// +64: unused +// +72: unused + +80 instance : i64; // Instance (shared with interpreter) + +88 curpc : int; // int — written on trap/inlined-frame paths for pc() lookup + +96 accessor : i64; // FrameAccessor (shared with interpreter) + =104; +} + // Lightweight, discriminated handler for interpreter and SPC frames. // Unlike {X86_64FrameAccessor}, does not require heap-caching on the frame's accessor slot, // and does not store a FuncDecl/stack/etc. Use this for operations that just need @@ -1052,21 +1075,21 @@ type X86_64FrameHandler { // SPC-only field getters (trap on Interpreter). def inlined_instance() -> Instance { checkSpc(); - return (sfp() + X86_64InterpreterFrame.stp.offset).load(); // +40, reuses stp slot + return (sfp() + X86_64SpcFrame.inlined_instance.offset).load(); } def inlined_mem0_base() -> Pointer { checkSpc(); - return (sfp() + X86_64InterpreterFrame.ip.offset).load(); // +56, reuses ip slot + return (sfp() + X86_64SpcFrame.inlined_mem0_base.offset).load(); } // SPC-only field setters (trap on Interpreter). def set_inlined_instance(val: Instance) { checkSpc(); - (sfp() + X86_64InterpreterFrame.stp.offset).store(val); + (sfp() + X86_64SpcFrame.inlined_instance.offset).store(val); } def set_inlined_mem0_base(val: Pointer) { checkSpc(); - (sfp() + X86_64InterpreterFrame.ip.offset).store(val); + (sfp() + X86_64SpcFrame.inlined_mem0_base.offset).store(val); } // Compound operations. @@ -1104,6 +1127,15 @@ type X86_64FrameHandler { (p + X86_64InterpreterFrame.stp.offset).store(st_ptr); } + // Write SPC execution state fields needed when an SPC frame transitions to a new + // program location (e.g., jumping to an exception/suspension handler stub). Does NOT + // write interpreter-only fields, which would corrupt the SPC slot aliasing + // ({inlined_instance} at +40, {inlined_mem0_base} at +56) and has no consumer in SPC code. + def writeSpcState(new_pc: int) { + set_curpc(new_pc); + set_accessor(null); + } + // Redirect return address {*(%srp)} to an SPC exception/suspension handler stub. def redirectToHandlerStub(func: FuncDecl, handler: ExHandler) { var dest_id = func.handlers.handler_dest_map[handler.index]; @@ -1147,33 +1179,27 @@ enum X86_64FrameState { } // Implements access to interpreter and SPC frames. // TODO: (stack) parent method -class X86_64FrameAccessor(stack: X86_64Stack, sfp: Pointer, decl: FuncDecl) extends FrameAccessor { +class X86_64FrameAccessor(stack: X86_64Stack, handler: X86_64FrameHandler, decl: FuncDecl) extends FrameAccessor { var writer: X86_64FrameWriter; // non-null if any writes have been made var cached_depth = -1; var cached_pc: int; + def sfp() -> Pointer { return handler.sfp(); } + // Returns {true} if this frame has been unwound, either due to returning, a trap, or exception. def isUnwound() -> bool { if (FeatureDisable.frameAccess) return false; // TODO: proper is-frame-unwound check - return this != (sfp + X86_64InterpreterFrame.accessor.offset).load(); + return this != handler.accessor(); } // Returns the Wasm function in this frame. def func() -> WasmFunction { checkNotUnwound(); - return (sfp + X86_64InterpreterFrame.wasm_func.offset).load(); + return handler.wasm_func(); } // Returns the current program counter. def pc() -> int { checkNotUnwound(); - var ip = readIp(); - var code = RiRuntime.findUserCode(ip); - match (code) { - x: X86_64SpcModuleCode => cached_pc = x.lookupTopPc(ip, true); - x: X86_64SpcInlinedFrame => cached_pc = (sfp + X86_64InterpreterFrame.curpc.offset).load(); - x: X86_64InterpreterCode => cached_pc = X86_64Interpreter.computePCFromFrame(sfp); - x: X86_64SpcTrapsStub => cached_pc = (sfp + X86_64InterpreterFrame.curpc.offset).load(); - _ => cached_pc = -1; - } + cached_pc = handler.computePc(); return cached_pc; } // Returns {true} if this frame is currently the top executing frame, {false} if the @@ -1189,7 +1215,7 @@ class X86_64FrameAccessor(stack: X86_64Stack, sfp: Pointer, decl: FuncDecl) exte } private def computeDepth() -> int { var depth = 0; - var next_sfp = sfp; + var next_sfp = sfp(); while (true) { next_sfp = next_sfp + X86_64InterpreterFrame.size + RETADDR_SIZE; var retip = (next_sfp + -RETADDR_SIZE).load(); @@ -1213,16 +1239,10 @@ class X86_64FrameAccessor(stack: X86_64Stack, sfp: Pointer, decl: FuncDecl) exte } def stp() -> int { checkNotUnwound(); - var ip = readIp(); - var code = RiRuntime.findUserCode(ip); - match (code) { - x: X86_64InterpreterCode => { - var stp = (sfp + X86_64InterpreterFrame.stp.offset).load(); - var stp0 = Pointer.atContents(func().decl.sidetable.entries); - return int.!((stp - stp0) / 4); - } - _ => return -1; - } + if (!handler.isInterpreter()) return -1; + var stp = handler.stp(); + var stp0 = Pointer.atContents(func().decl.sidetable.entries); + return int.!((stp - stp0) / 4); } // Get the number of local variables in this frame. def numLocals() -> int { @@ -1238,29 +1258,24 @@ class X86_64FrameAccessor(stack: X86_64Stack, sfp: Pointer, decl: FuncDecl) exte def getLocal(i: int) -> Value { checkNotUnwound(); checkLocalBounds(i); - var vfp = (sfp + X86_64InterpreterFrame.vfp.offset).load(); - return stack.readValue(vfp, i); + return stack.readValue(handler.vfp(), i); } // Get the value of frame variable {i}. def getFrameVar(i: int) -> Value { checkNotUnwound(); checkFrameVarBounds(i); - var vfp = (sfp + X86_64InterpreterFrame.vfp.offset).load(); - return stack.readValue(vfp, i + decl.num_locals); + return stack.readValue(handler.vfp(), i + decl.num_locals); } // Get the number of operand stack elements. def numOperands() -> int { checkNotUnwound(); - var vfp = (sfp + X86_64InterpreterFrame.vfp.offset).load(); - var vsp = (sfp + X86_64InterpreterFrame.vsp.offset).load(); - var diff = int.!((vsp - vfp) / Target.tagging.slot_size); + var diff = int.!((handler.vsp() - handler.vfp()) / Target.tagging.slot_size); return diff - decl.num_locals; } // Get operand at depth {i}, with 0 being the top of the stack, -1 being one lower, etc. def getOperand(i: int) -> Value { checkNotUnwound(); - var vsp = (sfp + X86_64InterpreterFrame.vsp.offset).load(); - return stack.readValue(vsp, i - 1); + return stack.readValue(handler.vsp(), i - 1); } // Get the frame writer. def getWriter() -> X86_64FrameWriter { @@ -1269,20 +1284,18 @@ class X86_64FrameAccessor(stack: X86_64Stack, sfp: Pointer, decl: FuncDecl) exte // Read the return address from the frame. def readRetAddr() -> Pointer { - return (sfp + X86_64InterpreterFrame.size).load(); + return (sfp() + X86_64InterpreterFrame.size).load(); } // Read the instruction pointer from the frame. def readIp() -> Pointer { - return (sfp + -RETADDR_SIZE).load(); + return handler.srp().load(); } // Compute the caller frame's stack frame pointer. def callerSfp() -> Pointer { - return sfp + X86_64InterpreterFrame.size + RETADDR_SIZE; + return sfp() + X86_64InterpreterFrame.size + RETADDR_SIZE; } def isSpc() -> bool { - var ip = readIp(); - var code = RiRuntime.findUserCode(ip); - return X86_64SpcCode.?(code); + return handler.isSpc(); } private def checkNotUnwound() { @@ -1297,46 +1310,13 @@ class X86_64FrameAccessor(stack: X86_64Stack, sfp: Pointer, decl: FuncDecl) exte if (u32.view(i) >= num) System.error("FrameAccessorError", Strings.format2("frame index %d out-of-bounds (%d)", u32.view(i), num)); } - private def resetToHandlerStub(func: FuncDecl, handler: ExHandler) { - var dest_id = func.handlers.handler_dest_map[handler.index]; - var dest = func.handlers.handler_dests[dest_id]; - var offset = X86_64MasmLabel.!(dest.stub_label).label.pos; - if (Trace.exception) { - var spc_entry = func.target_code.spc_entry - Pointer.NULL; - Trace.OUT.put2("xfer to handler stub [handler=#%d, dest=#%d]: ", handler.index, dest_id).ln(); - Trace.OUT.put1(" ret_addr=0x%x ", spc_entry + offset); - Trace.OUT.put2("[spc_entry=0x%x, offset=%d]", spc_entry, offset).ln(); - } - (sfp + -RETADDR_SIZE).store(func.target_code.spc_entry + offset); - } private def deoptToInterpreter() { var wf = func(), func = wf.decl; var pc = this.pc(); var map = SidetableMap.new(func); // TODO: cache in FuncDecl? var stp = map[pc]; - deoptToInterpreter0(func, pc, stp); - } - private def deoptToInterpreter0(func: FuncDecl, pc: int, stp: int) { - var ic = X86_64PreGenStubs.getInterpreterCode(); - setNewProgramLocation(func, pc, stp); - (sfp + -RETADDR_SIZE).store(ic.start + ic.header.deoptReentryOffset); - } - def setNewProgramLocation(func: FuncDecl, pc: int, stp: int) { - var code = func.cur_bytecode; - (sfp + X86_64InterpreterFrame.func_decl.offset) .store(func); - (sfp + X86_64InterpreterFrame.curpc.offset) .store(pc); - (sfp + X86_64InterpreterFrame.code.offset) .store>(code); - (sfp + X86_64InterpreterFrame.ip.offset) .store(Pointer.atElement(code, pc)); - (sfp + X86_64InterpreterFrame.eip.offset) .store(Pointer.atContents(code) + code.length); - var st_entries = func.sidetable.entries; - var st_ptr = if(stp == st_entries.length, Pointer.atContents(st_entries), Pointer.atElement(func.sidetable.entries, stp)); - (sfp + X86_64InterpreterFrame.stp.offset) .store(st_ptr); - } - private def vfp() -> Pointer { - return (sfp + X86_64InterpreterFrame.vfp.offset).load(); - } - private def set_vsp(p: Pointer) { - (sfp + X86_64InterpreterFrame.vsp.offset).store(p); + handler.writeInterpreterState(func, pc, stp); + handler.redirectToInterpreter(); } } private class X86_64FrameWriter extends FrameWriter { @@ -1348,16 +1328,14 @@ private class X86_64FrameWriter extends FrameWriter { def setLocal(i: int, v: Value) { accessor.checkNotUnwound(); accessor.checkLocalBounds(i); - var vfp = (accessor.sfp + X86_64InterpreterFrame.vfp.offset).load(); - accessor.stack.overwriteValue(vfp, i, v); + accessor.stack.overwriteValue(accessor.handler.vfp(), i, v); if (accessor.isSpc()) accessor.deoptToInterpreter(); } // Set the value of a frame variable. (dynamically typechecked). def setFrameVar(i: int, v: Value) { accessor.checkNotUnwound(); accessor.checkFrameVarBounds(i); - var vfp = (accessor.sfp + X86_64InterpreterFrame.vfp.offset).load(); - accessor.stack.overwriteValue(vfp, i + accessor.decl.num_locals, v); + accessor.stack.overwriteValue(accessor.handler.vfp(), i + accessor.decl.num_locals, v); if (accessor.isSpc()) accessor.deoptToInterpreter(); } } From 259a2d2e0c5f27434adba797a3f4a8f66b1cea4f Mon Sep 17 00:00:00 2001 From: davidmaamoaix Date: Sat, 11 Apr 2026 16:04:23 -0400 Subject: [PATCH 3/4] Misc renames --- src/engine/x86-64/X86_64Stack.v3 | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/engine/x86-64/X86_64Stack.v3 b/src/engine/x86-64/X86_64Stack.v3 index 883b60b03..489d7808a 100644 --- a/src/engine/x86-64/X86_64Stack.v3 +++ b/src/engine/x86-64/X86_64Stack.v3 @@ -961,10 +961,7 @@ layout X86_64SpcFrame { =104; } -// Lightweight, discriminated handler for interpreter and SPC frames. -// Unlike {X86_64FrameAccessor}, does not require heap-caching on the frame's accessor slot, -// and does not store a FuncDecl/stack/etc. Use this for operations that just need -// to read/write frame fields without full probe instrumentation support. +// Frame handler for interpreter and SPC stack frames; a lightweight wrapper for frame slot accesses. type X86_64FrameHandler { case Interpreter(sfp_: Pointer) { def sfp() -> Pointer { return sfp_; } @@ -1128,9 +1125,7 @@ type X86_64FrameHandler { } // Write SPC execution state fields needed when an SPC frame transitions to a new - // program location (e.g., jumping to an exception/suspension handler stub). Does NOT - // write interpreter-only fields, which would corrupt the SPC slot aliasing - // ({inlined_instance} at +40, {inlined_mem0_base} at +56) and has no consumer in SPC code. + // program location (e.g., jumping to an exception/suspension handler stub) def writeSpcState(new_pc: int) { set_curpc(new_pc); set_accessor(null); From 385b42d8dc24323df5e52be944c0d2db10b84dc5 Mon Sep 17 00:00:00 2001 From: davidmaamoaix Date: Wed, 15 Apr 2026 00:39:58 -0400 Subject: [PATCH 4/4] Addressed PR feedbacks --- src/engine/x86-64/V3Offsets.v3 | 2 +- src/engine/x86-64/X86_64Interpreter.v3 | 2 +- src/engine/x86-64/X86_64SinglePassCompiler.v3 | 8 +- src/engine/x86-64/X86_64Stack.v3 | 270 ++++++++---------- 4 files changed, 130 insertions(+), 152 deletions(-) diff --git a/src/engine/x86-64/V3Offsets.v3 b/src/engine/x86-64/V3Offsets.v3 index 7881429bc..511078aee 100644 --- a/src/engine/x86-64/V3Offsets.v3 +++ b/src/engine/x86-64/V3Offsets.v3 @@ -15,7 +15,7 @@ class V3Offsets { private def wf = WasmFunction.new(i, decl); private def mem = NativeWasmMemory.new(null); private def vs = X86_64Stack.new(2u * 4096u); - private def acc = X86_64FrameAccessor.new(vs, X86_64FrameHandler.Interpreter(Pointer.NULL), decl); + private def acc = X86_64FrameAccessor.new(vs, X86_64FrameHandle.Interpreter(Pointer.NULL), decl); private def ha = HeapArray.new(null, []); private def cnt = CountProbe.new(); private def metric = Metric.new("", "", ""); diff --git a/src/engine/x86-64/X86_64Interpreter.v3 b/src/engine/x86-64/X86_64Interpreter.v3 index ad92cf71c..2a778768d 100644 --- a/src/engine/x86-64/X86_64Interpreter.v3 +++ b/src/engine/x86-64/X86_64Interpreter.v3 @@ -65,7 +65,7 @@ class X86_64InterpreterCode extends RiUserCode { if (Debug.stack) X86_64Stacks.traceIpAndSp(ip, sp, out); var msg = "\tin [fast-int] "; out(msg); - var h = X86_64FrameHandler.Interpreter(sp); + var h = X86_64FrameHandle.Interpreter(sp); var instance = h.instance(); var func = h.func_decl(); // TODO: lazy parse of names section may allocate; must avoid this in OOM situation diff --git a/src/engine/x86-64/X86_64SinglePassCompiler.v3 b/src/engine/x86-64/X86_64SinglePassCompiler.v3 index eb6f9292a..e2af1f1d6 100644 --- a/src/engine/x86-64/X86_64SinglePassCompiler.v3 +++ b/src/engine/x86-64/X86_64SinglePassCompiler.v3 @@ -1159,7 +1159,7 @@ class X86_64SpcCode extends RiUserCode { out(msg); msg = "] "; out(msg); - var h = X86_64FrameHandler.Spc(sp); + var h = X86_64FrameHandle.Spc(sp); var instance = h.instance(); var wf = h.wasm_func(); // TODO: lazy parse of names section may allocate; must avoid this in OOM situation @@ -1242,7 +1242,7 @@ class X86_64SpcModuleCode extends X86_64SpcCode { // Update the current PC in the JIT frame, if it is accessible. var ip = p_rip.load(); var inline_ctx = lookupPc(ip, false); - var h = X86_64FrameHandler.Spc(p_rsp); + var h = X86_64FrameHandle.Spc(p_rsp); if (inline_ctx == null) { h.set_curpc(-1); } else if (inline_ctx.tail == null) { @@ -1265,7 +1265,7 @@ class X86_64SpcModuleCode extends X86_64SpcCode { def count = inlined.length; // set outermost pc in the real frame; read instance (shared across all inlined frames) - var outer_h = X86_64FrameHandler.Spc(r_rsp); + var outer_h = X86_64FrameHandle.Spc(r_rsp); outer_h.set_curpc(outer.pc); var instance = outer_h.instance(); @@ -1278,7 +1278,7 @@ class X86_64SpcModuleCode extends X86_64SpcCode { r_rsp.store(INLINED_FRAME_STUB.start); r_rsp += -X86_64InterpreterFrame.size; // move rsp? - var h = X86_64FrameHandler.Spc(r_rsp); + var h = X86_64FrameHandle.Spc(r_rsp); // write func, pc, frame accessor var wasm_func = WasmFunction.!(instance.functions[fid]); diff --git a/src/engine/x86-64/X86_64Stack.v3 b/src/engine/x86-64/X86_64Stack.v3 index 489d7808a..7534b3742 100644 --- a/src/engine/x86-64/X86_64Stack.v3 +++ b/src/engine/x86-64/X86_64Stack.v3 @@ -188,7 +188,7 @@ class X86_64Stack extends WasmStack { Trace.OUT.put1("searching for handler on stack[rsp=0x%x]", rsp - Pointer.NULL).ln(); } // Increment by {Pointer.SIZE} to skip the return address pushed by {resume}. - var h = X86_64FrameHandlers.from(rsp + Pointer.SIZE); + var h = X86_64FrameHandles.from(rsp + Pointer.SIZE); var func = h.wasm_func(); var handler = func.decl.findSuspensionHandler(func.instance, tag, h.computePc()); if (handler.handler_pc < 0) { // not found @@ -200,9 +200,10 @@ class X86_64Stack extends WasmStack { Trace.OUT.put1(" stp=%d", handler.sidetable_pos).ln(); } // Reset current stack to handler position. - if (h.isSpc()) { - h.writeSpcState(handler.handler_pc); - h.redirectToHandlerStub(func.decl, handler); + if (X86_64FrameHandle.Spc.?(h)) { + var sh = X86_64FrameHandle.Spc.!(h); + sh.writeSpcState(handler.handler_pc); + sh.redirectToHandlerStub(func.decl, handler); } else { h.writeInterpreterState(func.decl, handler.handler_pc, handler.sidetable_pos); // Set top address to suspension entry stub. @@ -225,7 +226,7 @@ class X86_64Stack extends WasmStack { } def tryHandleSwitch(tag: Tag) -> ExHandler { // Increment by {Pointer.SIZE} to skip the return address. - var h = X86_64FrameHandlers.from(rsp + Pointer.SIZE); + var h = X86_64FrameHandles.from(rsp + Pointer.SIZE); var func = h.wasm_func(); var handler = func.decl.findSwitchHandler(func.instance, tag, h.computePc()); if (handler.handler_pc < 0) { // not found @@ -238,17 +239,18 @@ class X86_64Stack extends WasmStack { private def findExHandler(ip: Pointer, code: RiUserCode, pos: StackFramePos, ex: Exception) -> bool { var stack = pos.stack; var frame = pos.frame; - var h = X86_64FrameHandlers.fromCode(frame.sfp, code); + var h = X86_64FrameHandles.fromCode(frame.sfp, code); var func = h.wasm_func(); var pc = h.computePcFromCode(code); var handler = func.decl.findExHandler(func.instance, ex.tag, pc); if (handler.handler_pc >= 0) { if (Trace.exception) Trace.OUT.put2(" walk found handler: pc=%d, stp=%d", handler.handler_pc, handler.sidetable_pos).ln(); - + // Write frame state and redirect the control flow. - if (h.isSpc()) { - h.writeSpcState(handler.handler_pc); - h.redirectToHandlerStub(func.decl, handler); + if (X86_64FrameHandle.Spc.?(h)) { + var sh = X86_64FrameHandle.Spc.!(h); + sh.writeSpcState(handler.handler_pc); + sh.redirectToHandlerStub(func.decl, handler); } else { h.writeInterpreterState(func.decl, handler.handler_pc, handler.sidetable_pos); h.redirectToInterpreter(); @@ -276,7 +278,7 @@ class X86_64Stack extends WasmStack { return true; } private def addFrameToTrace(ip: Pointer, code: RiUserCode, pos: StackFramePos, trace: Vector<(WasmFunction, int)>) -> bool { - var h = X86_64FrameHandlers.fromCode(pos.frame.sfp, code); + var h = X86_64FrameHandles.fromCode(pos.frame.sfp, code); trace.put(h.wasm_func(), h.computePcFromCode(code)); return true; } @@ -598,29 +600,30 @@ component X86_64Stacks { def getFrameAccessor(sfp: Pointer) -> X86_64FrameAccessor { var retip = (sfp + -RETADDR_SIZE).load(); var code = RiRuntime.findUserCode(retip); - var h: X86_64FrameHandler; - var decl: FuncDecl; match (code) { x: X86_64InterpreterCode => { - h = X86_64FrameHandler.Interpreter(sfp); + var h = X86_64FrameHandle.Interpreter(sfp); var prev = h.accessor(); if (prev != null) return prev; // Interpreter frames store the {WasmFunction} _and_ {FuncDecl}. - decl = h.func_decl(); + var decl = h.func_decl(); + var n = X86_64FrameAccessor.new(X86_64Runtime.curStack, h, decl); + h.set_accessor(n); + return n; } x: X86_64SpcCode => { - h = X86_64FrameHandler.Spc(sfp); + var h = X86_64FrameHandle.Spc(sfp); var prev = h.accessor(); if (prev != null) return prev; // SPC frames only store the {WasmFunction}. // TODO: assert wf.decl == x.decl() - decl = h.wasm_func().decl; + var decl = h.wasm_func().decl; + var n = X86_64FrameAccessor.new(X86_64Runtime.curStack, h, decl); + h.set_accessor(n); + return n; } _ => return null; } - var n = X86_64FrameAccessor.new(X86_64Runtime.curStack, h, decl); - h.set_accessor(n); - return n; } def traceIpAndSp(ip: Pointer, sfp: Pointer, out: Range -> void) { var buf = X86_64Runtime.globalFrameDescriptionBuf; @@ -961,14 +964,87 @@ layout X86_64SpcFrame { =104; } -// Frame handler for interpreter and SPC stack frames; a lightweight wrapper for frame slot accesses. -type X86_64FrameHandler { +// Frame handle for interpreter and SPC stack frames; a lightweight wrapper for frame slot accesses. +type X86_64FrameHandle #unboxed { case Interpreter(sfp_: Pointer) { def sfp() -> Pointer { return sfp_; } + + // Interpreter-only field getters. + def sidetable() -> Array { + return (sfp_ + X86_64InterpreterFrame.sidetable.offset).load>(); + } + def stp() -> Pointer { + return (sfp_ + X86_64InterpreterFrame.stp.offset).load(); + } + def code() -> Array { + return (sfp_ + X86_64InterpreterFrame.code.offset).load>(); + } + // XXX: rename into a non-ambiguous name with the return address returned by `X86_64FrameAccessor.readIp()`. + def ip() -> Pointer { + return (sfp_ + X86_64InterpreterFrame.ip.offset).load(); + } + def eip() -> Pointer { + return (sfp_ + X86_64InterpreterFrame.eip.offset).load(); + } + def func_decl() -> FuncDecl { + return (sfp_ + X86_64InterpreterFrame.func_decl.offset).load(); + } + + // Interpreter-only field setters. + def set_func_decl(val: FuncDecl) { + (sfp_ + X86_64InterpreterFrame.func_decl.offset).store(val); + } + def set_code(val: Array) { + (sfp_ + X86_64InterpreterFrame.code.offset).store>(val); + } + def set_ip(val: Pointer) { + (sfp_ + X86_64InterpreterFrame.ip.offset).store(val); + } + def set_eip(val: Pointer) { + (sfp_ + X86_64InterpreterFrame.eip.offset).store(val); + } + def set_stp(val: Pointer) { + (sfp_ + X86_64InterpreterFrame.stp.offset).store(val); + } + def set_sidetable(val: Array) { + (sfp_ + X86_64InterpreterFrame.sidetable.offset).store>(val); + } } case Spc(sfp_: Pointer) { def sfp() -> Pointer { return sfp_; } + + // SPC-only field getters. + def inlined_instance() -> Instance { + return (sfp_ + X86_64SpcFrame.inlined_instance.offset).load(); + } + def inlined_mem0_base() -> Pointer { + return (sfp_ + X86_64SpcFrame.inlined_mem0_base.offset).load(); + } + + // SPC-only field setters. + def set_inlined_instance(val: Instance) { + (sfp_ + X86_64SpcFrame.inlined_instance.offset).store(val); + } + def set_inlined_mem0_base(val: Pointer) { + (sfp_ + X86_64SpcFrame.inlined_mem0_base.offset).store(val); + } + + // Write SPC execution state fields needed when an SPC frame transitions to a new + // program location (e.g., jumping to an exception/suspension handler stub). + def writeSpcState(new_pc: int) { + set_curpc(new_pc); + set_accessor(null); + } + + // Redirect return address {*(%srp)} to an SPC exception/suspension handler stub. + def redirectToHandlerStub(func: FuncDecl, handler: ExHandler) { + var dest_id = func.handlers.handler_dest_map[handler.index]; + var dest = func.handlers.handler_dests[dest_id]; + var offset = X86_64MasmLabel.!(dest.stub_label).label.pos; + srp().store(func.target_code.spc_entry + offset); + } } + def sfp() -> Pointer; def srp() -> Pointer { return sfp() + -RETADDR_SIZE; } @@ -1009,86 +1085,6 @@ type X86_64FrameHandler { (sfp() + X86_64InterpreterFrame.accessor.offset).store(val); } - // Guards. - def isSpc() -> bool { return X86_64FrameHandler.Spc.?(this); } - def isInterpreter() -> bool { return X86_64FrameHandler.Interpreter.?(this); } - - def checkInterpreter() { if (!isInterpreter()) X86_64FrameHandlers.fatal("interpreter"); } - def checkSpc() { if (!isSpc()) X86_64FrameHandlers.fatal("spc"); } - - // Interpreter-only field getters (trap on Spc). - def sidetable() -> Array { - checkInterpreter(); - return (sfp() + X86_64InterpreterFrame.sidetable.offset).load>(); - } - def stp() -> Pointer { - checkInterpreter(); - return (sfp() + X86_64InterpreterFrame.stp.offset).load(); - } - def code() -> Array { - checkInterpreter(); - return (sfp() + X86_64InterpreterFrame.code.offset).load>(); - } - // XXX: rename into a non-ambiguous name with the return address returned by `X86_64FrameAccessor.readIp()`. - def ip() -> Pointer { - checkInterpreter(); - return (sfp() + X86_64InterpreterFrame.ip.offset).load(); - } - def eip() -> Pointer { - checkInterpreter(); - return (sfp() + X86_64InterpreterFrame.eip.offset).load(); - } - def func_decl() -> FuncDecl { - checkInterpreter(); - return (sfp() + X86_64InterpreterFrame.func_decl.offset).load(); - } - - // Interpreter-only field setters (trap on Spc). - def set_func_decl(val: FuncDecl) { - checkInterpreter(); - (sfp() + X86_64InterpreterFrame.func_decl.offset).store(val); - } - def set_code(val: Array) { - checkInterpreter(); - (sfp() + X86_64InterpreterFrame.code.offset).store>(val); - } - def set_ip(val: Pointer) { - checkInterpreter(); - (sfp() + X86_64InterpreterFrame.ip.offset).store(val); - } - def set_eip(val: Pointer) { - checkInterpreter(); - (sfp() + X86_64InterpreterFrame.eip.offset).store(val); - } - def set_stp(val: Pointer) { - checkInterpreter(); - (sfp() + X86_64InterpreterFrame.stp.offset).store(val); - } - def set_sidetable(val: Array) { - checkInterpreter(); - (sfp() + X86_64InterpreterFrame.sidetable.offset).store>(val); - } - - // SPC-only field getters (trap on Interpreter). - def inlined_instance() -> Instance { - checkSpc(); - return (sfp() + X86_64SpcFrame.inlined_instance.offset).load(); - } - def inlined_mem0_base() -> Pointer { - checkSpc(); - return (sfp() + X86_64SpcFrame.inlined_mem0_base.offset).load(); - } - - // SPC-only field setters (trap on Interpreter). - def set_inlined_instance(val: Instance) { - checkSpc(); - (sfp() + X86_64SpcFrame.inlined_instance.offset).store(val); - } - def set_inlined_mem0_base(val: Pointer) { - checkSpc(); - (sfp() + X86_64SpcFrame.inlined_mem0_base.offset).store(val); - } - // Compound operations. // Compute the program counter for this frame. @@ -1110,7 +1106,7 @@ type X86_64FrameHandler { // Write all interpreter execution state fields for resuming at (func, new_pc, stp_val). // Called on both frame types. For SPC frames, this overwrites SPC-only slots - // with interpreter state in preparation for an deopt reentry. + // with interpreter state in preparation for a deopt reentry. def writeInterpreterState(func: FuncDecl, new_pc: int, stp_val: int) { var p = sfp(); var bytecode = func.cur_bytecode; @@ -1124,21 +1120,6 @@ type X86_64FrameHandler { (p + X86_64InterpreterFrame.stp.offset).store(st_ptr); } - // Write SPC execution state fields needed when an SPC frame transitions to a new - // program location (e.g., jumping to an exception/suspension handler stub) - def writeSpcState(new_pc: int) { - set_curpc(new_pc); - set_accessor(null); - } - - // Redirect return address {*(%srp)} to an SPC exception/suspension handler stub. - def redirectToHandlerStub(func: FuncDecl, handler: ExHandler) { - var dest_id = func.handlers.handler_dest_map[handler.index]; - var dest = func.handlers.handler_dests[dest_id]; - var offset = X86_64MasmLabel.!(dest.stub_label).label.pos; - srp().store(func.target_code.spc_entry + offset); - } - // Redirect return address to the interpreter's deopt reentry point. def redirectToInterpreter() { var ic = X86_64PreGenStubs.getInterpreterCode(); @@ -1146,23 +1127,20 @@ type X86_64FrameHandler { } } -// Factory for constructing {X86_64FrameHandler} from a stack frame pointer. -component X86_64FrameHandlers { +// Factory for constructing {X86_64FrameHandle} from a stack frame pointer. +component X86_64FrameHandles { // Construct from an sfp by inspecting the return address. - def from(sfp: Pointer) -> X86_64FrameHandler { + def from(sfp: Pointer) -> X86_64FrameHandle { var retip = (sfp + -RETADDR_SIZE).load(); var code = RiRuntime.findUserCode(retip); return fromCode(sfp, code); } // Construct from an sfp and a code object. - def fromCode(sfp: Pointer, code: RiUserCode) -> X86_64FrameHandler { - if (X86_64InterpreterCode.?(code)) return X86_64FrameHandler.Interpreter(sfp); - if (X86_64SpcCode.?(code)) return X86_64FrameHandler.Spc(sfp); - System.error("X86_64FrameHandlerError", "unknown frame type"); - return X86_64FrameHandler.Interpreter(sfp); // unreachable - } - def fatal(ftype: string) { - System.error("X86_64FrameHandlerError", Strings.format1("field not available on frame type %s", ftype)); + def fromCode(sfp: Pointer, code: RiUserCode) -> X86_64FrameHandle { + if (X86_64InterpreterCode.?(code)) return X86_64FrameHandle.Interpreter(sfp); + if (X86_64SpcCode.?(code)) return X86_64FrameHandle.Spc(sfp); + System.error("X86_64FrameHandleError", "unknown frame type"); + return X86_64FrameHandle.Interpreter(sfp); // unreachable } } @@ -1174,27 +1152,27 @@ enum X86_64FrameState { } // Implements access to interpreter and SPC frames. // TODO: (stack) parent method -class X86_64FrameAccessor(stack: X86_64Stack, handler: X86_64FrameHandler, decl: FuncDecl) extends FrameAccessor { +class X86_64FrameAccessor(stack: X86_64Stack, handle: X86_64FrameHandle, decl: FuncDecl) extends FrameAccessor { var writer: X86_64FrameWriter; // non-null if any writes have been made var cached_depth = -1; var cached_pc: int; - def sfp() -> Pointer { return handler.sfp(); } + def sfp() -> Pointer { return handle.sfp(); } // Returns {true} if this frame has been unwound, either due to returning, a trap, or exception. def isUnwound() -> bool { if (FeatureDisable.frameAccess) return false; // TODO: proper is-frame-unwound check - return this != handler.accessor(); + return this != handle.accessor(); } // Returns the Wasm function in this frame. def func() -> WasmFunction { checkNotUnwound(); - return handler.wasm_func(); + return handle.wasm_func(); } // Returns the current program counter. def pc() -> int { checkNotUnwound(); - cached_pc = handler.computePc(); + cached_pc = handle.computePc(); return cached_pc; } // Returns {true} if this frame is currently the top executing frame, {false} if the @@ -1234,8 +1212,8 @@ class X86_64FrameAccessor(stack: X86_64Stack, handler: X86_64FrameHandler, decl: } def stp() -> int { checkNotUnwound(); - if (!handler.isInterpreter()) return -1; - var stp = handler.stp(); + if (!X86_64FrameHandle.Interpreter.?(handle)) return -1; + var stp = X86_64FrameHandle.Interpreter.!(handle).stp(); var stp0 = Pointer.atContents(func().decl.sidetable.entries); return int.!((stp - stp0) / 4); } @@ -1253,24 +1231,24 @@ class X86_64FrameAccessor(stack: X86_64Stack, handler: X86_64FrameHandler, decl: def getLocal(i: int) -> Value { checkNotUnwound(); checkLocalBounds(i); - return stack.readValue(handler.vfp(), i); + return stack.readValue(handle.vfp(), i); } // Get the value of frame variable {i}. def getFrameVar(i: int) -> Value { checkNotUnwound(); checkFrameVarBounds(i); - return stack.readValue(handler.vfp(), i + decl.num_locals); + return stack.readValue(handle.vfp(), i + decl.num_locals); } // Get the number of operand stack elements. def numOperands() -> int { checkNotUnwound(); - var diff = int.!((handler.vsp() - handler.vfp()) / Target.tagging.slot_size); + var diff = int.!((handle.vsp() - handle.vfp()) / Target.tagging.slot_size); return diff - decl.num_locals; } // Get operand at depth {i}, with 0 being the top of the stack, -1 being one lower, etc. def getOperand(i: int) -> Value { checkNotUnwound(); - return stack.readValue(handler.vsp(), i - 1); + return stack.readValue(handle.vsp(), i - 1); } // Get the frame writer. def getWriter() -> X86_64FrameWriter { @@ -1283,14 +1261,14 @@ class X86_64FrameAccessor(stack: X86_64Stack, handler: X86_64FrameHandler, decl: } // Read the instruction pointer from the frame. def readIp() -> Pointer { - return handler.srp().load(); + return handle.srp().load(); } // Compute the caller frame's stack frame pointer. def callerSfp() -> Pointer { return sfp() + X86_64InterpreterFrame.size + RETADDR_SIZE; } def isSpc() -> bool { - return handler.isSpc(); + return X86_64FrameHandle.Spc.?(handle); } private def checkNotUnwound() { @@ -1310,8 +1288,8 @@ class X86_64FrameAccessor(stack: X86_64Stack, handler: X86_64FrameHandler, decl: var pc = this.pc(); var map = SidetableMap.new(func); // TODO: cache in FuncDecl? var stp = map[pc]; - handler.writeInterpreterState(func, pc, stp); - handler.redirectToInterpreter(); + handle.writeInterpreterState(func, pc, stp); + handle.redirectToInterpreter(); } } private class X86_64FrameWriter extends FrameWriter { @@ -1323,14 +1301,14 @@ private class X86_64FrameWriter extends FrameWriter { def setLocal(i: int, v: Value) { accessor.checkNotUnwound(); accessor.checkLocalBounds(i); - accessor.stack.overwriteValue(accessor.handler.vfp(), i, v); + accessor.stack.overwriteValue(accessor.handle.vfp(), i, v); if (accessor.isSpc()) accessor.deoptToInterpreter(); } // Set the value of a frame variable. (dynamically typechecked). def setFrameVar(i: int, v: Value) { accessor.checkNotUnwound(); accessor.checkFrameVarBounds(i); - accessor.stack.overwriteValue(accessor.handler.vfp(), i + accessor.decl.num_locals, v); + accessor.stack.overwriteValue(accessor.handle.vfp(), i + accessor.decl.num_locals, v); if (accessor.isSpc()) accessor.deoptToInterpreter(); } }