Context finds functions and globals by name and by mangled hash through sealed perfect-hash tables; standalone exes and AOT contexts adopt emitter-sealed constant data - #3973
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Context reporting in src/runtime/context.cpp can null-dereference functionLookup/variableLookup on never-simulated contexts.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR replaces linear-by-name and hash-map-based mangled-name lookups in Context with sealed perfect-hash NameLookup tables, enabling O(1) function/global resolution by name and by mangled hash, and letting standalone -exe and AOT standalone contexts adopt emitter-sealed constant lookup tables (avoiding startup table construction).
Changes:
- Introduces
das::NameLookup(sealed perfect-hash tables for mangled-name hashes and plain names) and wires it intoContextfor function/global lookups. - Updates emitters (LLVM
-exeand AOT standalone) to build/seal/adopt lookup tables at code-generation time and updates runtime JIT registration APIs accordingly. - Adds C++/das tests plus RTTI builtins and documentation for building/serializing
NameLookuptables.
File summaries
| File | Description |
|---|---|
| tests-cpp/small/test_name_lookup.das | Adds a small script fixture used by C++ lookup tests. |
| tests-cpp/small/test_name_lookup.cpp | Adds doctest coverage for NameLookup sealing/adoption and Context by-name/by-hash behavior. |
| tests-cpp/big/standalone_ctx/test_standalone_ctx.cpp | Extends standalone-ctx integration test to validate new lookup behavior. |
| tests-cpp/big/standalone_ctx/standalone_init_fixture.das | Adds shared global + exported helper to exercise shared-global lookup/offset behavior. |
| tests-cpp/big/nano_ctx/test_nano_ctx.cpp | Extends nano context integration test to validate new by-name and by-hash lookups. |
| src/simulate/standalone_ctx_utils.cpp | Removes legacy registration into removed tabMnLookup. |
| src/simulate/name_lookup.cpp | Implements NameLookup building, sealing, and adoption logic. |
| src/runtime/context.cpp | Switches Context to use functionLookup/variableLookup for by-name lookups and reporting. |
| src/builtin/module_jit.cpp | Updates standalone-exe JIT context registration APIs and adds adoption entrypoint for sealed tables. |
| src/builtin/module_builtin_rtti.cpp | Exposes NameLookup builder/inspection functions to emitters via rtti_core. |
| src/ast/ast_simulate.cpp | Builds/seals Context function/global NameLookup tables during simulation (replacing old maps). |
| nano/src/nano_context.cpp | Updates nano’s Context by-name variable/function lookup implementations. |
| nano/REVIEW.md | Updates nano review checklist wording/scope related to context member changes and testing expectations. |
| nano/include/daScript/simulate/simulate.h | Updates nano Context API fields and by-hash lookup implementation to use NameLookup. |
| nano/CMakeLists.txt | Adds name_lookup.cpp to nano’s reused runtime sources list. |
| nano/ARCHITECTURE.md | Updates nano architecture description to reflect the new sealed lookup mechanism and reused source count. |
| modules/dasLLVM/tests/llvm_exe_name_lookup.das | Adds module-owned test that validates by-name lookups work both interpreted and in -exe artifacts. |
| modules/dasLLVM/tests/_name_lookup_root.das | Adds the script fixture used by the LLVM exe name-lookup test. |
| modules/dasLLVM/REVIEW.md | Clarifies wording of test discipline rules for spawned child processes and cache placement. |
| modules/dasLLVM/daslib/llvm_jit.das | Updates comment to match new standalone-exe global registration behavior. |
| modules/dasLLVM/daslib/llvm_jit_run.das | Bumps LLVM_JIT_CODEGEN_VERSION and emitter hash due to ABI/behavior changes. |
| modules/dasLLVM/daslib/llvm_exe.das | Emits sealed lookup tables as constant data and adopts them early; updates global registration signature. |
| include/daScript/simulate/simulate.h | Replaces tabMnLookup/tabGMnLookup with functionLookup/variableLookup and updates by-hash lookups. |
| include/daScript/simulate/REVIEW.md | Updates checklist criteria to cover broader ABI/public member changes and perf-validation wording. |
| include/daScript/simulate/name_lookup.h | Adds the public NameLookup API and layout pins for emitter serialization. |
| include/daScript/simulate/ARCHITECTURE.md | Documents the new lookup design, ownership modes, and performance characteristics. |
| include/daScript/simulate/aot_builtin_rtti.h | Declares AOT-visible RTTI entry points for NameLookup builder/inspection functions. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_seal-0xe859d3033156b01.rst | Documents name_lookup_seal. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_name_slots-0x843def8a75a15.rst | Documents name_lookup_name_slots. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_name_head-0x1db1092788d8cc73.rst | Documents name_lookup_name_head. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_name_hash-0xaaa1df5fdee04233.rst | Documents name_lookup_name_hash. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_name_disp-0xaa40dba56bcd5663.rst | Documents name_lookup_name_disp. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_name_buckets-0xc771560f6c131fb.rst | Documents name_lookup_name_buckets. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_mnh_slots-0xacc87eebdfa17415.rst | Documents name_lookup_mnh_slots. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_mnh_disp-0x5f1d16a55ceea87c.rst | Documents name_lookup_mnh_disp. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_mnh_buckets-0x623f630fac05a289.rst | Documents name_lookup_mnh_buckets. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_insert-0x1df6f40370eafa48.rst | Documents name_lookup_insert. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_entry_value-0x8770c19fdc477823.rst | Documents name_lookup_entry_value. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_entry_next-0xde41b4c5e481bd98.rst | Documents name_lookup_entry_next. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_entry_mnh-0x2dbf39242b1b1cfd.rst | Documents name_lookup_entry_mnh. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_entry_index-0xa8e92693addbad91.rst | Documents name_lookup_entry_index. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_destroy-0x3fdb742796d6d4b.rst | Documents name_lookup_destroy. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_create-0xdc119a2c4b28198f.rst | Documents name_lookup_create. |
| doc/source/stdlib/handmade/function-rtti-name_lookup_count-0x9780e5f677f52ae5.rst | Documents name_lookup_count. |
| doc/source/stdlib/handmade/annotation-rtti-NameLookup.rst | Documents the NameLookup handled type annotation. |
| doc/reflections/das2rst.das | Adds grouping for name_lookup_* functions in generated RTTI docs. |
| daslib/aot_standalone.das | Emits sealed lookup tables into generated standalone context C++ and adopts/verifies them at startup. |
| CMakeLists.txt | Adds name_lookup sources/headers to build+install lists. |
Review details
- Files reviewed: 48/48 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
d105a83 to
c1ba353
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
It changes core runtime lookup behavior and public ABI across runtime/JIT/nano/emitter paths, so it warrants final human review despite strong test coverage.
Review details
- Files reviewed: 48/48 changed files
- Comments generated: 0 new
- Review effort level: Lite
…ugh one sealed perfect-hash object each (functionLookup / variableLookup replace tabMnLookup / tabGMnLookup): findFunction, findFunctions, findFunction(name, isUnique) and findVariable were a strcmp walk over the whole table - an editor calling invoke_in_context by name per scene node on a program with thousands of functions spent 96% of its frame there - and are now one probe, one 64-bit compare and a same-name chain in function-index order, with no strcmp at all since hash equality is name equality everywhere else in the runtime; a distinct-name hash collision fails the seal the way a mangled-hash collision already does, and a failed seal or a never-sealed object answers every probe with a miss. fnByMangledName and globalOffsetByMangledName - the latter on every global-by-hash access in all three tiers - read one entry (2.2 ns against das_hash_map's 5.8 on 8k keys, an eighth of the memory). A simulated program builds the tables (buildMNLookup / buildGMNLookup insert then seal once, the blob is owned and freed with the object); a standalone exe and a standalone AOT context adopt them: the emitter seals the same object at code-generation time through the rtti_core name_lookup_* builtins and writes the arrays into the artifact as constant data in the word layout name_lookup.h pins, so the generated constructor builds and owns nothing - llvm_exe.das emits private constant globals and adopts them right after the context is created, before any registration call (jit_register_standalone_variable now carries index, name and the shared flag; codegen version 0x77, emitter pin re-hashed for a comment), aot_standalone.das emits static const aggregates and verifies every function's index and every global's runtime offset against the emitted tables. FillFunction no longer touches the table and the relocation walk is gone since entries hold indices. A standalone exe's globals now have names, which findVariable used to strcmp as null; the old buildGMNLookup collision path indexed globalVariables by byte offset. nano shares name_lookup.cpp unmodified (the SDK install ships it) and its findFunction / findVariable use the same tables. The new builtins are documented; the hot-path, nano and dasLLVM checklists carry the rulings the review round reached. Tests: tests-cpp/small/test_name_lookup (a 10k-key suffix-only adversarial table, an adopted copy answering like its builder and owning nothing, a name that dies right after its insert, a fresh object and a failed seal both missing, a Context's overloads, uniqueness, misses, a fork sharing the tables, a context that never simulated), modules/dasLLVM/tests/llvm_exe_name_lookup (interpreted and as an exe), cases in the standalone and nano big tests including a shared global. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
c1ba353 to
2e93819
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces broad, ABI-breaking changes across runtime lookup paths and standalone/exe emitters, which warrants final human verification despite strong new test coverage.
Review details
- Files reviewed: 48/48 changed files
- Comments generated: 0 new
- Review effort level: Lite
Rebuild required:
Contextloses the public memberstabMnLookupandtabGMnLookup,jit_register_standalone_variablechanges signature, andFillFunctionno longer registers functions - every external module, standalone context and-exeartifact is regenerated or rebuilt against this daslang.Why.
Context::findFunction,findFunctions,findFunction(name, isUnique)andfindVariablewalked the whole function or global table with astrcmpper entry. An editor that callsinvoke_in_contextby name for every scene node on a program with several thousand functions spent 96% of its frame time there.What changes.
Contextfinds a function or a global through one sealedNameLookup: two perfect hashes (mangled-name hash to entry, plain-name hash to chain head), same-name chains in function-index order, nostrcmpat lookup.rtti_corebuiltins (name_lookup_*) let the emitters build and read a sealed table; nano compiles the same source and answers from the same tables.Observable behavior.
findFunction(name)cost grows with N -> one probe, independent of N.fnByMangledNameandglobalOffsetByMangledName(every global-by-hash access in AOT, JIT and interpreter) 5.8 ns -> 2.2 ns on 8k keys, in an eighth of the memory.findFunctionon an overloaded name returns an arbitrary overload -> the lowest-index one;findFunctionsreturns hash-map order -> index order.findVariablefrom a standalone exe crashed on a null name -> answers; nano'sisUniqueon a miss reported true -> false, as the full runtime does.Where to look.
include/daScript/simulate/name_lookup.handsrc/simulate/name_lookup.cppare the object; the emitters aredaslib/aot_standalone.das(writeLookupTables) andmodules/dasLLVM/daslib/llvm_exe.das(emit_exe_lookups); the seal-time offset recomputation inwriteVariableLookupmirrorsInitGlobalVariableand is the one place two rules must agree.Validation, claims, ledger
Validation
bin/test_standalone_ctxandbin/test_nano_ctxboth exit 0 (macOS arm64, Release).bin/daslang dastest/dastest.das -jit -- --timing-outliers 10 --test modules/dasLLVM/tests86/86 on macOS arm64;modules/dasLLVM/tests/llvm_exe_name_lookup.daspasses interpreted and as an exe.fnByMangledName/globalOffsetByMangledName: a scratch benchmark of the probe againstdas_hash_mapon the same 8k keys, clang -O2, Apple M-series, best of five: 2.2 ns against 5.8 ns by hash, 25 ns against 41 ns by name.tests/assert_once/test_assert_over_temporary.das, a process-kill timeout intests/fio/test_process.das) that pass alone, and its AOT lane needed thertti_name_lookup_*declarations inaot_builtin_rtti.h, validated withpreflight --only tests-aot(full AOT build and sweep, pass).modules/dasVulkan/buildfirst: the rebase pulled avulkan_boost.dasthat calls a binding the older module lacked, which red-lit lint and the compile sweep on every Vulkan root.tests-cpp/small/test_compilation_callback_fail.das, unrelated to this change.Claims - stated, not tested
llvm_exe_name_lookup.das(theset "PATH=..."child command) copiesllvm_exe_native_paths.dasverbatim and was not run on Windows; the nightly module-suite lane runs it.static_asserts pin it on every target the runtime archive compiles for, and a wasm32 cross-target dump was read by hand, but no cross-compiled exe was executed.nano/README.mdwas not re-measured:name_lookup.cppadds the build half nano never runs, and the cortex-m4 toolchain is a nightly linux cell.Not done
tabAdLookupstays adas_hash_map; nothing hot reads it.Program::simulate, const-folding simulates included; sampled at under 0.1% of a real compile.jit_get_global_mnhis declaredmemory(none), which lets LLVM reorder it freely; adopting first removes the window this change would have widened, the attribute itself predates the change.modules/dasLLVM/REVIEW.mdintomodules/dasLLVM/tests/REVIEW.md, splitting two of its rules, the debug_info pin rule) are outside this arc.