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.
89 lines
3.6 KiB
Plaintext
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)
|