1
2
3
4
5 package arm64
6
7 import (
8 "flag"
9 "fmt"
10 "os"
11 "reflect"
12 "regexp"
13 "sort"
14 "testing"
15 )
16
17 var arm64Path = flag.String("arm64Path", "", "Path to ARM64 XML definitions")
18
19 func requireEqual(t *testing.T, expected, actual interface{}) bool {
20 t.Helper()
21 if !reflect.DeepEqual(expected, actual) {
22 t.Errorf("❌ expected %v, got %v", expected, actual)
23 return false
24 }
25
26 if expected != nil {
27 switch reflect.TypeOf(expected).Kind() {
28 case reflect.Slice, reflect.Array:
29 t.Logf("✅ %v", expected)
30 }
31 }
32 return true
33 }
34
35 func matchEqual(t *testing.T, expected, actual interface{}) bool {
36 t.Helper()
37 eq := reflect.DeepEqual(expected, actual)
38 if eq && expected != nil {
39 switch reflect.TypeOf(expected).Kind() {
40 case reflect.Slice, reflect.Array:
41 t.Logf("✅ %v", expected)
42 }
43 }
44 return eq
45 }
46
47 func arngs(t *testing.T, instr *Instruction, expectedArrangements []string, expectedShape ArngShape) {
48 t.Helper()
49 actualArrangements, actualShape := instr.Arrangements()
50 actualStrings := make([]string, len(actualArrangements))
51 for i, arr := range actualArrangements {
52 actualStrings[i] = fmt.Sprintf("%s%d:%s", arr.baseType, arr.elemBits, arr.arrangement)
53 }
54 sort.Strings(actualStrings)
55 sort.Strings(expectedArrangements)
56 requireEqual(t, expectedArrangements, actualStrings)
57 requireEqual(t, expectedShape, actualShape)
58 }
59
60 func ops(t *testing.T, instr *Instruction, expectedOps []string, equal func(*testing.T, interface{}, interface{}) bool) bool {
61 t.Helper()
62 templates := instr.templates()
63 if !equal(t, 1, len(templates)) {
64 return false
65 }
66 template := templates[0]
67 operands := template.operands
68 var actualOps []string
69 for _, operand := range operands {
70 opStr := fmt.Sprintf("%s:%d", operand.Type.String(), operand.AsmPos)
71 actualOps = append(actualOps, opStr)
72 }
73 return equal(t, expectedOps, actualOps)
74 }
75
76 func matchOps(expectedOps []string) func(*testing.T, *Instruction) bool {
77 return func(t *testing.T, instr *Instruction) bool {
78 return ops(t, instr, expectedOps, matchEqual)
79 }
80 }
81
82 func requireOps(expectedOps []string) func(*testing.T, *Instruction) bool {
83 return func(t *testing.T, instr *Instruction) bool {
84 return ops(t, instr, expectedOps, requireEqual)
85 }
86 }
87
88 func requireArngs(expectedArngs []string, expectedShape ArngShape) func(*testing.T, *Instruction) {
89 return func(t *testing.T, instr *Instruction) {
90 arngs(t, instr, expectedArngs, expectedShape)
91 }
92 }
93
94 func emitsDefs(expectedCount int) func(*testing.T, *Instruction) {
95 return func(t *testing.T, instr *Instruction) {
96 values := instr.EmitAll()
97 requireEqual(t, expectedCount, len(values))
98 }
99 }
100
101 var (
102
103 binary = []string{"VReg:0", "VReg:1", "VReg:2"}
104 unary = []string{"VReg:0", "VReg:1"}
105 twoArgsResultInArg0 = []string{"VReg:0", "VReg:0", "VReg:1"}
106 unaryWithImm = []string{"VReg:0", "Imm:2", "VReg:1"}
107 unaryWithImmResultInArg0 = []string{"VReg:0", "Imm:2", "VReg:0", "VReg:1"}
108 binaryWithImm = []string{"VReg:0", "Imm:3", "VReg:1", "VReg:2"}
109 elemToVreg = []string{"VReg:0", "Imm:1", "VReg:1"}
110 insertFromLane = []string{"VReg:0", "Imm:0", "Imm:1", "VReg:0", "VReg:1"}
111 insertFromGReg = []string{"VReg:0", "Imm:0", "VReg:0", "GReg:1"}
112 threeArgsResultInArg0 = []string{"VReg:0", "VReg:0", "VReg:1", "VReg:2"}
113 fourOperands = []string{"VReg:0", "VReg:1", "VReg:2", "VReg:3"}
114 elemToGReg = []string{"GReg:0", "Imm:1", "VReg:1"}
115
116
117 floatS32 = []string{"float32:4S"}
118 floating = []string{"float32:2S", "float32:4S", "float64:2D"}
119 bitwise16B = []string{"int8:16B", "uint8:16B"}
120 integer2D = []string{"int64:2D", "uint64:2D"}
121 integerUpTo8Bits = []string{"int8:16B", "int8:8B", "uint8:16B", "uint8:8B"}
122 integerUpTo16Bits = append([]string{"int16:4H", "int16:8H", "uint16:4H", "uint16:8H"}, integerUpTo8Bits...)
123 integerUpTo32Bits = append([]string{"int32:2S", "int32:4S", "uint32:2S", "uint32:4S"}, integerUpTo16Bits...)
124 integerWideOnly = []string{"int16:8H", "int32:4S", "int64:2D", "uint16:8H", "uint32:4S", "uint64:2D"}
125 polynomialArrngs = []string{"int8:8B", "int8:16B", "int64:1D", "int64:2D", "uint8:8B", "uint8:16B", "uint64:1D", "uint64:2D"}
126 integer32And8Bits = append([]string{"int32:2S", "int32:4S", "uint32:2S", "uint32:4S"}, integerUpTo8Bits...)
127 addvArngs = append([]string{"int32:4S", "uint32:4S"}, integerUpTo16Bits...)
128 integer = append([]string{"int64:2D", "uint64:2D"}, integerUpTo32Bits...)
129 integerWith1D = append([]string{"int64:1D", "uint64:1D"}, integer...)
130 allArngs = append(append([]string{}, floating...), integer...)
131 bitwise = []string{
132 "int8:16B", "uint8:16B", "int8:8B", "uint8:8B",
133 "int16:16B", "uint16:16B", "int16:8B", "uint16:8B",
134 "int32:16B", "uint32:16B", "int32:8B", "uint32:8B",
135 "int64:16B", "uint64:16B",
136 }
137 fullwidth = []string{
138 "float32:4S", "float64:2D",
139 "int8:16B", "int16:8H", "int32:4S", "int64:2D",
140 "uint8:16B", "uint16:8H", "uint32:4S", "uint64:2D",
141 }
142 )
143
144 type Arm64InstructionTestSpec struct {
145 Pattern string
146 OpMatch func(*testing.T, *Instruction) bool
147 ArngTest func(*testing.T, *Instruction)
148 EmitAllTest func(*testing.T, *Instruction)
149 }
150
151 var arm64InstructionTests = []Arm64InstructionTestSpec{
152 {"^((UQ|SQ)?ADD|(UQ|SQ)?SUB)$", requireOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)},
153 {"^ADDP$", matchOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)},
154 {"^ADDP$", matchOps(unary), requireArngs([]string{"int64:2D", "uint64:2D"}, DefaultArngs), emitsDefs(2)},
155 {"^FADDP$", matchOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
156 {"^FADDP$", matchOps(unary), requireArngs([]string{"float32:2S", "float64:2D"}, DefaultArngs), emitsDefs(2)},
157 {"^SABA$", matchOps(threeArgsResultInArg0), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)},
158 {"^SABAL$", matchOps(threeArgsResultInArg0), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)},
159 {"^F(ADD|SUB|DIV)$", requireOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
160 {"^(AND|ORR|EOR|BIC|ORN)$", matchOps(binary), requireArngs(bitwise, DefaultArngs), emitsDefs(14)},
161 {"^NOT$", requireOps(unary), requireArngs(bitwise, DefaultArngs), emitsDefs(14)},
162 {"^CM(GT|GE|EQ)$", matchOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)},
163 {"^CM(GT|GE|EQ)$", matchOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)},
164 {"^CM(HI|HS|TST)$", requireOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)},
165 {"^CM(LT|LE)$", requireOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)},
166 {"^FCM(GT|GE|EQ)$", matchOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
167 {"^FCM(GT|GE|EQ)$", matchOps(unaryWithImm), requireArngs(floating, DefaultArngs), emitsDefs(3)},
168 {"^FCM(LT|LE)$", requireOps(unaryWithImm), requireArngs(floating, DefaultArngs), emitsDefs(3)},
169 {"^(BIT|BIF|BSL)$", matchOps(threeArgsResultInArg0), requireArngs(bitwise, DefaultArngs), emitsDefs(14)},
170 {"^BCAX$", matchOps(fourOperands), requireArngs(bitwise16B, DefaultArngs), emitsDefs(2)},
171 {"^EOR3$", matchOps(fourOperands), requireArngs(bitwise16B, DefaultArngs), emitsDefs(2)},
172 {"^RAX1$", matchOps(binary), requireArngs(integer2D, DefaultArngs), emitsDefs(2)},
173 {"^XAR$", matchOps(binaryWithImm), requireArngs(integer2D, DefaultArngs), emitsDefs(2)},
174 {"^DUP$", matchOps(elemToVreg), requireArngs(allArngs, DefaultArngs), emitsDefs(17)},
175 {"^INS$", matchOps(insertFromLane), requireArngs(fullwidth, DefaultArngs), emitsDefs(10)},
176 {"^INS$", matchOps(insertFromGReg), requireArngs(fullwidth, DefaultArngs), emitsDefs(10)},
177 {"^UMOV$", matchOps(elemToGReg), requireArngs(integer, DefaultArngs), emitsDefs(14)},
178 {"^EXT$", requireOps(binaryWithImm), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)},
179 {"^TBL$", requireOps(binary), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)},
180 {"^TBX$", requireOps(threeArgsResultInArg0), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)},
181 {"^REV16$", requireOps(unary), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)},
182 {"^REV32$", requireOps(unary), requireArngs(integerUpTo16Bits, DefaultArngs), emitsDefs(8)},
183 {"^REV64$", requireOps(unary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)},
184 {"^(ZIP[12]|UZP[12]|TRN[12])$", requireOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)},
185 {"^(S|U)(MIN|MAX)P?$", requireOps(binary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)},
186 {"^((S|U)(MIN|MAX)|ADD)V$", requireOps(unary), requireArngs(addvArngs, DefaultArngs), emitsDefs(10)},
187 {"^F(MIN|MAX)(NM)?$", requireOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
188 {"^F(MIN|MAX)(NM)?P$", matchOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
189 {"^F(MIN|MAX)(NM)?V$", requireOps(unary), requireArngs(floatS32, DefaultArngs), emitsDefs(1)},
190 {"^(SQ)?(ABS|NEG)$", requireOps(unary), requireArngs(integer, DefaultArngs), emitsDefs(14)},
191 {"^F(ABS|NEG)$", requireOps(unary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
192 {"^(S|U)SHL$", requireOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)},
193 {"^(S|U)QSHL$", matchOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)},
194 {"^(S|U)QSHL$", matchOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)},
195 {"^SHL$", requireOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)},
196 {"^(S|U)SHR$", requireOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)},
197 {"^(S|U)SRA$", requireOps(unaryWithImmResultInArg0), requireArngs(integer, DefaultArngs), emitsDefs(14)},
198 {"^(S|U)SHLL$", requireOps(unaryWithImm), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)},
199 {"^SADALP$", matchOps(twoArgsResultInArg0), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)},
200 {"^((S|U)ADDLP)$", requireOps(unary), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)},
201 {"^(R?(ADD|SUB)HN)$", requireOps(binary), requireArngs(integerWideOnly, NarrowArngs), emitsDefs(6)},
202 {"^SHRN$", requireOps(unaryWithImm), requireArngs(integerWideOnly, NarrowArngs), emitsDefs(6)},
203 {"^(CLZ|CLS)$", requireOps(unary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)},
204 {"^(CNT|RBIT)$", requireOps(unary), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)},
205 {"^(S|U)R?HADD$", matchOps(binary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)},
206 {"^F(RINT(N|P|M|Z)?|SQRT)$", requireOps(unary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
207 {"^FMUL$", matchOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
208 {"^F(MLA|MLS)$", matchOps(threeArgsResultInArg0), requireArngs(floating, DefaultArngs), emitsDefs(3)},
209 {"^MUL$", matchOps(binary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)},
210 {"^((S|U)MULL)$", matchOps(binary), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)},
211 {"^(MLA|MLS)$", matchOps(threeArgsResultInArg0), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)},
212 {"^((S|U)Q)?XTN$", requireOps(unary), requireArngs(integerWideOnly, NarrowArngs), emitsDefs(6)},
213 {"^(S|U)XTL$", requireOps(unary), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)},
214 {"^FCVT[NMPZ](S|U)$", matchOps(unary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
215 {"^(S|U)CVTF$", matchOps(unary), requireArngs(floating, DefaultArngs), emitsDefs(3)},
216 {"^(S|U)ADDW$", requireOps(binary), requireArngs(integerWideOnly, WideArngs), emitsDefs(6)},
217 {"^(S|U)SUBW$", requireOps(binary), requireArngs(integerWideOnly, WideArngs), emitsDefs(6)},
218 {"^FCVTL$", requireOps(unary), requireArngs([]string{"float32:2S", "float32:4S"}, LongArngs), emitsDefs(2)},
219 {"^USDOT$", matchOps(threeArgsResultInArg0), requireArngs(integer32And8Bits, UnsupportedArngs), emitsDefs(0)},
220 {"^PMULL$", matchOps(binary), requireArngs(polynomialArrngs, LongArngs), emitsDefs(8)},
221 }
222
223 func TestArm64Instructions(t *testing.T) {
224 if *arm64Path == "" {
225 t.Skip("ARM64 path not specified, use -arm64Path flag")
226 }
227
228 instructions, err := ParseInstructions(*arm64Path)
229 if err != nil {
230 t.Fatalf("ParseInstructions failed: %v", err)
231 }
232 t.Logf("parsed %d ARM64 instructions", len(instructions))
233
234 for _, spec := range arm64InstructionTests {
235 regex, err := regexp.Compile(spec.Pattern)
236 requireEqual(t, error(nil), err)
237
238 t.Run(spec.Pattern, func(t *testing.T) {
239 var instrCount int
240 var matches []*Instruction
241
242 for _, instr := range instructions {
243 if regex.MatchString(instr.Mnemonic()) {
244 instrCount++
245 if spec.OpMatch(t, instr) {
246 matches = append(matches, instr)
247 }
248 }
249 }
250 requireEqual(t, true, len(matches) > 0)
251 t.Logf("🔍 pattern %s: %d instructions, %d matched", spec.Pattern, instrCount, len(matches))
252
253 for _, instr := range matches {
254 t.Run(instr.Mnemonic(), func(t *testing.T) {
255 requireEqual(t, "advsimd", instr.InstrClass())
256
257 t.Run("Arrangements", func(t *testing.T) {
258 spec.ArngTest(t, instr)
259 })
260
261 t.Run("EmitAll", func(t *testing.T) {
262 spec.EmitAllTest(t, instr)
263 })
264 })
265 }
266 })
267 }
268 }
269
270 func TestMain(m *testing.M) {
271 flag.Parse()
272 os.Exit(m.Run())
273 }
274
View as plain text