feat(asm): PCALIGN alignment on amd64
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user