spike/js/survey.sh is the x86 sweep's shape with one deliberate difference in what it counts. That backend is behind, so a refusal there is a regression and its strict mode fails on one. This is a dialect, so a refusal is the design working -- a pointer, an allocator, a Map, the FFI and conditions are refused permanently and correctly. What fails the @js alias is a DIFFER, which is a wrong answer, and a CRASH, which is JS this backend emitted and node would not run. Two probes carry the decisions the corpus does not reach. p1-int-semantics prints wrapping at all eight widths, a multiply past 2^53, truncating division with a negative operand, shifts whose count is out of range, bitwise over a u32, f32 that is not a double, and the conversions both ways -- 35 lines, all identical to the LLVM build. It found two real bugs: >>> binds tighter than & in JavaScript, so a bit-and on a u32 answered -1; and a 64-bit value through Number() rounds to 53 bits before it can be truncated, so (i32 i64hi) answered 0 where it must answer -1. p2-value-copies goes past values.flan to the cases a shallow copy would pass: a struct inside a struct, a struct returned out of a function, an element read out of an array of structs, and a global. Also fixed, and all three were found by the sweep rather than by reading: a unit-typed call in statement position was compiled to an expression nobody emitted, so (load-xs) silently did not happen; an arrow body that starts with a brace is a block, so a zeroed array of structs was a syntax error; and a bounds message must carry the index expression's location, not the form's, because that is the one emit.ml passes to check_at. Render reads an Option's tag as field 0 and a union's as field 0, which is the LLVM layout and not this one, so both are answered here rather than refused. fdefers is dropped rather than refused: nothing in the dialect can start a transfer, so the transfer exit path is unreachable, and refusing it would have refused every program that writes a plain defer.
107 lines
4.3 KiB
Plaintext
107 lines
4.3 KiB
Plaintext
;;;; The evidence for lib/js.ml's integer decisions, run by the survey.
|
|
;;;;
|
|
;;;; JavaScript has one number type and it is a double. Flan has eight integer
|
|
;;;; types and they wrap. Every line below is a place where the two disagree
|
|
;;;; unless the backend normalises, and the survey diffs this program's output
|
|
;;;; against the LLVM build's byte for byte — so a wrong answer here is a
|
|
;;;; DIFFER and not a discussion.
|
|
;;;;
|
|
;;;; Everything goes through a global rather than a literal, for the reason
|
|
;;;; arith.flan gives: a literal operand is exactly what a constant folder
|
|
;;;; removes, and a folded program does not contain the code being tested.
|
|
|
|
(defvar i8hi i8 127)
|
|
(defvar i16hi i16 32767)
|
|
(defvar i32hi i32 2147483647)
|
|
(defvar u8hi u8 255)
|
|
(defvar u16hi u16 65535)
|
|
(defvar u32hi u32 4294967295)
|
|
(defvar i64hi i64 9223372036854775807)
|
|
(defvar u64zero u64 0)
|
|
|
|
(defvar one8 i8 1)
|
|
(defvar one16 i16 1)
|
|
(defvar one32 i32 1)
|
|
(defvar oneu8 u8 1)
|
|
(defvar oneu16 u16 1)
|
|
(defvar oneu32 u32 1)
|
|
(defvar one64 i64 1)
|
|
(defvar oneu64 u64 1)
|
|
|
|
(defvar big32 i32 123456789)
|
|
(defvar big64 i64 1234567890123)
|
|
(defvar three i32 3)
|
|
(defvar three64 i64 3)
|
|
(defvar seven i32 7)
|
|
(defvar negsev i32 -7)
|
|
(defvar shift i32 33)
|
|
(defvar shift8 i8 9)
|
|
(defvar f32one f32 1.0)
|
|
(defvar f32big f32 16777217.0)
|
|
|
|
(defn main [] i32
|
|
;; ── Wrapping at every width ──────────────────────────────────────
|
|
;; The top of the type plus one. A JS number would answer 128, 32768,
|
|
;; 2147483648, 256, 65536, 4294967296 and never wrap at all.
|
|
(println (+ i8hi one8))
|
|
(println (+ i16hi one16))
|
|
(println (+ i32hi one32))
|
|
(println (+ u8hi oneu8))
|
|
(println (+ u16hi oneu16))
|
|
(println (+ u32hi oneu32))
|
|
(println (+ i64hi one64))
|
|
(println (- u64zero oneu64)) ; the top of u64, which no literal spells
|
|
|
|
;; A multiply whose exact product passes 2^53, where a * b in JS is not
|
|
;; exact and Math.imul is. 123456789 * 123456789 = 15241578750190521, which
|
|
;; is above 2^53, so a double rounds it before the truncation can happen.
|
|
(println (* big32 big32))
|
|
(println (* big64 big64))
|
|
|
|
;; ── Truncating division, and the sign of a remainder ─────────────
|
|
;; C truncates toward zero and so does JS's %, but (a / b) | 0 is only the
|
|
;; same answer below 2^31, which is why the backend uses Math.trunc.
|
|
(println (/ seven three))
|
|
(println (/ negsev three))
|
|
(println (% seven three))
|
|
(println (% negsev three))
|
|
(println (/ big64 three64))
|
|
|
|
;; ── Shifts, with the count masked to the operand's width ─────────
|
|
;; 33 is not a legal shift count for a 32-bit type, and the checker only
|
|
;; rejects a *literal* out of range, so this is the computed case emit.ml
|
|
;; masks and this backend has to mask too. JS masks a 32-bit shift itself
|
|
;; and has no 8- or 16-bit shift at all, which is why the mask is written
|
|
;; out rather than relied on.
|
|
(println (<< one32 shift))
|
|
(println (<< one8 shift8))
|
|
(println (>> i32hi one32))
|
|
(println (>> (- (i32 0) i32hi) one32)) ; arithmetic: signed stays signed
|
|
(println (>> u32hi oneu32)) ; logical: unsigned stays unsigned
|
|
(println (<< one64 (i64 65)))
|
|
|
|
;; ── Bitwise over an unsigned 32-bit value ────────────────────────
|
|
;; JS bitwise operators produce a *signed* 32-bit result, so every one of
|
|
;; these needs the >>> 0 back.
|
|
(println (bit-and u32hi u32hi))
|
|
(println (bit-or u32hi oneu32))
|
|
(println (bit-xor u32hi oneu32))
|
|
|
|
;; ── f32 is not a double ──────────────────────────────────────────
|
|
;; 16777217 is the first integer a float cannot hold, so this prints
|
|
;; 16777216 under a real f32 and 16777218 under a double.
|
|
(println (+ f32big f32one))
|
|
(println (/ (f32 1.0) (f32 3.0)))
|
|
(println (/ (f64 1.0) (f64 3.0)))
|
|
|
|
;; ── The conversions, both ways ───────────────────────────────────
|
|
(println (i8 i32hi))
|
|
(println (u8 (- (i32 0) one32)))
|
|
(println (i32 i64hi))
|
|
(println (i64 big32))
|
|
(println (u64 i64hi))
|
|
(println (f64 i64hi))
|
|
(println (i32 (f64 2.9)))
|
|
(println (i32 (f64 -2.9)))
|
|
0)
|