1 // Copyright 2020 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.
23 var platformEnvs = [][]string{
24 {"GOOS=aix", "GOARCH=ppc64"},
25 {"GOOS=linux", "GOARCH=ppc64"},
26 {"GOOS=linux", "GOARCH=ppc64le"},
29 const invalidPCAlignSrc = `
36 const validPCAlignSrc = `
165 // Test that nops are inserted when crossing 64B boundaries, and
166 // alignment is adjusted to avoid crossing.
167 func TestPfxAlign(t *testing.T) {
168 testenv.MustHaveGoBuild(t)
170 dir, err := os.MkdirTemp("", "testpfxalign")
172 t.Fatalf("could not create directory: %v", err)
174 defer os.RemoveAll(dir)
181 {[]byte(x0pgm), "align=0x0", false}, // No alignment or nop adjustments needed
182 {[]byte(x16pgm), "align=0x20", false}, // Increased alignment needed
183 {[]byte(x32pgm), "align=0x40", false}, // Worst case alignment needed
184 {[]byte(x64pgm), "align=0x0", true}, // 0 aligned is default (16B) alignment
185 {[]byte(x64pgmA64), "align=0x40", true}, // extra alignment + nop
186 {[]byte(x64pgmA32), "align=0x20", true}, // extra alignment + nop
189 for _, pgm := range pgms {
190 tmpfile := filepath.Join(dir, "x.s")
191 err = os.WriteFile(tmpfile, pgm.text, 0644)
193 t.Fatalf("can't write output: %v\n", err)
195 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
196 cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=ppc64le")
197 out, err := cmd.CombinedOutput()
199 t.Errorf("Failed to compile %v: %v\n", pgm, err)
201 if !strings.Contains(string(out), pgm.align) {
202 t.Errorf("Fatal, misaligned text with prefixed instructions:\n%s", out)
204 hasNop := strings.Contains(string(out), "00 00 00 60")
205 if hasNop != pgm.hasNop {
206 t.Errorf("Fatal, prefixed instruction is missing nop padding:\n%s", out)
211 // TestLarge generates a very large file to verify that large
212 // program builds successfully, and branches which exceed the
213 // range of BC are rewritten to reach.
214 func TestLarge(t *testing.T) {
216 t.Skip("Skip in short mode")
218 testenv.MustHaveGoBuild(t)
220 dir, err := os.MkdirTemp("", "testlarge")
222 t.Fatalf("could not create directory: %v", err)
224 defer os.RemoveAll(dir)
226 // A few interesting test cases for long conditional branch fixups
232 // Test the interesting cases of conditional branch rewrites for too-far targets. Simple conditional
233 // branches can be made to reach with one JMP insertion, compound conditionals require two.
235 // beq <-> bne conversion (insert one jump)
238 `0x20030 131120\s\(.*\)\tBC\t\$4,\sCR0EQ,\s131128`,
239 `0x20034 131124\s\(.*\)\tJMP\t0`},
241 `0x0000 00000\s\(.*\)\tBC\t\$4,\sCR0EQ,\s8`,
242 `0x0004 00004\s\(.*\)\tJMP\t131128`},
246 `0x20030 131120\s\(.*\)\tBC\t\$12,\sCR0EQ,\s131128`,
247 `0x20034 131124\s\(.*\)\tJMP\t0`},
249 `0x0000 00000\s\(.*\)\tBC\t\$12,\sCR0EQ,\s8`,
250 `0x0004 00004\s\(.*\)\tJMP\t131128`}},
251 // bdnz (BC 16,0,tgt) <-> bdz (BC 18,0,+4) conversion (insert one jump)
254 `0x20030 131120\s\(.*\)\tBC\t\$18,\sCR0LT,\s131128`,
255 `0x20034 131124\s\(.*\)\tJMP\t0`},
257 `0x0000 00000\s\(.*\)\tBC\t\$18,\sCR0LT,\s8`,
258 `0x0004 00004\s\(.*\)\tJMP\t131128`}},
261 `0x20030 131120\s\(.*\)\tBC\t\$16,\sCR0LT,\s131128`,
262 `0x20034 131124\s\(.*\)\tJMP\t0`},
264 `0x0000 00000\s\(.*\)\tBC\t\$16,\sCR0LT,\s8`,
265 `0x0004 00004\s\(.*\)\tJMP\t131128`}},
266 // bdnzt (BC 8,0,tgt) <-> bdnzt (BC 8,0,+4) conversion (insert two jumps)
269 `0x20034 131124\s\(.*\)\tBC\t\$8,\sCR0LT,\s131132`,
270 `0x20038 131128\s\(.*\)\tJMP\t131136`,
271 `0x2003c 131132\s\(.*\)\tJMP\t0\n`},
273 `0x0000 00000\s\(.*\)\tBC\t\$8,\sCR0LT,\s8`,
274 `0x0004 00004\s\(.*\)\tJMP\t12`,
275 `0x0008 00008\s\(.*\)\tJMP\t131136\n`}},
278 for _, test := range tests {
279 // generate a very large function
280 buf := bytes.NewBuffer(make([]byte, 0, 7000000))
281 gen(buf, test.jmpinsn)
283 tmpfile := filepath.Join(dir, "x.s")
284 err = os.WriteFile(tmpfile, buf.Bytes(), 0644)
286 t.Fatalf("can't write output: %v\n", err)
289 // Test on all supported ppc64 platforms
290 for _, platenv := range platformEnvs {
291 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
292 cmd.Env = append(os.Environ(), platenv...)
293 out, err := cmd.CombinedOutput()
295 t.Errorf("Assemble failed (%v): %v, output: %s", platenv, err, out)
297 matched, err := regexp.MatchString(strings.Join(test.fwdpattern, "\n\t*"), string(out))
302 t.Errorf("Failed to detect long forward BC fixup in (%v):%s\n", platenv, out)
304 matched, err = regexp.MatchString(strings.Join(test.backpattern, "\n\t*"), string(out))
309 t.Errorf("Failed to detect long backward BC fixup in (%v):%s\n", platenv, out)
315 // gen generates a very large program with a very long forward and backwards conditional branch.
316 func gen(buf *bytes.Buffer, jmpinsn string) {
317 fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
318 fmt.Fprintln(buf, "label_start:")
319 fmt.Fprintln(buf, jmpinsn, "label_end")
320 for i := 0; i < (1<<15 + 10); i++ {
321 fmt.Fprintln(buf, "MOVD R0, R1")
323 fmt.Fprintln(buf, jmpinsn, "label_start")
324 fmt.Fprintln(buf, "label_end:")
325 fmt.Fprintln(buf, "MOVD R0, R1")
326 fmt.Fprintln(buf, "RET")
329 // TestPCalign generates two asm files containing the
330 // PCALIGN directive, to verify correct values are and
331 // accepted, and incorrect values are flagged in error.
332 func TestPCalign(t *testing.T) {
333 var pattern8 = `0x...8\s.*ADD\s..,\sR8`
334 var pattern16 = `0x...[80]\s.*MOVD\s..,\sR16`
335 var pattern32 = `0x...0\s.*ADD\s..,\sR3`
337 testenv.MustHaveGoBuild(t)
339 dir, err := os.MkdirTemp("", "testpcalign")
341 t.Fatalf("could not create directory: %v", err)
343 defer os.RemoveAll(dir)
345 // generate a test with valid uses of PCALIGN
347 tmpfile := filepath.Join(dir, "x.s")
348 err = os.WriteFile(tmpfile, []byte(validPCAlignSrc), 0644)
350 t.Fatalf("can't write output: %v\n", err)
353 // build generated file without errors and assemble it
354 cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile)
355 cmd.Env = append(os.Environ(), "GOARCH=ppc64le", "GOOS=linux")
356 out, err := cmd.CombinedOutput()
358 t.Errorf("Build failed: %v, output: %s", err, out)
361 matched, err := regexp.MatchString(pattern8, string(out))
366 t.Errorf("The 8 byte alignment is not correct: %t, output:%s\n", matched, out)
369 matched, err = regexp.MatchString(pattern16, string(out))
374 t.Errorf("The 16 byte alignment is not correct: %t, output:%s\n", matched, out)
377 matched, err = regexp.MatchString(pattern32, string(out))
382 t.Errorf("The 32 byte alignment is not correct: %t, output:%s\n", matched, out)
385 // generate a test with invalid use of PCALIGN
387 tmpfile = filepath.Join(dir, "xi.s")
388 err = os.WriteFile(tmpfile, []byte(invalidPCAlignSrc), 0644)
390 t.Fatalf("can't write output: %v\n", err)
393 // build test with errors and check for messages
394 cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "xi.o"), "-S", tmpfile)
395 cmd.Env = append(os.Environ(), "GOARCH=ppc64le", "GOOS=linux")
396 out, err = cmd.CombinedOutput()
397 if !strings.Contains(string(out), "Unexpected alignment") {
398 t.Errorf("Invalid alignment not detected for PCALIGN\n")
402 // Verify register constants are correctly aligned. Much of the ppc64 assembler assumes masking out significant
403 // bits will produce a valid register number:
408 // REG_SPRx & 1023 == x
411 // VR and FPR disjointly overlap VSR, interpreting as VSR registers should produce the correctly overlapped VSR.
413 // REG_Vx & 63 == x + 32
414 func TestRegValueAlignment(t *testing.T) {
415 tstFunc := func(rstart, rend, msk, rout int) {
416 for i := rstart; i <= rend; i++ {
418 t.Errorf("%v is not aligned to 0x%X (expected %d, got %d)\n", rconv(i), msk, rout, rstart&msk)
423 var testType = []struct {
429 {REG_VS0, REG_VS63, 63, 0},
430 {REG_R0, REG_R31, 31, 0},
431 {REG_F0, REG_F31, 31, 0},
432 {REG_V0, REG_V31, 31, 0},
433 {REG_V0, REG_V31, 63, 32},
434 {REG_F0, REG_F31, 63, 0},
435 {REG_SPR0, REG_SPR0 + 1023, 1023, 0},
436 {REG_CR0, REG_CR7, 7, 0},
437 {REG_CR0LT, REG_CR7SO, 31, 0},
439 for _, t := range testType {
440 tstFunc(t.rstart, t.rend, t.msk, t.rout)
444 // Verify interesting obj.Addr arguments are classified correctly.
445 func TestAddrClassifier(t *testing.T) {
452 tsts := [...]struct {
456 // Supported register type args
457 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_R1}, C_REG},
458 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_R2}, C_REGP},
459 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_F1}, C_FREG},
460 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_F2}, C_FREGP},
461 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_V2}, C_VREG},
462 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS1}, C_VSREG},
463 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS2}, C_VSREGP},
464 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR}, C_CREG},
465 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR1}, C_CREG},
466 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR1SO}, C_CRBIT},
467 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0}, C_SPR},
468 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 1}, C_XER},
469 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 8}, C_LR},
470 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 9}, C_CTR},
471 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_FPSCR}, C_FPSCR},
472 {obj.Addr{Type: obj.TYPE_REG, Reg: REG_A1}, C_AREG},
474 // Memory type arguments.
475 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_GOTREF}, C_ADDR},
476 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_TOCREF}, C_ADDR},
477 {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}},
478 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_ADDR},
479 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO}, C_SOREG},
480 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: BIG}, C_LOREG},
481 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LOREG},
482 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM}, C_SOREG},
483 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: BIG}, C_LOREG},
484 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LOREG}, // 33 is FixedFrameSize-1
485 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE}, C_ZOREG},
486 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Index: REG_R4}, C_XOREG},
487 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: 1}, C_SOREG},
488 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: BIG}, C_LOREG},
489 {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: -BIG - 33}, C_LOREG},
491 // Misc (golang initializes -0.0 to 0.0, hence the obfuscation below)
492 {obj.Addr{Type: obj.TYPE_TEXTSIZE}, C_TEXTSIZE},
493 {obj.Addr{Type: obj.TYPE_FCONST, Val: 0.0}, C_ZCON},
494 {obj.Addr{Type: obj.TYPE_FCONST, Val: math.Float64frombits(0x8000000000000000)}, C_S16CON},
496 // Address type arguments
497 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1}, C_SACON},
498 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: BIG}, C_LACON},
499 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: -BIG - 1}, C_LACON},
500 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1 << 32}, C_DACON},
501 {obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
502 {obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_STATIC, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
503 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: 1}, C_SACON},
504 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: BIG}, C_LACON},
505 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LACON},
506 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: 1}, C_SACON},
507 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: BIG}, C_LACON},
508 {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LACON}, // 33 is FixedFrameSize-1
510 // Constant type arguments
511 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 0}, C_ZCON},
512 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1}, C_U1CON},
513 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 2}, C_U2CON},
514 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 4}, C_U3CON},
515 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 8}, C_U4CON},
516 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 16}, C_U5CON},
517 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 32}, C_U8CON},
518 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 14}, C_U15CON},
519 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 15}, C_U16CON},
520 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 16}, C_U3216CON},
521 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 + 1<<16}, C_U32CON},
522 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 32}, C_S34CON},
523 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 33}, C_64CON},
524 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -1}, C_S16CON},
525 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -0x10000}, C_S3216CON},
526 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -0x10001}, C_S32CON},
527 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 33)}, C_S34CON},
528 {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 34)}, C_64CON},
530 // Branch like arguments
531 {obj.Addr{Type: obj.TYPE_BRANCH, Sym: &obj.LSym{Type: objabi.SDATA}}, cmplx{C_SBRA, C_LBRAPIC, C_LBRAPIC, C_SBRA}},
532 {obj.Addr{Type: obj.TYPE_BRANCH}, C_SBRA},
535 pic_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Arch: &Linkppc64}, autosize: 0}
536 pic_dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
537 dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
538 nonpic_ctxt9 := ctxt9{ctxt: &obj.Link{Arch: &Linkppc64}, autosize: 0}
539 ctxts := [...]*ctxt9{&pic_ctxt9, &pic_dyn_ctxt9, &dyn_ctxt9, &nonpic_ctxt9}
540 name := [...]string{"pic", "pic_dyn", "dyn", "nonpic"}
541 for _, tst := range tsts {
543 switch tst.output.(type) {
545 v := tst.output.(cmplx)
546 expect = []int{v.pic, v.pic_dyn, v.dyn, v.nonpic}
548 expect = []int{tst.output.(int), tst.output.(int), tst.output.(int), tst.output.(int)}
550 for i := range ctxts {
551 if output := ctxts[i].aclass(&tst.arg); output != expect[i] {
552 t.Errorf("%s.aclass(%v) = %v, expected %v\n", name[i], tst.arg, DRconv(output), DRconv(expect[i]))
558 // The optab size should remain constant when reinitializing the PPC64 assembler backend.
559 func TestOptabReinit(t *testing.T) {
560 buildcfg.GOOS = "linux"
561 buildcfg.GOARCH = "ppc64le"
564 optabLen := len(optab)
567 reinitOptabLen := len(optab)
568 if reinitOptabLen != optabLen {
569 t.Errorf("rerunning buildop changes optab size from %d to %d", optabLen, reinitOptabLen)