fix(asm): reject the arm64 REGTMP spellings the toolchain refuses
Assisted-by: GLM 5.3
This commit is contained in:
1 parent
2385bb7069
commit
7a69be8b59
3 files changed
+88
-14
No files matched your search
+42
-7
@@ -980,9 +980,13 @@ func encodeARM64DPSR(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
|||||||
}
|
}
|
||||||
// Beyond the bitmask immediates, and for the ZR destinations, the
|
// Beyond the bitmask immediates, and for the ZR destinations, the
|
||||||
// toolchain materialises the constant into REGTMP and uses the
|
// toolchain materialises the constant into REGTMP and uses the
|
||||||
// register form (asm7.go cases 62 and 13). BIC/ORN/EON read the
|
// register form (asm7.go cases 62 and 13). A REGTMP source
|
||||||
// written value, so the materialisation uses v before any
|
// register is refused, the materialisation clobbering it before
|
||||||
// inversion.
|
// the register form reads it. BIC/ORN/EON read the written
|
||||||
|
// value, so the materialisation uses v before any inversion.
|
||||||
|
if rn == 27 {
|
||||||
|
return nil, fmt.Errorf("%s: cannot use REGTMP as source", mnem)
|
||||||
|
}
|
||||||
written := v
|
written := v
|
||||||
if inverted {
|
if inverted {
|
||||||
written = ^v
|
written = ^v
|
||||||
@@ -1515,7 +1519,12 @@ func arm64AddSubImmWords(mnem string, v int64, rn, rd int, ext bool) ([]uint32,
|
|||||||
// Constant into REGTMP (R27), then the register form. The first word
|
// Constant into REGTMP (R27), then the register form. The first word
|
||||||
// mirrors omovconst (asm7.go case 62): MOVZ for a movcon value, MOVN for
|
// mirrors omovconst (asm7.go case 62): MOVZ for a movcon value, MOVN for
|
||||||
// the complement form, the bitmask ORR otherwise, and the full
|
// the complement form, the bitmask ORR otherwise, and the full
|
||||||
// omovlconst sequence when no single word carries the value.
|
// omovlconst sequence when no single word carries the value. A REGTMP
|
||||||
|
// source register is refused: the materialisation would clobber it
|
||||||
|
// before the register form reads it (asm7.go cases 13 and 62).
|
||||||
|
if rn == 27 {
|
||||||
|
return nil, fmt.Errorf("%s: cannot use REGTMP as source", mnem)
|
||||||
|
}
|
||||||
var seq []uint32
|
var seq []uint32
|
||||||
switch s := arm64Movcon(d); {
|
switch s := arm64Movcon(d); {
|
||||||
case s >= 0:
|
case s >= 0:
|
||||||
@@ -2284,7 +2293,7 @@ func encodeARM64MemOp(mnem string, mem *ast.Operand, reg int, load bool, fi arm6
|
|||||||
// ADD offsets from the operand's own base register, [SP] and [Rn]
|
// ADD offsets from the operand's own base register, [SP] and [Rn]
|
||||||
// alike; beyond the split band the offset reaches the literal pool.
|
// alike; beyond the split band the offset reaches the literal pool.
|
||||||
if !arm64OffsetSplitReach(off, lt) {
|
if !arm64OffsetSplitReach(off, lt) {
|
||||||
return arm64PoolAccess(mnem, lt, opc, off, rn, reg, pc, pool, poolBase)
|
return arm64PoolAccess(mnem, lt, opc, off, rn, reg, pc, pool, poolBase, load)
|
||||||
}
|
}
|
||||||
hi, lo, ok := arm64SplitImm24(off, bits.TrailingZeros64(uint64(scale)))
|
hi, lo, ok := arm64SplitImm24(off, bits.TrailingZeros64(uint64(scale)))
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -2301,7 +2310,16 @@ func encodeARM64MemOp(mnem string, mem *ast.Operand, reg int, load bool, fi arm6
|
|||||||
// into REGTMP, then the access against the register pair (asm7.go cases
|
// into REGTMP, then the access against the register pair (asm7.go cases
|
||||||
// 30/31 and omovlit). The pooled words sit at poolBase plus the entry's
|
// 30/31 and omovlit). The pooled words sit at poolBase plus the entry's
|
||||||
// offset, both function-relative, so the imm19 distance resolves here.
|
// offset, both function-relative, so the imm19 distance resolves here.
|
||||||
func arm64PoolAccess(mnem string, lt a64LSType, opc int, off int64, rn, reg, pc int, pool *arm64Pool, poolBase int) ([]byte, error) {
|
func arm64PoolAccess(mnem string, lt a64LSType, opc int, off int64, rn, reg, pc int, pool *arm64Pool, poolBase int, load bool) ([]byte, error) {
|
||||||
|
// The toolchain refuses a REGTMP data register or base on the pool path
|
||||||
|
// (asm7.go cases 30/31): the literal load itself rides REGTMP.
|
||||||
|
if reg == 27 || rn == 27 {
|
||||||
|
kind := "load"
|
||||||
|
if !load {
|
||||||
|
kind = "store"
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s: REGTMP used in large offset %s", mnem, kind)
|
||||||
|
}
|
||||||
if pool == nil {
|
if pool == nil {
|
||||||
return nil, fmt.Errorf("%s: offset %d out of range (literal pool not supported)", mnem, off)
|
return nil, fmt.Errorf("%s: offset %d out of range (literal pool not supported)", mnem, off)
|
||||||
}
|
}
|
||||||
@@ -3904,7 +3922,12 @@ func encodeARM64Pair(mnem string, baseOp uint32, ops []*ast.Operand, pc int, fi
|
|||||||
}
|
}
|
||||||
// Offsets within ±4095 the imm7 field cannot carry move the whole
|
// Offsets within ±4095 the imm7 field cannot carry move the whole
|
||||||
// distance into REGTMP first (asm7.go cases 74/76: add/sub + ldp/stp).
|
// distance into REGTMP first (asm7.go cases 74/76: add/sub + ldp/stp).
|
||||||
|
// A store refuses a REGTMP pair member here (case 76: the add would
|
||||||
|
// clobber it before the store reads it); a load allows one.
|
||||||
if off >= -4095 && off <= 4095 {
|
if off >= -4095 && off <= 4095 {
|
||||||
|
if !load && (rt1 == 27 || rt2 == 27) {
|
||||||
|
return nil, fmt.Errorf("%s: cannot use REGTMP as source", mnem)
|
||||||
|
}
|
||||||
op, v := uint32(0), off // ADD
|
op, v := uint32(0), off // ADD
|
||||||
if v < 0 {
|
if v < 0 {
|
||||||
op, v = 1, -v // SUB
|
op, v = 1, -v // SUB
|
||||||
@@ -3916,8 +3939,20 @@ func encodeARM64Pair(mnem string, baseOp uint32, ops []*ast.Operand, pc int, fi
|
|||||||
}
|
}
|
||||||
// Positive offsets up to 16 MiB split into two ADDs: the low imm12 bits
|
// Positive offsets up to 16 MiB split into two ADDs: the low imm12 bits
|
||||||
// from the base register into REGTMP, the high multiple of 0x1000 on top
|
// from the base register into REGTMP, the high multiple of 0x1000 on top
|
||||||
// (asm7.go cases 75/77). Beyond the band the offset reaches the pool.
|
// (asm7.go cases 75/77). Beyond the band the offset reaches the pool,
|
||||||
|
// which refuses a REGTMP base outright and, for the stores, a REGTMP
|
||||||
|
// pair member as well.
|
||||||
if off < 0 || off > 0xffffff {
|
if off < 0 || off > 0xffffff {
|
||||||
|
if rn == 27 {
|
||||||
|
kind := "load"
|
||||||
|
if !load {
|
||||||
|
kind = "store"
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s: REGTMP used in large offset %s", mnem, kind)
|
||||||
|
}
|
||||||
|
if !load && (rt1 == 27 || rt2 == 27) {
|
||||||
|
return nil, fmt.Errorf("%s: REGTMP used in large offset store", mnem)
|
||||||
|
}
|
||||||
if pool == nil {
|
if pool == nil {
|
||||||
return nil, fmt.Errorf("%s: offset %d out of range (literal pool not supported)", mnem, off)
|
return nil, fmt.Errorf("%s: offset %d out of range (literal pool not supported)", mnem, off)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2267,6 +2267,52 @@ func TestArm64NoopVsNop(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestArm64RegtmpBoundaries pins the REGTMP acceptance the toolchain holds
|
||||||
|
// (probed against `go tool asm`, Go 1.27, arm64): the plain imm12 and the
|
||||||
|
// ±4095 offsets tolerate a REGTMP source or pair member, while every
|
||||||
|
// lowering that itself writes REGTMP (the constant materialisations, the
|
||||||
|
// pool path, the pair store expansions) refuses one, and the pool path
|
||||||
|
// refuses a REGTMP base on both the loads and the stores.
|
||||||
|
func TestArm64RegtmpBoundaries(t *testing.T) {
|
||||||
|
accept := []string{
|
||||||
|
"\tADD\t$5, R27, R3\n", // imm12, no REGTMP sequence
|
||||||
|
"\tLDP\t700(R2), (R26, R27)\n", // load add/sub path: unchecked pair
|
||||||
|
"\tMOVD\tR27, 4000(R2)\n", // misaligned ±4095: unchecked data
|
||||||
|
"\tSTP\t(R26, R3), 700(R2)\n", // store add/sub path, no pair member
|
||||||
|
"\tLDP\t0x1234567(R2), (R27, R3)\n", // pool load: base alone decides
|
||||||
|
"\tMOVD\t$0x1000000(R27), R1\n", // lacon: case 34 reads the base
|
||||||
|
}
|
||||||
|
reject := []string{
|
||||||
|
"\tADD\t$0x1234567, R27, R3\n", // materialisation, REGTMP source
|
||||||
|
"\tAND\t$0x22220000, R27, R4\n", // logical materialisation, ditto
|
||||||
|
"\tSTP\t(R26, R27), 700(R2)\n", // store add/sub path, pair member
|
||||||
|
"\tSTP\t(R3, R4), 0x1234567(R27)\n", // pool store, REGTMP base
|
||||||
|
"\tLDP\t0x1234567(R27), (R3, R4)\n", // pool load, REGTMP base
|
||||||
|
"\tSTP\t(R27, R3), 0x1234567(R2)\n", // pool store, REGTMP pair member
|
||||||
|
"\tMOVD\tR27, 0x1234567(R2)\n", // pool store, REGTMP data
|
||||||
|
"\tMOVD\t0x1234567(R2), R27\n", // pool load, REGTMP data
|
||||||
|
}
|
||||||
|
for _, src := range accept {
|
||||||
|
f, errs := parser.Parse("test_arm64.s", "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n"+src+"\tRET\n")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Errorf("expected acceptance for %q, got parse rejection", strings.TrimSpace(src))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := AssembleFileARM64(f); err != nil {
|
||||||
|
t.Errorf("expected acceptance for %q, got %v", strings.TrimSpace(src), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, src := range reject {
|
||||||
|
f, errs := parser.Parse("test_arm64.s", "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n"+src+"\tRET\n")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
continue // a parse rejection is a rejection
|
||||||
|
}
|
||||||
|
if _, err := AssembleFileARM64(f); err == nil {
|
||||||
|
t.Errorf("expected rejection for %q, got nil", strings.TrimSpace(src))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestArm64VLDSTPostIndexContract pins the structure-load post-index rules
|
// TestArm64VLDSTPostIndexContract pins the structure-load post-index rules
|
||||||
// against `go tool asm` (Go 1.27, arm64): the implicit by-size increment of
|
// against `go tool asm` (Go 1.27, arm64): the implicit by-size increment of
|
||||||
// an unspelled or zero offset (both load and replicate forms, the words
|
// an unspelled or zero offset (both load and replicate forms, the words
|
||||||
|
|||||||
@@ -20,13 +20,6 @@ var arm64AcceptedErrorShapes = []string{
|
|||||||
// spelling, the .P immediate against the aggregate register size, the
|
// spelling, the .P immediate against the aggregate register size, the
|
||||||
// REGTMP (R27) as an explicit operand the toolchain refuses, the pair
|
// REGTMP (R27) as an explicit operand the toolchain refuses, the pair
|
||||||
// split paths included.
|
// split paths included.
|
||||||
"ADD $0x1234567, R27, R3",
|
|
||||||
"ADD $0x3fffffffc000, R27, R5",
|
|
||||||
"AND $0x22220000, R27, R4",
|
|
||||||
"ANDW $0x6006000060060, R27, R5",
|
|
||||||
"STP (R3, R4), 0x1234567(R27)",
|
|
||||||
"LDP 0x1234567(R27), (R3, R4)",
|
|
||||||
"STP (R26, R27), 700(R2)",
|
|
||||||
// TLBI operand arity and the range-prefetch operand shapes.
|
// TLBI operand arity and the range-prefetch operand shapes.
|
||||||
"TLBI VMALLE1IS, R0",
|
"TLBI VMALLE1IS, R0",
|
||||||
"TLBI ALLE3OS, ZR",
|
"TLBI ALLE3OS, ZR",
|
||||||
|
|||||||
Reference in new issue
Block a user