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
71 changes: 55 additions & 16 deletions packages/bugc/src/evmgen/generation/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Block-level code generation
*/

import type * as Ast from "#ast";
import type * as Format from "@ethdebug/format";
import * as Ir from "#ir";
import type { Stack } from "#evm";
Expand Down Expand Up @@ -47,9 +48,13 @@ export function generate<S extends Stack>(

// Initialize memory for first block
if (isFirstBlock) {
// Always initialize the free memory pointer for consistency
// This ensures dynamic allocations start after static ones
result = result.then(initializeMemory(state.memory.nextStaticOffset));
const sourceInfo =
func?.sourceId && func?.loc
? { sourceId: func.sourceId, loc: func.loc }
: undefined;
result = result.then(
initializeMemory(state.memory.nextStaticOffset, sourceInfo),
);
}

// Set JUMPDEST for non-first blocks
Expand Down Expand Up @@ -104,6 +109,7 @@ export function generate<S extends Stack>(
predBlock.terminator.dest
) {
const destId = predBlock.terminator.dest;
const spillDebug = predBlock.terminator.operationDebug;
result = result.then(annotateTop(destId)).then((s) => {
const allocation = s.memory.allocations[destId];
if (!allocation) return s;
Expand All @@ -112,16 +118,25 @@ export function generate<S extends Stack>(
...s,
instructions: [
...s.instructions,
{ mnemonic: "DUP1" as const, opcode: 0x80 },
{
mnemonic: "DUP1" as const,
opcode: 0x80,
debug: spillDebug,
},
{
mnemonic: "PUSH2" as const,
opcode: 0x61,
immediates: [
(allocation.offset >> 8) & 0xff,
allocation.offset & 0xff,
],
debug: spillDebug,
},
{
mnemonic: "MSTORE" as const,
opcode: 0x52,
debug: spillDebug,
},
{ mnemonic: "MSTORE" as const, opcode: 0x52 },
],
};
});
Expand Down Expand Up @@ -205,21 +220,45 @@ function generatePhi<S extends Stack>(

/**
* Initialize the free memory pointer at runtime
* Sets the value at 0x40 to the next available memory location after static allocations
* Sets the value at 0x40 to the next available memory location
* after static allocations
*/
function initializeMemory<S extends Stack>(
nextStaticOffset: number,
sourceInfo?: { sourceId: string; loc: Ast.SourceLocation },
): Transition<S, S> {
const { PUSHn, MSTORE } = operations;

return (
pipe<S>()
// Push the static offset value (the value to store)
.then(PUSHn(BigInt(nextStaticOffset)), { as: "value" })
// Push the free memory pointer location (0x40) (the offset)
.then(PUSHn(BigInt(Memory.regions.FREE_MEMORY_POINTER)), { as: "offset" })
// Store the initial free pointer (expects [value, offset] on stack)
.then(MSTORE())
.done()
);
const debug = sourceInfo
? {
context: {
gather: [
{ remark: "initialize free memory pointer" },
{
code: {
source: { id: sourceInfo.sourceId },
range: sourceInfo.loc,
},
},
],
} as Format.Program.Context,
}
: {
context: {
remark: "initialize free memory pointer",
} as Format.Program.Context,
};

return pipe<S>()
.then(PUSHn(BigInt(nextStaticOffset), { debug }), {
as: "value",
})
.then(
PUSHn(BigInt(Memory.regions.FREE_MEMORY_POINTER), {
debug,
}),
{ as: "offset" },
)
.then(MSTORE({ debug }))
.done();
}
73 changes: 60 additions & 13 deletions packages/bugc/src/evmgen/generation/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,28 @@ function generatePrologue<S extends Stack>(
// Return PC is already in memory at 0x60 (stored by caller)
// Pop and store each arg from argN down to arg0

const prologueDebug = {
context: {
remark: `prologue: store ${params.length} parameter(s) to memory`,
},
};
const prologueDebug =
func.sourceId && func.loc
? {
context: {
gather: [
{
remark: `prologue: store ${params.length} parameter(s) to memory`,
},
{
code: {
source: { id: func.sourceId },
range: func.loc,
},
},
],
} as Format.Program.Context,
}
: {
context: {
remark: `prologue: store ${params.length} parameter(s) to memory`,
} as Format.Program.Context,
};

for (let i = params.length - 1; i >= 0; i--) {
const param = params[i];
Expand Down Expand Up @@ -104,7 +121,11 @@ function generatePrologue<S extends Stack>(
...currentState,
instructions: [
...currentState.instructions,
{ mnemonic: "MSTORE", opcode: 0x52 },
{
mnemonic: "MSTORE",
opcode: 0x52,
debug: prologueDebug,
},
],
};
}
Expand All @@ -113,11 +134,28 @@ function generatePrologue<S extends Stack>(
// so nested function calls don't clobber it.
const savedPcOffset = currentState.memory.savedReturnPcOffset;
if (savedPcOffset !== undefined) {
const savePcDebug = {
context: {
remark: `prologue: save return PC to 0x${savedPcOffset.toString(16)}`,
},
};
const savePcDebug =
func.sourceId && func.loc
? {
context: {
gather: [
{
remark: `prologue: save return PC to 0x${savedPcOffset.toString(16)}`,
},
{
code: {
source: { id: func.sourceId },
range: func.loc,
},
},
],
} as Format.Program.Context,
}
: {
context: {
remark: `prologue: save return PC to 0x${savedPcOffset.toString(16)}`,
} as Format.Program.Context,
};
const highByte = (savedPcOffset >> 8) & 0xff;
const lowByte = savedPcOffset & 0xff;
currentState = {
Expand All @@ -130,13 +168,22 @@ function generatePrologue<S extends Stack>(
immediates: [0x60],
debug: savePcDebug,
},
{ mnemonic: "MLOAD", opcode: 0x51 },
{
mnemonic: "MLOAD",
opcode: 0x51,
debug: savePcDebug,
},
{
mnemonic: "PUSH2",
opcode: 0x61,
immediates: [highByte, lowByte],
debug: savePcDebug,
},
{
mnemonic: "MSTORE",
opcode: 0x52,
debug: savePcDebug,
},
{ mnemonic: "MSTORE", opcode: 0x52 },
],
};
}
Expand Down
50 changes: 42 additions & 8 deletions packages/bugc/src/evmgen/generation/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,37 @@ export function generate(
// Insert STOP between main and user functions to prevent
// fall-through when the main function's last block omits
// STOP (the isLastBlock optimization).
const stopGuardDebug =
module.main.sourceId && module.main.loc
? {
context: {
gather: [
{
remark: "guard: prevent fall-through into functions",
},
{
code: {
source: { id: module.main.sourceId },
range: module.main.loc,
},
},
],
},
}
: {
context: {
remark: "guard: prevent fall-through into functions",
},
};
const stopGuard: Evm.Instruction[] =
patchedFunctions.length > 0
? [{ mnemonic: "STOP" as const, opcode: 0x00 }]
? [
{
mnemonic: "STOP" as const,
opcode: 0x00,
debug: stopGuardDebug,
},
]
: [];
const stopGuardBytes: number[] = patchedFunctions.length > 0 ? [0x00] : [];

Expand Down Expand Up @@ -243,13 +271,19 @@ function buildDeploymentInstructions(
function deploymentTransition(runtimeOffset: bigint, runtimeLength: bigint) {
const { PUSHn, CODECOPY, RETURN } = operations;

const debug = {
context: {
remark: "deployment: copy runtime bytecode and return",
},
};

return pipe()
.then(PUSHn(runtimeLength), { as: "size" })
.then(PUSHn(runtimeOffset), { as: "offset" })
.then(PUSHn(0n), { as: "destOffset" })
.then(CODECOPY())
.then(PUSHn(runtimeLength), { as: "size" })
.then(PUSHn(0n), { as: "offset" })
.then(RETURN())
.then(PUSHn(runtimeLength, { debug }), { as: "size" })
.then(PUSHn(runtimeOffset, { debug }), { as: "offset" })
.then(PUSHn(0n, { debug }), { as: "destOffset" })
.then(CODECOPY({ debug }))
.then(PUSHn(runtimeLength, { debug }), { as: "size" })
.then(PUSHn(0n, { debug }), { as: "offset" })
.then(RETURN({ debug }))
.done();
}
4 changes: 4 additions & 0 deletions packages/bugc/src/ir/spec/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export interface Function {
blocks: Map<string, Block>;
/** SSA variable metadata mapping temp IDs to original variables */
ssaVariables?: Map<string, Function.SsaVariable>;
/** Source location of the function body */
loc?: Ast.SourceLocation;
/** Source identifier for debug info */
sourceId?: string;
}

export namespace Function {
Expand Down
4 changes: 4 additions & 0 deletions packages/bugc/src/irgen/generate/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,16 @@ export function* buildFunction(
// Collect SSA variable metadata
const ssaVariables = yield* Process.Functions.collectSsaMetadata();

const module_ = yield* Process.Modules.current();

const function_: Ir.Function = {
name,
parameters: params,
entry: "entry",
blocks,
ssaVariables: ssaVariables.size > 0 ? ssaVariables : undefined,
loc: body.loc ?? undefined,
sourceId: module_.sourceId,
};

return function_;
Expand Down
Loading