Clause
← All documentation

Type specialization

Speed mode (-O2) uses inferred facts to remove runtime checks (proofs) and may clone a function into representation variants guarded by runtime tag checks. -O0 and --no-type-specialization produce generic code only: every check stays, every compound access goes through its runtime service.

Proofs

A proof comes only from facts that hold whenever the code runs:

Specifications never prove anything. term() and none() (code inference found unreachable) prove nothing; anonymous fun bodies get no proofs.

OperationProven byGenerated code
+ - *both operands small-integer ranges, result range within the immediate payloaddecode, compute and encode inline; no tag, overflow or fallback
+ - *one or both operands small-integer rangesthe proven tag tests go; overflow check and service fallback stay
tuple patternargument or scrutinee fact: exact tuples of the pattern's arityshape test removed (unproven: tested inline)
tuple elementa shape test or fact proving the tupleinline load of the element
list patternfact: surely a cons cellshape test removed (otherwise a primary-tag test at every level)
head, taila cons test or fact proving the cellinline loads
exact literal [], {}, small integer, atomalways (at every level)word comparison: equal immediates are one word
list generatorgenerator input fact: a proper listcons test and cell reads inline on every iteration

Facts reach patterns of function heads (caller-joined arguments), body matches (the right side) and case/try clauses (the scrutinee or try body value); element, head and tail facts follow extractions. An inline read is stored into a root slot like a service output, so frame lowering keeps it as a term across safepoints (reload rule). The only pointer conversions in generated code are these proven reads and the header loads of inline tuple shape tests (proven.cell). Layout constants come from abi/term.hpp; the runtime asserts them against its private cell layouts.

Variants

LLVM cloning/simplification replaces proven checks inside the clone. A bounded dispatcher tests every constrained tag and forwards context and arguments to a variant or the generic body; the public symbol and ABI do not change. No spec assumptions or fast-math flags are introduced.

Leaf inlining

At -O2 (codegen/lowering_leaf_inlining.cpp) a call of a small function of the same module that calls no Erlang code except other such leaves lowers the callee's clauses in place of the call. A call costs a frame push and a return through it (about 3 ns), often more than the leaf's own work, and a loop whose step calls a leaf becomes a loop without calls.

Constructions

At -O2 (codegen/lowering_constructions.cpp) a match of a tuple or a cons cell built in the same generated function reads the words it was built of instead of the cell; inlined leaves and funs make most such pairs ({Q, R} = divmod(X, 7), case lookup(K) of {ok, V} -> ..., the {Mapped, Acc} of a mapfoldl fun).

Current effect

Proofs act on real source: local functions whose callers pass small integers, tuples and lists lose their tag, shape and service checks (proofs, checked by CTest codegen_proofs and run as the executable golden executables_proofs). A variant must save more instructions than its dispatch costs; one removed tag test of an argument rarely does, so source variants are usually planned and then rejected by the growth limit. Focused LLVM fixtures inject repeated tag checks to exercise hits, fallbacks and growth rollback with native execution.

Executable goldens run under every policy in full mode; fast mode runs -O0 (generic) and -O2 with specialization (proofs).

codegen_measurements writes measurements/<config>/measurements.json in the codegen test build directory (compile time, IR/object bytes, variants, proven reads, native time). Times are descriptive, never thresholds.