Skip to content

No way to describe computed variable values (analogue of DWARF expression locations / DW_OP_stack_value) #291

Open

Description

@djolertrk

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.

Activity

  1. djolertrk commented on Aug 21, 2026

    @djolertrk
    ContributorAuthor
  2. gnidan commented on Sep 3, 2026

    @gnidan
    Member

    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.

    I assume that's fine on the solc side?

  3. djolertrk commented on Sep 3, 2026

    @djolertrk
    ContributorAuthor

    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.

    I assume that's fine on the solc side?

    I like it!

  4. cameel commented on Nov 12, 2026

    @cameel
    Contributor

    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

    bytes constant 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions