From 0fd3698ee5e60d9443ea9d0d33c6999dd2cc886a Mon Sep 17 00:00:00 2001 From: CharlesKWON Date: Fri, 17 Apr 2026 21:00:45 +0900 Subject: [PATCH] perf(vm): in-place compare ops + Int-Int fast path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the treatment LessEqual already got onto Equal/NotEqual/Less/ Greater/GreaterEqual — rewrite sp directly, check the type tag for Int==Int in the hot branch, short-circuit to cachedTrue/cachedFalse without a second method call. Keeps the slow fallback for mixed / string / date types. Bench movement is minor on SQL paths (WHERE is already pcode and skips these ops); the win is on PRG comparisons that cache-miss out of the pcode path — FOR-condition short forms, IF chains, etc. Verification - go test ./... ALL PASS - FiveSql2 test_sql1999 43/43 - tests/compat_harbour 56/56 Co-Authored-By: Claude Opus 4.7 (1M context) --- hbrt/ops_compare.go | 111 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 15 deletions(-) diff --git a/hbrt/ops_compare.go b/hbrt/ops_compare.go index 9f6693c..8982c83 100644 --- a/hbrt/ops_compare.go +++ b/hbrt/ops_compare.go @@ -22,9 +22,26 @@ import "strings" // Equal pops two values, pushes boolean result. // Harbour: hb_vmEqual (hvm.c:3974) func (t *Thread) Equal() { - b := t.pop() - a := t.pop() - t.push(MakeBool(valueEqual(a, b))) + t.sp -= 2 + a := t.stack[t.sp] + b := t.stack[t.sp+1] + t.stack[t.sp+1] = cachedNil + // Fast path: Int == Int + if a.Type() == tInt && b.Type() == tInt { + if int64(a.scalar) == int64(b.scalar) { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ + return + } + if valueEqual(a, b) { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ } // ExactEqual pops two values, pushes boolean result. @@ -37,9 +54,25 @@ func (t *Thread) ExactEqual() { // NotEqual pops two values, pushes boolean result. func (t *Thread) NotEqual() { - b := t.pop() - a := t.pop() - t.push(MakeBool(!valueEqual(a, b))) + t.sp -= 2 + a := t.stack[t.sp] + b := t.stack[t.sp+1] + t.stack[t.sp+1] = cachedNil + if a.Type() == tInt && b.Type() == tInt { + if int64(a.scalar) != int64(b.scalar) { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ + return + } + if !valueEqual(a, b) { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ } // --- Relational operators --- @@ -47,13 +80,29 @@ func (t *Thread) NotEqual() { // Less pops two values, pushes boolean result. // Harbour: hb_vmLess (hvm.c:4176) func (t *Thread) Less() { - b := t.pop() - a := t.pop() + t.sp -= 2 + a := t.stack[t.sp] + b := t.stack[t.sp+1] + t.stack[t.sp+1] = cachedNil + if a.Type() == tInt && b.Type() == tInt { + if int64(a.scalar) < int64(b.scalar) { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ + return + } cmp, ok := valueCompare(a, b) if !ok { panic(t.argError("<", a, b)) } - t.push(MakeBool(cmp < 0)) + if cmp < 0 { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ } // LessEqual pops two values, pushes boolean result. @@ -86,24 +135,56 @@ func (t *Thread) LessEqual() { // Greater pops two values, pushes boolean result. func (t *Thread) Greater() { - b := t.pop() - a := t.pop() + t.sp -= 2 + a := t.stack[t.sp] + b := t.stack[t.sp+1] + t.stack[t.sp+1] = cachedNil + if a.Type() == tInt && b.Type() == tInt { + if int64(a.scalar) > int64(b.scalar) { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ + return + } cmp, ok := valueCompare(a, b) if !ok { panic(t.argError(">", a, b)) } - t.push(MakeBool(cmp > 0)) + if cmp > 0 { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ } // GreaterEqual pops two values, pushes boolean result. func (t *Thread) GreaterEqual() { - b := t.pop() - a := t.pop() + t.sp -= 2 + a := t.stack[t.sp] + b := t.stack[t.sp+1] + t.stack[t.sp+1] = cachedNil + if a.Type() == tInt && b.Type() == tInt { + if int64(a.scalar) >= int64(b.scalar) { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ + return + } cmp, ok := valueCompare(a, b) if !ok { panic(t.argError(">=", a, b)) } - t.push(MakeBool(cmp >= 0)) + if cmp >= 0 { + t.stack[t.sp] = cachedTrue + } else { + t.stack[t.sp] = cachedFalse + } + t.sp++ } // --- Logical operators ---