Expand bytecode expression support for mixed ops
This commit is contained in:
parent
8ae6eb8d69
commit
9c56cf751b
@ -149,6 +149,8 @@ Note: Any opcode can be compiled to FALLBACK if not implemented in a VM pass.
|
|||||||
- CMP_GT_REAL_INT S, S -> S
|
- CMP_GT_REAL_INT S, S -> S
|
||||||
- CMP_GTE_INT_REAL S, S -> S
|
- CMP_GTE_INT_REAL S, S -> S
|
||||||
- CMP_GTE_REAL_INT S, S -> S
|
- CMP_GTE_REAL_INT S, S -> S
|
||||||
|
- CMP_NEQ_INT_REAL S, S -> S
|
||||||
|
- CMP_NEQ_REAL_INT S, S -> S
|
||||||
|
|
||||||
### Boolean ops
|
### Boolean ops
|
||||||
- NOT_BOOL S -> S
|
- NOT_BOOL S -> S
|
||||||
|
|||||||
@ -132,7 +132,7 @@ class BytecodeBuilder {
|
|||||||
Opcode.CMP_EQ_BOOL, Opcode.CMP_NEQ_BOOL,
|
Opcode.CMP_EQ_BOOL, Opcode.CMP_NEQ_BOOL,
|
||||||
Opcode.CMP_EQ_INT_REAL, Opcode.CMP_EQ_REAL_INT, Opcode.CMP_LT_INT_REAL, Opcode.CMP_LT_REAL_INT,
|
Opcode.CMP_EQ_INT_REAL, Opcode.CMP_EQ_REAL_INT, Opcode.CMP_LT_INT_REAL, Opcode.CMP_LT_REAL_INT,
|
||||||
Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT,
|
Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT,
|
||||||
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT,
|
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
|
||||||
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import net.sergeych.lyng.obj.*
|
|||||||
class BytecodeCompiler {
|
class BytecodeCompiler {
|
||||||
private val builder = BytecodeBuilder()
|
private val builder = BytecodeBuilder()
|
||||||
private var nextSlot = 0
|
private var nextSlot = 0
|
||||||
|
private val slotTypes = mutableMapOf<Int, SlotType>()
|
||||||
|
|
||||||
fun compileStatement(name: String, stmt: net.sergeych.lyng.Statement): BytecodeFunction? {
|
fun compileStatement(name: String, stmt: net.sergeych.lyng.Statement): BytecodeFunction? {
|
||||||
return when (stmt) {
|
return when (stmt) {
|
||||||
@ -52,7 +53,7 @@ class BytecodeCompiler {
|
|||||||
is LocalSlotRef -> {
|
is LocalSlotRef -> {
|
||||||
if (ref.name.isEmpty()) return null
|
if (ref.name.isEmpty()) return null
|
||||||
if (refDepth(ref) != 0) return null
|
if (refDepth(ref) != 0) return null
|
||||||
CompiledValue(refSlot(ref), SlotType.UNKNOWN)
|
CompiledValue(refSlot(ref), slotTypes[refSlot(ref)] ?: SlotType.UNKNOWN)
|
||||||
}
|
}
|
||||||
is BinaryOpRef -> compileBinary(ref)
|
is BinaryOpRef -> compileBinary(ref)
|
||||||
is UnaryOpRef -> compileUnary(ref)
|
is UnaryOpRef -> compileUnary(ref)
|
||||||
@ -131,51 +132,102 @@ class BytecodeCompiler {
|
|||||||
}
|
}
|
||||||
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
|
val typesMismatch = a.type != b.type && a.type != SlotType.UNKNOWN && b.type != SlotType.UNKNOWN
|
||||||
|
if (typesMismatch && op !in setOf(BinOp.EQ, BinOp.NEQ, BinOp.LT, BinOp.LTE, BinOp.GT, BinOp.GTE)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
val out = allocSlot()
|
val out = allocSlot()
|
||||||
return when (op) {
|
return when (op) {
|
||||||
BinOp.PLUS -> when (a.type) {
|
BinOp.PLUS -> when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
when (b.type) {
|
||||||
SlotType.INT -> {
|
SlotType.INT -> {
|
||||||
builder.emit(Opcode.ADD_INT, a.slot, b.slot, out)
|
builder.emit(Opcode.ADD_INT, a.slot, b.slot, out)
|
||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
|
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.ADD_REAL, a, b, out)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SlotType.REAL -> {
|
||||||
|
when (b.type) {
|
||||||
SlotType.REAL -> {
|
SlotType.REAL -> {
|
||||||
builder.emit(Opcode.ADD_REAL, a.slot, b.slot, out)
|
builder.emit(Opcode.ADD_REAL, a.slot, b.slot, out)
|
||||||
CompiledValue(out, SlotType.REAL)
|
CompiledValue(out, SlotType.REAL)
|
||||||
}
|
}
|
||||||
|
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.ADD_REAL, a, b, out)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
BinOp.MINUS -> when (a.type) {
|
BinOp.MINUS -> when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
when (b.type) {
|
||||||
SlotType.INT -> {
|
SlotType.INT -> {
|
||||||
builder.emit(Opcode.SUB_INT, a.slot, b.slot, out)
|
builder.emit(Opcode.SUB_INT, a.slot, b.slot, out)
|
||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
|
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.SUB_REAL, a, b, out)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SlotType.REAL -> {
|
||||||
|
when (b.type) {
|
||||||
SlotType.REAL -> {
|
SlotType.REAL -> {
|
||||||
builder.emit(Opcode.SUB_REAL, a.slot, b.slot, out)
|
builder.emit(Opcode.SUB_REAL, a.slot, b.slot, out)
|
||||||
CompiledValue(out, SlotType.REAL)
|
CompiledValue(out, SlotType.REAL)
|
||||||
}
|
}
|
||||||
|
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.SUB_REAL, a, b, out)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
BinOp.STAR -> when (a.type) {
|
BinOp.STAR -> when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
when (b.type) {
|
||||||
SlotType.INT -> {
|
SlotType.INT -> {
|
||||||
builder.emit(Opcode.MUL_INT, a.slot, b.slot, out)
|
builder.emit(Opcode.MUL_INT, a.slot, b.slot, out)
|
||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
|
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.MUL_REAL, a, b, out)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SlotType.REAL -> {
|
||||||
|
when (b.type) {
|
||||||
SlotType.REAL -> {
|
SlotType.REAL -> {
|
||||||
builder.emit(Opcode.MUL_REAL, a.slot, b.slot, out)
|
builder.emit(Opcode.MUL_REAL, a.slot, b.slot, out)
|
||||||
CompiledValue(out, SlotType.REAL)
|
CompiledValue(out, SlotType.REAL)
|
||||||
}
|
}
|
||||||
|
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.MUL_REAL, a, b, out)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
BinOp.SLASH -> when (a.type) {
|
BinOp.SLASH -> when (a.type) {
|
||||||
|
SlotType.INT -> {
|
||||||
|
when (b.type) {
|
||||||
SlotType.INT -> {
|
SlotType.INT -> {
|
||||||
builder.emit(Opcode.DIV_INT, a.slot, b.slot, out)
|
builder.emit(Opcode.DIV_INT, a.slot, b.slot, out)
|
||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
|
SlotType.REAL -> compileRealArithmeticWithCoercion(Opcode.DIV_REAL, a, b, out)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SlotType.REAL -> {
|
||||||
|
when (b.type) {
|
||||||
SlotType.REAL -> {
|
SlotType.REAL -> {
|
||||||
builder.emit(Opcode.DIV_REAL, a.slot, b.slot, out)
|
builder.emit(Opcode.DIV_REAL, a.slot, b.slot, out)
|
||||||
CompiledValue(out, SlotType.REAL)
|
CompiledValue(out, SlotType.REAL)
|
||||||
}
|
}
|
||||||
|
SlotType.INT -> compileRealArithmeticWithCoercion(Opcode.DIV_REAL, a, b, out)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
BinOp.PERCENT -> {
|
BinOp.PERCENT -> {
|
||||||
@ -184,90 +236,22 @@ class BytecodeCompiler {
|
|||||||
CompiledValue(out, SlotType.INT)
|
CompiledValue(out, SlotType.INT)
|
||||||
}
|
}
|
||||||
BinOp.EQ -> {
|
BinOp.EQ -> {
|
||||||
when (a.type) {
|
compileCompareEq(a, b, out)
|
||||||
SlotType.INT -> {
|
|
||||||
builder.emit(Opcode.CMP_EQ_INT, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
SlotType.REAL -> {
|
|
||||||
builder.emit(Opcode.CMP_EQ_REAL, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
SlotType.BOOL -> {
|
|
||||||
builder.emit(Opcode.CMP_EQ_BOOL, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BinOp.NEQ -> {
|
BinOp.NEQ -> {
|
||||||
when (a.type) {
|
compileCompareNeq(a, b, out)
|
||||||
SlotType.INT -> {
|
|
||||||
builder.emit(Opcode.CMP_NEQ_INT, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
SlotType.REAL -> {
|
|
||||||
builder.emit(Opcode.CMP_NEQ_REAL, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
SlotType.BOOL -> {
|
|
||||||
builder.emit(Opcode.CMP_NEQ_BOOL, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BinOp.LT -> {
|
BinOp.LT -> {
|
||||||
when (a.type) {
|
compileCompareLt(a, b, out)
|
||||||
SlotType.INT -> {
|
|
||||||
builder.emit(Opcode.CMP_LT_INT, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
SlotType.REAL -> {
|
|
||||||
builder.emit(Opcode.CMP_LT_REAL, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BinOp.LTE -> {
|
BinOp.LTE -> {
|
||||||
when (a.type) {
|
compileCompareLte(a, b, out)
|
||||||
SlotType.INT -> {
|
|
||||||
builder.emit(Opcode.CMP_LTE_INT, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
SlotType.REAL -> {
|
|
||||||
builder.emit(Opcode.CMP_LTE_REAL, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BinOp.GT -> {
|
BinOp.GT -> {
|
||||||
when (a.type) {
|
compileCompareGt(a, b, out)
|
||||||
SlotType.INT -> {
|
|
||||||
builder.emit(Opcode.CMP_GT_INT, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
SlotType.REAL -> {
|
|
||||||
builder.emit(Opcode.CMP_GT_REAL, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BinOp.GTE -> {
|
BinOp.GTE -> {
|
||||||
when (a.type) {
|
compileCompareGte(a, b, out)
|
||||||
SlotType.INT -> {
|
|
||||||
builder.emit(Opcode.CMP_GTE_INT, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
SlotType.REAL -> {
|
|
||||||
builder.emit(Opcode.CMP_GTE_REAL, a.slot, b.slot, out)
|
|
||||||
CompiledValue(out, SlotType.BOOL)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BinOp.AND -> {
|
BinOp.AND -> {
|
||||||
if (a.type != SlotType.BOOL) return null
|
if (a.type != SlotType.BOOL) return null
|
||||||
@ -308,6 +292,173 @@ class BytecodeCompiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun compileRealArithmeticWithCoercion(
|
||||||
|
op: Opcode,
|
||||||
|
a: CompiledValue,
|
||||||
|
b: CompiledValue,
|
||||||
|
out: Int
|
||||||
|
): CompiledValue? {
|
||||||
|
if (a.type == SlotType.INT && b.type == SlotType.REAL) {
|
||||||
|
val left = allocSlot()
|
||||||
|
builder.emit(Opcode.INT_TO_REAL, a.slot, left)
|
||||||
|
builder.emit(op, left, b.slot, out)
|
||||||
|
return CompiledValue(out, SlotType.REAL)
|
||||||
|
}
|
||||||
|
if (a.type == SlotType.REAL && b.type == SlotType.INT) {
|
||||||
|
val right = allocSlot()
|
||||||
|
builder.emit(Opcode.INT_TO_REAL, b.slot, right)
|
||||||
|
builder.emit(op, a.slot, right, out)
|
||||||
|
return CompiledValue(out, SlotType.REAL)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileCompareEq(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||||
|
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||||
|
return when {
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_EQ_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_EQ_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.BOOL && b.type == SlotType.BOOL -> {
|
||||||
|
builder.emit(Opcode.CMP_EQ_BOOL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_EQ_INT_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_EQ_REAL_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileCompareNeq(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||||
|
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||||
|
return when {
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_NEQ_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_NEQ_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.BOOL && b.type == SlotType.BOOL -> {
|
||||||
|
builder.emit(Opcode.CMP_NEQ_BOOL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_NEQ_INT_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_NEQ_REAL_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileCompareLt(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||||
|
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||||
|
return when {
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_LT_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_LT_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_LT_INT_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_LT_REAL_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileCompareLte(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||||
|
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||||
|
return when {
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_LTE_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_LTE_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_LTE_INT_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_LTE_REAL_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileCompareGt(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||||
|
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||||
|
return when {
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_GT_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_GT_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_GT_INT_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_GT_REAL_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compileCompareGte(a: CompiledValue, b: CompiledValue, out: Int): CompiledValue? {
|
||||||
|
if (a.type == SlotType.UNKNOWN || b.type == SlotType.UNKNOWN) return null
|
||||||
|
return when {
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_GTE_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_GTE_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.INT && b.type == SlotType.REAL -> {
|
||||||
|
builder.emit(Opcode.CMP_GTE_INT_REAL, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
a.type == SlotType.REAL && b.type == SlotType.INT -> {
|
||||||
|
builder.emit(Opcode.CMP_GTE_REAL_INT, a.slot, b.slot, out)
|
||||||
|
CompiledValue(out, SlotType.BOOL)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun compileLogical(op: BinOp, left: ObjRef, right: ObjRef, pos: Pos): CompiledValue? {
|
private fun compileLogical(op: BinOp, left: ObjRef, right: ObjRef, pos: Pos): CompiledValue? {
|
||||||
val leftValue = compileRefWithFallback(left, SlotType.BOOL, pos) ?: return null
|
val leftValue = compileRefWithFallback(left, SlotType.BOOL, pos) ?: return null
|
||||||
if (leftValue.type != SlotType.BOOL) return null
|
if (leftValue.type != SlotType.BOOL) return null
|
||||||
@ -346,6 +497,7 @@ class BytecodeCompiler {
|
|||||||
SlotType.BOOL -> builder.emit(Opcode.MOVE_BOOL, value.slot, slot)
|
SlotType.BOOL -> builder.emit(Opcode.MOVE_BOOL, value.slot, slot)
|
||||||
else -> builder.emit(Opcode.MOVE_OBJ, value.slot, slot)
|
else -> builder.emit(Opcode.MOVE_OBJ, value.slot, slot)
|
||||||
}
|
}
|
||||||
|
updateSlotType(slot, value.type)
|
||||||
return CompiledValue(slot, value.type)
|
return CompiledValue(slot, value.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,6 +564,7 @@ class BytecodeCompiler {
|
|||||||
}
|
}
|
||||||
val id = builder.addFallback(stmt)
|
val id = builder.addFallback(stmt)
|
||||||
builder.emit(Opcode.EVAL_FALLBACK, id, slot)
|
builder.emit(Opcode.EVAL_FALLBACK, id, slot)
|
||||||
|
updateSlotType(slot, forceType ?: SlotType.OBJ)
|
||||||
return CompiledValue(slot, forceType ?: SlotType.OBJ)
|
return CompiledValue(slot, forceType ?: SlotType.OBJ)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,4 +578,12 @@ class BytecodeCompiler {
|
|||||||
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
|
private fun refPos(ref: BinaryOpRef): Pos = Pos.builtIn
|
||||||
|
|
||||||
|
private fun updateSlotType(slot: Int, type: SlotType) {
|
||||||
|
if (type == SlotType.UNKNOWN) {
|
||||||
|
slotTypes.remove(slot)
|
||||||
|
} else {
|
||||||
|
slotTypes[slot] = type
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -100,7 +100,7 @@ object BytecodeDisassembler {
|
|||||||
Opcode.CMP_EQ_BOOL, Opcode.CMP_NEQ_BOOL,
|
Opcode.CMP_EQ_BOOL, Opcode.CMP_NEQ_BOOL,
|
||||||
Opcode.CMP_EQ_INT_REAL, Opcode.CMP_EQ_REAL_INT, Opcode.CMP_LT_INT_REAL, Opcode.CMP_LT_REAL_INT,
|
Opcode.CMP_EQ_INT_REAL, Opcode.CMP_EQ_REAL_INT, Opcode.CMP_LT_INT_REAL, Opcode.CMP_LT_REAL_INT,
|
||||||
Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT,
|
Opcode.CMP_LTE_INT_REAL, Opcode.CMP_LTE_REAL_INT, Opcode.CMP_GT_INT_REAL, Opcode.CMP_GT_REAL_INT,
|
||||||
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT,
|
Opcode.CMP_GTE_INT_REAL, Opcode.CMP_GTE_REAL_INT, Opcode.CMP_NEQ_INT_REAL, Opcode.CMP_NEQ_REAL_INT,
|
||||||
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
Opcode.AND_BOOL, Opcode.OR_BOOL ->
|
||||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||||
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
Opcode.INC_INT, Opcode.DEC_INT, Opcode.RET ->
|
||||||
|
|||||||
@ -115,6 +115,34 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setObj(dst, frame.getObj(src))
|
frame.setObj(dst, frame.getObj(src))
|
||||||
}
|
}
|
||||||
|
Opcode.INT_TO_REAL -> {
|
||||||
|
val src = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setReal(dst, frame.getInt(src).toDouble())
|
||||||
|
}
|
||||||
|
Opcode.REAL_TO_INT -> {
|
||||||
|
val src = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getReal(src).toLong())
|
||||||
|
}
|
||||||
|
Opcode.BOOL_TO_INT -> {
|
||||||
|
val src = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, if (frame.getBool(src)) 1L else 0L)
|
||||||
|
}
|
||||||
|
Opcode.INT_TO_BOOL -> {
|
||||||
|
val src = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(src) != 0L)
|
||||||
|
}
|
||||||
Opcode.ADD_INT -> {
|
Opcode.ADD_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
@ -124,6 +152,163 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setInt(dst, frame.getInt(a) + frame.getInt(b))
|
frame.setInt(dst, frame.getInt(a) + frame.getInt(b))
|
||||||
}
|
}
|
||||||
|
Opcode.SUB_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) - frame.getInt(b))
|
||||||
|
}
|
||||||
|
Opcode.MUL_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) * frame.getInt(b))
|
||||||
|
}
|
||||||
|
Opcode.DIV_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) / frame.getInt(b))
|
||||||
|
}
|
||||||
|
Opcode.MOD_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) % frame.getInt(b))
|
||||||
|
}
|
||||||
|
Opcode.NEG_INT -> {
|
||||||
|
val src = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, -frame.getInt(src))
|
||||||
|
}
|
||||||
|
Opcode.INC_INT -> {
|
||||||
|
val slot = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(slot, frame.getInt(slot) + 1L)
|
||||||
|
}
|
||||||
|
Opcode.DEC_INT -> {
|
||||||
|
val slot = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(slot, frame.getInt(slot) - 1L)
|
||||||
|
}
|
||||||
|
Opcode.ADD_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setReal(dst, frame.getReal(a) + frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.SUB_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setReal(dst, frame.getReal(a) - frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.MUL_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setReal(dst, frame.getReal(a) * frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.DIV_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setReal(dst, frame.getReal(a) / frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.NEG_REAL -> {
|
||||||
|
val src = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setReal(dst, -frame.getReal(src))
|
||||||
|
}
|
||||||
|
Opcode.AND_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) and frame.getInt(b))
|
||||||
|
}
|
||||||
|
Opcode.OR_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) or frame.getInt(b))
|
||||||
|
}
|
||||||
|
Opcode.XOR_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) xor frame.getInt(b))
|
||||||
|
}
|
||||||
|
Opcode.SHL_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) shl frame.getInt(b).toInt())
|
||||||
|
}
|
||||||
|
Opcode.SHR_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) shr frame.getInt(b).toInt())
|
||||||
|
}
|
||||||
|
Opcode.USHR_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(a) ushr frame.getInt(b).toInt())
|
||||||
|
}
|
||||||
|
Opcode.INV_INT -> {
|
||||||
|
val src = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setInt(dst, frame.getInt(src).inv())
|
||||||
|
}
|
||||||
Opcode.CMP_LT_INT -> {
|
Opcode.CMP_LT_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
@ -133,6 +318,33 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a) < frame.getInt(b))
|
frame.setBool(dst, frame.getInt(a) < frame.getInt(b))
|
||||||
}
|
}
|
||||||
|
Opcode.CMP_LTE_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(a) <= frame.getInt(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_GT_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(a) > frame.getInt(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_GTE_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(a) >= frame.getInt(b))
|
||||||
|
}
|
||||||
Opcode.CMP_EQ_INT -> {
|
Opcode.CMP_EQ_INT -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
@ -151,6 +363,60 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getInt(a) != frame.getInt(b))
|
frame.setBool(dst, frame.getInt(a) != frame.getInt(b))
|
||||||
}
|
}
|
||||||
|
Opcode.CMP_EQ_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) == frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_NEQ_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) != frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_LT_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) < frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_LTE_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) <= frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_GT_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) > frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_GTE_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) >= frame.getReal(b))
|
||||||
|
}
|
||||||
Opcode.CMP_EQ_BOOL -> {
|
Opcode.CMP_EQ_BOOL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
@ -169,6 +435,121 @@ class BytecodeVm {
|
|||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
frame.setBool(dst, frame.getBool(a) != frame.getBool(b))
|
frame.setBool(dst, frame.getBool(a) != frame.getBool(b))
|
||||||
}
|
}
|
||||||
|
Opcode.CMP_EQ_INT_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(a).toDouble() == frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_EQ_REAL_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) == frame.getInt(b).toDouble())
|
||||||
|
}
|
||||||
|
Opcode.CMP_LT_INT_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(a).toDouble() < frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_LT_REAL_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) < frame.getInt(b).toDouble())
|
||||||
|
}
|
||||||
|
Opcode.CMP_LTE_INT_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(a).toDouble() <= frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_LTE_REAL_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) <= frame.getInt(b).toDouble())
|
||||||
|
}
|
||||||
|
Opcode.CMP_GT_INT_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(a).toDouble() > frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_GT_REAL_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) > frame.getInt(b).toDouble())
|
||||||
|
}
|
||||||
|
Opcode.CMP_GTE_INT_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(a).toDouble() >= frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_GTE_REAL_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) >= frame.getInt(b).toDouble())
|
||||||
|
}
|
||||||
|
Opcode.CMP_NEQ_INT_REAL -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getInt(a).toDouble() != frame.getReal(b))
|
||||||
|
}
|
||||||
|
Opcode.CMP_NEQ_REAL_INT -> {
|
||||||
|
val a = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val b = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, frame.getReal(a) != frame.getInt(b).toDouble())
|
||||||
|
}
|
||||||
|
Opcode.NOT_BOOL -> {
|
||||||
|
val src = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
val dst = decoder.readSlot(code, ip)
|
||||||
|
ip += fn.slotWidth
|
||||||
|
frame.setBool(dst, !frame.getBool(src))
|
||||||
|
}
|
||||||
Opcode.AND_BOOL -> {
|
Opcode.AND_BOOL -> {
|
||||||
val a = decoder.readSlot(code, ip)
|
val a = decoder.readSlot(code, ip)
|
||||||
ip += fn.slotWidth
|
ip += fn.slotWidth
|
||||||
|
|||||||
@ -81,6 +81,8 @@ enum class Opcode(val code: Int) {
|
|||||||
CMP_GT_REAL_INT(0x67),
|
CMP_GT_REAL_INT(0x67),
|
||||||
CMP_GTE_INT_REAL(0x68),
|
CMP_GTE_INT_REAL(0x68),
|
||||||
CMP_GTE_REAL_INT(0x69),
|
CMP_GTE_REAL_INT(0x69),
|
||||||
|
CMP_NEQ_INT_REAL(0x6A),
|
||||||
|
CMP_NEQ_REAL_INT(0x6B),
|
||||||
|
|
||||||
NOT_BOOL(0x70),
|
NOT_BOOL(0x70),
|
||||||
AND_BOOL(0x71),
|
AND_BOOL(0x71),
|
||||||
|
|||||||
@ -25,12 +25,16 @@ 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.LocalSlotRef
|
||||||
import net.sergeych.lyng.obj.ObjFalse
|
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.ObjTrue
|
||||||
|
import net.sergeych.lyng.obj.ObjReal
|
||||||
|
import net.sergeych.lyng.obj.AssignRef
|
||||||
import net.sergeych.lyng.obj.ValueFnRef
|
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.toBool
|
||||||
|
import net.sergeych.lyng.obj.toDouble
|
||||||
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
|
||||||
@ -129,4 +133,97 @@ class BytecodeVmTest {
|
|||||||
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||||
assertEquals(true, result.toBool())
|
assertEquals(true, result.toBool())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun realArithmeticUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||||
|
val expr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.PLUS,
|
||||||
|
ConstRef(ObjReal.of(2.5).asReadonly),
|
||||||
|
ConstRef(ObjReal.of(3.25).asReadonly),
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val fn = BytecodeCompiler().compileExpression("realPlus", expr) ?: error("bytecode compile failed")
|
||||||
|
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||||
|
assertEquals(5.75, result.toDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mixedIntRealComparisonUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||||
|
val ltExpr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.LT,
|
||||||
|
ConstRef(ObjInt.of(2).asReadonly),
|
||||||
|
ConstRef(ObjReal.of(2.5).asReadonly),
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val ltFn = BytecodeCompiler().compileExpression("mixedLt", ltExpr) ?: error("bytecode compile failed")
|
||||||
|
val ltResult = BytecodeVm().execute(ltFn, Scope(), emptyList())
|
||||||
|
assertEquals(true, ltResult.toBool())
|
||||||
|
|
||||||
|
val eqExpr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.EQ,
|
||||||
|
ConstRef(ObjReal.of(4.0).asReadonly),
|
||||||
|
ConstRef(ObjInt.of(4).asReadonly),
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val eqFn = BytecodeCompiler().compileExpression("mixedEq", eqExpr) ?: error("bytecode compile failed")
|
||||||
|
val eqResult = BytecodeVm().execute(eqFn, Scope(), emptyList())
|
||||||
|
assertEquals(true, eqResult.toBool())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mixedIntRealArithmeticUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||||
|
val expr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.PLUS,
|
||||||
|
ConstRef(ObjInt.of(2).asReadonly),
|
||||||
|
ConstRef(ObjReal.of(3.5).asReadonly),
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val fn = BytecodeCompiler().compileExpression("mixedPlus", expr) ?: error("bytecode compile failed")
|
||||||
|
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||||
|
assertEquals(5.5, result.toDouble())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mixedIntRealNotEqualUsesBytecodeOps() = kotlinx.coroutines.test.runTest {
|
||||||
|
val expr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.NEQ,
|
||||||
|
ConstRef(ObjInt.of(3).asReadonly),
|
||||||
|
ConstRef(ObjReal.of(2.5).asReadonly),
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val fn = BytecodeCompiler().compileExpression("mixedNeq", expr) ?: error("bytecode compile failed")
|
||||||
|
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||||
|
assertEquals(true, result.toBool())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun localSlotTypeTrackingEnablesArithmetic() = kotlinx.coroutines.test.runTest {
|
||||||
|
val slotRef = LocalSlotRef("a", 0, 0, net.sergeych.lyng.Pos.builtIn)
|
||||||
|
val assign = AssignRef(
|
||||||
|
slotRef,
|
||||||
|
ConstRef(ObjInt.of(2).asReadonly),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val expr = ExpressionStatement(
|
||||||
|
BinaryOpRef(
|
||||||
|
BinOp.PLUS,
|
||||||
|
assign,
|
||||||
|
slotRef
|
||||||
|
),
|
||||||
|
net.sergeych.lyng.Pos.builtIn
|
||||||
|
)
|
||||||
|
val fn = BytecodeCompiler().compileExpression("localSlotAdd", expr) ?: error("bytecode compile failed")
|
||||||
|
val result = BytecodeVm().execute(fn, Scope(), emptyList())
|
||||||
|
assertEquals(4, result.toInt())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user