fix(asm): enforce the arm64 TLBI, RPRFM, FCVT and integer-pair arities
Assisted-by: GLM 5.3
This commit is contained in:
1 parent
7a69be8b59
commit
33e7fdac98
4 files changed
+140
-33
No files matched your search
+71
-7
@@ -3448,6 +3448,34 @@ func arm64SimdNarrowPair(mnem, src, dst string, two bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// arm64SimdFCVTLongPair validates the FCVTL width pair: S to D alone, with
|
||||
// the plain spelling reading S2 and the .2 spelling S4, the destination
|
||||
// always D2.
|
||||
func arm64SimdFCVTLongPair(mnem, src, dst string, two bool) error {
|
||||
wantSrc := "S2"
|
||||
if two {
|
||||
wantSrc = "S4"
|
||||
}
|
||||
if src != wantSrc || dst != "D2" {
|
||||
return fmt.Errorf("%s: operand mismatch for the %s spelling: want %s, %s", mnem, mnem, wantSrc, "D2")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// arm64SimdFCVTNarrowPair validates the FCVTN width pair: D to S alone, the
|
||||
// source always D2, the destination S2 for the plain spelling and S4 for
|
||||
// the .2 spelling.
|
||||
func arm64SimdFCVTNarrowPair(mnem, src, dst string, two bool) error {
|
||||
wantDst := "S2"
|
||||
if two {
|
||||
wantDst = "S4"
|
||||
}
|
||||
if src != "D2" || dst != wantDst {
|
||||
return fmt.Errorf("%s: operand mismatch for the %s spelling: want %s, %s", mnem, mnem, "D2", wantDst)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// arm64SimdNLArrBits returns the arrangement bits a narrow/long/wide
|
||||
// instruction contributes: the driving arrangement's size and Q bits, for
|
||||
// the FCVT family only the Q bit (whose size field is fixed in the base),
|
||||
@@ -3504,7 +3532,19 @@ func encodeARM64SimdNL(mnem string, spec a64SimdNLSpec, ops []*ast.Operand) ([]b
|
||||
}
|
||||
var drive string
|
||||
var pairErr error
|
||||
if spec.form == a64NLTwoNarrow {
|
||||
if spec.qonly {
|
||||
// The FCVT conversions are pinned to one width pair: S to D for
|
||||
// the lengthening (FCVTL S2→D2, FCVTL2 S4→D2), D to S for the
|
||||
// narrowing (FCVTN D2→S2, FCVTN2 D2→S4); the size field is
|
||||
// fixed in the opcode and no other arrangement exists.
|
||||
if spec.form == a64NLTwoNarrow {
|
||||
drive = vd.arr
|
||||
pairErr = arm64SimdFCVTNarrowPair(mnem, vn.arr, vd.arr, two)
|
||||
} else {
|
||||
drive = vn.arr
|
||||
pairErr = arm64SimdFCVTLongPair(mnem, vn.arr, vd.arr, two)
|
||||
}
|
||||
} else if spec.form == a64NLTwoNarrow {
|
||||
// XTN/FCVTN: wide source into a narrow destination; the
|
||||
// arrangement bits follow the destination.
|
||||
drive, pairErr = vd.arr, arm64SimdNarrowPair(mnem, vn.arr, vd.arr, two)
|
||||
@@ -3863,7 +3903,9 @@ func encodeARM64Pair(mnem string, baseOp uint32, ops []*ast.Operand, pc int, fi
|
||||
}
|
||||
}
|
||||
// The FP pairs take FP registers against a GP base (asm7.go: invalid
|
||||
// register pair).
|
||||
// register pair). The integer pairs are the mirror image: an F pair on
|
||||
// LDP/STP is an invalid register pair too, the toolchain having no class
|
||||
// for it.
|
||||
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 {
|
||||
@@ -3875,6 +3917,14 @@ func encodeARM64Pair(mnem string, baseOp uint32, ops []*ast.Operand, pc int, fi
|
||||
if strings.HasPrefix(strings.TrimLeft(operandRegName(memOp), "( "), "F") {
|
||||
return nil, fmt.Errorf("%s: invalid register pair: the base must be a general register", mnem)
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Pair access against a static symbol: ADRP R27, sym; ADD R27, R27, #lo;
|
||||
@@ -4192,19 +4242,28 @@ func encodeARM64Sys(mnem string, ops []*ast.Operand) ([]byte, error) {
|
||||
w := 0xd5080000 | inst.op1<<16 | 7<<12 | inst.cm<<8 | inst.op2<<5
|
||||
return a64wordLE(w | uint32(rn)&31), nil
|
||||
case "TLBI":
|
||||
// The register operand is optional: TLBI VMALLE1IS alone means ZR.
|
||||
// The register operand's arity follows the operation (asm7.go's
|
||||
// sysInstFields): the by-address spellings take the Xt and the
|
||||
// whole-entry ones (VMALL*, ALL**) take none, an explicit register
|
||||
// being extraneous.
|
||||
if len(ops) != 1 && len(ops) != 2 {
|
||||
return nil, fmt.Errorf("TLBI expects <op>[, Rn]")
|
||||
}
|
||||
inst, ok := a64TLBIOps[operandRegName(ops[0])]
|
||||
name := operandRegName(ops[0])
|
||||
inst, ok := a64TLBIOps[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("TLBI: unknown operation %q", operandRegName(ops[0]))
|
||||
return nil, fmt.Errorf("TLBI: unknown operation %q", name)
|
||||
}
|
||||
rt := 31
|
||||
if len(ops) == 2 {
|
||||
if !arm64TLBITakesReg(name) {
|
||||
return nil, fmt.Errorf("TLBI %s: extraneous register at operand 2", name)
|
||||
}
|
||||
if rt = arm64RegNum(operandRegName(ops[1])); rt < 0 {
|
||||
return nil, fmt.Errorf("TLBI: invalid register operand")
|
||||
}
|
||||
} else if arm64TLBITakesReg(name) {
|
||||
return nil, fmt.Errorf("TLBI %s: missing register at operand 2", name)
|
||||
}
|
||||
w := 0xd5080000 | inst.op1<<16 | 8<<12 | inst.cm<<8 | inst.op2<<5
|
||||
return a64wordLE(w | uint32(rt)&31), nil
|
||||
@@ -4328,6 +4387,11 @@ func encodeARM64Sys(mnem string, ops []*ast.Operand) ([]byte, error) {
|
||||
if rm < 0 {
|
||||
return nil, fmt.Errorf("RPRFM: invalid register operand")
|
||||
}
|
||||
// The toolchain's class ladder takes a plain R register alone: RSP
|
||||
// and ZR are illegal combinations (asm7.go case 110).
|
||||
if name := operandRegName(ops[1]); strings.EqualFold(name, "RSP") || strings.EqualFold(name, "SP") || strings.EqualFold(name, "ZR") {
|
||||
return nil, fmt.Errorf("RPRFM: illegal combination: %s is not a general register", name)
|
||||
}
|
||||
var op uint64
|
||||
if isImmOperand(ops[2]) {
|
||||
op = uint64(arm64Imm64(ops[2]))
|
||||
@@ -5015,8 +5079,8 @@ func encodeARM64VLDST(mnem string, post uint32, ops []*ast.Operand) ([]byte, err
|
||||
}
|
||||
// The toolchain's addressing contract for the structure forms (asm7.go's
|
||||
// class ladder): an index register exists only as the post-index
|
||||
// increment, so it is illegal without .P and takes neither an offset nor
|
||||
// an extend nor a shift beside it.
|
||||
// increment, so it is illegal without .P, and the immediate increment
|
||||
// must be exactly the bytes the whole list transfers.
|
||||
idx := ops[memIdx].Addr.Index
|
||||
if idx != "" && post == 0 {
|
||||
return nil, fmt.Errorf("%s: illegal combination: the register index is a post-index, it needs the .P spelling", mnem)
|
||||
|
||||
Reference in new issue
Block a user