| // Copyright 2025 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 && amd64 |
| |
| package simd_test |
| |
| import ( |
| "simd/archsimd" |
| "testing" |
| ) |
| |
| func TestRotateAllLeft(t *testing.T) { |
| x := uint8(0x81) |
| if y := rotl(x, 1); y != 3 { |
| t.Errorf("Expected 3, got 0x%x", y) |
| } |
| if y := rotl(x, 7); y != 0xc0 { |
| t.Errorf("Expected 0xc0, got 0x%x", y) |
| } |
| if y := rotr(x, 4); y != 0x18 { |
| t.Errorf("Expected 0x18, got 0x%x", y) |
| } |
| |
| for i := uint64(0); i < 65; i++ { |
| testUint64x2Unary(t, curry2(archsimd.Uint64x2.RotateAllLeft, i), rotlOfSlice[uint64](i)) |
| testUint32x4Unary(t, curry2(archsimd.Uint32x4.RotateAllLeft, i), rotlOfSlice[uint32](i)) |
| // testUint16x8Unary(t, curry2(archsimd.Uint16x8.RotateAllLeft, i), rotlOfSlice[uint16](i)) |
| // testUint8x16Unary(t, curry2(archsimd.Uint8x16.RotateAllLeft, i), rotlOfSlice[uint8](i)) |
| } |
| for i := uint64(0); i < 65; i++ { |
| testUint64x4Unary(t, curry2(archsimd.Uint64x4.RotateAllLeft, i), rotlOfSlice[uint64](i)) |
| testUint32x8Unary(t, curry2(archsimd.Uint32x8.RotateAllLeft, i), rotlOfSlice[uint32](i)) |
| // testUint16x16Unary(t, curry2(archsimd.Uint16x16.RotateAllLeft, i), rotlOfSlice[uint16](i)) |
| // testUint8x32Unary(t, curry2(archsimd.Uint8x32.RotateAllLeft, i), rotlOfSlice[uint8](i)) |
| } |
| |
| } |
| |
| func TestRotateAllRight(t *testing.T) { |
| x := uint8(0x81) |
| if y := rotr(x, 1); y != 0xc0 { |
| t.Errorf("Expected 0xc0, got 0x%x", y) |
| } |
| if y := rotr(x, 7); y != 3 { |
| t.Errorf("Expected 3, got 0x%x", y) |
| } |
| if y := rotr(x, 4); y != 0x18 { |
| t.Errorf("Expected 0x18, got 0x%x", y) |
| } |
| |
| for i := uint64(0); i < 65; i++ { |
| testUint64x2Unary(t, curry2(archsimd.Uint64x2.RotateAllRight, i), rotrOfSlice[uint64](i)) |
| testUint32x4Unary(t, curry2(archsimd.Uint32x4.RotateAllRight, i), rotrOfSlice[uint32](i)) |
| // testUint16x8Unary(t, curry2(archsimd.Uint16x8.RotateAllLeft, i), rotlOfSlice[uint16](i)) |
| // testUint8x16Unary(t, curry2(archsimd.Uint8x16.RotateAllLeft, i), rotlOfSlice[uint8](i)) |
| } |
| for i := uint64(0); i < 65; i++ { |
| testUint64x4Unary(t, curry2(archsimd.Uint64x4.RotateAllRight, i), rotrOfSlice[uint64](i)) |
| testUint32x8Unary(t, curry2(archsimd.Uint32x8.RotateAllRight, i), rotrOfSlice[uint32](i)) |
| // testUint16x16Unary(t, curry2(archsimd.Uint16x16.RotateAllLeft, i), rotlOfSlice[uint16](i)) |
| // testUint8x32Unary(t, curry2(archsimd.Uint8x32.RotateAllLeft, i), rotlOfSlice[uint8](i)) |
| } |
| } |
| |
| func TestShift(t *testing.T) { |
| if !archsimd.X86.AVX2() { |
| t.Skip("requires AVX2") |
| } |
| |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftLeft(y.AsUint32x4()) }, |
| map2(func(x, y int32) int32 { return x << uint32(y) })) |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftRight(y.AsUint32x4()) }, |
| map2(func(x, y int32) int32 { return x >> uint32(y) })) |
| testUint32x4Binary(t, |
| func(x, y archsimd.Uint32x4) archsimd.Uint32x4 { return x.ShiftRight(y) }, |
| map2(func(x, y uint32) uint32 { return x >> y })) |
| } |
| |
| func concatInt32s(x, y int32) int64 { |
| return (int64(x) << 32) | int64(uint32(y)) |
| } |
| |
| func concatUint32s(x, y uint32) uint64 { |
| return (uint64(x) << 32) | uint64(y) |
| } |
| |
| func TestShiftAllConcat(t *testing.T) { |
| if !archsimd.X86.AVX512VBMI2() { |
| t.Skip("requires AVX512-VBMI2") |
| } |
| |
| // Note that unlike their non-Concat counterparts, these wrap the shift count. |
| |
| hide := hideConst[uint64] |
| |
| // ShiftAllLeftConcat |
| salc := func(shift uint64) func(x, y int32) int32 { |
| return func(x, y int32) int32 { |
| return int32(concatInt32s(x, y) >> (32 - shift%32)) |
| } |
| } |
| |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftAllLeftConcatMod32(y, 2) }, |
| map2(salc(2))) |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftAllLeftConcatMod32(y, hide(2)) }, |
| map2(salc(hide(2)))) |
| |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftAllLeftConcatMod32(y, 128) }, |
| map2(salc(128))) |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftAllLeftConcatMod32(y, hide(128)) }, |
| map2(salc(hide(128)))) |
| |
| // Signed ShiftAllRightConcat |
| sarc := func(shift uint64) func(x, y int32) int32 { |
| return func(x, y int32) int32 { |
| return int32(concatInt32s(y, x) >> (shift % 32)) |
| } |
| } |
| |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftAllRightConcatMod32(y, 2) }, |
| map2(sarc(2))) |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftAllRightConcatMod32(y, hide(2)) }, |
| map2(sarc(hide(2)))) |
| |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftAllRightConcatMod32(y, 128) }, |
| map2(sarc(128))) |
| testInt32x4Binary(t, |
| func(x, y archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftAllRightConcatMod32(y, hide(128)) }, |
| map2(sarc(hide(128)))) |
| |
| // Unsigned ShiftAllRightConcat |
| usarc := func(shift uint64) func(x, y uint32) uint32 { |
| return func(x, y uint32) uint32 { |
| return uint32(concatUint32s(y, x) >> (shift % 32)) |
| } |
| } |
| |
| testUint32x4Binary(t, |
| func(x, y archsimd.Uint32x4) archsimd.Uint32x4 { return x.ShiftAllRightConcatMod32(y, 2) }, |
| map2(usarc(2))) |
| testUint32x4Binary(t, |
| func(x, y archsimd.Uint32x4) archsimd.Uint32x4 { return x.ShiftAllRightConcatMod32(y, hide(2)) }, |
| map2(usarc(hide(2)))) |
| |
| testUint32x4Binary(t, |
| func(x, y archsimd.Uint32x4) archsimd.Uint32x4 { return x.ShiftAllRightConcatMod32(y, 128) }, |
| map2(usarc(128))) |
| testUint32x4Binary(t, |
| func(x, y archsimd.Uint32x4) archsimd.Uint32x4 { return x.ShiftAllRightConcatMod32(y, hide(128)) }, |
| map2(usarc(hide(128)))) |
| } |
| |
| func TestShiftConcat(t *testing.T) { |
| if !archsimd.X86.AVX512VBMI2() { |
| t.Skip("requires AVX512-VBMI2") |
| } |
| |
| // Note that unlike their non-Concat counterparts, these wrap the shift count. |
| |
| testInt32x4Ternary(t, |
| func(x, y, z archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftLeftConcatMod32(y, z.AsUint32x4()) }, |
| map3(func(x, y, z int32) int32 { |
| return int32(concatInt32s(x, y) >> (32 - uint32(z)%32)) |
| })) |
| |
| testInt32x4Ternary(t, |
| func(x, y, z archsimd.Int32x4) archsimd.Int32x4 { return x.ShiftRightConcatMod32(y, z.AsUint32x4()) }, |
| map3(func(x, y, z int32) int32 { |
| return int32(concatInt32s(y, x) >> (uint32(z) % 32)) |
| })) |
| |
| testUint32x4Ternary(t, |
| func(x, y, z archsimd.Uint32x4) archsimd.Uint32x4 { return x.ShiftRightConcatMod32(y, z) }, |
| map3(func(x, y, z uint32) uint32 { |
| return uint32(concatUint32s(y, x) >> (z % 32)) |
| })) |
| } |
| |
| func TestConcatShiftBytesRight(t *testing.T) { |
| hide := hideConst[uint64] |
| |
| csbr := func(shift uint64) func(x, y []uint8) []uint8 { |
| return func(x, y []uint8) []uint8 { |
| z := make([]uint8, len(x)) |
| for i := range z { |
| target := i + int(shift) |
| if target < 16 { |
| z[i] = y[target] |
| } else if target < 32 { |
| z[i] = x[(target - 16)] |
| } |
| } |
| return z |
| } |
| } |
| |
| t.Run("Uint8x16", func(t *testing.T) { |
| if !archsimd.X86.AVX() { |
| t.Skip("requires AVX") |
| } |
| for _, shift := range []uint64{0, 2, 16, 20, 32, 128} { |
| t.Log("shift", shift) |
| testUint8x16Binary(t, |
| func(x, y archsimd.Uint8x16) archsimd.Uint8x16 { return x.ConcatShiftBytesRight(y, shift) }, |
| csbr(shift)) |
| testUint8x16Binary(t, |
| func(x, y archsimd.Uint8x16) archsimd.Uint8x16 { return x.ConcatShiftBytesRight(y, hide(shift)) }, |
| csbr(hide(shift))) |
| } |
| }) |
| |
| t.Run("Uint8x32", func(t *testing.T) { |
| if !archsimd.X86.AVX2() { |
| t.Skip("requires AVX2") |
| } |
| for _, shift := range []uint64{0, 2, 16, 20, 32, 128} { |
| t.Log("shift", shift) |
| testUint8x32Binary(t, |
| func(x, y archsimd.Uint8x32) archsimd.Uint8x32 { return x.ConcatShiftBytesRightGrouped(y, shift) }, |
| grouped2(csbr(shift))) |
| testUint8x32Binary(t, |
| func(x, y archsimd.Uint8x32) archsimd.Uint8x32 { return x.ConcatShiftBytesRightGrouped(y, hide(shift)) }, |
| grouped2(csbr(hide(shift)))) |
| } |
| }) |
| |
| t.Run("Uint8x64", func(t *testing.T) { |
| if !archsimd.X86.AVX512() { |
| t.Skip("requires AVX512") |
| } |
| for _, shift := range []uint64{0, 2, 16, 20, 32, 128} { |
| t.Log("shift", shift) |
| testUint8x64Binary(t, |
| func(x, y archsimd.Uint8x64) archsimd.Uint8x64 { return x.ConcatShiftBytesRightGrouped(y, shift) }, |
| grouped2(csbr(shift))) |
| testUint8x64Binary(t, |
| func(x, y archsimd.Uint8x64) archsimd.Uint8x64 { return x.ConcatShiftBytesRightGrouped(y, hide(shift)) }, |
| grouped2(csbr(hide(shift)))) |
| } |
| }) |
| } |