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

Reduce code size by sharing code for clamps #2387

Merged
merged 2 commits into from Jul 19, 2021

Conversation

charles-cooper
Copy link
Member

@charles-cooper charles-cooper commented Jul 15, 2021

What I did

See title

How I did it

All clamps have the following code:

(assume condition is on the stack)
ISZERO _revert JUMPI # if not condition goto _revert
PUSH1 0 DUP REVERT
_revert
JUMPDEST

Since clamps are super common this code gets repeated. This reduces code
size by adding the PUSH1 0 DUP REVERT to each program's postamble, and
then clamps are simplified to the following

(assume condition is on the stack)
_revert JUMPI # if not condition goto _revert
-- cool now 5 opcodes are skipped

Then at the end of the contract, the following "postamble" is inserted:

_revert
JUMPDEST
PUSH1 0 DUP REVERT # repeated code is shared, yay

This commit also optimizes the following common "truthy" sequences:

ISZERO ISZERO ISZERO -> ISZERO
ISZERO ISZERO _jumpdest JUMPI -> _jumpdest JUMPI

How to verify it

Tests pass. I compared with master (ba0676b) and compiling a couple large contracts resulted in nearly 10% codesize reduction:

vyper -f bytecode_runtime
master (ba0676b) This branch (revert_codesize)
curve-contract/StableSwapIB.vy 23792 bytes 21629 bytes
yearn-vaults/Vault.vy 21816 bytes 20243 bytes

Description for the changelog

Reduce code size by ~10% by sharing clamp code

Cute Animal Picture

photo_2021-07-16_07-36-32

All clamps have the following code

(assume condition is on the stack)
ISZERO _revert JUMPI # if not condition goto _revert
PUSH1 0 DUP REVERT
_revert
JUMPDEST

Since clamps are super common this code gets repeated. This reduces code
size by adding the PUSH1 0 DUP REVERT to each program's postamble, and
then clamps are simplified to the following

(assume condition is on the stack)
_revert JUMPI # if not condition goto _revert
-- cool now 5 opcodes are skipped

This commit also optimizes the following common "truthy" sequences:
ISZERO ISZERO ISZERO -> ISZERO
ISZERI ISZERO _jumpdest JUMPI -> _jumpdest JUMPI
@codecov-commenter
Copy link

codecov-commenter commented Jul 15, 2021

Codecov Report

Merging #2387 (82c5641) into master (ba0676b) will increase coverage by 0.09%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2387      +/-   ##
==========================================
+ Coverage   85.74%   85.84%   +0.09%     
==========================================
  Files          91       91              
  Lines        8995     9021      +26     
  Branches     2145     2152       +7     
==========================================
+ Hits         7713     7744      +31     
+ Misses        788      785       -3     
+ Partials      494      492       -2     
Impacted Files Coverage Δ
vyper/compile_lll.py 93.88% <100.00%> (+1.02%) ⬆️
vyper/functions/functions.py 88.74% <0.00%> (+0.38%) ⬆️

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 ba0676b...82c5641. Read the comment docs.

@iamdefinitelyahuman
Copy link
Contributor

Tried testing this by compiling a Curve contract and running the related tests, it's failing in Brownie during the compilation phase because of an error in the source map.

@iamdefinitelyahuman
Copy link
Contributor

Could this change instead be applied during LLL optimizations in optimizer.py ?

@charles-cooper
Copy link
Member Author

charles-cooper commented Jul 17, 2021

@iamdefinitelyahuman it would require adding a conditional jump macro to LLL. Otherwise, we will have sequences like the following

(if condition (goto fail))

Which would generate an extra 3 instruction JUMP block like this

condition
PUSH _escape
JUMPI
PUSH _fail_block
JUMP
_escape
JUMPDEST

instead of

not_condition
PUSH _fail_block
JUMPI

But the way I have it also causes optimization problems being generated so late, for instance the jumpdest label can't be optimized into a dup.

@charles-cooper
Copy link
Member Author

charles-cooper commented Jul 18, 2021

@iamdefinitelyahuman source map error should be fixed in 82c5641

(I still have reservations about the approach of doing this in the opcode gen instead of as an LLL optimization step)

EDIT: After sleeping on it I decided I'm OK with doing it in the opcode gen since I plan to rewrite all of LLL anyway.

@fubuloubu fubuloubu merged commit a433212 into vyperlang:master Jul 19, 2021
@charles-cooper charles-cooper deleted the revert_codesize branch July 19, 2021 19:19
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

4 participants