feat(asm): materialise frame-relative addresses the way the toolchain does

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 21:34:30 +02:00
1 parent 1120a52a54
commit c1cef7b6e8
4 files changed
+394 -13

No files matched your search

+103 -6
View File
@@ -791,7 +791,7 @@ func encodeARM64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi arm64
// ADR/ADRP: (label, Rd) with the byte distance split into immlo and // ADR/ADRP: (label, Rd) with the byte distance split into immlo and
// immhi. // immhi.
if enc, ok := a64InstrTable[mnem]; ok && enc.format == a64FADR { if enc, ok := a64InstrTable[mnem]; ok && enc.format == a64FADR {
return encodeARM64ADR(mnem, enc.op, ops, pc, offsets, resolve) return encodeARM64ADR(mnem, enc.op, ops, pc, offsets)
} }
// Bitfield extract with wrapping immr: UBFX, SBFX. // Bitfield extract with wrapping immr: UBFX, SBFX.
@@ -1897,6 +1897,13 @@ func encodeARM64Mov(instr *ast.Instr, mnem string, wb string, fi arm64FrameInfo,
} }
return encodeARM64SBAddr(src.Imm.Sym, rd, relocs), nil return encodeARM64SBAddr(src.Imm.Sym, rd, relocs), nil
} }
// Frame-relative immediate address: MOVD $sym+off(FP|SP), Rd
// materialises the address with ADD/SUB from the hardware SP
// (asm7.go case 4), the shape every runtime address-of-argument
// load is written in.
if src.Imm.Sym != nil && (src.Imm.Sym.Pseudo == "FP" || src.Imm.Sym.Pseudo == "SP") {
return encodeARM64FrameAddr(src.Imm.Sym, dst, mnem, fi)
}
rd := arm64RegNum(operandRegName(dst)) rd := arm64RegNum(operandRegName(dst))
// The FP immediates: FMOVS/FMOVD $f, Fd ride the FMOV (immediate) // The FP immediates: FMOVS/FMOVD $f, Fd ride the FMOV (immediate)
// instruction when the 8-bit field carries the value and the // instruction when the 8-bit field carries the value and the
@@ -2066,6 +2073,21 @@ func arm64MovSize(mnem string, ops []*ast.Operand, fi arm64FrameInfo) int {
if src.Imm.Sym != nil && src.Imm.Sym.Pseudo == "SB" { if src.Imm.Sym != nil && src.Imm.Sym.Pseudo == "SB" {
return 8 // ADRP + ADD return 8 // ADRP + ADD
} }
// The frame-relative immediate address: one ADD/SUB imm12 word in
// the addcon band, the hi<<12 plus lo pair in the 24-bit band, and
// an error (so an irrelevant size) for the pool form beyond either.
if src.Imm.Sym != nil && (src.Imm.Sym.Pseudo == "FP" || src.Imm.Sym.Pseudo == "SP") {
v := arm64FrameAddrValue(src.Imm.Sym, fi)
switch {
case mnem != "MOVD" && mnem != "MOV",
!arm64IsAddcon(v) && !arm64IsAddcon(-v) && (v < 0 || v > 0xFFFFFF):
return 4
case arm64IsAddcon(v) || arm64IsAddcon(-v):
return 4
default:
return 8
}
}
// The con(register) form lowers to the toolchain's ADD/SUB chain: // The con(register) form lowers to the toolchain's ADD/SUB chain:
// one word in the addcon band, two in the 24-bit band, and the two // one word in the addcon band, two in the 24-bit band, and the two
// pool words (LDR X plus the UXTX add) beyond it. // pool words (LDR X plus the UXTX add) beyond it.
@@ -2332,6 +2354,17 @@ func encodeARM64RegMove(mnem string, src, dst *ast.Operand) ([]byte, error) {
// (SXTB, SXTH, SXTW), the unsigned byte and halfword forms to UBFM // (SXTB, SXTH, SXTW), the unsigned byte and halfword forms to UBFM
// (UXTB, UXTH), and only MOVWU to an ORR against WZR. MOVD stays // (UXTB, UXTH), and only MOVWU to an ORR against WZR. MOVD stays
// ORR Xd, XZR, Xm. // ORR Xd, XZR, Xm.
//
// The SP register moves ride the ADD (immediate) form instead: ORR
// cannot address SP and register 31 encodes ZR there (asm7.go case
// 24), whose C_RSP row exists for MOVD alone, so every other width is
// an illegal combination.
if sn, dn := operandRegName(src), operandRegName(dst); sn == "RSP" || sn == "SP" || dn == "RSP" || dn == "SP" {
if mnem != "MOVD" && mnem != "MOV" {
return nil, fmt.Errorf("%s: illegal combination: the SP register move exists for MOVD only", mnem)
}
return a64wordLE(a64AddSub(1, 0, 0, 0, 0, uint32(rs), uint32(rd))), nil
}
if rs != 31 { if rs != 31 {
switch mnem { switch mnem {
case "MOVB": case "MOVB":
@@ -2770,6 +2803,28 @@ func encodeARM64SBAddr(sym *ast.Symbol, rd int, relocs *[]Reloc) []byte {
) )
} }
// encodeARM64FrameAddr lowers MOVD $sym+off(FP) and its SP spelling: the
// address rides ADD/SUB from the hardware SP, one imm12 word inside the
// addcon band and the hi<<12 plus lo pair inside the 24-bit band (asm7.go
// optab case 4). Beyond either the toolchain pools the constant (case 34),
// and no pool the plain form reaches exists here, so that shape is refused.
// Every other width is an illegal combination: the toolchain's AACON rows
// exist for MOVD alone.
func encodeARM64FrameAddr(sym *ast.Symbol, dst *ast.Operand, mnem string, fi arm64FrameInfo) ([]byte, error) {
if mnem != "MOVD" && mnem != "MOV" {
return nil, fmt.Errorf("%s: illegal combination: the $frame-address form exists for MOVD only", mnem)
}
rd := arm64RegNum(operandRegName(dst))
if rd < 0 {
return nil, fmt.Errorf("%s %s: invalid destination register", mnem, sym.Raw)
}
v := arm64FrameAddrValue(sym, fi)
if !arm64IsAddcon(v) && !arm64IsAddcon(-v) && (v < 0 || v > 0xFFFFFF) {
return nil, fmt.Errorf("%s: %s needs the literal pool, which no arm64 pool reaches here", mnem, sym.Raw)
}
return a64WordsLE(arm64FrameAddrWords(v, rd)...), nil
}
// encodeARM64SBLoad emits ADRP R27, 0; LDR Rd, [R27, 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 // matching the toolchain: the scratch register is REGTMP (R27) and the pair
// carries R_ARM64_PCREL_LDST64. FMOVQ has no LDST relocation width, so it // carries R_ARM64_PCREL_LDST64. FMOVQ has no LDST relocation width, so it
@@ -3820,7 +3875,7 @@ func encodeARM64DP1(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, err
// target split into immlo (bits 30:29) and immhi (bits 23:5), with bit 31 // target split into immlo (bits 30:29) and immhi (bits 23:5), with bit 31
// selecting the page form. An n(PC) operand resolves to the instruction's // selecting the page form. An n(PC) operand resolves to the instruction's
// own address: the toolchain rewrites it away and encodes displacement 0. // own address: the toolchain rewrites it away and encodes displacement 0.
func encodeARM64ADR(mnem string, page uint32, ops []*ast.Operand, pc int, offsets map[string]int, resolve func(string) string) ([]byte, error) { func encodeARM64ADR(mnem string, page uint32, ops []*ast.Operand, pc int, offsets map[string]int) ([]byte, error) {
if len(ops) != 2 { if len(ops) != 2 {
return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops)) return nil, fmt.Errorf("%s expects 2 operands, got %d", mnem, len(ops))
} }
@@ -3830,7 +3885,10 @@ func encodeARM64ADR(mnem string, page uint32, ops []*ast.Operand, pc int, offset
} }
var rel int64 var rel int64
if _, pcRel := arm64PCRelOffset(ops[0]); !pcRel { if _, pcRel := arm64PCRelOffset(ops[0]); !pcRel {
target := resolve(arm64Label(ops[0])) // The label resolves to its own statement: the toolchain's
// jump-to-jump collapse rewrites p.To alone (obj/pass.go), so an
// ADR/ADRP's From-side label is never chased through a chain.
target := arm64Label(ops[0])
targetOff, ok := offsets[target] targetOff, ok := offsets[target]
if !ok { if !ok {
return nil, fmt.Errorf("undefined label %q", target) return nil, fmt.Errorf("undefined label %q", target)
@@ -5816,24 +5874,63 @@ func arm64ResolveAliases(f *ast.File) {
} }
aliases := map[string]alias{} aliases := map[string]alias{}
raws := map[string]string{} raws := map[string]string{}
// The directive lines the parser records include the dead branches of
// every conditional, and a define inside a disabled region must not
// become an alias (go_tls.h's `#ifdef GOARCH_arm \n #define LR R14`
// must stay dead on arm64, where LR is R30). The liveness walk mirrors
// the preprocessor's: a conditional is live when its name was defined
// by a live define earlier in the file; every other name is undefined,
// the predefines (GOARCH_arm64 and friends) included, which the
// assembler never sees.
seen := map[string]bool{} // every live-defined name, any macro shape
cond := []bool{} // one entry per open #ifdef/#ifndef: its truth
live := func() bool {
for _, c := range cond {
if !c {
return false
}
}
return true
}
for _, d := range f.Decls { for _, d := range f.Decls {
pre, ok := d.(*ast.Preproc) pre, ok := d.(*ast.Preproc)
if !ok { if !ok {
continue continue
} }
fields := strings.Fields(pre.Raw) fields := strings.Fields(pre.Raw)
if len(fields) < 3 || fields[0] != "define" { if len(fields) == 0 {
continue
}
switch fields[0] {
case "ifdef", "ifndef":
truth := false
if len(fields) >= 2 {
truth = seen[fields[1]] != (fields[0] == "ifndef")
}
cond = append(cond, truth)
case "else":
if len(cond) > 0 {
cond[len(cond)-1] = !cond[len(cond)-1]
}
case "endif":
if len(cond) > 0 {
cond = cond[:len(cond)-1]
}
case "define":
if !live() || len(fields) < 3 {
continue continue
} }
name, body := fields[1], strings.Join(fields[2:], " ") name, body := fields[1], strings.Join(fields[2:], " ")
// A parameterised macro spells its parameter list right after the seen[name] = true
// name; a multi-instruction body needs statement expansion. // A parameterised macro spells its parameter list right after
// the name; a multi-instruction body needs statement expansion.
if strings.ContainsAny(name, "(") || body == "" || if strings.ContainsAny(name, "(") || body == "" ||
strings.HasPrefix(body, "(") || strings.ContainsAny(body, "();") { strings.HasPrefix(body, "(") || strings.ContainsAny(body, "();") {
continue continue
} }
raws[name] = body raws[name] = body
} }
}
// Alias bodies may name other aliases (hlp1 → res_ptr → R0): substitute // Alias bodies may name other aliases (hlp1 → res_ptr → R0): substitute
// transitively until nothing changes, bounded against cycles. // transitively until nothing changes, bounded against cycles.
for range 8 { for range 8 {
+53
View File
@@ -403,6 +403,59 @@ func arm64ResolvePseudo(sym *ast.Symbol, fi arm64FrameInfo) (base int, off int32
return -1, 0 return -1, 0
} }
// arm64FrameAddrValue returns the SP-relative displacement a $sym+off(FP)
// or $sym+off(SP) immediate-address operand stands for, the toolchain's
// aclass arithmetic (asm7.go): a parameter reference sits autosize+8 above
// the hardware SP, and a pseudo-SP reference sits frame+8 above it, the
// alignment padding cancelling out of the autosize.
func arm64FrameAddrValue(sym *ast.Symbol, fi arm64FrameInfo) int64 {
switch sym.Pseudo {
case "FP":
return sym.Offset + int64(fi.autosize) + 8
default: // SP
return sym.Offset + int64(fi.frame) + 8
}
}
// arm64IsAddcon reports whether v is an addcon value (asm7.go isaddcon): an
// unsigned imm12, or a multiple of 4096 whose shifted form fits imm12.
func arm64IsAddcon(v int64) bool {
if v < 0 {
return false
}
if v&0xFFF == 0 {
v >>= 12
}
return v <= 0xFFF
}
// arm64FrameAddrWords returns the word sequence of the toolchain's optab
// case 4 for a frame-relative address (the C_AACON and C_AACON2 rows): one
// ADD/SUB imm12 word inside the addcon band, SUB carrying a negative
// displacement, and otherwise the hi<<12 word from SP followed by the low
// word added in place. The 24-bit band never reaches the REGTMP
// materialisation the ADD/SUB immediate ladder uses: aclass classifies the
// address straight into C_AACON2.
func arm64FrameAddrWords(v int64, rd int) []uint32 {
word := func(v int64, rn int) uint32 {
op := uint32(0) // ADD
if v < 0 {
op = 1 // SUB
v = -v
}
sh := uint32(0)
if v&0xFFF000 != 0 { // asm7.go oaddi: the shift form when low 12 bits are clear
sh = 1
v >>= 12
}
return a64AddSub(1, op, 0, sh, uint32(v), uint32(rn), uint32(rd))
}
if arm64IsAddcon(v) || arm64IsAddcon(-v) {
return []uint32{word(v, 31)}
}
return []uint32{word(v&^int64(0xFFF), 31), word(v&0xFFF, rd)}
}
// arm64PreStoreImm encodes a pre-index store (STR with writeback): // arm64PreStoreImm encodes a pre-index store (STR with writeback):
// size<<30 | 7<<27 | V<<26 | opc<<22 | 1<<11 | 1<<10 | imm9<<12 | Rn<<5 | Rt. // size<<30 | 7<<27 | V<<26 | opc<<22 | 1<<11 | 1<<10 | imm9<<12 | Rn<<5 | Rt.
func arm64PreStoreImm(size, V int, imm9 int32, rn, rt int) uint32 { func arm64PreStoreImm(size, V int, imm9 int32, rn, rt int) uint32 {
+236
View File
@@ -0,0 +1,236 @@
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
package asm
import (
"strings"
"testing"
"sourcedock.dev/petrbalvin/gasm-sdk/ast"
"sourcedock.dev/petrbalvin/gasm-sdk/parser"
)
// assembleArm64Source parses src and assembles it for arm64, returning the
// first function's words little-endian.
func assembleArm64Source(t *testing.T, src string) []uint32 {
t.Helper()
f, errs := parser.Parse("test_arm64.s", src)
if len(errs) > 0 {
t.Fatalf("parse: %v", errs)
}
img, err := AssembleFileARM64(f)
if err != nil {
t.Fatalf("AssembleFileARM64: %v", err)
}
if len(img.Funcs) != 1 {
t.Fatalf("got %d funcs, want 1", len(img.Funcs))
}
return wordsOf(img.Code[img.Funcs[0].Offset : img.Funcs[0].Offset+img.Funcs[0].Size])
}
func TestArm64FrameAddrEncoding(t *testing.T) {
src := `#include "textflag.h"
TEXT ·fr(SB), NOSPLIT, $432-24
MOVD $argframe+0(FP), R3
MOVD $big+4096(FP), R4
MOVD $ret-8(FP), R2
MOVD $x-64(FP), R5
MOVD RSP, R19
MOVD R20, RSP
RET
`
ws := assembleArm64Source(t, src)
// Prologue (4: large frame) then the body at words 4..9, the toolchain's
// own encodings for the same statements:
// ADD $456, RSP, R3 (456 = 448 + 8 + 0)
// ADD $(1<<12), RSP, R4 (4552, the hi<<12 half)
// ADD $456, R4, R4 (then the lo half)
// ADD $448, RSP, R2 (448 - 8 + 8)
// ADD $392, RSP, R5 (448 - 64 + 8)
// ADD $0, RSP, R19 (the SP register move)
// ADD $0, R20, RSP
want := []uint32{
0xd10703f4, 0xa93ffa9d, 0x9100029f, 0xd10023fd,
0x910723e3, 0x914007e4, 0x91072084, 0x910703e2,
0x910623e5, 0x910003f3, 0x9100029f,
}
if len(ws) < len(want) {
t.Fatalf("got %d words, want at least %d", len(ws), len(want))
}
for i, w := range want {
if ws[i] != w {
t.Errorf("word %d: got %08x, want %08x", i, ws[i], w)
}
}
}
func TestArm64FrameAddrSizes(t *testing.T) {
// One imm12 word inside the addcon band, two inside the 24-bit band.
tests := []struct {
v int64
verb int
}{
{456, 1},
{0xFFF, 1},
{0x1000, 1}, // the shifted imm12 form
{0x1005, 2}, // the hi<<12 plus lo pair
{0xFFFFFF, 2},
}
for _, tt := range tests {
got := len(arm64FrameAddrWords(tt.v, 3))
if got != tt.verb {
t.Errorf("arm64FrameAddrWords(%d) took %d words, want %d", tt.v, got, tt.verb)
}
}
if ws := arm64FrameAddrWords(0x1000, 3); len(ws) != 1 || ws[0] != 0x914007e3 {
t.Errorf("arm64FrameAddrWords(0x1000) = %08x, want the shifted ADD 914007e3", ws[0])
}
if !arm64IsAddcon(0xFFF) || arm64IsAddcon(0x1001) || arm64IsAddcon(-1) {
t.Error("arm64IsAddcon misclassifies the band edges")
}
fp := &ast.Symbol{Name: "x", Pseudo: "FP", Offset: 8}
if v := arm64FrameAddrValue(fp, arm64FrameInfo{autosize: 448}); v != 464 {
t.Errorf("FP address: got %d, want 464", v)
}
sp := &ast.Symbol{Name: "x", Pseudo: "SP", Offset: 8}
if v := arm64FrameAddrValue(sp, arm64FrameInfo{frame: 432}); v != 448 {
t.Errorf("SP address: got %d, want 448", v)
}
}
func TestArm64FrameAddrRejects(t *testing.T) {
tests := []struct {
name string
src string
want string
}{
{
"width", `TEXT ·f(SB), NOSPLIT, $16-8
MOVW $x+0(FP), R7
RET
`, "illegal combination",
},
{
"pool band", `TEXT ·f(SB), NOSPLIT, $20000000-8
MOVD $x+20000000(FP), R7
RET
`, "literal pool",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, errs := parser.Parse("test_arm64.s", tt.src)
if len(errs) > 0 {
t.Fatalf("parse: %v", errs)
}
_, err := AssembleFileARM64(f)
if err == nil {
t.Fatalf("%s: no error, want one naming %q", tt.name, tt.want)
}
if !strings.Contains(err.Error(), tt.want) {
t.Errorf("%s: error %q, want it to name %q", tt.name, err, tt.want)
}
})
}
}
func TestArm64SPMoveEncoding(t *testing.T) {
src := `TEXT ·f(SB), NOSPLIT, $0-8
MOVD RSP, R19
MOVD R20, RSP
MOVD ZR, R4
MOVD RSP, RSP
RET
`
ws := assembleArm64Source(t, src)
// The SP register moves ride ADD $0; the zero move stays ORR.
want := []uint32{
0x910003f3, // ADD $0, RSP, R19
0x9100029f, // ADD $0, R20, RSP
0xaa1f03e4, // ORR R4, ZR, ZR
0x910003ff, // ADD $0, RSP, RSP
0xd65f03c0, // RET
}
if len(ws) != len(want) {
t.Fatalf("got %d words, want %d", len(ws), len(want))
}
for i, w := range want {
if ws[i] != w {
t.Errorf("word %d: got %08x, want %08x", i, ws[i], w)
}
}
rej := `TEXT ·f(SB), NOSPLIT, $0-8
MOVW RSP, R7
RET
`
f, errs := parser.Parse("test_arm64.s", rej)
if len(errs) > 0 {
t.Fatalf("parse: %v", errs)
}
if _, err := AssembleFileARM64(f); err == nil || !strings.Contains(err.Error(), "illegal combination") {
t.Errorf("MOVW RSP: error %v, want an illegal-combination refusal", err)
}
}
func TestArm64AliasLiveness(t *testing.T) {
// A define inside a dead conditional branch must not become an alias:
// go_tls.h's `#ifdef GOARCH_arm` block defines LR as R14, which would
// silently renumber the link register on arm64, where LR is R30.
src := `#define RARG R5
#ifdef GOARCH_arm
#define LR R14
#endif
TEXT ·f(SB), NOSPLIT, $0-8
MOVD LR, R0
MOVD RARG, R1
MOVD R14, R2
RET
`
ws := assembleArm64Source(t, src)
want := []uint32{
0xaa1e03e0, // ORR R0, ZR, R30: LR stayed the link register
0xaa0503e1, // ORR R1, ZR, R5: the live alias applied
0xaa0e03e2, // ORR R2, ZR, R14: R14 is R14
0xd65f03c0,
}
if len(ws) != len(want) {
t.Fatalf("got %d words, want %d", len(ws), len(want))
}
for i, w := range want {
if ws[i] != w {
t.Errorf("word %d: got %08x, want %08x", i, ws[i], w)
}
}
}
func TestArm64ADRNoChainChase(t *testing.T) {
// The toolchain's jump-to-jump collapse rewrites branch targets only:
// a B to a chain-leading label is redirected, an ADR to the same label
// resolves to the label itself.
src := `TEXT ·adrchain(SB), NOSPLIT, $0-0
B a
a:
B b
b:
ADR a, R0
RET
`
ws := assembleArm64Source(t, src)
want := []uint32{
0x14000002, // B +2: chased through a to b
0x14000001, // B +1: a's own jump to b
0x10ffffe0, // ADR a, R0: -4, the label itself, unchased
0xd65f03c0,
}
if len(ws) != len(want) {
t.Fatalf("got %d words, want %d", len(ws), len(want))
}
for i, w := range want {
if ws[i] != w {
t.Errorf("word %d: got %08x, want %08x", i, ws[i], w)
}
}
}
+2 -7
View File
@@ -55,12 +55,9 @@ var gorootARM64Packages = []string{
// not silently dropped: the gaps are findings, and closing one is a matter // not silently dropped: the gaps are findings, and closing one is a matter
// of removing its entry and watching the file pin itself. // of removing its entry and watching the file pin itself.
var gorootARM64GapFiles = map[string]string{ var gorootARM64GapFiles = map[string]string{
"runtime/asm_arm64.s": "the unparenthesised NOSPLIT|NOFRAME flag list is not recognised, so the prologue and guard shapes diverge, and MOVD $sym+off(FP) materialises from ZR", "runtime/asm_arm64.s": "the unparenthesised NOSPLIT|NOFRAME flag list is not recognised, so the prologue and guard shapes diverge",
"runtime/asan_arm64.s": "MOVD $sym+off(FP) materialises MOV from ZR where the toolchain emits ADD $off, SP, Rd", "runtime/sys_linux_arm64.s": "the unparenthesised NOSPLIT|NOFRAME flag list is not recognised, so the prologue and guard shapes diverge (cgoSigtramp, clone)",
"runtime/libfuzzer_arm64.s": "MOVD $sym+off(FP) materialises MOV from ZR where the toolchain emits ADD $off, SP, Rd",
"runtime/msan_arm64.s": "MOVD $sym+off(FP) materialises MOV from ZR where the toolchain emits ADD $off, SP, Rd",
"runtime/tls_arm64.s": "a branch distance diverges: an earlier statement's expansion is sized differently", "runtime/tls_arm64.s": "a branch distance diverges: an earlier statement's expansion is sized differently",
"runtime/valgrind_arm64.s": "MOVD $sym+off(FP) materialises MOV from ZR where the toolchain emits ADD $off, SP, Rd",
"crypto/internal/fips140/aes/aes_arm64.s": "a tested immediate bit in a TBZ diverges", "crypto/internal/fips140/aes/aes_arm64.s": "a tested immediate bit in a TBZ diverges",
"crypto/internal/fips140/aes/gcm/gcm_arm64.s": "an ADD immediate's value diverges mid-function", "crypto/internal/fips140/aes/gcm/gcm_arm64.s": "an ADD immediate's value diverges mid-function",
"crypto/internal/fips140/nistec/p256_asm_arm64.s": "a branch distance and a logical-immediate lowering diverge", "crypto/internal/fips140/nistec/p256_asm_arm64.s": "a branch distance and a logical-immediate lowering diverge",
@@ -68,9 +65,7 @@ var gorootARM64GapFiles = map[string]string{
"runtime/preempt_arm64.s": "the prologue shape for the function's frame diverges at the first word", "runtime/preempt_arm64.s": "the prologue shape for the function's frame diverges at the first word",
"runtime/race_arm64.s": "the flag list and prologue shape diverge at the first words", "runtime/race_arm64.s": "the flag list and prologue shape diverge at the first words",
"runtime/rt0_linux_arm64.s": "the #ifdef GOOS selection diverges: gasm keeps a word the toolchain drops", "runtime/rt0_linux_arm64.s": "the #ifdef GOOS selection diverges: gasm keeps a word the toolchain drops",
"runtime/sys_linux_arm64.s": "MOVD $sym+off(FP) materialises MOV from ZR where the toolchain emits ADD $off, SP, Rd",
"internal/runtime/syscall/linux/asm_linux_arm64.s": "a branch distance diverges: an earlier statement's expansion is sized differently", "internal/runtime/syscall/linux/asm_linux_arm64.s": "a branch distance diverges: an earlier statement's expansion is sized differently",
"reflect/asm_arm64.s": "MOVD $sym+off(FP) materialises MOV from ZR where the toolchain emits ADD $off, SP, Rd",
} }
// gorootOtherGOOS matches the file names of the ports the linux build never // gorootOtherGOOS matches the file names of the ports the linux build never