Section 05: Variant Codegen
Context: J6 showed that creating Success(42) takes 12 instructions (alloca, store tag, GEP, store value, load tag, insertvalue, load payload, insertvalue) when it could be 2 insertvalue instructions. J6 also showed that Success and Failure match arms compile to identical code (same offset extraction) but aren’t merged. This also fixes M14 (uninitialized payload) as a side effect — insertvalue doesn’t read uninitialized memory.
Cross-section: This subsumes the M14 fix from Section 02 for unit variants. Unit variant payloads use zeroinitializer (not undef) — this is the consistent invariant across the plan. While undef in unused positions is technically well-defined LLVM IR, zeroinitializer is strictly safer and matches Section 02’s recommendation.
05.1 Fix M7 — insertvalue for Variant Construction
Journey: J6 (confirmed J6, J11, J12) | Severity: MEDIUM
File(s): compiler/ori_llvm/src/codegen/ (variant construction)
Replace:
; Current — 12 instructions:
%variant = alloca { i64, [1 x i64] }, align 8
%tag.ptr = getelementptr inbounds ..., ptr %variant, i32 0, i32 0
store i64 0, ptr %tag.ptr, align 4
%payload.ptr = getelementptr inbounds ..., ptr %variant, i32 0, i32 1, i64 0
store i64 42, ptr %payload.ptr, align 4
%tag = load i64, ptr %tag.ptr, align 4
%s0 = insertvalue { i64, [1 x i64] } zeroinitializer, i64 %tag, 0
%payload = load i64, ptr %payload.ptr, align 4
%s1 = insertvalue { i64, [1 x i64] } %s0, i64 %payload, 1, 0
With:
; Target — 2 instructions:
%s0 = insertvalue { i64, [1 x i64] } zeroinitializer, i64 0, 0 ; tag = 0 (Some)
%s1 = insertvalue { i64, [1 x i64] } %s0, i64 42, 1, 0 ; payload = 42
- Find the variant construction code in codegen
- Replace alloca+store+load pattern with direct
insertvaluechain - Handle unit variants:
insertvaluewith tag only, payload left aszeroinitializer(avoids M14) - Handle multi-field payload variants (e.g.,
Rect(w: int, h: int)) - Verify: Journey 6
Success(42)compiles to 2 insertvalue instructions - Verify: Journey 12
Noneconstruction has no uninitialized load
05.2 Fix M8 — Deduplicate Identical Match Arms
Journey: J6 | Severity: MEDIUM
File(s): compiler/ori_llvm/src/codegen/ (match codegen)
In Journey 6, the extract function’s Success and Failure arms generate identical code (both extract payload[0] from the same offset). They should share a single block.
; Current — 2 identical blocks:
bb2: ; Success arm
%proj.alloca = alloca %ori.Result2, ...
store %ori.Result2 %0, ptr %proj.alloca
%proj.payload = getelementptr ... i32 0, i32 1
%proj.1 = load i64, ptr %proj.payload
br label %bb1
bb3: ; Failure arm — IDENTICAL!
%proj.alloca1 = alloca %ori.Result2, ...
store %ori.Result2 %0, ptr %proj.alloca1
%proj.payload2 = getelementptr ... i32 0, i32 1
%proj.14 = load i64, ptr %proj.payload2
br label %bb1
Target: single block when the codegen for different arms is structurally identical.
- Analyze: how common is this pattern? (Only when all arms extract from the same offset)
- Alternative: LLVM’s MergeIdenticalBlocks pass may handle this — check if it does
- Confirmed: LLVM O2 eliminates the switch entirely (both arms extract same value → dead switch)
- If LLVM handles it: consider this LOW priority (optimization passes clean it up) — CONFIRMED: no codegen change needed
05.3 Completion Checklist
- Variant construction uses
insertvaluechain (no alloca+store+load) - Unit variant construction produces no uninitialized loads (M14 fixed as side effect)
- Match arm deduplication evaluated (deferred to LLVM passes — O2 eliminates dead switches)
-
./test-all.shgreen (11,955 pass, 1 flaky unrelated to changes) -
diagnostics/valgrind-aot.sh— 0 errors (7/7 pass)
Exit Criteria: grep -c "alloca.*variant" *.ll shows no variant construction allocas. Journey 6 Success(42) compiles to 2-3 instructions.