fix(parser): bound folding depth and macro expansion work
Assisted-by: GLM 5.3
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/ast"
|
||||
)
|
||||
@@ -58,6 +59,64 @@ func wantLines(t *testing.T, got []string, want ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestManyInvocationsExpandInLinearTime pins the cost contract of macro
|
||||
// expansion: a long run of invocations costs work proportional to what it
|
||||
// produces. Splicing each expansion into one flat slice instead made every
|
||||
// invocation shift the whole remaining tail, so the 200000 invocations here
|
||||
// took minutes; the pending stream is a stack of frames since, measured at
|
||||
// roughly 120 ms on this machine, and ten seconds stays a generous ceiling
|
||||
// even on a runner an order of magnitude slower.
|
||||
func TestManyInvocationsExpandInLinearTime(t *testing.T) {
|
||||
const n = 200000
|
||||
src := "#define A MOVQ AX, BX\nTEXT ·f(SB), NOSPLIT, $0\n\t" +
|
||||
strings.Repeat("A\n", n) + "\tRET\n"
|
||||
start := time.Now()
|
||||
f, errs := ParseWithOptions("t_amd64.s", src, Options{Expand: true})
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse: %v", errs)
|
||||
}
|
||||
var ts []*ast.Text
|
||||
for _, d := range f.Decls {
|
||||
if text, ok := d.(*ast.Text); ok {
|
||||
ts = append(ts, text)
|
||||
}
|
||||
}
|
||||
if len(ts) != 1 {
|
||||
t.Fatalf("got %d TEXT declarations, want 1", len(ts))
|
||||
}
|
||||
if len(ts[0].Body) != n+1 {
|
||||
t.Fatalf("got %d body statements, want %d", len(ts[0].Body), n+1)
|
||||
}
|
||||
if instr, ok := ts[0].Body[0].(*ast.Instr); !ok || instr.Mnemonic.Text != "MOVQ" {
|
||||
t.Fatalf("first body statement is not the expansion: %#v", ts[0].Body[0])
|
||||
}
|
||||
if elapsed := time.Since(start); elapsed > 10*time.Second {
|
||||
t.Fatalf("expansion of %d invocations took %v, want the linear-cost contract", n, elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAmplifyingMacrosStopAtBudget pins the work budget of expansion: a body
|
||||
// that repeats its argument multiplies every level of nesting, so a short
|
||||
// line of nested invocations names exponentially many tokens. The expansion
|
||||
// must stop at the budget with a diagnostic instead of committing the
|
||||
// machine to producing them.
|
||||
func TestAmplifyingMacrosStopAtBudget(t *testing.T) {
|
||||
src := "#define A(x) x x x x x x x x x x\n" +
|
||||
"A(A(A(A(A(A(A(A(A(A(1))))))))))\n"
|
||||
f, errs := ParseWithOptions("t_amd64.s", src, Options{Expand: true})
|
||||
if len(errs) == 0 {
|
||||
t.Fatal("amplifying macro expansion reported no error")
|
||||
}
|
||||
if !strings.Contains(errs[0].Error(), "work budget") {
|
||||
t.Fatalf("error is not the budget diagnostic: %v", errs[0])
|
||||
}
|
||||
for _, d := range f.Decls {
|
||||
if _, ok := d.(*ast.Text); ok {
|
||||
t.Fatal("amplified line produced a TEXT declaration")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjectMacroExpandsAtUse(t *testing.T) {
|
||||
_, got := expand(t, `
|
||||
#define REGTMP CX
|
||||
|
||||
Reference in New Issue
Block a user