• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5package ppc64
6
7import (
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
23var platformEnvs = [][]string{
24	{"GOOS=aix", "GOARCH=ppc64"},
25	{"GOOS=linux", "GOARCH=ppc64"},
26	{"GOOS=linux", "GOARCH=ppc64le"},
27}
28
29const invalidPCAlignSrc = `
30TEXT test(SB),0,$0-0
31ADD $2, R3
32PCALIGN $128
33RET
34`
35
36const validPCAlignSrc = `
37TEXT test(SB),0,$0-0
38ADD $2, R3
39PCALIGN $16
40MOVD $8, R16
41ADD $8, R4
42PCALIGN $32
43ADD $8, R3
44PCALIGN $8
45ADD $4, R8
46RET
47`
48
49const x64pgm = `
50TEXT test(SB),0,$0-0
51OR R0, R0
52OR R0, R0
53OR R0, R0
54OR R0, R0
55OR R0, R0
56OR R0, R0
57OR R0, R0
58OR R0, R0
59OR R0, R0
60OR R0, R0
61OR R0, R0
62OR R0, R0
63OR R0, R0
64OR R0, R0
65OR R0, R0
66PNOP
67`
68const x32pgm = `
69TEXT test(SB),0,$0-0
70OR R0, R0
71OR R0, R0
72OR R0, R0
73OR R0, R0
74OR R0, R0
75OR R0, R0
76OR R0, R0
77PNOP
78OR R0, R0
79OR R0, R0
80OR R0, R0
81OR R0, R0
82OR R0, R0
83OR R0, R0
84OR R0, R0
85OR R0, R0
86`
87
88const x16pgm = `
89TEXT test(SB),0,$0-0
90OR R0, R0
91OR R0, R0
92OR R0, R0
93PNOP
94OR R0, R0
95OR R0, R0
96OR R0, R0
97OR R0, R0
98OR R0, R0
99OR R0, R0
100OR R0, R0
101OR R0, R0
102OR R0, R0
103OR R0, R0
104OR R0, R0
105OR R0, R0
106`
107
108const x0pgm = `
109TEXT test(SB),0,$0-0
110OR R0, R0
111OR R0, R0
112OR R0, R0
113OR R0, R0
114PNOP
115OR R0, R0
116OR R0, R0
117OR R0, R0
118OR R0, R0
119OR R0, R0
120OR R0, R0
121OR R0, R0
122OR R0, R0
123OR R0, R0
124OR R0, R0
125OR R0, R0
126`
127const x64pgmA64 = `
128TEXT test(SB),0,$0-0
129OR R0, R0
130OR R0, R0
131OR R0, R0
132OR R0, R0
133OR R0, R0
134OR R0, R0
135OR R0, R0
136PNOP
137OR R0, R0
138OR R0, R0
139OR R0, R0
140OR R0, R0
141OR R0, R0
142OR R0, R0
143PNOP
144`
145
146const x64pgmA32 = `
147TEXT test(SB),0,$0-0
148OR R0, R0
149OR R0, R0
150OR R0, R0
151PNOP
152OR R0, R0
153OR R0, R0
154OR R0, R0
155OR R0, R0
156OR R0, R0
157OR R0, R0
158OR R0, R0
159OR R0, R0
160OR R0, R0
161OR R0, R0
162PNOP
163`
164
165// Test that nops are inserted when crossing 64B boundaries, and
166// alignment is adjusted to avoid crossing.
167func 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.
214func 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.
316func 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.
332func 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
414func 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.
445func 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.
558func 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