feat(amd64): LOCK and REP prefixes, literal data pseudo-ops and ADJSP
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -67,6 +67,9 @@ type spadjStep struct {
|
||||
// patch sites (for the file-level layout to resolve), the label table and the
|
||||
// stack-adjustment boundaries.
|
||||
func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, []spadjStep, []LineEntry, error) {
|
||||
if err := checkAdjspBalance(t); err != nil {
|
||||
return nil, nil, nil, nil, nil, err
|
||||
}
|
||||
fi := computeFrame(t)
|
||||
chain := jumpChain(t)
|
||||
resolve := func(name string) string {
|
||||
@@ -203,6 +206,14 @@ func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, [
|
||||
spadjStep{guardLen + len(fi.prologue), 8 + fi.size},
|
||||
)
|
||||
}
|
||||
// frameBase is the SP delta the prologue leaves: 8 for the saved base
|
||||
// pointer plus the frame, 0 frameless. bodyDelta tracks the ADJSP
|
||||
// statements' straight-line sum, so a mid-body step's value is the
|
||||
// frame base plus what the body has opened so far.
|
||||
frameBase, bodyDelta := 0, 0
|
||||
if fi.useFP {
|
||||
frameBase = 8 + fi.size
|
||||
}
|
||||
pos := guardLen + len(fi.prologue)
|
||||
for i, stmt := range t.Body {
|
||||
s, ok := stmt.(*ast.Instr)
|
||||
@@ -230,6 +241,16 @@ func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, [
|
||||
ps[k].kind = RelCall
|
||||
}
|
||||
}
|
||||
if strings.ToUpper(s.Mnemonic.Text) == "ADJSP" && len(s.Operands) == 1 && s.Operands[0].Imm.HasVal {
|
||||
// The statement shifted SP mid-body: record the new running
|
||||
// delta as the value in effect from just past the instruction.
|
||||
v := s.Operands[0].Imm.Val
|
||||
if s.Operands[0].Imm.Neg {
|
||||
v = -v
|
||||
}
|
||||
bodyDelta += int(v)
|
||||
steps = append(steps, spadjStep{pos + len(code), frameBase + bodyDelta})
|
||||
}
|
||||
patches = append(patches, ps...)
|
||||
lines = append(lines, LineEntry{Offset: pos, Line: s.Pos().Line})
|
||||
out = append(out, code...)
|
||||
@@ -412,6 +433,40 @@ func hasCall(t *ast.Text) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// checkAdjspBalance mirrors the toolchain's push/pop walk: every ADJSP
|
||||
// shifts SP away from the entry state and every RET must see the shifts
|
||||
// closed. The assembler's own prologue and epilogue contribute matching
|
||||
// deltas on both sides, so the statements' straight-line sum must be zero
|
||||
// at each RET; branches do not reset the walk, which runs over the program
|
||||
// list in source order. go tool asm reports an offender as "unbalanced
|
||||
// PUSH/POP" (verified against ADJSP $16 before a RET, accepted as a
|
||||
// $16/$-16 pair, per-RET rather than per-function).
|
||||
func checkAdjspBalance(t *ast.Text) error {
|
||||
delta := 0
|
||||
for _, stmt := range t.Body {
|
||||
in, ok := stmt.(*ast.Instr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch strings.ToUpper(in.Mnemonic.Text) {
|
||||
case "ADJSP":
|
||||
if len(in.Operands) != 1 || !in.Operands[0].Imm.HasVal {
|
||||
continue // reported during emission
|
||||
}
|
||||
v := in.Operands[0].Imm.Val
|
||||
if in.Operands[0].Imm.Neg {
|
||||
v = -v
|
||||
}
|
||||
delta += int(v)
|
||||
case "RET":
|
||||
if delta != 0 {
|
||||
return fmt.Errorf("unbalanced PUSH/POP")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// guardLen returns the byte length of the stack-split guard prefix. The
|
||||
// final conditional branch (JBE, and JB in the big class) is 2 bytes in the
|
||||
// short form and 6 in the long form.
|
||||
|
||||
Reference in New Issue
Block a user