diff --git a/compiler/gengo/gengo.go b/compiler/gengo/gengo.go index 70265d4..34cc52e 100644 --- a/compiler/gengo/gengo.go +++ b/compiler/gengo/gengo.go @@ -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 } diff --git a/compiler/gengo/gengo_test.go b/compiler/gengo/gengo_test.go index 37d6517..301d241 100644 --- a/compiler/gengo/gengo_test.go +++ b/compiler/gengo/gengo_test.go @@ -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 } diff --git a/hbrt/ops_compare.go b/hbrt/ops_compare.go index 0889d90..82919d5 100644 --- a/hbrt/ops_compare.go +++ b/hbrt/ops_compare.go @@ -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.