feat(debug): complete the interactive debugger with disassembly, breakpoints, watchpoints and execution control

Assisted-by: DeepSeek V4 Pro
This commit is contained in:
2026-08-01 09:47:00 +02:00
parent c77d68018c
commit d114b3412c
9 changed files with 922 additions and 34 deletions
+8 -6
View File
@@ -23,7 +23,7 @@ import (
// operands require relocations and are not yet supported; the SIMD (VEX/AVX2)
// integer and shuffle/extract/permute/move set is in.
func Assemble(t *ast.Text) ([]byte, map[string]int, error) {
code, _, labels, _, err := assemble(t, nil)
code, _, labels, _, _, err := assemble(t, nil)
return code, labels, err
}
@@ -60,7 +60,7 @@ type spadjStep struct {
// assemble encodes a TEXT body, returning the machine code, the static-symbol
// 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, error) {
func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, []spadjStep, []LineEntry, error) {
fi := computeFrame(t)
chain := jumpChain(t)
resolve := func(name string) string {
@@ -84,7 +84,7 @@ func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, [
case *ast.Instr:
sz, err := instrSize(s, fi, long[i], link)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("%s: %w", s.Mnemonic.Text, err)
return nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", s.Mnemonic.Text, err)
}
sizes[i] = sz
pcs[i] = pos
@@ -125,6 +125,7 @@ func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, [
out := append([]byte(nil), fi.prologue...)
var patches []sbPatch
var steps []spadjStep
var lines []LineEntry
if fi.useFP {
// PUSHQ BP saves the return-address-relative base (+8); the MOVQ
// changes nothing; SUBQ $size, SP completes the frame.
@@ -150,16 +151,17 @@ func assemble(t *ast.Text, link *linkInfo) ([]byte, []sbPatch, map[string]int, [
}
code, ps, err := encodeInstr(s, pos, offsets, fi, long[i], resolve, link)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("%s: %w", s.Mnemonic.Text, err)
return nil, nil, nil, nil, nil, fmt.Errorf("%s: %w", s.Mnemonic.Text, err)
}
if len(code) != sizes[i] {
return nil, nil, nil, nil, fmt.Errorf("%s: size mismatch (%d vs %d)", s.Mnemonic.Text, len(code), sizes[i])
return nil, nil, nil, nil, nil, fmt.Errorf("%s: size mismatch (%d vs %d)", s.Mnemonic.Text, len(code), sizes[i])
}
patches = append(patches, ps...)
lines = append(lines, LineEntry{Offset: pos, Line: s.Pos().Line})
out = append(out, code...)
pos += len(code)
}
return out, patches, offsets, steps, nil
return out, patches, offsets, steps, lines, nil
}
// jumpChain precomputes jump-to-jump folding: a label whose first instruction
+32 -1
View File
@@ -41,6 +41,7 @@ type FuncLayout struct {
Labels map[string]int // local labels, function-relative
Relocs []Reloc // static-symbol references, in emission order
Spadj []SpadjStep // stack-adjustment boundaries, ascending by PC
Lines []LineEntry // source-line table: byte offset → source line
}
// SpadjStep is one stack-adjustment boundary: Value is the SP delta from the
@@ -50,6 +51,35 @@ type SpadjStep struct {
Value int
}
// LineEntry maps a byte offset (function-relative) to a source line number.
type LineEntry struct {
Offset int
Line int
}
// LineAt returns the source line number for the given function-relative byte
// offset, using a binary search on the line table. Returns 0 if the offset
// is before the first instruction or the table is empty.
func (fl *FuncLayout) LineAt(offset int) int {
if len(fl.Lines) == 0 {
return 0
}
// Binary search: find the last entry with Offset <= offset.
lo, hi := 0, len(fl.Lines)-1
for lo < hi {
mid := (lo + hi + 1) / 2
if fl.Lines[mid].Offset <= offset {
lo = mid
} else {
hi = mid - 1
}
}
if fl.Lines[lo].Offset <= offset {
return fl.Lines[lo].Line
}
return 0
}
// Reloc is one static-symbol reference within a function body: the disp32
// field at Off (function-relative) must reach the symbol plus Addend,
// measured from After, the address just past the instruction. An External
@@ -110,7 +140,7 @@ func AssembleFile(f *ast.File) (*Image, error) {
if !ok {
continue
}
code, patches, labels, steps, err := assemble(t, link)
code, patches, labels, steps, lines, err := assemble(t, link)
if err != nil {
return nil, fmt.Errorf("%s: %w", t.Name.Name, err)
}
@@ -124,6 +154,7 @@ func AssembleFile(f *ast.File) (*Image, error) {
Args: argsSize(t),
Line: t.Pos().Line,
Labels: labels,
Lines: lines,
}
for _, f := range t.Flags {
switch f {