• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016 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 arm64
6
7import (
8	"bytes"
9	"fmt"
10	"internal/testenv"
11	"os"
12	"path/filepath"
13	"regexp"
14	"testing"
15)
16
17func runAssembler(t *testing.T, srcdata string) []byte {
18	dir := t.TempDir()
19	defer os.RemoveAll(dir)
20	srcfile := filepath.Join(dir, "testdata.s")
21	outfile := filepath.Join(dir, "testdata.o")
22	os.WriteFile(srcfile, []byte(srcdata), 0644)
23	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "asm", "-S", "-o", outfile, srcfile)
24	cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=arm64")
25	out, err := cmd.CombinedOutput()
26	if err != nil {
27		t.Errorf("The build failed: %v, output:\n%s", err, out)
28	}
29	return out
30}
31
32func TestSplitImm24uScaled(t *testing.T) {
33	tests := []struct {
34		v       int32
35		shift   int
36		wantErr bool
37		wantHi  int32
38		wantLo  int32
39	}{
40		{
41			v:      0,
42			shift:  0,
43			wantHi: 0,
44			wantLo: 0,
45		},
46		{
47			v:      0x1001,
48			shift:  0,
49			wantHi: 0x1000,
50			wantLo: 0x1,
51		},
52		{
53			v:      0xffffff,
54			shift:  0,
55			wantHi: 0xfff000,
56			wantLo: 0xfff,
57		},
58		{
59			v:       0xffffff,
60			shift:   1,
61			wantErr: true,
62		},
63		{
64			v:      0xfe,
65			shift:  1,
66			wantHi: 0x0,
67			wantLo: 0x7f,
68		},
69		{
70			v:      0x10fe,
71			shift:  1,
72			wantHi: 0x0,
73			wantLo: 0x87f,
74		},
75		{
76			v:      0x2002,
77			shift:  1,
78			wantHi: 0x2000,
79			wantLo: 0x1,
80		},
81		{
82			v:      0xfffffe,
83			shift:  1,
84			wantHi: 0xffe000,
85			wantLo: 0xfff,
86		},
87		{
88			v:      0x1000ffe,
89			shift:  1,
90			wantHi: 0xfff000,
91			wantLo: 0xfff,
92		},
93		{
94			v:       0x1001000,
95			shift:   1,
96			wantErr: true,
97		},
98		{
99			v:       0xfffffe,
100			shift:   2,
101			wantErr: true,
102		},
103		{
104			v:      0x4004,
105			shift:  2,
106			wantHi: 0x4000,
107			wantLo: 0x1,
108		},
109		{
110			v:      0xfffffc,
111			shift:  2,
112			wantHi: 0xffc000,
113			wantLo: 0xfff,
114		},
115		{
116			v:      0x1002ffc,
117			shift:  2,
118			wantHi: 0xfff000,
119			wantLo: 0xfff,
120		},
121		{
122			v:       0x1003000,
123			shift:   2,
124			wantErr: true,
125		},
126		{
127			v:       0xfffffe,
128			shift:   3,
129			wantErr: true,
130		},
131		{
132			v:      0x8008,
133			shift:  3,
134			wantHi: 0x8000,
135			wantLo: 0x1,
136		},
137		{
138			v:      0xfffff8,
139			shift:  3,
140			wantHi: 0xff8000,
141			wantLo: 0xfff,
142		},
143		{
144			v:      0x1006ff8,
145			shift:  3,
146			wantHi: 0xfff000,
147			wantLo: 0xfff,
148		},
149		{
150			v:       0x1007000,
151			shift:   3,
152			wantErr: true,
153		},
154	}
155	for _, test := range tests {
156		hi, lo, err := splitImm24uScaled(test.v, test.shift)
157		switch {
158		case err == nil && test.wantErr:
159			t.Errorf("splitImm24uScaled(%v, %v) succeeded, want error", test.v, test.shift)
160		case err != nil && !test.wantErr:
161			t.Errorf("splitImm24uScaled(%v, %v) failed: %v", test.v, test.shift, err)
162		case !test.wantErr:
163			if got, want := hi, test.wantHi; got != want {
164				t.Errorf("splitImm24uScaled(%x, %x) - got hi %x, want %x", test.v, test.shift, got, want)
165			}
166			if got, want := lo, test.wantLo; got != want {
167				t.Errorf("splitImm24uScaled(%x, %x) - got lo %x, want %x", test.v, test.shift, got, want)
168			}
169		}
170	}
171	for shift := 0; shift <= 3; shift++ {
172		for v := int32(0); v < 0xfff000+0xfff<<shift; v = v + 1<<shift {
173			hi, lo, err := splitImm24uScaled(v, shift)
174			if err != nil {
175				t.Fatalf("splitImm24uScaled(%x, %x) failed: %v", v, shift, err)
176			}
177			if hi+lo<<shift != v {
178				t.Fatalf("splitImm24uScaled(%x, %x) = (%x, %x) is incorrect", v, shift, hi, lo)
179			}
180		}
181	}
182}
183
184// TestLarge generates a very large file to verify that large
185// program builds successfully, in particular, too-far
186// conditional branches are fixed, and also verify that the
187// instruction's pc can be correctly aligned even when branches
188// need to be fixed.
189func TestLarge(t *testing.T) {
190	if testing.Short() {
191		t.Skip("Skip in short mode")
192	}
193	testenv.MustHaveGoBuild(t)
194
195	// generate a very large function
196	buf := bytes.NewBuffer(make([]byte, 0, 7000000))
197	fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
198	fmt.Fprintln(buf, "TBZ $5, R0, label")
199	fmt.Fprintln(buf, "CBZ R0, label")
200	fmt.Fprintln(buf, "BEQ label")
201	fmt.Fprintln(buf, "PCALIGN $128")
202	fmt.Fprintln(buf, "MOVD $3, R3")
203	for i := 0; i < 1<<19; i++ {
204		fmt.Fprintln(buf, "MOVD R0, R1")
205	}
206	fmt.Fprintln(buf, "label:")
207	fmt.Fprintln(buf, "RET")
208
209	// assemble generated file
210	out := runAssembler(t, buf.String())
211
212	pattern := `0x0080\s00128\s\(.*\)\tMOVD\t\$3,\sR3`
213	matched, err := regexp.MatchString(pattern, string(out))
214
215	if err != nil {
216		t.Fatal(err)
217	}
218	if !matched {
219		t.Errorf("The alignment is not correct: %t\n", matched)
220	}
221}
222
223// Issue 20348.
224func TestNoRet(t *testing.T) {
225	runAssembler(t, "TEXT ·stub(SB),$0-0\nNOP\n")
226}
227
228// TestPCALIGN verifies the correctness of the PCALIGN by checking if the
229// code can be aligned to the alignment value.
230func TestPCALIGN(t *testing.T) {
231	testenv.MustHaveGoBuild(t)
232
233	code1 := "TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $8\nMOVD $1, R1\nRET\n"
234	code2 := "TEXT ·foo(SB),$0-0\nMOVD $0, R0\nPCALIGN $16\nMOVD $2, R2\nRET\n"
235	// If the output contains this pattern, the pc-offset of "MOVD $1, R1" is 8 bytes aligned.
236	out1 := `0x0008\s00008\s\(.*\)\tMOVD\t\$1,\sR1`
237	// If the output contains this pattern, the pc-offset of "MOVD $2, R2" is 16 bytes aligned.
238	out2 := `0x0010\s00016\s\(.*\)\tMOVD\t\$2,\sR2`
239	var testCases = []struct {
240		name string
241		code string
242		out  string
243	}{
244		{"8-byte alignment", code1, out1},
245		{"16-byte alignment", code2, out2},
246	}
247
248	for _, test := range testCases {
249		out := runAssembler(t, test.code)
250		matched, err := regexp.MatchString(test.out, string(out))
251		if err != nil {
252			t.Fatal(err)
253		}
254		if !matched {
255			t.Errorf("The %s testing failed!\ninput: %s\noutput: %s\n", test.name, test.code, out)
256		}
257	}
258}
259