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
|
||||
// toolchain materialises the constant into REGTMP and uses the
|
||||
// register form (asm7.go cases 62 and 13). BIC/ORN/EON read the
|
||||
// written value, so the materialisation uses v before any
|
||||
// inversion.
|
||||
// register form (asm7.go cases 62 and 13). A REGTMP source
|
||||
// register is refused, the materialisation clobbering it before
|
||||
// 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
|
||||
if inverted {
|
||||
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
|
||||
// mirrors omovconst (asm7.go case 62): MOVZ for a movcon value, MOVN for
|
||||
// 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
|
||||
switch s := arm64Movcon(d); {
|
||||
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]
|
||||
// alike; beyond the split band the offset reaches the literal pool.
|
||||
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)))
|
||||
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
|
||||
// 30/31 and omovlit). The pooled words sit at poolBase plus the entry's
|
||||
// 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 {
|
||||
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
|
||||
// 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 !load && (rt1 == 27 || rt2 == 27) {
|
||||
return nil, fmt.Errorf("%s: cannot use REGTMP as source", mnem)
|
||||
}
|
||||
op, v := uint32(0), off // ADD
|
||||
if v < 0 {
|
||||
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
|
||||
// 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 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 {
|
||||
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
|
||||
// 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
|
||||
|
||||
@@ -20,13 +20,6 @@ var arm64AcceptedErrorShapes = []string{
|
||||
// spelling, the .P immediate against the aggregate register size, the
|
||||
// REGTMP (R27) as an explicit operand the toolchain refuses, the pair
|
||||
// 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 VMALLE1IS, R0",
|
||||
"TLBI ALLE3OS, ZR",
|
||||
|
||||
Reference in new issue
Block a user