1
2
3
4
5 package riscv
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/testenv"
11 "os"
12 "os/exec"
13 "path/filepath"
14 "regexp"
15 "runtime"
16 "testing"
17 )
18
19
20
21
22 func TestLargeBranch(t *testing.T) {
23 if testing.Short() {
24 t.Skip("Skipping test in short mode")
25 }
26 testenv.MustHaveGoBuild(t)
27
28 dir := t.TempDir()
29
30 if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module largecall"), 0644); err != nil {
31 t.Fatalf("Failed to write file: %v\n", err)
32 }
33 main := `package main
34
35 import "fmt"
36
37 func main() {
38 fmt.Print(x())
39 }
40
41 func x() uint64
42 `
43 if err := os.WriteFile(filepath.Join(dir, "x.go"), []byte(main), 0644); err != nil {
44 t.Fatalf("failed to write main: %v\n", err)
45 }
46
47
48 buf := bytes.NewBuffer(make([]byte, 0, 7000000))
49 genLargeBranch(buf)
50
51 tmpfile := filepath.Join(dir, "x.s")
52 if err := os.WriteFile(tmpfile, buf.Bytes(), 0644); err != nil {
53 t.Fatalf("Failed to write file: %v", err)
54 }
55
56
57 cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile)
58 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
59 out, err := cmd.CombinedOutput()
60 if err != nil {
61 t.Errorf("Failed to assemble: %v\n%s", err, out)
62 }
63
64
65
66
67
68 want := regexp.MustCompile(`\sBNEZ\s.*\s.*\n.*\n.*AUIPC\s\$\d+, X31.*\n.*JALR\sX0, \$\d+, ?X31`)
69 if !want.Match(out) {
70 t.Error("Missing assembly instructions")
71 }
72
73
74 cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "x.exe", "-ldflags=-linkmode=internal")
75 cmd.Dir = dir
76 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
77 out, err = cmd.CombinedOutput()
78 if err != nil {
79 t.Errorf("Build failed: %v, output: %s", err, out)
80 }
81
82 if runtime.GOARCH == "riscv64" && runtime.GOOS == "linux" {
83 cmd = testenv.Command(t, filepath.Join(dir, "x.exe"))
84 out, err = cmd.CombinedOutput()
85 if err != nil {
86 t.Errorf("Failed to run test binary: %v", err)
87 }
88 if string(out) != "1" {
89 t.Errorf(`Got test output %q, want "2"`, string(out))
90 }
91 }
92 }
93
94 func genLargeBranch(buf *bytes.Buffer) {
95 fmt.Fprintln(buf, "TEXT ·x(SB),0,$0-8")
96 fmt.Fprintln(buf, "MOV X0, X10")
97 fmt.Fprintln(buf, "BEQZ X10, label")
98 for i := 0; i < 1<<18; i++ {
99
100 fmt.Fprintln(buf, "ADD $0, X5, X0")
101 }
102 fmt.Fprintln(buf, "ADD $1, X10, X10")
103 fmt.Fprintln(buf, "label:")
104 fmt.Fprintln(buf, "ADD $1, X10, X10")
105 fmt.Fprintln(buf, "MOV X10, r+0(FP)")
106 fmt.Fprintln(buf, "RET")
107 }
108
109
110
111
112
113 func TestLargeCall(t *testing.T) {
114 if testing.Short() {
115 t.Skip("Skipping test in short mode")
116 }
117 testenv.MustHaveGoBuild(t)
118
119 dir := t.TempDir()
120
121 if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module largecall"), 0644); err != nil {
122 t.Fatalf("Failed to write file: %v\n", err)
123 }
124 main := `package main
125
126 import "fmt"
127
128 func main() {
129 fmt.Print(x())
130 }
131
132 func x() uint64
133 func y() uint64
134 `
135 if err := os.WriteFile(filepath.Join(dir, "x.go"), []byte(main), 0644); err != nil {
136 t.Fatalf("failed to write main: %v\n", err)
137 }
138
139
140 buf := bytes.NewBuffer(make([]byte, 0, 7000000))
141 genLargeCall(buf)
142
143 tmpfile := filepath.Join(dir, "x.s")
144 if err := os.WriteFile(tmpfile, buf.Bytes(), 0644); err != nil {
145 t.Fatalf("Failed to write file: %v\n", err)
146 }
147
148
149 cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile)
150 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
151 out, err := cmd.CombinedOutput()
152 if err != nil {
153 t.Errorf("Failed to assemble: %v\n%s", err, out)
154 }
155
156
157
158
159 want := regexp.MustCompile(`\sAUIPC\s\$0, \$0, X31.*\n.*\sJALR\sX.*, X31`)
160 if !want.Match(out) {
161 t.Error("Missing assembly instructions")
162 }
163
164
165 cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "x.exe", "-ldflags=-linkmode=internal")
166 cmd.Dir = dir
167 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
168 out, err = cmd.CombinedOutput()
169 if err != nil {
170 t.Errorf("Build failed: %v, output: %s", err, out)
171 }
172
173 if runtime.GOARCH == "riscv64" && runtime.GOOS == "linux" {
174 cmd = testenv.Command(t, filepath.Join(dir, "x.exe"))
175 out, err = cmd.CombinedOutput()
176 if err != nil {
177 t.Errorf("Failed to run test binary: %v", err)
178 }
179 if string(out) != "2" {
180 t.Errorf(`Got test output %q, want "2"`, string(out))
181 }
182 }
183
184 if runtime.GOARCH == "riscv64" && testenv.HasCGO() {
185 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "x.exe", "-ldflags=-linkmode=external")
186 cmd.Dir = dir
187 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
188 out, err := cmd.CombinedOutput()
189 if err != nil {
190 t.Errorf("Build failed: %v, output: %s", err, out)
191 }
192
193 if runtime.GOARCH == "riscv64" && runtime.GOOS == "linux" {
194 cmd = testenv.Command(t, filepath.Join(dir, "x.exe"))
195 out, err = cmd.CombinedOutput()
196 if err != nil {
197 t.Errorf("Failed to run test binary: %v", err)
198 }
199 if string(out) != "2" {
200 t.Errorf(`Got test output %q, want "2"`, string(out))
201 }
202 }
203 }
204 }
205
206 func genLargeCall(buf *bytes.Buffer) {
207 fmt.Fprintln(buf, "TEXT ·x(SB),0,$0-8")
208 fmt.Fprintln(buf, "MOV X0, X10")
209 fmt.Fprintln(buf, "CALL ·y(SB)")
210 fmt.Fprintln(buf, "ADD $1, X10, X10")
211 fmt.Fprintln(buf, "MOV X10, r+0(FP)")
212 fmt.Fprintln(buf, "RET")
213 for i := 0; i < 1<<18; i++ {
214
215 fmt.Fprintln(buf, "ADD $0, X5, X0")
216 }
217 fmt.Fprintln(buf, "ADD $1, X10, X10")
218 fmt.Fprintln(buf, "RET")
219 fmt.Fprintln(buf, "TEXT ·y(SB),0,$0-0")
220 fmt.Fprintln(buf, "ADD $1, X10, X10")
221 fmt.Fprintln(buf, "RET")
222 }
223
224
225
226
227 func TestLargeJump(t *testing.T) {
228 if testing.Short() {
229 t.Skip("Skipping test in short mode")
230 }
231 testenv.MustHaveGoBuild(t)
232
233 dir := t.TempDir()
234
235 if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module largejump"), 0644); err != nil {
236 t.Fatalf("Failed to write file: %v\n", err)
237 }
238 main := `package main
239
240 import "fmt"
241
242 func main() {
243 fmt.Print(x())
244 }
245
246 func x() uint64
247 `
248 if err := os.WriteFile(filepath.Join(dir, "x.go"), []byte(main), 0644); err != nil {
249 t.Fatalf("failed to write main: %v\n", err)
250 }
251
252
253 buf := bytes.NewBuffer(make([]byte, 0, 7000000))
254 genLargeJump(buf)
255
256 tmpfile := filepath.Join(dir, "x.s")
257 if err := os.WriteFile(tmpfile, buf.Bytes(), 0644); err != nil {
258 t.Fatalf("Failed to write file: %v\n", err)
259 }
260
261
262 cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile)
263 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
264 out, err := cmd.CombinedOutput()
265 if err != nil {
266 t.Errorf("Failed to assemble: %v\n%s", err, out)
267 }
268
269
270
271
272 want := regexp.MustCompile(`\sAUIPC\s\$\d+, X31.*\n.*\sJALR\sX0, \$\d+, ?X31`)
273 if !want.Match(out) {
274 t.Error("Missing assembly instructions")
275 t.Errorf("%s", out)
276 }
277
278
279 cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "x.exe")
280 cmd.Dir = dir
281 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
282 out, err = cmd.CombinedOutput()
283 if err != nil {
284 t.Errorf("Build failed: %v, output: %s", err, out)
285 }
286
287 if runtime.GOARCH == "riscv64" && runtime.GOOS == "linux" {
288 cmd = testenv.Command(t, filepath.Join(dir, "x.exe"))
289 out, err = cmd.CombinedOutput()
290 if err != nil {
291 t.Errorf("Failed to run test binary: %v", err)
292 }
293 if string(out) != "1" {
294 t.Errorf(`Got test output %q, want "1"`, string(out))
295 }
296 }
297 }
298
299 func genLargeJump(buf *bytes.Buffer) {
300 fmt.Fprintln(buf, "TEXT ·x(SB),0,$0-8")
301 fmt.Fprintln(buf, "MOV X0, X10")
302 fmt.Fprintln(buf, "JMP end")
303 for i := 0; i < 1<<18; i++ {
304
305 fmt.Fprintln(buf, "ADD $0, X5, X0")
306 }
307 fmt.Fprintln(buf, "ADD $1, X10, X10")
308 fmt.Fprintln(buf, "end:")
309 fmt.Fprintln(buf, "ADD $1, X10, X10")
310 fmt.Fprintln(buf, "MOV X10, r+0(FP)")
311 fmt.Fprintln(buf, "RET")
312 }
313
314
315 func TestNoRet(t *testing.T) {
316 dir := t.TempDir()
317 tmpfile := filepath.Join(dir, "x.s")
318 if err := os.WriteFile(tmpfile, []byte("TEXT ·stub(SB),$0-0\nNOP\n"), 0644); err != nil {
319 t.Fatal(err)
320 }
321 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
322 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
323 if out, err := cmd.CombinedOutput(); err != nil {
324 t.Errorf("%v\n%s", err, out)
325 }
326 }
327
328 func TestImmediateSplitting(t *testing.T) {
329 dir := t.TempDir()
330 tmpfile := filepath.Join(dir, "x.s")
331 asm := `
332 TEXT _stub(SB),$0-0
333 LB 4096(X5), X6
334 LH 4096(X5), X6
335 LW 4096(X5), X6
336 LD 4096(X5), X6
337 LBU 4096(X5), X6
338 LHU 4096(X5), X6
339 LWU 4096(X5), X6
340 SB X6, 4096(X5)
341 SH X6, 4096(X5)
342 SW X6, 4096(X5)
343 SD X6, 4096(X5)
344
345 FLW 4096(X5), F6
346 FLD 4096(X5), F6
347 FSW F6, 4096(X5)
348 FSD F6, 4096(X5)
349
350 MOVB 4096(X5), X6
351 MOVH 4096(X5), X6
352 MOVW 4096(X5), X6
353 MOV 4096(X5), X6
354 MOVBU 4096(X5), X6
355 MOVHU 4096(X5), X6
356 MOVWU 4096(X5), X6
357
358 MOVB X6, 4096(X5)
359 MOVH X6, 4096(X5)
360 MOVW X6, 4096(X5)
361 MOV X6, 4096(X5)
362
363 MOVF 4096(X5), F6
364 MOVD 4096(X5), F6
365 MOVF F6, 4096(X5)
366 MOVD F6, 4096(X5)
367 `
368 if err := os.WriteFile(tmpfile, []byte(asm), 0644); err != nil {
369 t.Fatal(err)
370 }
371 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
372 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
373 if out, err := cmd.CombinedOutput(); err != nil {
374 t.Errorf("%v\n%s", err, out)
375 }
376 }
377
378 func TestBranch(t *testing.T) {
379 if runtime.GOARCH != "riscv64" {
380 t.Skip("Requires riscv64 to run")
381 }
382
383 testenv.MustHaveGoBuild(t)
384
385 cmd := testenv.Command(t, testenv.GoToolPath(t), "test")
386 cmd.Dir = "testdata/testbranch"
387 if out, err := testenv.CleanCmdEnv(cmd).CombinedOutput(); err != nil {
388 t.Errorf("Branch test failed: %v\n%s", err, out)
389 }
390 }
391
392 func TestMinMax(t *testing.T) {
393 if runtime.GOARCH != "riscv64" {
394 t.Skip("Requires riscv64 to run")
395 }
396
397 testenv.MustHaveGoBuild(t)
398
399 cmd := testenv.Command(t, testenv.GoToolPath(t), "test")
400 cmd.Dir = "testdata/testminmax"
401 if out, err := testenv.CleanCmdEnv(cmd).CombinedOutput(); err != nil {
402 t.Errorf("Min max test failed: %v\n%s", err, out)
403 }
404 }
405
406 func TestPCAlign(t *testing.T) {
407 dir := t.TempDir()
408 tmpfile := filepath.Join(dir, "x.s")
409 asm := `
410 TEXT _stub(SB),$0-0
411 FENCE
412 PCALIGN $8
413 FENCE
414 RET
415 `
416 if err := os.WriteFile(tmpfile, []byte(asm), 0644); err != nil {
417 t.Fatal(err)
418 }
419 cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile)
420 cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
421 out, err := cmd.CombinedOutput()
422 if err != nil {
423 t.Errorf("Failed to assemble: %v\n%s", err, out)
424 }
425
426
427
428
429
430 want := regexp.MustCompile("0x0000 0f 00 f0 0f 13 00 00 00 0f 00 f0 0f (82 80|67 80 00 00) ")
431 if !want.Match(out) {
432 t.Errorf("PCALIGN test failed - got %s\nwant %s", out, want)
433 }
434 }
435
View as plain text