1
2
3
4
5 package ppc64
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/buildcfg"
11 "internal/testenv"
12 "math"
13 "os"
14 "path/filepath"
15 "regexp"
16 "strconv"
17 "strings"
18 "testing"
19
20 "internal/abi"
21
22 "cmd/internal/obj"
23 "cmd/internal/objabi"
24 )
25
26 var platformEnvs = [][]string{
27 {"GOOS=aix", "GOARCH=ppc64"},
28 {"GOOS=linux", "GOARCH=ppc64"},
29 {"GOOS=linux", "GOARCH=ppc64le"},
30 }
31
32 const invalidPCAlignSrc = `
33 TEXT test(SB),0,$0-0
34 ADD $2, R3
35 PCALIGN $128
36 RET
37 `
38
39 const validPCAlignSrc = `
40 TEXT test(SB),0,$0-0
41 ADD $2, R3
42 PCALIGN $16
43 MOVD $8, R16
44 ADD $8, R4
45 PCALIGN $32
46 ADD $8, R3
47 PCALIGN $8
48 ADD $4, R8
49 RET
50 `
51
52 const x64pgm = `
53 TEXT test(SB),0,$0-0
54 OR R0, R0
55 OR R0, R0
56 OR R0, R0
57 OR R0, R0
58 OR R0, R0
59 OR R0, R0
60 OR R0, R0
61 OR R0, R0
62 OR R0, R0
63 OR R0, R0
64 OR R0, R0
65 OR R0, R0
66 OR R0, R0
67 OR R0, R0
68 OR R0, R0
69 PNOP
70 `
71 const x32pgm = `
72 TEXT test(SB),0,$0-0
73 OR R0, R0
74 OR R0, R0
75 OR R0, R0
76 OR R0, R0
77 OR R0, R0
78 OR R0, R0
79 OR R0, R0
80 PNOP
81 OR R0, R0
82 OR R0, R0
83 OR R0, R0
84 OR R0, R0
85 OR R0, R0
86 OR R0, R0
87 OR R0, R0
88 OR R0, R0
89 `
90
91 const x16pgm = `
92 TEXT test(SB),0,$0-0
93 OR R0, R0
94 OR R0, R0
95 OR R0, R0
96 PNOP
97 OR R0, R0
98 OR R0, R0
99 OR R0, R0
100 OR R0, R0
101 OR R0, R0
102 OR R0, R0
103 OR R0, R0
104 OR R0, R0
105 OR R0, R0
106 OR R0, R0
107 OR R0, R0
108 OR R0, R0
109 `
110
111 const x0pgm = `
112 TEXT test(SB),0,$0-0
113 OR R0, R0
114 OR R0, R0
115 OR R0, R0
116 OR R0, R0
117 PNOP
118 OR R0, R0
119 OR R0, R0
120 OR R0, R0
121 OR R0, R0
122 OR R0, R0
123 OR R0, R0
124 OR R0, R0
125 OR R0, R0
126 OR R0, R0
127 OR R0, R0
128 OR R0, R0
129 `
130 const x64pgmA64 = `
131 TEXT test(SB),0,$0-0
132 OR R0, R0
133 OR R0, R0
134 OR R0, R0
135 OR R0, R0
136 OR R0, R0
137 OR R0, R0
138 OR R0, R0
139 PNOP
140 OR R0, R0
141 OR R0, R0
142 OR R0, R0
143 OR R0, R0
144 OR R0, R0
145 OR R0, R0
146 PNOP
147 `
148
149 const x64pgmA32 = `
150 TEXT test(SB),0,$0-0
151 OR R0, R0
152 OR R0, R0
153 OR R0, R0
154 PNOP
155 OR R0, R0
156 OR R0, R0
157 OR R0, R0
158 OR R0, R0
159 OR R0, R0
160 OR R0, R0
161 OR R0, R0
162 OR R0, R0
163 OR R0, R0
164 OR R0, R0
165 PNOP
166 `
167
168
169
170 func TestPfxAlign(t *testing.T) {
171 testenv.MustHaveGoBuild(t)
172
173 dir := t.TempDir()
174
175 pgms := []struct {
176 text []byte
177 align string
178 hasNop bool
179 }{
180 {[]byte(x0pgm), "align=0x0", false},
181 {[]byte(x16pgm), "align=0x20", false},
182 {[]byte(x32pgm), "align=0x40", false},
183 {[]byte(x64pgm), "align=0x0", true},
184 {[]byte(x64pgmA64), "align=0x40", true},
185 {[]byte(x64pgmA32), "align=0x20", true},
186 }
187
188 for _, pgm := range pgms {
189 tmpfile := filepath.Join(dir, "x.s")
190 err := os.WriteFile(tmpfile, pgm.text, 0644)
191 if err != nil {
192 t.Fatalf("can't write output: %v\n", err)
193 }
194 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
195 cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=ppc64le")
196 out, err := cmd.CombinedOutput()
197 if err != nil {
198 t.Errorf("Failed to compile %v: %v\n", pgm, err)
199 }
200 if !strings.Contains(string(out), pgm.align) {
201 t.Errorf("Fatal, misaligned text with prefixed instructions:\n%s", out)
202 }
203 hasNop := strings.Contains(string(out), "00 00 00 60")
204 if hasNop != pgm.hasNop {
205 t.Errorf("Fatal, prefixed instruction is missing nop padding:\n%s", out)
206 }
207 }
208 }
209
210
211
212
213 func TestLarge(t *testing.T) {
214 if testing.Short() {
215 t.Skip("Skip in short mode")
216 }
217 testenv.MustHaveGoBuild(t)
218
219 dir := t.TempDir()
220
221
222 tests := []struct {
223 jmpinsn string
224 backpattern []string
225 fwdpattern []string
226 }{
227
228
229
230
231 {"BEQ",
232 []string{``,
233 `0x20030 131120\s\(.*\)\tBC\t\$4,\sCR0EQ,\s131128`,
234 `0x20034 131124\s\(.*\)\tJMP\t0`},
235 []string{``,
236 `0x0000 00000\s\(.*\)\tBC\t\$4,\sCR0EQ,\s8`,
237 `0x0004 00004\s\(.*\)\tJMP\t131128`},
238 },
239 {"BNE",
240 []string{``,
241 `0x20030 131120\s\(.*\)\tBC\t\$12,\sCR0EQ,\s131128`,
242 `0x20034 131124\s\(.*\)\tJMP\t0`},
243 []string{``,
244 `0x0000 00000\s\(.*\)\tBC\t\$12,\sCR0EQ,\s8`,
245 `0x0004 00004\s\(.*\)\tJMP\t131128`}},
246
247 {"BC 16,0,",
248 []string{``,
249 `0x20030 131120\s\(.*\)\tBC\t\$18,\sCR0LT,\s131128`,
250 `0x20034 131124\s\(.*\)\tJMP\t0`},
251 []string{``,
252 `0x0000 00000\s\(.*\)\tBC\t\$18,\sCR0LT,\s8`,
253 `0x0004 00004\s\(.*\)\tJMP\t131128`}},
254 {"BC 18,0,",
255 []string{``,
256 `0x20030 131120\s\(.*\)\tBC\t\$16,\sCR0LT,\s131128`,
257 `0x20034 131124\s\(.*\)\tJMP\t0`},
258 []string{``,
259 `0x0000 00000\s\(.*\)\tBC\t\$16,\sCR0LT,\s8`,
260 `0x0004 00004\s\(.*\)\tJMP\t131128`}},
261
262 {"BC 8,0,",
263 []string{``,
264 `0x20034 131124\s\(.*\)\tBC\t\$8,\sCR0LT,\s131132`,
265 `0x20038 131128\s\(.*\)\tJMP\t131136`,
266 `0x2003c 131132\s\(.*\)\tJMP\t0\n`},
267 []string{``,
268 `0x0000 00000\s\(.*\)\tBC\t\$8,\sCR0LT,\s8`,
269 `0x0004 00004\s\(.*\)\tJMP\t12`,
270 `0x0008 00008\s\(.*\)\tJMP\t131136\n`}},
271 }
272
273 for _, test := range tests {
274
275 buf := bytes.NewBuffer(make([]byte, 0, 7000000))
276 gen(buf, test.jmpinsn)
277
278 tmpfile := filepath.Join(dir, "x.s")
279 err := os.WriteFile(tmpfile, buf.Bytes(), 0644)
280 if err != nil {
281 t.Fatalf("can't write output: %v\n", err)
282 }
283
284
285 for _, platenv := range platformEnvs {
286 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
287 cmd.Env = append(os.Environ(), platenv...)
288 out, err := cmd.CombinedOutput()
289 if err != nil {
290 t.Errorf("Assemble failed (%v): %v, output: %s", platenv, err, out)
291 }
292 matched, err := regexp.MatchString(strings.Join(test.fwdpattern, "\n\t*"), string(out))
293 if err != nil {
294 t.Fatal(err)
295 }
296 if !matched {
297 t.Errorf("Failed to detect long forward BC fixup in (%v):%s\n", platenv, out)
298 }
299 matched, err = regexp.MatchString(strings.Join(test.backpattern, "\n\t*"), string(out))
300 if err != nil {
301 t.Fatal(err)
302 }
303 if !matched {
304 t.Errorf("Failed to detect long backward BC fixup in (%v):%s\n", platenv, out)
305 }
306 }
307 }
308 }
309
310
311 func gen(buf *bytes.Buffer, jmpinsn string) {
312 fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
313 fmt.Fprintln(buf, "label_start:")
314 fmt.Fprintln(buf, jmpinsn, "label_end")
315 for i := 0; i < (1<<15 + 10); i++ {
316 fmt.Fprintln(buf, "MOVD R0, R1")
317 }
318 fmt.Fprintln(buf, jmpinsn, "label_start")
319 fmt.Fprintln(buf, "label_end:")
320 fmt.Fprintln(buf, "MOVD R0, R1")
321 fmt.Fprintln(buf, "RET")
322 }
323
324
325
326
327 func TestPCalign(t *testing.T) {
328 var pattern8 = `0x...8\s.*ADD\s..,\sR8`
329 var pattern16 = `0x...[80]\s.*MOVD\s..,\sR16`
330 var pattern32 = `0x...0\s.*ADD\s..,\sR3`
331
332 testenv.MustHaveGoBuild(t)
333
334 dir := t.TempDir()
335
336
337
338 tmpfile := filepath.Join(dir, "x.s")
339 err := os.WriteFile(tmpfile, []byte(validPCAlignSrc), 0644)
340 if err != nil {
341 t.Fatalf("can't write output: %v\n", err)
342 }
343
344
345 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile)
346 cmd.Env = append(os.Environ(), "GOARCH=ppc64le", "GOOS=linux")
347 out, err := cmd.CombinedOutput()
348 if err != nil {
349 t.Errorf("Build failed: %v, output: %s", err, out)
350 }
351
352 matched, err := regexp.MatchString(pattern8, string(out))
353 if err != nil {
354 t.Fatal(err)
355 }
356 if !matched {
357 t.Errorf("The 8 byte alignment is not correct: %t, output:%s\n", matched, out)
358 }
359
360 matched, err = regexp.MatchString(pattern16, string(out))
361 if err != nil {
362 t.Fatal(err)
363 }
364 if !matched {
365 t.Errorf("The 16 byte alignment is not correct: %t, output:%s\n", matched, out)
366 }
367
368 matched, err = regexp.MatchString(pattern32, string(out))
369 if err != nil {
370 t.Fatal(err)
371 }
372 if !matched {
373 t.Errorf("The 32 byte alignment is not correct: %t, output:%s\n", matched, out)
374 }
375
376
377
378 tmpfile = filepath.Join(dir, "xi.s")
379 err = os.WriteFile(tmpfile, []byte(invalidPCAlignSrc), 0644)
380 if err != nil {
381 t.Fatalf("can't write output: %v\n", err)
382 }
383
384
385 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "xi.o"), "-S", tmpfile)
386 cmd.Env = append(os.Environ(), "GOARCH=ppc64le", "GOOS=linux")
387 out, err = cmd.CombinedOutput()
388 if !strings.Contains(string(out), "Unexpected alignment") {
389 t.Errorf("Invalid alignment not detected for PCALIGN\n")
390 }
391 }
392
393
394
395
396
397
398
399
400
401
402
403
404
405 func TestRegValueAlignment(t *testing.T) {
406 tstFunc := func(rstart, rend, msk, rout int) {
407 for i := rstart; i <= rend; i++ {
408 if i&msk != rout {
409 t.Errorf("%v is not aligned to 0x%X (expected %d, got %d)\n", rconv(i), msk, rout, rstart&msk)
410 }
411 rout++
412 }
413 }
414 var testType = []struct {
415 rstart int
416 rend int
417 msk int
418 rout int
419 }{
420 {REG_VS0, REG_VS63, 63, 0},
421 {REG_R0, REG_R31, 31, 0},
422 {REG_F0, REG_F31, 31, 0},
423 {REG_V0, REG_V31, 31, 0},
424 {REG_V0, REG_V31, 63, 32},
425 {REG_F0, REG_F31, 63, 0},
426 {REG_SPR0, REG_SPR0 + 1023, 1023, 0},
427 {REG_CR0, REG_CR7, 7, 0},
428 {REG_CR0LT, REG_CR7SO, 31, 0},
429 }
430 for _, t := range testType {
431 tstFunc(t.rstart, t.rend, t.msk, t.rout)
432 }
433 }
434
435
436 func TestAddrClassifier(t *testing.T) {
437 type cmplx struct {
438 pic int
439 pic_dyn int
440 dyn int
441 nonpic int
442 }
443 tsts := [...]struct {
444 arg obj.Addr
445 output any
446 }{
447
448 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_R1}, C_REG},
449 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_R2}, C_REGP},
450 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_F1}, C_FREG},
451 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_F2}, C_FREGP},
452 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_V2}, C_VREG},
453 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS1}, C_VSREG},
454 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS2}, C_VSREGP},
455 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR}, C_CREG},
456 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR1}, C_CREG},
457 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR1SO}, C_CRBIT},
458 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0}, C_SPR},
459 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 8}, C_LR},
460 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 9}, C_CTR},
461 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_FPSCR}, C_FPSCR},
462 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_A1}, C_AREG},
463
464
465 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_GOTREF}, C_ADDR},
466 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_TOCREF}, C_ADDR},
467 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.STLSBSS}}, cmplx{C_TLS_IE, C_TLS_IE, C_TLS_LE, C_TLS_LE}},
468 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_ADDR},
469 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO}, C_SOREG},
470 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: BIG}, C_LOREG},
471 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LOREG},
472 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM}, C_SOREG},
473 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: BIG}, C_LOREG},
474 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LOREG},
475 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE}, C_ZOREG},
476 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Index: REG_R4}, C_XOREG},
477 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: 1}, C_SOREG},
478 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: BIG}, C_LOREG},
479 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: -BIG - 33}, C_LOREG},
480
481
482 {obj.Addr{Type: obj.TYPE_TEXTSIZE}, C_TEXTSIZE},
483 {obj.Addr{Type: obj.TYPE_FCONST, Val: 0.0}, C_ZCON},
484 {obj.Addr{Type: obj.TYPE_FCONST, Val: math.Float64frombits(0x8000000000000000)}, C_S16CON},
485
486
487 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1}, C_SACON},
488 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: BIG}, C_LACON},
489 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: -BIG - 1}, C_LACON},
490 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1 << 32}, C_DACON},
491 {obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
492 {obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_STATIC, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
493 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: 1}, C_SACON},
494 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: BIG}, C_LACON},
495 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LACON},
496 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: 1}, C_SACON},
497 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: BIG}, C_LACON},
498 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LACON},
499
500
501 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 0}, C_ZCON},
502 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1}, C_U1CON},
503 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 2}, C_U2CON},
504 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 4}, C_U3CON},
505 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 8}, C_U4CON},
506 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 16}, C_U5CON},
507 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 32}, C_U8CON},
508 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 14}, C_U15CON},
509 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 15}, C_U16CON},
510 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 + 1<<16}, C_U31CON},
511 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 31}, C_U32CON},
512 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 32}, C_S34CON},
513 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 33}, C_64CON},
514 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -1}, C_S16CON},
515 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -0x10001}, C_S32CON},
516 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 0x10001}, C_U31CON},
517 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 33)}, C_S34CON},
518 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 34)}, C_64CON},
519
520
521 {obj.Addr{Type: obj.TYPE_BRANCH, Sym: &obj.LSym{Type: objabi.SDATA}}, cmplx{C_BRA, C_BRAPIC, C_BRAPIC, C_BRA}},
522 {obj.Addr{Type: obj.TYPE_BRANCH}, C_BRA},
523 }
524
525 pic_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Arch: &Linkppc64}, autosize: 0}
526 pic_dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
527 dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
528 nonpic_ctxt9 := ctxt9{ctxt: &obj.Link{Arch: &Linkppc64}, autosize: 0}
529 ctxts := [...]*ctxt9{&pic_ctxt9, &pic_dyn_ctxt9, &dyn_ctxt9, &nonpic_ctxt9}
530 name := [...]string{"pic", "pic_dyn", "dyn", "nonpic"}
531 for _, tst := range tsts {
532 var expect []int
533 switch tst.output.(type) {
534 case cmplx:
535 v := tst.output.(cmplx)
536 expect = []int{v.pic, v.pic_dyn, v.dyn, v.nonpic}
537 case int:
538 expect = []int{tst.output.(int), tst.output.(int), tst.output.(int), tst.output.(int)}
539 }
540 for i := range ctxts {
541 if output := ctxts[i].aclass(&tst.arg); output != expect[i] {
542 t.Errorf("%s.aclass(%v) = %v, expected %v\n", name[i], tst.arg, DRconv(output), DRconv(expect[i]))
543 }
544 }
545 }
546 }
547
548
549 func TestOptabReinit(t *testing.T) {
550 buildcfg.GOOS = "linux"
551 buildcfg.GOARCH = "ppc64le"
552 buildcfg.GOPPC64 = 8
553 buildop(nil)
554 optabLen := len(optab)
555 buildcfg.GOPPC64 = 9
556 buildop(nil)
557 reinitOptabLen := len(optab)
558 if reinitOptabLen != optabLen {
559 t.Errorf("rerunning buildop changes optab size from %d to %d", optabLen, reinitOptabLen)
560 }
561 }
562
563
564
565
566
567
568
569 const tailCallSrc = `
570 // 4 = NOSPLIT, 512 = NOFRAME (see textflag.h).
571 TEXT ·leafNoFrame(SB),4|512,$0-0
572 MOVD $0, R3
573 RET (R3)
574
575 TEXT ·leafFrame(SB),4,$8-0
576 MOVD $0, R3
577 RET (R3)
578
579 TEXT ·nonLeaf(SB),4,$8-0
580 CALL ·leafNoFrame(SB)
581 MOVD $0, R3
582 RET (R3)
583
584 // A tail call that is not the last instruction in the function,
585 // to check that the unsafe point ends at the branch and does not
586 // swallow the rest of the function.
587 TEXT ·leafNoFrameCond(SB),4|512,$0-0
588 CMP R3, $0
589 BEQ skip
590 RET (R3)
591 skip:
592 MOVD $0, R3
593 RET
594 `
595
596 func TestTailCallUnsafePoint(t *testing.T) {
597 testenv.MustHaveGoBuild(t)
598
599 dir := t.TempDir()
600 tmpfile := filepath.Join(dir, "x.s")
601 if err := os.WriteFile(tmpfile, []byte(tailCallSrc), 0644); err != nil {
602 t.Fatalf("can't write output: %v\n", err)
603 }
604
605 for _, goarch := range []string{"ppc64", "ppc64le"} {
606 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile)
607 cmd.Env = append(os.Environ(), "GOARCH="+goarch, "GOOS=linux")
608 out, err := cmd.CombinedOutput()
609 if err != nil {
610 t.Fatalf("GOARCH=%s: assembly failed: %v, output:\n%s", goarch, err, out)
611 }
612
613
614
615
616
617 branches := 0
618 sym := ""
619 unsafePoint := int64(abi.UnsafePointSafe)
620 atBranch := false
621 endFunc := func() {
622 if sym != "" && unsafePoint != abi.UnsafePointSafe {
623 t.Errorf("GOARCH=%s: %s: unsafe point %d at end of function, want %d",
624 goarch, sym, unsafePoint, abi.UnsafePointSafe)
625 }
626 }
627 for _, line := range strings.Split(string(out), "\n") {
628 f := strings.Fields(line)
629
630
631
632
633 switch {
634 case len(f) >= 2 && f[1] == "STEXT":
635 endFunc()
636 sym, unsafePoint, atBranch = f[0], abi.UnsafePointSafe, false
637 case len(f) < 4 || !strings.HasPrefix(f[2], "("):
638
639 case len(f) >= 6 && f[3] == "PCDATA" && f[4] == fmt.Sprintf("$%d,", abi.PCDATA_UnsafePoint):
640 v, err := strconv.ParseInt(strings.TrimPrefix(f[5], "$"), 10, 64)
641 if err != nil {
642 t.Fatalf("GOARCH=%s: can't parse %q: %v", goarch, line, err)
643 }
644 unsafePoint = v
645 case f[3] == "PCDATA" || f[3] == "FUNCDATA" || f[3] == "TEXT":
646
647 case len(f) >= 5 && f[3] == "JMP" && f[4] == "CTR":
648 branches++
649 atBranch = true
650 if unsafePoint != abi.UnsafePointUnsafe {
651 t.Errorf("GOARCH=%s: %s\n\tbranch through CTR has unsafe point %d, want %d",
652 goarch, strings.TrimSpace(line), unsafePoint, abi.UnsafePointUnsafe)
653 }
654 case atBranch:
655 atBranch = false
656 if unsafePoint != abi.UnsafePointSafe {
657 t.Errorf("GOARCH=%s: %s\n\tinstruction after branch through CTR has unsafe point %d, want %d",
658 goarch, strings.TrimSpace(line), unsafePoint, abi.UnsafePointSafe)
659 }
660 }
661 }
662 endFunc()
663 if want := strings.Count(tailCallSrc, "RET\t(R3)"); branches != want {
664 t.Errorf("GOARCH=%s: found %d branches through CTR, want %d; output:\n%s", goarch, branches, want, out)
665 }
666 }
667 }
668
View as plain text