Luce / engineering
Learn Luce LuciaOS

MIR optimization

Luce removes unused functions and obviously dead MIR. LLVM then does the machine-oriented work: inlining clamp_double, turning * 2 into a shift, and replacing a small branch with csel.

Two optimizers, two jobs. Luce knows which language operations have effects and which declarations are unreachable. LLVM knows instruction costs, registers, and the target processor. The boundary keeps each decision with the system that has the facts to make it.

Three passes

Reachability pruneStarting from the selected entry, follow direct calls, function values, interface witnesses, class deinitializers, and closure edges; remove everything unreachable.
Dead instructionsRemove unread pure results and the ownership temporaries that became redundant, while respecting runtime effects and destruction order.
Register compactionRenumber surviving registers into a dense space and update every use so serialization and downstream lowering stay small.

Reachability is more than a call graph

Edges prune must followA function can remain reachable without a direct call
Direct code

Calls and selected entry points.

Stored behavior

Function values, bound methods, closure bodies, and interface witness slots.

Lifetime behavior

Class deinit functions and layouts referenced by live values.

An imported standard module arrives as source, so the initial program may contain many declarations the entry never reaches. Pruning is what makes an unused hosted helper free: its Builtin effect never survives into the artifact.

Dead does not mean unread

A result register may be unread while its instruction still matters. A host call can print. A container mutation can change a shared object. A release can run deinit, close a file, or join a task. The effects table classifies what can disappear and what must stay.

Optimization cannot change destruction order, resource behavior, traps, or host effects. A retain/release pair is removable only when none of those observations can change.

Why the optimizer stops here

Constant folding, inlining, loop transforms, vectorization, instruction selection, register allocation, and target scheduling are LLVM's work. Reimplementing them over MIR would create a second optimizer to maintain and a second place for target-sensitive bugs.

Luce MIR owns

Language reachability, explicit runtime effects, cleanup semantics, and compact protocol.

LLVM owns

General scalar and loop optimization, target instructions, physical registers, and object emission.

Verify, transform, verify

The input must already pass the MIR verifier. After passes delete blocks and renumber registers, the whole result passes again. This makes each optimizer bug a compiler diagnostic in the build path rather than malformed native code downstream.