Source file src/simd/archsimd/internal/simd_test/loadstore_sve_arm64_test.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.simd && arm64
     6  
     7  package simd_test
     8  
     9  import (
    10  	"simd/archsimd"
    11  	"testing"
    12  )
    13  
    14  func mustPanic(t *testing.T, name string, f func()) {
    15  	t.Helper()
    16  	defer func() {
    17  		if recover() == nil {
    18  			t.Errorf("%s: expected panic but did not panic", name)
    19  		}
    20  	}()
    21  	f()
    22  }
    23  
    24  // testLoadStorePart exercises a scalable type's LoadTPart/StorePart across slice
    25  // lengths smaller than, equal to, and larger than the vector length, plus empty.
    26  // It checks the documented behavior — each reads/writes exactly min(len(s), Len())
    27  // elements — and that neither reads nor writes past the slice (memory safety):
    28  // a short load zeroes the inactive lanes rather than reading past the slice, and
    29  // a short store leaves the trailing elements untouched.
    30  func testLoadStorePart[T number, V any](t *testing.T, name string, load func([]T) (V, int), store func(V, []T) int, vlen int) {
    31  	t.Helper()
    32  	// Distinct nonzero source, longer than a full vector.
    33  	data := make([]T, vlen+8)
    34  	for i := range data {
    35  		data[i] = T(i + 1)
    36  	}
    37  
    38  	// Load: reads n = min(len, vlen) elements (returned), zeroes the rest, and
    39  	// never reads past the slice (a lane beyond the slice would be nonzero if it
    40  	// had). A nil slice reads nothing.
    41  	loadCases := []int{0, 1, vlen / 2, vlen - 1, vlen, vlen + 1, vlen + 8}
    42  	for ci, k := range loadCases {
    43  		if k < 0 || k > len(data) {
    44  			continue
    45  		}
    46  		src := data[:k]
    47  		if ci == 0 {
    48  			src = nil // exercise a nil (not just empty) slice
    49  		}
    50  		v, gotN := load(src)
    51  		active := min(k, vlen)
    52  		if gotN != active {
    53  			t.Errorf("%s Load len=%d: returned n=%d, want %d", name, k, gotN, active)
    54  		}
    55  		out := make([]T, vlen)
    56  		store(v, out) // len(out)==vlen: write back all lanes
    57  		for i := 0; i < active; i++ {
    58  			if out[i] != data[i] {
    59  				t.Errorf("%s Load len=%d: lane %d = %v, want %v", name, k, i, out[i], data[i])
    60  			}
    61  		}
    62  		for i := active; i < vlen; i++ {
    63  			if out[i] != 0 {
    64  				t.Errorf("%s Load len=%d: lane %d = %v, want 0 (read past slice?)", name, k, i, out[i])
    65  			}
    66  		}
    67  	}
    68  
    69  	// Store: writes exactly n = min(len, vlen) elements (returned), leaving the
    70  	// rest untouched. A nil slice writes nothing.
    71  	full, _ := load(data[:vlen])
    72  	const sentinel = 99
    73  	storeCases := []int{0, 1, vlen / 2, vlen - 1, vlen}
    74  	for ci, k := range storeCases {
    75  		if k < 0 {
    76  			continue
    77  		}
    78  		out := make([]T, vlen)
    79  		for i := range out {
    80  			out[i] = T(sentinel)
    81  		}
    82  		dst := out[:k]
    83  		if ci == 0 {
    84  			dst = nil
    85  		}
    86  		gotN := store(full, dst) // writes min(k, vlen) == k elements
    87  		if gotN != k {
    88  			t.Errorf("%s Store len=%d: returned n=%d, want %d", name, k, gotN, k)
    89  		}
    90  		for i := 0; i < k; i++ {
    91  			if out[i] != data[i] {
    92  				t.Errorf("%s Store len=%d: elem %d = %v, want %v", name, k, i, out[i], data[i])
    93  			}
    94  		}
    95  		for i := k; i < vlen; i++ {
    96  			if out[i] != T(sentinel) {
    97  				t.Errorf("%s Store len=%d: elem %d = %v, want sentinel (wrote past len?)", name, k, i, out[i])
    98  			}
    99  		}
   100  	}
   101  }
   102  
   103  func TestLoadStorePartSVE(t *testing.T) {
   104  	if !archsimd.ARM64.SVE() {
   105  		t.Skip("no sve")
   106  	}
   107  	testLoadStorePart(t, "Int8s", archsimd.LoadInt8sPart, archsimd.Int8s.StorePart, archsimd.Int8s{}.Len())
   108  	testLoadStorePart(t, "Uint8s", archsimd.LoadUint8sPart, archsimd.Uint8s.StorePart, archsimd.Uint8s{}.Len())
   109  	testLoadStorePart(t, "Int16s", archsimd.LoadInt16sPart, archsimd.Int16s.StorePart, archsimd.Int16s{}.Len())
   110  	testLoadStorePart(t, "Uint16s", archsimd.LoadUint16sPart, archsimd.Uint16s.StorePart, archsimd.Uint16s{}.Len())
   111  	testLoadStorePart(t, "Int32s", archsimd.LoadInt32sPart, archsimd.Int32s.StorePart, archsimd.Int32s{}.Len())
   112  	testLoadStorePart(t, "Uint32s", archsimd.LoadUint32sPart, archsimd.Uint32s.StorePart, archsimd.Uint32s{}.Len())
   113  	testLoadStorePart(t, "Float32s", archsimd.LoadFloat32sPart, archsimd.Float32s.StorePart, archsimd.Float32s{}.Len())
   114  	testLoadStorePart(t, "Int64s", archsimd.LoadInt64sPart, archsimd.Int64s.StorePart, archsimd.Int64s{}.Len())
   115  	testLoadStorePart(t, "Uint64s", archsimd.LoadUint64sPart, archsimd.Uint64s.StorePart, archsimd.Uint64s{}.Len())
   116  	testLoadStorePart(t, "Float64s", archsimd.LoadFloat64sPart, archsimd.Float64s.StorePart, archsimd.Float64s{}.Len())
   117  }
   118  
   119  // TestLoadStorePlainSVE checks the whole-vector Load/Store: they round-trip a
   120  // full slice and panic when the slice is shorter than the vector.
   121  func TestLoadStorePlainSVE(t *testing.T) {
   122  	if !archsimd.ARM64.SVE() {
   123  		t.Skip("no sve")
   124  	}
   125  	var z archsimd.Int8s
   126  	n := z.Len()
   127  	data := make([]int8, n)
   128  	for i := range data {
   129  		data[i] = int8(i + 1)
   130  	}
   131  	v := archsimd.LoadInt8s(data)
   132  	out := make([]int8, n)
   133  	v.Store(out)
   134  	for i := range data {
   135  		if out[i] != data[i] {
   136  			t.Errorf("LoadInt8s/Store round-trip: lane %d = %d, want %d", i, out[i], data[i])
   137  		}
   138  	}
   139  	mustPanic(t, "LoadInt8s short", func() { archsimd.LoadInt8s(make([]int8, n-1)) })
   140  	mustPanic(t, "Int8s.Store short", func() { v.Store(make([]int8, n-1)) })
   141  }
   142  

View as plain text