sb-simd: Fix a bunch of bugs

64-bit integer min/max don't have actual instructions, fake instead
Fix signed lane extraction not sign-extending
Add float abs/neg
Fix fsqrt encoding
Fix u32.4-from-u64.2/s32.4-from-s64.2 family, wrong size
Remove 64-bit integer vector mul, instruction doesn't exist
Add missing exports for integer min/max
Align spilled neon registers properly
Validate more vector instruction forms properly
Fix movi immediates
This commit is contained in:
Sylvia Harrington 2026-08-26 19:02:38 +01:00 committed by Stas Boukarev
parent 62f4bd1032
commit e4e628ab84
10 changed files with 340 additions and 92 deletions

View file

@ -109,6 +109,12 @@
(define-fake-vop u64.2-horizontal+ (a)
(sb-simd::%two-arg-u64+ (%u64.2-lane-extract a 0) (%u64.2-lane-extract a 1)))
(define-fake-vop two-arg-u64.2-min (a b)
(%u64.2-bit-select (%two-arg-u64.2> b a) a b))
(define-fake-vop two-arg-u64.2-max (a b)
(%u64.2-bit-select (%two-arg-u64.2> a b) a b))
(define-fake-vop s64.2-pair-min (a b)
(%make-s64.2 (sb-simd::%two-arg-s64-min (%s64.2-lane-extract a 0) (%s64.2-lane-extract a 1))
(sb-simd::%two-arg-s64-min (%s64.2-lane-extract b 0) (%s64.2-lane-extract b 1))))
@ -129,3 +135,9 @@
(define-fake-vop s64.2-horizontal+ (a)
(sb-simd::%two-arg-s64+ (%s64.2-lane-extract a 0) (%s64.2-lane-extract a 1)))
(define-fake-vop two-arg-s64.2-min (a b)
(%s64.2-bit-select (%two-arg-s64.2> b a) a b))
(define-fake-vop two-arg-s64.2-max (a b)
(%s64.2-bit-select (%two-arg-s64.2> a b) a b))

View file

@ -313,7 +313,10 @@
(:info lane)
(:results (dst :scs (,scalar-reg)))
(:generator
(inst umov dst src lane ,element)))
(inst ,(if (and (eql sign :s) (not (eql width 64)))
'smov
'umov)
dst src lane ,element)))
(define-custom-vop ,(name "~a~d.~d-LANE-INSERT" sign width count)
(:args (src1 :scs (int-neon-reg) :target dst)
(src2 :scs (,scalar-reg) :to :save))

View file

@ -85,6 +85,8 @@
(f32.4-andc2 #:bic (f32.4) (f32.4 f32.4) :cost 1 :suffix '(:16b))
(f32.4-not #:not (f32.4) (f32.4) :cost 1 :suffix '(:16b))
(f32.4-sqrt #:fsqrt (f32.4) (f32.4) :cost 15 :suffix '(:4s))
(f32.4-abs #:fabs (f32.4) (f32.4) :cost 1 :suffix '(:4s))
(f32.4-neg #:fneg (f32.4) (f32.4) :cost 1 :suffix '(:4s))
(f32.4-bit-select #:bsl (f32.4) (u32.4 f32.4 f32.4) :cost 1 :encoding :neon-rmw :suffix '(:16b))
(f32.4-lane-extract nil (f32) (f32.4 imm2) :cost 1 :encoding :custom)
(f32.4-lane-insert nil (f32.4) (f32.4 f32 imm2) :cost 1 :encoding :custom)
@ -133,6 +135,8 @@
(f64.2-andc2 #:bic (f64.2) (f64.2 f64.2) :cost 1 :suffix '(:16b))
(f64.2-not #:not (f64.2) (f64.2) :cost 1 :suffix '(:16b))
(f64.2-sqrt #:fsqrt (f64.2) (f64.2) :cost 15 :suffix '(:2d))
(f64.2-abs #:fabs (f64.2) (f64.2) :cost 1 :suffix '(:2d))
(f64.2-neg #:fneg (f64.2) (f64.2) :cost 1 :suffix '(:2d))
(f64.2-bit-select #:bsl (f64.2) (u64.2 f64.2 f64.2) :cost 1 :encoding :neon-rmw :suffix '(:16b))
(f64.2-lane-extract nil (f64) (f64.2 imm1) :cost 1 :encoding :custom)
(f64.2-lane-insert nil (f64.2) (f64.2 f64 imm1) :cost 1 :encoding :custom)
@ -266,10 +270,10 @@
(u32.4-from-f32.4 #:fcvtnu (u32.4) (f32.4) :cost 5 :suffix '(:4s))
(u32.4-from-u16.8 nil (u32.4) (u16.8) :cost 1 :encoding :custom)
(u32.4-from-u16.8-hi nil (u32.4) (u16.8) :cost 1 :encoding :custom)
(u32.4-from-u64.2 #:xtn (u32.4) (u64.2) :cost 1 :suffix '(:4h))
(u32.4-from-u64.2-hi #:xtn2 (u32.4) (u32.4 u64.2) :cost 1 :encoding :neon-rmw :suffix '(:8h))
(u32.4-from-u64.2-saturating #:uqxtn (u32.4) (u64.2) :cost 1 :suffix '(:4h))
(u32.4-from-u64.2-saturating-hi #:uqxtn2 (u32.4) (u32.4 u64.2) :cost 1 :encoding :neon-rmw :suffix '(:8h))
(u32.4-from-u64.2 #:xtn (u32.4) (u64.2) :cost 1 :suffix '(:2s))
(u32.4-from-u64.2-hi #:xtn2 (u32.4) (u32.4 u64.2) :cost 1 :encoding :neon-rmw :suffix '(:4s))
(u32.4-from-u64.2-saturating #:uqxtn (u32.4) (u64.2) :cost 1 :suffix '(:2s))
(u32.4-from-u64.2-saturating-hi #:uqxtn2 (u32.4) (u32.4 u64.2) :cost 1 :encoding :neon-rmw :suffix '(:4s))
(make-u32.4 nil (u32.4) (u32 u32 u32 u32) :cost 1 :encoding :fake-vop)
(u32.4-values nil (u32 u32 u32 u32) (u32.4) :cost 1 :encoding :fake-vop)
(u32.4-broadcast #:dup (u32.4) (u32) :cost 1 :suffix '(:4s))
@ -329,8 +333,8 @@
(two-arg-u64.2-and #:and (u64.2) (u64.2 u64.2) :cost 1 :associative t :suffix '(:16b))
(two-arg-u64.2-or #:orr (u64.2) (u64.2 u64.2) :cost 1 :associative t :suffix '(:16b))
(two-arg-u64.2-xor #:eor (u64.2) (u64.2 u64.2) :cost 1 :associative t :suffix '(:16b))
(two-arg-u64.2-max #:umax (u64.2) (u64.2 u64.2) :cost 3 :associative t :suffix '(:2d))
(two-arg-u64.2-min #:umin (u64.2) (u64.2 u64.2) :cost 3 :associative t :suffix '(:2d))
(two-arg-u64.2-max nil (u64.2) (u64.2 u64.2) :cost 3 :associative t :encoding :fake-vop)
(two-arg-u64.2-min nil (u64.2) (u64.2 u64.2) :cost 3 :associative t :encoding :fake-vop)
(u64.2-andc1 nil (u64.2) (u64.2 u64.2) :cost 1 :encoding :fake-vop)
(u64.2-andc2 #:bic (u64.2) (u64.2 u64.2) :cost 1 :suffix '(:16b))
(u64.2-not #:not (u64.2) (u64.2) :cost 1 :suffix '(:16b))
@ -338,7 +342,6 @@
(two-arg-u64.2+-saturating #:uqadd (u64.2) (u64.2 u64.2) :cost 2 :suffix '(:2d) :associative t)
(two-arg-u64.2- #:sub (u64.2) (u64.2 u64.2) :cost 2 :suffix '(:2d))
(two-arg-u64.2--saturating #:uqsub (u64.2) (u64.2 u64.2) :cost 2 :suffix '(:2d))
(two-arg-u64.2* #:mul (u64.2) (u64.2 u64.2) :cost 2 :suffix '(:2d) :associative t)
(two-arg-u64.2= #:cmeq (u64.2) (u64.2 u64.2) :cost 1 :suffix '(:2d))
(two-arg-u64.2/= nil (u64.2) (u64.2 u64.2) :cost 2 :associative t :encoding :fake-vop)
(two-arg-u64.2> #:cmhi (u64.2) (u64.2 u64.2) :cost 1 :suffix '(:2d))
@ -478,10 +481,10 @@
(s32.4-from-f32.4 #:fcvtns (s32.4) (f32.4) :cost 5 :suffix '(:4s))
(s32.4-from-s16.8 nil (s32.4) (s16.8) :cost 1 :encoding :custom)
(s32.4-from-s16.8-hi nil (s32.4) (s16.8) :cost 1 :encoding :custom)
(s32.4-from-s64.2 #:xtn (s32.4) (s64.2) :cost 1 :suffix '(:4h))
(s32.4-from-s64.2-hi #:xtn2 (s32.4) (s32.4 s64.2) :cost 1 :encoding :neon-rmw :suffix '(:8h))
(s32.4-from-s64.2-saturating #:sqxtn (s32.4) (s64.2) :cost 1 :suffix '(:4h))
(s32.4-from-s64.2-saturating-hi #:sqxtn2 (s32.4) (s32.4 s64.2) :cost 1 :encoding :neon-rmw :suffix '(:8h))
(s32.4-from-s64.2 #:xtn (s32.4) (s64.2) :cost 1 :suffix '(:2s))
(s32.4-from-s64.2-hi #:xtn2 (s32.4) (s32.4 s64.2) :cost 1 :encoding :neon-rmw :suffix '(:4s))
(s32.4-from-s64.2-saturating #:sqxtn (s32.4) (s64.2) :cost 1 :suffix '(:2s))
(s32.4-from-s64.2-saturating-hi #:sqxtn2 (s32.4) (s32.4 s64.2) :cost 1 :encoding :neon-rmw :suffix '(:4s))
(make-s32.4 nil (s32.4) (s32 s32 s32 s32) :cost 1 :encoding :fake-vop)
(s32.4-values nil (s32 s32 s32 s32) (s32.4) :cost 1 :encoding :fake-vop)
(s32.4-broadcast #:dup (s32.4) (s32) :cost 1 :suffix '(:4s))
@ -539,8 +542,8 @@
(two-arg-s64.2-and #:and (s64.2) (s64.2 s64.2) :cost 1 :associative t :suffix '(:16b))
(two-arg-s64.2-or #:orr (s64.2) (s64.2 s64.2) :cost 1 :associative t :suffix '(:16b))
(two-arg-s64.2-xor #:eor (s64.2) (s64.2 s64.2) :cost 1 :associative t :suffix '(:16b))
(two-arg-s64.2-max #:smax (s64.2) (s64.2 s64.2) :cost 3 :associative t :suffix '(:2d))
(two-arg-s64.2-min #:smin (s64.2) (s64.2 s64.2) :cost 3 :associative t :suffix '(:2d))
(two-arg-s64.2-max nil (s64.2) (s64.2 s64.2) :cost 3 :associative t :encoding :fake-vop)
(two-arg-s64.2-min nil (s64.2) (s64.2 s64.2) :cost 3 :associative t :encoding :fake-vop)
(s64.2-andc1 nil (s64.2) (s64.2 s64.2) :cost 1 :encoding :fake-vop)
(s64.2-andc2 #:bic (s64.2) (s64.2 s64.2) :cost 1 :suffix '(:16b))
(s64.2-not #:not (s64.2) (s64.2) :cost 1 :suffix '(:16b))
@ -548,7 +551,6 @@
(two-arg-s64.2+-saturating #:sqadd (s64.2) (s64.2 s64.2) :cost 2 :suffix '(:2d) :associative t)
(two-arg-s64.2- #:sub (s64.2) (s64.2 s64.2) :cost 2 :suffix '(:2d))
(two-arg-s64.2--saturating #:sqsub (s64.2) (s64.2 s64.2) :cost 2 :suffix '(:2d))
(two-arg-s64.2* #:mul (s64.2) (s64.2 s64.2) :cost 2 :suffix '(:2d) :associative t)
(two-arg-s64.2= #:cmeq (u64.2) (s64.2 s64.2) :cost 1 :suffix '(:2d))
(two-arg-s64.2/= nil (u64.2) (s64.2 s64.2) :cost 2 :associative t :encoding :fake-vop)
(two-arg-s64.2> #:cmgt (u64.2) (s64.2 s64.2) :cost 1 :suffix '(:2d))
@ -646,7 +648,6 @@
(u64.2-min two-arg-u64.2-min nil)
(u64.2+ two-arg-u64.2+ 0)
(u64.2+-saturating two-arg-u64.2+-saturating 0)
(u64.2* two-arg-u64.2* 1)
(s8.16-and two-arg-s8.16-and +s8-true+)
(s8.16-or two-arg-s8.16-or +s8-false+)
(s8.16-xor two-arg-s8.16-xor +s8-false+)
@ -677,8 +678,7 @@
(s64.2-max two-arg-s64.2-max nil)
(s64.2-min two-arg-s64.2-min nil)
(s64.2+ two-arg-s64.2+ 0)
(s64.2+-saturating two-arg-s64.2+-saturating 0)
(s64.2* two-arg-s64.2* 1))
(s64.2+-saturating two-arg-s64.2+-saturating 0))
(:comparisons
(f32.4= two-arg-f32.4= u32.4-and +u32-true+)
(f32.4< two-arg-f32.4< u32.4-and +u32-true+)

View file

@ -2170,6 +2170,8 @@
#:f32.4-max
#:f32.4-min
#:f32.4-sqrt
#:f32.4-abs
#:f32.4-neg
#:f32.4+
#:f32.4-
#:f32.4*
@ -2220,6 +2222,8 @@
#:f64.2-max
#:f64.2-min
#:f64.2-sqrt
#:f64.2-abs
#:f64.2-neg
#:f64.2+
#:f64.2-
#:f64.2*
@ -2268,6 +2272,8 @@
#:u8.16-andc1
#:u8.16-andc2
#:u8.16-not
#:u8.16-min
#:u8.16-max
#:u8.16+
#:u8.16+-saturating
#:u8.16*
@ -2326,6 +2332,8 @@
#:u16.8-andc1
#:u16.8-andc2
#:u16.8-not
#:u16.8-min
#:u16.8-max
#:u16.8+
#:u16.8+-saturating
#:u16.8*
@ -2382,6 +2390,8 @@
#:u32.4-andc1
#:u32.4-andc2
#:u32.4-not
#:u32.4-min
#:u32.4-max
#:u32.4+
#:u32.4+-saturating
#:u32.4*
@ -2438,9 +2448,10 @@
#:u64.2-andc1
#:u64.2-andc2
#:u64.2-not
#:u64.2-min
#:u64.2-max
#:u64.2+
#:u64.2+-saturating
#:u64.2*
#:u64.2-
#:u64.2--saturating
#:u64.2=
@ -2487,6 +2498,8 @@
#:s8.16-andc1
#:s8.16-andc2
#:s8.16-not
#:s8.16-min
#:s8.16-max
#:s8.16+
#:s8.16+-saturating
#:s8.16*
@ -2540,6 +2553,8 @@
#:s16.8-andc1
#:s16.8-andc2
#:s16.8-not
#:s16.8-min
#:s16.8-max
#:s16.8+
#:s16.8+-saturating
#:s16.8*
@ -2594,6 +2609,8 @@
#:s32.4-andc1
#:s32.4-andc2
#:s32.4-not
#:s32.4-min
#:s32.4-max
#:s32.4+
#:s32.4+-saturating
#:s32.4*
@ -2644,9 +2661,10 @@
#:s64.2-andc1
#:s64.2-andc2
#:s64.2-not
#:s64.2-min
#:s64.2-max
#:s64.2+
#:s64.2+-saturating
#:s64.2*
#:s64.2-
#:s64.2--saturating
#:s64.2=

View file

@ -0,0 +1,179 @@
;;; Regression tests for AdvSIMD bugs found by auditing sb-simd against
;;; the A64 instruction set. Each test targets a bug that once produced
;;; wrong results or illegal instructions rather than a clean error.
(in-package #:sb-simd-neon)
;;; Values that sit on both sides of the signed/unsigned wrap-around
;;; point. Random sampling almost never generates pairs like
;;; (0, #xFFFFFFFFFFFFFFFF), so test them exhaustively.
(defparameter *u64-boundary-values*
(list 0 1 2 3 5
#x7FFFFFFFFFFFFFFE #x7FFFFFFFFFFFFFFF
#x8000000000000000 #x8000000000000001
#xFFFFFFFFFFFFFFFD #xFFFFFFFFFFFFFFFE #xFFFFFFFFFFFFFFFF))
(defparameter *s64-boundary-values*
(list -9223372036854775808 -9223372036854775807 -1 0 1 42
9223372036854775806 9223372036854775807))
;;; Issue: UMAX/UMIN/SMAX/SMIN have no .2d form; max/min are fake vops
;;; built from CMHI/CMGT plus BSL.
(sb-simd-test-suite:define-test u64.2-max-min-boundaries
(dolist (a *u64-boundary-values*)
(dolist (b *u64-boundary-values*)
(let ((pa (make-u64.2 a a))
(pb (make-u64.2 b b)))
(sb-simd-test-suite:is (= (u64.2-lane-extract (u64.2-max pa pb) 0) (max a b)))
(sb-simd-test-suite:is (= (u64.2-lane-extract (u64.2-min pa pb) 0) (min a b)))))))
(sb-simd-test-suite:define-test s64.2-max-min-boundaries
(dolist (a *s64-boundary-values*)
(dolist (b *s64-boundary-values*)
(let ((pa (make-s64.2 a a))
(pb (make-s64.2 b b)))
(sb-simd-test-suite:is (= (s64.2-lane-extract (s64.2-max pa pb) 0) (max a b)))
(sb-simd-test-suite:is (= (s64.2-lane-extract (s64.2-min pa pb) 0) (min a b)))))))
;;; Random cross-check of the same fake vops against the scalar
;;; reference implementation.
(sb-simd-test-suite:define-test u64.2-max-min-random
(flet ((to-signed (x) (if (logbitp 63 x) (- x (ash 1 64)) x)))
(dotimes (i 2000)
(let* ((bits (lambda () (logior (ash (random (ash 1 32)) 32)
(random (ash 1 32)))))
(a0 (funcall bits)) (a1 (funcall bits))
(b0 (funcall bits)) (b1 (funcall bits))
(ua (make-u64.2 a0 a1))
(ub (make-u64.2 b0 b1))
(sa (make-s64.2 (to-signed a0) (to-signed a1)))
(sb (make-s64.2 (to-signed b0) (to-signed b1))))
(sb-simd-test-suite:is (= (u64.2-lane-extract (u64.2-max ua ub) 0) (max a0 b0)))
(sb-simd-test-suite:is (= (u64.2-lane-extract (u64.2-min ua ub) 1) (min a1 b1)))
(sb-simd-test-suite:is (= (s64.2-lane-extract (s64.2-max sa sb) 0)
(max (to-signed a0) (to-signed b0))))
(sb-simd-test-suite:is (= (s64.2-lane-extract (s64.2-min sa sb) 1)
(min (to-signed a1) (to-signed b1))))))))
;;; Issue: the XTN-family narrowing records used .4h/.8h suffixes,
;;; silently computing a 32-to-16-bit narrowing on 64-bit lanes.
(sb-simd-test-suite:define-test u32.4-from-u64.2-narrowing
;; Plain narrowing takes the low half of each lane and zeroes the rest.
(let ((r (u32.4-from-u64.2 (make-u64.2 #x1111111122222222
#x3333333344444444))))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 0) #x22222222))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 1) #x44444444))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 2) 0))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 3) 0)))
;; Saturating narrowing clamps into the target range.
(let ((r (u32.4-from-u64.2-saturating (make-u64.2 #xFFFFFFFFFFFFFFFF 3))))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 0) #xFFFFFFFF))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 1) 3)))
;; The -hi variants append to the high half and preserve the low half.
(let ((r (u32.4-from-u64.2-hi (make-u32.4 10 20 30 40)
(make-u64.2 #x1111111122222222
#x3333333344444444))))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 0) 10))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 1) 20))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 2) #x22222222))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 3) #x44444444)))
(let ((r (u32.4-from-u64.2-saturating-hi
(make-u32.4 10 20 30 40)
(make-u64.2 #xFFFFFFFFFFFFFFFF #xFFFFFFFFFFFFFFFF))))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 0) 10))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 1) 20))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 2) #xFFFFFFFF))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 3) #xFFFFFFFF))))
(sb-simd-test-suite:define-test s32.4-from-s64.2-narrowing
(let ((r (s32.4-from-s64.2 (make-s64.2 -5 #x7000000011112222))))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 0) -5))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 1) #x11112222))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 2) 0))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 3) 0)))
;; Saturation clamps in both directions.
(let ((r (s32.4-from-s64.2-saturating
(make-s64.2 #x7FFFFFFFFFFFFFFF -99999999999999))))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 0) most-positive-s32))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 1) most-negative-s32)))
(let ((r (s32.4-from-s64.2-hi (make-s32.4 10 20 30 40)
(make-s64.2 -5 #x7000000011112222))))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 0) 10))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 1) 20))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 2) -5))
(sb-simd-test-suite:is (= (s32.4-lane-extract r 3) #x11112222))))
;;; Issue: signed lane extraction returned zero-extended values because
;;; the custom vops emitted UMOV instead of SMOV.
(sb-simd-test-suite:define-test signed-lane-extraction-sign-extends
(let ((v (make-s8.16 -128 -1 127 42 0 -42 1 -127 2 -2 3 -3 4 -4 5 -5)))
(loop for expected in '(-128 -1 127 42 0 -42 1 -127 2 -2 3 -3 4 -4 5 -5)
for i from 0
do (sb-simd-test-suite:is (= (s8.16-lane-extract v i) expected))))
(let ((v (make-s16.8 -32768 -1 32767 12345 -5432 0 1 -1)))
(loop for expected in '(-32768 -1 32767 12345 -5432 0 1 -1)
for i from 0
do (sb-simd-test-suite:is (= (s16.8-lane-extract v i) expected))))
(let ((v (make-s32.4 -2147483648 -1 2147483647 42)))
(loop for expected in '(-2147483648 -1 2147483647 42)
for i from 0
do (sb-simd-test-suite:is (= (s32.4-lane-extract v i) expected))))
;; Unsigned shapes must remain unaffected.
(let ((v (make-u8.16 255 254 253 252 0 1 2 3 4 5 6 7 8 9 10 11)))
(sb-simd-test-suite:is (= (u8.16-lane-extract v 0) 255))
(sb-simd-test-suite:is (= (u8.16-lane-extract v 3) 252))))
;;; Issue: emit-movi-vector-imm passed replicated broadcast patterns to
;;; MOVI instead of lane immediates, aborting compilation with "bad
;;; immediate". Every constant below is materialized through that code
;;; path; several exercise the shifted-cmode forms. The tests are split
;;; into several functions because packing too many distinct vector
;;; constants into one function currently overflows the LDR spill
;;; offset range - see issue about large SIMD constant pools.
(sb-simd-test-suite:define-test integer-broadcast-constants-simple
;; The original reproducer: 1 does not fit MOVI's simple .8h form.
(let ((r (u16.8-and (u16.8-broadcast 5) 1)))
(sb-simd-test-suite:is (= (u16.8-lane-extract r 0) 1))
(sb-simd-test-suite:is (= (u16.8-lane-extract r 7) 1)))
;; Shifted immediates (low byte zero).
(let ((r (u16.8-and (u16.8-broadcast #x0100) #x0100)))
(sb-simd-test-suite:is (= (u16.8-lane-extract r 3) #x0100)))
;; Byte-replicated patterns.
(dolist (c (list 0 1 #x7F #x80 #xFF))
(let ((r (u8.16-or (u8.16-broadcast c) (u8.16-broadcast c))))
(sb-simd-test-suite:is (= (u8.16-lane-extract r 15) c)))))
(sb-simd-test-suite:define-test integer-broadcast-constants-word
;; 32-bit constants with shifted bytes.
(dolist (c (list 0 1 255 256 #xFFFF #x10000 #xFF0000 #xFF000000))
(let ((r (u32.4-and (u32.4-broadcast c) (u32.4-broadcast c))))
(sb-simd-test-suite:is (= (u32.4-lane-extract r 0) c)))))
;;; Bit-replication patterns (.2d MOVI form): every byte is 0 or #xFF.
(sb-simd-test-suite:define-test integer-broadcast-constants-bit-replicated
(dolist (c (list #xFF00FF00FF00FF00
#x8000000000000000
#xFFFFFFFFFFFFFFFF
#x7F7F7F7F7F7F7F7F
#x00FF00FF00FF00FF))
(let ((r (u64.2-and (u64.2-broadcast c) (u64.2-broadcast c))))
(sb-simd-test-suite:is (= (u64.2-lane-extract r 0) c))
(sb-simd-test-suite:is (= (u64.2-lane-extract r 1) c)))))
;;; Patterns that fit no MOVI form fall back to register synthesis
;;; (MOVZ/DUP or logical-immediate ORR).
(sb-simd-test-suite:define-test integer-broadcast-constants-synthesized
(dolist (c (list #x0102030405060708 #xDEADBEEFCAFEBABE))
(let ((r (u64.2-and (u64.2-broadcast c) (u64.2-broadcast c))))
(sb-simd-test-suite:is (= (u64.2-lane-extract r 1) c)))))
;;; Float constants take a different path (movi-immediate-p guarded);
;;; keep them covered too.
(sb-simd-test-suite:define-test float-broadcast-constants
(let ((r (f32.4-max (make-f32.4 0.5 -1.0 2.0 3.0) 1.0f0)))
(sb-simd-test-suite:is (= (f32.4-lane-extract r 0) 1.0f0))
(sb-simd-test-suite:is (= (f32.4-lane-extract r 1) 1.0f0))
(sb-simd-test-suite:is (= (f32.4-lane-extract r 2) 2.0f0)))
(let ((r (f64.2-min (make-f64.2 0.5d0 -5d0) -1d0)))
(sb-simd-test-suite:is (= (f64.2-lane-extract r 0) -1d0))
(sb-simd-test-suite:is (= (f64.2-lane-extract r 1) -5d0))))

View file

@ -8,6 +8,8 @@
(sb-simd-test-suite:define-simple-simd-test f32.4-not (f32.4) (f32.4) f32-not)
(sb-simd-test-suite:define-simple-simd-test f32.4-max (f32.4) (f32.4 &rest f32.4) f32-max)
(sb-simd-test-suite:define-simple-simd-test f32.4-min (f32.4) (f32.4 &rest f32.4) f32-min)
(sb-simd-test-suite:define-simple-simd-test f32.4-abs (f32.4) (f32.4) abs)
(sb-simd-test-suite:define-simple-simd-test f32.4-neg (f32.4) (f32.4) -)
(sb-simd-test-suite:define-simple-simd-test f32.4+ (f32.4) (&rest f32.4) f32+)
(sb-simd-test-suite:define-simple-simd-test f32.4- (f32.4) (f32.4 &rest f32.4) f32-)
(sb-simd-test-suite:define-simple-simd-test f32.4* (f32.4) (&rest f32.4) f32*)
@ -26,6 +28,8 @@
(sb-simd-test-suite:define-simple-simd-test f64.2-not (f64.2) (f64.2) f64-not)
(sb-simd-test-suite:define-simple-simd-test f64.2-max (f64.2) (f64.2 &rest f64.2) f64-max)
(sb-simd-test-suite:define-simple-simd-test f64.2-min (f64.2) (f64.2 &rest f64.2) f64-min)
(sb-simd-test-suite:define-simple-simd-test f64.2-abs (f64.2) (f64.2) abs)
(sb-simd-test-suite:define-simple-simd-test f64.2-neg (f64.2) (f64.2) -)
(sb-simd-test-suite:define-simple-simd-test f64.2+ (f64.2) (&rest f64.2) f64+)
(sb-simd-test-suite:define-simple-simd-test f64.2- (f64.2) (f64.2 &rest f64.2) f64-)
(sb-simd-test-suite:define-simple-simd-test f64.2* (f64.2) (&rest f64.2) f64*)
@ -42,6 +46,8 @@
(sb-simd-test-suite:define-simple-simd-test u8.16-xor (u8.16) (&rest u8.16) u8-xor)
(sb-simd-test-suite:define-simple-simd-test u8.16-andc1 (u8.16) (u8.16 u8.16) u8-andc1)
(sb-simd-test-suite:define-simple-simd-test u8.16-not (u8.16) (u8.16) u8-not)
(sb-simd-test-suite:define-simple-simd-test u8.16-max (u8.16) (u8.16 &rest u8.16) u8-max)
(sb-simd-test-suite:define-simple-simd-test u8.16-min (u8.16) (u8.16 &rest u8.16) u8-min)
(sb-simd-test-suite:define-simple-simd-test u8.16+ (u8.16) (&rest u8.16) u8+)
(sb-simd-test-suite:define-simple-simd-test u8.16- (u8.16) (u8.16 &rest u8.16) u8-)
(sb-simd-test-suite:define-simple-simd-test u8.16= (u8.16) (u8.16 &rest u8.16) u8=)
@ -56,6 +62,8 @@
(sb-simd-test-suite:define-simple-simd-test u16.8-xor (u16.8) (&rest u16.8) u16-xor)
(sb-simd-test-suite:define-simple-simd-test u16.8-andc1 (u16.8) (u16.8 u16.8) u16-andc1)
(sb-simd-test-suite:define-simple-simd-test u16.8-not (u16.8) (u16.8) u16-not)
(sb-simd-test-suite:define-simple-simd-test u16.8-max (u16.8) (u16.8 &rest u16.8) u16-max)
(sb-simd-test-suite:define-simple-simd-test u16.8-min (u16.8) (u16.8 &rest u16.8) u16-min)
(sb-simd-test-suite:define-simple-simd-test u16.8+ (u16.8) (&rest u16.8) u16+)
(sb-simd-test-suite:define-simple-simd-test u16.8- (u16.8) (u16.8 &rest u16.8) u16-)
(sb-simd-test-suite:define-simple-simd-test u16.8= (u16.8) (u16.8 &rest u16.8) u16=)
@ -70,6 +78,8 @@
(sb-simd-test-suite:define-simple-simd-test u32.4-xor (u32.4) (&rest u32.4) u32-xor)
(sb-simd-test-suite:define-simple-simd-test u32.4-andc1 (u32.4) (u32.4 u32.4) u32-andc1)
(sb-simd-test-suite:define-simple-simd-test u32.4-not (u32.4) (u32.4) u32-not)
(sb-simd-test-suite:define-simple-simd-test u32.4-max (u32.4) (u32.4 &rest u32.4) u32-max)
(sb-simd-test-suite:define-simple-simd-test u32.4-min (u32.4) (u32.4 &rest u32.4) u32-min)
(sb-simd-test-suite:define-simple-simd-test u32.4+ (u32.4) (&rest u32.4) u32+)
(sb-simd-test-suite:define-simple-simd-test u32.4- (u32.4) (u32.4 &rest u32.4) u32-)
(sb-simd-test-suite:define-simple-simd-test u32.4= (u32.4) (u32.4 &rest u32.4) u32=)
@ -84,6 +94,8 @@
(sb-simd-test-suite:define-simple-simd-test u64.2-xor (u64.2) (&rest u64.2) u64-xor)
(sb-simd-test-suite:define-simple-simd-test u64.2-andc1 (u64.2) (u64.2 u64.2) u64-andc1)
(sb-simd-test-suite:define-simple-simd-test u64.2-not (u64.2) (u64.2) u64-not)
(sb-simd-test-suite:define-simple-simd-test u64.2-max (u64.2) (u64.2 &rest u64.2) u64-max)
(sb-simd-test-suite:define-simple-simd-test u64.2-min (u64.2) (u64.2 &rest u64.2) u64-min)
(sb-simd-test-suite:define-simple-simd-test u64.2+ (u64.2) (&rest u64.2) u64+)
(sb-simd-test-suite:define-simple-simd-test u64.2- (u64.2) (u64.2 &rest u64.2) u64-)
(sb-simd-test-suite:define-simple-simd-test u64.2= (u64.2) (u64.2 &rest u64.2) u64=)
@ -98,6 +110,8 @@
(sb-simd-test-suite:define-simple-simd-test s8.16-xor (s8.16) (&rest s8.16) s8-xor)
(sb-simd-test-suite:define-simple-simd-test s8.16-andc1 (s8.16) (s8.16 s8.16) s8-andc1)
(sb-simd-test-suite:define-simple-simd-test s8.16-not (s8.16) (s8.16) s8-not)
(sb-simd-test-suite:define-simple-simd-test s8.16-max (s8.16) (s8.16 &rest s8.16) s8-max)
(sb-simd-test-suite:define-simple-simd-test s8.16-min (s8.16) (s8.16 &rest s8.16) s8-min)
(sb-simd-test-suite:define-simple-simd-test s8.16+ (s8.16) (&rest s8.16) s8+)
(sb-simd-test-suite:define-simple-simd-test s8.16- (s8.16) (s8.16 &rest s8.16) s8-)
(sb-simd-test-suite:define-simple-simd-test s8.16= (u8.16) (s8.16 &rest s8.16) s8=)
@ -112,6 +126,8 @@
(sb-simd-test-suite:define-simple-simd-test s16.8-xor (s16.8) (&rest s16.8) s16-xor)
(sb-simd-test-suite:define-simple-simd-test s16.8-andc1 (s16.8) (s16.8 s16.8) s16-andc1)
(sb-simd-test-suite:define-simple-simd-test s16.8-not (s16.8) (s16.8) s16-not)
(sb-simd-test-suite:define-simple-simd-test s16.8-max (s16.8) (s16.8 &rest s16.8) s16-max)
(sb-simd-test-suite:define-simple-simd-test s16.8-min (s16.8) (s16.8 &rest s16.8) s16-min)
(sb-simd-test-suite:define-simple-simd-test s16.8+ (s16.8) (&rest s16.8) s16+)
(sb-simd-test-suite:define-simple-simd-test s16.8- (s16.8) (s16.8 &rest s16.8) s16-)
(sb-simd-test-suite:define-simple-simd-test s16.8= (u16.8) (s16.8 &rest s16.8) s16=)
@ -126,6 +142,8 @@
(sb-simd-test-suite:define-simple-simd-test s32.4-xor (s32.4) (&rest s32.4) s32-xor)
(sb-simd-test-suite:define-simple-simd-test s32.4-andc1 (s32.4) (s32.4 s32.4) s32-andc1)
(sb-simd-test-suite:define-simple-simd-test s32.4-not (s32.4) (s32.4) s32-not)
(sb-simd-test-suite:define-simple-simd-test s32.4-max (s32.4) (s32.4 &rest s32.4) s32-max)
(sb-simd-test-suite:define-simple-simd-test s32.4-min (s32.4) (s32.4 &rest s32.4) s32-min)
(sb-simd-test-suite:define-simple-simd-test s32.4+ (s32.4) (&rest s32.4) s32+)
(sb-simd-test-suite:define-simple-simd-test s32.4- (s32.4) (s32.4 &rest s32.4) s32-)
(sb-simd-test-suite:define-simple-simd-test s32.4= (u32.4) (s32.4 &rest s32.4) s32=)
@ -140,6 +158,8 @@
(sb-simd-test-suite:define-simple-simd-test s64.2-xor (s64.2) (&rest s64.2) s64-xor)
(sb-simd-test-suite:define-simple-simd-test s64.2-andc1 (s64.2) (s64.2 s64.2) s64-andc1)
(sb-simd-test-suite:define-simple-simd-test s64.2-not (s64.2) (s64.2) s64-not)
(sb-simd-test-suite:define-simple-simd-test s64.2-max (s64.2) (s64.2 &rest s64.2) s64-max)
(sb-simd-test-suite:define-simple-simd-test s64.2-min (s64.2) (s64.2 &rest s64.2) s64-min)
(sb-simd-test-suite:define-simple-simd-test s64.2+ (s64.2) (&rest s64.2) s64+)
(sb-simd-test-suite:define-simple-simd-test s64.2- (s64.2) (s64.2 &rest s64.2) s64-)
(sb-simd-test-suite:define-simple-simd-test s64.2= (u64.2) (s64.2 &rest s64.2) s64=)

View file

@ -2810,38 +2810,42 @@
(fpr-offset rd)))))
(defmacro def-fp-data-processing-1+simd (name op
simd-u simd-neg simd-op)
simd-u simd-neg simd-op6 simd-op)
`(define-instruction ,name (segment rd rn &optional vector-size)
(:printer fp-data-processing-1 ((op ,op)))
(:printer simd-two-same-float ((u ,simd-u) (neg ,simd-neg) (op ,simd-op)))
(:printer simd-two-same-float ((u ,simd-u) (neg ,simd-neg) (op6 ,simd-op6)
(op ,simd-op)))
(:emitter
(assert (and (eq (tn-sc rd)
(tn-sc rn)))
(rd rn)
"Arguments should have the same FP storage class: ~s ~s." rd rn)
(if vector-size
(multiple-value-bind (q size) (encode-vector-size vector-size)
(emit-simd-two-same-float
segment
q
,simd-u
,simd-neg
(logand 1 size)
,simd-op
(fpr-offset rn)
(fpr-offset rd)))
(emit-fp-data-processing-1 segment
(fp-reg-type rn)
,op
(fpr-offset rn)
(fpr-offset rd))))))
(cond (vector-size
(aver (member vector-size '(:2s :4s :2d)))
(multiple-value-bind (q size) (encode-vector-size vector-size)
(emit-simd-two-same-float
segment
q
,simd-u
,simd-neg
(logand 1 size)
,simd-op6
,simd-op
(fpr-offset rn)
(fpr-offset rd))))
(t
(emit-fp-data-processing-1 segment
(fp-reg-type rn)
,op
(fpr-offset rn)
(fpr-offset rd)))))))
(def-fp-data-processing-1+simd fabs #b0001
#b0 #b1 #b11111)
#b0 #b1 #b00000 #b11111)
(def-fp-data-processing-1+simd fneg #b0010
#b1 #b1 #b11111)
#b1 #b1 #b00000 #b11111)
(def-fp-data-processing-1+simd fsqrt #b0011
#b1 #b0 #b11111)
#b1 #b1 #b00001 #b11111)
(def-fp-data-processing-1 frintn #b1000)
(def-fp-data-processing-1 frintp #b1001)
(def-fp-data-processing-1 frintm #b1010)
@ -2888,24 +2892,26 @@
(:printer fp-data-processing-2 ((op ,op)))
(:printer simd-three-same-float ((u ,simd-u) (neg ,simd-neg) (op ,simd-op)))
(:emitter
(if vector-size
(multiple-value-bind (q size) (encode-vector-size vector-size)
(emit-simd-three-same-float
segment
q
,simd-u
,simd-neg
(logand 1 size)
(fpr-offset rm)
,simd-op
(fpr-offset rn)
(fpr-offset rd)))
(emit-fp-data-processing-2 segment
(fp-reg-type rn)
(fpr-offset rm)
,op
(fpr-offset rn)
(fpr-offset rd))))))
(cond (vector-size
(aver (member vector-size '(:2s :4s :2d)))
(multiple-value-bind (q size) (encode-vector-size vector-size)
(emit-simd-three-same-float
segment
q
,simd-u
,simd-neg
(logand 1 size)
(fpr-offset rm)
,simd-op
(fpr-offset rn)
(fpr-offset rd))))
(t
(emit-fp-data-processing-2 segment
(fp-reg-type rn)
(fpr-offset rm)
,op
(fpr-offset rn)
(fpr-offset rd)))))))
(def-fp-data-processing-2+simd fmul #b0000
#b1 #b0 #b11011)
@ -3479,8 +3485,8 @@
(neg 1 23)
(size 1 22)
(#b1 1 21)
(#b00000 5 16)
(opc 5 11)
(op6 5 16)
(op 5 11)
(#b0 1 10)
(rn 5 5)
(rd 5 0))
@ -3533,10 +3539,10 @@
(op3 :field (byte 1 31) :value #b0)
(u :field (byte 1 29))
(op4 :field (byte 5 24) :value #b01110)
(neg :field (byte 1 23))
(neg :field (byte 1 23) :value #b1)
(size :field (byte 1 22))
(op5 :field (byte 1 21) :value #b1)
(op6 :field (byte 5 16) :value #b00000)
(op6 :field (byte 5 16))
(op :field (byte 5 11))
(op7 :field (byte 1 10) :value #b0)
(rn :fields (list (byte 1 30) (byte 1 22) (byte 5 5)) :type 'simd-float-reg)
@ -3612,7 +3618,9 @@
(def bit #b1 #b10 #b00011)
(def bif #b1 #b11 #b00011))
(macrolet ((def (name u op &optional zero-u zero)
(macrolet ((def (name u op &optional
(2d t)
zero-u zero)
`(define-instruction ,name (segment rd rn rm size)
,@(when op
`((:printer simd-three-same-sized ((u ,u) (op ,op)))))
@ -3620,6 +3628,9 @@
`((:printer simd-two-misc ((u ,zero-u) (op ,zero))
'(:name :tab rd ", " rn ", " "#0"))))
(:emitter
(aver (member size ',(if 2d
'(:8b :16b :4h :8h :2s :4s :2d)
'(:8b :16b :4h :8h :2s :4s))))
(multiple-value-bind (q size) (encode-vector-size size)
(cond ,@(when zero
`(((eql rm 0)
@ -3644,32 +3655,32 @@
(fpr-offset rd))
`(error "Can be compared only with zero, not ~s" rm)))))))))
(def cmtst #b0 #b10001)
(def cmeq #b1 #b10001 0 #b01001)
(def cmgt #b0 #b00110 0 #b01000)
(def cmge #b0 #b00111 1 #b01000)
(def cmlt nil nil 0 #b01010)
(def cmle nil nil 1 #b01001)
(def cmeq #b1 #b10001 t 0 #b01001)
(def cmgt #b0 #b00110 t 0 #b01000)
(def cmge #b0 #b00111 t 1 #b01000)
(def cmlt nil nil t 0 #b01010)
(def cmle nil nil t 1 #b01001)
(def cmhi #b1 #b00110)
(def cmhs #b1 #b00111)
(def umin #b1 #b01101)
(def umax #b1 #b01100)
(def smin #b0 #b01101)
(def smax #b0 #b01100)
(def uhadd #b1 #b00000)
(def umin #b1 #b01101 nil)
(def umax #b1 #b01100 nil)
(def smin #b0 #b01101 nil)
(def smax #b0 #b01100 nil)
(def uhadd #b1 #b00000 nil)
(def uqadd #b1 #b00001)
(def urhadd #b1 #b00010)
(def uhsub #b1 #b00100)
(def urhadd #b1 #b00010 nil)
(def uhsub #b1 #b00100 nil)
(def uqsub #b1 #b00101)
(def addp #b0 #b10111)
(def shadd #b0 #b00000)
(def shadd #b0 #b00000 nil)
(def sqadd #b0 #b00001)
(def srhadd #b0 #b00010)
(def shsub #b0 #b00100)
(def srhadd #b0 #b00010 nil)
(def shsub #b0 #b00100 nil)
(def sqsub #b0 #b00101)
(def uminp #b1 #b10101)
(def umaxp #b1 #b10100)
(def sminp #b0 #b10101)
(def smaxp #b0 #b10100)
(def uminp #b1 #b10101 nil)
(def umaxp #b1 #b10100 nil)
(def sminp #b0 #b10101 nil)
(def smaxp #b0 #b10100 nil)
(def sshl #b0 #b01001)
(def ushl #b1 #b01001))
@ -3677,6 +3688,7 @@
`(define-instruction ,name (segment rd rn rm size)
(:printer simd-three-same-float ((u ,u) (neg ,neg) (op ,op)))
(:emitter
(aver (member size '(:2s :4s :2d)))
(multiple-value-bind (q size) (encode-vector-size size)
(emit-simd-three-same-float
segment
@ -3759,6 +3771,7 @@
(fpr-offset rn)
(fpr-offset rd)))
(t
(aver (member vector-size '(:2s :4s :2d)))
(multiple-value-bind (q size) (encode-vector-size vector-size)
(emit-simd-three-same-float
segment
@ -4082,6 +4095,7 @@
(:printer simd-across-lanes ((u ,u) (op ,op)
(rd nil :type 'vbhs)))
(:emitter
(aver (member size '(:8b :16b :4h :8h :2s :4s)))
(multiple-value-bind (q size) (encode-vector-size size)
(emit-simd-across-lanes
segment
@ -4171,6 +4185,7 @@
`(define-instruction ,name (segment rd rn size)
(:printer simd-two-misc ((u ,u) (op ,op) (rd nil :type 'simd-reg-2x)))
(:emitter
(aver (member size '(:8b :16b :4h :8h :2s :4s)))
(multiple-value-bind (q size) (encode-vector-size size)
(emit-simd-two-misc segment
q
@ -4366,8 +4381,8 @@
(fpr-offset rn)
(fpr-offset rd)))))))
(def shl #b0 #b01010 nil t)
(def sli #b1 #b01010)
(def sri #b1 #b01000 t)
(def sli #b1 #b01010 nil t)
(def sri #b1 #b01000 t t)
(def sshr #b0 #b00000 t t)
(def shrn #b0 #b10000 t)
(def ssra #b0 #b00010 t t)

View file

@ -35,13 +35,13 @@
(zerop (dpb 0 (byte 8 8) (ldb (byte 32 0) value)))
(zerop (dpb 0 (byte 8 16) (ldb (byte 32 0) value)))
(zerop (dpb 0 (byte 8 24) (ldb (byte 32 0) value)))))
(inst movi dst value :4s))
(inst movi dst (ldb (byte 32 0) value) :4s))
((and wordp
(or (zerop (dpb 0 (byte 8 0) (ldb (byte 16 0) value)))
(zerop (dpb 0 (byte 8 8) (ldb (byte 16 0) value)))))
(inst movi dst value :8h))
(inst movi dst (ldb (byte 16 0) value) :8h))
(bytep
(inst movi dst value :16b))
(inst movi dst (ldb (byte 8 0) value) :16b))
((and (member (ldb (byte 8 0) value) '(0 #xFF))
(member (ldb (byte 8 8) value) '(0 #xFF))
(member (ldb (byte 8 16) value) '(0 #xFF))
@ -50,7 +50,7 @@
(member (ldb (byte 8 40) value) '(0 #xFF))
(member (ldb (byte 8 48) value) '(0 #xFF))
(member (ldb (byte 8 56) value) '(0 #xFF)))
(inst movi value :2d))
(inst movi dst value :2d))
;; Can't do movi, try via the scalar regs.
(wordp
(inst movz tmp-tn (ldb (byte 16 0) value))

View file

@ -148,11 +148,11 @@
(complex-single-stack non-descriptor-stack)
(complex-double-stack non-descriptor-stack :element-size 2 :alignment 2)
#+sb-simd-pack
(int-neon-stack non-descriptor-stack :element-size 2)
(int-neon-stack non-descriptor-stack :element-size 2 :alignment 2)
#+sb-simd-pack
(double-neon-stack non-descriptor-stack :element-size 2)
(double-neon-stack non-descriptor-stack :element-size 2 :alignment 2)
#+sb-simd-pack
(single-neon-stack non-descriptor-stack :element-size 2)
(single-neon-stack non-descriptor-stack :element-size 2 :alignment 2)
;; **** Things that can go in the integer registers.

View file

@ -28,6 +28,7 @@
"test-horizontal-functions.lisp"
#+x86-64 "test-horizontal-functions-x86-64.lisp"
#+arm64 "test-horizontal-functions-arm64.lisp"
#+arm64 "test-arm64-regressions.lisp"
"test-hairy-simd-functions.lisp"
"test-packages.lisp"))
(load (merge-pathnames file #P"../contrib/sb-simd/test-suite/"))))