-
Notifications
You must be signed in to change notification settings - Fork 25
perf(evm): add constant-shift fast paths #388
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,11 +4,14 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "compiler/evm_frontend/evm_mir_compiler.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "action/evm_bytecode_visitor.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "compiler/evm_frontend/evm_imported.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "compiler/mir/constants.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "compiler/mir/module.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "evm/gas_storage_cost.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "runtime/evm_instance.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "utils/hash_utils.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "llvm/Support/Casting.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <cstring> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <optional> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #ifdef ZEN_ENABLE_EVM_GAS_REGISTER | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "compiler/llvm-prebuild/Target/X86/X86Subtarget.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1876,6 +1879,18 @@ EVMMirBuilder::handleClz(const Operand &ValueOp) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RuntimeFunctions.GetClz, ValueOp); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Extract constant shift amount from MInstruction if it is a constant. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::optional<uint64_t> getConstShiftAmount(MInstruction *Inst) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (auto *CI = llvm::dyn_cast<ConstantInstruction>(Inst)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (auto *IntConst = llvm::dyn_cast<MConstantInt>(&CI->getConstant())) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return IntConst->getValue().getZExtValue(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return std::nullopt; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } // namespace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| EVMMirBuilder::U256Inst | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| EVMMirBuilder::handleLeftShift(const U256Inst &Value, MInstruction *ShiftAmount, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MInstruction *IsLargeShift) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1884,6 +1899,41 @@ EVMMirBuilder::handleLeftShift(const U256Inst &Value, MInstruction *ShiftAmount, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| U256Inst Result = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MInstruction *Zero = createIntConstInstruction(MirI64Type, 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fast path: constant shift amount — direct limb logic, no Select/cmp loops. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (auto ShiftOpt = getConstShiftAmount(ShiftAmount)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uint64_t Shift = *ShiftOpt; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (Shift >= 256) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Result[I] = Zero; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uint64_t CompShift = Shift / 64; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uint64_t ShiftMod = Shift % 64; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| uint64_t RemainingBits = (ShiftMod == 0) ? 0 : (64 - ShiftMod); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MInstruction *R = Zero; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (I >= CompShift) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size_t SrcIdx = I - CompShift; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MInstruction *SrcVal = Value[SrcIdx]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MInstruction *Shifted = createInstruction<BinaryInstruction>( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| false, OP_shl, MirI64Type, SrcVal, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createIntConstInstruction(MirI64Type, ShiftMod)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (SrcIdx > 0 && RemainingBits > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MInstruction *Carry = createInstruction<BinaryInstruction>( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| false, OP_ushr, MirI64Type, Value[SrcIdx - 1], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createIntConstInstruction(MirI64Type, RemainingBits)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| R = createInstruction<BinaryInstruction>(false, OP_or, MirI64Type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Shifted, Carry); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| R = Shifted; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Result[I] = protectUnsafeValue(R, MirI64Type); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1903
to
+1935
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fast path: constant shift amount — direct limb logic, no Select/cmp loops. | |
| if (auto ShiftOpt = getConstShiftAmount(ShiftAmount)) { | |
| uint64_t Shift = *ShiftOpt; | |
| if (Shift >= 256) { | |
| for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) | |
| Result[I] = Zero; | |
| return Result; | |
| } | |
| uint64_t CompShift = Shift / 64; | |
| uint64_t ShiftMod = Shift % 64; | |
| uint64_t RemainingBits = (ShiftMod == 0) ? 0 : (64 - ShiftMod); | |
| for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { | |
| MInstruction *R = Zero; | |
| if (I >= CompShift) { | |
| size_t SrcIdx = I - CompShift; | |
| MInstruction *SrcVal = Value[SrcIdx]; | |
| MInstruction *Shifted = createInstruction<BinaryInstruction>( | |
| false, OP_shl, MirI64Type, SrcVal, | |
| createIntConstInstruction(MirI64Type, ShiftMod)); | |
| if (SrcIdx > 0 && RemainingBits > 0) { | |
| MInstruction *Carry = createInstruction<BinaryInstruction>( | |
| false, OP_ushr, MirI64Type, Value[SrcIdx - 1], | |
| createIntConstInstruction(MirI64Type, RemainingBits)); | |
| R = createInstruction<BinaryInstruction>(false, OP_or, MirI64Type, | |
| Shifted, Carry); | |
| } else { | |
| R = Shifted; | |
| } | |
| } | |
| Result[I] = protectUnsafeValue(R, MirI64Type); | |
| } | |
| return Result; | |
| } | |
| // Note: We deliberately avoid a constant-shift fast path here because | |
| // deriving the 256-bit shift solely from the low 64-bit ShiftAmount | |
| // can bypass the IsLargeShift guard and break EVM semantics when any | |
| // high limb of the shift value is non-zero. All shifts are handled | |
| // by the generic implementation below, which correctly applies | |
| // IsLargeShift to enforce zeroing for large shifts. |
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as SHL fast path: the constant-shift path uses only ShiftAmount (low 64 bits) and ignores IsLargeShift, so shifts with any non-zero high limb but small low limb will incorrectly behave like a small shift instead of producing 0 per EVM spec. The fast path should still incorporate IsLargeShift (e.g., per-limb select to 0) to preserve correctness for large 256-bit shift values.
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the constant-SHR fast path, the shift amounts are materialized with createIntConstInstruction inside the per-limb loop, which produces duplicate OP_const instructions for ShiftMod/CarryShift. Hoist these constants outside the loop (and when ShiftMod == 0, avoid generating the shift/or at all by directly using the source limb) to maximize the intended perf win and reduce MIR bloat.
| for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { | |
| MInstruction *R = Zero; | |
| if (I + CompShift < EVM_ELEMENTS_COUNT) { | |
| size_t SrcIdx = I + CompShift; | |
| MInstruction *SrcVal = Value[SrcIdx]; | |
| MInstruction *Shifted = createInstruction<BinaryInstruction>( | |
| false, OP_ushr, MirI64Type, SrcVal, | |
| createIntConstInstruction(MirI64Type, ShiftMod)); | |
| if (SrcIdx + 1 < EVM_ELEMENTS_COUNT && CarryShift > 0) { | |
| MInstruction *Carry = createInstruction<BinaryInstruction>( | |
| false, OP_shl, MirI64Type, Value[SrcIdx + 1], | |
| createIntConstInstruction(MirI64Type, CarryShift)); | |
| // If the shift is a multiple of 64, we only need to move whole limbs. | |
| if (ShiftMod == 0) { | |
| for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { | |
| MInstruction *R = Zero; | |
| if (I + CompShift < EVM_ELEMENTS_COUNT) { | |
| size_t SrcIdx = I + CompShift; | |
| R = Value[SrcIdx]; | |
| } | |
| Result[I] = protectUnsafeValue(R, MirI64Type); | |
| } | |
| return Result; | |
| } | |
| // Hoist loop-invariant shift constants out of the limb loop. | |
| MInstruction *ShiftModConst = | |
| createIntConstInstruction(MirI64Type, ShiftMod); | |
| MInstruction *CarryShiftConst = nullptr; | |
| if (CarryShift > 0) | |
| CarryShiftConst = createIntConstInstruction(MirI64Type, CarryShift); | |
| for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { | |
| MInstruction *R = Zero; | |
| if (I + CompShift < EVM_ELEMENTS_COUNT) { | |
| size_t SrcIdx = I + CompShift; | |
| MInstruction *SrcVal = Value[SrcIdx]; | |
| MInstruction *Shifted = createInstruction<BinaryInstruction>( | |
| false, OP_ushr, MirI64Type, SrcVal, ShiftModConst); | |
| if (SrcIdx + 1 < EVM_ELEMENTS_COUNT && CarryShiftConst != nullptr) { | |
| MInstruction *Carry = createInstruction<BinaryInstruction>( | |
| false, OP_shl, MirI64Type, Value[SrcIdx + 1], CarryShiftConst); |
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SAR constant-shift fast path ignores IsLargeShift and only checks Shift >= 256 based on the low 64 bits. This is incorrect for 256-bit shift values with high limbs set but Shift[0] < 256 (EVM requires full sign-extension result when shift >= 256). Make the fast path depend on IsLargeShift (e.g., select LargeShiftResult vs computed limb result) or lift constant-shift evaluation to handleShift() where the full 256-bit shift is known.
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the constant-SAR fast path, createIntConstInstruction(..., ShiftMod) / createIntConstInstruction(..., CarryShift) are created inside the loop even though they are loop-invariant, adding extra OP_const nodes. Hoist the constants outside the loop (and if ShiftMod == 0, avoid emitting a redundant shift-by-0 instruction) so this path stays minimal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the constant-SHL fast path,
createIntConstInstruction(..., ShiftMod)andcreateIntConstInstruction(..., RemainingBits)are called inside the loop, creating a new OP_const each iteration even though the value is invariant. Hoist these constant instructions outside the loop (and consider skipping the shift/or entirely whenShiftMod == 0by directly using the source limb) to keep the fast path from inflating MIR instruction count.