Skip to content

Commit

Permalink
Merge branch 'evm-equivalence-yul' into yul-environment-1
Browse files Browse the repository at this point in the history
  • Loading branch information
jrchatruc committed Apr 12, 2024
2 parents a417561 + 67fa431 commit 5ed0a47
Showing 1 changed file with 149 additions and 3 deletions.
152 changes: 149 additions & 3 deletions system-contracts/contracts/EvmInterpreter.yul
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,35 @@ object "EVMInterpreter" {
gasRemaining := sub(prevGas, toCharge)
}

// Note, that this function can overflow. It's up to the caller to ensure that it does not.
function memCost(memSizeWords) -> gasCost {
// The first term of the sum is the quadratic cost, the second one the linear one.
gasCost := add(div(mul(memSizeWords, memSizeWords), 512), mul(3, memSizeWords))
}

// This function can overflow, it is the job of the caller to ensure that it does not.
// The argument to this function is the offset into the memory region IN BYTES.
function expandMemory(newSize) -> gasCost {
let oldSizeInWords := mload(MEM_OFFSET())

// The add 31 here before dividing is there to account for misaligned
// memory expansions, where someone calls this with a newSize that is not
// a multiple of 32. For instance, if someone calls it with an offset of 33,
// the new size in words should be 2, not 1, but dividing by 32 will give 1.
// Adding 31 solves it.
let newSizeInWords := div(add(newSize, 31), 32)

if gt(newSizeInWords, oldSizeInWords) {
// TODO: Check this, it feels like there might be a more optimized way
// of doing this cost calculation.
let oldCost := memCost(oldSizeInWords)
let newCost := memCost(newSizeInWords)

gasCost := sub(newCost, oldCost)
mstore(MEM_OFFSET(), newSizeInWords)
}
}

// Essentially a NOP that will not get optimized away by the compiler
function $llvm_NoInline_llvm$_unoptimized() {
pop(1)
Expand All @@ -239,6 +268,44 @@ object "EVMInterpreter" {
$llvm_NoInline_llvm$_unoptimized()
}

function isSlotWarm(key) -> isWarm {
// TODO: Unhardcode this selector 0x482d2e74
mstore8(0, 0x48)
mstore8(1, 0x2d)
mstore8(2, 0x2e)
mstore8(3, 0x74)
mstore(4, key)

let success := call(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 0, 36, 0, 32)

if iszero(success) {
// This error should never happen
revert(0, 0)
}

isWarm := mload(0)
}

function warmSlot(key,currentValue) -> isWarm, originalValue {
// TODO: Unhardcode this selector 0xbdf78160
mstore8(0, 0xbd)
mstore8(1, 0xf7)
mstore8(2, 0x81)
mstore8(3, 0x60)
mstore(4, key)
mstore(36,currentValue)

let success := call(gas(), EVM_GAS_MANAGER_CONTRACT(), 0, 0, 68, 0, 64)

if iszero(success) {
// This error should never happen
revert(0, 0)
}

isWarm := mload(0)
originalValue := mload(32)
}

////////////////////////////////////////////////////////////////
// FALLBACK
////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -490,6 +557,46 @@ object "EVMInterpreter" {

evmGasLeft := chargeGas(evmGasLeft, 2)
}
case 0x50 { // OP_POP
let _y

_y, sp := popStackItem(sp)

evmGasLeft := chargeGas(evmGasLeft, 2)
}
case 0x51 { // OP_MLOAD
let offset

offset, sp := popStackItem(sp)

let expansionGas := expandMemory(add(offset, 32))

let memValue := mload(add(MEM_OFFSET_INNER(), offset))
sp := pushStackItem(sp, memValue)
evmGasLeft := chargeGas(evmGasLeft, add(3, expansionGas))
}
case 0x52 { // OP_MSTORE
let offset, value

offset, sp := popStackItem(sp)
value, sp := popStackItem(sp)

let expansionGas := expandMemory(add(offset, 32))

mstore(add(MEM_OFFSET_INNER(), offset), value)
evmGasLeft := chargeGas(evmGasLeft, add(3, expansionGas))
}
case 0x53 { // OP_MSTORE8
let offset, value

offset, sp := popStackItem(sp)
value, sp := popStackItem(sp)

let expansionGas := expandMemory(add(offset, 1))

mstore8(add(MEM_OFFSET_INNER(), offset), value)
evmGasLeft := chargeGas(evmGasLeft, add(3, expansionGas))
}
// NOTE: We don't currently do full jumpdest validation
// (i.e. validating a jumpdest isn't in PUSH data)
case 0x56 { // OP_JUMP
Expand Down Expand Up @@ -527,15 +634,54 @@ object "EVMInterpreter" {
revert(0, 0)
}
}
case 0x54 { // OP_SLOAD
let key,value,isWarm

key, sp := popStackItem(sp)

isWarm := isSlotWarm(key)
switch isWarm
case 0 { evmGasLeft := chargeGas(evmGasLeft,2100) }
default { evmGasLeft := chargeGas(evmGasLeft,100) }

value := sload(key)

sp := pushStackItem(sp,value)
}
case 0x55 { // OP_SSTORE
let key, value
let key, value,gasSpent

key, sp := popStackItem(sp)
value, sp := popStackItem(sp)

{
// Here it is okay to read before we charge since we known anyway that
// the context has enough funds to compensate at least for the read.
// Im not sure if we need this before: require(gasLeft > GAS_CALL_STIPEND);
let currentValue := sload(key)
let wasWarm,originalValue := warmSlot(key,currentValue)
gasSpent := 100
if and(not(eq(value,currentValue)),eq(originalValue,currentValue)) {
switch originalValue
case 0 { gasSpent := 20000}
default { gasSpent := 2900}
}
if iszero(wasWarm) {
gasSpent := add(gasSpent,2100)
}
}

evmGasLeft := chargeGas(evmGasLeft, gasSpent) //gasSpent
sstore(key, value)
// TODO: Handle cold/warm slots and updates, etc for gas costs.
evmGasLeft := chargeGas(evmGasLeft, 100)
}
case 0x59 { // OP_MSIZE
let size
evmGasLeft := chargeGas(evmGasLeft,2)

size := mload(MEM_OFFSET())
size := shl(5,size)
sp := pushStackItem(sp,size)

}
case 0x58 { // OP_PC
// PC = ip - 32 (bytecode size) - 1 (current instruction)
Expand Down

0 comments on commit 5ed0a47

Please sign in to comment.