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