From 2df40fc117d326a8661741e109c5bc9a804324b4 Mon Sep 17 00:00:00 2001 From: abmcar <52450271+abmcar@users.noreply.github.com> Date: Thu, 23 Jul 2026 22:29:39 +0800 Subject: [PATCH 1/4] perf(compiler): use indexed MIR block membership Replace the EVM MIR builder linear membership scan with MFunction indexed pointer identity lookup. Add focused tests for membership and entry block reuse. --- src/compiler/evm_frontend/evm_mir_compiler.h | 4 +-- src/compiler/mir/function.h | 6 +++++ src/tests/evm_jit_frontend_tests.cpp | 26 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/compiler/evm_frontend/evm_mir_compiler.h b/src/compiler/evm_frontend/evm_mir_compiler.h index c6161384e..4da6a349a 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.h +++ b/src/compiler/evm_frontend/evm_mir_compiler.h @@ -1143,9 +1143,7 @@ class EVMMirBuilder final { void setInsertBlock(MBasicBlock *BB) { CurBB = BB; - // Check if this basic block is already in the function's BasicBlocks list - // to avoid duplicate insertion - if (std::find(CurFunc->begin(), CurFunc->end(), BB) == CurFunc->end()) { + if (!CurFunc->containsBasicBlock(BB)) { CurFunc->appendBlock(BB); } } diff --git a/src/compiler/mir/function.h b/src/compiler/mir/function.h index a25d07efe..d544be01e 100644 --- a/src/compiler/mir/function.h +++ b/src/compiler/mir/function.h @@ -84,6 +84,12 @@ class MFunction : public ContextObject { BasicBlocks.emplace_back(BB); } + bool containsBasicBlock(const MBasicBlock *BB) const { + ZEN_ASSERT(BB); + uint32_t BBIdx = BB->getIdx(); + return BBIdx < BasicBlocks.size() && BasicBlocks[BBIdx] == BB; + } + MBasicBlock *getBasicBlock(uint32_t BBIdx) const { ZEN_ASSERT(BBIdx < BasicBlocks.size()); return BasicBlocks[BBIdx]; diff --git a/src/tests/evm_jit_frontend_tests.cpp b/src/tests/evm_jit_frontend_tests.cpp index 0d20ca369..123167f12 100644 --- a/src/tests/evm_jit_frontend_tests.cpp +++ b/src/tests/evm_jit_frontend_tests.cpp @@ -68,6 +68,32 @@ class MirBuilderConstFoldHarness { EVMMirBuilder Builder; }; +TEST(MFunctionBasicBlockTest, MembershipUsesIndexAndPointerIdentity) { + COMPILER::EVMFrontendContext Ctx; + COMPILER::MFunction Func(Ctx, 0); + COMPILER::MBasicBlock *First = Func.createBasicBlock(); + COMPILER::MBasicBlock *Second = Func.createBasicBlock(); + + EXPECT_FALSE(Func.containsBasicBlock(First)); + Func.appendBlock(First); + EXPECT_TRUE(Func.containsBasicBlock(First)); + + EXPECT_EQ(Second->getIdx(), 0U); + EXPECT_FALSE(Func.containsBasicBlock(Second)); + Func.appendBlock(Second); + EXPECT_TRUE(Func.containsBasicBlock(Second)); + + EXPECT_EQ(Func.getNumBasicBlocks(), 2U); + EXPECT_EQ(Func.getBasicBlock(First->getIdx()), First); + EXPECT_EQ(Func.getBasicBlock(Second->getIdx()), Second); +} + +TEST(EVMMirBuilderBasicBlockTest, InitDoesNotReappendEntryBlock) { + MirBuilderConstFoldHarness Harness; + + EXPECT_EQ(Harness.Func.getNumBasicBlocks(), 1U); +} + void expectPCList(const std::vector &Actual, std::initializer_list Expected) { ASSERT_EQ(Actual.size(), Expected.size()); From 68ed337749225eb1fd08f89fa104ea02868b22e6 Mon Sep 17 00:00:00 2001 From: abmcar <52450271+abmcar@users.noreply.github.com> Date: Thu, 23 Jul 2026 22:30:18 +0800 Subject: [PATCH 2/4] perf(compiler): scan CGIR uses from the chain tail Short-circuit dead-instruction use checks by walking the use suffix backward instead of traversing the full def-use chain. Preserve self-use semantics and cover the tail instruction explicitly. --- src/compiler/cgir/pass/cg_register_info.h | 18 +++ .../cgir/pass/dead_cg_instruction_elim.cpp | 9 +- src/tests/evm_jit_frontend_tests.cpp | 110 ++++++++++++++++++ 3 files changed, 131 insertions(+), 6 deletions(-) diff --git a/src/compiler/cgir/pass/cg_register_info.h b/src/compiler/cgir/pass/cg_register_info.h index 839406207..2a06980d4 100644 --- a/src/compiler/cgir/pass/cg_register_info.h +++ b/src/compiler/cgir/pass/cg_register_info.h @@ -334,6 +334,24 @@ class CgRegisterInfo : public NonCopyable { return use_nodbg_begin(RegNo) == use_nodbg_end(); } + // The use-def list stores definitions as a prefix and uses as a suffix. Walk + // backward from the tail through only the uses; MO == Head terminates the + // circular Prev chain when the list contains only uses. + bool hasUseOutside(Register RegNo, const CgInstruction &MI) const { + CgOperand *Head = getRegUseDefListHead(RegNo); + if (!Head) + return false; + + for (CgOperand *MO = Head->Contents.Reg.Prev; MO->isUse(); + MO = MO->Contents.Reg.Prev) { + if (MO->getParent() != &MI) + return true; + if (MO == Head) + break; + } + return false; + } + /// use_instr_iterator/use_instr_begin/use_instr_end - Walk all uses of the /// specified register, stepping by CgInstruction. using use_instr_iterator = diff --git a/src/compiler/cgir/pass/dead_cg_instruction_elim.cpp b/src/compiler/cgir/pass/dead_cg_instruction_elim.cpp index 1651a4ad9..bf1b70c30 100644 --- a/src/compiler/cgir/pass/dead_cg_instruction_elim.cpp +++ b/src/compiler/cgir/pass/dead_cg_instruction_elim.cpp @@ -46,12 +46,9 @@ bool CgDeadCgInstructionElim::isDead(const CgInstruction *MI) const { #endif continue; } - for (const CgInstruction &Use : MRI->use_nodbg_instructions(Reg)) { - if (&Use != MI) - // This def has a non-debug use. Don't delete the - // instruction! - return false; - } + if (MRI->hasUseOutside(Reg, *MI)) + // This def has a use in another instruction. Don't delete it! + return false; } } } diff --git a/src/tests/evm_jit_frontend_tests.cpp b/src/tests/evm_jit_frontend_tests.cpp index 123167f12..da128a479 100644 --- a/src/tests/evm_jit_frontend_tests.cpp +++ b/src/tests/evm_jit_frontend_tests.cpp @@ -2,10 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 #include "action/evm_bytecode_visitor.h" +#include "compiler/cgir/cg_function.h" +#include "compiler/cgir/pass/dead_cg_instruction_elim.h" #include "compiler/evm_frontend/evm_analyzer.h" #include "compiler/evm_frontend/evm_mir_compiler.h" #include +#include +#include #include #include @@ -94,6 +98,112 @@ TEST(EVMMirBuilderBasicBlockTest, InitDoesNotReappendEntryBlock) { EXPECT_EQ(Harness.Func.getNumBasicBlocks(), 1U); } +TEST(CgRegisterInfoTest, FindsOnlyUsesOutsideInstruction) { + COMPILER::EVMFrontendContext Ctx; + Ctx.initialize(); + COMPILER::MFunction MFunc(Ctx, 0); + COMPILER::CgFunction CgFunc(Ctx, MFunc); + COMPILER::CgBasicBlock *BB = CgFunc.createCgBasicBlock(); + CgFunc.appendCgBasicBlock(BB); + + auto &MRI = CgFunc.getRegInfo(); + const auto &TII = CgFunc.getTargetInstrInfo(); + auto AddDef = [&](COMPILER::CgRegister Reg) { + llvm::SmallVector Operands{ + COMPILER::CgOperand::createRegOperand(Reg, true)}; + return CgFunc.createCgInstruction( + *BB, TII.get(llvm::TargetOpcode::IMPLICIT_DEF), Operands, + /*no_implicit=*/true); + }; + auto AddCopy = [&](COMPILER::CgRegister Dst, COMPILER::CgRegister Src) { + llvm::SmallVector Operands{ + COMPILER::CgOperand::createRegOperand(Dst, true), + COMPILER::CgOperand::createRegOperand(Src, false)}; + return CgFunc.createCgInstruction(*BB, TII.get(llvm::TargetOpcode::COPY), + Operands, + /*no_implicit=*/true); + }; + + COMPILER::CgRegister MultiDefReg = MRI.createIncompleteVirtualRegister(); + COMPILER::CgInstruction *FirstDef = AddDef(MultiDefReg); + COMPILER::CgInstruction *SecondDef = AddDef(MultiDefReg); + EXPECT_FALSE(MRI.hasUseOutside(MultiDefReg, *FirstDef)); + EXPECT_FALSE(MRI.hasUseOutside(MultiDefReg, *SecondDef)); + + COMPILER::CgRegister SelfReg = MRI.createIncompleteVirtualRegister(); + COMPILER::CgInstruction *SelfCopy = AddCopy(SelfReg, SelfReg); + EXPECT_FALSE(MRI.hasUseOutside(SelfReg, *SelfCopy)); + + COMPILER::CgRegister CopyDst = MRI.createIncompleteVirtualRegister(); + COMPILER::CgInstruction *ExternalUse = AddCopy(CopyDst, MultiDefReg); + EXPECT_TRUE(MRI.hasUseOutside(MultiDefReg, *FirstDef)); + ExternalUse->eraseFromParent(); + EXPECT_FALSE(MRI.hasUseOutside(MultiDefReg, *FirstDef)); + + COMPILER::CgRegister UseOnlyReg = MRI.createIncompleteVirtualRegister(); + COMPILER::CgInstruction *FirstUse = + AddCopy(MRI.createIncompleteVirtualRegister(), UseOnlyReg); + COMPILER::CgInstruction *SecondUse = + AddCopy(MRI.createIncompleteVirtualRegister(), UseOnlyReg); + EXPECT_TRUE(MRI.hasUseOutside(UseOnlyReg, *SecondUse)); + FirstUse->eraseFromParent(); + EXPECT_FALSE(MRI.hasUseOutside(UseOnlyReg, *SecondUse)); +} + +TEST(CgDeadInstructionElimTest, PreservesExternalUsesAndDeletesSelfUses) { + COMPILER::EVMFrontendContext Ctx; + Ctx.initialize(); + COMPILER::MFunction MFunc(Ctx, 0); + COMPILER::CgFunction CgFunc(Ctx, MFunc); + COMPILER::CgBasicBlock *DefBB = CgFunc.createCgBasicBlock(); + COMPILER::CgBasicBlock *UseBB = CgFunc.createCgBasicBlock(); + CgFunc.appendCgBasicBlock(DefBB); + CgFunc.appendCgBasicBlock(UseBB); + DefBB->addSuccessorWithoutProb(UseBB); + + auto &MRI = CgFunc.getRegInfo(); + const auto &TII = CgFunc.getTargetInstrInfo(); + auto AddInstruction = + [&](COMPILER::CgBasicBlock &BB, unsigned Opcode, + llvm::SmallVector Operands) { + return CgFunc.createCgInstruction(BB, TII.get(Opcode), Operands, + /*no_implicit=*/true); + }; + + COMPILER::CgRegister LiveReg = MRI.createIncompleteVirtualRegister(); + for (unsigned I = 0; I != 2; ++I) { + AddInstruction(*DefBB, llvm::TargetOpcode::IMPLICIT_DEF, + {COMPILER::CgOperand::createRegOperand(LiveReg, true)}); + } + + COMPILER::CgRegister SelfReg = MRI.createIncompleteVirtualRegister(); + AddInstruction(*DefBB, llvm::TargetOpcode::COPY, + {COMPILER::CgOperand::createRegOperand(SelfReg, true), + COMPILER::CgOperand::createRegOperand(SelfReg, false)}); + + COMPILER::CgInstruction *InlineAsm = AddInstruction( + *UseBB, llvm::TargetOpcode::INLINEASM, + {COMPILER::CgOperand::createRegOperand(LiveReg, false, + /*IsImplicit=*/true)}); + + MRI.freezeReservedRegs(CgFunc); + (void)COMPILER::CgDeadCgInstructionElim(CgFunc); + auto CountOpcode = [](const COMPILER::CgBasicBlock &BB, unsigned Opcode) { + return std::count_if(BB.begin(), BB.end(), + [Opcode](const COMPILER::CgInstruction &MI) { + return MI.getOpcode() == Opcode; + }); + }; + EXPECT_EQ(CountOpcode(*DefBB, llvm::TargetOpcode::IMPLICIT_DEF), 2); + EXPECT_EQ(CountOpcode(*DefBB, llvm::TargetOpcode::COPY), 0); + EXPECT_EQ(CountOpcode(*UseBB, llvm::TargetOpcode::INLINEASM), 1); + + InlineAsm->eraseFromParent(); + (void)COMPILER::CgDeadCgInstructionElim(CgFunc); + EXPECT_TRUE(DefBB->empty()); + EXPECT_TRUE(UseBB->empty()); +} + void expectPCList(const std::vector &Actual, std::initializer_list Expected) { ASSERT_EQ(Actual.size(), Expected.size()); From 7390935f9fe19d325fb096b787b6d643762262f0 Mon Sep 17 00:00:00 2001 From: abmcar <52450271+abmcar@users.noreply.github.com> Date: Fri, 24 Jul 2026 02:43:02 +0000 Subject: [PATCH 3/4] test(compiler): exercise DCE through observable behavior --- src/tests/evm_jit_frontend_tests.cpp | 91 +++++++--------------------- 1 file changed, 21 insertions(+), 70 deletions(-) diff --git a/src/tests/evm_jit_frontend_tests.cpp b/src/tests/evm_jit_frontend_tests.cpp index da128a479..e70cb886f 100644 --- a/src/tests/evm_jit_frontend_tests.cpp +++ b/src/tests/evm_jit_frontend_tests.cpp @@ -98,68 +98,22 @@ TEST(EVMMirBuilderBasicBlockTest, InitDoesNotReappendEntryBlock) { EXPECT_EQ(Harness.Func.getNumBasicBlocks(), 1U); } -TEST(CgRegisterInfoTest, FindsOnlyUsesOutsideInstruction) { - COMPILER::EVMFrontendContext Ctx; - Ctx.initialize(); - COMPILER::MFunction MFunc(Ctx, 0); - COMPILER::CgFunction CgFunc(Ctx, MFunc); - COMPILER::CgBasicBlock *BB = CgFunc.createCgBasicBlock(); - CgFunc.appendCgBasicBlock(BB); - - auto &MRI = CgFunc.getRegInfo(); - const auto &TII = CgFunc.getTargetInstrInfo(); - auto AddDef = [&](COMPILER::CgRegister Reg) { - llvm::SmallVector Operands{ - COMPILER::CgOperand::createRegOperand(Reg, true)}; - return CgFunc.createCgInstruction( - *BB, TII.get(llvm::TargetOpcode::IMPLICIT_DEF), Operands, - /*no_implicit=*/true); - }; - auto AddCopy = [&](COMPILER::CgRegister Dst, COMPILER::CgRegister Src) { - llvm::SmallVector Operands{ - COMPILER::CgOperand::createRegOperand(Dst, true), - COMPILER::CgOperand::createRegOperand(Src, false)}; - return CgFunc.createCgInstruction(*BB, TII.get(llvm::TargetOpcode::COPY), - Operands, - /*no_implicit=*/true); - }; - - COMPILER::CgRegister MultiDefReg = MRI.createIncompleteVirtualRegister(); - COMPILER::CgInstruction *FirstDef = AddDef(MultiDefReg); - COMPILER::CgInstruction *SecondDef = AddDef(MultiDefReg); - EXPECT_FALSE(MRI.hasUseOutside(MultiDefReg, *FirstDef)); - EXPECT_FALSE(MRI.hasUseOutside(MultiDefReg, *SecondDef)); - - COMPILER::CgRegister SelfReg = MRI.createIncompleteVirtualRegister(); - COMPILER::CgInstruction *SelfCopy = AddCopy(SelfReg, SelfReg); - EXPECT_FALSE(MRI.hasUseOutside(SelfReg, *SelfCopy)); - - COMPILER::CgRegister CopyDst = MRI.createIncompleteVirtualRegister(); - COMPILER::CgInstruction *ExternalUse = AddCopy(CopyDst, MultiDefReg); - EXPECT_TRUE(MRI.hasUseOutside(MultiDefReg, *FirstDef)); - ExternalUse->eraseFromParent(); - EXPECT_FALSE(MRI.hasUseOutside(MultiDefReg, *FirstDef)); - - COMPILER::CgRegister UseOnlyReg = MRI.createIncompleteVirtualRegister(); - COMPILER::CgInstruction *FirstUse = - AddCopy(MRI.createIncompleteVirtualRegister(), UseOnlyReg); - COMPILER::CgInstruction *SecondUse = - AddCopy(MRI.createIncompleteVirtualRegister(), UseOnlyReg); - EXPECT_TRUE(MRI.hasUseOutside(UseOnlyReg, *SecondUse)); - FirstUse->eraseFromParent(); - EXPECT_FALSE(MRI.hasUseOutside(UseOnlyReg, *SecondUse)); -} - TEST(CgDeadInstructionElimTest, PreservesExternalUsesAndDeletesSelfUses) { COMPILER::EVMFrontendContext Ctx; Ctx.initialize(); COMPILER::MFunction MFunc(Ctx, 0); COMPILER::CgFunction CgFunc(Ctx, MFunc); - COMPILER::CgBasicBlock *DefBB = CgFunc.createCgBasicBlock(); + COMPILER::CgBasicBlock *FirstDefBB = CgFunc.createCgBasicBlock(); + COMPILER::CgBasicBlock *SecondDefBB = CgFunc.createCgBasicBlock(); + COMPILER::CgBasicBlock *SelfCopyBB = CgFunc.createCgBasicBlock(); COMPILER::CgBasicBlock *UseBB = CgFunc.createCgBasicBlock(); - CgFunc.appendCgBasicBlock(DefBB); + CgFunc.appendCgBasicBlock(FirstDefBB); + CgFunc.appendCgBasicBlock(SecondDefBB); + CgFunc.appendCgBasicBlock(SelfCopyBB); CgFunc.appendCgBasicBlock(UseBB); - DefBB->addSuccessorWithoutProb(UseBB); + FirstDefBB->addSuccessorWithoutProb(SecondDefBB); + SecondDefBB->addSuccessorWithoutProb(SelfCopyBB); + SelfCopyBB->addSuccessorWithoutProb(UseBB); auto &MRI = CgFunc.getRegInfo(); const auto &TII = CgFunc.getTargetInstrInfo(); @@ -171,13 +125,13 @@ TEST(CgDeadInstructionElimTest, PreservesExternalUsesAndDeletesSelfUses) { }; COMPILER::CgRegister LiveReg = MRI.createIncompleteVirtualRegister(); - for (unsigned I = 0; I != 2; ++I) { - AddInstruction(*DefBB, llvm::TargetOpcode::IMPLICIT_DEF, - {COMPILER::CgOperand::createRegOperand(LiveReg, true)}); - } + AddInstruction(*FirstDefBB, llvm::TargetOpcode::IMPLICIT_DEF, + {COMPILER::CgOperand::createRegOperand(LiveReg, true)}); + AddInstruction(*SecondDefBB, llvm::TargetOpcode::IMPLICIT_DEF, + {COMPILER::CgOperand::createRegOperand(LiveReg, true)}); COMPILER::CgRegister SelfReg = MRI.createIncompleteVirtualRegister(); - AddInstruction(*DefBB, llvm::TargetOpcode::COPY, + AddInstruction(*SelfCopyBB, llvm::TargetOpcode::COPY, {COMPILER::CgOperand::createRegOperand(SelfReg, true), COMPILER::CgOperand::createRegOperand(SelfReg, false)}); @@ -188,19 +142,16 @@ TEST(CgDeadInstructionElimTest, PreservesExternalUsesAndDeletesSelfUses) { MRI.freezeReservedRegs(CgFunc); (void)COMPILER::CgDeadCgInstructionElim(CgFunc); - auto CountOpcode = [](const COMPILER::CgBasicBlock &BB, unsigned Opcode) { - return std::count_if(BB.begin(), BB.end(), - [Opcode](const COMPILER::CgInstruction &MI) { - return MI.getOpcode() == Opcode; - }); - }; - EXPECT_EQ(CountOpcode(*DefBB, llvm::TargetOpcode::IMPLICIT_DEF), 2); - EXPECT_EQ(CountOpcode(*DefBB, llvm::TargetOpcode::COPY), 0); - EXPECT_EQ(CountOpcode(*UseBB, llvm::TargetOpcode::INLINEASM), 1); + EXPECT_FALSE(FirstDefBB->empty()); + EXPECT_FALSE(SecondDefBB->empty()); + EXPECT_TRUE(SelfCopyBB->empty()); + EXPECT_FALSE(UseBB->empty()); InlineAsm->eraseFromParent(); (void)COMPILER::CgDeadCgInstructionElim(CgFunc); - EXPECT_TRUE(DefBB->empty()); + EXPECT_TRUE(FirstDefBB->empty()); + EXPECT_TRUE(SecondDefBB->empty()); + EXPECT_TRUE(SelfCopyBB->empty()); EXPECT_TRUE(UseBB->empty()); } From 56729ee377635bbfd869b686713bbd44ab7ac606 Mon Sep 17 00:00:00 2001 From: abmcar <52450271+abmcar@users.noreply.github.com> Date: Fri, 24 Jul 2026 03:51:08 +0000 Subject: [PATCH 4/4] docs(compiler): document cold-path scan optimizations --- .../README.md | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 docs/changes/2026-07-24-reduce-compiler-cold-path-scans/README.md diff --git a/docs/changes/2026-07-24-reduce-compiler-cold-path-scans/README.md b/docs/changes/2026-07-24-reduce-compiler-cold-path-scans/README.md new file mode 100644 index 000000000..bd5e30d5f --- /dev/null +++ b/docs/changes/2026-07-24-reduce-compiler-cold-path-scans/README.md @@ -0,0 +1,109 @@ +# Change: Reduce repeated compiler cold-path scans + +- **Status**: Accepted +- **Date**: 2026-07-24 +- **Tier**: Light + +## Overview + +This change removes two repeated linear scans from cold multipass compilation: + +- MIR block membership now uses the block's index and pointer identity instead + of scanning every block in the function. +- CGIR dead-instruction elimination now walks backward across the use suffix of + a register chain instead of traversing its definition prefix for each query. + +Both changes preserve the existing compiler semantics and data-structure +contracts. + +## Motivation + +The following diagnostic measurements were collected on pre-rebase +implementation head `1edac697773e3f20fe186a6a071a42793ee22b81`, based on +`57324bda91c945dc4c11c29d9809c2085b74d9d2`. They motivate this change but +do not measure the rebased integration. + +On LabPerf's frozen 4-block, 499-transaction `prefix004` diagnostic corpus +(`SHA256 7242de6d9331621b27a4422891a05f0f167c49f0183c69c96b321a81fd38cc52`), +the two original scans dominated cold compilation: + +| Flat sampled cycles | Before | After | +|---|---:|---:| +| `CgDeadCgInstructionElim::isDead` | 39.53% | 0.28% | +| `EVMMirBuilder::setInsertBlock` | 12.92% | 0.01% | + +For the combined change, first cold block-execution time decreased from +2,238.890–2,263.209 seconds to 1,083.372 seconds, a diagnostic reduction of +51.6–52.1% or 2.07–2.09x. The two baseline values are non-identical reference +runs, not statistical replicates or a confidence interval. + +These measurements apply only to `prefix004`. They do not establish a +32-block, mainnet-wide, production, steady-state runtime, or formal benchmark +result. + +## Implementation + +### MIR block membership + +`MFunction::appendBlock` assigns each appended block its position in the +function's block vector. The new membership query accepts a block only when +its index is in range and the pointer at that index is identical. The pointer +check prevents a newly created block with the default index from being +mistaken for the entry block. `EVMMirBuilder::setInsertBlock` uses this +constant-time query before appending a block. + +This relies on the existing append-only, stable-index invariant for live MIR +blocks. + +### CGIR external-use lookup + +The CGIR register chain stores definitions as a prefix and uses as a suffix. +The new lookup starts at the chain tail and walks backward only while operands +are uses. A use owned by another instruction keeps the definition live; a use +owned by the definition itself does not. The walk stops at the first +definition, with an explicit head check for a circular chain containing only +uses. + +This preserves dead-instruction elimination behavior for self-uses, external +uses, multiple definitions, and fixed-point deletion. + +## Impact + +The blast radius is limited to the multipass compiler's MIR membership lookup, +CGIR dead-instruction elimination, and their regression tests. There is no +public ABI, SONAME, dependency, object-layout, global-state, threading, or +determinism contract change. + +The compiler module specification was reviewed and remains accurate; no +module-spec update is required. The implementation depends on the existing +MIR stable-index and CGIR definition-prefix/use-suffix invariants. Future +changes to either data structure must update the corresponding query and +tests. + +The measured hotspot shift identifies register allocation and liveness as +subsequent diagnostic targets. It does not show identical generated machine +code or a runtime-execution improvement. + +## Validation + +Local validation on implementation head +`7390935f9fe19d325fb096b787b6d643762262f0`, rebased onto +`a7e73059ffc46b14978e87d04a3fa6b36d4f6c3a`: + +- Format and diff checks: passed +- Release build: passed +- Release EVM frontend: 53/53 +- EVM range analyzer: 50/50 +- EVM differential: 65/65 +- ASAN EVM frontend: 53/53, with 0 sanitizer errors +- `tools/dtvm_local_test.sh --auto`: unit 223/223, EEST state + 2723/2723, EVM assembly 209/209, and CTest 12/12 + +GitHub Actions status for the rebased branch is tracked by PR #577. + +## Checklist + +- [x] Implementation complete +- [x] Tests added/updated +- [x] Module specs reviewed; no contract update required +- [x] Build and tests pass