From 523d3fcf2e1f2eefd92ddc831d5e0983cc478a3d Mon Sep 17 00:00:00 2001 From: CharlesKWON Date: Sat, 18 Apr 2026 08:16:32 +0900 Subject: [PATCH] perf(vm): in-place Plus/Minus/Mult with tInt fast path Apply the sp-rewrite shape to the three binary arithmetic ops. The tInt==tInt fast branch reads scalar directly (skips the AsNumInt method) so the hot path is int64 ops + an overflow check; mixed-type branches keep AsNumDouble unchanged. PRG tight loops (FOR counter, SUM accumulators outside SQL aggregate path) skip one cachedNil store and two bounds-check sequences per op. 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_arith.go | 95 +++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/hbrt/ops_arith.go b/hbrt/ops_arith.go index 65ccbe4..2a8f179 100644 --- a/hbrt/ops_arith.go +++ b/hbrt/ops_arith.go @@ -20,47 +20,50 @@ import "math" // Date + Numeric -> Date // Timestamp + Numeric -> Timestamp func (t *Thread) Plus() { - 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 + dst := &t.stack[t.sp] - // Fast path: Int + Int - if a.IsNumInt() && b.IsNumInt() { - an, bn := a.AsNumInt(), b.AsNumInt() + // Fast path: Int + Int (tInt tag — skip tLong to stay branch-light) + if a.Type() == tInt && b.Type() == tInt { + an, bn := int64(a.scalar), int64(b.scalar) r := an + bn - // Overflow detection (Harbour pattern) if (bn >= 0 && r >= an) || (bn < 0 && r < an) { - t.push(MakeNumInt(r)) + *dst = MakeNumInt(r) } else { - t.push(MakeDoubleAuto(float64(an) + float64(bn))) + *dst = MakeDoubleAuto(float64(an) + float64(bn)) } + t.sp++ return } - // Numeric + Numeric -> Double if a.IsNumeric() && b.IsNumeric() { ad, bd := a.AsNumDouble(), b.AsNumDouble() dec := maxDec(a.Decimal(), b.Decimal()) - t.push(MakeDouble(ad+bd, 255, dec)) + *dst = MakeDouble(ad+bd, 255, dec) + t.sp++ return } - // String + String -> concatenation if a.IsString() && b.IsString() { - t.push(MakeString(a.AsString() + b.AsString())) + *dst = MakeString(a.AsString() + b.AsString()) + t.sp++ return } - // Date + Numeric -> Date (add days — truncate fractional) if a.IsDate() && b.IsNumeric() { - t.push(MakeDate(a.AsJulian() + int64(b.AsNumDouble()))) + *dst = MakeDate(a.AsJulian() + int64(b.AsNumDouble())) + t.sp++ return } if a.IsNumeric() && b.IsDate() { - t.push(MakeDate(int64(a.AsNumDouble()) + b.AsJulian())) + *dst = MakeDate(int64(a.AsNumDouble()) + b.AsJulian()) + t.sp++ return } - // Timestamp + Numeric -> Timestamp if a.IsTimestamp() && b.IsNumeric() { days := int64(b.AsNumDouble()) frac := b.AsNumDouble() - float64(days) @@ -74,7 +77,8 @@ func (t *Thread) Plus() { newJulian-- newTime += 86400000 } - t.push(MakeTimestamp(newJulian, newTime)) + *dst = MakeTimestamp(newJulian, newTime) + t.sp++ return } @@ -84,50 +88,53 @@ func (t *Thread) Plus() { // Minus pops two values, pushes their difference. // Harbour: hb_vmMinus (hvm.c:3401) func (t *Thread) Minus() { - 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 + dst := &t.stack[t.sp] - // Fast path: Int - Int - if a.IsNumInt() && b.IsNumInt() { - an, bn := a.AsNumInt(), b.AsNumInt() + if a.Type() == tInt && b.Type() == tInt { + an, bn := int64(a.scalar), int64(b.scalar) r := an - bn if (bn <= 0 && r >= an) || (bn > 0 && r < an) { - t.push(MakeNumInt(r)) + *dst = MakeNumInt(r) } else { - t.push(MakeDoubleAuto(float64(an) - float64(bn))) + *dst = MakeDoubleAuto(float64(an) - float64(bn)) } + t.sp++ return } - // Numeric - Numeric -> Double if a.IsNumeric() && b.IsNumeric() { ad, bd := a.AsNumDouble(), b.AsNumDouble() dec := maxDec(a.Decimal(), b.Decimal()) - t.push(MakeDouble(ad-bd, 255, dec)) + *dst = MakeDouble(ad-bd, 255, dec) + t.sp++ return } - // Date - Date -> Long (difference in days) if a.IsDate() && b.IsDate() { - t.push(MakeLong(a.AsJulian() - b.AsJulian())) + *dst = MakeLong(a.AsJulian() - b.AsJulian()) + t.sp++ return } - // Date - Numeric -> Date if a.IsDate() && b.IsNumeric() { - t.push(MakeDate(a.AsJulian() - int64(b.AsNumDouble()))) + *dst = MakeDate(a.AsJulian() - int64(b.AsNumDouble())) + t.sp++ return } - // Timestamp - Timestamp -> Double or Long if a.IsTimestamp() && b.IsTimestamp() { dayDiff := a.AsJulian() - b.AsJulian() timeDiff := a.AsTimeMs() - b.AsTimeMs() if timeDiff != 0 { - t.push(MakeDoubleAuto(float64(dayDiff) + float64(timeDiff)/86400000.0)) + *dst = MakeDoubleAuto(float64(dayDiff) + float64(timeDiff)/86400000.0) } else { - t.push(MakeLong(dayDiff)) + *dst = MakeLong(dayDiff) } + t.sp++ return } @@ -138,21 +145,26 @@ func (t *Thread) Minus() { // Harbour: hb_vmMult (hvm.c:3510) // Decimal rule: dec = dec1 + dec2 func (t *Thread) Mult() { - 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 + dst := &t.stack[t.sp] - if a.IsNumInt() && b.IsNumInt() { - an, bn := a.AsNumInt(), b.AsNumInt() + if a.Type() == tInt && b.Type() == tInt { + an, bn := int64(a.scalar), int64(b.scalar) if an == 0 || bn == 0 { - t.push(MakeNumInt(0)) + *dst = MakeNumInt(0) + t.sp++ return } r := an * bn if r/an == bn { - t.push(MakeNumInt(r)) + *dst = MakeNumInt(r) } else { - t.push(MakeDoubleAuto(float64(an) * float64(bn))) + *dst = MakeDoubleAuto(float64(an) * float64(bn)) } + t.sp++ return } @@ -162,7 +174,8 @@ func (t *Thread) Mult() { if dec > 255 { dec = 255 } - t.push(MakeDouble(ad*bd, 255, dec)) + *dst = MakeDouble(ad*bd, 255, dec) + t.sp++ return }