Skip to content
Open
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
6 changes: 4 additions & 2 deletions packages/bugc/src/evmgen/call-contexts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ code {
expect(typeof invoke.declaration!.range!.length).toBe("number");

// Target should be a code pointer (not stack)
expect(Pointer.Region.isCode(call.target.pointer)).toBe(true);
expect(call.target).toBeDefined();
expect(Pointer.Region.isCode(call.target!.pointer)).toBe(true);

// Caller JUMP should NOT have argument pointers
// (args live on the callee JUMPDEST invoke context)
Expand Down Expand Up @@ -156,7 +157,8 @@ code {
expect(call.identifier).toBe("add");

// Target should be a code pointer
expect(Pointer.Region.isCode(call.target.pointer)).toBe(true);
expect(call.target).toBeDefined();
expect(Pointer.Region.isCode(call.target!.pointer)).toBe(true);

// Should have argument pointers matching
// function parameters
Expand Down
33 changes: 23 additions & 10 deletions packages/bugc/src/evmgen/generation/control-flow/terminator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type * as Format from "@ethdebug/format";
import type * as Ir from "#ir";
import { Utils as IrUtils } from "#ir";
import type * as Evm from "#evm";
import type { Stack } from "#evm";
import type { State } from "#evmgen/state";
Expand Down Expand Up @@ -411,16 +412,25 @@ function generateReturnEpilogue<S extends Stack>(
/**
* Build JUMP instruction options for a TCO-replaced tail call.
*
* The JUMP carries BOTH contexts in a gather:
* The JUMP carries three keys on a single flat context
* object:
* - return: the previous iteration's return
* - invoke: the new iteration's call
* - transform: ["tailcall"]
*
* Semantically the debugger sees frame depth stay constant
* across the back-edge JUMP: the previous frame pops, the
* new one pushes, on the same instruction. The function's
* terminal RETURN (elsewhere) emits a return context
* normally, popping the final iteration's frame.
*
* The `transform: ["tailcall"]` key is an additive
* annotation: it does not replace the invoke/return pair
* (which state the source-level facts) but tells debuggers
* the pair was realized as a TCO back-edge rather than a
* real frame push/pop, so they can avoid inventing a
* spurious frame.
*
* The invoke mirrors the normal caller-JUMP invoke
* (identity + declaration + code target, no argument
* pointers). The return omits `data` because TCO does not
Expand All @@ -431,7 +441,7 @@ function generateReturnEpilogue<S extends Stack>(
* resolved later by patchInvokeTarget.
*/
function buildTailCallJumpOptions(tailCall: Ir.Block.TailCall): {
debug: { context: Format.Program.Context };
debug: Ir.Instruction.Debug;
} {
const declaration =
tailCall.declarationLoc && tailCall.declarationSourceId
Expand All @@ -441,14 +451,12 @@ function buildTailCallJumpOptions(tailCall: Ir.Block.TailCall): {
}
: undefined;

const returnCtx: Format.Program.Context.Return = {
const combined: Format.Program.Context.Return &
Format.Program.Context.Invoke = {
return: {
identifier: tailCall.function,
...(declaration ? { declaration } : {}),
},
};

const invoke: Format.Program.Context.Invoke = {
invoke: {
jump: true as const,
identifier: tailCall.function,
Expand All @@ -463,11 +471,16 @@ function buildTailCallJumpOptions(tailCall: Ir.Block.TailCall): {
},
};

const gather: Format.Program.Context.Gather = {
gather: [returnCtx, invoke],
// Route through the shared helper so all transform emission
// (fold/tailcall/coalesce/...) composes consistently: the
// `transform` marker becomes a flat sibling key appended to
// any existing transform array.
return {
debug: IrUtils.addTransform(
{ context: combined as Format.Program.Context },
"tailcall",
),
};

return { debug: { context: gather as Format.Program.Context } };
}

/** PUSH an integer as the smallest PUSHn. */
Expand Down
2 changes: 2 additions & 0 deletions packages/bugc/src/evmgen/generation/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,8 @@ function patchInvokeInContext(
const offset = functionRegistry[invoke.identifier];
if (offset === undefined) return;

if (!invoke.target) return;

const ptr = invoke.target.pointer;
if (Format.Pointer.Region.isCode(ptr)) {
ptr.offset = `0x${offset.toString(16)}`;
Expand Down
Loading
Loading