| // 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 && amd64 |
| |
| package simd_test |
| |
| import ( |
| "simd/archsimd" |
| "testing" |
| ) |
| |
| func TestFloatMinMax32AMD64(t *testing.T) { |
| for _, tc := range []struct { |
| name string |
| lanes int |
| run func([]float32, []float32, [4][]float32, bool, bool) |
| }{ |
| {"Float32x8", 8, floatMinMax32x8}, |
| {"Float32x16", 16, floatMinMax32x16}, |
| } { |
| t.Run(tc.name, func(t *testing.T) { |
| if tc.lanes == 16 && !archsimd.X86.AVX512() { |
| t.Skip("requires AVX512") |
| } |
| testFloatMinMax32(t, tc.lanes, tc.run) |
| }) |
| } |
| } |
| |
| func TestFloatMinMax64AMD64(t *testing.T) { |
| for _, tc := range []struct { |
| name string |
| lanes int |
| run func([]float64, []float64, [4][]float64, bool, bool) |
| }{ |
| {"Float64x4", 4, floatMinMax64x4}, |
| {"Float64x8", 8, floatMinMax64x8}, |
| } { |
| t.Run(tc.name, func(t *testing.T) { |
| if tc.lanes == 8 && !archsimd.X86.AVX512() { |
| t.Skip("requires AVX512") |
| } |
| testFloatMinMax64(t, tc.lanes, tc.run) |
| }) |
| } |
| } |
| |
| func floatMinMaxPlatformConfig() floatMinMaxConfig { |
| return floatMinMaxConfig{allowMasked: archsimd.X86.AVX512()} |
| } |
| |
| func floatMinMaxMask32x4() archsimd.Mask32x4 { |
| return archsimd.Mask32x4FromBits(0x5) |
| } |
| |
| func floatMinMaxMask64x2() archsimd.Mask64x2 { |
| return archsimd.Mask64x2FromBits(0x1) |
| } |
| |
| //go:noinline |
| func floatMinMax32x8(a, b []float32, out [4][]float32, reverse, masked bool) { |
| x, y := archsimd.LoadFloat32x8(a), archsimd.LoadFloat32x8(b) |
| var minXY, minYX, maxXY, maxYX archsimd.Float32x8 |
| if masked { |
| mask := archsimd.Mask32x8FromBits(0x55) |
| if reverse { |
| minYX, minXY = y.Min(x).Masked(mask), x.Min(y).Masked(mask) |
| maxYX, maxXY = y.Max(x).Masked(mask), x.Max(y).Masked(mask) |
| } else { |
| minXY, minYX = x.Min(y).Masked(mask), y.Min(x).Masked(mask) |
| maxXY, maxYX = x.Max(y).Masked(mask), y.Max(x).Masked(mask) |
| } |
| } else if reverse { |
| minYX, minXY = y.Min(x), x.Min(y) |
| maxYX, maxXY = y.Max(x), x.Max(y) |
| } else { |
| minXY, minYX = x.Min(y), y.Min(x) |
| maxXY, maxYX = x.Max(y), y.Max(x) |
| } |
| minXY.Store(out[0]) |
| minYX.Store(out[1]) |
| maxXY.Store(out[2]) |
| maxYX.Store(out[3]) |
| } |
| |
| //go:noinline |
| func floatMinMax32x16(a, b []float32, out [4][]float32, reverse, masked bool) { |
| x, y := archsimd.LoadFloat32x16(a), archsimd.LoadFloat32x16(b) |
| var minXY, minYX, maxXY, maxYX archsimd.Float32x16 |
| if masked { |
| mask := archsimd.Mask32x16FromBits(0x5555) |
| if reverse { |
| minYX, minXY = y.Min(x).Masked(mask), x.Min(y).Masked(mask) |
| maxYX, maxXY = y.Max(x).Masked(mask), x.Max(y).Masked(mask) |
| } else { |
| minXY, minYX = x.Min(y).Masked(mask), y.Min(x).Masked(mask) |
| maxXY, maxYX = x.Max(y).Masked(mask), y.Max(x).Masked(mask) |
| } |
| } else if reverse { |
| minYX, minXY = y.Min(x), x.Min(y) |
| maxYX, maxXY = y.Max(x), x.Max(y) |
| } else { |
| minXY, minYX = x.Min(y), y.Min(x) |
| maxXY, maxYX = x.Max(y), y.Max(x) |
| } |
| minXY.Store(out[0]) |
| minYX.Store(out[1]) |
| maxXY.Store(out[2]) |
| maxYX.Store(out[3]) |
| } |
| |
| //go:noinline |
| func floatMinMax64x4(a, b []float64, out [4][]float64, reverse, masked bool) { |
| x, y := archsimd.LoadFloat64x4(a), archsimd.LoadFloat64x4(b) |
| var minXY, minYX, maxXY, maxYX archsimd.Float64x4 |
| if masked { |
| mask := archsimd.Mask64x4FromBits(0x5) |
| if reverse { |
| minYX, minXY = y.Min(x).Masked(mask), x.Min(y).Masked(mask) |
| maxYX, maxXY = y.Max(x).Masked(mask), x.Max(y).Masked(mask) |
| } else { |
| minXY, minYX = x.Min(y).Masked(mask), y.Min(x).Masked(mask) |
| maxXY, maxYX = x.Max(y).Masked(mask), y.Max(x).Masked(mask) |
| } |
| } else if reverse { |
| minYX, minXY = y.Min(x), x.Min(y) |
| maxYX, maxXY = y.Max(x), x.Max(y) |
| } else { |
| minXY, minYX = x.Min(y), y.Min(x) |
| maxXY, maxYX = x.Max(y), y.Max(x) |
| } |
| minXY.Store(out[0]) |
| minYX.Store(out[1]) |
| maxXY.Store(out[2]) |
| maxYX.Store(out[3]) |
| } |
| |
| //go:noinline |
| func floatMinMax64x8(a, b []float64, out [4][]float64, reverse, masked bool) { |
| x, y := archsimd.LoadFloat64x8(a), archsimd.LoadFloat64x8(b) |
| var minXY, minYX, maxXY, maxYX archsimd.Float64x8 |
| if masked { |
| mask := archsimd.Mask64x8FromBits(0x55) |
| if reverse { |
| minYX, minXY = y.Min(x).Masked(mask), x.Min(y).Masked(mask) |
| maxYX, maxXY = y.Max(x).Masked(mask), x.Max(y).Masked(mask) |
| } else { |
| minXY, minYX = x.Min(y).Masked(mask), y.Min(x).Masked(mask) |
| maxXY, maxYX = x.Max(y).Masked(mask), y.Max(x).Masked(mask) |
| } |
| } else if reverse { |
| minYX, minXY = y.Min(x), x.Min(y) |
| maxYX, maxXY = y.Max(x), x.Max(y) |
| } else { |
| minXY, minYX = x.Min(y), y.Min(x) |
| maxXY, maxYX = x.Max(y), y.Max(x) |
| } |
| minXY.Store(out[0]) |
| minYX.Store(out[1]) |
| maxXY.Store(out[2]) |
| maxYX.Store(out[3]) |
| } |