Source file src/cmd/internal/obj/ppc64/asm_test.go

     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  	"internal/buildcfg"
    11  	"internal/testenv"
    12  	"math"
    13  	"os"
    14  	"path/filepath"
    15  	"regexp"
    16  	"strings"
    17  	"testing"
    18  
    19  	"cmd/internal/obj"
    20  	"cmd/internal/objabi"
    21  )
    22  
    23  var platformEnvs = [][]string{
    24  	{"GOOS=aix", "GOARCH=ppc64"},
    25  	{"GOOS=linux", "GOARCH=ppc64"},
    26  	{"GOOS=linux", "GOARCH=ppc64le"},
    27  }
    28  
    29  const invalidPCAlignSrc = `
    30  TEXT test(SB),0,$0-0
    31  ADD $2, R3
    32  PCALIGN $128
    33  RET
    34  `
    35  
    36  const validPCAlignSrc = `
    37  TEXT test(SB),0,$0-0
    38  ADD $2, R3
    39  PCALIGN $16
    40  MOVD $8, R16
    41  ADD $8, R4
    42  PCALIGN $32
    43  ADD $8, R3
    44  PCALIGN $8
    45  ADD $4, R8
    46  RET
    47  `
    48  
    49  const x64pgm = `
    50  TEXT test(SB),0,$0-0
    51  OR R0, R0
    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  PNOP
    67  `
    68  const x32pgm = `
    69  TEXT test(SB),0,$0-0
    70  OR R0, R0
    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  PNOP
    78  OR R0, R0
    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  `
    87  
    88  const x16pgm = `
    89  TEXT test(SB),0,$0-0
    90  OR R0, R0
    91  OR R0, R0
    92  OR R0, R0
    93  PNOP
    94  OR R0, R0
    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  `
   107  
   108  const x0pgm = `
   109  TEXT test(SB),0,$0-0
   110  OR R0, R0
   111  OR R0, R0
   112  OR R0, R0
   113  OR R0, R0
   114  PNOP
   115  OR R0, R0
   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  `
   127  const x64pgmA64 = `
   128  TEXT test(SB),0,$0-0
   129  OR R0, R0
   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  PNOP
   137  OR R0, R0
   138  OR R0, R0
   139  OR R0, R0
   140  OR R0, R0
   141  OR R0, R0
   142  OR R0, R0
   143  PNOP
   144  `
   145  
   146  const x64pgmA32 = `
   147  TEXT test(SB),0,$0-0
   148  OR R0, R0
   149  OR R0, R0
   150  OR R0, R0
   151  PNOP
   152  OR R0, R0
   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  PNOP
   163  `
   164  
   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)
   169  
   170  	dir, err := os.MkdirTemp("", "testpfxalign")
   171  	if err != nil {
   172  		t.Fatalf("could not create directory: %v", err)
   173  	}
   174  	defer os.RemoveAll(dir)
   175  
   176  	pgms := []struct {
   177  		text   []byte
   178  		align  string
   179  		hasNop bool
   180  	}{
   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
   187  	}
   188  
   189  	for _, pgm := range pgms {
   190  		tmpfile := filepath.Join(dir, "x.s")
   191  		err = os.WriteFile(tmpfile, pgm.text, 0644)
   192  		if err != nil {
   193  			t.Fatalf("can't write output: %v\n", err)
   194  		}
   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()
   198  		if err != nil {
   199  			t.Errorf("Failed to compile %v: %v\n", pgm, err)
   200  		}
   201  		if !strings.Contains(string(out), pgm.align) {
   202  			t.Errorf("Fatal, misaligned text with prefixed instructions:\n%s", out)
   203  		}
   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)
   207  		}
   208  	}
   209  }
   210  
   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) {
   215  	if testing.Short() {
   216  		t.Skip("Skip in short mode")
   217  	}
   218  	testenv.MustHaveGoBuild(t)
   219  
   220  	dir, err := os.MkdirTemp("", "testlarge")
   221  	if err != nil {
   222  		t.Fatalf("could not create directory: %v", err)
   223  	}
   224  	defer os.RemoveAll(dir)
   225  
   226  	// A few interesting test cases for long conditional branch fixups
   227  	tests := []struct {
   228  		jmpinsn     string
   229  		backpattern []string
   230  		fwdpattern  []string
   231  	}{
   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.
   234  		//
   235  		// beq <-> bne conversion (insert one jump)
   236  		{"BEQ",
   237  			[]string{``,
   238  				`0x20030 131120\s\(.*\)\tBC\t\$4,\sCR0EQ,\s131128`,
   239  				`0x20034 131124\s\(.*\)\tJMP\t0`},
   240  			[]string{``,
   241  				`0x0000 00000\s\(.*\)\tBC\t\$4,\sCR0EQ,\s8`,
   242  				`0x0004 00004\s\(.*\)\tJMP\t131128`},
   243  		},
   244  		{"BNE",
   245  			[]string{``,
   246  				`0x20030 131120\s\(.*\)\tBC\t\$12,\sCR0EQ,\s131128`,
   247  				`0x20034 131124\s\(.*\)\tJMP\t0`},
   248  			[]string{``,
   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)
   252  		{"BC 16,0,",
   253  			[]string{``,
   254  				`0x20030 131120\s\(.*\)\tBC\t\$18,\sCR0LT,\s131128`,
   255  				`0x20034 131124\s\(.*\)\tJMP\t0`},
   256  			[]string{``,
   257  				`0x0000 00000\s\(.*\)\tBC\t\$18,\sCR0LT,\s8`,
   258  				`0x0004 00004\s\(.*\)\tJMP\t131128`}},
   259  		{"BC 18,0,",
   260  			[]string{``,
   261  				`0x20030 131120\s\(.*\)\tBC\t\$16,\sCR0LT,\s131128`,
   262  				`0x20034 131124\s\(.*\)\tJMP\t0`},
   263  			[]string{``,
   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)
   267  		{"BC 8,0,",
   268  			[]string{``,
   269  				`0x20034 131124\s\(.*\)\tBC\t\$8,\sCR0LT,\s131132`,
   270  				`0x20038 131128\s\(.*\)\tJMP\t131136`,
   271  				`0x2003c 131132\s\(.*\)\tJMP\t0\n`},
   272  			[]string{``,
   273  				`0x0000 00000\s\(.*\)\tBC\t\$8,\sCR0LT,\s8`,
   274  				`0x0004 00004\s\(.*\)\tJMP\t12`,
   275  				`0x0008 00008\s\(.*\)\tJMP\t131136\n`}},
   276  	}
   277  
   278  	for _, test := range tests {
   279  		// generate a very large function
   280  		buf := bytes.NewBuffer(make([]byte, 0, 7000000))
   281  		gen(buf, test.jmpinsn)
   282  
   283  		tmpfile := filepath.Join(dir, "x.s")
   284  		err = os.WriteFile(tmpfile, buf.Bytes(), 0644)
   285  		if err != nil {
   286  			t.Fatalf("can't write output: %v\n", err)
   287  		}
   288  
   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()
   294  			if err != nil {
   295  				t.Errorf("Assemble failed (%v): %v, output: %s", platenv, err, out)
   296  			}
   297  			matched, err := regexp.MatchString(strings.Join(test.fwdpattern, "\n\t*"), string(out))
   298  			if err != nil {
   299  				t.Fatal(err)
   300  			}
   301  			if !matched {
   302  				t.Errorf("Failed to detect long forward BC fixup in (%v):%s\n", platenv, out)
   303  			}
   304  			matched, err = regexp.MatchString(strings.Join(test.backpattern, "\n\t*"), string(out))
   305  			if err != nil {
   306  				t.Fatal(err)
   307  			}
   308  			if !matched {
   309  				t.Errorf("Failed to detect long backward BC fixup in (%v):%s\n", platenv, out)
   310  			}
   311  		}
   312  	}
   313  }
   314  
   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")
   322  	}
   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")
   327  }
   328  
   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`
   336  
   337  	testenv.MustHaveGoBuild(t)
   338  
   339  	dir, err := os.MkdirTemp("", "testpcalign")
   340  	if err != nil {
   341  		t.Fatalf("could not create directory: %v", err)
   342  	}
   343  	defer os.RemoveAll(dir)
   344  
   345  	// generate a test with valid uses of PCALIGN
   346  
   347  	tmpfile := filepath.Join(dir, "x.s")
   348  	err = os.WriteFile(tmpfile, []byte(validPCAlignSrc), 0644)
   349  	if err != nil {
   350  		t.Fatalf("can't write output: %v\n", err)
   351  	}
   352  
   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()
   357  	if err != nil {
   358  		t.Errorf("Build failed: %v, output: %s", err, out)
   359  	}
   360  
   361  	matched, err := regexp.MatchString(pattern8, string(out))
   362  	if err != nil {
   363  		t.Fatal(err)
   364  	}
   365  	if !matched {
   366  		t.Errorf("The 8 byte alignment is not correct: %t, output:%s\n", matched, out)
   367  	}
   368  
   369  	matched, err = regexp.MatchString(pattern16, string(out))
   370  	if err != nil {
   371  		t.Fatal(err)
   372  	}
   373  	if !matched {
   374  		t.Errorf("The 16 byte alignment is not correct: %t, output:%s\n", matched, out)
   375  	}
   376  
   377  	matched, err = regexp.MatchString(pattern32, string(out))
   378  	if err != nil {
   379  		t.Fatal(err)
   380  	}
   381  	if !matched {
   382  		t.Errorf("The 32 byte alignment is not correct: %t, output:%s\n", matched, out)
   383  	}
   384  
   385  	// generate a test with invalid use of PCALIGN
   386  
   387  	tmpfile = filepath.Join(dir, "xi.s")
   388  	err = os.WriteFile(tmpfile, []byte(invalidPCAlignSrc), 0644)
   389  	if err != nil {
   390  		t.Fatalf("can't write output: %v\n", err)
   391  	}
   392  
   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")
   399  	}
   400  }
   401  
   402  // Verify register constants are correctly aligned. Much of the ppc64 assembler assumes masking out significant
   403  // bits will produce a valid register number:
   404  // REG_Rx & 31 == x
   405  // REG_Fx & 31 == x
   406  // REG_Vx & 31 == x
   407  // REG_VSx & 63 == x
   408  // REG_SPRx & 1023 == x
   409  // REG_CRx & 7 == x
   410  //
   411  // VR and FPR disjointly overlap VSR, interpreting as VSR registers should produce the correctly overlapped VSR.
   412  // REG_FPx & 63 == x
   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++ {
   417  			if i&msk != rout {
   418  				t.Errorf("%v is not aligned to 0x%X (expected %d, got %d)\n", rconv(i), msk, rout, rstart&msk)
   419  			}
   420  			rout++
   421  		}
   422  	}
   423  	var testType = []struct {
   424  		rstart int
   425  		rend   int
   426  		msk    int
   427  		rout   int
   428  	}{
   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},
   438  	}
   439  	for _, t := range testType {
   440  		tstFunc(t.rstart, t.rend, t.msk, t.rout)
   441  	}
   442  }
   443  
   444  // Verify interesting obj.Addr arguments are classified correctly.
   445  func TestAddrClassifier(t *testing.T) {
   446  	type cmplx struct {
   447  		pic     int
   448  		pic_dyn int
   449  		dyn     int
   450  		nonpic  int
   451  	}
   452  	tsts := [...]struct {
   453  		arg    obj.Addr
   454  		output interface{}
   455  	}{
   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 + 8}, C_LR},
   469  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 9}, C_CTR},
   470  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_FPSCR}, C_FPSCR},
   471  		{obj.Addr{Type: obj.TYPE_REG, Reg: REG_A1}, C_AREG},
   472  
   473  		// Memory type arguments.
   474  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_GOTREF}, C_ADDR},
   475  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_TOCREF}, C_ADDR},
   476  		{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}},
   477  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_ADDR},
   478  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO}, C_SOREG},
   479  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: BIG}, C_LOREG},
   480  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LOREG},
   481  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM}, C_SOREG},
   482  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: BIG}, C_LOREG},
   483  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LOREG}, // 33 is FixedFrameSize-1
   484  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE}, C_ZOREG},
   485  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Index: REG_R4}, C_XOREG},
   486  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: 1}, C_SOREG},
   487  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: BIG}, C_LOREG},
   488  		{obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: -BIG - 33}, C_LOREG},
   489  
   490  		// Misc (golang initializes -0.0 to 0.0, hence the obfuscation below)
   491  		{obj.Addr{Type: obj.TYPE_TEXTSIZE}, C_TEXTSIZE},
   492  		{obj.Addr{Type: obj.TYPE_FCONST, Val: 0.0}, C_ZCON},
   493  		{obj.Addr{Type: obj.TYPE_FCONST, Val: math.Float64frombits(0x8000000000000000)}, C_S16CON},
   494  
   495  		// Address type arguments
   496  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1}, C_SACON},
   497  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: BIG}, C_LACON},
   498  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: -BIG - 1}, C_LACON},
   499  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1 << 32}, C_DACON},
   500  		{obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
   501  		{obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_STATIC, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
   502  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: 1}, C_SACON},
   503  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: BIG}, C_LACON},
   504  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LACON},
   505  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: 1}, C_SACON},
   506  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: BIG}, C_LACON},
   507  		{obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LACON}, // 33 is FixedFrameSize-1
   508  
   509  		// Constant type arguments
   510  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 0}, C_ZCON},
   511  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1}, C_U1CON},
   512  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 2}, C_U2CON},
   513  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 4}, C_U3CON},
   514  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 8}, C_U4CON},
   515  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 16}, C_U5CON},
   516  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 32}, C_U8CON},
   517  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 14}, C_U15CON},
   518  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 15}, C_U16CON},
   519  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 + 1<<16}, C_U31CON},
   520  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 31}, 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: 0x10001}, C_U31CON},
   526  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 33)}, C_S34CON},
   527  		{obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 34)}, C_64CON},
   528  
   529  		// Branch like arguments
   530  		{obj.Addr{Type: obj.TYPE_BRANCH, Sym: &obj.LSym{Type: objabi.SDATA}}, cmplx{C_BRA, C_BRAPIC, C_BRAPIC, C_BRA}},
   531  		{obj.Addr{Type: obj.TYPE_BRANCH}, C_BRA},
   532  	}
   533  
   534  	pic_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Arch: &Linkppc64}, autosize: 0}
   535  	pic_dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
   536  	dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
   537  	nonpic_ctxt9 := ctxt9{ctxt: &obj.Link{Arch: &Linkppc64}, autosize: 0}
   538  	ctxts := [...]*ctxt9{&pic_ctxt9, &pic_dyn_ctxt9, &dyn_ctxt9, &nonpic_ctxt9}
   539  	name := [...]string{"pic", "pic_dyn", "dyn", "nonpic"}
   540  	for _, tst := range tsts {
   541  		var expect []int
   542  		switch tst.output.(type) {
   543  		case cmplx:
   544  			v := tst.output.(cmplx)
   545  			expect = []int{v.pic, v.pic_dyn, v.dyn, v.nonpic}
   546  		case int:
   547  			expect = []int{tst.output.(int), tst.output.(int), tst.output.(int), tst.output.(int)}
   548  		}
   549  		for i := range ctxts {
   550  			if output := ctxts[i].aclass(&tst.arg); output != expect[i] {
   551  				t.Errorf("%s.aclass(%v) = %v, expected %v\n", name[i], tst.arg, DRconv(output), DRconv(expect[i]))
   552  			}
   553  		}
   554  	}
   555  }
   556  
   557  // The optab size should remain constant when reinitializing the PPC64 assembler backend.
   558  func TestOptabReinit(t *testing.T) {
   559  	buildcfg.GOOS = "linux"
   560  	buildcfg.GOARCH = "ppc64le"
   561  	buildcfg.GOPPC64 = 8
   562  	buildop(nil)
   563  	optabLen := len(optab)
   564  	buildcfg.GOPPC64 = 9
   565  	buildop(nil)
   566  	reinitOptabLen := len(optab)
   567  	if reinitOptabLen != optabLen {
   568  		t.Errorf("rerunning buildop changes optab size from %d to %d", optabLen, reinitOptabLen)
   569  	}
   570  }
   571  

View as plain text