Add short-circuit ops in bytecode compiler and VM

This commit is contained in:
sergeych committed 2026-01-25 19:59:22 +03:00
1 parent 3d9170d677
commit 8ae6eb8d69
3 files changed
+77 -1

No files matched your search

@@ -125,11 +125,14 @@ class BytecodeCompiler {
}
private fun compileBinary(ref: BinaryOpRef): CompiledValue? {
val op = binaryOp(ref)
if (op == BinOp.AND || op == BinOp.OR) {
return compileLogical(op, binaryLeft(ref), binaryRight(ref), refPos(ref))
}
val a = compileRef(binaryLeft(ref)) ?: return null
val b = compileRef(binaryRight(ref)) ?: return null
if (a.type != b.type && a.type != SlotType.UNKNOWN && b.type != SlotType.UNKNOWN) return null
val out = allocSlot()
val op = binaryOp(ref)
return when (op) {
BinOp.PLUS -> when (a.type) {
SlotType.INT -> {
@@ -305,6 +308,33 @@ class BytecodeCompiler {
}
}
private fun compileLogical(op: BinOp, left: ObjRef, right: ObjRef, pos: Pos): CompiledValue? {
val leftValue = compileRefWithFallback(left, SlotType.BOOL, pos) ?: return null
if (leftValue.type != SlotType.BOOL) return null
val resultSlot = allocSlot()
val shortLabel = builder.label()
val endLabel = builder.label()
if (op == BinOp.AND) {
builder.emit(
Opcode.JMP_IF_FALSE,
listOf(BytecodeBuilder.Operand.IntVal(leftValue.slot), BytecodeBuilder.Operand.LabelRef(shortLabel))
)
} else {
builder.emit(
Opcode.JMP_IF_TRUE,
listOf(BytecodeBuilder.Operand.IntVal(leftValue.slot), BytecodeBuilder.Operand.LabelRef(shortLabel))
)
}
val rightValue = compileRefWithFallback(right, SlotType.BOOL, pos) ?: return null
emitMove(rightValue, resultSlot)
builder.emit(Opcode.JMP, listOf(BytecodeBuilder.Operand.LabelRef(endLabel)))
builder.mark(shortLabel)
val constId = builder.addConst(BytecodeConst.Bool(op == BinOp.OR))
builder.emit(Opcode.CONST_BOOL, constId, resultSlot)
builder.mark(endLabel)
return CompiledValue(resultSlot, SlotType.BOOL)
}
private fun compileAssign(ref: AssignRef): CompiledValue? {
val target = assignTarget(ref) ?: return null
if (refDepth(target) != 0) return null
@@ -394,4 +424,5 @@ class BytecodeCompiler {
private fun unaryOp(ref: UnaryOpRef): UnaryOp = ref.op
private fun assignTarget(ref: AssignRef): LocalSlotRef? = ref.target as? LocalSlotRef
private fun assignValue(ref: AssignRef): ObjRef = ref.value
private fun refPos(ref: BinaryOpRef): Pos = Pos.builtIn
}
@@ -200,6 +200,15 @@ class BytecodeVm {
ip = target
}
}
Opcode.JMP_IF_TRUE -> {
val cond = decoder.readSlot(code, ip)
ip += fn.slotWidth
val target = decoder.readIp(code, ip, fn.ipWidth)
ip += fn.ipWidth
if (frame.getBool(cond)) {
ip = target
}
}
Opcode.EVAL_FALLBACK -> {
val id = decoder.readConstId(code, ip, 2)
ip += 2