Add short-circuit ops in bytecode compiler and VM
This commit is contained in:
parent
3d9170d677
commit
8ae6eb8d69
@ -125,11 +125,14 @@ class BytecodeCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun compileBinary(ref: BinaryOpRef): CompiledValue? {
|
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 a = compileRef(binaryLeft(ref)) ?: return null
|
||||||
val b = compileRef(binaryRight(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
|
if (a.type != b.type && a.type != SlotType.UNKNOWN && b.type != SlotType.UNKNOWN) return null
|
||||||
val out = allocSlot()
|
val out = allocSlot()
|
||||||
val op = binaryOp(ref)
|
|
||||||
return when (op) {
|
return when (op) {
|
||||||
BinOp.PLUS -> when (a.type) {
|
BinOp.PLUS -> when (a.type) {
|
||||||
SlotType.INT -> {
|
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? {
|
private fun compileAssign(ref: AssignRef): CompiledValue? {
|
||||||
val target = assignTarget(ref) ?: return null
|
val target = assignTarget(ref) ?: return null
|
||||||
if (refDepth(target) != 0) return null
|
if (refDepth(target) != 0) return null
|
||||||
@ -394,4 +424,5 @@ class BytecodeCompiler {
|
|||||||
private fun unaryOp(ref: UnaryOpRef): UnaryOp = ref.op
|
private fun unaryOp(ref: UnaryOpRef): UnaryOp = ref.op
|
||||||
private fun assignTarget(ref: AssignRef): LocalSlotRef? = ref.target as? LocalSlotRef
|
private fun assignTarget(ref: AssignRef): LocalSlotRef? = ref.target as? LocalSlotRef
|
||||||
private fun assignValue(ref: AssignRef): ObjRef = ref.value
|
private fun assignValue(ref: AssignRef): ObjRef = ref.value
|
||||||
|
private fun refPos(ref: BinaryOpRef): Pos = Pos.builtIn
|
||||||
}
|
}
|
||||||
|
|||||||
@ -200,6 +200,15 @@ class BytecodeVm {
|
|||||||
ip = target
|
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 -> {
|
Opcode.EVAL_FALLBACK -> {
|
||||||
val id = decoder.readConstId(code, ip, 2)
|
val id = decoder.readConstId(code, ip, 2)
|
||||||
ip += 2
|
ip += 2
|
||||||
|
|||||||
@ -25,8 +25,12 @@ import net.sergeych.lyng.bytecode.Opcode
|
|||||||
import net.sergeych.lyng.obj.BinaryOpRef
|
import net.sergeych.lyng.obj.BinaryOpRef
|
||||||
import net.sergeych.lyng.obj.BinOp
|
import net.sergeych.lyng.obj.BinOp
|
||||||
import net.sergeych.lyng.obj.ConstRef
|
import net.sergeych.lyng.obj.ConstRef
|
||||||
|
import net.sergeych.lyng.obj.ObjFalse
|
||||||
import net.sergeych.lyng.obj.ObjInt
|
import net.sergeych.lyng.obj.ObjInt
|
||||||
|
import net.sergeych.lyng.obj.ObjTrue
|
||||||
|
import net.sergeych.lyng.obj.ValueFnRef
|
||||||
import net.sergeych.lyng.obj.ObjVoid
|
import net.sergeych.lyng.obj.ObjVoid
|
||||||
|
import net.sergeych.lyng.obj.toBool
|
||||||
import net.sergeych.lyng.obj.toInt
|
import net.sergeych.lyng.obj.toInt
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -93,4 +97,36 @@ class BytecodeVmTest {
|
|||||||
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||||
assertEquals(ObjVoid, result)
|
assertEquals(ObjVoid, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun andIsShortCircuit() = kotlinx.coroutines.test.runTest {
|
||||||
|
val throwingRef = ValueFnRef { error("should not execute") }
|
||||||
|
val expr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.AND,
|
||||||
|
ConstRef(ObjFalse.asReadonly),
|
||||||
|
throwingRef
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val fn = BytecodeCompiler().compileExpression("andShort", expr) ?: error("bytecode compile failed")
|
||||||
|
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||||
|
assertEquals(false, result.toBool())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun orIsShortCircuit() = kotlinx.coroutines.test.runTest {
|
||||||
|
val throwingRef = ValueFnRef { error("should not execute") }
|
||||||
|
val expr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.OR,
|
||||||
|
ConstRef(ObjTrue.asReadonly),
|
||||||
|
throwingRef
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val fn = BytecodeCompiler().compileExpression("orShort", expr) ?: error("bytecode compile failed")
|
||||||
|
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||||
|
assertEquals(true, result.toBool())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user