github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/obj/ppc64/asm_test.go (about)

     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.
     4  
     5  package ppc64
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"math"
    11  	"os"
    12  	"path/filepath"
    13  	"regexp"
    14  	"strings"
    15  	"testing"
    16  
    17  	"github.com/go-asm/go/buildcfg"
    18  	"github.com/go-asm/go/testenv"
    19  
    20  	"github.com/go-asm/go/cmd/obj"
    21  	"github.com/go-asm/go/cmd/objabi"
    22  )
    23  
    24  var platformEnvs = [][]string{
    25  	{"GOOS=aix", "GOARCH=ppc64"},
    26  	{"GOOS=linux", "GOARCH=ppc64"},
    27  	{"GOOS=linux", "GOARCH=ppc64le"},
    28  }
    29  
    30  const invalidPCAlignSrc = `
    31  TEXT test(SB),0,$0-0
    32  ADD $2, R3
    33  PCALIGN $128
    34  RET
    35  `
    36  
    37  const validPCAlignSrc = `
    38  TEXT test(SB),0,$0-0
    39  ADD $2, R3
    40  PCALIGN $16
    41  MOVD $8, R16
    42  ADD $8, R4
    43  PCALIGN $32
    44  ADD $8, R3
    45  PCALIGN $8
    46  ADD $4, R8
    47  RET
    48  `
    49  
    50  const x64pgm = `
    51  TEXT test(SB),0,$0-0
    52  OR R0, R0
    53  OR R0, R0
    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  PNOP
    68  `
    69  const x32pgm = `
    70  TEXT test(SB),0,$0-0
    71  OR R0, R0
    72  OR R0, R0
    73  OR R0, R0
    74  OR R0, R0
    75  OR R0, R0
    76  OR R0, R0
    77  OR R0, R0
    78  PNOP
    79  OR R0, R0
    80  OR R0, R0
    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  `
    88  
    89  const x16pgm = `
    90  TEXT test(SB),0,$0-0
    91  OR R0, R0
    92  OR R0, R0
    93  OR R0, R0
    94  PNOP
    95  OR R0, R0
    96  OR R0, R0
    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  `
   108  
   109  const x0pgm = `
   110  TEXT test(SB),0,$0-0
   111  OR R0, R0
   112  OR R0, R0
   113  OR R0, R0
   114  OR R0, R0
   115  PNOP
   116  OR R0, R0
   117  OR R0, R0
   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  `
   128  const x64pgmA64 = `
   129  TEXT test(SB),0,$0-0
   130  OR R0, R0
   131  OR R0, R0
   132  OR R0, R0
   133  OR R0, R0
   134  OR R0, R0
   135  OR R0, R0
   136  OR R0, R0
   137  PNOP
   138  OR R0, R0
   139  OR R0, R0
   140  OR R0, R0
   141  OR R0, R0
   142  OR R0, R0
   143  OR R0, R0
   144  PNOP
   145  `
   146  
   147  const x64pgmA32 = `
   148  TEXT test(SB),0,$0-0
   149  OR R0, R0
   150  OR R0, R0
   151  OR R0, R0
   152  PNOP
   153  OR R0, R0
   154  OR R0, R0
   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  PNOP
   164  `
   165  
   166  // Test that nops are inserted when crossing 64B boundaries, and
   167  // alignment is adjusted to avoid crossing.
   168  func TestPfxAlign(t *testing.T) {
   169  	testenv.MustHaveGoBuild(t)
   170  
   171  	dir, err := os.MkdirTemp("", "testpfxalign")
   172  	if err != nil {
   173  		t.Fatalf("could not create directory: %v", err)
   174  	}
   175  	defer os.RemoveAll(dir)
   176  
   177  	pgms := []struct {
   178  		text   []byte
   179  		align  string
   180  		hasNop bool
   181  	}{
   182  		{[]byte(x0pgm), "align=0x0", false},     // No alignment or nop adjustments needed
   183  		{[]byte(x16pgm), "align=0x20", false},   // Increased alignment needed
   184  		{[]byte(x32pgm), "align=0x40", false},   // Worst case alignment needed
   185  		{[]byte(x64pgm), "align=0x0", true},     // 0 aligned is default (16B) alignment
   186  		{[]byte(x64pgmA64), "align=0x40", true}, // extra alignment + nop
   187  		{[]byte(x64pgmA32), "align=0x20", true}, // extra alignment + nop
   188  	}
   189  
   190  	for _, pgm := range pgms {
   191  		tmpfile := filepath.Join(dir, "x.s")
   192  		err = os.WriteFile(tmpfile, pgm.text, 0644)
   193  		if err != nil {
   194  			t.Fatalf("can't write output: %v\n", err)
   195  		}
   196  		cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
   197  		cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=ppc64le")
   198  		out, err := cmd.CombinedOutput()
   199  		if err != nil {
   200  			t.Errorf("Failed to compile %v: %v\n", pgm, err)
   201  		}
   202  		if !strings.Contains(string(out), pgm.align) {
   203  			t.Errorf("Fatal, misaligned text with prefixed instructions:\n%s", out)
   204  		}
   205  		hasNop := strings.Contains(string(out), "00 00 00 60")
   206  		if hasNop != pgm.hasNop {
   207  			t.Errorf("Fatal, prefixed instruction is missing nop padding:\n%s", out)
   208  		}
   209  	}
   210  }
   211  
   212  // TestLarge generates a very large file to verify that large
   213  // program builds successfully, and branches which exceed the
   214  // range of BC are rewritten to reach.
   215  func TestLarge(t *testing.T) {
   216  	if testing.Short() {
   217  		t.Skip("Skip in short mode")
   218  	}
   219  	testenv.MustHaveGoBuild(t)
   220  
   221  	dir, err := os.MkdirTemp("", "testlarge")
   222  	if err != nil {
   223  		t.Fatalf("could not create directory: %v", err)
   224  	}
   225  	defer os.RemoveAll(dir)
   226  
   227  	// A few interesting test cases for long conditional branch fixups
   228  	tests := []struct {
   229  		jmpinsn     string
   230  		backpattern []string
   231  		fwdpattern  []string
   232  	}{
   233  		// Test the interesting cases of conditional branch rewrites for too-far targets. Simple conditional
   234  		// branches can be made to reach with one JMP insertion, compound conditionals require two.
   235  		//
   236  		// beq <-> bne conversion (insert one jump)
   237  		{"BEQ",
   238  			[]string{``,
   239  				`0x20030 131120\s\(.*\)\tBC\t\$4,\sCR0EQ,\s131128`,
   240  				`0x20034 131124\s\(.*\)\tJMP\t0`},
   241  			[]string{``,
   242  				`0x0000 00000\s\(.*\)\tBC\t\$4,\sCR0EQ,\s8`,
   243  				`0x0004 00004\s\(.*\)\tJMP\t131128`},
   244  		},
   245  		{"BNE",
   246  			[]string{``,
   247  				`0x20030 131120\s\(.*\)\tBC\t\$12,\sCR0EQ,\s131128`,
   248  				`0x20034 131124\s\(.*\)\tJMP\t0`},
   249  			[]string{``,
   250  				`0x0000 00000\s\(.*\)\tBC\t\$12,\sCR0EQ,\s8`,
   251  				`0x0004 00004\s\(.*\)\tJMP\t131128`}},
   252  		// bdnz (BC 16,0,tgt) <-> bdz (BC 18,0,+4) conversion (insert one jump)
   253  		{"BC 16,0,",
   254  			[]string{``,
   255  				`0x20030 131120\s\(.*\)\tBC\t\$18,\sCR0LT,\s131128`,
   256  				`0x20034 131124\s\(.*\)\tJMP\t0`},
   257  			[]string{``,
   258  				`0x0000 00000\s\(.*\)\tBC\t\$18,\sCR0LT,\s8`,
   259  				`0x0004 00004\s\(.*\)\tJMP\t131128`}},
   260  		{"BC 18,0,",
   261  			[]string{``,
   262  				`0x20030 131120\s\(.*\)\tBC\t\$16,\sCR0LT,\s131128`,
   263  				`0x20034 131124\s\(.*\)\tJMP\t0`},
   264  			[]string{``,
   265  				`0x0000 00000\s\(.*\)\tBC\t\$16,\sCR0LT,\s8`,
   266  				`0x0004 00004\s\(.*\)\tJMP\t131128`}},
   267  		// bdnzt (BC 8,0,tgt) <-> bdnzt (BC 8,0,+4) conversion (insert two jumps)
   268  		{"BC 8,0,",
   269  			[]string{``,
   270  				`0x20034 131124\s\(.*\)\tBC\t\$8,\sCR0LT,\s131132`,
   271  				`0x20038 131128\s\(.*\)\tJMP\t131136`,
   272  				`0x2003c 131132\s\(.*\)\tJMP\t0\n`},
   273  			[]string{``,
   274  				`0x0000 00000\s\(.*\)\tBC\t\$8,\sCR0LT,\s8`,
   275  				`0x0004 00004\s\(.*\)\tJMP\t12`,
   276  				`0x0008 00008\s\(.*\)\tJMP\t131136\n`}},
   277  	}
   278  
   279  	for _, test := range tests {
   280  		// generate a very large function
   281  		buf := bytes.NewBuffer(make([]byte, 0, 7000000))
   282  		gen(buf, test.jmpinsn)
   283  
   284  		tmpfile := filepath.Join(dir, "x.s")
   285  		err = os.WriteFile(tmpfile, buf.Bytes(), 0644)
   286  		if err != nil {
   287  			t.Fatalf("can't write output: %v\n", err)
   288  		}
   289  
   290  		// Test on all supported ppc64 platforms
   291  		for _, platenv := range platformEnvs {
   292  			cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", filepath.Join(dir, "test.o"), tmpfile)
   293  			cmd.Env = append(os.Environ(), platenv...)
   294  			out, err := cmd.CombinedOutput()
   295  			if err != nil {
   296  				t.Errorf("Assemble failed (%v): %v, output: %s", platenv, err, out)
   297  			}
   298  			matched, err := regexp.MatchString(strings.Join(test.fwdpattern, "\n\t*"), string(out))
   299  			if err != nil {
   300  				t.Fatal(err)
   301  			}
   302  			if !matched {
   303  				t.Errorf("Failed to detect long forward BC fixup in (%v):%s\n", platenv, out)
   304  			}
   305  			matched, err = regexp.MatchString(strings.Join(test.backpattern, "\n\t*"), string(out))
   306  			if err != nil {
   307  				t.Fatal(err)
   308  			}
   309  			if !matched {
   310  				t.Errorf("Failed to detect long backward BC fixup in (%v):%s\n", platenv, out)
   311  			}
   312  		}
   313  	}
   314  }
   315  
   316  // gen generates a very large program with a very long forward and backwards conditional branch.
   317  func gen(buf *bytes.Buffer, jmpinsn string) {
   318  	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
   319  	fmt.Fprintln(buf, "label_start:")
   320  	fmt.Fprintln(buf, jmpinsn, "label_end")
   321  	for i := 0; i < (1<<15 + 10); i++ {
   322  		fmt.Fprintln(buf, "MOVD R0, R1")
   323  	}
   324  	fmt.Fprintln(buf, jmpinsn, "label_start")
   325  	fmt.Fprintln(buf, "label_end:")
   326  	fmt.Fprintln(buf, "MOVD R0, R1")
   327  	fmt.Fprintln(buf, "RET")
   328  }
   329  
   330  // TestPCalign generates two asm files containing the
   331  // PCALIGN directive, to verify correct values are and
   332  // accepted, and incorrect values are flagged in error.
   333  func TestPCalign(t *testing.T) {
   334  	var pattern8 = `0x...8\s.*ADD\s..,\sR8`
   335  	var pattern16 = `0x...[80]\s.*MOVD\s..,\sR16`
   336  	var pattern32 = `0x...0\s.*ADD\s..,\sR3`
   337  
   338  	testenv.MustHaveGoBuild(t)
   339  
   340  	dir, err := os.MkdirTemp("", "testpcalign")
   341  	if err != nil {
   342  		t.Fatalf("could not create directory: %v", err)
   343  	}
   344  	defer os.RemoveAll(dir)
   345  
   346  	// generate a test with valid uses of PCALIGN
   347  
   348  	tmpfile := filepath.Join(dir, "x.s")
   349  	err = os.WriteFile(tmpfile, []byte(validPCAlignSrc), 0644)
   350  	if err != nil {
   351  		t.Fatalf("can't write output: %v\n", err)
   352  	}
   353  
   354  	// build generated file without errors and assemble it
   355  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), "-S", tmpfile)
   356  	cmd.Env = append(os.Environ(), "GOARCH=ppc64le", "GOOS=linux")
   357  	out, err := cmd.CombinedOutput()
   358  	if err != nil {
   359  		t.Errorf("Build failed: %v, output: %s", err, out)
   360  	}
   361  
   362  	matched, err := regexp.MatchString(pattern8, string(out))
   363  	if err != nil {
   364  		t.Fatal(err)
   365  	}
   366  	if !matched {
   367  		t.Errorf("The 8 byte alignment is not correct: %t, output:%s\n", matched, out)
   368  	}
   369  
   370  	matched, err = regexp.MatchString(pattern16, string(out))
   371  	if err != nil {
   372  		t.Fatal(err)
   373  	}
   374  	if !matched {
   375  		t.Errorf("The 16 byte alignment is not correct: %t, output:%s\n", matched, out)
   376  	}
   377  
   378  	matched, err = regexp.MatchString(pattern32, string(out))
   379  	if err != nil {
   380  		t.Fatal(err)
   381  	}
   382  	if !matched {
   383  		t.Errorf("The 32 byte alignment is not correct: %t, output:%s\n", matched, out)
   384  	}
   385  
   386  	// generate a test with invalid use of PCALIGN
   387  
   388  	tmpfile = filepath.Join(dir, "xi.s")
   389  	err = os.WriteFile(tmpfile, []byte(invalidPCAlignSrc), 0644)
   390  	if err != nil {
   391  		t.Fatalf("can't write output: %v\n", err)
   392  	}
   393  
   394  	// build test with errors and check for messages
   395  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "xi.o"), "-S", tmpfile)
   396  	cmd.Env = append(os.Environ(), "GOARCH=ppc64le", "GOOS=linux")
   397  	out, err = cmd.CombinedOutput()
   398  	if !strings.Contains(string(out), "Unexpected alignment") {
   399  		t.Errorf("Invalid alignment not detected for PCALIGN\n")
   400  	}
   401  }
   402  
   403  // Verify register constants are correctly aligned. Much of the ppc64 assembler assumes masking out significant
   404  // bits will produce a valid register number:
   405  // REG_Rx & 31 == x
   406  // REG_Fx & 31 == x
   407  // REG_Vx & 31 == x
   408  // REG_VSx & 63 == x
   409  // REG_SPRx & 1023 == x
   410  // REG_CRx & 7 == x
   411  //
   412  // VR and FPR disjointly overlap VSR, interpreting as VSR registers should produce the correctly overlapped VSR.
   413  // REG_FPx & 63 == x
   414  // REG_Vx & 63 == x + 32
   415  func TestRegValueAlignment(t *testing.T) {
   416  	tstFunc := func(rstart, rend, msk, rout int) {
   417  		for i := rstart; i <= rend; i++ {
   418  			if i&msk != rout {
   419  				t.Errorf("%v is not aligned to 0x%X (expected %d, got %d)\n", rconv(i), msk, rout, rstart&msk)
   420  			}
   421  			rout++
   422  		}
   423  	}
   424  	var testType = []struct {
   425  		rstart int
   426  		rend   int
   427  		msk    int
   428  		rout   int
   429  	}{
   430  		{REG_VS0, REG_VS63, 63, 0},
   431  		{REG_R0, REG_R31, 31, 0},
   432  		{REG_F0, REG_F31, 31, 0},
   433  		{REG_V0, REG_V31, 31, 0},
   434  		{REG_V0, REG_V31, 63, 32},
   435  		{REG_F0, REG_F31, 63, 0},
   436  		{REG_SPR0, REG_SPR0 + 1023, 1023, 0},
   437  		{REG_CR0, REG_CR7, 7, 0},
   438  		{REG_CR0LT, REG_CR7SO, 31, 0},
   439  	}
   440  	for _, t := range testType {
   441  		tstFunc(t.rstart, t.rend, t.msk, t.rout)
   442  	}
   443  }
   444  
   445  // Verify interesting obj.Addr arguments are classified correctly.
   446  func TestAddrClassifier(t *testing.T) {
   447  	type cmplx struct {
   448  		pic     int
   449  		pic_dyn int
   450  		dyn     int
   451  		nonpic  int
   452  	}
   453  	tsts := [...]struct {
   454  		arg    obj.Addr
   455  		output interface{}
   456  	}{
   457  		// Supported register type args
   458  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_R1}, C_REG},
   459  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_R2}, C_REGP},
   460  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_F1}, C_FREG},
   461  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_F2}, C_FREGP},
   462  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_V2}, C_VREG},
   463  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS1}, C_VSREG},
   464  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS2}, C_VSREGP},
   465  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR}, C_CREG},
   466  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR1}, C_CREG},
   467  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR1SO}, C_CRBIT},
   468  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0}, C_SPR},
   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},
   473  
   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},
   490  
   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},
   495  
   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
   509  
   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 + 1<<16}, C_U32CON},
   521  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 32}, C_S34CON},
   522  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 33}, C_64CON},
   523  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -1}, C_S16CON},
   524  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -0x10001}, C_S32CON},
   525  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 33)}, C_S34CON},
   526  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 34)}, C_64CON},
   527  
   528  		// Branch like arguments
   529  		{obj.Addr{Type: obj.TYPE_BRANCH, Sym: &obj.LSym{Type: objabi.SDATA}}, cmplx{C_SBRA, C_LBRAPIC, C_LBRAPIC, C_SBRA}},
   530  		{obj.Addr{Type: obj.TYPE_BRANCH}, C_SBRA},
   531  	}
   532  
   533  	pic_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Arch: &Linkppc64}, autosize: 0}
   534  	pic_dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
   535  	dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
   536  	nonpic_ctxt9 := ctxt9{ctxt: &obj.Link{Arch: &Linkppc64}, autosize: 0}
   537  	ctxts := [...]*ctxt9{&pic_ctxt9, &pic_dyn_ctxt9, &dyn_ctxt9, &nonpic_ctxt9}
   538  	name := [...]string{"pic", "pic_dyn", "dyn", "nonpic"}
   539  	for _, tst := range tsts {
   540  		var expect []int
   541  		switch tst.output.(type) {
   542  		case cmplx:
   543  			v := tst.output.(cmplx)
   544  			expect = []int{v.pic, v.pic_dyn, v.dyn, v.nonpic}
   545  		case int:
   546  			expect = []int{tst.output.(int), tst.output.(int), tst.output.(int), tst.output.(int)}
   547  		}
   548  		for i := range ctxts {
   549  			if output := ctxts[i].aclass(&tst.arg); output != expect[i] {
   550  				t.Errorf("%s.aclass(%v) = %v, expected %v\n", name[i], tst.arg, DRconv(output), DRconv(expect[i]))
   551  			}
   552  		}
   553  	}
   554  }
   555  
   556  // The optab size should remain constant when reinitializing the PPC64 assembler backend.
   557  func TestOptabReinit(t *testing.T) {
   558  	buildcfg.GOOS = "linux"
   559  	buildcfg.GOARCH = "ppc64le"
   560  	buildcfg.GOPPC64 = 8
   561  	buildop(nil)
   562  	optabLen := len(optab)
   563  	buildcfg.GOPPC64 = 9
   564  	buildop(nil)
   565  	reinitOptabLen := len(optab)
   566  	if reinitOptabLen != optabLen {
   567  		t.Errorf("rerunning buildop changes optab size from %d to %d", optabLen, reinitOptabLen)
   568  	}
   569  }