flan/test/programs/fill.flan
Joseph Ferano 99f519ba6f Two byte fills: (filled BYTE) and (sentinel-filled)
DISCUSS.org's sentinel-fill idea, built as two builtins because the author
asked for both: a memset with a byte the program picks, and the fixed
DE AD BE EF pattern a hex dump reads as DEADBEEF.

Both are spelled the way (zeroed) is — the value of whatever type is
expected of them — so (set grid (filled 0xFF)) fills a place and there is
no second, place-taking form beside set.

What may be filled is numbers, and structs and fixed arrays built out of
them. Everything else is refused by name: a filled dyn is a collector root
pointing at nothing, a filled Vec header frees a wild address, a filled
slice length is a bounds check that passes, and a filled bool is an i1 to
LLVM and a whole byte to x86, which is the one divergence this feature
cannot have.

The byte fill is llvm.memset / rep stosb. The four-byte pattern cannot be
a memset on either side — the intrinsic takes one repeated i8 — so it is a
counted dword loop in emit.ml and rep stosd in x86.ml, with the pattern
bytes and their little-endian word living once, in Emit. A size that is
not a multiple of four ends on DE, DE AD, or DE AD BE.
2026-09-20 18:15:19 +07:00

89 lines
3.6 KiB
Plaintext

;;;; (filled b) and (sentinel-filled) — the two byte fills, read back as bytes.
;;;;
;;;; The whole point of this program is that every row is a *byte* and not a
;;;; value: the sentinel's contract is "a hex dump reads DEADBEEF", which is a
;;;; claim about which byte lands at which address, and only reading the bytes
;;;; back in address order can check it. The u32 rows are the same claim from
;;;; the other side — a little-endian load of DE AD BE EF is 0xEFBEADDE, which
;;;; is 4022250974, so a backend that wrote the word the other way round would
;;;; print 3735928559 here and be caught.
;;;;
;;;; The lengths are chosen for the tail. 8 is a whole number of patterns, 9
;;;; ends on DE, 6 ends on DE AD, and 7 ends on DE AD BE — the three truncated
;;;; endings and the one that is not truncated at all.
(defstruct Words [a u32 b u32])
;;;; A computed global initialiser: a fill is never a constant the linker can
;;;; write, so this one goes through the startup function on both backends.
(defvar gfill [4 u8] (filled 0x41))
(defvar gsent [5 u8])
(defn bytes4 [b [4 u8]] ()
(dotimes [i 4] (print (at b i)) (print " ")))
(defn main [] i32
;; One byte, repeated. 0xFF is the -1 fill a debug allocator wants.
(let [a (array 4 u8)]
(set a (filled 0xFF))
(bytes4 a) (println "")) ; 255 255 255 255
;; The byte is an expression, not a literal: this is a memset with the
;; operand in a register, and the register path is the one a constant would
;; otherwise hide.
(let [v (u8 7)
a (array 4 u8)]
(set a (filled v))
(bytes4 a) (println "")) ; 7 7 7 7
;; (filled 0) is (zeroed), byte for byte, and saying so here is what keeps
;; the two from drifting.
(let [a (array 4 u8)]
(set a (filled 0))
(bytes4 a) (println "")) ; 0 0 0 0
;; Four bytes, ascending, with nothing truncated.
(let [a (array 8 u8)]
(set a (sentinel-filled))
(dotimes [i 8] (print (at a i)) (print " "))
(println "")) ; 222 173 190 239 x2
;; The three truncated tails.
(let [a (array 9 u8)]
(set a (sentinel-filled))
(dotimes [i 9] (print (at a i)) (print " "))
(println "")) ; ... ends on 222
(let [a (array 6 u8)]
(set a (sentinel-filled))
(dotimes [i 6] (print (at a i)) (print " "))
(println "")) ; ... ends on 222 173
(let [a (array 7 u8)]
(set a (sentinel-filled))
(dotimes [i 7] (print (at a i)) (print " "))
(println "")) ; ... ends on 222 173 190
;; The same pattern read as words rather than as bytes, which is what says
;; which end the DE is at.
(let [w (Words {})]
(set w (sentinel-filled))
(print (.a w)) (print " ") (print (.b w)) (println ""))
;; A nested aggregate: a fixed array of structs is plain data all the way
;; down, so the fill reaches every byte of it.
(let [g (array 2 Words)]
(set g (filled 0xFF))
(print (.a (at g 0))) (print " ") (print (.b (at g 1))) (println ""))
;; The globals. [gfill] was filled before main ran; [gsent] is filled here.
(dotimes [i 4] (print (at gfill i)) (print " "))
(println "") ; 65 65 65 65
(set gsent (sentinel-filled))
(dotimes [i 5] (print (at gsent i)) (print " "))
(println "") ; 222 173 190 239 222
;; A fill in value position rather than as the value of a [set]: the same
;; bytes, reached through a temporary instead of through the place.
(bytes4 (filled 0xFF)) (println "") ; 255 255 255 255
0)