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) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 08:16:32 +09:00
parent 54b35f9325
commit 523d3fcf2e

View File

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