Skip to content

feat(vm): Memory variable #1609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

feat(vm): Memory variable #1609

wants to merge 2 commits into from

Conversation

marioevz
Copy link
Member

🗒️ Description

Introduces the MemoryVariable abstraction to make contracts more readable when there's heavy use of the EVM memory as variable storage location.

To use, simply declare a variable with an unique offset that is not used by any other variable, and then the variable can be used in-place to read the value from memory:

v = MemoryVariable(128)

bytecode = Op.ADD(v, Op.CALLDATASIZE())

The previous example is equivalent to:

bytecode = Op.ADD(Op.MLOAD(offset=128), Op.CALLDATASIZE())

The variable also contains methods to generate bytecode to modify the variable in memory.

v = MemoryVariable(128)

bytecode = (
    v.store(0xff)
    + v.add(1)
    + v.return_value()
)

The previous example is equivalent to:

bytecode = (
    Op.MSTORE(offset=128, value=0xff)
    + Op.MSTORE(offset=128, value=Op.ADD(Op.MLOAD(offset=128), 1))
    + Op.RETURN(offset=128, size=32)
)

🔗 Related Issues

✅ Checklist

  • All: Set appropriate labels for the changes.
  • All: Considered squashing commits to improve commit history.
  • All: Added an entry to CHANGELOG.md.
  • All: Considered updating the online docs in the ./docs/ directory.
  • Tests: All converted JSON/YML tests from ethereum/tests have been added to converted-ethereum-tests.txt.
  • Tests: A PR with removal of converted JSON/YML tests from ethereum/tests have been opened.
  • Tests: Included the type and version of evm t8n tool used to locally execute test cases: e.g., ref with commit hash or geth 1.13.1-stable-3f40e65.
  • Tests: Ran mkdocs serve locally and verified the auto-generated docs for new tests in the Test Case Reference are correctly formatted.
  • Tests: For PRs implementing a missed test case, update the post-mortem document to add an entry the list.

@marioevz marioevz requested a review from winsvega May 15, 2025 18:19
@marioevz
Copy link
Member Author

@winsvega I thought of this when reading the code to make_gas_hash_contract and I thought that the hardest part of following the logic was mentally tracking the memory offsets and what they meant, so I modified the python code in that function to use this to see if it makes it more readable.

Let me know what you think.

Copy link
Contributor

@winsvega winsvega left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest I read plain evm opcodes much better. The fact that I don't know what's going on in this optimisations makes me go back and forth in defenitions.

Let me read it again tomorrow

+ Op.JUMPDEST
+ Op.CALLDATACOPY(63, Op.MLOAD(0), 1)
+ Op.JUMPDEST
code=initialize_code
Copy link
Contributor

@winsvega winsvega May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer not to subfunc the code for readability

+ Op.CALLDATACOPY(63, Op.MLOAD(0), 1)
+ Op.JUMPDEST
code=initialize_code
+ calldata_copy # offset_calldata_copy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it less readable

@winsvega
Copy link
Contributor

I was thinking the other night. maybe it will be even better for readability if Op will support memory variables?
so you can call it like

call(gas, address, balance,    input_var,  output_var) 
add(variable_name,  variable_name)
add(variable_name, value) 

# Code for memory initialization
initialize_code = variable_byte_offset.store(0)
calldata_copy = Op.JUMPDEST + Op.CALLDATACOPY(
dest_offset=variable_current_byte.offset + 32 - 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

32, -1 magic numbers?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was 63 before, we were putting it in the last byte of the variable.
So the offset, plus its size (32), minus the one byte.

Copy link
Contributor

@winsvega winsvega left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can be made more readable.
maybe Op.Mstore(var, value) can be supported?

but the most important. is to have some unit test for this logic.
to check that before and after refactoring it perfroms the same result.

and if you find it usefull it can be moved to a global namespace so we can reuse it in other tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants