Skip to content
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
104 changes: 77 additions & 27 deletions src/compiler/evm_frontend/evm_imported.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,15 @@ const RuntimeFunctions &getRuntimeFunctionTable() {
.HandleCreate = &evmHandleCreate,
.HandleCreate2 = &evmHandleCreate2,
.HandleCall = &evmHandleCall,
.HandleCallNoExpand = &evmHandleCallNoExpand,
.HandleCallCode = &evmHandleCallCode,
.HandleCallCodeNoExpand = &evmHandleCallCodeNoExpand,
.SetReturn = &evmSetReturn,
.SetReturnNoExpand = &evmSetReturnNoExpand,
.HandleDelegateCall = &evmHandleDelegateCall,
.HandleDelegateCallNoExpand = &evmHandleDelegateCallNoExpand,
.HandleStaticCall = &evmHandleStaticCall,
.HandleStaticCallNoExpand = &evmHandleStaticCallNoExpand,
.SetRevert = &evmSetRevert,
.SetRevertNoExpand = &evmSetRevertNoExpand,
.HandleInvalid = &evmHandleInvalid,
Expand Down Expand Up @@ -1088,10 +1092,13 @@ const uint8_t *evmHandleCreate2(zen::runtime::EVMInstance *Instance,
}

// Helper function for all call types
static uint64_t evmHandleCallInternal(
zen::runtime::EVMInstance *Instance, evmc_call_kind CallKind, uint64_t Gas,
const uint8_t *ToAddr, const intx::uint256 &Value, uint64_t ArgsOffset,
uint64_t ArgsSize, uint64_t RetOffset, uint64_t RetSize, bool ForceStatic) {
static uint64_t evmHandleCallInternal(zen::runtime::EVMInstance *Instance,
evmc_call_kind CallKind, uint64_t Gas,
const uint8_t *ToAddr,
const intx::uint256 &Value,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize,
bool ForceStatic, bool MemoryPrepared) {
const zen::runtime::EVMModule *Module = Instance->getModule();
ZEN_ASSERT(Module && Module->Host);

Expand Down Expand Up @@ -1122,25 +1129,28 @@ static uint64_t evmHandleCallInternal(
return 0;
}

// Calculate required memory sizes for input and output
// Only expand memory if we actually need to access it
bool needArgsMemory = ArgsSize > 0;
bool needRetMemory = RetSize > 0;
if (needArgsMemory && needRetMemory) {
if (!Instance->expandMemoryChecked(ArgsOffset, ArgsSize, RetOffset,
RetSize)) {
Instance->setReturnData({});
return 0;
}
} else if (needArgsMemory) {
if (!Instance->expandMemoryChecked(ArgsOffset, ArgsSize)) {
Instance->setReturnData({});
return 0;
}
} else if (needRetMemory) {
if (!Instance->expandMemoryChecked(RetOffset, RetSize)) {
Instance->setReturnData({});
return 0;
// No-expand callers must prove both non-empty ranges before entering this
// helper. Account access, static-mode checks, and all call gas handling stay
// in their original order.
if (!MemoryPrepared) {
const bool NeedArgsMemory = ArgsSize > 0;
const bool NeedRetMemory = RetSize > 0;
if (NeedArgsMemory && NeedRetMemory) {
if (!Instance->expandMemoryChecked(ArgsOffset, ArgsSize, RetOffset,
RetSize)) {
Instance->setReturnData({});
return 0;
}
} else if (NeedArgsMemory) {
if (!Instance->expandMemoryChecked(ArgsOffset, ArgsSize)) {
Instance->setReturnData({});
return 0;
}
} else if (NeedRetMemory) {
if (!Instance->expandMemoryChecked(RetOffset, RetSize)) {
Instance->setReturnData({});
return 0;
}
}
}

Expand Down Expand Up @@ -1271,15 +1281,37 @@ uint64_t evmHandleCall(zen::runtime::EVMInstance *Instance, uint64_t Gas,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize) {
return evmHandleCallInternal(Instance, EVMC_CALL, Gas, ToAddr, Value,
ArgsOffset, ArgsSize, RetOffset, RetSize, false);
ArgsOffset, ArgsSize, RetOffset, RetSize, false,
false);
}

uint64_t evmHandleCallNoExpand(zen::runtime::EVMInstance *Instance,
uint64_t Gas, const uint8_t *ToAddr,
const intx::uint256 &Value, uint64_t ArgsOffset,
uint64_t ArgsSize, uint64_t RetOffset,
uint64_t RetSize) {
return evmHandleCallInternal(Instance, EVMC_CALL, Gas, ToAddr, Value,
ArgsOffset, ArgsSize, RetOffset, RetSize, false,
true);
}

uint64_t evmHandleCallCode(zen::runtime::EVMInstance *Instance, uint64_t Gas,
const uint8_t *ToAddr, const intx::uint256 &Value,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize) {
return evmHandleCallInternal(Instance, EVMC_CALLCODE, Gas, ToAddr, Value,
ArgsOffset, ArgsSize, RetOffset, RetSize, false);
ArgsOffset, ArgsSize, RetOffset, RetSize, false,
false);
}

uint64_t evmHandleCallCodeNoExpand(zen::runtime::EVMInstance *Instance,
uint64_t Gas, const uint8_t *ToAddr,
const intx::uint256 &Value,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize) {
return evmHandleCallInternal(Instance, EVMC_CALLCODE, Gas, ToAddr, Value,
ArgsOffset, ArgsSize, RetOffset, RetSize, false,
true);
}

void evmHandleInvalid(zen::runtime::EVMInstance *Instance) {
Expand Down Expand Up @@ -1312,7 +1344,16 @@ uint64_t evmHandleDelegateCall(zen::runtime::EVMInstance *Instance,
uint64_t RetOffset, uint64_t RetSize) {
return evmHandleCallInternal(Instance, EVMC_DELEGATECALL, Gas, ToAddr,
intx::uint256{0}, ArgsOffset, ArgsSize,
RetOffset, RetSize, false);
RetOffset, RetSize, false, false);
}

uint64_t evmHandleDelegateCallNoExpand(zen::runtime::EVMInstance *Instance,
uint64_t Gas, const uint8_t *ToAddr,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize) {
return evmHandleCallInternal(Instance, EVMC_DELEGATECALL, Gas, ToAddr,
intx::uint256{0}, ArgsOffset, ArgsSize,
RetOffset, RetSize, false, true);
}

// EVMC spec: STATICCALL uses kind=EVMC_CALL + flags=EVMC_STATIC
Expand All @@ -1324,7 +1365,16 @@ uint64_t evmHandleStaticCall(zen::runtime::EVMInstance *Instance, uint64_t Gas,
uint64_t RetSize) {
return evmHandleCallInternal(Instance, EVMC_CALL, Gas, ToAddr,
intx::uint256{0}, ArgsOffset, ArgsSize,
RetOffset, RetSize, true);
RetOffset, RetSize, true, false);
}

uint64_t evmHandleStaticCallNoExpand(zen::runtime::EVMInstance *Instance,
uint64_t Gas, const uint8_t *ToAddr,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize) {
return evmHandleCallInternal(Instance, EVMC_CALL, Gas, ToAddr,
intx::uint256{0}, ArgsOffset, ArgsSize,
RetOffset, RetSize, true, true);
}

void evmSetRevert(zen::runtime::EVMInstance *Instance, uint64_t Offset,
Expand Down
22 changes: 22 additions & 0 deletions src/compiler/evm_frontend/evm_imported.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,15 @@ struct RuntimeFunctions {
CreateFn HandleCreate;
Create2Fn HandleCreate2;
CallFn HandleCall;
CallFn HandleCallNoExpand;
CallFn HandleCallCode;
CallFn HandleCallCodeNoExpand;
VoidWithUInt64UInt64Fn SetReturn;
VoidWithUInt64UInt64Fn SetReturnNoExpand;
DelegateCallFn HandleDelegateCall;
DelegateCallFn HandleDelegateCallNoExpand;
DelegateCallFn HandleStaticCall;
DelegateCallFn HandleStaticCallNoExpand;
VoidWithUInt64UInt64Fn SetRevert;
VoidWithUInt64UInt64Fn SetRevertNoExpand;
VoidFn HandleInvalid;
Expand Down Expand Up @@ -285,10 +289,20 @@ uint64_t evmHandleCall(zen::runtime::EVMInstance *Instance, uint64_t Gas,
const uint8_t *ToAddr, const intx::uint256 &Value,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize);
uint64_t evmHandleCallNoExpand(zen::runtime::EVMInstance *Instance,
uint64_t Gas, const uint8_t *ToAddr,
const intx::uint256 &Value, uint64_t ArgsOffset,
uint64_t ArgsSize, uint64_t RetOffset,
uint64_t RetSize);
uint64_t evmHandleCallCode(zen::runtime::EVMInstance *Instance, uint64_t Gas,
const uint8_t *ToAddr, const intx::uint256 &Value,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize);
uint64_t evmHandleCallCodeNoExpand(zen::runtime::EVMInstance *Instance,
uint64_t Gas, const uint8_t *ToAddr,
const intx::uint256 &Value,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize);
void evmSetReturn(zen::runtime::EVMInstance *Instance, uint64_t MemOffset,
uint64_t Length);
void evmSetReturnNoExpand(zen::runtime::EVMInstance *Instance,
Expand All @@ -297,10 +311,18 @@ uint64_t evmHandleDelegateCall(zen::runtime::EVMInstance *Instance,
uint64_t Gas, const uint8_t *ToAddr,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize);
uint64_t evmHandleDelegateCallNoExpand(zen::runtime::EVMInstance *Instance,
uint64_t Gas, const uint8_t *ToAddr,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize);
uint64_t evmHandleStaticCall(zen::runtime::EVMInstance *Instance, uint64_t Gas,
const uint8_t *ToAddr, uint64_t ArgsOffset,
uint64_t ArgsSize, uint64_t RetOffset,
uint64_t RetSize);
uint64_t evmHandleStaticCallNoExpand(zen::runtime::EVMInstance *Instance,
uint64_t Gas, const uint8_t *ToAddr,
uint64_t ArgsOffset, uint64_t ArgsSize,
uint64_t RetOffset, uint64_t RetSize);
void evmSetRevert(zen::runtime::EVMInstance *Instance, uint64_t Offset,
uint64_t Size);
void evmSetRevertNoExpand(zen::runtime::EVMInstance *Instance, uint64_t Offset,
Expand Down
56 changes: 48 additions & 8 deletions src/compiler/evm_frontend/evm_mir_compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5594,11 +5594,39 @@ typename EVMMirBuilder::Operand EVMMirBuilder::handleCreate2(Operand ValueOp,
return Result;
}

bool EVMMirBuilder::canUseGuaranteedCallMemoryRanges(const Operand &ArgsOffset,
const Operand &ArgsSize,
const Operand &RetOffset,
const Operand &RetSize) {
const auto IsCovered = [this](const Operand &Offset, const Operand &Size) {
if (Size.isZeroConstant()) {
return true;
}
if (!Offset.isConstU64() || !Size.isConstU64()) {
return false;
}
return tryUseGuaranteedMinBytesExpansionElision(
true, Offset.getConstValue()[0], Size.getConstValue()[0]);
};

const bool CanReuse =
IsCovered(ArgsOffset, ArgsSize) && IsCovered(RetOffset, RetSize);
#ifdef ZEN_ENABLE_MULTIPASS_JIT_LOGGING
if (CanReuse) {
ZEN_LOG_DEBUG("[EVM-CALL-MEMORY-PROOF] pc=%llu",
static_cast<unsigned long long>(CurrentMemoryOpPC));
}
#endif
return CanReuse;
}

typename EVMMirBuilder::Operand
EVMMirBuilder::handleCall(Operand GasOp, Operand ToAddrOp, Operand ValueOp,
Operand ArgsOffsetOp, Operand ArgsSizeOp,
Operand RetOffsetOp, Operand RetSizeOp) {
const auto &RuntimeFunctions = getRuntimeFunctionTable();
const bool UsePreparedMemory = canUseGuaranteedCallMemoryRanges(
ArgsOffsetOp, ArgsSizeOp, RetOffsetOp, RetSizeOp);
// When gas value exceeds 64 bits, use max uint64 as fallback.
// The runtime will cap it to available gas per EIP-150.
uint64_t Non64Value = std::numeric_limits<uint64_t>::max();
Expand All @@ -5613,8 +5641,10 @@ EVMMirBuilder::handleCall(Operand GasOp, Operand ToAddrOp, Operand ValueOp,
callRuntimeForWithErrorCheck<uint64_t, uint64_t, const uint8_t *,
const intx::uint256 &, uint64_t, uint64_t,
uint64_t, uint64_t>(
RuntimeFunctions.HandleCall, GasOp, ToAddrOp, ValueOp, ArgsOffsetOp,
ArgsSizeOp, RetOffsetOp, RetSizeOp);
UsePreparedMemory ? RuntimeFunctions.HandleCallNoExpand
: RuntimeFunctions.HandleCall,
GasOp, ToAddrOp, ValueOp, ArgsOffsetOp, ArgsSizeOp, RetOffsetOp,
RetSizeOp);
#ifdef ZEN_ENABLE_EVM_GAS_REGISTER
reloadGasFromMemory();
#endif
Expand All @@ -5627,6 +5657,8 @@ EVMMirBuilder::handleCallCode(Operand GasOp, Operand ToAddrOp, Operand ValueOp,
Operand ArgsOffsetOp, Operand ArgsSizeOp,
Operand RetOffsetOp, Operand RetSizeOp) {
const auto &RuntimeFunctions = getRuntimeFunctionTable();
const bool UsePreparedMemory = canUseGuaranteedCallMemoryRanges(
ArgsOffsetOp, ArgsSizeOp, RetOffsetOp, RetSizeOp);
// When gas value exceeds 64 bits, use max uint64 as fallback.
// The runtime will cap it to available gas per EIP-150.
uint64_t Non64Value = std::numeric_limits<uint64_t>::max();
Expand All @@ -5641,8 +5673,10 @@ EVMMirBuilder::handleCallCode(Operand GasOp, Operand ToAddrOp, Operand ValueOp,
callRuntimeForWithErrorCheck<uint64_t, uint64_t, const uint8_t *,
const intx::uint256 &, uint64_t, uint64_t,
uint64_t, uint64_t>(
RuntimeFunctions.HandleCallCode, GasOp, ToAddrOp, ValueOp,
ArgsOffsetOp, ArgsSizeOp, RetOffsetOp, RetSizeOp);
UsePreparedMemory ? RuntimeFunctions.HandleCallCodeNoExpand
: RuntimeFunctions.HandleCallCode,
GasOp, ToAddrOp, ValueOp, ArgsOffsetOp, ArgsSizeOp, RetOffsetOp,
RetSizeOp);
#ifdef ZEN_ENABLE_EVM_GAS_REGISTER
reloadGasFromMemory();
#endif
Expand Down Expand Up @@ -5678,6 +5712,8 @@ EVMMirBuilder::handleDelegateCall(Operand GasOp, Operand ToAddrOp,
Operand ArgsOffsetOp, Operand ArgsSizeOp,
Operand RetOffsetOp, Operand RetSizeOp) {
const auto &RuntimeFunctions = getRuntimeFunctionTable();
const bool UsePreparedMemory = canUseGuaranteedCallMemoryRanges(
ArgsOffsetOp, ArgsSizeOp, RetOffsetOp, RetSizeOp);
// When gas value exceeds 64 bits, use max uint64 as fallback.
// The runtime will cap it to available gas per EIP-150.
uint64_t Non64Value = std::numeric_limits<uint64_t>::max();
Expand All @@ -5691,8 +5727,9 @@ EVMMirBuilder::handleDelegateCall(Operand GasOp, Operand ToAddrOp,
auto Result =
callRuntimeForWithErrorCheck<uint64_t, uint64_t, const uint8_t *,
uint64_t, uint64_t, uint64_t, uint64_t>(
RuntimeFunctions.HandleDelegateCall, GasOp, ToAddrOp, ArgsOffsetOp,
ArgsSizeOp, RetOffsetOp, RetSizeOp);
UsePreparedMemory ? RuntimeFunctions.HandleDelegateCallNoExpand
: RuntimeFunctions.HandleDelegateCall,
GasOp, ToAddrOp, ArgsOffsetOp, ArgsSizeOp, RetOffsetOp, RetSizeOp);
#ifdef ZEN_ENABLE_EVM_GAS_REGISTER
reloadGasFromMemory();
#endif
Expand All @@ -5705,6 +5742,8 @@ EVMMirBuilder::handleStaticCall(Operand GasOp, Operand ToAddrOp,
Operand ArgsOffsetOp, Operand ArgsSizeOp,
Operand RetOffsetOp, Operand RetSizeOp) {
const auto &RuntimeFunctions = getRuntimeFunctionTable();
const bool UsePreparedMemory = canUseGuaranteedCallMemoryRanges(
ArgsOffsetOp, ArgsSizeOp, RetOffsetOp, RetSizeOp);
// When gas value exceeds 64 bits, use max uint64 as fallback.
// The runtime will cap it to available gas per EIP-150.
uint64_t Non64Value = std::numeric_limits<uint64_t>::max();
Expand All @@ -5718,8 +5757,9 @@ EVMMirBuilder::handleStaticCall(Operand GasOp, Operand ToAddrOp,
auto Result =
callRuntimeForWithErrorCheck<uint64_t, uint64_t, const uint8_t *,
uint64_t, uint64_t, uint64_t, uint64_t>(
RuntimeFunctions.HandleStaticCall, GasOp, ToAddrOp, ArgsOffsetOp,
ArgsSizeOp, RetOffsetOp, RetSizeOp);
UsePreparedMemory ? RuntimeFunctions.HandleStaticCallNoExpand
: RuntimeFunctions.HandleStaticCall,
GasOp, ToAddrOp, ArgsOffsetOp, ArgsSizeOp, RetOffsetOp, RetSizeOp);
#ifdef ZEN_ENABLE_EVM_GAS_REGISTER
reloadGasFromMemory();
#endif
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/evm_frontend/evm_mir_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1851,6 +1851,10 @@ class EVMMirBuilder final {
bool tryUseGuaranteedMinBytesExpansionElision(bool OffsetWasConst,
uint64_t ConstOffset,
uint64_t AccessSize);
bool canUseGuaranteedCallMemoryRanges(const Operand &ArgsOffset,
const Operand &ArgsSize,
const Operand &RetOffset,
const Operand &RetSize);
void applyMemoryExpansionPlan(const MemoryExpansionPlan &Plan);
void noteMemoryExpansionPlan(const MemoryExpansionPlan &Plan);
void noteMemoryExpansionPlanDiagnostics(
Expand Down
1 change: 1 addition & 0 deletions src/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ if(ZEN_ENABLE_SPEC_TEST)
add_executable(evmJitFrontendTests evm_jit_frontend_tests.cpp)
add_executable(evmRangeAnalyzerTests evm_range_analyzer_tests.cpp)
add_executable(evmDifferentialTests evm_differential_tests.cpp)
target_sources(evmJitFrontendTests PRIVATE evm_call_memory_tests.cpp)
add_executable(
evmMemoryExecuteOnlyBench evm_memory_execute_only_bench.cpp
)
Expand Down
Loading
Loading