perf(vm): in-place compare ops + Int-Int fast path

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) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 21:00:45 +09:00
parent 15aa6dd4b3
commit 0fd3698ee5

View File

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