182 lines
6.9 KiB
Go
182 lines
6.9 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
||
package arch
|
||
|
||
import "fmt"
|
||
|
||
func buildARM64() *Table {
|
||
return newTable(ARM64, arm64Registers(), relaxCounts(mergedInstrs(arm64Summaries(), commonGeneratedInstrs, arm64GeneratedInstrs, arm64Aliases())))
|
||
}
|
||
|
||
// arm64Aliases are the branch/jump spellings the assembler front-end accepts in
|
||
// addition to the generated opcode table (notably the unconditional B and BL).
|
||
func arm64Aliases() []string {
|
||
return []string{
|
||
"B", "BL", "BCS", "BHS", "BCC", "BLO", "BMI", "BPL", "BVS", "BVC",
|
||
"BHI", "BLS", "CBZW", "CBNZW", "ADR", "ADRP",
|
||
}
|
||
}
|
||
|
||
// arm64Summaries returns the curated documentation/operand-count table keyed by
|
||
// upper-case mnemonic; it enriches the complete generated name list.
|
||
func arm64Summaries() map[string]Instr { return toMap(arm64Curated()) }
|
||
|
||
// arm64Registers builds the arm64 (AArch64) register file.
|
||
func arm64Registers() []Register {
|
||
var regs []Register
|
||
add := func(name string, class RegClass, desc string) {
|
||
regs = append(regs, Register{Name: name, Class: class, Desc: desc})
|
||
}
|
||
|
||
// General-purpose integer registers R0–R30.
|
||
for i := 0; i <= 30; i++ {
|
||
add(fmt.Sprintf("R%d", i), GPR, "64-bit general-purpose register")
|
||
}
|
||
add("ZR", Special, "zero register (reads as 0)")
|
||
add("SP", Special, "stack pointer")
|
||
add("LR", Special, "link register (alias of R30)")
|
||
add("PC", Special, "program counter")
|
||
add("RSP", Special, "stack pointer (alias)")
|
||
|
||
// Floating-point / SIMD registers: F (scalar FP) and V (vector).
|
||
for i := 0; i <= 31; i++ {
|
||
add(fmt.Sprintf("F%d", i), Float, "floating-point register")
|
||
add(fmt.Sprintf("V%d", i), VecARM, "128-bit SIMD/vector register")
|
||
}
|
||
return regs
|
||
}
|
||
|
||
// arm64Curated returns the hand-written subset of arm64 (AArch64) instructions
|
||
// that carry a summary and/or an operand-count range. 32-bit operations carry
|
||
// a W suffix. The authoritative, complete set is arm64GeneratedInstrs.
|
||
func arm64Curated() []Instr {
|
||
var t []Instr
|
||
|
||
// Data movement (loads and stores are MOVx with a memory operand).
|
||
for _, m := range []string{
|
||
"MOVB", "MOVBU", "MOVH", "MOVHU", "MOVW", "MOVWU", "MOVD",
|
||
"FMOVS", "FMOVD",
|
||
} {
|
||
t = append(t, ic(m, "Move / load / store", 2, 2))
|
||
}
|
||
for _, m := range []string{"MOVK", "MOVN", "MOVZ", "MOVKW", "MOVNW", "MOVZW"} {
|
||
t = append(t, i(m, "Move wide constant"))
|
||
}
|
||
for _, m := range []string{"ADR", "ADRP"} {
|
||
t = append(t, ic(m, "Address of label/page", 2, 2))
|
||
}
|
||
|
||
// Integer arithmetic and logic (64-bit and W 32-bit forms).
|
||
for _, op := range []string{"ADD", "ADDS", "SUB", "SUBS", "AND", "ANDS", "ORR", "ORN", "EOR", "EON", "BIC", "BICS", "ADC", "ADCS", "SBC", "SBCS"} {
|
||
t = append(t, i(op, op+" (64-bit)"))
|
||
t = append(t, i(op+"W", op+" (32-bit)"))
|
||
}
|
||
for _, op := range []string{"NEG", "NGC", "MVN"} {
|
||
t = append(t, i(op, op+" (64-bit)"))
|
||
t = append(t, i(op+"W", op+" (32-bit)"))
|
||
}
|
||
for _, op := range []string{"MUL", "MNEG", "SMULL", "UMULL", "SMULH", "UMULH", "MADD", "MSUB", "SMADDL", "UMADDL", "SMSUBL", "UMSUBL"} {
|
||
t = append(t, i(op, "Multiply / multiply-accumulate"))
|
||
}
|
||
for _, op := range []string{"UDIV", "SDIV", "UDIVW", "SDIVW"} {
|
||
t = append(t, ic(op, "Divide", 3, 3))
|
||
}
|
||
|
||
// Shifts, rotates and bit manipulation.
|
||
for _, op := range []string{"LSL", "LSR", "ASR", "ROR"} {
|
||
t = append(t, i(op, op+" shift"))
|
||
t = append(t, i(op+"W", op+" shift (32-bit)"))
|
||
}
|
||
for _, op := range []string{"LSLV", "LSRV", "ASRV", "RORV", "LSLVW", "LSRVW", "ASRVW", "RORVW"} {
|
||
t = append(t, i(op, "Variable shift"))
|
||
}
|
||
for _, op := range []string{"RBIT", "REV", "REV16", "REV32", "REV64", "CLZ", "CLS", "RBITW", "REVW", "CLZW", "CLSW"} {
|
||
t = append(t, ic(op, "Bit manipulation", 2, 2))
|
||
}
|
||
for _, op := range []string{"UBFX", "SBFX", "UBFM", "SBFM", "BFXIL", "EXTR"} {
|
||
t = append(t, i(op, "Bitfield extract"))
|
||
}
|
||
|
||
// Compare and test.
|
||
for _, op := range []string{"CMP", "CMN", "TST"} {
|
||
t = append(t, i(op, op+" (64-bit)"))
|
||
t = append(t, i(op+"W", op+" (32-bit)"))
|
||
}
|
||
|
||
// Conditional select.
|
||
for _, op := range []string{"CSEL", "CSINC", "CSINV", "CSNEG", "CSET", "CSETM", "CINC", "CINV", "CNEG"} {
|
||
t = append(t, i(op, "Conditional select"))
|
||
t = append(t, i(op+"W", "Conditional select (32-bit)"))
|
||
}
|
||
for _, op := range []string{"CCMP", "CCMN", "CCMPW", "CCMNW"} {
|
||
t = append(t, i(op, "Conditional compare"))
|
||
}
|
||
|
||
// Control flow.
|
||
t = append(t, ic("B", "Unconditional branch", 1, 1))
|
||
t = append(t, ic("BL", "Branch with link", 1, 1))
|
||
for _, cc := range []string{
|
||
"EQ", "NE", "CS", "HS", "CC", "LO", "MI", "PL", "VS", "VC",
|
||
"HI", "LS", "GE", "LT", "GT", "LE", "AL", "NV",
|
||
} {
|
||
t = append(t, ic("B"+cc, "Conditional branch", 1, 1))
|
||
}
|
||
for _, op := range []string{"CBZ", "CBNZ", "TBZ", "TBNZ"} {
|
||
t = append(t, i(op, "Compare/test and branch"))
|
||
t = append(t, i(op+"W", "Compare/test and branch (32-bit)"))
|
||
}
|
||
t = append(t, ic("RET", "Return", 0, 1))
|
||
t = append(t, ic("BR", "Branch to register", 1, 1))
|
||
t = append(t, ic("BLR", "Branch with link to register", 1, 1))
|
||
t = append(t, ic("NOP", "No operation", 0, 1))
|
||
t = append(t, ic("BRK", "Breakpoint", 0, 1))
|
||
for _, op := range []string{"SVC", "HVC", "SMC"} {
|
||
t = append(t, i(op, "Exception generation"))
|
||
}
|
||
for _, op := range []string{"DMB", "DSB", "ISB"} {
|
||
t = append(t, i(op, "Barrier"))
|
||
}
|
||
for _, op := range []string{"MRS", "MSR"} {
|
||
t = append(t, ic(op, "System register access", 2, 2))
|
||
}
|
||
|
||
// Atomics (LSE and load-exclusive/store-exclusive).
|
||
for _, op := range []string{
|
||
"LDAXR", "LDAXRB", "LDAXRH", "LDAXRW", "STXR", "STXRB", "STXRH", "STXRW",
|
||
"LDAR", "LDARB", "LDARH", "LDARW", "STLR", "STLRB", "STLRH", "STLRW",
|
||
"LDADD", "LDCLR", "LDEOR", "LDSET", "SWP", "CAS", "CASAL", "CASL", "CASAL",
|
||
} {
|
||
t = append(t, i(op, "Atomic memory operation"))
|
||
}
|
||
|
||
// Floating-point scalar.
|
||
for _, op := range []string{
|
||
"FADD", "FSUB", "FMUL", "FDIV", "FNEG", "FABS", "FSQRT", "FMIN", "FMAX",
|
||
"FMADD", "FMSUB", "FNMADD", "FNMSUB", "FCMP", "FCMPE",
|
||
"FCVT", "FCVTZS", "FCVTZU", "FCVTNS", "FCVTNU", "FCVTAS", "FCVTAU",
|
||
"SCVTF", "UCVTF", "FRINTM", "FRINTN", "FRINTP", "FRINTZ",
|
||
} {
|
||
t = append(t, i(op, "Floating-point operation"))
|
||
}
|
||
t = append(t, i("FMOV", "Floating-point move"))
|
||
|
||
// NEON / SIMD vector (arrangement carried by the operand suffix).
|
||
for _, op := range []string{
|
||
"VADD", "VSUB", "VMUL", "VMLA", "VMLS", "VNEG", "VABS", "VMIN", "VMAX",
|
||
"VAND", "VORR", "VEOR", "VBIC", "VBIF", "VBSL", "VNOT",
|
||
"VDUP", "VMOV", "VMOVI", "VMOVQ",
|
||
"VLD1", "VLD2", "VLD3", "VLD4", "VST1", "VST2", "VST3", "VST4",
|
||
"VCNT", "VREV16", "VREV32", "VREV64", "VUZP1", "VUZP2", "VZIP1", "VZIP2", "VTRN1", "VTRN2",
|
||
"VSHL", "VSHR", "VSSHLL", "VUSHR", "VEXT", "VTBL", "VTBX",
|
||
"VADDV", "VUMAXV", "VUMINV", "VSMAXV", "VSMINV",
|
||
"VFADD", "VFSUB", "VFMUL", "VFDIV", "VFNEG", "VFABS", "VFMIN", "VFMAX",
|
||
"VFMLA", "VFMLS", "VFCVT", "VSCVTF", "VUCVTF", "VFCMEQ", "VFCMGT", "VFCMLT",
|
||
"VCMPEQ", "VCMPGT", "VCMPGE", "VSHLL",
|
||
} {
|
||
t = append(t, i(op, "NEON SIMD vector operation"))
|
||
}
|
||
|
||
return t
|
||
}
|