// Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package arm64 import ( "flag" "fmt" "os" "reflect" "regexp" "sort" "testing" ) var arm64Path = flag.String("arm64Path", "", "Path to ARM64 XML definitions") func requireEqual(t *testing.T, expected, actual interface{}) bool { t.Helper() if !reflect.DeepEqual(expected, actual) { t.Errorf("❌ expected %v, got %v", expected, actual) return false } if expected != nil { switch reflect.TypeOf(expected).Kind() { case reflect.Slice, reflect.Array: t.Logf("✅ %v", expected) } } return true } func matchEqual(t *testing.T, expected, actual interface{}) bool { t.Helper() eq := reflect.DeepEqual(expected, actual) if eq && expected != nil { switch reflect.TypeOf(expected).Kind() { case reflect.Slice, reflect.Array: t.Logf("✅ %v", expected) } } return eq } func arngs(t *testing.T, instr *Instruction, expectedArrangements []string, expectedShape ArngShape) { t.Helper() actualArrangements, actualShape := instr.Arrangements() actualStrings := make([]string, len(actualArrangements)) for i, arr := range actualArrangements { actualStrings[i] = fmt.Sprintf("%s%d:%s", arr.baseType, arr.elemBits, arr.arrangement) } sort.Strings(actualStrings) sort.Strings(expectedArrangements) requireEqual(t, expectedArrangements, actualStrings) requireEqual(t, expectedShape, actualShape) } func ops(t *testing.T, instr *Instruction, expectedOps []string, equal func(*testing.T, interface{}, interface{}) bool) bool { t.Helper() templates := instr.templates() if !equal(t, 1, len(templates)) { return false } template := templates[0] operands := template.operands var actualOps []string for _, operand := range operands { opStr := fmt.Sprintf("%s:%d", operand.Type.String(), operand.AsmPos) actualOps = append(actualOps, opStr) } return equal(t, expectedOps, actualOps) } func matchOps(expectedOps []string) func(*testing.T, *Instruction) bool { return func(t *testing.T, instr *Instruction) bool { return ops(t, instr, expectedOps, matchEqual) } } func requireOps(expectedOps []string) func(*testing.T, *Instruction) bool { return func(t *testing.T, instr *Instruction) bool { return ops(t, instr, expectedOps, requireEqual) } } func requireArngs(expectedArngs []string, expectedShape ArngShape) func(*testing.T, *Instruction) { return func(t *testing.T, instr *Instruction) { arngs(t, instr, expectedArngs, expectedShape) } } func emitsDefs(expectedCount int) func(*testing.T, *Instruction) { return func(t *testing.T, instr *Instruction) { values := instr.EmitAll() requireEqual(t, expectedCount, len(values)) } } var ( // Operand patterns binary = []string{"VReg:0", "VReg:1", "VReg:2"} unary = []string{"VReg:0", "VReg:1"} twoArgsResultInArg0 = []string{"VReg:0", "VReg:0", "VReg:1"} unaryWithImm = []string{"VReg:0", "Imm:2", "VReg:1"} unaryWithImmResultInArg0 = []string{"VReg:0", "Imm:2", "VReg:0", "VReg:1"} binaryWithImm = []string{"VReg:0", "Imm:3", "VReg:1", "VReg:2"} elemToVreg = []string{"VReg:0", "Imm:1", "VReg:1"} insertFromLane = []string{"VReg:0", "Imm:0", "Imm:1", "VReg:0", "VReg:1"} insertFromGReg = []string{"VReg:0", "Imm:0", "VReg:0", "GReg:1"} threeArgsResultInArg0 = []string{"VReg:0", "VReg:0", "VReg:1", "VReg:2"} fourOperands = []string{"VReg:0", "VReg:1", "VReg:2", "VReg:3"} elemToGReg = []string{"GReg:0", "Imm:1", "VReg:1"} // Arrangement patterns floatS32 = []string{"float32:4S"} floating = []string{"float32:2S", "float32:4S", "float64:2D"} bitwise16B = []string{"int8:16B", "uint8:16B"} integer2D = []string{"int64:2D", "uint64:2D"} integerUpTo8Bits = []string{"int8:16B", "int8:8B", "uint8:16B", "uint8:8B"} integerUpTo16Bits = append([]string{"int16:4H", "int16:8H", "uint16:4H", "uint16:8H"}, integerUpTo8Bits...) integerUpTo32Bits = append([]string{"int32:2S", "int32:4S", "uint32:2S", "uint32:4S"}, integerUpTo16Bits...) integerWideOnly = []string{"int16:8H", "int32:4S", "int64:2D", "uint16:8H", "uint32:4S", "uint64:2D"} polynomialArrngs = []string{"int8:8B", "int8:16B", "int64:1D", "int64:2D", "uint8:8B", "uint8:16B", "uint64:1D", "uint64:2D"} integer32And8Bits = append([]string{"int32:2S", "int32:4S", "uint32:2S", "uint32:4S"}, integerUpTo8Bits...) addvArngs = append([]string{"int32:4S", "uint32:4S"}, integerUpTo16Bits...) integer = append([]string{"int64:2D", "uint64:2D"}, integerUpTo32Bits...) integerWith1D = append([]string{"int64:1D", "uint64:1D"}, integer...) allArngs = append(append([]string{}, floating...), integer...) bitwise = []string{ "int8:16B", "uint8:16B", "int8:8B", "uint8:8B", "int16:16B", "uint16:16B", "int16:8B", "uint16:8B", "int32:16B", "uint32:16B", "int32:8B", "uint32:8B", "int64:16B", "uint64:16B", } fullwidth = []string{ "float32:4S", "float64:2D", "int8:16B", "int16:8H", "int32:4S", "int64:2D", "uint8:16B", "uint16:8H", "uint32:4S", "uint64:2D", } ) type Arm64InstructionTestSpec struct { Pattern string // to match instruction mnemonics OpMatch func(*testing.T, *Instruction) bool // returns true if operands match ArngTest func(*testing.T, *Instruction) EmitAllTest func(*testing.T, *Instruction) } var arm64InstructionTests = []Arm64InstructionTestSpec{ {"^((UQ|SQ)?ADD|(UQ|SQ)?SUB)$", requireOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^ADDP$", matchOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^ADDP$", matchOps(unary), requireArngs([]string{"int64:2D", "uint64:2D"}, DefaultArngs), emitsDefs(2)}, {"^FADDP$", matchOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^FADDP$", matchOps(unary), requireArngs([]string{"float32:2S", "float64:2D"}, DefaultArngs), emitsDefs(2)}, {"^SABA$", matchOps(threeArgsResultInArg0), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)}, {"^SABAL$", matchOps(threeArgsResultInArg0), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)}, {"^F(ADD|SUB|DIV)$", requireOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^(AND|ORR|EOR|BIC|ORN)$", matchOps(binary), requireArngs(bitwise, DefaultArngs), emitsDefs(14)}, {"^NOT$", requireOps(unary), requireArngs(bitwise, DefaultArngs), emitsDefs(14)}, {"^CM(GT|GE|EQ)$", matchOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^CM(GT|GE|EQ)$", matchOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^CM(HI|HS|TST)$", requireOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^CM(LT|LE)$", requireOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^FCM(GT|GE|EQ)$", matchOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^FCM(GT|GE|EQ)$", matchOps(unaryWithImm), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^FCM(LT|LE)$", requireOps(unaryWithImm), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^(BIT|BIF|BSL)$", matchOps(threeArgsResultInArg0), requireArngs(bitwise, DefaultArngs), emitsDefs(14)}, {"^BCAX$", matchOps(fourOperands), requireArngs(bitwise16B, DefaultArngs), emitsDefs(2)}, {"^EOR3$", matchOps(fourOperands), requireArngs(bitwise16B, DefaultArngs), emitsDefs(2)}, {"^RAX1$", matchOps(binary), requireArngs(integer2D, DefaultArngs), emitsDefs(2)}, {"^XAR$", matchOps(binaryWithImm), requireArngs(integer2D, DefaultArngs), emitsDefs(2)}, {"^DUP$", matchOps(elemToVreg), requireArngs(allArngs, DefaultArngs), emitsDefs(17)}, {"^INS$", matchOps(insertFromLane), requireArngs(fullwidth, DefaultArngs), emitsDefs(10)}, {"^INS$", matchOps(insertFromGReg), requireArngs(fullwidth, DefaultArngs), emitsDefs(10)}, {"^UMOV$", matchOps(elemToGReg), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^EXT$", requireOps(binaryWithImm), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)}, {"^TBL$", requireOps(binary), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)}, {"^TBX$", requireOps(threeArgsResultInArg0), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)}, {"^REV16$", requireOps(unary), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)}, {"^REV32$", requireOps(unary), requireArngs(integerUpTo16Bits, DefaultArngs), emitsDefs(8)}, {"^REV64$", requireOps(unary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)}, {"^(ZIP[12]|UZP[12]|TRN[12])$", requireOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^(S|U)(MIN|MAX)P?$", requireOps(binary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)}, {"^((S|U)(MIN|MAX)|ADD)V$", requireOps(unary), requireArngs(addvArngs, DefaultArngs), emitsDefs(10)}, {"^F(MIN|MAX)(NM)?$", requireOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^F(MIN|MAX)(NM)?P$", matchOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^F(MIN|MAX)(NM)?V$", requireOps(unary), requireArngs(floatS32, DefaultArngs), emitsDefs(1)}, {"^(SQ)?(ABS|NEG)$", requireOps(unary), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^F(ABS|NEG)$", requireOps(unary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^(S|U)SHL$", requireOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^(S|U)QSHL$", matchOps(binary), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^(S|U)QSHL$", matchOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^SHL$", requireOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^(S|U)SHR$", requireOps(unaryWithImm), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^(S|U)SRA$", requireOps(unaryWithImmResultInArg0), requireArngs(integer, DefaultArngs), emitsDefs(14)}, {"^(S|U)SHLL$", requireOps(unaryWithImm), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)}, {"^SADALP$", matchOps(twoArgsResultInArg0), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)}, {"^((S|U)ADDLP)$", requireOps(unary), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)}, {"^(R?(ADD|SUB)HN)$", requireOps(binary), requireArngs(integerWideOnly, NarrowArngs), emitsDefs(6)}, {"^SHRN$", requireOps(unaryWithImm), requireArngs(integerWideOnly, NarrowArngs), emitsDefs(6)}, {"^(CLZ|CLS)$", requireOps(unary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)}, {"^(CNT|RBIT)$", requireOps(unary), requireArngs(integerUpTo8Bits, DefaultArngs), emitsDefs(4)}, {"^(S|U)R?HADD$", matchOps(binary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)}, {"^F(RINT(N|P|M|Z)?|SQRT)$", requireOps(unary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^FMUL$", matchOps(binary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^F(MLA|MLS)$", matchOps(threeArgsResultInArg0), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^MUL$", matchOps(binary), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)}, {"^((S|U)MULL)$", matchOps(binary), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)}, {"^(MLA|MLS)$", matchOps(threeArgsResultInArg0), requireArngs(integerUpTo32Bits, DefaultArngs), emitsDefs(12)}, {"^((S|U)Q)?XTN$", requireOps(unary), requireArngs(integerWideOnly, NarrowArngs), emitsDefs(6)}, {"^(S|U)XTL$", requireOps(unary), requireArngs(integerUpTo32Bits, LongArngs), emitsDefs(12)}, {"^FCVT[NMPZ](S|U)$", matchOps(unary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^(S|U)CVTF$", matchOps(unary), requireArngs(floating, DefaultArngs), emitsDefs(3)}, {"^(S|U)ADDW$", requireOps(binary), requireArngs(integerWideOnly, WideArngs), emitsDefs(6)}, {"^(S|U)SUBW$", requireOps(binary), requireArngs(integerWideOnly, WideArngs), emitsDefs(6)}, {"^FCVTL$", requireOps(unary), requireArngs([]string{"float32:2S", "float32:4S"}, LongArngs), emitsDefs(2)}, {"^USDOT$", matchOps(threeArgsResultInArg0), requireArngs(integer32And8Bits, UnsupportedArngs), emitsDefs(0)}, {"^PMULL$", matchOps(binary), requireArngs(polynomialArrngs, LongArngs), emitsDefs(8)}, } func TestArm64Instructions(t *testing.T) { if *arm64Path == "" { t.Skip("ARM64 path not specified, use -arm64Path flag") } instructions, err := ParseInstructions(*arm64Path) if err != nil { t.Fatalf("ParseInstructions failed: %v", err) } t.Logf("parsed %d ARM64 instructions", len(instructions)) for _, spec := range arm64InstructionTests { regex, err := regexp.Compile(spec.Pattern) requireEqual(t, error(nil), err) t.Run(spec.Pattern, func(t *testing.T) { var instrCount int var matches []*Instruction for _, instr := range instructions { if regex.MatchString(instr.Mnemonic()) { instrCount++ if spec.OpMatch(t, instr) { matches = append(matches, instr) } } } requireEqual(t, true, len(matches) > 0) t.Logf("🔍 pattern %s: %d instructions, %d matched", spec.Pattern, instrCount, len(matches)) for _, instr := range matches { t.Run(instr.Mnemonic(), func(t *testing.T) { requireEqual(t, "advsimd", instr.InstrClass()) t.Run("Arrangements", func(t *testing.T) { spec.ArngTest(t, instr) }) t.Run("EmitAll", func(t *testing.T) { spec.EmitAllTest(t, instr) }) }) } }) } } func TestMain(m *testing.M) { flag.Parse() os.Exit(m.Run()) }