# scriptc LLVM code-generation helper This out-of-process helper owns LLVM assembly and object emission for scriptc. It is built against exactly LLVM 22.1.8 and currently contains only the AArch64 backend. The shipping `@scriptc/llvm-darwin-arm64` package builds and carries the executable; the compiler resolves that package directly and never searches `PATH` for this program. The ordinary workspace `pnpm -r build` does not rebuild this release artifact. On macOS arm64, install CMake, Ninja, and Homebrew `llvm@22`, then build it explicitly when working on native emission or preparing a package: ```console $ brew install cmake ninja llvm@22 $ pnpm --filter @scriptc/llvm-darwin-arm64 build:native ``` The protocol is intentionally small and versioned: The packaged helper itself requires macOS 15 or newer because that is the minimum version of the pinned LLVM bottle it statically links. Its emitted assembly and objects separately target macOS 14 via the triple below. ```console scriptc-llvm-codegen version --format=json scriptc-llvm-codegen emit --input app.ll --output app.o --filetype obj \ --target arm64-apple-macosx14.0.0 --opt-level 2 \ --relocation-model pic --diagnostic-format json --source-path app.ts ``` Emission uses LLVM 22's default per-module O2 pipeline, including coroutine lowering, verifies before and after optimization, and publishes through a private sibling file so a failed or interrupted request cannot truncate the requested output. Repeating `--output` divides the program into that many partitions, which are compiled concurrently and linked together. The helper splits the emitter's LLVM assembly text directly, so partitions also parse concurrently; input outside the emitter's narrow grammar is parsed whole and split with LLVM instead. Internal definitions that no other definition reaches are left out of every partition, and the others become hidden so partitions can reference each other. At opt-level 0 each partition is compiled independently. Optimized partitions follow the ThinLTO model: each is simplified and summarized on its own thread, a thin link decides which small functions every partition imports for inlining and which hidden definitions become internal again, and each partition is then optimized and compiled on its own thread. Partition placement depends only on the module and the partition count, so output never depends on the host. With `--split codegen`, several outputs divide only code generation. The helper optimizes the module whole, as it does for a single output, then divides its functions in module order into runs of about the same instruction count, one per output, and generates code for every output on its own thread. Linking the outputs in order lays the functions out as one output would. A local definition that another output uses becomes hidden so the objects can reference each other; the others stay local. Every output after the first is generated from a copy of the optimized module read back from bitcode with its use-list order, so each function's machine code matches a single output except for jumps to definitions that another output also uses. When one run would hold more than three quarters of the module's instructions, the first output holds the whole module and the other outputs are empty objects. Placement depends only on the optimized module and the output count. `--split codegen` requires an optimized build and no `--cache-dir`; scriptc passes it for optimized executables with more than 128 KiB of LLVM text that stay one partition. In a `--cpu-dispatch` module, each function has a baseline copy and an x86-64-v3 copy marked `scriptc-cpu-copy`. Partitioning places each x86-64-v3 copy with its baseline copy. After optimization, an x86-64-v3 copy whose code has vectors of at most 128 bits, none wider, and no rounding or bit-count intrinsic in a loop takes its baseline copy's optimized code, and its calls to baseline copies call their x86-64-v3 copies (`src/cpu-copies.h`). WebAssembly code generation differs from LLVM's default pipeline in two ways that do not change its output. The pass that gives multiple-entry loops a single entry runs only on functions whose control flow is irreducible; its reachability analysis grows with the square of a function's block count even when it has nothing to change. Object emission also leaves out the module pass that collects external symbols for assembly declarations, so each function's machine code is freed once it is printed instead of when the whole module is compiled. `--cache-dir