Compare commits
9 Commits
fd2da1efd3
...
7f2f99524f
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f2f99524f | |||
| bfa8a59df3 | |||
| 09b1eb68ae | |||
| fdb23b3a76 | |||
| 7c28296f92 | |||
| 3e654ddd60 | |||
| e8f5c9eaf4 | |||
| 44f9573eec | |||
| a8f6aa31f1 |
@ -86,6 +86,7 @@ class BytecodeCompiler(
|
||||
private val loopVarSlots = HashSet<Int>()
|
||||
private val loopStack = ArrayDeque<LoopContext>()
|
||||
private var currentPos: Pos? = null
|
||||
private var cachedVoidSlot: Int? = null
|
||||
|
||||
private data class LoopContext(
|
||||
val label: String?,
|
||||
@ -1374,23 +1375,6 @@ class BytecodeCompiler(
|
||||
val rightRef = binaryRight(ref)
|
||||
var a = compileRefWithFallback(leftRef, null, refPos(ref)) ?: return null
|
||||
var b = compileRefWithFallback(rightRef, null, refPos(ref)) ?: return null
|
||||
if (op in setOf(BinOp.PLUS, BinOp.MINUS, BinOp.STAR, BinOp.SLASH, BinOp.PERCENT)) {
|
||||
val leftNeedsObj = a.type == SlotType.INT && b.type == SlotType.REAL
|
||||
val rightNeedsObj = b.type == SlotType.INT && a.type == SlotType.REAL
|
||||
if (leftNeedsObj || rightNeedsObj) {
|
||||
val leftObj = if (leftNeedsObj) {
|
||||
compileScopeSlotObj(leftRef) ?: a
|
||||
} else {
|
||||
a
|
||||
}
|
||||
val rightObj = if (rightNeedsObj) {
|
||||
compileScopeSlotObj(rightRef) ?: b
|
||||
} else {
|
||||
b
|
||||
}
|
||||
return compileObjBinaryOp(leftRef, leftObj, rightObj, op, refPos(ref))
|
||||
}
|
||||
}
|
||||
val intOps = setOf(
|
||||
BinOp.PLUS, BinOp.MINUS, BinOp.STAR, BinOp.SLASH, BinOp.PERCENT,
|
||||
BinOp.BAND, BinOp.BOR, BinOp.BXOR, BinOp.SHL, BinOp.SHR
|
||||
@ -1413,7 +1397,11 @@ class BytecodeCompiler(
|
||||
}
|
||||
val typesMismatch = a.type != b.type && a.type != SlotType.UNKNOWN && b.type != SlotType.UNKNOWN
|
||||
val allowMixedNumeric = op in setOf(BinOp.PLUS, BinOp.MINUS, BinOp.STAR, BinOp.SLASH)
|
||||
if (typesMismatch && op in setOf(BinOp.PLUS, BinOp.MINUS, BinOp.STAR, BinOp.SLASH, BinOp.PERCENT)) {
|
||||
val isMixedNumeric = (a.type == SlotType.INT && b.type == SlotType.REAL) ||
|
||||
(a.type == SlotType.REAL && b.type == SlotType.INT)
|
||||
if (typesMismatch && op in setOf(BinOp.PLUS, BinOp.MINUS, BinOp.STAR, BinOp.SLASH, BinOp.PERCENT) &&
|
||||
!(allowMixedNumeric && isMixedNumeric)
|
||||
) {
|
||||
return compileObjBinaryOp(leftRef, a, b, op, refPos(ref))
|
||||
}
|
||||
if ((a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) &&
|
||||
@ -1683,12 +1671,12 @@ class BytecodeCompiler(
|
||||
val left = ensureObjSlot(a)
|
||||
val right = ensureObjSlot(b)
|
||||
val opcode = when {
|
||||
isExactNonNullSlotClass(left.slot, ObjString.type) &&
|
||||
isExactNonNullSlotClass(right.slot, ObjString.type) -> stringOp
|
||||
isExactNonNullSlotClass(left.slot, ObjInt.type) &&
|
||||
isExactNonNullSlotClass(right.slot, ObjInt.type) -> intOp
|
||||
isExactNonNullSlotClass(left.slot, ObjReal.type) &&
|
||||
isExactNonNullSlotClass(right.slot, ObjReal.type) -> realOp
|
||||
isExactNonNullSlotClassOrTemp(left.slot, ObjString.type) &&
|
||||
isExactNonNullSlotClassOrTemp(right.slot, ObjString.type) -> stringOp
|
||||
isExactNonNullSlotClassOrTemp(left.slot, ObjInt.type) &&
|
||||
isExactNonNullSlotClassOrTemp(right.slot, ObjInt.type) -> intOp
|
||||
isExactNonNullSlotClassOrTemp(left.slot, ObjReal.type) &&
|
||||
isExactNonNullSlotClassOrTemp(right.slot, ObjReal.type) -> realOp
|
||||
else -> objOp
|
||||
}
|
||||
builder.emit(opcode, left.slot, right.slot, out)
|
||||
@ -2352,60 +2340,36 @@ class BytecodeCompiler(
|
||||
|
||||
private fun compileLogicalAnd(ref: LogicalAndRef): CompiledValue? {
|
||||
val leftValue = compileRefWithFallback(ref.left(), SlotType.BOOL, Pos.builtIn) ?: return null
|
||||
val leftBool = if (leftValue.type == SlotType.BOOL) {
|
||||
leftValue
|
||||
} else {
|
||||
val slot = allocSlot()
|
||||
builder.emit(Opcode.OBJ_TO_BOOL, leftValue.slot, slot)
|
||||
CompiledValue(slot, SlotType.BOOL)
|
||||
}
|
||||
if (leftValue.type != SlotType.BOOL) return null
|
||||
val resultSlot = allocSlot()
|
||||
val falseId = builder.addConst(BytecodeConst.Bool(false))
|
||||
builder.emit(Opcode.CONST_BOOL, falseId, resultSlot)
|
||||
val endLabel = builder.label()
|
||||
builder.emit(
|
||||
Opcode.JMP_IF_FALSE,
|
||||
listOf(CmdBuilder.Operand.IntVal(leftBool.slot), CmdBuilder.Operand.LabelRef(endLabel))
|
||||
listOf(CmdBuilder.Operand.IntVal(leftValue.slot), CmdBuilder.Operand.LabelRef(endLabel))
|
||||
)
|
||||
val rightValue = compileRefWithFallback(ref.right(), SlotType.BOOL, Pos.builtIn) ?: return null
|
||||
val rightBool = if (rightValue.type == SlotType.BOOL) {
|
||||
rightValue
|
||||
} else {
|
||||
val slot = allocSlot()
|
||||
builder.emit(Opcode.OBJ_TO_BOOL, rightValue.slot, slot)
|
||||
CompiledValue(slot, SlotType.BOOL)
|
||||
}
|
||||
builder.emit(Opcode.MOVE_BOOL, rightBool.slot, resultSlot)
|
||||
if (rightValue.type != SlotType.BOOL) return null
|
||||
builder.emit(Opcode.MOVE_BOOL, rightValue.slot, resultSlot)
|
||||
builder.mark(endLabel)
|
||||
return CompiledValue(resultSlot, SlotType.BOOL)
|
||||
}
|
||||
|
||||
private fun compileLogicalOr(ref: LogicalOrRef): CompiledValue? {
|
||||
val leftValue = compileRefWithFallback(ref.left(), SlotType.BOOL, Pos.builtIn) ?: return null
|
||||
val leftBool = if (leftValue.type == SlotType.BOOL) {
|
||||
leftValue
|
||||
} else {
|
||||
val slot = allocSlot()
|
||||
builder.emit(Opcode.OBJ_TO_BOOL, leftValue.slot, slot)
|
||||
CompiledValue(slot, SlotType.BOOL)
|
||||
}
|
||||
if (leftValue.type != SlotType.BOOL) return null
|
||||
val resultSlot = allocSlot()
|
||||
val trueId = builder.addConst(BytecodeConst.Bool(true))
|
||||
builder.emit(Opcode.CONST_BOOL, trueId, resultSlot)
|
||||
val endLabel = builder.label()
|
||||
builder.emit(
|
||||
Opcode.JMP_IF_TRUE,
|
||||
listOf(CmdBuilder.Operand.IntVal(leftBool.slot), CmdBuilder.Operand.LabelRef(endLabel))
|
||||
listOf(CmdBuilder.Operand.IntVal(leftValue.slot), CmdBuilder.Operand.LabelRef(endLabel))
|
||||
)
|
||||
val rightValue = compileRefWithFallback(ref.right(), SlotType.BOOL, Pos.builtIn) ?: return null
|
||||
val rightBool = if (rightValue.type == SlotType.BOOL) {
|
||||
rightValue
|
||||
} else {
|
||||
val slot = allocSlot()
|
||||
builder.emit(Opcode.OBJ_TO_BOOL, rightValue.slot, slot)
|
||||
CompiledValue(slot, SlotType.BOOL)
|
||||
}
|
||||
builder.emit(Opcode.MOVE_BOOL, rightBool.slot, resultSlot)
|
||||
if (rightValue.type != SlotType.BOOL) return null
|
||||
builder.emit(Opcode.MOVE_BOOL, rightValue.slot, resultSlot)
|
||||
builder.mark(endLabel)
|
||||
return CompiledValue(resultSlot, SlotType.BOOL)
|
||||
}
|
||||
@ -3569,6 +3533,11 @@ class BytecodeCompiler(
|
||||
val elementClass = listElementClassBySlot[receiver.slot] ?: listElementClassFromReceiverRef(ref.targetRef)
|
||||
if (elementClass != null) {
|
||||
slotObjClass[dst] = elementClass
|
||||
if (elementClass == ObjString.type && elementClass.isClosed) {
|
||||
stableObjSlots.add(dst)
|
||||
} else {
|
||||
stableObjSlots.remove(dst)
|
||||
}
|
||||
}
|
||||
return CompiledValue(dst, SlotType.OBJ)
|
||||
}
|
||||
@ -3635,6 +3604,12 @@ class BytecodeCompiler(
|
||||
}
|
||||
SlotType.OBJ -> {
|
||||
if (objOp == null) return null
|
||||
if (isExactNonNullSlotClassOrTemp(rhs.slot, ObjInt.type)) {
|
||||
val right = allocSlot()
|
||||
builder.emit(Opcode.UNBOX_INT_OBJ, rhs.slot, right)
|
||||
builder.emit(intOp, out, right, out)
|
||||
return CompiledValue(out, SlotType.INT)
|
||||
}
|
||||
val leftObj = allocSlot()
|
||||
builder.emit(Opcode.BOX_OBJ, out, leftObj)
|
||||
updateSlotType(leftObj, SlotType.OBJ)
|
||||
@ -3660,6 +3635,12 @@ class BytecodeCompiler(
|
||||
}
|
||||
SlotType.OBJ -> {
|
||||
if (objOp == null) return null
|
||||
if (isExactNonNullSlotClassOrTemp(rhs.slot, ObjReal.type)) {
|
||||
val right = allocSlot()
|
||||
builder.emit(Opcode.UNBOX_REAL_OBJ, rhs.slot, right)
|
||||
builder.emit(realOp, out, right, out)
|
||||
return CompiledValue(out, SlotType.REAL)
|
||||
}
|
||||
val leftObj = allocSlot()
|
||||
builder.emit(Opcode.BOX_OBJ, out, leftObj)
|
||||
updateSlotType(leftObj, SlotType.OBJ)
|
||||
@ -5660,6 +5641,17 @@ class BytecodeCompiler(
|
||||
private fun emitInlineBlock(stmt: BlockStatement, needResult: Boolean): CompiledValue? =
|
||||
emitInlineStatements(stmt.statements(), needResult)
|
||||
|
||||
private fun ensureVoidSlot(): Int {
|
||||
val existing = cachedVoidSlot
|
||||
if (existing != null) return existing
|
||||
val slot = allocSlot()
|
||||
val voidId = builder.addConst(BytecodeConst.ObjRef(ObjVoid))
|
||||
builder.emit(Opcode.CONST_OBJ, voidId, slot)
|
||||
updateSlotType(slot, SlotType.OBJ)
|
||||
cachedVoidSlot = slot
|
||||
return slot
|
||||
}
|
||||
|
||||
private fun shouldInlineBlock(stmt: BlockStatement): Boolean {
|
||||
return allowLocalSlots
|
||||
}
|
||||
@ -6002,6 +5994,11 @@ class BytecodeCompiler(
|
||||
|
||||
try {
|
||||
val needsBreakFlag = stmt.canBreak || stmt.elseStatement != null
|
||||
val realWidenSlots = collectLoopRealWidenSlots(stmt.body)
|
||||
val hasRealWiden = realWidenSlots.isNotEmpty()
|
||||
if (hasRealWiden) {
|
||||
applySlotTypes(realWidenSlots, SlotType.REAL)
|
||||
}
|
||||
val breakFlagSlot = allocSlot()
|
||||
if (range == null && rangeRef == null && typedRangeLocal == null) {
|
||||
val sourceValue = compileStatementValueOrFallback(stmt.source) ?: return null
|
||||
@ -6075,12 +6072,18 @@ class BytecodeCompiler(
|
||||
)
|
||||
)
|
||||
val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null
|
||||
if (hasRealWiden) {
|
||||
applySlotTypes(realWidenSlots, SlotType.UNKNOWN)
|
||||
}
|
||||
loopStack.removeLast()
|
||||
if (wantResult) {
|
||||
val bodyObj = ensureObjSlot(bodyValue)
|
||||
builder.emit(Opcode.MOVE_OBJ, bodyObj.slot, resultSlot!!)
|
||||
}
|
||||
builder.mark(continueLabel)
|
||||
if (hasRealWiden) {
|
||||
emitLoopRealCoercions(realWidenSlots)
|
||||
}
|
||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(loopLabel)))
|
||||
|
||||
builder.mark(endLabel)
|
||||
@ -6188,6 +6191,9 @@ class BytecodeCompiler(
|
||||
)
|
||||
)
|
||||
val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null
|
||||
if (hasRealWiden) {
|
||||
applySlotTypes(realWidenSlots, SlotType.UNKNOWN)
|
||||
}
|
||||
loopStack.removeLast()
|
||||
if (wantResult) {
|
||||
val bodyObj = ensureObjSlot(bodyValue)
|
||||
@ -6195,6 +6201,9 @@ class BytecodeCompiler(
|
||||
}
|
||||
builder.mark(continueLabel)
|
||||
builder.emit(Opcode.INC_INT, iSlot)
|
||||
if (hasRealWiden) {
|
||||
emitLoopRealCoercions(realWidenSlots)
|
||||
}
|
||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(loopLabel)))
|
||||
|
||||
builder.mark(endLabel)
|
||||
@ -6265,6 +6274,9 @@ class BytecodeCompiler(
|
||||
)
|
||||
)
|
||||
val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null
|
||||
if (hasRealWiden) {
|
||||
applySlotTypes(realWidenSlots, SlotType.UNKNOWN)
|
||||
}
|
||||
loopStack.removeLast()
|
||||
if (wantResult) {
|
||||
val bodyObj = ensureObjSlot(bodyValue)
|
||||
@ -6272,6 +6284,9 @@ class BytecodeCompiler(
|
||||
}
|
||||
builder.mark(continueLabel)
|
||||
builder.emit(Opcode.INC_INT, iSlot)
|
||||
if (hasRealWiden) {
|
||||
emitLoopRealCoercions(realWidenSlots)
|
||||
}
|
||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(loopLabel)))
|
||||
|
||||
builder.mark(endLabel)
|
||||
@ -6331,6 +6346,9 @@ class BytecodeCompiler(
|
||||
)
|
||||
)
|
||||
val bodyValue = compileLoopBody(stmt.body, wantResult) ?: return null
|
||||
if (hasRealWiden) {
|
||||
applySlotTypes(realWidenSlots, SlotType.UNKNOWN)
|
||||
}
|
||||
loopStack.removeLast()
|
||||
if (wantResult) {
|
||||
val bodyObj = ensureObjSlot(bodyValue)
|
||||
@ -6460,10 +6478,7 @@ class BytecodeCompiler(
|
||||
restoreFlowTypeOverride(elseRestore)
|
||||
}
|
||||
builder.mark(endLabel)
|
||||
val slot = allocSlot()
|
||||
val voidId = builder.addConst(BytecodeConst.ObjRef(ObjVoid))
|
||||
builder.emit(Opcode.CONST_OBJ, voidId, slot)
|
||||
return CompiledValue(slot, SlotType.OBJ)
|
||||
return CompiledValue(ensureVoidSlot(), SlotType.OBJ)
|
||||
}
|
||||
|
||||
private fun updateSlotTypeByName(name: String, type: SlotType) {
|
||||
|
||||
93
lynglib/src/commonTest/kotlin/MixedCompareBenchmarkTest.kt
Normal file
93
lynglib/src/commonTest/kotlin/MixedCompareBenchmarkTest.kt
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.Benchmarks
|
||||
import net.sergeych.lyng.Script
|
||||
import net.sergeych.lyng.obj.ObjInt
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.time.TimeSource
|
||||
|
||||
class MixedCompareBenchmarkTest {
|
||||
@Test
|
||||
fun benchmarkMixedCompareOps() = runTest {
|
||||
if (!Benchmarks.enabled) return@runTest
|
||||
val iterations = 200000
|
||||
val script = """
|
||||
fun mixedCompareBench(n) {
|
||||
var acc = 0
|
||||
var r = 0.0
|
||||
val strs = ["a","b","aa","bb","abc","abd","zzz",""]
|
||||
var i = 0
|
||||
while(i < n) {
|
||||
val si = strs[i % 8]
|
||||
if( si == "a" ) acc += 1 else acc -= 1
|
||||
if( si != "zzz" ) acc += 2
|
||||
if( si == "" ) acc += 3
|
||||
|
||||
if( i < (i % 5) ) acc += 1 else acc -= 1
|
||||
if( (i % 3) == 0 ) acc += 2
|
||||
|
||||
val r1 = i + 0.5
|
||||
if( r1 > i ) acc += 1
|
||||
if( i < r1 ) acc += 1
|
||||
r += r1 * 0.25
|
||||
if( r > 1000.0 ) acc += 1
|
||||
|
||||
i++
|
||||
}
|
||||
acc
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val scope = Script.newScope()
|
||||
scope.eval(script)
|
||||
val expected = expectedValue(iterations)
|
||||
|
||||
val start = TimeSource.Monotonic.markNow()
|
||||
val result = scope.eval("mixedCompareBench($iterations)") as ObjInt
|
||||
val elapsedMs = start.elapsedNow().inWholeMilliseconds
|
||||
println("[DEBUG_LOG] [BENCH] mixed-compare elapsed=${elapsedMs} ms")
|
||||
assertEquals(expected, result.value)
|
||||
}
|
||||
|
||||
private fun expectedValue(iterations: Int): Long {
|
||||
val strs = arrayOf("a", "b", "aa", "bb", "abc", "abd", "zzz", "")
|
||||
var acc = 0L
|
||||
var r = 0.0
|
||||
var i = 0
|
||||
while (i < iterations) {
|
||||
val si = strs[i % 8]
|
||||
if (si == "a") acc += 1 else acc -= 1
|
||||
if (si != "zzz") acc += 2
|
||||
if (si == "") acc += 3
|
||||
|
||||
if (i < (i % 5)) acc += 1 else acc -= 1
|
||||
if ((i % 3) == 0) acc += 2
|
||||
|
||||
val r1 = i + 0.5
|
||||
if (r1 > i) acc += 1
|
||||
if (i < r1) acc += 1
|
||||
r += r1 * 0.25
|
||||
if (r > 1000.0) acc += 1
|
||||
|
||||
i += 1
|
||||
}
|
||||
return acc
|
||||
}
|
||||
}
|
||||
@ -1258,6 +1258,28 @@ class ScriptTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testForLoopRealWidenDisasm() = runTest {
|
||||
val scope = Script.newScope()
|
||||
scope.eval(
|
||||
"""
|
||||
fun widenFor() {
|
||||
var acc = 0
|
||||
for(i in 0..3) {
|
||||
if (i == 1) acc = 0.5
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
val disasm = scope.disassembleSymbol("widenFor")
|
||||
println("[DEBUG_LOG] widenFor disasm:\n$disasm")
|
||||
val incIndex = disasm.indexOf("INC_INT")
|
||||
assertTrue(incIndex >= 0, "expected INC_INT in for-loop disasm")
|
||||
val convIndex = disasm.indexOf("INT_TO_REAL")
|
||||
assertTrue(convIndex >= 0, "expected INT_TO_REAL in for-loop disasm")
|
||||
assertTrue(convIndex > incIndex, "INT_TO_REAL should appear after INC_INT")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIntClosedRangeInclusive() = runTest {
|
||||
eval(
|
||||
|
||||
39
notes/fast_ops_optimizations_plan.md
Normal file
39
notes/fast_ops_optimizations_plan.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Fast Ops Optimizations Plan (Draft)
|
||||
|
||||
Baseline
|
||||
- See `notes/nested_range_baseline.md`
|
||||
|
||||
Candidates (not started)
|
||||
1) Primitive comparisons (done)
|
||||
- Emit fast CMP variants for known ObjString/ObjInt/ObjReal using temp/stable slots.
|
||||
- MixedCompareBenchmarkTest: 374 ms -> 347 ms.
|
||||
2) Mixed numeric ops (done)
|
||||
- Allow INT+REAL arithmetic to use primitive REAL ops (no obj fallback).
|
||||
- MixedCompareBenchmarkTest: 347 ms -> 275 ms.
|
||||
3) Boolean conversion (done; do not revert without review)
|
||||
- Skip redundant OBJ_TO_BOOL in logical AND/OR when compiler already emits BOOL.
|
||||
- MixedCompareBenchmarkTest: 275 ms -> 249 ms.
|
||||
4) Range/loop hot path (done)
|
||||
- Reuse a cached ObjVoid slot for if-statements in statement context (avoids per-iteration CONST_OBJ).
|
||||
- MixedCompareBenchmarkTest: 249 ms -> 247 ms.
|
||||
5) String ops (done)
|
||||
- Mark GET_INDEX results as stable only for closed ObjString elements to enable fast compares.
|
||||
- MixedCompareBenchmarkTest: 247 ms -> 240 ms.
|
||||
6) Box/unbox audit (done)
|
||||
- Unbox ObjInt/ObjReal in assign-op when target is INT/REAL to avoid boxing + obj ops.
|
||||
- MixedCompareBenchmarkTest: 240 ms -> 234 ms.
|
||||
7) Mixed compare coverage
|
||||
- Emit CMP_*_REAL when one operand is known ObjReal in more expression forms (not just assign-op).
|
||||
- Verify with disassembly that fast cmp opcodes are emitted.
|
||||
8) Range-loop invariant hoist
|
||||
- Cache range end/step into temps once per loop; avoid repeated slot reads/boxing in body.
|
||||
- Confirm no extra CONST_OBJ in hot path.
|
||||
9) Boxing elision pass
|
||||
- Remove redundant BOX_OBJ when value feeds only primitive ops afterward (local liveness).
|
||||
- Ensure no impact on closures/escaping values.
|
||||
10) Closed-type fast paths expansion
|
||||
- Apply closed-type trust for ObjBool/ObjInt/ObjReal/ObjString in ternaries and conditional chains.
|
||||
- Guard with exact non-null temp/slot checks only.
|
||||
11) VM hot op micro-optimizations
|
||||
- Reduce frame reads/writes in ADD_INT, MUL_REAL, CMP_*_INT/REAL when operands are temps.
|
||||
- Compare against baseline; revert if regression after 10-run median.
|
||||
12
notes/mixed_compare_benchmark_baseline.md
Normal file
12
notes/mixed_compare_benchmark_baseline.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Mixed Compare Benchmark Baseline
|
||||
|
||||
Date: 2026-02-16
|
||||
|
||||
Benchmark:
|
||||
- MixedCompareBenchmarkTest.benchmarkMixedCompareOps
|
||||
|
||||
Command:
|
||||
`BENCHMARKS=true timeout 20s ./gradlew :lynglib:jvmTest --tests MixedCompareBenchmarkTest --rerun-tasks`
|
||||
|
||||
Result:
|
||||
- mixed-compare elapsed=374 ms
|
||||
9
notes/nested_range_baseline.md
Normal file
9
notes/nested_range_baseline.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Nested Range Benchmark Baseline
|
||||
|
||||
Date: 2026-02-16
|
||||
|
||||
Command:
|
||||
`BENCHMARKS=true timeout 20s ./gradlew :lynglib:jvmTest --tests NestedRangeBenchmarkTest --rerun-tasks`
|
||||
|
||||
Result:
|
||||
- nested-happy elapsed=56 ms
|
||||
Loading…
x
Reference in New Issue
Block a user