feat(asm): PCALIGN alignment on amd64
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+8
-7
@@ -22,13 +22,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
relocation, per-GOOS); arm64 accepts the bare-register indirect branch
|
||||
(`BL R9` beside `BL (R9)`, both BLR) and the zero-immediate store
|
||||
(`MOVD $0, mem` through the zero register, rejecting non-zero immediates
|
||||
as the toolchain does); and `gasm asm` predefines the `GOARCH_<arch>`
|
||||
and `GOOS_<goos>` macros the go command passes to `go tool asm`, so
|
||||
GOROOT headers' `#ifdef GOARCH_amd64` platform blocks (`go_tls.h`'s
|
||||
`get_tls` and friends) select as intended. The GOROOT corpus measure
|
||||
moves to 276 of 353 files assembling for every target architecture
|
||||
(78.2 %), 91.4 % of the real-code corpus (280 of 303), from 70.8 %
|
||||
and 82.2 %.
|
||||
as the toolchain does); `PCALIGN` now aligns on amd64, padding with the
|
||||
toolchain's greedy single-instruction NOPs; and `gasm asm` predefines
|
||||
the `GOARCH_<arch>` and `GOOS_<goos>` macros the go command passes to
|
||||
`go tool asm`, so GOROOT headers' `#ifdef GOARCH_amd64` platform blocks
|
||||
(`go_tls.h`'s `get_tls` and friends) select as intended. The GOROOT
|
||||
corpus measure moves to 285 of 353 files assembling for every target
|
||||
architecture (80.7 %), 91.4 % of the real-code corpus (280 of 303),
|
||||
from 70.8 % and 82.2 %.
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@@ -122,6 +122,19 @@ func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, [
|
||||
case *ast.Label:
|
||||
offsets[s.Name.Text] = pos
|
||||
case *ast.Instr:
|
||||
if strings.ToUpper(s.Mnemonic.Text) == "PCALIGN" {
|
||||
// The alignment pseudo-statement: its size is the
|
||||
// padding to the next boundary at this very position,
|
||||
// filled with NOPs at emission.
|
||||
pad, err := pcAlignPad(pcAlignValue(s), pos)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, fmt.Errorf("PCALIGN: %w", err)
|
||||
}
|
||||
sizes[i] = pad
|
||||
pcs[i] = pos
|
||||
pos += pad
|
||||
continue
|
||||
}
|
||||
sz, err := instrSize(s, fi, long[i], link)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", s.Mnemonic.Text, err)
|
||||
@@ -548,6 +561,52 @@ func pcJumpTarget(t *ast.Text, j, n int, pcs []int) (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// x86 NOP encodings, single-instruction no-ops of lengths 1 to 9 (the
|
||||
// toolchain's asm6.go nop table); longer padding repeats the largest that
|
||||
// fits, greedy from the end.
|
||||
var x86Nops = [][]byte{
|
||||
{0x90},
|
||||
{0x66, 0x90},
|
||||
{0x0F, 0x1F, 0x00},
|
||||
{0x0F, 0x1F, 0x40, 0x00},
|
||||
{0x0F, 0x1F, 0x44, 0x00, 0x00},
|
||||
{0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00},
|
||||
{0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00},
|
||||
{0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
{0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
}
|
||||
|
||||
// fillNOPs fills p with the greedy largest single-instruction NOPs, exactly
|
||||
// the toolchain's fillnop.
|
||||
func fillNOPs(p []byte) {
|
||||
for len(p) > 0 {
|
||||
m := min(len(p), len(x86Nops))
|
||||
copy(p[:m], x86Nops[m-1])
|
||||
p = p[m:]
|
||||
}
|
||||
}
|
||||
|
||||
// pcAlignPad computes the padding PCALIGN $align inserts at pos: the
|
||||
// alignment must be a power of two in [8, 2048] and the padding runs to the
|
||||
// next boundary (zero when the position is already aligned).
|
||||
func pcAlignPad(align, pos int) (int, error) {
|
||||
if align <= 0 || align&(align-1) != 0 || align < 8 || align > 2048 {
|
||||
return 0, fmt.Errorf("alignment value of an instruction must be a power of two and in the range [8, 2048], got %d", align)
|
||||
}
|
||||
if lob := pos & (align - 1); lob != 0 {
|
||||
return align - lob, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// pcAlignValue reads a PCALIGN statement's alignment operand.
|
||||
func pcAlignValue(s *ast.Instr) int {
|
||||
if len(s.Operands) == 1 && s.Operands[0].Kind == ast.OpImmediate && s.Operands[0].Imm.HasVal {
|
||||
return int(s.Operands[0].Imm.Val)
|
||||
}
|
||||
return 0 // rejected by pcAlignPad's range check
|
||||
}
|
||||
|
||||
// hasCall reports whether the function body contains a CALL instruction.
|
||||
func hasCall(t *ast.Text) bool {
|
||||
for _, stmt := range t.Body {
|
||||
@@ -760,6 +819,18 @@ func jumpSize(mnem string, long bool) int {
|
||||
func encodeInstr(s *ast.Instr, pc int, offsets map[string]int, fi frameInfo, long bool, resolve func(string) string, link *linkInfo, numTarget int) ([]byte, []sbPatch, []floatPoolEntry, error) {
|
||||
mnem := strings.ToUpper(s.Mnemonic.Text)
|
||||
|
||||
if mnem == "PCALIGN" {
|
||||
// The layout pass already accounted the padding; emit the same
|
||||
// amount of NOP bytes for the statement's own position.
|
||||
pad, err := pcAlignPad(pcAlignValue(s), pc)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
out := make([]byte, pad)
|
||||
fillNOPs(out)
|
||||
return out, nil, nil, nil
|
||||
}
|
||||
|
||||
var prefix []byte
|
||||
if mnem == "RET" && fi.useFP {
|
||||
prefix = fi.epilogue
|
||||
|
||||
Vendored
+9
@@ -41,3 +41,12 @@ TEXT ·Tls(SB), NOSPLIT, $0-8
|
||||
MOVQ 0(BX)(TLS*1), AX
|
||||
MOVQ AX, ret+0(FP)
|
||||
RET
|
||||
|
||||
// func Aligned() int64
|
||||
TEXT ·Aligned(SB), NOSPLIT, $0-8
|
||||
MOVQ $1, AX
|
||||
PCALIGN $16
|
||||
MOVQ $2, AX
|
||||
PCALIGN $32
|
||||
MOVQ AX, ret+0(FP)
|
||||
RET
|
||||
|
||||
Reference in New Issue
Block a user