simd/archsimd: correct ARM64 IfElse semantics ARM64's IfElse behavior is reversed from other platforms. Reverse it. Internally, its bitSelect is also the reverse of Wasm's BitSelect. Reverse the ARM64 one to match. Make Masked and IfElse tests portable. Change-Id: Icd2dbcb3383b2be642fd6fc7115ef1cbef0f9b78 Reviewed-on: https://go-review.googlesource.com/c/go/+/793361 Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/src/cmd/compile/internal/arm64/simdssa.go b/src/cmd/compile/internal/arm64/simdssa.go index dd89daf..41ae292 100644 --- a/src/cmd/compile/internal/arm64/simdssa.go +++ b/src/cmd/compile/internal/arm64/simdssa.go
@@ -272,8 +272,8 @@ p = simdV21Imm(s, v, arm64.ARNG_16B) case ssa.OpARM64VMLA16B, - ssa.OpARM64VBIT16B, - ssa.OpARM64VBIF16B: + ssa.OpARM64VBIF16B, + ssa.OpARM64VBIT16B: p = simdV31ResultInArg0(s, v, arm64.ARNG_16B) case ssa.OpARM64VFMLA2D:
diff --git a/src/cmd/compile/internal/ssa/_gen/simdARM64.rules b/src/cmd/compile/internal/ssa/_gen/simdARM64.rules index fb85f66..131ab8e 100644 --- a/src/cmd/compile/internal/ssa/_gen/simdARM64.rules +++ b/src/cmd/compile/internal/ssa/_gen/simdARM64.rules
@@ -376,10 +376,10 @@ (XorUint16x8 ...) => (VEOR16B ...) // pureVreg (XorUint32x4 ...) => (VEOR16B ...) // pureVreg (XorUint64x2 ...) => (VEOR16B ...) // pureVreg -(bitSelectInt8x16 ...) => (VBIT16B ...) // pureVreg -(VBIT16B x y (VNOT16B mask)) => (VBIF16B x y mask) // specialLower -(bitSelectNotInt8x16 ...) => (VBIF16B ...) // pureVreg +(bitSelectInt8x16 ...) => (VBIF16B ...) // pureVreg (VBIF16B x y (VNOT16B mask)) => (VBIT16B x y mask) // specialLower +(bitSelectNotInt8x16 ...) => (VBIT16B ...) // pureVreg +(VBIT16B x y (VNOT16B mask)) => (VBIF16B x y mask) // specialLower (broadcast1To2Float64x2 x) => (VDUPDbcast [0] x) // pureVreg (broadcast1To2Int64x2 x) => (VDUPDbcast [0] x) // pureVreg (broadcast1To2Uint64x2 x) => (VDUPDbcast [0] x) // pureVreg
diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index d93a204..26d94b9 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go
@@ -2276,10 +2276,10 @@ case OpZeroSIMD: return rewriteValueARM64_OpZeroSIMD(v) case OpbitSelectInt8x16: - v.Op = OpARM64VBIT16B + v.Op = OpARM64VBIF16B return true case OpbitSelectNotInt8x16: - v.Op = OpARM64VBIF16B + v.Op = OpARM64VBIT16B return true case Opbroadcast1To16Int8x16: return rewriteValueARM64_Opbroadcast1To16Int8x16(v)
diff --git a/src/simd/archsimd/_gen/simdgen/ops/Moves/categories.yaml b/src/simd/archsimd/_gen/simdgen/ops/Moves/categories.yaml index 68dc1ab..872628e 100644 --- a/src/simd/archsimd/_gen/simdgen/ops/Moves/categories.yaml +++ b/src/simd/archsimd/_gen/simdgen/ops/Moves/categories.yaml
@@ -85,11 +85,11 @@ - go: bitSelect commutative: false documentation: !string |- - // NAME selects bits from y where mask is 1, keeps bits from x where mask is 0. + // NAME returns the bitwise selection if mask[i] then x[i] else y[i]. - go: bitSelectNot commutative: false documentation: !string |- - // NAME selects bits from y where mask is 0, keeps bits from x where mask is 1. + // NAME returns the bitwise selection if mask[i] then y[i] else x[i]. - go: move commutative: false noTypes: "true"
diff --git a/src/simd/archsimd/_gen/simdgen/ops/Moves/go_arm64.yaml b/src/simd/archsimd/_gen/simdgen/ops/Moves/go_arm64.yaml index e81879a..1f6ed73 100644 --- a/src/simd/archsimd/_gen/simdgen/ops/Moves/go_arm64.yaml +++ b/src/simd/archsimd/_gen/simdgen/ops/Moves/go_arm64.yaml
@@ -122,12 +122,12 @@ out: - *bcast64 -# bitSelect — VBIT (bit insert if true, internal) -# Only Int8x16 is needed since VBIT operates on the full 128-bit register. +# bitSelect — VBIF (bit insert if false, internal) +# Only Int8x16 is needed since VBIF operates on the full 128-bit register. # Other types cast to Int8x16 before calling bitSelect. - go: bitSelect - asm: "VBIT" - specialLower: !string "match (%h x y (VNOT16B mask)) => (VBIF16B x y mask)" + asm: "VBIF" + specialLower: !string "match (%h x y (VNOT16B mask)) => (VBIT16B x y mask)" in: - &v8 go: $t @@ -142,10 +142,10 @@ out: - *v8 -# bitSelectNot — VBIF (bit insert if false, internal) +# bitSelectNot — VBIT (bit insert if true, internal) - go: bitSelectNot - asm: "VBIF" - specialLower: !string "match (%h x y (VNOT16B mask)) => (VBIT16B x y mask)" + asm: "VBIT" + specialLower: !string "match (%h x y (VNOT16B mask)) => (VBIF16B x y mask)" in: - *v8 - *v8
diff --git a/src/simd/archsimd/internal/simd_test/arm64_compare_test.go b/src/simd/archsimd/internal/simd_test/arm64_compare_test.go deleted file mode 100644 index ad1c55b..0000000 --- a/src/simd/archsimd/internal/simd_test/arm64_compare_test.go +++ /dev/null
@@ -1,90 +0,0 @@ -// Copyright 2026 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build goexperiment.simd && arm64 - -package simd_test - -import ( - "simd/archsimd" - "testing" -) - -// --- Masked: zero elements where mask is false --- - -func TestMasked(t *testing.T) { - // Test Masked for Int8x16 - forSlicePair(t, int8s, 16, func(x, y []int8) bool { - t.Helper() - a := archsimd.LoadInt8x16(x) - mask := archsimd.LoadInt8x16(y).Greater(archsimd.Int8x16{}) // mask: y > 0 - g := make([]int8, 16) - a.Masked(mask).Store(g) - w := make([]int8, 16) - for i := range w { - if y[i] > 0 { - w[i] = x[i] - } - } - return checkSlicesLogInput(t, g, w, 0.0, func() { t.Helper(); t.Logf("x=%v, y=%v", x, y) }) - }) - - // Test Masked for Float64x2 - forSlicePair(t, float64s, 2, func(x, y []float64) bool { - t.Helper() - a := archsimd.LoadFloat64x2(x) - mask := archsimd.LoadFloat64x2(y).Greater(archsimd.Float64x2{}) // mask: y > 0 - g := make([]float64, 2) - a.Masked(mask).Store(g) - w := make([]float64, 2) - for i := range w { - if y[i] > 0 { - w[i] = x[i] - } - } - return checkSlicesLogInput(t, g, w, 0.0, func() { t.Helper(); t.Logf("x=%v, y=%v", x, y) }) - }) -} - -// --- IfElse: set elements to y where mask is true, keep x where true --- - -func TestIfElse(t *testing.T) { - // Test Merge for Int8x16 - forSliceTriple(t, int8s, 16, func(x, y, m []int8) bool { - t.Helper() - a := archsimd.LoadInt8x16(x) - b := archsimd.LoadInt8x16(y) - mask := archsimd.LoadInt8x16(m).Greater(archsimd.Int8x16{}) // mask: m > 0 - g := make([]int8, 16) - a.IfElse(mask, b).Store(g) - w := make([]int8, 16) - for i := range w { - if m[i] > 0 { - w[i] = y[i] - } else { - w[i] = x[i] - } - } - return checkSlicesLogInput(t, g, w, 0.0, func() { t.Helper(); t.Logf("x=%v, y=%v, m=%v", x, y, m) }) - }) - - // Test Merge for Float32x4 - forSliceTriple(t, float32s, 4, func(x, y, m []float32) bool { - t.Helper() - a := archsimd.LoadFloat32x4(x) - b := archsimd.LoadFloat32x4(y) - mask := archsimd.LoadFloat32x4(m).Greater(archsimd.Float32x4{}) // mask: m > 0 - g := make([]float32, 4) - a.IfElse(mask, b).Store(g) - w := make([]float32, 4) - for i := range w { - if m[i] > 0 { - w[i] = y[i] - } else { - w[i] = x[i] - } - } - return checkSlicesLogInput(t, g, w, 0.0, func() { t.Helper(); t.Logf("x=%v, y=%v, m=%v", x, y, m) }) - }) -}
diff --git a/src/simd/archsimd/internal/simd_test/compare_128_test.go b/src/simd/archsimd/internal/simd_test/compare_128_test.go index 98ade49..2d1b05a 100644 --- a/src/simd/archsimd/internal/simd_test/compare_128_test.go +++ b/src/simd/archsimd/internal/simd_test/compare_128_test.go
@@ -118,3 +118,77 @@ // return x.IsNaN().Or(y.IsNaN()) // }, want64) // } + +func TestMasked(t *testing.T) { + // Test Masked for Int8x16 + forSlicePair(t, int8s, 16, func(x, y []int8) bool { + t.Helper() + a := archsimd.LoadInt8x16(x) + mask := archsimd.LoadInt8x16(y).Greater(archsimd.Int8x16{}) // mask: y > 0 + g := make([]int8, 16) + a.Masked(mask).Store(g) + w := make([]int8, 16) + for i := range w { + if y[i] > 0 { + w[i] = x[i] + } + } + return checkSlicesLogInput(t, g, w, 0.0, func() { t.Helper(); t.Logf("x=%v, y=%v", x, y) }) + }) + + // Test Masked for Float64x2 + forSlicePair(t, float64s, 2, func(x, y []float64) bool { + t.Helper() + a := archsimd.LoadFloat64x2(x) + mask := archsimd.LoadFloat64x2(y).Greater(archsimd.Float64x2{}) // mask: y > 0 + g := make([]float64, 2) + a.Masked(mask).Store(g) + w := make([]float64, 2) + for i := range w { + if y[i] > 0 { + w[i] = x[i] + } + } + return checkSlicesLogInput(t, g, w, 0.0, func() { t.Helper(); t.Logf("x=%v, y=%v", x, y) }) + }) +} + +func TestIfElse(t *testing.T) { + // Test Merge for Int8x16 + forSliceTriple(t, int8s, 16, func(x, y, m []int8) bool { + t.Helper() + a := archsimd.LoadInt8x16(x) + b := archsimd.LoadInt8x16(y) + mask := archsimd.LoadInt8x16(m).Greater(archsimd.Int8x16{}) // mask: m > 0 + g := make([]int8, 16) + a.IfElse(mask, b).Store(g) + w := make([]int8, 16) + for i := range w { + if m[i] > 0 { + w[i] = x[i] + } else { + w[i] = y[i] + } + } + return checkSlicesLogInput(t, g, w, 0.0, func() { t.Helper(); t.Logf("x=%v, y=%v, m=%v", x, y, m) }) + }) + + // Test Merge for Float32x4 + forSliceTriple(t, float32s, 4, func(x, y, m []float32) bool { + t.Helper() + a := archsimd.LoadFloat32x4(x) + b := archsimd.LoadFloat32x4(y) + mask := archsimd.LoadFloat32x4(m).Greater(archsimd.Float32x4{}) // mask: m > 0 + g := make([]float32, 4) + a.IfElse(mask, b).Store(g) + w := make([]float32, 4) + for i := range w { + if m[i] > 0 { + w[i] = x[i] + } else { + w[i] = y[i] + } + } + return checkSlicesLogInput(t, g, w, 0.0, func() { t.Helper(); t.Logf("x=%v, y=%v, m=%v", x, y, m) }) + }) +}
diff --git a/src/simd/archsimd/ops_internal_arm64.go b/src/simd/archsimd/ops_internal_arm64.go index 8a47bfe..49f92db 100644 --- a/src/simd/archsimd/ops_internal_arm64.go +++ b/src/simd/archsimd/ops_internal_arm64.go
@@ -6,16 +6,16 @@ /* bitSelect */ -// bitSelect selects bits from y where mask is 1, keeps bits from x where mask is 0. +// bitSelect returns the bitwise selection if mask[i] then x[i] else y[i]. // -// Asm: VBIT, CPU Feature: NEON +// Asm: VBIF, CPU Feature: NEON func (x Int8x16) bitSelect(y Int8x16, mask Int8x16) Int8x16 /* bitSelectNot */ -// bitSelectNot selects bits from y where mask is 0, keeps bits from x where mask is 1. +// bitSelectNot returns the bitwise selection if mask[i] then y[i] else x[i]. // -// Asm: VBIF, CPU Feature: NEON +// Asm: VBIT, CPU Feature: NEON func (x Int8x16) bitSelectNot(y Int8x16, mask Int8x16) Int8x16 /* broadcast1To2 */
diff --git a/test/codegen/simd_arm64.go b/test/codegen/simd_arm64.go index 5be078b..209e1fe 100644 --- a/test/codegen/simd_arm64.go +++ b/test/codegen/simd_arm64.go
@@ -110,14 +110,14 @@ } func mergeWithNotMask(x, y archsimd.Int8x16, mask archsimd.Mask8x16, f1, f2 archsimd.Float32x4) { - // arm64:`VBIF` -`VBIT` -`VNOT` + // arm64:`VBIT` -`VBIF` -`VNOT` sinkI8 = x.IfElse(mask.Not(), y) // arm64: `VFCMEQ` eq := f1.Equal(f2) // The next line `ne` should be CSEd with `eq` above ne := f1.NotEqual(f2) // arm64: -`.*` - fne := f1.IfElse(eq, f2) // arm64:`VBIT` - feq := f1.IfElse(ne, f2) // arm64:`VBIF` + feq := f1.IfElse(eq, f2) // arm64:`VBIF` + fne := f1.IfElse(ne, f2) // arm64:`VBIT` sinkF32 = fne.Add(feq) }