perf: fused opcodes + inline EOF/BOF/Found/RecNo/Deleted
Fused opcodes (ops_compare.go): - LocalLessEqualInt: FOR i<=N without Push+LessEqual+PopLogical - LocalGreaterEqualInt: FOR STEP -1 - Direct local access + int comparison (no stack, no Value boxing) gengo FOR loop: - Detects literal TO value → emits LocalLessEqualInt (3 calls → 1) - Falls back to stack-based for variable limits Inline RDD functions (gengo tryEmitInlineRTL): - EOF/BOF/Found/Deleted/RecNo/RecCount: direct area method call - No FindSymbol + PushNil + Do(0) + Frame/EndProc overhead - Uses hoisted _darea when inside DO WHILE context Results (50K, ext4): SEEK random: 63ms (Harbour 67ms — FASTER!) SEEK seq: 44ms (Harbour 27ms — 1.6x) CDX SEEK NAME: 47ms (Harbour 27ms — 1.7x) CDX SEEK ID: 24ms (Harbour 17ms — 1.4x) All counts correct. 82/82 stress PASS. 14 packages ALL PASS. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1083,15 +1083,24 @@ func (g *Generator) emitFor(s *ast.ForStmt, locals localMap) {
|
||||
g.writeln("for {")
|
||||
g.indent++
|
||||
|
||||
// Comparison: ascending → i <= to, descending → i >= to
|
||||
g.writeln(fmt.Sprintf("t.PushLocal(%d)", idx))
|
||||
g.emitExpr(s.To)
|
||||
if isNegStep {
|
||||
g.writeln("t.GreaterEqual()")
|
||||
// Comparison: fused opcode when limit is literal int (most common)
|
||||
if lit, ok := s.To.(*ast.LiteralExpr); ok && lit.Kind == token.INT {
|
||||
if isNegStep {
|
||||
g.writeln(fmt.Sprintf("if !t.LocalGreaterEqualInt(%d, %s) { break }", idx, lit.Value))
|
||||
} else {
|
||||
g.writeln(fmt.Sprintf("if !t.LocalLessEqualInt(%d, %s) { break }", idx, lit.Value))
|
||||
}
|
||||
} else {
|
||||
g.writeln("t.LessEqual()")
|
||||
// General case: stack-based comparison
|
||||
g.writeln(fmt.Sprintf("t.PushLocal(%d)", idx))
|
||||
g.emitExpr(s.To)
|
||||
if isNegStep {
|
||||
g.writeln("t.GreaterEqual()")
|
||||
} else {
|
||||
g.writeln("t.LessEqual()")
|
||||
}
|
||||
g.writeln("if !t.PopLogical() { break }")
|
||||
}
|
||||
g.writeln("if !t.PopLogical() { break }")
|
||||
|
||||
// body
|
||||
for _, stmt := range s.Body {
|
||||
@@ -1618,6 +1627,56 @@ func (g *Generator) tryEmitInlineRTL(name string, args []ast.Expr) bool {
|
||||
g.writeln("{ _s := t.Pop2().AsString(); if len(_s)>0 { t.PushInt(int(_s[0])) } else { t.PushInt(0) } }")
|
||||
return true
|
||||
}
|
||||
case "EOF":
|
||||
if len(args) == 0 {
|
||||
if g.hoistedDW {
|
||||
g.writeln(fmt.Sprintf("t.PushBool(%s != nil && %s.EOF())", g.hoistedAreaVar(), g.hoistedAreaVar()))
|
||||
} else {
|
||||
g.writeln("{ _wa := t.WA.(*hbrdd.WorkAreaManager); if _a := _wa.Current(); _a != nil { t.PushBool(_a.EOF()) } else { t.PushBool(true) } }")
|
||||
}
|
||||
return true
|
||||
}
|
||||
case "BOF":
|
||||
if len(args) == 0 {
|
||||
if g.hoistedDW {
|
||||
g.writeln(fmt.Sprintf("t.PushBool(%s != nil && %s.BOF())", g.hoistedAreaVar(), g.hoistedAreaVar()))
|
||||
} else {
|
||||
g.writeln("{ _wa := t.WA.(*hbrdd.WorkAreaManager); if _a := _wa.Current(); _a != nil { t.PushBool(_a.BOF()) } else { t.PushBool(true) } }")
|
||||
}
|
||||
return true
|
||||
}
|
||||
case "FOUND":
|
||||
if len(args) == 0 {
|
||||
if g.hoistedDW {
|
||||
g.writeln(fmt.Sprintf("t.PushBool(%s != nil && %s.Found())", g.hoistedAreaVar(), g.hoistedAreaVar()))
|
||||
} else {
|
||||
g.writeln("{ _wa := t.WA.(*hbrdd.WorkAreaManager); if _a := _wa.Current(); _a != nil { t.PushBool(_a.Found()) } else { t.PushBool(false) } }")
|
||||
}
|
||||
return true
|
||||
}
|
||||
case "RECNO":
|
||||
if len(args) == 0 {
|
||||
if g.hoistedDW {
|
||||
g.writeln(fmt.Sprintf("if %s != nil { t.PushInt(int(%s.RecNo())) } else { t.PushInt(0) }", g.hoistedAreaVar(), g.hoistedAreaVar()))
|
||||
} else {
|
||||
g.writeln("{ _wa := t.WA.(*hbrdd.WorkAreaManager); if _a := _wa.Current(); _a != nil { t.PushInt(int(_a.RecNo())) } else { t.PushInt(0) } }")
|
||||
}
|
||||
return true
|
||||
}
|
||||
case "DELETED":
|
||||
if len(args) == 0 {
|
||||
if g.hoistedDW {
|
||||
g.writeln(fmt.Sprintf("t.PushBool(%s != nil && %s.Deleted())", g.hoistedAreaVar(), g.hoistedAreaVar()))
|
||||
} else {
|
||||
g.writeln("{ _wa := t.WA.(*hbrdd.WorkAreaManager); if _a := _wa.Current(); _a != nil { t.PushBool(_a.Deleted()) } else { t.PushBool(false) } }")
|
||||
}
|
||||
return true
|
||||
}
|
||||
case "RECCOUNT":
|
||||
if len(args) == 0 {
|
||||
g.writeln("{ _wa := t.WA.(*hbrdd.WorkAreaManager); if _a := _wa.Current(); _a != nil { _rc, _ := _a.RecCount(); t.PushInt(int(_rc)) } else { t.PushInt(0) } }")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func TestGenerateForNext(t *testing.T) {
|
||||
`)
|
||||
assertContains(t, code, "t.Frame(0, 2)")
|
||||
assertContains(t, code, "for {")
|
||||
assertContains(t, code, "t.LessEqual()")
|
||||
assertContains(t, code, "LocalLessEqualInt(")
|
||||
assertContains(t, code, "t.LocalAdd(") // nSum += i
|
||||
assertContains(t, code, "t.LocalAddInt(") // i += 1
|
||||
}
|
||||
|
||||
@@ -172,6 +172,35 @@ func (t *Thread) PopLogical() bool {
|
||||
panic(t.argError("logical", v))
|
||||
}
|
||||
|
||||
// --- Fused opcodes: combined stack operations for hot loops ---
|
||||
// Eliminates 3-4 function calls per FOR iteration.
|
||||
|
||||
// LocalLessEqualInt: t.Local(idx) <= val (no stack ops)
|
||||
func (t *Thread) LocalLessEqualInt(localIdx, val int) bool {
|
||||
idx := t.curFrame.localBase + localIdx - 1
|
||||
v := t.locals[idx]
|
||||
if v.Type() == tInt {
|
||||
return int64(v.scalar) <= int64(val)
|
||||
}
|
||||
if v.Type() == tDouble {
|
||||
return v.AsNumDouble() <= float64(val)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LocalGreaterEqualInt: t.Local(idx) >= val (for descending FOR)
|
||||
func (t *Thread) LocalGreaterEqualInt(localIdx, val int) bool {
|
||||
idx := t.curFrame.localBase + localIdx - 1
|
||||
v := t.locals[idx]
|
||||
if v.Type() == tInt {
|
||||
return int64(v.scalar) >= int64(val)
|
||||
}
|
||||
if v.Type() == tDouble {
|
||||
return v.AsNumDouble() >= float64(val)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// --- Optimized comparison (used by generated code) ---
|
||||
|
||||
// EqualIntIs compares stack top with an integer constant, returns bool.
|
||||
|
||||
Reference in New Issue
Block a user