backend: emit bitwise ops as native ops (test.check PRNG ~2.4x)

Profiling the test.check distribution/large-sample slowness (jolt-i5if): the
hot path is the SplitMix PRNG, dominated by 64-bit mix arithmetic, and the
bitwise ops (bit-and/or/xor/not, shifts) were NOT in the backend native-ops
table — so (bit-xor a b) compiled to a var-deref through the variadic overlay
(__bit-xor) instead of a direct call, the way +/-/* already emit.

Map bit-and/or/xor/not to the Chez bitwise-and/ior/xor/not primitives (inlined
to native code; a non-integer operand now errors like the JVM instead of being
silently truncated) and the shifts to a direct helper call. bit-and-not stays on
its overlay — its only Scheme impl is 2-arg, so a value-position arity-3 use
would mis-emit.

mix-64 arithmetic 2.7x faster, raw split+rand-long 2.4x, gen/vector ~1.4x. The
remaining gap is the bignum-vs-native-long floor (~20x, substrate) plus the
generator machinery (deftype/fn dispatch, separate). Corpus rows added for value
position, bit-not, apply, and a full-64-bit unsigned shift.
This commit is contained in:
Yogthos 2026-06-28 11:25:52 -04:00
parent b5ea06c5c2
commit f17b68ccfe
4 changed files with 153 additions and 135 deletions

File diff suppressed because one or more lines are too long

View file

@ -539,11 +539,11 @@
(guard (e (#t #f)) (guard (e (#t #f))
(def-var! "clojure.core" "to-array-2d" (letrec ((to-array-2d (lambda (coll) (let fnrec4639 ((coll coll)) (jolt-invoke (var-deref "clojure.core" "to-array") (jolt-map (var-deref "clojure.core" "to-array") coll)))))) to-array-2d))) (def-var! "clojure.core" "to-array-2d" (letrec ((to-array-2d (lambda (coll) (let fnrec4639 ((coll coll)) (jolt-invoke (var-deref "clojure.core" "to-array") (jolt-map (var-deref "clojure.core" "to-array") coll)))))) to-array-2d)))
(guard (e (#t #f)) (guard (e (#t #f))
(def-var! "clojure.core" "unchecked-byte" (letrec ((unchecked-byte (lambda (x) (let fnrec4640 ((x x)) (jolt-invoke (var-deref "clojure.core" "bit-and") (jolt-invoke (var-deref "clojure.core" "int") x) 255))))) unchecked-byte))) (def-var! "clojure.core" "unchecked-byte" (letrec ((unchecked-byte (lambda (x) (let fnrec4640 ((x x)) (bitwise-and (jolt-invoke (var-deref "clojure.core" "int") x) 255))))) unchecked-byte)))
(guard (e (#t #f)) (guard (e (#t #f))
(def-var! "clojure.core" "unchecked-short" (letrec ((unchecked-short (lambda (x) (let fnrec4641 ((x x)) (jolt-invoke (var-deref "clojure.core" "bit-and") (jolt-invoke (var-deref "clojure.core" "int") x) 65535))))) unchecked-short))) (def-var! "clojure.core" "unchecked-short" (letrec ((unchecked-short (lambda (x) (let fnrec4641 ((x x)) (bitwise-and (jolt-invoke (var-deref "clojure.core" "int") x) 65535))))) unchecked-short)))
(guard (e (#t #f)) (guard (e (#t #f))
(def-var! "clojure.core" "unchecked-char" (letrec ((unchecked-char (lambda (x) (let fnrec4642 ((x x)) (jolt-invoke (var-deref "clojure.core" "char") (jolt-invoke (var-deref "clojure.core" "bit-and") (jolt-invoke (var-deref "clojure.core" "int") x) 65535)))))) unchecked-char))) (def-var! "clojure.core" "unchecked-char" (letrec ((unchecked-char (lambda (x) (let fnrec4642 ((x x)) (jolt-invoke (var-deref "clojure.core" "char") (bitwise-and (jolt-invoke (var-deref "clojure.core" "int") x) 65535)))))) unchecked-char)))
(guard (e (#t #f)) (guard (e (#t #f))
(def-var! "clojure.core" "unchecked-float" (letrec ((unchecked-float (lambda (x) (let fnrec4643 ((x x)) (jolt-invoke (var-deref "clojure.core" "double") x))))) unchecked-float))) (def-var! "clojure.core" "unchecked-float" (letrec ((unchecked-float (lambda (x) (let fnrec4643 ((x x)) (jolt-invoke (var-deref "clojure.core" "double") x))))) unchecked-float)))
(guard (e (#t #f)) (guard (e (#t #f))

View file

@ -37,6 +37,16 @@
"even?" "jolt-even?" "odd?" "jolt-odd?" "pos?" "jolt-pos?" "neg?" "jolt-neg?" "even?" "jolt-even?" "odd?" "jolt-odd?" "pos?" "jolt-pos?" "neg?" "jolt-neg?"
"zero?" "jolt-zero?" "identity" "jolt-identity" "nil?" "jolt-nil?" "some?" "jolt-some?" "zero?" "jolt-zero?" "identity" "jolt-identity" "nil?" "jolt-nil?" "some?" "jolt-some?"
"ex-info" "jolt-ex-info" "ex-info" "jolt-ex-info"
;; bit ops: and/or/xor/not are Chez bitwise primitives (inlined to native code,
;; no helper call); operands must be integers (a non-integer errors, like the
;; JVM). The shifts keep their helpers (Java >>> masking / arithmetic shift) but
;; emit a direct call instead of var-deref + the variadic overlay.
;; and/or/xor/not map to variadic Chez bitwise prims (safe as a value at any
;; arity). bit-and-not is left to its overlay: its only Scheme impl is 2-arg, so
;; a value-position arity-3 use (via the variadic overlay) would mis-emit.
"bit-and" "bitwise-and" "bit-or" "bitwise-ior" "bit-xor" "bitwise-xor" "bit-not" "bitwise-not"
"bit-shift-left" "jolt-bit-shift-left" "bit-shift-right" "jolt-bit-shift-right"
"unsigned-bit-shift-right" "jolt-unsigned-bit-shift-right"
;; positional protocol-method dispatch (defprotocol-emitted shims) — bind ;; positional protocol-method dispatch (defprotocol-emitted shims) — bind
;; directly to the records.ss entry points so a protocol call doesn't var-deref. ;; directly to the records.ss entry points so a protocol call doesn't var-deref.
"protocol-dispatch1" "protocol-dispatch1" "protocol-dispatch2" "protocol-dispatch2" "protocol-dispatch1" "protocol-dispatch1" "protocol-dispatch2" "protocol-dispatch2"
@ -66,7 +76,10 @@
"cons" #(= % 2) "filter" #(= % 2) "remove" #(= % 2) "into" #(= % 2) "cons" #(= % 2) "filter" #(= % 2) "remove" #(= % 2) "into" #(= % 2)
"take" #(= % 2) "drop" #(= % 2) "map" #(>= % 2) "apply" #(>= % 2) "take" #(= % 2) "drop" #(= % 2) "map" #(>= % 2) "apply" #(>= % 2)
"reduce" #(or (= % 2) (= % 3)) "range" #(and (>= % 0) (<= % 3)) "reduce" #(or (= % 2) (= % 3)) "range" #(and (>= % 0) (<= % 3))
"ex-info" #(or (= % 2) (= % 3))}) "ex-info" #(or (= % 2) (= % 3))
"bit-and" #(= % 2) "bit-or" #(= % 2) "bit-xor" #(= % 2) "bit-not" #(= % 1)
"bit-shift-left" #(= % 2) "bit-shift-right" #(= % 2)
"unsigned-bit-shift-right" #(= % 2)})
;; jolt's comparison ops are vacuously true at arity 1 and DON'T inspect the arg, ;; jolt's comparison ops are vacuously true at arity 1 and DON'T inspect the arg,
;; but Scheme's < demands a number even there — special-case. ;; but Scheme's < demands a number even there — special-case.

View file

@ -1545,6 +1545,11 @@
{:suite "numbers / variadic bit ops" :label "bit-and-not 3" :expected "8" :actual "(bit-and-not 12 6 3)"} {:suite "numbers / variadic bit ops" :label "bit-and-not 3" :expected "8" :actual "(bit-and-not 12 6 3)"}
{:suite "numbers / variadic bit ops" :label "bit-and single arg throws" :expected :throws :actual "(bit-and 5)"} {:suite "numbers / variadic bit ops" :label "bit-and single arg throws" :expected :throws :actual "(bit-and 5)"}
{:suite "numbers / variadic bit ops" :label "bit-or keeps binary fast path" :expected "3" :actual "(bit-or 1 2)"} {:suite "numbers / variadic bit ops" :label "bit-or keeps binary fast path" :expected "3" :actual "(bit-or 1 2)"}
{:suite "numbers / variadic bit ops" :label "bit-not" :expected "-6" :actual "(bit-not 5)"}
{:suite "numbers / variadic bit ops" :label "bit-and in value position" :expected "8" :actual "(reduce bit-and [15 9 12])"}
{:suite "numbers / variadic bit ops" :label "bit-xor in value position" :expected "0" :actual "(reduce bit-xor 0 [1 2 3])"}
{:suite "numbers / variadic bit ops" :label "bit-or via apply" :expected "7" :actual "(apply bit-or [1 2 4])"}
{:suite "numbers / variadic bit ops" :label "unsigned-shift over a full 64-bit value" :expected "17179869183" :actual "(unsigned-bit-shift-right -1 30)"}
{:suite "predicates / nil & boolean" :label "nil? true" :expected "true" :actual "(nil? nil)"} {:suite "predicates / nil & boolean" :label "nil? true" :expected "true" :actual "(nil? nil)"}
{:suite "predicates / nil & boolean" :label "nil? false" :expected "false" :actual "(nil? 0)"} {:suite "predicates / nil & boolean" :label "nil? false" :expected "false" :actual "(nil? 0)"}
{:suite "predicates / nil & boolean" :label "some? true" :expected "true" :actual "(some? 0)"} {:suite "predicates / nil & boolean" :label "some? true" :expected "true" :actual "(some? 0)"}