| // 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]) | |
| } |