Repository navigation
Conversation
buildBytecodeCache called resolveJumpTargetsByAbstractStack twice in a row with the same arguments: once inside the buildJumpDestMap profile region and once right after it, clearing the first result in between. The function reads only the bytecode, the jump-destination map and the metrics table, and writes only Cache.ResolvedJumpTargets, so the second call rebuilt an identical map. The duplicate dates from DTVMStack#462, whose comment already says the pass runs once. Drop the second call. On 338 mainnet contracts (5.1 MB of bytecode) all six cache fields are identical before and after, with EnableSPP off and on, and the total cache build time falls from 135.3 ms to 104.9 ms (-22.5%) with SPP off and from 147.0 ms to 114.8 ms (-21.9%) with SPP on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
buildBytecodeCache resolved the constant jump targets for every module. The SPP pipeline reads them and so does the JIT front end, but a build without SPP does not, and neither does the interpreter. Modules that are only interpreted, in interpreter mode or in profile-guided JIT mode before they are hot, paid for the abstract-stack pass and never used its result. Let a build without SPP skip the pass, and add resolveJumpTargets() to run it later with the same result. EVMModule builds its interpreter-side cache without the table and completes it in getBytecodeCacheForJIT(), which the compiler now calls. The profile-guided trigger calls it before it queues a compile, on the thread that interprets the module, so the compile thread still only reads the cache. SPP builds and direct callers of buildBytecodeCache are unchanged. On 338 mainnet contracts the interpreter-side cache build takes 71.7 ms instead of 105.3 ms (-31.9%). The other five cache fields are identical, the table resolved later equals the one resolved up front, and the JIT code of a module loaded for profile-guided JIT and then compiled is byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
⚡ Performance Regression Check Results✅ Performance Check Passed (interpreter)Performance Benchmark Results (threshold: 25%)
Summary: 194 benchmarks, 0 regressions ✅ Performance Check Passed (multipass)Performance Benchmark Results (threshold: 25%)
Summary: 194 benchmarks, 0 regressions |
Contributor
Author
|
Closing: #615 reworks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Does this PR affect any open issues?(Y/N) and add issue references (e.g. "fix #123", "re #123".):
2. What is the scope of this PR (e.g. component or file name):
buildBytecodeCacheand the newresolveJumpTargetsinsrc/evm/evm_cache.{h,cpp}EVMModule::getBytecodeCacheForJITinsrc/runtime/evm_module.{h,cpp}, and its two callers insrc/compiler/evm_compiler.cppandsrc/vm/dt_evmc_vm.cppdocs/modules/evm/cache-build.mdand a Light change documentStacked on #616. The first commit of this PR is #616; only the second commit is new. I will rebase this branch when #616 lands.
3. Provide a description of the PR(e.g. more details, effects, motivations or doc link):
buildBytecodeCacheresolves the constant JUMP/JUMPI targets of every module with an abstract-stack pass. The resulting table,ResolvedJumpTargets, has two readers:buildCFGEdgesin the SPP pipeline, and the JIT front end. A build without SPP returns frombuildGasChunksSPPbeforebuildCFGEdges, and the interpreter never reads the table. A module that is only interpreted therefore pays for a pass whose result nothing reads. That is every module in interpreter mode, and in profile-guided JIT mode every module until it is hot enough to be compiled.This PR moves the pass to the point where a compile needs it:
buildBytecodeCachetakes aResolveJumpTargetsargument. It defaults totrue, so direct callers behave as before, and an SPP build resolves the targets regardless.resolveJumpTargets()fills the table of a cache built without it, with the same result.EVMModulebuilds a cache without SPP without the table. The newgetBytecodeCacheForJIT()completes it, andEVMJITCompiler::compile()reads the cache through that accessor.getBytecodeCacheForJIT()before it queues the background compile. The table is therefore filled on the thread that interprets the module, and the compile thread still only reads the cache.Measured on
mainate839f6bplus #616, Release build, on 338 mainnet contracts (5.1 MB of runtime bytecode):buildBytecodeCachewithout SPPEach value is the median of 30 in-process repetitions on one pinned core; the ranges are [104.0, 107.3] ms and [70.9, 79.9] ms.
Change document:
docs/changes/2026-10-09-evm-jump-targets-on-demand/README.md4. Are there any breaking changes?(Y/N) and describe the breaking changes(e.g. more details, motivations or doc link):
EVMModule::getBytecodeCache()can now return a cache whoseResolvedJumpTargetsis empty. The only reader outside the cache build, the JIT compiler, is moved togetBytecodeCacheForJIT()in this PR.5. Are there test cases for these changes?(Y/N) select and add more details, references or doc links:
New tests:
EVMCacheJumpTargets.DeferredResolutionMatchesEagerBuild: a build without the table leaves it empty and every other field equal to an eager build;resolveJumpTargets()then gives the eager table.EVMCacheJumpTargets.SPPBuildAlwaysResolves: an SPP build resolves the targets even when asked not to.EVMModuleBytecodeCache.JumpTargetsAreResolvedForTheJITOnly: a module loaded for profile-guided JIT has no jump targets ingetBytecodeCache(), andgetBytecodeCacheForJIT()fills them into the same cache object.Equivalence on the 338 contracts:
Also checked on this branch:
ctestin a Release multipass build withDTVM_TEST_REVISION=Cancun: 14/14 with default arguments, and 14/14 withSPEC_TESTS_ARGSset as in the CI multipass job. This includesevmCacheTests(18 cases),evmDifferentialTests(80),evmInterpTests(275) andevmProfileGuidedJITTests(6), which exercises the trigger path.tools/run_evm_tests.py -r build/dtvm --format evm: 209/209 with-m multipass --enable-evm-gas, and 209/209 with-m interpreter.clang-format15.0.7 with--dry-run -Werroron the changed files, andgit diff --check.Benchmark method: a temporary gtest case, not part of this PR, calls
buildBytecodeCacheon each of the 338 bytecode files withEnableSPP = false, once with and once withoutResolveJumpTargets, and prints the totals.6. Release note
🤖 Generated with Claude Code