jit: hot-path wins, [jit] in a C++ AOT host, and unresolved AOT-object address globals - #3917
Open
aleksisch wants to merge 4 commits into
Open
jit: hot-path wins, [jit] in a C++ AOT host, and unresolved AOT-object address globals#3917aleksisch wants to merge 4 commits into
aleksisch wants to merge 4 commits into
Conversation
Every annotation in tree yields a ref from makeIndexType, so nothing reached the jit's at`handle path for a pointer result - the shape an embedder's keyed container has. dasUnitTest gains PtrSlots, indexed by string, yielding `int?`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An address global nothing fills at load is not merely unresolved: LLVM folds its loads to null, proves the body UB and drops it to a zero-length `unreachable` that shares an address with the next function, so das_aot_register binds that hash to alien code. collect_unresolved_address_globals reports them and run_jit panics before the optimizer erases the evidence. It found one live case: ascend-new of a handled type, which the emitter names after the ascend node while CollectExternVisitor visited only ExprNew. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
32-bit % was lowered as a sitofp, a double divide, an fptosi, a multiply and a subtract, where every other width already emitted srem. The guards that made the float path safe - the divide-by-zero check and the INT_MIN % -1 rewrite - run before the emitter, so they cover the native form too. On an enlisted ES dominated by `x % 4093`: 131 -> 53 us/tick, against 45 for C++ AOT. Jitted code also resolved a block's annotation data by sid on every call, 212 cycles each. A 16-slot direct-mapped memo answers the repeats; it is keyed on the map it was filled from, so a rebuilt tabAdLookup invalidates it without a cooperating caller. Act-stage das per tick: 191.6 -> 180.9 us. Context grows 256 bytes. The memo sits last so no baked offset moves, and the DLL cache key now folds the Context offsets the emitter bakes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
run_jit returned early for any AOT program, and AOT emitted a direct C++ call that could never reach a jitted body. The early return now respects jit_enabled. Selection narrows to what linkCppAot left unbound - it runs before the simulate macros, so SimFunction.aot marks the covered set - plus anything marked [jit]; jitting the covered set would displace a native body at full codegen cost per load. A [jit] function takes the hybrid call form, which dispatches aot, then jit, then interpreted, and is what makes a re-jit visible to callers. The backend's own daslib modules opt out of the host's script policies: a game sets no_global_variables, which they legitimately trip. Verified in a game host built with C++ AOT plus the JIT runtime. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
aleksisch
force-pushed
the
aleksisch/jit-runtime-host
branch
from
September 1, 2026 13:14
7fde9c3 to
e7a5fbf
Compare
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.
ABI break:
Contextgrows 256 bytes - every embedder and prebuilt shared_module rebuilds. No baked offset moves, so warm JIT caches stay valid.Four changes to the LLVM JIT backend, from profiling a game host that runs daslang through C++ AOT with the runtime JIT enabled beside it.
Two are emitted-code wins. The 32-bit integer remainder was lowered as a sitofp, a double divide, an fptosi, a multiply and a subtract, where every other width already emitted srem - roughly 20 cycles of divide latency instead of the multiply-and-shift LLVM picks for a constant divisor. The guards that made the float path safe, the divide-by-zero check and the INT_MIN % -1 rewrite, already run before the emitter reaches that point, so they cover the native form too. Separately, jitted code resolved a block's annotation data by sid on every call - once per tick for an entity system, 212 cycles each - recomputing a value that cannot change. A 16-slot direct-mapped memo answers the repeats, keyed on the map it was filled from so a rebuilt
tabAdLookupinvalidates it without a cooperating caller. It sits last inContext, so the offsets the emitter bakes into every jitted function do not move.The third makes
[jit]usable in a host that ships C++ AOT.run_jitreturned early for any AOT program, and AOT emitted a direct C++ call that could never reach a jitted body. The early return now respectsjit_enabled; selection narrows to whatlinkCppAotleft unbound, since it runs before the simulate macros andSimFunction.aotmarks the covered set; and a[jit]function takes the hybrid call form that dispatches aot, then jit, then interpreted. The fourth catches offline-AOT-object address globals that nothing fills at load: LLVM folds their loads to null, proves the body UB and reduces it to a zero-lengthunreachablesharing an address with the next function, sodas_aot_registerbinds a hash to alien code. It found one live case, ascend-new of a handled type.Where to look:
Context::AdMemoandadBySidMemoinsimulate.h;jit_selectsinllvm_jit_run.das;collect_unresolved_address_globalsinllvm_aot.das.Validation, claims, ledger
Validation
--test modules/dasLLVM/tests) passes. It sits outside the coretests/sweep, so no CI lane covers it, and this branch is mostly dasLLVM.das2rstregenerates with zero changes and zeroUncategorized: the newis_aot_functionbinding joins the hiddenJitgroup besideis_jit_function. No.rstdiffers from master, sosphinx-buildwas not re-run.Contextoffsets measured against master:stopFlagsstays at 640,sizeofgoes 816 -> 1072.Claims - stated, not tested
policies.aotandpolicies.jit_enabledtogether, so theaot_hostarm ofjit_selectshas no in-tree coverage. Verified by reading the ordering:linkCppAotruns before the simulate macros, soSimFunction.aotis authoritative whenrun_jitreads it. A break shows as an AOT host either putting the whole program through codegen at load, or jitting nothing.DllName.glob()name space. That path runs only underemit_aot_object, and no in-tree test builds an AOT object carrying a stray zero global. Verified by enumerating every zero-initialized global the emitter creates: the three address-global creators all name throughglob(), and the two that do not (fileInfo, the wasm field-offset global) are not address slots. A das module-scope global is not an LLVM global at all - it lives in theContextglobals block - so it cannot reach this check.extraOffsetonPtrSlots.int?has no fields, so no site folds a nonzero offset onto this annotation and the plumbing cannot be exercised. Carried because it is part of thesimulateGetAtcontract and this annotation is the shape embedders copy.Not done
adBySidMemois not concurrency-safe. It replaces a read-only map lookup with two array writes, so two threads driving oneContextcan match a sid against the previous occupant's value. AContextis single-threaded by contract.LLVMBuildSRemInt32andLLVMBuildURemUInt32are removed fromllvm_boostwith no deprecation shim. In-tree callers are gone; an out-of-tree consumer calling either stops compiling.🤖 Generated with Claude Code