fix(asm): tighten the arm64 acceptance toward the toolchain
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
82d741514a
commit
8ab99c9b0c
4 files changed
+250
-29
No files matched your search
+129
-6
@@ -890,13 +890,38 @@ func encodeARM64DPSR(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
||||
if rn < 0 || rd < 0 {
|
||||
return nil, fmt.Errorf("invalid register operand in %s", mnem)
|
||||
}
|
||||
// SP rules the toolchain enforces on the logical immediates: a
|
||||
// two-operand spelling with SP as the source is rejected outright
|
||||
// (illegal source register), a flag-setting logical rejects SP as
|
||||
// the destination, and a plain logical to SP takes the fast
|
||||
// bitmask path only inside the addcon band (asm7.go cases 62/13).
|
||||
if !zrDest && len(ops) == 2 && rn == 31 {
|
||||
if name := operandRegName(ops[1]); strings.EqualFold(name, "RSP") {
|
||||
return nil, fmt.Errorf("%s: illegal source register RSP", mnem)
|
||||
}
|
||||
}
|
||||
rspDest := false
|
||||
if len(ops) == 3 {
|
||||
rspDest = strings.EqualFold(operandRegName(ops[2]), "RSP")
|
||||
}
|
||||
sLogical := mnem == "ANDS" || mnem == "ANDSW" || mnem == "BICS" || mnem == "BICSW"
|
||||
if rspDest && sLogical {
|
||||
return nil, fmt.Errorf("%s: illegal combination: the destination cannot be RSP", mnem)
|
||||
}
|
||||
n, immr, imms, ok := a64LogicalImm(v, width)
|
||||
// The toolchain's logical-immediate rows take a real destination
|
||||
// The toolchain.s logical-immediate rows take a real destination
|
||||
// only (omovconst guards the bitmask path with rt != REGZERO): a
|
||||
// non-flag-setting logical to ZR materialises the constant into
|
||||
// REGTMP (R27) and takes the register form. RSP is a real
|
||||
// register here, and the flags-only TST spellings keep the fast
|
||||
// path: ANDS ZR, Rn, #imm is their form.
|
||||
// register here, but keeps the fast bitmask path inside the
|
||||
// addcon band alone, and the flags-only TST spellings keep the
|
||||
// fast path: ANDS ZR, Rn, #imm is their form.
|
||||
if rspDest {
|
||||
inBand := v > 0 && (v <= 0xFFF || (v&0xFFF == 0 && v>>12 <= 0xFFF))
|
||||
if !ok || !inBand {
|
||||
return nil, fmt.Errorf("%s: illegal combination: the destination cannot be RSP", mnem)
|
||||
}
|
||||
}
|
||||
if ok && (isCmp || !zrDest) {
|
||||
opc := (baseOp >> 29) & 7
|
||||
sf := (baseOp >> 31) & 1
|
||||
@@ -967,10 +992,15 @@ func encodeARM64DPSR(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
||||
spInvolved = true
|
||||
}
|
||||
}
|
||||
if spInvolved && strings.HasPrefix(mnem, "ADD") || spInvolved && strings.HasPrefix(mnem, "SUB") {
|
||||
if spInvolved && (strings.HasPrefix(mnem, "ADD") || strings.HasPrefix(mnem, "SUB") || isCmp) {
|
||||
if shiftBits != 0 {
|
||||
return nil, fmt.Errorf("%s: right shift not encodable against SP", mnem)
|
||||
}
|
||||
// The extend field carries 0..4 only (asm7.go: shift amount
|
||||
// out of range 0 to 4).
|
||||
if amount > 4 {
|
||||
return nil, fmt.Errorf("%s: shift amount out of range 0 to 4", mnem)
|
||||
}
|
||||
opt := uint32(3) // UXTX
|
||||
if strings.HasSuffix(mnem, "W") {
|
||||
opt = 2 // UXTW
|
||||
@@ -982,6 +1012,13 @@ func encodeARM64DPSR(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
||||
} else {
|
||||
baseOp |= 1<<21 | extendOpt<<13 | uint32(amount)<<10
|
||||
}
|
||||
// The flag-setting add/sub family rejects SP as its destination.
|
||||
switch mnem {
|
||||
case "ADDS", "ADDSW", "SUBS", "SUBSW":
|
||||
if strings.EqualFold(operandRegName(ops[len(ops)-1]), "RSP") {
|
||||
return nil, fmt.Errorf("%s: illegal destination register RSP", mnem)
|
||||
}
|
||||
}
|
||||
rd := arm64RegNum(operandRegName(ops[len(ops)-1]))
|
||||
rn := rd
|
||||
if len(ops) == 3 {
|
||||
@@ -1028,6 +1065,13 @@ func encodeARM64DPSR(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
||||
if rm < 0 || rn < 0 || rd < 0 {
|
||||
return nil, fmt.Errorf("invalid register operand in %s", mnem)
|
||||
}
|
||||
// The flag-setting add/sub family rejects SP as its destination.
|
||||
switch mnem {
|
||||
case "ADDS", "ADDSW", "SUBS", "SUBSW":
|
||||
if strings.EqualFold(operandRegName(ops[2]), "RSP") {
|
||||
return nil, fmt.Errorf("%s: illegal destination register RSP", mnem)
|
||||
}
|
||||
}
|
||||
// ADD/SUB against SP take the extended-register form with the
|
||||
// identity extend, the toolchain's spelling of a plain register
|
||||
// operand against the stack pointer (asm7.go opxrrr against C_RSP).
|
||||
@@ -1335,6 +1379,14 @@ func encodeARM64AddSubImm(mnem string, ops []*ast.Operand) ([]byte, error) {
|
||||
if rn < 0 || rd < 0 {
|
||||
return nil, fmt.Errorf("invalid register operand in %s", mnem)
|
||||
}
|
||||
// The flag-setting add/sub family rejects SP as its destination
|
||||
// (asm7.go: illegal destination register).
|
||||
switch mnem {
|
||||
case "ADDS", "ADDSW", "SUBS", "SUBSW":
|
||||
if strings.EqualFold(operandRegName(ops[len(ops)-1]), "RSP") {
|
||||
return nil, fmt.Errorf("%s: illegal destination register RSP", mnem)
|
||||
}
|
||||
}
|
||||
// CMP/CMN discard the destination.
|
||||
if mnem == "CMP" || mnem == "CMPW" || mnem == "CMN" || mnem == "CMNW" {
|
||||
rd = 31 // ZR
|
||||
@@ -2105,6 +2157,15 @@ func encodeARM64MemOp(mnem string, mem *ast.Operand, reg int, load bool, fi arm6
|
||||
if rn < 0 {
|
||||
return nil, fmt.Errorf("invalid memory operand")
|
||||
}
|
||||
// The MOV family addresses memory through a general register only.
|
||||
if mem.Addr.Base != "" && arm64RegClassOf(mem.Addr.Base) == arm64ClsFP {
|
||||
return nil, fmt.Errorf("%s: illegal combination: the base register cannot be FP", mnem)
|
||||
}
|
||||
// Writeback with the base doubling as the data register is constrained
|
||||
// unpredictable.
|
||||
if wb != "" && rn == reg && rn != 31 {
|
||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the base rides the data register", mnem)
|
||||
}
|
||||
lt, ok := a64LoadTable[mnem]
|
||||
if !ok {
|
||||
// MOV defaults to MOVD (64-bit load/store).
|
||||
@@ -2968,6 +3029,11 @@ func encodeARM64CSEL(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
||||
mnem == "CNEG" || mnem == "CNEGW"
|
||||
|
||||
if isAlias {
|
||||
// The aliases invert the condition, which is undefined for AL and NV
|
||||
// (asm7.go: invalid condition).
|
||||
if condName := operandRegName(ops[0]); strings.EqualFold(condName, "AL") || strings.EqualFold(condName, "NV") {
|
||||
return nil, fmt.Errorf("%s: invalid condition %s", mnem, condName)
|
||||
}
|
||||
is2op := mnem == "CSET" || mnem == "CSETW" || mnem == "CSETM" || mnem == "CSETMW"
|
||||
if is2op {
|
||||
// CSET cond, Rd → CSEL XZR, XZR, Rd, inverted_cond
|
||||
@@ -3112,6 +3178,14 @@ func encodeARM64Excl(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
||||
// The single-register opcodes pre-set the unused Rs (bits 20:16)
|
||||
// and Rt2 (bits 14:10) fields to 31; the pair forms carry a real
|
||||
// Rt2 and keep Rs at 31.
|
||||
// Constrained unpredictable: the base rides no pair member and
|
||||
// the pair registers differ.
|
||||
if rt1 == rt2 {
|
||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the pair registers match", mnem)
|
||||
}
|
||||
if bname := operandRegName(ops[0]); strings.EqualFold(bname, fmt.Sprintf("R%d", rt1)) || strings.EqualFold(bname, fmt.Sprintf("R%d", rt2)) {
|
||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the base rides a pair register", mnem)
|
||||
}
|
||||
return a64wordLE(baseOp | 0x1F<<16 | uint32(rt2)<<10 | uint32(rn)<<5 | uint32(rt1)), nil
|
||||
}
|
||||
rt := arm64RegNum(operandRegName(ops[1]))
|
||||
@@ -3128,17 +3202,35 @@ func encodeARM64Excl(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The status register cannot be SP (asm7.go: illegal combination).
|
||||
if strings.EqualFold(operandRegName(ops[2]), "RSP") {
|
||||
return nil, fmt.Errorf("%s: illegal combination: the status register cannot be RSP", mnem)
|
||||
}
|
||||
rs := arm64RegNum(operandRegName(ops[2]))
|
||||
if rs < 0 {
|
||||
return nil, fmt.Errorf("invalid operand in %s", mnem)
|
||||
}
|
||||
if rt1, rt2, ok := arm64PairOf(ops[0]); ok {
|
||||
// Constrained unpredictable (asm7.go case 59): the pair registers
|
||||
// differ, and the status register differs from both pair members and
|
||||
// from a non-SP base.
|
||||
if rt1 == rt2 {
|
||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the pair registers match", mnem)
|
||||
}
|
||||
if rs == rt1 || rs == rt2 || (rs == rn && rn != 31) {
|
||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the status register rides a pair register or the base", mnem)
|
||||
}
|
||||
return a64wordLE(baseOp | uint32(rs)<<16 | uint32(rt2)<<10 | uint32(rn)<<5 | uint32(rt1)), nil
|
||||
}
|
||||
// Constrained unpredictable (asm7.go case 59): the status register
|
||||
// differs from Rt and from a non-SP base.
|
||||
rt := arm64RegNum(operandRegName(ops[0]))
|
||||
if rt < 0 {
|
||||
return nil, fmt.Errorf("invalid operand in %s", mnem)
|
||||
}
|
||||
if rs == rt || (rs == rn && rn != 31) {
|
||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the status register matches Rt or the base", mnem)
|
||||
}
|
||||
return a64wordLE(baseOp | uint32(rs)<<16 | uint32(rn)<<5 | uint32(rt)), nil
|
||||
}
|
||||
|
||||
@@ -3157,6 +3249,10 @@ func encodeARM64LSEAtom(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The result register cannot be SP (asm7.go: illegal combination).
|
||||
if strings.EqualFold(operandRegName(ops[2]), "RSP") {
|
||||
return nil, fmt.Errorf("%s: illegal combination: the result register cannot be RSP", mnem)
|
||||
}
|
||||
rt := arm64RegNum(operandRegName(ops[2]))
|
||||
if rt < 0 {
|
||||
return nil, fmt.Errorf("invalid operand in %s", mnem)
|
||||
@@ -3681,6 +3777,30 @@ func encodeARM64Pair(mnem string, baseOp uint32, ops []*ast.Operand, pc int, fi
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s expects a register pair (Rt1, Rt2)", mnem)
|
||||
}
|
||||
// Constrained unpredictable: the pair registers differ, and a
|
||||
// writeback base rides no pair member.
|
||||
if rt1 == rt2 {
|
||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the pair registers match", mnem)
|
||||
}
|
||||
if wb != "" {
|
||||
if strings.EqualFold(operandRegName(memOp), fmt.Sprintf("R%d", rt1)) || strings.EqualFold(operandRegName(memOp), fmt.Sprintf("R%d", rt2)) {
|
||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the base rides a pair register", mnem)
|
||||
}
|
||||
}
|
||||
// The FP pairs take FP registers against a GP base (asm7.go: invalid
|
||||
// register pair).
|
||||
if strings.HasPrefix(mnem, "FLDP") || strings.HasPrefix(mnem, "FSTP") {
|
||||
first := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(pairOp.Raw), "("))
|
||||
if i := strings.IndexAny(first, ",)"); i >= 0 {
|
||||
first = strings.TrimSpace(first[:i])
|
||||
}
|
||||
if !strings.HasPrefix(first, "F") {
|
||||
return nil, fmt.Errorf("%s: invalid register pair %s", mnem, pairOp.Raw)
|
||||
}
|
||||
if strings.HasPrefix(strings.TrimLeft(operandRegName(memOp), "( "), "F") {
|
||||
return nil, fmt.Errorf("%s: invalid register pair: the base must be a general register", mnem)
|
||||
}
|
||||
}
|
||||
|
||||
// Pair access against a static symbol: ADRP R27, sym; ADD R27, R27, #lo;
|
||||
// LDP/STP (R27), (Rt1, Rt2), with the R_ADDRARM64 pair riding the first
|
||||
@@ -4274,7 +4394,7 @@ func arm64SimdHasElement(ops []*ast.Operand) bool {
|
||||
// admits it. It returns the arrangement's index, with a64Arr8B for a bare
|
||||
// V/F spelling.
|
||||
func arm64SimdArrs(mnem string, arrs []string, allowed uint16) (int, error) {
|
||||
sel := a64Arr8B
|
||||
sel := -1
|
||||
for _, a := range arrs {
|
||||
if a == "" {
|
||||
continue
|
||||
@@ -4283,11 +4403,14 @@ func arm64SimdArrs(mnem string, arrs []string, allowed uint16) (int, error) {
|
||||
if i < 0 || specBit(i)&allowed == 0 {
|
||||
return 0, fmt.Errorf("%s: invalid arrangement %q", mnem, a)
|
||||
}
|
||||
if sel != a64Arr8B && sel != i {
|
||||
if sel >= 0 && sel != i {
|
||||
return 0, fmt.Errorf("%s: mixed arrangements", mnem)
|
||||
}
|
||||
sel = i
|
||||
}
|
||||
if sel < 0 {
|
||||
sel = a64Arr8B
|
||||
}
|
||||
return sel, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user