From 7c28296f92d479df307a36efbf8f5665b0d81944 Mon Sep 17 00:00:00 2001 From: sergeych Date: Mon, 16 Feb 2026 17:42:04 +0300 Subject: [PATCH] Elide redundant bool conversions in logical ops --- .../lyng/bytecode/BytecodeCompiler.kt | 40 ++++--------------- notes/fast_ops_optimizations_plan.md | 5 ++- 2 files changed, 11 insertions(+), 34 deletions(-) diff --git a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt index 523911a..7920f0d 100644 --- a/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt +++ b/lynglib/src/commonMain/kotlin/net/sergeych/lyng/bytecode/BytecodeCompiler.kt @@ -2339,60 +2339,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) } diff --git a/notes/fast_ops_optimizations_plan.md b/notes/fast_ops_optimizations_plan.md index cbe8d64..def0503 100644 --- a/notes/fast_ops_optimizations_plan.md +++ b/notes/fast_ops_optimizations_plan.md @@ -10,8 +10,9 @@ Candidates (not started) 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 - - Skip OBJ_TO_BOOL when compiler already has a BOOL slot. +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 - Keep range iteration in INT ops, avoid accidental boxing. - Confirm loop-var slots avoid re-boxing in loop bodies.