The interpreter's variable lookup is now blazing through arithmetic loops at a 57% faster pace. But this isn't just about raw speed — four tracker items were checked off the list, culminating in a more robust system. All rolled out on submain, a direct result of a thorough four-pillar audit.
BindingId replaces string hashing at runtime
The heavyweight change came from tracker #009. Previously, our interpreter was busy hashing variable names every time they were accessed. That was a repeat offender in wasted cycles. Now, by using a pre-assigned numeric BindingId, we seize efficiency. The semantic phase was already handing out these IDs, but the runtime kept adding the overhead back. It doesn’t anymore.
ScopeFrame.vars has transitioned to a HashMap equipped with a zero-cost identity hasher keyed by u32 binding IDs. Name-based lookups are still around but tucked away for less frequent operations like string interpolation.
Fixes were necessary upstream: ConstDecl and semantic_impls now hold onto their BindingId, making sure our semantic phase pipelines gracefully into the interpreter's primary key system — narrowing the gap with JIT's variable handling.
Here's how the numbers shine on a Windows release build:
- arith_loop (5M iterations): from 5744ms down to 2481ms (56.8% boost)
- nested_loops (4M iterations): from 2675ms down to 1796ms (32.8% boost)
- fib_recursive: from 6835ms down to 6488ms (merely 5.1% faster due to call-frame constraints)
Our findings align with expectations: recursive functions aren't bogged down by variable lookups as much as by setting up call frames.
Array bounds errors stop lying
Misleading diagnostic labels are on their way out, thanks to tracker #002 and #032. Attempts to access out-of-bounds array indices once triggered an error as unhelpful as variable 'index 5' has not been declared. Not anymore.
Changes came in two waves. First, we introduced a new error variant: RuntimeError::IndexOutOfBounds { pos, index, length }. Then, three runtime.rs cases were refactored to employ this newfound precision. Now, programs react with clarity, showing array index N out of bounds for array of length M.
New fixtures have been added to validate this behavior and to handle discrepancies with the JIT through a .jit_known_unsound sidecar. This move optimally separates interpreter improvements from JIT limitations, acknowledging that our current JIT doesn’t yet include bounds checks (tracker #003).
Builtin registry
Though tracker #008 tackled a narrower terrain, its ripples will be felt across the project. By consolidating all of the site-dependent lists for Cx builtins in src/frontend/builtins.rs, we ended the structural divergence that was starting to creep in. Future integrations are now a single-source update, negating the chance for list drift.
This tidy up is directly tackling findings from the audit's Pillar 2 concerns. Where once four lists zigged separately, now they zag together.
Deliberate sequencing
Our line of commits didn't happen by chance. Tracker #012 initiated the new error variant, leading to #002/#032 fixing use-sites, and deploying a JIT exclusion practice. Tracker #008 cleared the path by resolving the builtin conundrum, before tracker #009 flexed the freshly implemented BindingId advantages.
Each stage acted on precise dependencies with grounded purpose and references back to their origins in the audit pillars.
What is next
We’re setting our sights on the lazy arena (tracker #010) identified in #009’s commit log. The 64KB memset seen in function calls sticks out like a sore thumb in call-heavy operations, hogging 90% of runtime. The solution is clear-cut and well within scope.
After a successful deployment of these fixes, merging submain to main comes naturally. With submain miles ahead by seven commits and improvements across performance and correctness, attention can shift to JIT’s array bounds safeguard (tracker #003), leveraging the existing jit_known_unsound mechanism to master this transition.
Main remains unchanged with a clean slate of 182/0, while today’s improvements have all bolstered the growth of submain.
Follow the Cx language project:
- Website: cx-lang.com
- GitHub: github.com/COMMENTERTHE9/Cx_lang
- Dev.to: dev.to/commenterthe9
- Bluesky: thecomment.bsky.social
- Twitter/X: @commenterthe9
Originally published at https://cx-lang.com/blog/2026-05-25
Top comments (0)