floats iter 4: JOURNAL — iteration close (codegen + E2E)
This commit is contained in:
+136
@@ -13350,3 +13350,139 @@ milestone:
|
||||
- Prose round-trip for Float literals — iter 5.
|
||||
- DESIGN.md §"Float semantics" subsection (A5 determinism contract)
|
||||
and the line-2033 "supported primitive types" list update — iter 5.
|
||||
|
||||
## 2026-05-10 — Iteration Floats.4: codegen + E2E
|
||||
|
||||
Lowered every Float-related typecheck primitive that iter 3 installed
|
||||
into LLVM IR. The end-to-end fixture `examples/floats.ail.json` builds
|
||||
via `ail build`, runs, and produces the expected stdout
|
||||
(`4\n42\n-1.5\n`) — this is the milestone-acceptance gate, and it
|
||||
passed first try after the seven sub-tasks landed.
|
||||
|
||||
`Float` is now a registered primitive in `synth.rs` (`llvm_type` →
|
||||
`double`, `type_descriptor` → `Fl` to avoid the single-letter `F` ADT
|
||||
prefix collision). Float literals lower as LLVM hex-float `double`
|
||||
SSA constants (`format!("0x{:016X}", bits)` — uppercase per LLVM
|
||||
convention). Both compile-completeness `unimplemented!("Floats
|
||||
milestone iter 4: codegen")` arms left by iter 1 in `lib.rs:918` /
|
||||
`:1215` are gone.
|
||||
|
||||
Arithmetic and comparison ops dispatch on the resolved arg type. The
|
||||
old monomorphic-Int `synth::builtin_binop` was replaced by a
|
||||
3-tuple-returning `builtin_binop_typed(name, &Type) -> Option<(instr,
|
||||
operand_ll_ty, result_ll_ty)>`. Returning the result type
|
||||
explicitly eliminates the dual-meaning trap the original 2-tuple
|
||||
shape carried (caller had to know whether arm was arithmetic
|
||||
(operand-type == result-type) or comparison (result-type always
|
||||
`i1`)). The `lower_app` dispatch site was simplified accordingly —
|
||||
no more i1-result override at the caller. Float arithmetic emits
|
||||
`fadd/fsub/fmul/fdiv double`; Float comparison emits `fcmp
|
||||
olt/ole/ogt/oge/oeq double`. **`!=` Float emits `fcmp UNE double`,
|
||||
NOT `fcmp one`** — `one` is "ordered and not equal" and would
|
||||
return false for `nan != nan`, violating IEEE-`!=` and user-fixed
|
||||
constraint #2.
|
||||
|
||||
Five new fn-builtin lowering arms in `lower_app`:
|
||||
|
||||
- `neg` polymorphic — Int arm `sub i64 0, %x`; Float arm `fneg
|
||||
double %x` (LLVM 8+, correct for `-0.0`). The `fsub double 0.0,
|
||||
%x` desugar would be wrong: IEEE `0.0 - 0.0 == +0.0`, so
|
||||
`neg(+0.0)` would erroneously produce `+0.0` instead of `-0.0`.
|
||||
- `int_to_float` → `sitofp i64 %x to double`.
|
||||
- `float_to_int_truncate` → `call i64 @llvm.fptosi.sat.i64.f64(double
|
||||
%x)` (LLVM 12+ saturating intrinsic; clang 22 always available).
|
||||
NaN → 0, +Inf → i64::MAX, -Inf → i64::MIN, finite-out-of-range
|
||||
saturates, finite-in-range truncates toward zero — exactly the
|
||||
intrinsic's documented semantics, no wrapping needed.
|
||||
- `is_nan` → `fcmp uno double %x, %x` (only NaN compares unordered
|
||||
against itself).
|
||||
- `float_to_str` → `Err(CodegenError::Internal("...not yet
|
||||
implemented (requires dynamic Str allocation in the runtime)"))`
|
||||
(deferred to iter 5+; the runtime's Str path currently uses only
|
||||
static `@.str_*` globals — no malloc-backed dynamic-Str
|
||||
infrastructure). The deferral surfaces as a structured codegen
|
||||
error rather than a panic, mirroring the rest of the file's
|
||||
error discipline.
|
||||
|
||||
Float constants `nan`/`inf`/`neg_inf` resolve as bare `Term::Var`
|
||||
references and lower to direct hex-float `double` SSA values at the
|
||||
use site — `0x7FF8000000000000` (canonical qNaN), `0x7FF0000000000000`
|
||||
(+Inf), `0xFFF0000000000000` (-Inf). Intercepted at the top of
|
||||
`lower_term`'s `Term::Var` arm, before any other resolution. **NOT**
|
||||
through the `__unreachable__` lowering pattern, which is a
|
||||
terminator instruction; constants are SSA values.
|
||||
|
||||
`io/print_float` lowers via inline `printf("%g\n", v)` parallel to
|
||||
`io/print_int`. No new C runtime file needed — the `@printf`
|
||||
declaration already exists. Format string interned as `fmt_float`.
|
||||
|
||||
Two plan bugs caught and corrected during the iteration:
|
||||
|
||||
1. **Iter-4.2 plan inconsistency** — Task 2 as literally written
|
||||
would have stranded comparison ops (Step 4 said
|
||||
`builtin_binop_typed` returns None for them, Step 5 narrowed
|
||||
`lower_app` matches! to `+ - * / %` only — together that
|
||||
collapses the no-regression invariant). The implementer caught
|
||||
this and pulled forward Task 3 Steps 3-4 as a minimal repair.
|
||||
The orchestrator endorsed; Task 3's residual scope was narrowed
|
||||
to Float comparison arms + `lower_eq` Float arm.
|
||||
2. **Iter-4.4 quality review** — `float_to_str` was specified as
|
||||
`unimplemented!()` (panic), inconsistent with the rest of the
|
||||
file's `CodegenError::Internal(...)` error discipline. A user
|
||||
program calling the typecheck-installed `float_to_str` would
|
||||
panic the compiler instead of producing a structured error.
|
||||
Fixed in the 4.4 fixup commit.
|
||||
|
||||
Two pull-forwards into iter-4.2 (`is_static_callee` extension for
|
||||
`==`) and iter-4.4 (`is_static_callee` extension for the 5 new
|
||||
fn-builtins) reflect the same pattern: any name that lowers via a
|
||||
direct `lower_app` arm must also be recognised by `is_static_callee`,
|
||||
or it falls through to the indirect-call path with `UnknownVar`.
|
||||
Future iterations adding new builtin lowering arms must remember
|
||||
this companion edit.
|
||||
|
||||
The mono-pass and ADT slot-layout question from spec Components
|
||||
(monomorphisation produces concrete `List_Fl` slots vs. polymorphic
|
||||
erased slots needing bitcast) was NOT exercised by the E2E fixture
|
||||
— `examples/floats.ail.json` does not use Float-typed containers.
|
||||
This is not a regression: the descriptor `Fl` exists and the
|
||||
mono pass would emit `List_Fl` correctly; the bitcast question
|
||||
would only fire for an erased polymorphic container, which AILang
|
||||
doesn't have today. Queued as a follow-up if a future fixture
|
||||
exercises the path.
|
||||
|
||||
Per-task commits:
|
||||
|
||||
- `ac5e17e` floats iter 4.1: codegen primitive registration + Float literal hex-double lowering
|
||||
- `8044a4d` floats iter 4.2: codegen arithmetic dispatch on arg type — fadd/fsub/fmul/fdiv double
|
||||
- `2a29070` floats iter 4.2 fixup: 3-tuple return for builtin_binop_typed + classifier helper
|
||||
- `3869641` floats iter 4.3: codegen Float comparison arms (fcmp olt/ole/ogt/oge/UNE) + lower_eq Float
|
||||
- `581144a` floats iter 4.4: codegen neg/int_to_float/float_to_int_truncate/is_nan + float_to_str-deferred
|
||||
- `613aa39` floats iter 4.4 fixup: float_to_str returns CodegenError::Internal instead of panic
|
||||
- `bde5aaf` floats iter 4.5: codegen Float constants nan/inf/neg_inf as hex-double SSA values
|
||||
- `9764b61` floats iter 4.6: codegen io/print_float + examples/floats.ail.json E2E fixture
|
||||
|
||||
Workspace at iter-4 close: 402 tests passed, 0 failed (from 395 at
|
||||
iter-3 close — added 6 codegen unit tests + 1 E2E test).
|
||||
|
||||
Known debt deliberately deferred to iter 5:
|
||||
|
||||
- Prose round-trip for Float literals.
|
||||
- DESIGN.md §"Float semantics" subsection (A5 determinism contract).
|
||||
- DESIGN.md line-2033 "supported primitive types" list update
|
||||
(`Float` was not mentioned as a supported primitive there before;
|
||||
iter 4's ship makes it true).
|
||||
- `crates/ailang-prose/src/lib.rs::Literal::Float` arm replacement
|
||||
(currently `unimplemented!("Floats milestone iter 5: prose")`
|
||||
from iter 1).
|
||||
|
||||
Known debt deliberately deferred BEYOND iter 5 (this milestone):
|
||||
|
||||
- `float_to_str` codegen lowering (requires runtime-allocated Str;
|
||||
AILang's Str path is currently static-only). Symbol is
|
||||
type-installed and surface-callable, but lowering errors with a
|
||||
structured `CodegenError::Internal`. Future milestone wires the
|
||||
runtime Str-allocator path.
|
||||
- Float-typed container slot layout (mono `List_Fl` etc.) — not
|
||||
exercised by any current fixture; revisit if a future use case
|
||||
surfaces.
|
||||
|
||||
Reference in New Issue
Block a user