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:
2026-04-07 22:07:34 +09:00
parent ad1bc23e36
commit 44d3c7385c
3 changed files with 96 additions and 8 deletions

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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.