fix(asm): make arm64 and loong64 relocations match the toolchain
This commit is contained in:
+24
-15
@@ -12,14 +12,17 @@ import (
|
||||
|
||||
// assembleARM64 assembles an AArch64 (arm64) TEXT function body into machine
|
||||
// code. Every instruction is 4 bytes; the MOV pseudo-instruction and the
|
||||
// immediate-arithmetic forms expand to 2–4 instructions when the immediate
|
||||
// immediate-arithmetic forms expand to 2-4 instructions when the immediate
|
||||
// does not fit, so the layout is computed in two passes (sizes, then encoding
|
||||
// with resolved branch targets).
|
||||
//
|
||||
// The emitted bytes match the Go toolchain's arm64 assembler, which is the
|
||||
// ground-truth oracle: prologue/epilogue, FP/SP frame mapping, branch
|
||||
// encodings and the MOV immediate expansions all follow cmd/internal/obj/
|
||||
// arm64's asmout cases.
|
||||
// arm64's asmout cases. One deliberate difference: the stack-growth guard
|
||||
// (the morestack check in the prologue and the call back into the runtime in
|
||||
// the epilogue) is not emitted, so the bytes match only for NOSPLIT functions
|
||||
// or zero-frame leaves, where the toolchain emits no guard either.
|
||||
func assembleARM64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, []SpadjStep, error) {
|
||||
fi := arm64ComputeFrame(t)
|
||||
prologue := arm64Prologue(fi)
|
||||
@@ -67,7 +70,12 @@ func assembleARM64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, [
|
||||
return nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", in.Mnemonic.Text, err)
|
||||
}
|
||||
for j := preCount; j < len(relocs); j++ {
|
||||
relocs[j].Off += pc - len(prologue)
|
||||
// Make the relocation offsets function-relative: each instruction
|
||||
// records its reloc offset relative to its own start, and pc is
|
||||
// that instruction's offset from the function start (prologue
|
||||
// included). After shifts by the same amount.
|
||||
relocs[j].Off += pc
|
||||
relocs[j].After += pc
|
||||
}
|
||||
preCount = len(relocs)
|
||||
lines = append(lines, LineEntry{Offset: pc, Line: in.Pos().Line})
|
||||
@@ -462,7 +470,7 @@ func encodeARM64AddSubImm(mnem string, ops []*ast.Operand) ([]byte, error) {
|
||||
|
||||
// ---- MOV pseudo-instruction ----
|
||||
|
||||
// encodeARM64Mov encodes the MOV family — the load/store/immediate workhorse
|
||||
// encodeARM64Mov encodes the MOV family, the load/store/immediate workhorse
|
||||
// of Go's arm64 assembly. MOV is an alias of MOVD (the width mnemonics
|
||||
// select the access width). The forms, mirroring the toolchain:
|
||||
//
|
||||
@@ -839,7 +847,9 @@ func encodeARM64SBAddr(sym *ast.Symbol, rd int, relocs *[]Reloc) []byte {
|
||||
)
|
||||
}
|
||||
|
||||
// encodeARM64SBLoad emits ADRP R20, 0; LDR Rd, [R20, 0] with relocations.
|
||||
// encodeARM64SBLoad emits ADRP R27, 0; LDR Rd, [R27, 0] with relocations,
|
||||
// matching the toolchain: the scratch register is REGTMP (R27) and the pair
|
||||
// carries R_ARM64_PCREL_LDST64.
|
||||
func encodeARM64SBLoad(sym *ast.Symbol, rd int, mnem string, relocs *[]Reloc) ([]byte, error) {
|
||||
lt, ok := a64LoadTable[mnem]
|
||||
if !ok {
|
||||
@@ -847,17 +857,17 @@ func encodeARM64SBLoad(sym *ast.Symbol, rd int, mnem string, relocs *[]Reloc) ([
|
||||
}
|
||||
if relocs != nil {
|
||||
*relocs = append(*relocs,
|
||||
Reloc{Off: 0, After: 0, Name: sym.Name, Kind: RelArm64Addr, Addend: sym.Offset},
|
||||
Reloc{Off: 4, After: 4, Name: sym.Name, Kind: RelArm64Addr, Addend: sym.Offset},
|
||||
Reloc{Off: 0, After: 8, Name: sym.Name, Kind: RelArm64LDST64, Addend: sym.Offset},
|
||||
)
|
||||
}
|
||||
return a64WordsLE(
|
||||
a64ADR(1, 0, 0, 20), // ADRP R20, 0
|
||||
a64LSU(uint32(lt.size), uint32(lt.V), uint32(lt.opc), 0, 20, uint32(rd)), // LDR Rd, [R20, #0]
|
||||
a64ADR(1, 0, 0, 27), // ADRP R27, 0
|
||||
a64LSU(uint32(lt.size), uint32(lt.V), uint32(lt.opc), 0, 27, uint32(rd)), // LDR Rd, [R27, #0]
|
||||
), nil
|
||||
}
|
||||
|
||||
// encodeARM64SBStore emits ADRP R20, 0; STR Rs, [R20, 0] with relocations.
|
||||
// encodeARM64SBStore emits ADRP R27, 0; STR Rs, [R27, 0] with relocations,
|
||||
// matching the toolchain's R27 scratch and R_ARM64_PCREL_LDST64 pair.
|
||||
func encodeARM64SBStore(sym *ast.Symbol, rs int, mnem string, relocs *[]Reloc) ([]byte, error) {
|
||||
lt, ok := a64LoadTable[mnem]
|
||||
if !ok {
|
||||
@@ -866,13 +876,12 @@ func encodeARM64SBStore(sym *ast.Symbol, rs int, mnem string, relocs *[]Reloc) (
|
||||
storeOpc := a64StoreOpc(lt)
|
||||
if relocs != nil {
|
||||
*relocs = append(*relocs,
|
||||
Reloc{Off: 0, After: 0, Name: sym.Name, Kind: RelArm64Addr, Addend: sym.Offset},
|
||||
Reloc{Off: 4, After: 4, Name: sym.Name, Kind: RelArm64Addr, Addend: sym.Offset},
|
||||
Reloc{Off: 0, After: 8, Name: sym.Name, Kind: RelArm64LDST64, Addend: sym.Offset},
|
||||
)
|
||||
}
|
||||
return a64WordsLE(
|
||||
a64ADR(1, 0, 0, 20), // ADRP R20, 0
|
||||
a64LSU(uint32(lt.size), uint32(lt.V), uint32(storeOpc), 0, 20, uint32(rs)), // STR Rs, [R20, #0]
|
||||
a64ADR(1, 0, 0, 27), // ADRP R27, 0
|
||||
a64LSU(uint32(lt.size), uint32(lt.V), uint32(storeOpc), 0, 27, uint32(rs)), // STR Rs, [R27, #0]
|
||||
), nil
|
||||
}
|
||||
|
||||
@@ -1092,7 +1101,7 @@ func encodeARM64CSEL(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
||||
return a64wordLE(baseOp | uint32(rn)<<16 | invCond<<12 | uint32(rn)<<5 | uint32(rd)), nil
|
||||
}
|
||||
|
||||
// CSEL cond, Rn, Rm, Rd (4 operands) — condition first.
|
||||
// CSEL cond, Rn, Rm, Rd (4 operands), condition first.
|
||||
// Go assembler syntax: CSEL cond, Rn, Rm, Rd
|
||||
// ARM64 encoding: Rm in bits[20:16], Rn in bits[9:5], Rd in bits[4:0].
|
||||
if len(ops) != 4 {
|
||||
|
||||
Reference in New Issue
Block a user