Skip to content
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

Assign storage locations prior to LLL gen / place arrays sequentially in storage #2361

Conversation

iamdefinitelyahuman
Copy link
Contributor

@iamdefinitelyahuman iamdefinitelyahuman commented Apr 26, 2021

What I did

  • Add the start of an expansion pass that assigns variable positions prior to generating the LLL
  • Use this new pass to refactor the layout of storage variables

How I did it

Currently, whenever dealing with a type that requires >1 storage slot, we calculate the storage location in the same way as that of a mapping (using a keccak). This is inefficient for both gas costs and bytecode size. Because Vyper does not support dynamically-sized variables without a limit on their length, it is possible to calculate the maximum required number of slots for all storage variables and instead place them sequentially to avoid the expensive SHA3 operation.

To do this, I've added the DataPosition class, and related child classes, to represent positions within storage/memory/calldata. After type checking is completed, the AST is parsed once more and these objects applied to each previously assigned type. Handling this as a separate pass is imo a cleaner approach, making it easier to understand where and how this happens.

Armed with this information and the new storage layout, I've then removed various references to sha_32 within parser. I had to do a couple hacky things to make the new play well with the old, but mostly it was a reductive operation so I think this is a net win in terms of the bigger refactor goal.

I have more logic ready to go re: assigning locations for memory and calldata, but for the scope of this PR all I've included is the eventual API to show my idea for the bigger picture without doing too much in a single chunk.

How to verify it

Run the tests. I added a lovely (awful?) new test case that assigns to and reads from a variety of storage slots to check for corruption. I also ran the Curve test suite against this PR, everything passes.

Cute Animal Picture

image

Copy link
Member

@fubuloubu fubuloubu left a comment

Choose a reason for hiding this comment

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

Overall, I think this represents a breaking change to how our storage allocation algorithm works, and should perhaps target a v0.3.0 release

Could do with more robust testing, but we only have Bytes and String in terms of dynamic types right now.

vyper/context/types/bases.py Show resolved Hide resolved
vyper/context/types/bases.py Show resolved Hide resolved
vyper/context/types/bases.py Show resolved Hide resolved
vyper/context/validation/data_positions.py Outdated Show resolved Hide resolved
@@ -175,6 +175,7 @@ def generate_folded_ast(
vy_ast.folding.fold(vyper_module_folded)
validate_semantics(vyper_module_folded, interface_codes)
vy_ast.expansion.expand_annotated_ast(vyper_module_folded)
set_data_positions(vyper_module_folded)
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice if the storage and memory layout generated during this phase were made available externally

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, makes sense as a compiler output. But I think better done after the entirety of data positions are being calculated in that pass.

vyper/parser/parser_utils.py Show resolved Hide resolved
vyper/parser/parser_utils.py Show resolved Hide resolved
vyper/parser/parser_utils.py Outdated Show resolved Hide resolved
@codecov-commenter
Copy link

codecov-commenter commented Apr 27, 2021

Codecov Report

Merging #2361 (13e8ae8) into master (b2ae3a8) will decrease coverage by 0.10%.
The diff coverage is 80.00%.

❗ Current head 13e8ae8 differs from pull request most recent head 5ed060f. Consider uploading reports for the commit 5ed060f to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2361      +/-   ##
==========================================
- Coverage   85.90%   85.80%   -0.11%     
==========================================
  Files          90       91       +1     
  Lines        8940     8986      +46     
  Branches     2139     2142       +3     
==========================================
+ Hits         7680     7710      +30     
- Misses        771      783      +12     
- Partials      489      493       +4     
Impacted Files Coverage Δ
vyper/parser/events.py 99.21% <ø> (ø)
vyper/parser/stmt.py 84.50% <ø> (ø)
vyper/parser/external_call.py 83.67% <50.00%> (-0.71%) ⬇️
vyper/context/types/bases.py 85.09% <64.28%> (-4.39%) ⬇️
vyper/context/validation/data_positions.py 86.66% <86.66%> (ø)
vyper/compiler/phases.py 94.50% <100.00%> (+0.06%) ⬆️
vyper/context/__init__.py 100.00% <100.00%> (ø)
vyper/context/types/value/array_value.py 92.59% <100.00%> (ø)
vyper/functions/convert.py 73.29% <100.00%> (ø)
vyper/functions/functions.py 88.36% <100.00%> (ø)
... and 5 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b2ae3a8...5ed060f. Read the comment docs.

@iamdefinitelyahuman
Copy link
Contributor Author

I don't think this requires a bump to 0.3.0 because:

  1. the way we lay out storage is undocumented
  2. we handle storage differently from solidity
  3. vyper's range of functionality does not easily support upgradeability, where a change to storage would have a major impact

@fubuloubu
Copy link
Member

Should at least require a shoutout in release notes

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.

None yet

3 participants