fix(asm): emit nothing for the arm64 NOP pseudo-instruction
Assisted-by: GLM 5.3
This commit is contained in:
1 parent
405e2427ed
commit
7bc80ccb54
2 files changed
+66
-4
No files matched your search
+30
-1
@@ -287,6 +287,9 @@ func arm64InstrSize(instr *ast.Instr, fi arm64FrameInfo, pos int) int {
|
||||
if mnem == "BYTE" {
|
||||
return len(ops)
|
||||
}
|
||||
if mnem == "NOP" {
|
||||
return 0 // the zero-size pseudo-instruction, operand or not
|
||||
}
|
||||
switch mnem {
|
||||
case "VMOVS", "VMOVD", "VMOVQ":
|
||||
// ADRP + ADD + wide load against a pooled literal.
|
||||
@@ -414,7 +417,33 @@ func encodeARM64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi arm64
|
||||
switch mnem {
|
||||
case "RET":
|
||||
return arm64RetInstr(fi, ops, relocs), nil
|
||||
case "NOP", "NOOP":
|
||||
case "NOP":
|
||||
// The toolchain's ANOP is a zero-size pseudo-instruction: no bytes
|
||||
// whatever operand rides it, the immediate, register and vector
|
||||
// classes included (asm7.go's ANOP rows), and anything else is an
|
||||
// illegal combination. A bare register operand parses as a symbol
|
||||
// reference with no base, the way operandRegName reads it.
|
||||
if len(ops) > 1 {
|
||||
return nil, fmt.Errorf("NOP: illegal combination")
|
||||
}
|
||||
if len(ops) == 1 && !isImmOperand(ops[0]) {
|
||||
op := ops[0]
|
||||
name := operandRegName(op)
|
||||
bare := op.Addr.Base == "" && op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "" &&
|
||||
!op.Addr.HasOff && op.Addr.Index == ""
|
||||
reg := arm64RegNum(name) >= 0 ||
|
||||
len(name) > 1 && (name[0] == 'F' || name[0] == 'V') && strings.Trim(name[1:], "0123456789") == ""
|
||||
if !bare || !reg {
|
||||
return nil, fmt.Errorf("NOP: illegal combination")
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
case "NOOP":
|
||||
// NOOP is the real hint instruction: one word, and it takes no
|
||||
// operand at all.
|
||||
if len(ops) != 0 {
|
||||
return nil, fmt.Errorf("NOOP: illegal combination")
|
||||
}
|
||||
return a64wordLE(a64NOP), nil
|
||||
case "UNDEF":
|
||||
return a64wordLE(a64BRK(0)), nil
|
||||
|
||||
@@ -568,10 +568,11 @@ func TestArm64CondCompare(t *testing.T) {
|
||||
// TestArm64CompareBranch pins CBZ/CBNZ/TBZ/TBNZ against a label five and
|
||||
// six words ahead, matching go tool asm's own offsets.
|
||||
func TestArm64CompareBranch(t *testing.T) {
|
||||
// Layout: CBZ(0) TBZ(4) TBNZ(8) CBNZ(12) NOP(16) NOP(17th word...) done.
|
||||
// Layout: CBZ(0) TBZ(4) TBNZ(8) CBNZ(12) NOOP(16) NOOP(17th word...) done.
|
||||
// The fillers are NOOP, not NOP: the toolchain's NOP emits nothing.
|
||||
src := "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n" +
|
||||
"\tCBZ R1, done\n\tTBZ $4, R7, done\n\tTBNZ $33, R7, done\n\tCBNZW R2, done\n" +
|
||||
"\tNOP\n\tNOP\n\tdone:\tNOP\n\tRET\n"
|
||||
"\tNOOP\n\tNOOP\n\tdone:\tNOOP\n\tRET\n"
|
||||
f, errs := parser.Parse("test_arm64.s", src)
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
@@ -604,7 +605,7 @@ func TestArm64CompareBranch(t *testing.T) {
|
||||
// TestArm64ADR pins ADR against a forward label.
|
||||
func TestArm64ADR(t *testing.T) {
|
||||
src := "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n" +
|
||||
"\tADR done, R10\n\tNOP\n\tNOP\n\tdone:\tNOP\n\tRET\n"
|
||||
"\tADR done, R10\n\tNOOP\n\tNOOP\n\tdone:\tNOOP\n\tRET\n"
|
||||
f, errs := parser.Parse("test_arm64.s", src)
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
@@ -2234,6 +2235,38 @@ func TestArm64LiteralPoolGuard(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestArm64NoopVsNop pins the two spellings apart, the way the toolchain
|
||||
// holds them: NOOP is the real hint instruction and NOP a zero-size pseudo
|
||||
// whose operand, when it is an immediate or a register, rides along for go
|
||||
// vet's benefit; every other operand shape and the NOOP operands are illegal
|
||||
// combinations.
|
||||
func TestArm64NoopVsNop(t *testing.T) {
|
||||
got := arm64Words(t, "\tNOP\n\tNOOP\n\tNOP\tR0\n")
|
||||
want := []uint32{0xd503201f, 0xd65f03c0}
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("word count = %d, want %d", len(got), len(want))
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Errorf("word %d = %08x, want %08x", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
for _, src := range []string{
|
||||
"\tNOOP\t$0\n",
|
||||
"\tNOOP\tR0\n",
|
||||
"\tNOP\t8(R0)\n",
|
||||
"\tNOP\t$0, $1\n",
|
||||
} {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestArm64FPImmediate pins the FP immediate moves against `go tool asm`
|
||||
// words: the FMOV (immediate) instruction for the 8-bit encodable values and
|
||||
// the FMOV-from-ZR move for zero, plus the rejections the toolchain raises
|
||||
|
||||
Reference in new issue
Block a user