perf(vm): in-place And/Or with logical-only fast path

Fold And/Or into the same in-place sp-rewrite shape as Not/LessEqual.
Both args must be tLogical — short-circuit on the raw scalar field so
the hot path is pure integer arithmetic + two cached bool Values.

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-18 03:16:01 +09:00
parent 0fd3698ee5
commit 54b35f9325

View File

@@ -209,22 +209,38 @@ func (t *Thread) Not() {
// And pops two values, pushes logical AND.
// Harbour evaluates both sides (no short-circuit in VM ops).
func (t *Thread) And() {
b := t.pop()
a := t.pop()
if !a.IsLogical() || !b.IsLogical() {
panic(t.argError(".AND.", 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() == tLogical && b.Type() == tLogical {
if a.scalar != 0 && b.scalar != 0 {
t.stack[t.sp] = cachedTrue
} else {
t.stack[t.sp] = cachedFalse
}
t.sp++
return
}
t.push(MakeBool(a.AsBool() && b.AsBool()))
panic(t.argError(".AND.", a, b))
}
// Or pops two values, pushes logical OR.
func (t *Thread) Or() {
b := t.pop()
a := t.pop()
if !a.IsLogical() || !b.IsLogical() {
panic(t.argError(".OR.", 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() == tLogical && b.Type() == tLogical {
if a.scalar != 0 || b.scalar != 0 {
t.stack[t.sp] = cachedTrue
} else {
t.stack[t.sp] = cachedFalse
}
t.sp++
return
}
t.push(MakeBool(a.AsBool() || b.AsBool()))
panic(t.argError(".OR.", a, b))
}
// InString implements the $ operator: "bc" $ "abcde" → .T.