diff --git a/src/runtime/evm_module.cpp b/src/runtime/evm_module.cpp index 673054110..1d8fdfbcf 100644 --- a/src/runtime/evm_module.cpp +++ b/src/runtime/evm_module.cpp @@ -19,6 +19,10 @@ #include "compiler/evm_compiler.h" #endif +#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK +#include "compiler/evm_frontend/evm_analyzer.h" +#endif + namespace zen::runtime { EVMModule::EVMModule(Runtime *RT) @@ -66,7 +70,19 @@ EVMModuleUniquePtr EVMModule::newEVMModule(Runtime &RT, Mod->Host = RT.getEVMHost(); if (RT.getConfig().Mode != common::RunMode::InterpMode) { - action::performEVMJITCompile(*Mod); +#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK + // Run the EVMAnalyzer once at module creation to determine if this + // contract should fall back to interpreter. This avoids per-call O(n) + // bytecode scans in the execute() hot path. + COMPILER::EVMAnalyzer Analyzer(Rev); + Analyzer.analyze(reinterpret_cast(Mod->Code), + Mod->CodeSize); + Mod->ShouldFallbackToInterp = Analyzer.getJITSuitability().ShouldFallback; + if (!Mod->ShouldFallbackToInterp) +#endif // ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK + { + action::performEVMJITCompile(*Mod); + } } return Mod; diff --git a/src/runtime/evm_module.h b/src/runtime/evm_module.h index 43094717e..54374b6b8 100644 --- a/src/runtime/evm_module.h +++ b/src/runtime/evm_module.h @@ -44,6 +44,13 @@ class EVMModule final : public BaseModule { return static_cast(offsetof(EVMModule, CodeSize)); } +#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK + /// Cached result from EVMAnalyzer: true if the contract should fall back + /// to interpreter mode instead of JIT. Set once at module creation to + /// avoid per-call O(n) bytecode scans. + bool ShouldFallbackToInterp = false; +#endif // ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK + #ifdef ZEN_ENABLE_JIT common::CodeMemPool &getJITCodeMemPool() { return JITCodeMemPool; } diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index e686b78ed..fde8d8a32 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -331,6 +331,10 @@ class Runtime final { void callEVMMain(EVMInstance &Inst, evmc_message &Msg, evmc::Result &Result); void callEVMMainOnPhysStack(EVMInstance &Inst, evmc_message &Msg, evmc::Result &Result); +#ifdef ZEN_ENABLE_JIT + void callEVMInJITMode(EVMInstance &Inst, evmc_message &Msg, + evmc::Result &Result); +#endif // ZEN_ENABLE_JIT evmc::Host *getEVMHost() const { return EVMHost; } void setEVMHost(evmc::Host *Host) { EVMHost = Host; } #endif // ZEN_ENABLE_EVM @@ -365,12 +369,6 @@ class Runtime final { void callWasmFunctionInJITMode(Instance &Inst, uint32_t FuncIdx, const std::vector &Args, std::vector &Results); - -#ifdef ZEN_ENABLE_EVM - void callEVMInJITMode(EVMInstance &Inst, evmc_message &Msg, - evmc::Result &Result); -#endif // ZEN_ENABLE_EVM - #endif // ZEN_ENABLE_JIT common::Mutex Mtx; diff --git a/src/vm/dt_evmc_vm.cpp b/src/vm/dt_evmc_vm.cpp index 57f6e4d72..626b4c52a 100644 --- a/src/vm/dt_evmc_vm.cpp +++ b/src/vm/dt_evmc_vm.cpp @@ -19,30 +19,15 @@ #include #include -#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK -#include "compiler/evm_frontend/evm_analyzer.h" -#endif +#ifdef ZEN_ENABLE_VIRTUAL_STACK +#include "utils/virtual_stack.h" +#endif // ZEN_ENABLE_VIRTUAL_STACK namespace { using namespace zen::runtime; using namespace zen::common; -// RAII helper for temporarily changing runtime configuration -class ScopedConfig { -public: - ScopedConfig(Runtime *Runtime, const RuntimeConfig &NewConfig) - : RT(Runtime), PreviousConfig(Runtime->getConfig()) { - RT->setConfig(NewConfig); - } - - ~ScopedConfig() { RT->setConfig(PreviousConfig); } - -private: - Runtime *RT; - RuntimeConfig PreviousConfig; -}; - // Forward declaration for InstanceGuard struct DTVM; @@ -506,48 +491,37 @@ evmc_result executeInterpreterFastPath(DTVM *VM, return Result.release_raw(); } -/// The implementation of the evmc_vm::execute() method. -evmc_result execute(evmc_vm *EVMInstance, const evmc_host_interface *Host, - evmc_host_context *Context, enum evmc_revision Rev, - const evmc_message *Msg, const uint8_t *Code, - size_t CodeSize) { - auto *VM = static_cast(EVMInstance); - - // Interpreter mode: use optimized fast path (bypasses callEVMMain) - if (VM->Config.Mode == RunMode::InterpMode) { - return executeInterpreterFastPath(VM, Host, Context, Rev, Msg, Code, - CodeSize); - } - - // ---- Multipass / other modes: use callEVMMain for JIT execution ---- +#ifdef ZEN_ENABLE_JIT + +#ifdef ZEN_ENABLE_VIRTUAL_STACK +/// Virtual stack callback for JIT fast path: invoked with RSP on the virtual +/// stack. Follows the same pattern as callEVMFuncFromVirtualStack in +/// runtime.cpp. +static void callJITFromVirtualStack(zen::utils::VirtualStackInfo *StackInfo) { + auto *Inst = static_cast(StackInfo->SavedPtr1); + auto *Msg = static_cast(StackInfo->SavedPtr2); + auto *Result = static_cast(StackInfo->SavedPtr3); + Inst->getRuntime()->callEVMInJITMode(*Inst, *Msg, *Result); +} +#endif // ZEN_ENABLE_VIRTUAL_STACK + +/// Fast path for multipass JIT mode: reuse cached instance, call JIT code +/// directly. This avoids per-call callEVMMain overhead while delegating +/// actual JIT execution to Runtime::callEVMInJITMode (single source of truth +/// for CPU exception handling, error mapping, etc.). +evmc_result executeMultipassFastPath(DTVM *VM, const evmc_host_interface *Host, + evmc_host_context *Context, + evmc_revision Rev, const evmc_message *Msg, + const uint8_t *Code, size_t CodeSize) { // RAII guard for host context save/restore (exception safety) HostContextScope HostScope(VM->ExecHost.get(), Host, Context); + // Ensure runtime and isolation exist if (!ensureRuntimeAndIsolation(VM)) { return evmc_make_result(EVMC_FAILURE, 0, 0, nullptr, 0); } -#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK - // Use interpreter mode for bytecode that would be too expensive to JIT. - // The EVMAnalyzer performs a pattern-aware O(n) scan that detects: - // - raw bytecode size / estimated MIR instruction count too large - // - high density of RA-expensive opcodes (SHL/SHR/SAR/MUL/SIGNEXTEND) - // - long consecutive runs of RA-expensive ops - // - DUP-induced feedback loops (b0 pattern) - std::unique_ptr TempConfig; - if (VM->Config.Mode == RunMode::MultipassMode) { - COMPILER::EVMAnalyzer Analyzer(Rev); - Analyzer.analyze(Code, CodeSize); - const auto &JITResult = Analyzer.getJITSuitability(); - if (JITResult.ShouldFallback) { - RuntimeConfig NewConfig = VM->Config; - NewConfig.Mode = RunMode::InterpMode; - TempConfig = std::make_unique(VM->RT.get(), NewConfig); - } - } -#endif // ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK - - // Module lookup: L0 -> L1 -> Cold (shared with interpreter path) + // Module lookup: L1 address-based cache -> Cold load bool IsTransientMod = false; EVMModule *Mod = findModuleCached(VM, Code, CodeSize, Rev, Msg, IsTransientMod); @@ -556,19 +530,74 @@ evmc_result execute(evmc_vm *EVMInstance, const evmc_host_interface *Host, } ModuleGuard ModGuard(VM, Mod, IsTransientMod); +#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK + // O(1) flag check replaces per-call O(n) EVMAnalyzer scan. + // The flag was set once at module creation in EVMModule::newEVMModule(). + if (Mod->ShouldFallbackToInterp) { + return executeInterpreterFastPath(VM, Host, Context, Rev, Msg, Code, + CodeSize); + } +#endif // ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK + // Instance reuse (shared only for cacheable top-level calls) - auto *TheInst = getOrCreateInstance(VM, Mod, Rev, Msg->depth); + EVMInstance *TheInst = getOrCreateInstance(VM, Mod, Rev, Msg->depth); if (!TheInst) { return evmc_make_result(EVMC_FAILURE, 0, 0, nullptr, 0); } - // Execute via callEVMMain (handles both JIT and interpreter fallback) - evmc_message Message = *Msg; + // Setup message with code pointers (same pattern as interpreter fast path) + evmc_message MsgWithCode = *Msg; + MsgWithCode.code = reinterpret_cast(Mod->Code); + MsgWithCode.code_size = Mod->CodeSize; + TheInst->setExeResult(evmc::Result{EVMC_SUCCESS, 0, 0}); + TheInst->pushMessage(&MsgWithCode); + evmc::Result Result; - VM->RT->callEVMMain(*TheInst, Message, Result); +#ifdef ZEN_ENABLE_VIRTUAL_STACK + if (Msg->depth == 0) { + // depth==0: set up virtual stack for stack overflow protection via guard + // pages. The virtual stack switches RSP to a separate mmap'd region. + zen::utils::VirtualStackInfo StackInfo; + StackInfo.SavedPtr1 = TheInst; + StackInfo.SavedPtr2 = &MsgWithCode; + StackInfo.SavedPtr3 = &Result; + TheInst->pushVirtualStack(&StackInfo); + StackInfo.runInVirtualStack(&callJITFromVirtualStack); + TheInst->popVirtualStack(); + } else { + // depth>0: re-entered via EVMC host callback, already on physical stack + VM->RT->callEVMInJITMode(*TheInst, MsgWithCode, Result); + } +#else + VM->RT->callEVMInJITMode(*TheInst, MsgWithCode, Result); +#endif // ZEN_ENABLE_VIRTUAL_STACK + + Result.gas_left = TheInst->getGas(); return Result.release_raw(); } +#endif // ZEN_ENABLE_JIT + +/// The implementation of the evmc_vm::execute() method. +evmc_result execute(evmc_vm *EVMInstance, const evmc_host_interface *Host, + evmc_host_context *Context, enum evmc_revision Rev, + const evmc_message *Msg, const uint8_t *Code, + size_t CodeSize) { + auto *VM = static_cast(EVMInstance); + + // Interpreter mode: use optimized fast path (bypasses callEVMMain) + if (VM->Config.Mode == RunMode::InterpMode) { + return executeInterpreterFastPath(VM, Host, Context, Rev, Msg, Code, + CodeSize); + } + +#ifdef ZEN_ENABLE_JIT + // JIT mode: use optimized fast path (bypasses callEVMMain/virtual stack) + return executeMultipassFastPath(VM, Host, Context, Rev, Msg, Code, CodeSize); +#else + return evmc_make_result(EVMC_FAILURE, 0, 0, nullptr, 0); +#endif // ZEN_ENABLE_JIT +} /// @cond internal #if !defined(PROJECT_VERSION)