Native compilation of user code
Compile a bounded typed CPU subset into standalone executables, inspect generated C++20 and preserve checked numerical and buffer behavior.
Development APIsThe current development source implements aner build for a bounded typed CPU subset. It compiles user written arithmetic, loops, conditions, calls and admitted typed parallel tasks into a standalone executable. The generated program does not evaluate Aner syntax tree nodes or fall back to the interpreter. Downloaded Aner runtime packages do not include a usable native compiler SDK. This remains a source build capability; no reviewed relocatable SDK artifact is offered with the runtime release.
This is a source build feature. Building requires the matching development SDK, its configured C++20 compiler and native helper libraries. Running the resulting executable needs the platform's normal C++ runtime and any shared provider libraries selected by the source build SDK, with no Aner interpreter. For example, a CUDA enabled source build can retain CUDA library dependencies even though this user code subset runs on CPU; Apple system frameworks are supplied by the operating system. This is not a promise of an entirely static executable. A compatible operating system and CPU are still required. Cross compilation and relocatable compiler SDK packaging are separate work. The normal source build configuration generates the private SDK in its build tree and records that location in aner. The generated project uses the SDK’s matching runtime build configuration, including the MSVC runtime/iterator ABI settings; mixing Debug helper libraries with a Release C++ runtime is not supported. Keep the matching source and build tree available while compiling programs; copying only the aner executable is insufficient. The produced program does not need those build directories to run.
Development cluster setup uses aner parallel native enroll for fresh private records and aner parallel native preflight for admission only checks through an approved compiled program. These commands retain C++20, strict host key checks and ABI/protocol identities; preflight runs no source level statements, scientific parameters or task callbacks. See native enrollment/preflight. Linux and Windows execution tests remain deferred.
Build and inspect a complete example
Use the development aner executable built from this source checkout:
aner check examples/native_numeric.aner
aner run examples/native_numeric.aner
aner build examples/native_numeric.aner -o native-numeric --emit-cpp native-numeric.cpp
./native-numericWindows PowerShell:
aner check examples/native_numeric.aner
aner build examples/native_numeric.aner -o native-numeric.exe
.\native-numeric.exe --aner-native-info
.\native-numeric.exeChoose fresh output paths: an existing executable or requested C++ destination is rejected. The default build manifest is OUTPUT.build.json; keep it with the executable and generated C++. Output directories must exist and support hard links: completed files are staged on the destination filesystem and linked without replacing a racing destination. Building does not execute the result. If --emit-cpp is omitted, generated source is retained at OUTPUT.cpp; it remains inspectable in either case.
The complete source exercises a user numerical loop, calls between functions and a checked transformation of a fixed numerical array. Compare the interpreted and native program output. aner check validates the full language; a successful check alone does not establish that every construct belongs to the native subset.
| Build option | Meaning |
|---|---|
-o OUTPUT | Required standalone executable destination. |
--emit-cpp FILE | Explicit destination for inspectable generated C++20; otherwise OUTPUT.cpp. |
--emit-manifest FILE | Explicit JSON build manifest destination; otherwise OUTPUT.build.json. Records source/generated-code identity, runtime ABI, compiler/target, matching runtime build configuration, strict numerical policy and compiled capabilities. |
--strict-declarations | Requires let or var when introducing a name, using the existing checker. |
Source limits and ordinary frontend diagnostics remain in force. Unsupported source reports E8001 at the Aner location. Runtime checks preserve located Aner diagnostics, including integer/conversion and array failures. Toolchain setup and native compiler failures also report the underlying build error.
Supported CPU subset
| Surface | Development support |
|---|---|
| Scalars | Int64, Float64, Float32, Bool, String, Unit. |
| Functions | Typed supported parameters/results, calls between supported file level functions, local bindings and assignment. |
| Control flow | Conditions, while, and iteration over Range. |
| Expressions | Supported scalar arithmetic, comparisons, logical operations and explicit numerical conversions. |
| Numerical buffers | Fixed Array<Int64>, Array<Float64> and Array<Float32>, plus admitted typed task output regions/results. |
| Parallel tasks | Checked parallel.repeat tasks with immutable scalar List parameters and exclusive typed output; explicit local or matching compiled cluster workers. See the exact compiled contract. |
| Array construction and access | Array<T>(size: ..., fill: ...), get, set, len, capacity, is_empty, copy, fill, brackets and supported compound assignment. |
| Native helpers | integer_div, type/type_of, dtype, and the checked aner.math functions sqrt, abs, pow, exp, log, log2, log10, round, floor, ceil, sin, cos, tan. |
| Addressed randomness | Explicit seed/draw random.uniform, random.integer, random.normal and random.bernoulli; no mutable generator or entropy acquisition. |
| Output | Scalar print calls in admitted serial code; task effects remain restricted. |
| Placement | CPU, selected by the development build's toolchain and target. |
The compiler checks every source construct it admits. Unsupported constructs are rejected even when a condition would make them unreachable. Generic functions and classes, unrestricted Any or object values, mutable or nested Lists, other dynamic collections, DynamicArray, NDArray, units, contextual scientific objects, data plans, Tensor/model execution, notebook state and GPU kernels remain outside the compiled subset. The scalar List and typed parallel boundary is deliberately narrower than the interpreted API. Existing interpreted APIs remain available through aner run and notebook sessions.
This boundary prevents a scientific object from losing its units, observation identity, fitted state or execution policy merely to enter the new compiler. A broader lowering must preserve that type's contract before becoming supported. There is no mixed execution mode in this milestone.
Numerical and storage contracts
The compiler uses the checked frontend's resolved types and calls, and the existing checked numerical/native storage helpers. Integer overflow, zero integer divisors, signed minimum edge cases, conversions and array bounds remain checked. Source order evaluation must preserve observable argument and mutation order. C++ undefined behavior does not define Aner's integer contract.
Floating point compilation disables fast math and contraction. The compiler does not select reduced precision, reassociate an expression under a new policy or silently move work to a GPU. Float32 storage and calculations retain their explicit type; Float64 remains distinct. Numerical agreement tests cover the supported common subset and its located failures.
Assigning or passing a fixed array retains shared mutable storage, as in interpreted Aner. let fixes the binding; it does not freeze the contents. .copy() allocates an independent buffer. Returned arrays retain their owner, so a function local descriptor does not leave a dangling buffer. There are no borrowed strided views or resizable arrays in this subset.
The existing array limits remain explicit: a shared 64 MiB execution quota for owned fixed array storage and a maximum of ten million elements per array. Aliases share the owner's reservation; an independent copy requires a new allocation. This is managed array accounting, not a process RSS limit. Native function calls retain a depth limit of 128. The interpreter also limits active AST evaluations to 256 and can therefore reject deep recursion earlier; these are separate execution resource limits. Native loops have no interpreter step counter; a native executable is not a sandbox or a time limited execution service.
Why C++20 is the first backend
The useful idea from Mojo’s vision is that researchers' own loops should become native instructions while language contracts stay visible. Its parameterization and ownership designs motivate explicit specialization and buffer lifetime contracts. Aner applies those lessons through its checked frontend and existing owners; this increment does not implement a general generic specialization system or borrow checker. Aner retains its own syntax, C++ foundation, checked scientific types and explicit parallel contexts. These references were reviewed on October 24, 2026; they are design evidence, not implementation dependencies.
| Alternative | Decision for this increment |
|---|---|
| Generated C++20 | Chosen: the repository already has a supported toolchain, strict floating point build policy and checked numerical/storage helpers. Generated code is inspectable and produces a real executable now. |
| LLVM | A reasonable later backend when direct optimization or code generation needs justify it. The repository had no working LLVM integration to extend; adding a development SDK would expand the initial dependency and maintenance scope. |
| MLIR | Consider when typed numerical/device transformations need a shared lowering representation. Mojo's architecture alone is insufficient reason to add it. |
| More interpreter optimizations | Useful for the broad interactive runtime, but do not remove AST dispatch from custom scalar loops. They remain a separate execution path. |
The backend consumes checked Aner operations rather than redefining parser or public language rules. The internal helper ABI is native-subset-v3; rebuild earlier generated artifacts and executables for this runtime. The build manifest records the selected ABI; generated source and linked native helpers must match it. There is no persistent compilation cache or claim of cross run specialization reuse. The executable's native helper boundary does not invoke the interpreter.
Measure the relevant work
The new path removes interpretation from supported user operations. Its performance still depends on the loop, allocations, checks, compiler target and workload size. It does not establish a universal speedup, GPU scaling or automatic distribution.
Keep compilation separate from execution. Compare the same source and input under aner run and the produced executable; record the exact source/build identity, CPU, compiler, precision, iteration count, whether startup and allocation are included, repeated timings and numerical agreement. A process level timing includes startup. To examine steady numerical work, repeat the kernel within the program and report that boundary explicitly. A matrix kernel benchmark alone does not measure compilation of user loops.
The following historical measurements use the earlier native-subset-v1 serial compiler, before the compiled parallel increment. They do not measure the current parallel/cluster path. On October 24, 2026, a Release source build on an Apple M2 Max (AppleClang 21, M1 compatible CPU instruction baseline) produced the following scoped results. Each mode and source had one warm up followed by five fresh process samples. Storage, computation and accumulation used strict Float64 with checked Int64 counters; exact stdout agreed between modes.
| Workload | Interpreted median | Native median | Separate compilation |
|---|---|---|---|
| 100,000 step user scalar loop | 36.758 ms | 6.884 ms | 2.095 s |
| User fixed array transformation, 4,096 cells × 16 passes | 31.791 ms | 7.194 ms | 2.170 s |
These are process wall times, including startup, allocation, checks, output and shutdown; the interpreter also parses and checks source on each run. They are not isolated kernel timers. Compilation includes source checking, C++ emission, configuration, compilation/linking and artifact publication. Repeated inner work amortizes startup but does not remove it. The raw samples and identities retain workload dimensions, source/generated-code/executable hashes, medians, observed ranges and median absolute deviations. No RSS or device memory measurement was taken. These two small CPU workloads do not predict other workloads, accelerators or cluster performance.
The compiled parallel guide describes the current task and worker boundary. Broader checked operations, relocatable SDK packaging, additional platform validation, generic specialization and accelerator code generation remain separate work. C++26 readiness keeps C++20 required while reporting exact toolchain/library observations and gating future adoption.