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
3 changes: 2 additions & 1 deletion packages/bugc/src/evmgen/call-contexts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ code {

// Should have data pointer to return value at
// TOS (stack slot 0)
expect(ret.data.pointer).toEqual({
expect(ret.data).toBeDefined();
expect(ret.data!.pointer).toEqual({
location: "stack",
slot: 0,
});
Expand Down
5 changes: 2 additions & 3 deletions packages/format/src/types/program/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,16 +239,15 @@ export namespace Context {

export namespace Return {
export interface Info extends Function.Identity {
data: Function.PointerRef;
data?: Function.PointerRef;
success?: Function.PointerRef;
}

export const isInfo = (value: unknown): value is Info =>
Function.isIdentity(value) &&
typeof value === "object" &&
!!value &&
"data" in value &&
Function.isPointerRef(value.data) &&
(!("data" in value) || Function.isPointerRef(value.data)) &&
(!("success" in value) || Function.isPointerRef(value.success));
}

Expand Down
40 changes: 31 additions & 9 deletions packages/web/spec/program/context/function/return.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@ import SchemaViewer from "@site/src/components/SchemaViewer";
A return context marks an instruction associated with a successful
function return. It extends the
[function identity](/spec/program/context/function) schema with
a pointer to the return data and, for external calls, the
success status.
an optional pointer to the return data and, for external calls,
the success status.

<SchemaViewer
schema={{ id: "schema:ethdebug/format/program/context/function/return" }}
/>

## Return data

The `data` field is required and contains a pointer to the value
being returned. For internal calls this typically points to a
stack location; for external calls it points into the
`returndata` buffer.
The `data` field contains a pointer to the value being returned.
For internal calls this typically points to a stack location;
for external calls it points into the `returndata` buffer.

`data` is optional. Omit it when no return value is observable
at this instruction—see
[Field optionality](#field-optionality) below.

## Internal return

Expand All @@ -48,9 +51,28 @@ reverts at the EVM level.

## Field optionality

The `data` pointer is always required—every return context must
indicate where the return value can be found. The `success`
field is optional and only meaningful for external calls.
All fields on a return context are optional. A bare `return: {}`
is permitted when the compiler knows a return occurred but has
no further detail to record.

The `data` pointer is omitted when no return value is observable
at the marked instruction. This occurs in several legitimate
cases:

- **Void functions.** The function produces no return value, so
there is nothing to point at.
- **Tail-call-optimized back-edges.** When a tail call is
rewritten to a back-edge JUMP, the intermediate return value
is not materialized—it would have become the next iteration's
argument, which the compiler has already folded into the new
call's setup. A return semantically happens, but no pointer
records where the value lives (it does not live anywhere).
- **Lost compiler precision.** The compiler knows a return
occurred but has dropped the location tracking for the value.

The `success` field is optional and only meaningful for external
call returns, where it points to the boolean status placed on
the stack by CALL/DELEGATECALL/STATICCALL.

Function identity fields (`identifier`, `declaration`, `type`)
are all optional. A compiler may omit them when it cannot
Expand Down
36 changes: 31 additions & 5 deletions schemas/program/context/function/return.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ title: ethdebug/format/program/context/function/return
description: |
This context indicates that the marked instruction is
associated with a successful function return. Extends the
function identity schema with a pointer to the return data
and, for external calls, the success status.
function identity schema with an optional pointer to the
return data and, for external calls, the success status.

All fields are optional. A bare `return: {}` is permitted
when the compiler knows a return occurred but has no further
detail—for example, at a tail-call-optimized back-edge where
the intermediate return value is not materialized, or for a
void function with no return value.

type: object
properties:
Expand All @@ -21,6 +27,10 @@ properties:
title: Return data
description: |
Pointer to the data being returned from the function.
Optional: may be omitted when no return value is
observable at this instruction (e.g., void functions,
tail-call-optimized returns where the intermediate
value is not materialized).
properties:
pointer:
$ref: "schema:ethdebug/format/pointer"
Expand All @@ -39,9 +49,6 @@ properties:
required:
- pointer

required:
- data

unevaluatedProperties: false

required:
Expand Down Expand Up @@ -103,3 +110,22 @@ examples:
pointer:
location: stack
slot: 0

# -----------------------------------------------------------
# Return without observable data: TCO back-edge
# -----------------------------------------------------------
# At a tail-call-optimized back-edge JUMP, the intermediate
# return value is not materialized on the stack — it would
# have been the argument to the next iteration, which the
# compiler has already folded into the new call's setup.
# A return semantically happens (the outer activation's
# iteration N is returning), but there is no pointer to
# record for `data`.
- return:
identifier: "fact"
declaration:
source:
id: 0
range:
offset: 64
length: 120
Loading