1
2
3
4
5
6
7 package simd_test
8
9 import (
10 "fmt"
11 "path/filepath"
12 "reflect"
13 "runtime"
14 "runtime/debug"
15 "simd/archsimd"
16 "strings"
17 "syscall"
18 "testing"
19 "unsafe"
20 )
21
22
23
24
25
26
27 func TestLoadPartPageBoundary128(t *testing.T) {
28 testLoadPartPageBoundary(t, archsimd.LoadInt8x16Part)
29 testLoadPartPageBoundary(t, archsimd.LoadInt16x8Part)
30 testLoadPartPageBoundary(t, archsimd.LoadInt32x4Part)
31 testLoadPartPageBoundary(t, archsimd.LoadInt64x2Part)
32 testLoadPartPageBoundary(t, archsimd.LoadUint8x16Part)
33 testLoadPartPageBoundary(t, archsimd.LoadUint16x8Part)
34 testLoadPartPageBoundary(t, archsimd.LoadUint32x4Part)
35 testLoadPartPageBoundary(t, archsimd.LoadUint64x2Part)
36 testLoadPartPageBoundary(t, archsimd.LoadFloat32x4Part)
37 testLoadPartPageBoundary(t, archsimd.LoadFloat64x2Part)
38 }
39
40
41
42 type HasLenAndStore[T number] interface {
43 Len() int
44 Store(s []T)
45 }
46
47
48
49
50
51 func testLoadPartPageBoundary[T number, V HasLenAndStore[T]](t *testing.T, load func(s []T) (V, int)) {
52 name := runtime.FuncForPC(reflect.ValueOf(load).Pointer()).Name()
53 name = name[strings.LastIndexByte(name, '.')+1:]
54 t.Run(name, func(t *testing.T) {
55 var zero V
56 n := zero.Len()
57 size := int(unsafe.Sizeof(T(0)))
58 page := guardedPage(t)
59 for l := 1; l <= n; l++ {
60 for _, off := range []int{0, len(page) - l*size} {
61 s := unsafe.Slice((*T)(unsafe.Pointer(&page[off])), l)
62 for i := range s {
63 s[i] = T(i + 1)
64 }
65 v, got, fault := loadCatchingFault(load, s)
66 if fault != nil {
67 start := uintptr(unsafe.Pointer(&s[0]))
68 where := "past the end of"
69 if fault.addr < start {
70 where = "before the start of"
71 }
72 t.Fatalf("%s read %s a %d-element slice at [%#x, %#x): fault at %#x in %s",
73 name, where, l, start, start+uintptr(l*size), fault.addr, fault.loc)
74 }
75 if got != l {
76 t.Errorf("%s(s) with len(s) = %d returned %d, want %d", name, l, got, l)
77 }
78 gotElems := make([]T, n)
79 v.Store(gotElems)
80 wantElems := make([]T, n)
81 copy(wantElems, s)
82 checkSlicesLogInput(t, gotElems, wantElems, 0.0, func() { t.Helper(); t.Logf("len(s) = %d", l) })
83 }
84 }
85 })
86 }
87
88
89
90 func guardedPage(t *testing.T) []byte {
91 t.Helper()
92 size := syscall.Getpagesize()
93 mem, err := syscall.Mmap(-1, 0, 3*size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
94 if err != nil {
95 t.Fatalf("mmap failed: %v", err)
96 }
97 t.Cleanup(func() {
98 if err := syscall.Munmap(mem); err != nil {
99 t.Errorf("munmap failed: %v", err)
100 }
101 })
102 if err := syscall.Mprotect(mem[:size], syscall.PROT_NONE); err != nil {
103 t.Fatalf("mprotect of low page failed: %v", err)
104 }
105 if err := syscall.Mprotect(mem[2*size:], syscall.PROT_NONE); err != nil {
106 t.Fatalf("mprotect of high page failed: %v", err)
107 }
108 return mem[size : 2*size : 2*size]
109 }
110
111
112 type faultInfo struct {
113 addr uintptr
114 loc string
115 }
116
117
118
119 func loadCatchingFault[T number, V any](load func(s []T) (V, int), s []T) (v V, n int, fault *faultInfo) {
120 old := debug.SetPanicOnFault(true)
121 defer debug.SetPanicOnFault(old)
122 defer func() {
123 r := recover()
124 if r == nil {
125 return
126 }
127 err, ok := r.(interface{ Addr() uintptr })
128 if !ok {
129 panic(r)
130 }
131 fault = &faultInfo{addr: err.Addr(), loc: faultLocation()}
132 }()
133 v, n = load(s)
134 return v, n, nil
135 }
136
137
138
139
140
141
142 func faultLocation() string {
143 pcs := make([]uintptr, 64)
144 frames := runtime.CallersFrames(pcs[:runtime.Callers(1, pcs)])
145
146 for {
147 frame, more := frames.Next()
148 if !more {
149 return "unknown location"
150 }
151 if frame.Function == "runtime.sigpanic" {
152 break
153 }
154 }
155 var locs []string
156 for {
157 frame, more := frames.Next()
158 if len(locs) > 0 && !strings.HasPrefix(frame.Function, "simd/archsimd.") {
159 break
160 }
161 locs = append(locs, fmt.Sprintf("%s (%s:%d)", frame.Function, filepath.Base(frame.File), frame.Line))
162 if !more {
163 break
164 }
165 }
166 return strings.Join(locs, ", called from ")
167 }
168
View as plain text