The guard flag was named .init-once.<global>, and . and - are ordinary symbol constituents, so (defvar .init-once.x i64 7) beside a computed x emitted the same symbol twice: the dev build died at the assembler on both backends, and the flag's Bool was registered over the user's global in Emit.globals so the store came out as an i1. It is .init~once.<global> now; ~ terminates a symbol in the reader, the same trick destructure~N uses. test/programs/dev-rerun.flan carries such a global and no new printed line. Three places said a defconst is the linker's image on one backend and a constructor's stores on the other and a re-run reaches neither. Tast.const_init splits on the initialiser and not on the form, so that holds only for a constant initialiser; a computed defconst is guarded like a defvar on x86 and refused outright by emit.ml's const. The sentences now say that, including the divergence. emit.ml also claimed Check.no_transfer_in_init made it impossible to leave the guarded branch between the store and the flag. It is syntactic over the written initialiser only: a callee can signal unhandled and take the call's transfer edge out, leaving the flag false — which is what should happen, since the next run retries. Read off the emitted IR for such a program. The x86 float-Rem comment says why the dead movabs before fmod is kept, and its mid-sentence line break is gone; math3.flan's first float-% line prints six values, not four.
136 lines
5.7 KiB
Plaintext
136 lines
5.7 KiB
Plaintext
;;;; The rest of libm, at both widths — what math.flan and math2.flan left out.
|
|
;;;;
|
|
;;;; The rule those two set holds here unchanged and is the only reason this
|
|
;;;; file looks the way it does: none of these is correctly rounded under
|
|
;;;; IEEE-754 except sqrt, fabs, the rounding three and fmod, so every value
|
|
;;;; below is one whose answer is exact in binary — zero, one, a power of two,
|
|
;;;; a perfect square, a perfect cube. A case that pinned glibc's last bit
|
|
;;;; would pass here and fail on wasi-libc.
|
|
;;;;
|
|
;;;; The acceptance table builds this at -O0 as well, and that run is the one
|
|
;;;; that matters: at -O2 LLVM folds a libm call over two literals and leaves
|
|
;;;; no symbol to resolve, which is how a missing -lm hid the first time.
|
|
|
|
(defn show [x f32] ()
|
|
(print x)
|
|
(print " "))
|
|
|
|
(defn show64 [x f64] ()
|
|
(print x)
|
|
(print " "))
|
|
|
|
;;; The operands of the (% ...) block at the end. See the note there for why
|
|
;;; they are globals.
|
|
(defvar rem-a f64 7.5)
|
|
(defvar rem-na f64 -7.5)
|
|
(defvar rem-b f64 2.0)
|
|
(defvar rem-nb f64 -2.0)
|
|
(defvar rem-z f64 0.0)
|
|
(defvar rem-huge f64 1e300)
|
|
(defvar rem-a32 f32 7.5)
|
|
(defvar rem-na32 f32 -7.5)
|
|
(defvar rem-b32 f32 2.0)
|
|
(defvar rem-z32 f32 0.0)
|
|
|
|
(defn main [] i32
|
|
;; The f32 half. tan, the three inverses, the three logarithms and exp.
|
|
(show (tan-f32 0.0)) ; 0
|
|
(show (asin-f32 0.0)) ; 0
|
|
(show (acos-f32 1.0)) ; 0
|
|
(show (atan-f32 0.0)) ; 0
|
|
(show (log-f32 1.0)) ; 0
|
|
(show (log2-f32 8.0)) ; 3
|
|
(show (log10-f32 1000.0)) ; 3
|
|
(show (exp-f32 0.0)) ; 1
|
|
(println "")
|
|
|
|
(show (fmod-f32 7.0 4.0)) ; 3
|
|
;; The sign follows the dividend and not the divisor, which is the line a
|
|
;; caller reaching for a modulo gets wrong. Written down as a case.
|
|
(show (fmod-f32 -1.0 3.0)) ; -1
|
|
(show (hypot-f32 3.0 4.0)) ; 5
|
|
(show (cbrt-f32 27.0)) ; 3
|
|
;; The negative root, where (pow-f32 x 0.33333334) would be NaN: pow goes
|
|
;; through a logarithm and cbrt does not.
|
|
(show (cbrt-f32 -8.0)) ; -2
|
|
(show (abs-f32 -2.5)) ; 2.5
|
|
(println "")
|
|
|
|
;; The f64 half, at the same exact values. This block is the whole point of
|
|
;; the f64 face existing: before it, every one of these was a cast down to
|
|
;; f32 and back, and the cast down is where the precision went.
|
|
(show64 (sqrt-f64 16.0)) ; 4
|
|
(show64 (sin-f64 0.0)) ; 0
|
|
(show64 (cos-f64 0.0)) ; 1
|
|
(show64 (tan-f64 0.0)) ; 0
|
|
(show64 (asin-f64 0.0)) ; 0
|
|
(show64 (acos-f64 1.0)) ; 0
|
|
(show64 (atan-f64 0.0)) ; 0
|
|
(show64 (atan2-f64 0.0 1.0)) ; 0
|
|
(println "")
|
|
|
|
(show64 (log-f64 1.0)) ; 0
|
|
(show64 (log2-f64 1024.0)) ; 10
|
|
(show64 (log10-f64 100.0)) ; 2
|
|
(show64 (exp-f64 0.0)) ; 1
|
|
(show64 (pow-f64 2.0 10.0)) ; 1024
|
|
(show64 (fmod-f64 7.0 4.0)) ; 3
|
|
(show64 (hypot-f64 3.0 4.0)) ; 5
|
|
(show64 (cbrt-f64 8.0)) ; 2
|
|
(show64 (abs-f64 -1.5)) ; 1.5
|
|
(println "")
|
|
|
|
;; The f64 rounding family, which is libm's where the f32 one is Flan's —
|
|
;; and it agrees with the Flan one where they overlap: half away from zero,
|
|
;; so -2.5 goes to -3 and not to -2.
|
|
(show64 (floor-f64 -2.5)) ; -3
|
|
(show64 (ceil-f64 -2.5)) ; -2
|
|
(show64 (round-f64 -2.5)) ; -3
|
|
(show64 (round-f64 2.5)) ; 3
|
|
(println "")
|
|
|
|
;; Integer magnitude, one per width.
|
|
(print (abs-i32 -7)) (print " ") ; 7
|
|
(print (abs-i64 (i64 -7))) (print " ") ; 7
|
|
(print (abs-i32 7)) (print " ") ; 7
|
|
;; tau is 2pi at both widths. Pinning the relation rather than the digits is
|
|
;; what catches a constant written to too few of them.
|
|
(print (= tau-f32 (* 2.0 pi-f32))) (print " ")
|
|
(print (= tau-f64 (* 2.0 pi-f64)))
|
|
(println "")
|
|
|
|
;; pi is the one value here that can be pinned without pinning a libm: it is
|
|
;; a literal the compiler rounds, so it is the same on every target.
|
|
(println (and (> pi-f64 3.14159265) (< pi-f64 3.14159266)))
|
|
|
|
;; The operator spelling of the same function. A typed float (% x y) is
|
|
;; fmod: LLVM lowers it to `frem`, which at -O0 becomes a call to fmod or
|
|
;; fmodf, and the x86 backend calls the same two symbols. Everything here
|
|
;; comes through a global rather than a literal for the reason arith.flan
|
|
;; gives — a literal pair is folded before either backend sees an operator,
|
|
;; and the folded answer would be evidence about the constant folder and
|
|
;; not about the lowering.
|
|
;;
|
|
;; Six values on the first line: the four sign combinations, where a modulo
|
|
;; written in place of a remainder disagrees — the sign follows the dividend
|
|
;; — and then the f32 pair, asking fmodf the same. The second line is the
|
|
;; two answers IEEE defines where an integer % would have died: a zero
|
|
;; divisor and a NaN dividend are both NaN, not a signal.
|
|
(show64 (% rem-a rem-b)) ; 1.5
|
|
(show64 (% rem-na rem-b)) ; -1.5
|
|
(show64 (% rem-a rem-nb)) ; 1.5
|
|
(show64 (% rem-na rem-nb)) ; -1.5
|
|
(show (% rem-a32 rem-b32)) ; 1.5
|
|
(show (% rem-na32 rem-b32)) ; -1.5
|
|
(println "")
|
|
|
|
(show64 (% rem-a rem-z)) ; nan
|
|
(show64 (% (/ rem-z rem-z) rem-b)) ; nan
|
|
(show (% rem-a32 rem-z32)) ; nan
|
|
;; A quotient past anything an integer could name, which is where a
|
|
;; remainder written as x - trunc(x/y)*y has nothing left to truncate.
|
|
(show64 (% rem-huge rem-b)) ; 0
|
|
(show64 (% rem-b rem-huge)) ; 2
|
|
(println "")
|
|
0)
|