perf: inline RTL + symbol cache infrastructure + EndProcFast
gengo inline RTL (tryEmitInlineRTL): - LTrim/RTrim/AllTrim/Upper/Lower/Len/Empty/Chr/Asc - Skip Frame/EndProc/VM dispatch entirely - Emit direct Go code (strings.TrimLeft, etc.) Symbol cache infrastructure (collectSymbols): - AST walker collects all referenced symbol names - symCache field ready for future per-function hoisting - Currently disabled (function-level hoisting caused side effects) NTX TestGetMmap helper for profiling. 82/82 stress PASS. 14 packages ALL PASS. 50K SEEK random: 64-66ms (Harbour 67ms — equal or faster) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,7 @@ type Generator struct {
|
||||
IsLibrary bool // if true, no main() generated, symbols use unique name
|
||||
hoistedFields []string // field names hoisted outside FOR loop (nil = not hoisting)
|
||||
hoistedDW bool // DO WHILE has hoisted _dwa/_darea
|
||||
symCache map[string]string // symbol name → cached variable name (nil = not caching)
|
||||
Debug bool // if true, emit t.DebugLine() calls
|
||||
}
|
||||
|
||||
@@ -922,6 +923,73 @@ func hasWorkareaChange(stmts []ast.Stmt) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// collectSymbols scans AST for all symbol names referenced by function calls.
|
||||
// Returns unique names for hoisting FindSymbol to function prologue.
|
||||
func collectSymbols(stmts []ast.Stmt) []string {
|
||||
seen := map[string]bool{}
|
||||
var names []string
|
||||
var walk func([]ast.Stmt)
|
||||
var walkExpr func(ast.Expr)
|
||||
|
||||
walkExpr = func(e ast.Expr) {
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
switch v := e.(type) {
|
||||
case *ast.CallExpr:
|
||||
if ident, ok := v.Func.(*ast.IdentExpr); ok {
|
||||
name := strings.ToUpper(ident.Name)
|
||||
if !seen[name] {
|
||||
seen[name] = true
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
for _, a := range v.Args {
|
||||
walkExpr(a)
|
||||
}
|
||||
case *ast.BinaryExpr:
|
||||
walkExpr(v.Left)
|
||||
walkExpr(v.Right)
|
||||
case *ast.UnaryExpr:
|
||||
walkExpr(v.X)
|
||||
}
|
||||
}
|
||||
|
||||
walk = func(stmts []ast.Stmt) {
|
||||
for _, s := range stmts {
|
||||
switch v := s.(type) {
|
||||
case *ast.ExprStmt:
|
||||
walkExpr(v.X)
|
||||
case *ast.ReturnStmt:
|
||||
if v.Value != nil {
|
||||
walkExpr(v.Value)
|
||||
}
|
||||
case *ast.IfStmt:
|
||||
walkExpr(v.Cond)
|
||||
walk(v.Body)
|
||||
walk(v.ElseBody)
|
||||
case *ast.ForStmt:
|
||||
walk(v.Body)
|
||||
case *ast.ForEachStmt:
|
||||
walk(v.Body)
|
||||
case *ast.DoWhileStmt:
|
||||
walkExpr(v.Cond)
|
||||
walk(v.Body)
|
||||
case *ast.SeqStmt:
|
||||
walk(v.Body)
|
||||
walk(v.RecoverBody)
|
||||
case *ast.SwitchStmt:
|
||||
for _, c := range v.Cases {
|
||||
walk(c.Body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(stmts)
|
||||
return names
|
||||
}
|
||||
|
||||
// collectReplaceFields scans statements for REPLACE field names.
|
||||
// Returns nil if unsafe to hoist (USE/SELECT/CLOSE found).
|
||||
func collectReplaceFields(stmts []ast.Stmt) []string {
|
||||
@@ -1464,7 +1532,16 @@ func (g *Generator) emitCall(e *ast.CallExpr) {
|
||||
if g.tryEmitInlineRTL(ident.Name, e.Args) {
|
||||
return
|
||||
}
|
||||
g.writeln(fmt.Sprintf("t.PushSymbol(t.VM().FindSymbol(%q))", strings.ToUpper(ident.Name)))
|
||||
upper := strings.ToUpper(ident.Name)
|
||||
if g.symCache != nil {
|
||||
if varName, ok := g.symCache[upper]; ok {
|
||||
g.writeln(fmt.Sprintf("t.PushSymbol(%s)", varName))
|
||||
} else {
|
||||
g.writeln(fmt.Sprintf("t.PushSymbol(t.VM().FindSymbol(%q))", upper))
|
||||
}
|
||||
} else {
|
||||
g.writeln(fmt.Sprintf("t.PushSymbol(t.VM().FindSymbol(%q))", upper))
|
||||
}
|
||||
} else {
|
||||
g.emitExpr(e.Func)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user