You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
No way to describe computed variable values (analogue of DWARF expression locations / DW_OP_stack_value) #291
Pointers can describe where a variable's bytes live (regions in stack, storage, memory, ...), and the expression grammar can compute positions ($sum, $read, $keccak256, ...). But there is no form that says: "this variable currently has no allocation at all — its value equals this expression".
Optimizing compilers need this constantly. After rematerialization or expression folding, a source variable may have no region anywhere, while its value is still perfectly recoverable from other live state, e.g.:
a constant folded into every use site (value = literal 42),
let x := add(a, 1) eliminated, with a still live (value = a + 1),
a value recomputable from a storage slot that is still intact.
DWARF handles this with expression-based location descriptions, with a stack-machine expression (see e.g. https://www.hitzhangjie.pro/debugger101-en.io/8-dwarf/4-die/2-desc-locations.html) terminated by DW_OP_stack_value, meaning "the computed result is the value, there is no address". Without an equivalent, the only honest thing a producer can emit for such variables is "unavailable", which loses debugging quality exactly where optimizations kick in.
The expression grammar seems to already contain most of the machinery; what is missing is a schema form marking the result as the value itself rather than a position. A minimal sketch:
{ "value": <expression> }
allowed wherever a variable's pointer goes in a program context (perhaps not inside region collections, since it denotes no data range).
Context: we hit this in the Solidity compiler's ethdebug work (solc #16780 PR) — we currently model these variables with a "computed" phase that admits the value is recomputable in principle but cannot carry the recipe.
Ooh, thanks @djolertrk. I like your idea of putting { "value": <expression> } into the pointer schema. Maybe change the key from "value" to "expression"? (you'll see my examples below; I think they read better and it's more straightforward for the key to match the value type.)
I think we can do better than just allowing it when a variable's pointer goes in a program context. How about this instead: a pointer right now is either a region or a collection. We could instead say that a pointer is a region, a collection, or a value expression, which becomes a second kind of leaf node that fits into pointer trees normally. Like in the example you describe, we could just have this for x's pointer:
group:
- name: alocation: stackslot: 2# or wherever it lives
- name: valueexpression:
$sum:
- $read: a
- 1
And then assuming I can magically handwave this as the domain of the "interpretation" business I have insufficiently formulated, basically we can ensure that the debugger doesn't lose x.
There is one hiccough though. I want to improve the semantics of these expressions regarding an expression's bytes size, and I've got #286 open to do just that. That PR solidifies the idea that expressions evaluate either to an unbounded integer or a bytes with a definite width. Normally it's the length field that allows the width calculations to work, but a value pointer doesn't have such a thing, so the example would have to make sure the expression resolves ultimately to a sized value, like:
- name: valueexpression:
$wordsized:
$sum:
- $read: a
- 1
It's the pointer's job to produce bytes for a debugger, after all. #286 makes this separation explicit by sorting expressions into integers or bytes, and since a pointer has always resolved to bytes, an expression leaf can't be in the place where an unbounded integer leaks out.
Ooh, thanks @djolertrk. I like your idea of putting { "value": <expression> } into the pointer schema. Maybe change the key from "value" to "expression"? (you'll see my examples below; I think they read better and it's more straightforward for the key to match the value type.)
I think we can do better than just allowing it when a variable's pointer goes in a program context. How about this instead: a pointer right now is either a region or a collection. We could instead say that a pointer is a region, a collection, or a value expression, which becomes a second kind of leaf node that fits into pointer trees normally. Like in the example you describe, we could just have this for x's pointer:
group:
name: a
location: stack
slot: 2 # or wherever it lives
name: value
expression:
$sum:
- $read: a
- 1
And then assuming I can magically handwave this as the domain of the "interpretation" business I have insufficiently formulated, basically we can ensure that the debugger doesn't lose x.
There is one hiccough though. I want to improve the semantics of these expressions regarding an expression's bytes size, and I've got #286 open to do just that. That PR solidifies the idea that expressions evaluate either to an unbounded integer or a bytes with a definite width. Normally it's the length field that allows the width calculations to work, but a value pointer doesn't have such a thing, so the example would have to make sure the expression resolves ultimately to a sized value, like:
name: value
expression:
$wordsized:
$sum:
- $read: a
- 1
It's the pointer's job to produce bytes for a debugger, after all. #286 makes this separation explicit by sorting expressions into integers or bytes, and since a pointer has always resolved to bytes, an expression leaf can't be in the place where an unbounded integer leaks out.
Some thoughts after skimming through #411. Posting them here since I don't really want to review the PR but rather discuss this on a more general level:
Expressivity of the expression syntax. The set of operations I see in Core schemas > Pointers > Expressions is pretty bare bones and it does not look like the PR extends it. For example, one thing that stands out immediately is the lack of bitwise operations like shifts and masking. Or for strings you can concatenate but not index/split. I think you can build almost everything from scratch by combining basic arithmetic and conversions to/from strings, but I wonder if that won't just add complexity and bloat up the ethdebug output size on the compiler side. Just a general concern for now since I don't yet have a good picture of what expressions we'll need (@djolertrk: that's something we should actually prepare a spec for on our side), but I expect that we might want to come up with a broader set of operations that'll make common calculations more straightforward.
Function calls. I don't think the PR addresses the case where the value comes from some opaque calculation that we can't or don't want to just inline into an expression. TBH, looking at the issue I don't see it mentioned either so maybe it's out of scope, but I thought I'd mention it. What do we do for cases like
bytesconstant X =abi.encode("123", true, [[1], [2], [3]]);
where you need to execute some code to get at the final value? It's not even like we need to know the value here. A debugger should be able to calculate it on its own, but it needs to know which bytecode to execute and how.
Activity
djolertrk commentedon Aug 21, 2026
cc @cameel @gnidan
gnidan commentedon Sep 3, 2026
Ooh, thanks @djolertrk. I like your idea of putting
{ "value": <expression> }into the pointer schema. Maybe change the key from"value"to"expression"? (you'll see my examples below; I think they read better and it's more straightforward for the key to match the value type.)I think we can do better than just allowing it when a variable's pointer goes in a program context. How about this instead: a pointer right now is either a region or a collection. We could instead say that a pointer is a region, a collection, or a value expression, which becomes a second kind of leaf node that fits into pointer trees normally. Like in the example you describe, we could just have this for
x's pointer:And then assuming I can magically handwave this as the domain of the "interpretation" business I have insufficiently formulated, basically we can ensure that the debugger doesn't lose
x.There is one hiccough though. I want to improve the semantics of these expressions regarding an expression's bytes size, and I've got #286 open to do just that. That PR solidifies the idea that expressions evaluate either to an unbounded integer or a bytes with a definite width. Normally it's the
lengthfield that allows the width calculations to work, but a value pointer doesn't have such a thing, so the example would have to make sure the expression resolves ultimately to a sized value, like:It's the pointer's job to produce bytes for a debugger, after all. #286 makes this separation explicit by sorting expressions into integers or bytes, and since a pointer has always resolved to bytes, an expression leaf can't be in the place where an unbounded integer leaks out.
I assume that's fine on the solc side?
djolertrk commentedon Sep 3, 2026
I like it!
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data
bugc: emit local variables in the debug data (#328)
cameel commentedon Nov 12, 2026
Some thoughts after skimming through #411. Posting them here since I don't really want to review the PR but rather discuss this on a more general level:
Expressivity of the expression syntax. The set of operations I see in Core schemas > Pointers > Expressions is pretty bare bones and it does not look like the PR extends it. For example, one thing that stands out immediately is the lack of bitwise operations like shifts and masking. Or for strings you can concatenate but not index/split. I think you can build almost everything from scratch by combining basic arithmetic and conversions to/from strings, but I wonder if that won't just add complexity and bloat up the ethdebug output size on the compiler side. Just a general concern for now since I don't yet have a good picture of what expressions we'll need (@djolertrk: that's something we should actually prepare a spec for on our side), but I expect that we might want to come up with a broader set of operations that'll make common calculations more straightforward.
Function calls. I don't think the PR addresses the case where the value comes from some opaque calculation that we can't or don't want to just inline into an expression. TBH, looking at the issue I don't see it mentioned either so maybe it's out of scope, but I thought I'd mention it. What do we do for cases like
where you need to execute some code to get at the final value? It's not even like we need to know the value here. A debugger should be able to calculate it on its own, but it needs to know which bytecode to execute and how.