Skip to content

Commit 5eb229b

Browse files
committed
Add support for doubles/f64 to uvclang
1 parent b32938b commit 5eb229b

4 files changed

Lines changed: 327 additions & 155 deletions

File tree

uvclang/include/math.h

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,13 @@
33

44
// Minimal ISO <math.h> for the uvclang/UVM target.
55
//
6-
// UVM has single-precision (f32) floating-point only — there are no f64 ops —
7-
// so this header declares just the `float` (`*f`) functions, each of which
8-
// uvclang lowers inline to a UVM `*_f32` instruction (see codegen's
9-
// `is_float_builtin` / `gen_intrinsic`). The `double` versions (`sin`, `sqrt`,
10-
// ...) are intentionally *not* declared: a program that calls them would make
11-
// clang emit f64 IR that the back-end cannot lower.
6+
// UVM has both single-precision (f32) and double-precision (f64) floating-point
7+
// ops, so this header declares the `float` (`*f`) functions and their `double`
8+
// counterparts. uvclang lowers each inline to the matching UVM `*_f32`/`*_f64`
9+
// instruction (see codegen's `is_float_builtin` / `gen_intrinsic`).
1210
//
1311
// Constants are provided in both the traditional `double` spelling and a `*_F`
14-
// `float` spelling (referenced by <uvm/math.h>'s DEG2RAD); with UVM's f32-only
15-
// arithmetic the values collapse to float at use anyway.
12+
// `float` spelling (referenced by <uvm/math.h>'s DEG2RAD).
1613

1714
#define M_E_F 2.71828182845904523536f
1815
#define M_LOG2E_F 1.44269504088896340736f
@@ -50,4 +47,16 @@ float fabsf(float x);
5047
float powf(float x, float y);
5148
float exp2f(float x);
5249

50+
// Double-precision functions backed by UVM f64 instructions.
51+
double sin(double x);
52+
double cos(double x);
53+
double tan(double x);
54+
double asin(double x);
55+
double acos(double x);
56+
double atan(double x);
57+
double sqrt(double x);
58+
double fabs(double x);
59+
double pow(double x, double y);
60+
double exp2(double x);
61+
5362
#endif // __UVCLANG_MATH_H__

uvclang/plan.md

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ the front-end to clang, so its scope is the IR→UVM lowering plus the thin driv
4444
- **Back-end codegen: Phases 0–8 done** (arithmetic, control flow, memory,
4545
globals, calls, intrinsics, corner cases) and **Phase 9 done** (the
4646
clang-driver front-end — `uvclang foo.c` drives clang + back-end in one
47-
command). Scalar **`float` (f32) is supported** (arithmetic, compare, int↔float
48-
conversions, libm f32 calls; see *Floating point* below), and the **printf
49-
family** (`printf`/`sprintf`/`snprintf`/…) is implemented on the callee-side
47+
command). Scalar **`float` (f32) and `double` (f64) are supported**
48+
(arithmetic, compare, int↔float conversions, float↔double conversions, libm
49+
f32/f64 calls; see *Floating point* below), and the **printf family**
50+
(`printf`/`sprintf`/`snprintf`/…) is implemented on the callee-side
5051
`va_arg` support (see *C standard library*`<stdio.h>`). The back-end
5152
differential suite (`.ll` path) is **108 pass / 0 fail / 0 skip**; the
5253
end-to-end front-end suite (`tests/run_frontend_tests.sh`, single-command
@@ -152,34 +153,39 @@ support ordinary variadic C (the `printf` family).
152153
- **`zext`** = already satisfied by the invariant (or mask/`trunc` to source
153154
width). **`sext`** = `sx_iW_iW2`. **`trunc`** = `trunc_uW`.
154155

155-
### Floating point (f32) — DONE
156-
UVM has **single-precision only** — a fixed set of `*_f32` ops and no f64
157-
instructions at all — so uvclang supports `float` and rejects `double`
158-
*arithmetic* (a `double` value can still be stored/moved, just not computed on;
159-
`fpext`/`fptrunc` error). This mirrors ncc, which is also f32-only.
160-
- **Slot representation is identical to `i32`:** a float lives in the low 32
161-
bits of its slot as the IEEE-754 pattern, upper bits zero (`Value::as_f32`
162-
reads `f32::from_bits(low32)`). So `load float`/`store float` are just
163-
`load_u32`/`store_u32`, and float phi/select/call-args/returns reuse the exact
156+
### Floating point (f32 + f64) — DONE
157+
UVM has parallel single- and double-precision op sets (`*_f32` and `*_f64`), so
158+
uvclang supports both `float` and `double` as first-class scalars. Each float
159+
codegen path picks its ops/width through one helper, `float_kind(ty)`, which
160+
maps `float``("f32", 32)` and `double``("f64", 64)`.
161+
- **Slot representation mirrors the same-width integer:** a `float` lives in the
162+
low 32 bits of its slot as the IEEE-754 pattern (like `i32`); a `double` fills
163+
the whole 64-bit slot (like `i64`). So `load/store` of a float/double are just
164+
`load_u32`/`load_u64` etc., and float phi/select/call-args/returns reuse the
164165
integer value-moving paths (see `scalar_width`).
165-
- **Arithmetic/compare:** `fadd/fsub/fmul/fdiv``add/sub/mul/div_f32`;
166-
`fneg` → xor the sign bit (`^ 0x80000000`); `fcmp` → the six ordered VM ops
167-
(`eq/lt/le/gt/ge_f32`, all NaN-false) with `ne_f32` for `une`, and the
168-
remaining unordered/`one`/`ord`/`uno` predicates composed by boolean
169-
complement/disjunction.
170-
- **Conversions:** `sitofp` sign-extends to i64 then `i64_to_f32`; `uitofp` uses
171-
`i64_to_f32` on the already-zero-extended slot; `fptosi`/`fptoui`
172-
`f32_to_i32` then re-truncate to restore the width invariant (target width
173-
≤ 32; float→i64 is unsupported). Float constants lower to `push_f32` (finite)
174-
or the raw bit pattern (inf/NaN); `float` globals emit `.f32`.
175-
- **libm / intrinsics:** `sinf/cosf/tanf/asinf/acosf/atanf/sqrtf/powf` (libcalls)
176-
and `llvm.sqrt/sin/cos/fabs/pow.f32` map to the matching `*_f32` op; `fabsf`/
177-
`llvm.fabs` clear the sign bit; `llvm.fmuladd.f32``mul_f32; add_f32` (two
178-
roundings — matches a baseline no-FMA native target; a float test compares
166+
- **Arithmetic/compare:** `fadd/fsub/fmul/fdiv``add/sub/mul/div_{f32,f64}`;
167+
`fneg` → xor the sign bit (`^ 0x8000_0000` for f32, `^ 0x8000_0000_0000_0000`
168+
for f64); `fcmp` → the six ordered VM ops (`eq/lt/le/gt/ge_{f32,f64}`, all
169+
NaN-false) with `ne_*` for `une`, and the remaining unordered/`one`/`ord`/`uno`
170+
predicates composed by boolean complement/disjunction (which act on the 0/1
171+
results, so they're width-agnostic).
172+
- **Conversions:** `sitofp` sign-extends to i64 then `i64_to_{f32,f64}`; `uitofp`
173+
uses `i64_to_{f32,f64}` on the already-zero-extended slot. `fptosi`/`fptoui`
174+
from f32 to a ≤32-bit int use `f32_to_i32` then re-truncate; every other case
175+
(any float → i64, or f64 → any int) goes through `f64_to_i64` (widening an f32
176+
with `f32_to_f64` first) then truncates to the target width. `fpext`
177+
(float→double) → `f32_to_f64`; `fptrunc` (double→float) → `f64_to_f32`. Float
178+
constants lower to `push_f32`/`push_f64` (finite) or the raw bit pattern
179+
(inf/NaN); `float`/`double` globals emit `.f32`/`.f64`.
180+
- **libm / intrinsics:** `sinf/…/powf` and `sin/…/pow` (libcalls) plus
181+
`llvm.sqrt/sin/cos/fabs/pow.{f32,f64}` map to the matching `*_f32`/`*_f64` op
182+
(dispatched on the call name / intrinsic result type); `fabsf`/`fabs`/
183+
`llvm.fabs` clear the sign bit; `llvm.fmuladd``mul; add` (two roundings —
184+
matches a baseline no-FMA native target; the float tests compare
179185
transcendentals with tolerance to absorb any last-bit host/libm difference).
180186
- **Header:** a minimal ISO `<math.h>` in `uvclang/include/` declares the f32
181-
functions (+ `M_PI`/`M_PI_F` constants). Covered by `tests/floats.c`
182-
(differential vs native + self-checking, -O0/-O1/-O2).
187+
and f64 functions (+ `M_PI`/`M_PI_F` constants). Covered by `tests/floats.c`
188+
and `tests/doubles.c` (differential vs native + self-checking, -O0/-O1/-O2).
183189

184190
### Memory & pointers
185191
- **`alloca`:** assign each alloca a fixed offset within the function's
@@ -446,15 +452,17 @@ verbatim without consuming an argument.
446452
|---|---|---|
447453
| `assert` || ✅ passing path (`uvm_*` self-checks) + failure path (`xfail_assert.c`) |
448454

449-
### `<math.h>` — partial (f32)
455+
### `<math.h>` — partial (f32 + f64)
450456
A minimal ISO `<math.h>` ships in `uvclang/include/`, declaring the
451457
single-precision functions uvclang lowers to UVM `*_f32` ops
452-
(`sinf`/`cosf`/`tanf`/`asinf`/`acosf`/`atanf`/`sqrtf`/`fabsf`/`powf`) plus the
453-
`M_*`/`M_*_F` constants. Exercised by `tests/floats.c`. **Not** provided: the
454-
`double` variants (UVM has no f64), and f32 ops with no UVM instruction
455-
(`floorf`/`ceilf`/`fmodf`/`expf`/`logf`/`atan2f`/…) — add when a test needs
456-
them. Note `uvm/math.h` is a separate UVM-specific header (min/max/lerp macros),
457-
not ISO `<math.h>`.
458+
(`sinf`/`cosf`/`tanf`/`asinf`/`acosf`/`atanf`/`sqrtf`/`fabsf`/`powf`) and their
459+
double-precision counterparts lowered to `*_f64` ops
460+
(`sin`/`cos`/`tan`/`asin`/`acos`/`atan`/`sqrt`/`fabs`/`pow`) plus the
461+
`M_*`/`M_*_F` constants. Exercised by `tests/floats.c` and `tests/doubles.c`.
462+
**Not** provided: functions with no UVM instruction
463+
(`floorf`/`ceilf`/`fmodf`/`expf`/`logf`/`atan2f`/… and their `double` forms) —
464+
add when a test needs them. Note `uvm/math.h` is a separate UVM-specific header
465+
(min/max/lerp macros), not ISO `<math.h>`.
458466

459467
### Out of scope for now
460468
`<time.h>`, `<locale.h>`, `<signal.h>`, `<setjmp.h>`, `<errno.h>`, `<wchar.h>`
@@ -709,5 +717,5 @@ the doom path (doom's only libc external is `@strlen`, already covered).
709717
`print`, window/framebuffer, keyboard/mouse input) is in scope (Phase 10);
710718
only **audio** (sound/MIDI) is deferred.
711719
- No support for IR features clang `-O2` C output never emits (vectors,
712-
`double`/f64 arithmetic — UVM is f32-only, exception handling, etc.) until a
713-
test needs them. (Scalar `float`/f32 *is* supported — see *Floating point*.)
720+
exception handling, etc.) until a test needs them. (Scalar `float`/f32 and
721+
`double`/f64 *are* supported — see *Floating point*.)

0 commit comments

Comments
 (0)