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

[EVM-Equivalence-YUL] Interpeter optimizations #729

Merged
merged 6 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 102 additions & 82 deletions system-contracts/contracts/EvmInterpreterFunctions.template.yul
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ function popStackCheck(sp, evmGasLeft, numInputs) {
}
}

function pushStackCheck(sp, evmGasLeft, numInputs) {
if iszero(lt(add(sp, mul(0x20, sub(numInputs, 1))), BYTECODE_OFFSET())) {
revertWithGas(evmGasLeft)
}
}

function getCodeAddress() -> addr {
addr := verbatim_0i_1o("code_source")
}
Expand Down Expand Up @@ -470,6 +476,13 @@ function getGasForPrecompiles(addr, argsOffset, argsSize) -> gasToCharge {
}
}

function checkMemOverflowByOffset(offset, evmGasLeft) {
if gt(offset, MAX_POSSIBLE_MEM()) {
mstore(0, evmGasLeft)
revert(0, 32)
}
}

function checkMemOverflow(location, evmGasLeft) {
if gt(location, MAX_MEMORY_FRAME()) {
mstore(0, evmGasLeft)
Expand Down Expand Up @@ -592,29 +605,34 @@ function getNewAddress(addr) -> newAddr {
if iszero(nonce) {
nonceEncoded := 128
}
// The nonce has 4 bytes
if gt(nonce, 0xFFFFFF) {
nonceEncoded := shl(32, 0x84)
nonceEncoded := add(nonceEncoded, nonce)
nonceEncodedLength := 5
}
// The nonce has 3 bytes
if and(gt(nonce, 0xFFFF), lt(nonce, 0x1000000)) {
nonceEncoded := shl(24, 0x83)
nonceEncoded := add(nonceEncoded, nonce)
nonceEncodedLength := 4
}
// The nonce has 2 bytes
if and(gt(nonce, 0xFF), lt(nonce, 0x10000)) {
nonceEncoded := shl(16, 0x82)
switch gt(nonce, 0xFFFF)
case 1 {
switch gt(nonce, 0xFFFFFF)
case 1 {
// The nonce has 4 bytes
nonceEncoded := shl(32, 0x84)
nonceEncodedLength := 5
}
default {
// The nonce has 3 bytes
nonceEncoded := shl(24, 0x83)
nonceEncodedLength := 4
}
nonceEncoded := add(nonceEncoded, nonce)
nonceEncodedLength := 3
}
// The nonce has 1 byte and it's in [0x80, 0xFF]
if and(gt(nonce, 0x7F), lt(nonce, 0x100)) {
nonceEncoded := shl(8, 0x81)
nonceEncoded := add(nonceEncoded, nonce)
nonceEncodedLength := 2
default {
// The nonce has 2 bytes
if gt(nonce, 0xFF) {
nonceEncoded := shl(16, 0x82)
nonceEncoded := add(nonceEncoded, nonce)
nonceEncodedLength := 3
}
// The nonce has 1 byte and it's in [0x80, 0xFF]
if and(gt(nonce, 0x7F), lt(nonce, 0x100)) {
nonceEncoded := shl(8, 0x81)
nonceEncoded := add(nonceEncoded, nonce)
nonceEncodedLength := 2
}
}

listLength := add(21, nonceEncodedLength)
Expand Down Expand Up @@ -857,11 +875,11 @@ function performStaticCall(oldSp,evmGasLeft) -> extraCost, sp {

addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff)

checkMultipleOverflow(argsOffset,argsSize,MEM_OFFSET_INNER(), evmGasLeft)
checkMultipleOverflow(retOffset, retSize,MEM_OFFSET_INNER(), evmGasLeft)
checkOverflow(argsOffset,argsSize, evmGasLeft)
checkOverflow(retOffset, retSize, evmGasLeft)

checkMemOverflow(add(add(argsOffset, argsSize), MEM_OFFSET_INNER()), evmGasLeft)
checkMemOverflow(add(add(retOffset, retSize), MEM_OFFSET_INNER()), evmGasLeft)
checkMemOverflowByOffset(add(argsOffset, argsSize), evmGasLeft)
checkMemOverflowByOffset(add(retOffset, retSize), evmGasLeft)

extraCost := 0
if iszero($llvm_AlwaysInline_llvm$_warmAddress(addr)) {
Expand All @@ -879,16 +897,9 @@ function performStaticCall(oldSp,evmGasLeft) -> extraCost, sp {

let frameGasLeft
let success
if _isEVM(addr) {
_pushEVMFrame(gasToPass, true)
success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, 0, 0)

frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET_INNER(), retOffset), retSize)
_popEVMFrame()
}

// zkEVM native
if iszero(_isEVM(addr)) {
switch _isEVM(addr)
case 0 {
// zkEVM native
gasToPass := _getZkEVMGas(gasToPass, addr)
let zkevmGasBefore := gas()
success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, add(MEM_OFFSET_INNER(), retOffset), retSize)
Expand All @@ -901,6 +912,13 @@ function performStaticCall(oldSp,evmGasLeft) -> extraCost, sp {
frameGasLeft := sub(gasToPass, gasUsed)
}
}
default {
_pushEVMFrame(gasToPass, true)
success := staticcall(gasToPass, addr, add(MEM_OFFSET_INNER(), argsOffset), argsSize, 0, 0)

frameGasLeft := _saveReturndataAfterEVMCall(add(MEM_OFFSET_INNER(), retOffset), retSize)
_popEVMFrame()
}

let precompileCost := getGasForPrecompiles(addr, argsOffset, argsSize)
switch iszero(precompileCost)
Expand Down Expand Up @@ -935,7 +953,31 @@ function getMaxExpansionMemory(retOffset,retSize,argsOffset,argsSize) -> maxExpa
function _performCall(addr,gasToPass,value,argsOffset,argsSize,retOffset,retSize,isStatic) -> success, frameGasLeft, gasToPassNew{
gasToPassNew := gasToPass
let is_evm := _isEVM(addr)
if isStatic {

switch isStatic
case 0 {
switch is_evm
case 0 {
// zkEVM native
gasToPassNew := _getZkEVMGas(gasToPassNew, addr)
let zkevmGasBefore := gas()
success := call(gasToPassNew, addr, value, argsOffset, argsSize, retOffset, retSize)
_saveReturndataAfterZkEVMCall()
let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas()))

frameGasLeft := 0
if gt(gasToPassNew, gasUsed) {
frameGasLeft := sub(gasToPassNew, gasUsed)
}
}
default {
_pushEVMFrame(gasToPassNew, isStatic)
success := call(EVM_GAS_STIPEND(), addr, value, argsOffset, argsSize, 0, 0)
frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize)
_popEVMFrame()
}
}
default {
if value {
revertWithGas(gasToPassNew)
}
Expand All @@ -949,27 +991,6 @@ function _performCall(addr,gasToPass,value,argsOffset,argsSize,retOffset,retSize
retSize
)
}

if and(is_evm, iszero(isStatic)) {
_pushEVMFrame(gasToPassNew, isStatic)
success := call(EVM_GAS_STIPEND(), addr, value, argsOffset, argsSize, 0, 0)
frameGasLeft := _saveReturndataAfterEVMCall(retOffset, retSize)
_popEVMFrame()
}

// zkEVM native
if and(iszero(is_evm), iszero(isStatic)) {
gasToPassNew := _getZkEVMGas(gasToPassNew, addr)
let zkevmGasBefore := gas()
success := call(gasToPassNew, addr, value, argsOffset, argsSize, retOffset, retSize)
_saveReturndataAfterZkEVMCall()
let gasUsed := _calcEVMGas(sub(zkevmGasBefore, gas()))

frameGasLeft := 0
if gt(gasToPassNew, gasUsed) {
frameGasLeft := sub(gasToPassNew, gasUsed)
}
}
}

function performCall(oldSp, evmGasLeft, isStatic) -> extraCost, sp {
Expand Down Expand Up @@ -1060,11 +1081,11 @@ function delegateCall(oldSp, oldIsStatic, evmGasLeft) -> sp, isStatic, extraCost

// addr := and(addr, 0xffffffffffffffffffffffffffffffffffffffff)

checkMultipleOverflow(argsOffset,argsSize,MEM_OFFSET_INNER(), evmGasLeft)
checkMultipleOverflow(retOffset, retSize,MEM_OFFSET_INNER(), evmGasLeft)
checkOverflow(argsOffset, argsSize, evmGasLeft)
checkOverflow(retOffset, retSize, evmGasLeft)

checkMemOverflow(add(add(argsOffset, argsSize), MEM_OFFSET_INNER()), evmGasLeft)
checkMemOverflow(add(add(retOffset, retSize), MEM_OFFSET_INNER()), evmGasLeft)
checkMemOverflowByOffset(add(argsOffset, argsSize), evmGasLeft)
checkMemOverflowByOffset(add(retOffset, retSize), evmGasLeft)

if iszero(_isEVM(addr)) {
revertWithGas(evmGasLeft)
Expand Down Expand Up @@ -1148,16 +1169,9 @@ function _performStaticCall(
_outputOffset,
_outputLen
) -> success, _gasLeft {
if _calleeIsEVM {
_pushEVMFrame(_calleeGas, true)
success := staticcall(EVM_GAS_STIPEND(), _callee, _inputOffset, _inputLen, 0, 0)

_gasLeft := _saveReturndataAfterEVMCall(_outputOffset, _outputLen)
_popEVMFrame()
}

// zkEVM native
if iszero(_calleeIsEVM) {
switch _calleeIsEVM
case 0 {
// zkEVM native
_calleeGas := _getZkEVMGas(_calleeGas, _callee)
let zkevmGasBefore := gas()
success := staticcall(_calleeGas, _callee, _inputOffset, _inputLen, _outputOffset, _outputLen)
Expand All @@ -1171,6 +1185,13 @@ function _performStaticCall(
_gasLeft := sub(_calleeGas, gasUsed)
}
}
default {
_pushEVMFrame(_calleeGas, true)
success := staticcall(EVM_GAS_STIPEND(), _callee, _inputOffset, _inputLen, 0, 0)

_gasLeft := _saveReturndataAfterEVMCall(_outputOffset, _outputLen)
_popEVMFrame()
}
}

function isAddrEmpty(addr) -> isEmpty {
Expand Down Expand Up @@ -1210,10 +1231,11 @@ function $llvm_NoInline_llvm$_genericCreate(addr, offset, size, sp, value, evmGa

offset := add(MEM_OFFSET_INNER(), offset)

sp := pushStackItem(sp, mload(sub(offset, 0x80)), evmGasLeftOld)
sp := pushStackItem(sp, mload(sub(offset, 0x60)), evmGasLeftOld)
sp := pushStackItem(sp, mload(sub(offset, 0x40)), evmGasLeftOld)
sp := pushStackItem(sp, mload(sub(offset, 0x20)), evmGasLeftOld)
pushStackCheck(sp, evmGasLeftOld, 4)
sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x80)))
sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x60)))
sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x40)))
sp := pushStackItemWithoutCheck(sp, mload(sub(offset, 0x20)))

// Selector
mstore(sub(offset, 0x80), 0x5b16a23c)
Expand Down Expand Up @@ -1257,7 +1279,7 @@ function $llvm_NoInline_llvm$_genericCreate(addr, offset, size, sp, value, evmGa

let back

popStackCheck(sp, evmGasLeft, 4)
// skipping check since we pushed exactly 4 items earlier
back, sp := popStackItemWithoutCheck(sp)
mstore(sub(offset, 0x20), back)
back, sp := popStackItemWithoutCheck(sp)
Expand Down Expand Up @@ -1351,9 +1373,8 @@ function performCreate(evmGas,oldSp,isStatic) -> evmGasLeft, sp {
offset, sp := popStackItemWithoutCheck(sp)
size, sp := popStackItemWithoutCheck(sp)

checkMultipleOverflow(offset, size, MEM_OFFSET_INNER(), evmGasLeft)

checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size)), evmGasLeft)
checkOverflow(offset, size, evmGasLeft)
checkMemOverflowByOffset(add(offset, size), evmGasLeft)

if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) {
revertWithGas(evmGasLeft)
Expand Down Expand Up @@ -1398,9 +1419,8 @@ function performCreate2(evmGas, oldSp, isStatic) -> evmGasLeft, sp, result, addr
size, sp := popStackItemWithoutCheck(sp)
salt, sp := popStackItemWithoutCheck(sp)

checkMultipleOverflow(offset, size, MEM_OFFSET_INNER(), evmGasLeft)

checkMemOverflow(add(MEM_OFFSET_INNER(), add(offset, size)), evmGasLeft)
checkOverflow(offset, size, evmGasLeft)
checkMemOverflowByOffset(add(offset, size), evmGasLeft)

if gt(size, mul(2, MAX_POSSIBLE_BYTECODE())) {
revertWithGas(evmGasLeft)
Expand Down
Loading
Loading