ir: fix handling of labeled chunks

This commit is contained in:
Irmen de Jong
2022-10-16 18:30:14 +02:00
parent a9f9c40d8a
commit fabae6e970
17 changed files with 517 additions and 424 deletions
@@ -58,7 +58,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
else {
// read and write a (i/o) memory location to itself.
val tempReg = codeGen.registers.nextFree()
val code = IRCodeChunk(null, origAssign.position)
val code = IRCodeChunk(null, origAssign.position, null)
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
listOf(code)
@@ -80,7 +80,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
is PtPrefix -> inplacePrefix(value.operator, vmDt, null, symbol, value.position)
is PtBinaryExpression -> inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
is PtMemoryByte -> {
val code = IRCodeChunk(null, origAssign.position)
val code = IRCodeChunk(null, origAssign.position, null)
val tempReg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
@@ -138,7 +138,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?, position: Position): IRCodeChunks {
val code= IRCodeChunk(null, position)
val code= IRCodeChunk(null, position, null)
when(operator) {
"+" -> { }
"-" -> {
@@ -196,7 +196,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
else
IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
}
result += IRCodeChunk(null, ident.position).also { it += instruction }
result += IRCodeChunk(null, ident.position, null).also { it += instruction }
return result
}
else if(array!=null) {
@@ -211,7 +211,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
throw AssemblyError("non-array var indexing requires bytes index")
val idxReg = codeGen.registers.nextFree()
result += expressionEval.translateExpression(array.index, idxReg, -1)
val code = IRCodeChunk(null, assignment.position)
val code = IRCodeChunk(null, assignment.position, null)
if(zero) {
// there's no STOREZIX instruction
resultRegister = codeGen.registers.nextFree()
@@ -226,33 +226,33 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(zero) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, assignment.position, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val indexReg = codeGen.registers.nextFree()
result += loadIndexReg(array, itemsize, indexReg)
result += IRCodeChunk(null, array.position).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, array.position, null).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
}
} else {
if(vmDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, assignment.position, null).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val indexReg = codeGen.registers.nextFree()
result += loadIndexReg(array, itemsize, indexReg)
result += IRCodeChunk(null, array.position).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, array.position, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
}
} else {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset") }
val chunk = IRCodeChunk(null, assignment.position, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val indexReg = codeGen.registers.nextFree()
result += loadIndexReg(array, itemsize, indexReg)
result += IRCodeChunk(null, array.position).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
result += IRCodeChunk(null, array.position, null).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
}
}
}
@@ -262,21 +262,21 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
require(vmDt== IRDataType.BYTE)
if(zero) {
if(memory.address is PtNumber) {
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) }
val chunk = IRCodeChunk(null, assignment.position, null).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val addressReg = codeGen.registers.nextFree()
result += expressionEval.translateExpression(memory.address, addressReg, -1)
result += IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
result += IRCodeChunk(null, assignment.position, null).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
}
} else {
if(memory.address is PtNumber) {
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) }
val chunk = IRCodeChunk(null, assignment.position, null).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val addressReg = codeGen.registers.nextFree()
result += expressionEval.translateExpression(memory.address, addressReg, -1)
result += IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) }
result += IRCodeChunk(null, assignment.position, null).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) }
}
}
@@ -55,7 +55,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], leftRegister, -1)
result += exprGen.translateExpression(call.args[1], rightRegister, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.CMP, codeGen.irType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
}
return result
@@ -75,7 +75,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], 0, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
if (resultRegister != 0)
@@ -98,7 +98,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], 0, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
if (resultRegister != 0)
@@ -114,32 +114,32 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
when (sourceDt) {
DataType.UBYTE -> {
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = resultRegister)
}
}
DataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=resultRegister)
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=resultRegister)
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultRegister)
}
result += IRCodeChunk(notNegativeLabel, call.position)
result += IRCodeChunk(notNegativeLabel, call.position, null)
}
DataType.WORD -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=resultRegister)
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=resultRegister)
}
result += IRCodeChunk(notNegativeLabel, call.position)
result += IRCodeChunk(notNegativeLabel, call.position, null)
}
else -> throw AssemblyError("weird type")
}
@@ -151,7 +151,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val reg = codeGen.registers.nextFree()
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args.single(), reg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.SGN, codeGen.irType(call.type), reg1 = resultRegister, reg2 = reg)
}
return result
@@ -161,14 +161,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val reg = codeGen.registers.nextFree()
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args.single(), reg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultRegister, reg2=reg)
}
return result
}
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(null, call.position)
val code = IRCodeChunk(null, call.position, null)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
val result = mutableListOf<IRCodeChunkBase>(code)
@@ -177,7 +177,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(null, call.position)
val code = IRCodeChunk(null, call.position, null)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
val result = mutableListOf<IRCodeChunkBase>(code)
@@ -189,7 +189,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>()
val reg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args.single(), reg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=reg)
}
return result
@@ -199,7 +199,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>()
val reg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args.single(), reg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = reg)
}
return result
@@ -217,7 +217,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], 0, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
}
@@ -239,7 +239,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], 0, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
}
@@ -251,7 +251,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], msbReg, -1)
result += exprGen.translateExpression(call.args[1], resultRegister, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = resultRegister, reg2 = msbReg)
}
return result
@@ -262,13 +262,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg)
}
}
@@ -277,14 +277,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
result += exprGen.translateExpression(call.args[1], valueReg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = valueReg, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += exprGen.translateExpression(call.args[1], valueReg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueReg, reg2 = addressReg)
}
}
@@ -297,13 +297,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg)
}
}
@@ -312,14 +312,14 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
result += exprGen.translateExpression(call.args[1], valueReg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueReg, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += exprGen.translateExpression(call.args[1], valueReg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
}
}
@@ -331,13 +331,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>()
if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
}
}
@@ -348,13 +348,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>()
if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
}
}
@@ -362,20 +362,20 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunks {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
code += IRInstruction(Opcode.RND, IRDataType.BYTE, reg1=resultRegister)
return listOf(code)
}
private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunks {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
code += IRInstruction(Opcode.RND, IRDataType.WORD, reg1=resultRegister)
return listOf(code)
}
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val name = (call.args[0] as PtString).value
val code = IRCodeChunk(null, call.position)
val code = IRCodeChunk(null, call.position, null)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
return listOf(code)
}
@@ -388,7 +388,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args.single(), resultRegister, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultRegister, reg2 = resultRegister)
}
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
@@ -399,7 +399,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val vmDt = codeGen.irType(call.args[0].type)
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
result += IRCodeChunk(null, call.position).also {
result += IRCodeChunk(null, call.position, null).also {
it += IRInstruction(opcode, vmDt, reg1 = resultRegister)
}
result += assignRegisterTo(call.args[0], resultRegister)
@@ -16,7 +16,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
is PtMachineRegister -> {
if(resultRegister!=expr.register) {
val vmDt = codeGen.irType(expr.type)
val code = IRCodeChunk(null, expr.position)
val code = IRCodeChunk(null, expr.position, null)
code += IRInstruction(Opcode.LOADR, vmDt, reg1=resultRegister, reg2=expr.register)
listOf(code)
} else {
@@ -25,7 +25,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
is PtNumber -> {
val vmDt = codeGen.irType(expr.type)
val code = IRCodeChunk(null, expr.position)
val code = IRCodeChunk(null, expr.position, null)
code += if(vmDt==IRDataType.FLOAT)
IRInstruction(Opcode.LOAD, vmDt, fpReg1 = resultFpRegister, fpValue = expr.number.toFloat())
else
@@ -35,7 +35,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
is PtIdentifier -> {
val vmDt = codeGen.irType(expr.type)
val symbol = expr.targetName.joinToString(".")
val code = IRCodeChunk(null, expr.position)
val code = IRCodeChunk(null, expr.position, null)
code += if (expr.type in PassByValueDatatypes) {
if(vmDt==IRDataType.FLOAT)
IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = symbol)
@@ -51,7 +51,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val vmDt = codeGen.irType(expr.type)
val symbol = expr.identifier.targetName.joinToString(".")
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
val code = IRCodeChunk(null, expr.position)
val code = IRCodeChunk(null, expr.position, null)
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, labelSymbol = symbol)
listOf(code)
}
@@ -394,7 +394,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=valueReg, fpReg1 = leftFpReg, fpReg2 = rightFpReg), null, binExpr.position)
addInstr(result, IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=valueReg, labelSymbol = label), null, binExpr.position)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=resultRegister, value=0), null, binExpr.position)
result += IRCodeChunk(label, binExpr.position)
result += IRCodeChunk(label, binExpr.position, null)
}
} else {
if(binExpr.left.type==DataType.STR && binExpr.right.type==DataType.STR) {
@@ -985,7 +985,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
internal fun addInstr(code: MutableList<IRCodeChunkBase>, instr: IRInstruction, label: String?, position: Position = Position.DUMMY) {
code += IRCodeChunk(label, position).also {
code += IRCodeChunk(label, position, null).also {
it += instr
}
}
@@ -0,0 +1,43 @@
package prog8.codegen.intermediate
import prog8.code.core.AssemblyError
import prog8.intermediate.*
internal class IRChunkLinker(private val irprog: IRProgram) {
private val labeledChunks = irprog.blocks.flatMap { it.subroutines }.flatMap { it.chunks }.associateBy { it.label }
fun link() {
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
sub.chunks.withIndex().forEach { (index, chunk) ->
if(chunk is IRCodeChunk) {
// link sequential chunks
val jump = chunk.instructions.lastOrNull()?.opcode
if (jump == null || jump !in setOf(Opcode.JUMP, Opcode.JUMPA, Opcode.RETURN)) {
// no jump at the end, so link to next chunk (if it exists)
if(index<sub.chunks.size-1) {
val nextChunk = sub.chunks[index + 1]
if (nextChunk is IRCodeChunk)
chunk.next = nextChunk
else
throw AssemblyError("code chunk flows into following non-code chunk")
}
}
// link all jump and branching instructions to their target
chunk.instructions.forEach {
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.labelSymbol!=null) {
val targetChunk = labeledChunks.getValue(it.labelSymbol)
require(targetChunk is IRCodeChunk) { "target $targetChunk with label ${targetChunk.label} has to be a code chunk" }
it.branchTarget = targetChunk
}
// note: branches with an address value cannot be linked to something...
}
}
}
}
}
}
@@ -49,10 +49,12 @@ class IRCodeGen(
irProg.addBlock(translate(block))
replaceMemoryMappedVars(irProg)
IRChunkLinker(irProg).link()
if(options.optimize) {
val optimizer = IRPeepholeOptimizer(irProg)
optimizer.optimize()
IRChunkLinker(irProg).link() // re-link
}
irProg.validate()
@@ -67,7 +69,7 @@ class IRCodeGen(
val replacements = mutableListOf<Triple<IRCodeChunkBase, Int, UInt>>()
irProg.blocks.asSequence().flatMap { it.subroutines }.flatMap { it.chunks }.forEach { chunk ->
chunk.instructions.withIndex().forEach {
(idx, instr) -> if(instr is IRInstruction) {
(idx, instr) ->
val symbolExpr = instr.labelSymbol
if(symbolExpr!=null) {
val symbol: String
@@ -86,11 +88,10 @@ class IRCodeGen(
}
}
}
}
}
replacements.forEach {
val old = it.first.instructions[it.second] as IRInstruction
val old = it.first.instructions[it.second]
it.first.instructions[it.second] = IRInstruction(
old.opcode,
old.type,
@@ -230,9 +231,9 @@ class IRCodeGen(
is PtIfElse -> translate(node)
is PtPostIncrDecr -> translate(node)
is PtRepeatLoop -> translate(node)
is PtLabel -> listOf(IRCodeChunk(node.name, node.position))
is PtLabel -> listOf(IRCodeChunk(node.name, node.position, null))
is PtBreakpoint -> {
val chunk = IRCodeChunk(null, node.position)
val chunk = IRCodeChunk(null, node.position, null)
chunk += IRInstruction(Opcode.BREAKPOINT)
listOf(chunk)
}
@@ -295,9 +296,9 @@ class IRCodeGen(
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null, branch.position)
val chunks = translateNode(branch.falseScope)
result += labelFirstChunk(chunks, elseLabel)
result += IRCodeChunk(endLabel, branch.position)
result += IRCodeChunk(endLabel, branch.position, null)
} else {
result += IRCodeChunk(elseLabel, branch.position)
result += IRCodeChunk(elseLabel, branch.position, null)
}
return result
}
@@ -307,7 +308,7 @@ class IRCodeGen(
val newChunks = chunks.drop(1).toMutableList()
val labeledFirstChunk = when(val first=chunks[0]) {
is IRCodeChunk -> {
val newChunk = IRCodeChunk(label, first.position)
val newChunk = IRCodeChunk(label, first.position, null)
first.instructions.forEach { newChunk += it }
newChunk
}
@@ -342,7 +343,7 @@ class IRCodeGen(
val skipLabel = createLabelName()
val values = choice.values.children.map {it as PtNumber}
if(values.size==1) {
val chunk = IRCodeChunk(null, whenStmt.position)
val chunk = IRCodeChunk(null, whenStmt.position, null)
chunk += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = skipLabel)
result += chunk
@@ -351,7 +352,7 @@ class IRCodeGen(
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null, whenStmt.position)
} else {
val matchLabel = createLabelName()
val chunk = IRCodeChunk(null, whenStmt.position)
val chunk = IRCodeChunk(null, whenStmt.position, null)
for (value in values) {
chunk += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
@@ -362,10 +363,10 @@ class IRCodeGen(
if(choice.statements.children.last() !is PtReturn)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null, whenStmt.position)
}
result += IRCodeChunk(skipLabel, whenStmt.position)
result += IRCodeChunk(skipLabel, whenStmt.position, null)
}
}
result += IRCodeChunk(endLabel, whenStmt.position)
result += IRCodeChunk(endLabel, whenStmt.position, null)
return result
}
@@ -391,17 +392,17 @@ class IRCodeGen(
if(iterableVar.dt==DataType.STR) {
// iterate over a zero-terminated string
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0), null, forLoop.position)
val chunk = IRCodeChunk(loopLabel, forLoop.position)
val chunk = IRCodeChunk(loopLabel, forLoop.position, null)
chunk += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = symbol)
chunk += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=tmpReg, labelSymbol = endLabel)
chunk += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
result += chunk
result += translateNode(forLoop.statements)
val jumpChunk = IRCodeChunk(null, forLoop.position)
val jumpChunk = IRCodeChunk(null, forLoop.position, null)
jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
jumpChunk += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
result += jumpChunk
result += IRCodeChunk(endLabel, forLoop.position)
result += IRCodeChunk(endLabel, forLoop.position, null)
} else {
// iterate over array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
@@ -409,11 +410,11 @@ class IRCodeGen(
val lengthBytes = iterableVar.length!! * elementSize
if(lengthBytes<256) {
val lengthReg = registers.nextFree()
val chunk = IRCodeChunk(null, forLoop.position)
val chunk = IRCodeChunk(null, forLoop.position, null)
chunk += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0)
chunk += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, value=lengthBytes)
result += chunk
val chunk2 = IRCodeChunk(loopLabel, forLoop.position)
val chunk2 = IRCodeChunk(loopLabel, forLoop.position, null)
chunk2 += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
chunk2 += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
result += chunk2
@@ -422,7 +423,7 @@ class IRCodeGen(
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel), null, forLoop.position)
} else if(lengthBytes==256) {
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0), null, forLoop.position)
val chunk = IRCodeChunk(loopLabel, forLoop.position)
val chunk = IRCodeChunk(loopLabel, forLoop.position, null)
chunk += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
chunk += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
result += chunk
@@ -478,7 +479,7 @@ class IRCodeGen(
val rangeEndWrapped = if(loopvarDt==IRDataType.BYTE) rangeEndUntyped and 255 else rangeEndUntyped and 65535
val endvalueReg: Int
val result = mutableListOf<IRCodeChunkBase>()
val chunk = IRCodeChunk(null, forLoop.position)
val chunk = IRCodeChunk(null, forLoop.position, null)
if(rangeEndWrapped!=0) {
endvalueReg = registers.nextFree()
chunk += IRInstruction(Opcode.LOAD, loopvarDt, reg1 = endvalueReg, value = rangeEndWrapped)
@@ -490,7 +491,7 @@ class IRCodeGen(
result += chunk
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
result += addConstMem(loopvarDt, null, loopvarSymbol, step, iterable.position)
val chunk2 = IRCodeChunk(null, forLoop.position)
val chunk2 = IRCodeChunk(null, forLoop.position, null)
chunk2 += IRInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
chunk2 += if(rangeEndWrapped==0) {
IRInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
@@ -502,7 +503,7 @@ class IRCodeGen(
}
private fun addConstReg(dt: IRDataType, reg: Int, value: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
when(value) {
0 -> { /* do nothing */ }
1 -> {
@@ -531,7 +532,7 @@ class IRCodeGen(
}
private fun addConstMem(dt: IRDataType, knownAddress: UInt?, symbol: String?, value: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
when(value) {
0 -> { /* do nothing */ }
1 -> {
@@ -586,7 +587,7 @@ class IRCodeGen(
}
internal fun multiplyByConstFloat(fpReg: Int, factor: Float, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
if(factor==1f)
return code
code += if(factor==0f) {
@@ -598,7 +599,7 @@ class IRCodeGen(
}
internal fun multiplyByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Float, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
if(factor==1f)
return code
if(factor==0f) {
@@ -620,7 +621,7 @@ class IRCodeGen(
internal val powersOfTwo = (0..16).map { 2.0.pow(it.toDouble()).toInt() }
internal fun multiplyByConst(dt: IRDataType, reg: Int, factor: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
@@ -644,7 +645,7 @@ class IRCodeGen(
}
internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
@@ -683,7 +684,7 @@ class IRCodeGen(
}
internal fun divideByConstFloat(fpReg: Int, factor: Float, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
if(factor==1f)
return code
code += if(factor==0f) {
@@ -695,7 +696,7 @@ class IRCodeGen(
}
internal fun divideByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Float, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
if(factor==1f)
return code
if(factor==0f) {
@@ -717,7 +718,7 @@ class IRCodeGen(
}
internal fun divideByConst(dt: IRDataType, reg: Int, factor: Int, signed: Boolean, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
@@ -746,7 +747,7 @@ class IRCodeGen(
}
internal fun divideByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int, signed: Boolean, position: Position): IRCodeChunk {
val code = IRCodeChunk(null, position)
val code = IRCodeChunk(null, position, null)
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
@@ -833,13 +834,13 @@ class IRCodeGen(
result += translateNode(ifElse.ifScope)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null, ifElse.position)
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
result += IRCodeChunk(afterIfLabel, ifElse.position)
result += IRCodeChunk(afterIfLabel, ifElse.position, null)
} else {
// only if part
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel), null, ifElse.position)
result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, ifElse.position)
result += IRCodeChunk(afterIfLabel, ifElse.position, null)
}
return result
}
@@ -857,13 +858,13 @@ class IRCodeGen(
result += translateNode(ifElse.ifScope)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null, ifElse.position)
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
result += IRCodeChunk(afterIfLabel, ifElse.position)
result += IRCodeChunk(afterIfLabel, ifElse.position, null)
} else {
// only if part
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = afterIfLabel), null, ifElse.position)
result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, ifElse.position)
result += IRCodeChunk(afterIfLabel, ifElse.position, null)
}
return result
}
@@ -919,7 +920,7 @@ class IRCodeGen(
val incReg = registers.nextFree()
val addressReg = registers.nextFree()
result += expressionEval.translateExpression(memory.address, addressReg, -1)
val chunk = IRCodeChunk(null, postIncrDecr.position)
val chunk = IRCodeChunk(null, postIncrDecr.position, null)
chunk += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = addressReg)
chunk += IRInstruction(operationRegister, irDt, reg1 = incReg)
chunk += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = addressReg)
@@ -936,7 +937,7 @@ class IRCodeGen(
val incReg = registers.nextFree()
val indexReg = registers.nextFree()
result += expressionEval.translateExpression(array.index, indexReg, -1)
val chunk = IRCodeChunk(null, postIncrDecr.position)
val chunk = IRCodeChunk(null, postIncrDecr.position, null)
chunk += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
chunk += IRInstruction(operationRegister, irDt, reg1=incReg)
chunk += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
@@ -966,11 +967,11 @@ class IRCodeGen(
result += expressionEval.translateExpression(repeat.count, counterReg, -1)
addInstr(result, IRInstruction(Opcode.BZ, irDt, reg1=counterReg, labelSymbol = skipRepeatLabel), null, repeat.position)
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
val chunk = IRCodeChunk(null, repeat.position)
val chunk = IRCodeChunk(null, repeat.position, null)
chunk += IRInstruction(Opcode.DEC, irDt, reg1=counterReg)
chunk += IRInstruction(Opcode.BNZ, irDt, reg1=counterReg, labelSymbol = repeatLabel)
result += chunk
result += IRCodeChunk(skipRepeatLabel, repeat.position)
result += IRCodeChunk(skipRepeatLabel, repeat.position, null)
return result
}
@@ -7,24 +7,25 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
irprog.blocks.asSequence().flatMap { it.subroutines }.forEach { sub ->
removeEmptyChunks(sub)
joinChunks(sub)
sub.chunks.forEach { chunk ->
sub.chunks.withIndex().forEach { (index, chunk1) ->
// we don't optimize Inline Asm chunks here.
if(chunk is IRCodeChunk) {
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
if(chunk1 is IRCodeChunk) {
do {
val indexedInstructions = chunk.instructions.withIndex()
.filter { it.value is IRInstruction }
.map { IndexedValue(it.index, it.value as IRInstruction) }
val changed = removeNops(chunk, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(chunk, indexedInstructions)
|| removeWeirdBranches(chunk, indexedInstructions)
|| removeDoubleSecClc(chunk, indexedInstructions)
|| cleanupPushPop(chunk, indexedInstructions)
val indexedInstructions = chunk1.instructions.withIndex()
.map { IndexedValue(it.index, it.value) }
val changed = removeNops(chunk1, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(chunk1, indexedInstructions)
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|| removeDoubleSecClc(chunk1, indexedInstructions)
|| cleanupPushPop(chunk1, indexedInstructions)
// TODO other optimizations:
// more complex optimizations such as unused registers
} while (changed)
}
}
removeEmptyChunks(sub)
}
}
@@ -36,6 +37,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original
If next chunk has label -> label name should be the same, remove original. Otherwise FOR NOW leave it in place. (TODO: consolidate labels into 1)
If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label ->
should not have been generated! ERROR.
*/
@@ -45,28 +47,30 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
val removeChunks = mutableListOf<Int>()
sub.chunks.withIndex().forEach { (index, chunk) ->
require(chunk.isNotEmpty() || chunk.label!=null) {
"chunk should have instructions and/or a label"
}
if(chunk is IRCodeChunk && chunk.label!=null && chunk.instructions.isEmpty()) {
val nextchunk = sub.chunks[index+1]
if(nextchunk.label==null) {
// can transplant label to next chunk and remove this empty one.
relabelChunks += Pair(index+1, chunk.label!!)
if(chunk is IRCodeChunk && chunk.instructions.isEmpty()) {
if(chunk.label==null) {
removeChunks += index
} else {
if(chunk.label==nextchunk.label)
removeChunks += index
else {
// TODO: consolidate labels on same chunk
} else {
if (index < sub.chunks.size - 1) {
val nextchunk = sub.chunks[index + 1]
if (nextchunk.label == null) {
// can transplant label to next chunk and remove this empty one.
relabelChunks += Pair(index + 1, chunk.label!!)
removeChunks += index
} else {
if (chunk.label == nextchunk.label)
removeChunks += index
else {
// TODO: consolidate labels on same chunk
}
}
}
}
}
}
relabelChunks.forEach { (index, label) ->
val chunk = IRCodeChunk(label, sub.chunks[index].position)
val chunk = IRCodeChunk(label, sub.chunks[index].position, null)
chunk.instructions += sub.chunks[index].instructions
sub.chunks[index] = chunk
}
@@ -155,20 +159,17 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeWeirdBranches(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
private fun removeWeirdBranches(chunk: IRCodeChunk, nextChunk: IRCodeChunkBase?, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
val labelSymbol = ins.labelSymbol
// remove jump/branch to label immediately below (= next chunk if it has that label)
if(ins.opcode== Opcode.JUMP && labelSymbol!=null) {
// TODO remove jump/branch to label immediately below (= next chunk if it has that label)
// if(idx < chunk.instructions.size-1) {
// val label = chunk.instructions[idx+1] as? IRCodeLabel
// if(label?.name == labelSymbol) {
// chunk.instructions.removeAt(idx)
// changed = true
// }
// }
if(idx==chunk.instructions.size-1 && ins.branchTarget===nextChunk) {
chunk.instructions.removeAt(idx)
changed = true
}
}
// remove useless RETURN
if(ins.opcode == Opcode.RETURN && idx>0) {
+20 -9
View File
@@ -2,6 +2,7 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.codegen.intermediate.IRChunkLinker
import prog8.codegen.intermediate.IRPeepholeOptimizer
import prog8.intermediate.*
@@ -24,11 +25,12 @@ class TestIRPeepholeOpt: FunSpec({
)
val prog = IRProgram("test", IRSymbolTable(null), options, target)
prog.addBlock(block)
prog.validate()
return prog
}
fun makeIRProgram(instructions: List<IRInstruction>): IRProgram {
val chunk = IRCodeChunk(null, Position.DUMMY)
val chunk = IRCodeChunk(null, Position.DUMMY, null)
instructions.forEach { chunk += it }
return makeIRProgram(listOf(chunk))
}
@@ -37,35 +39,38 @@ class TestIRPeepholeOpt: FunSpec({
test("remove nops") {
val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.JUMP, labelSymbol = "dummy"),
IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=42),
IRInstruction(Opcode.NOP),
IRInstruction(Opcode.NOP)
))
irProg.chunks().single().instructions.size shouldBe 3
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.chunks().single().instructions.size shouldBe 1
}
test("remove jmp to label below") {
val c1 = IRCodeChunk(null, Position.DUMMY)
val c1 = IRCodeChunk(null, Position.DUMMY, null)
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed
val c2 = IRCodeChunk("label", Position.DUMMY)
val c2 = IRCodeChunk("label", Position.DUMMY, null)
c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed
c2 += IRInstruction(Opcode.NOP) // removed
val c3 = IRCodeChunk("label2", Position.DUMMY)
val c3 = IRCodeChunk("label2", Position.DUMMY, null)
c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3")
c3 += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=1)
val c4 = IRCodeChunk("label3", Position.DUMMY)
val c4 = IRCodeChunk("label3", Position.DUMMY, null)
val irProg = makeIRProgram(listOf(c1, c2, c3, c4))
irProg.chunks().size shouldBe 4
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.chunks().size shouldBe 2
irProg.chunks()[0].label shouldBe "label2"
irProg.chunks()[1].label shouldBe "label3"
irProg.chunks().size shouldBe 3
irProg.chunks()[0].label shouldBe "label"
irProg.chunks()[1].label shouldBe "label2"
irProg.chunks()[2].label shouldBe "label3"
val instr = irProg.chunks().flatMap { it.instructions }
instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.JUMP
@@ -82,6 +87,7 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.CLC)
))
irProg.chunks().single().instructions.size shouldBe 6
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val instr = irProg.chunks().single().instructions
@@ -97,6 +103,7 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=222)
))
irProg.chunks().single().instructions.size shouldBe 4
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val instr = irProg.chunks().single().instructions
@@ -120,6 +127,7 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0)
))
irProg.chunks().single().instructions.size shouldBe 10
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.chunks().single().instructions.size shouldBe 4
@@ -131,6 +139,7 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1)
))
irProg.chunks().single().instructions.size shouldBe 2
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val instr = irProg.chunks().single().instructions
@@ -151,6 +160,7 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1)
))
irProg.chunks().single().instructions.size shouldBe 8
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.chunks().single().instructions.size shouldBe 4
@@ -164,6 +174,7 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535)
))
irProg.chunks().single().instructions.size shouldBe 4
IRChunkLinker(irProg).link()
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val instr = irProg.chunks().single().instructions
+6 -4
View File
@@ -3,12 +3,14 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- ir: fix program to be list of chunks
- ir: fix unit tests
- ir: check that block and sub labels are also on the first chunk in said block/sub
- ir: link all sequential chunks to another (exiting one chunk 'calls' the next chunk)
- ir: jump/branch instructions don't link to a PC index anymore, but to the actual chunk with that label
- ir: fix joinChunks() in the IR optimizer ? Fix TestIRPeepholeOptimizer and TestVm
- ir: fix JUMP, RETURN and all branching instructions to reference a chunk + index instead of just a single programcounter index
- ir: fix removeWeirdBranches in IR optimizer
- ir: fix unit tests
- ir: fix joinChunks() in the IR optimizer
- vm: program is list of chunks, fix dispatcher
- add cx16diskio.vload_raw() to load headerless files into vram
...
+3 -2
View File
@@ -1,9 +1,10 @@
%import textio
main {
sub start() {
ubyte aa = 42
ubyte bb = 99
aa += bb
%asmbinary "build.gradle"
txt.print_ub(aa)
}
}
@@ -261,7 +261,7 @@ class IRFileReader {
if(line!="<INITGLOBALS>")
throw IRParseException("invalid INITGLOBALS")
line = lines.next()
var chunk = IRCodeChunk(null, Position.DUMMY)
var chunk = IRCodeChunk(null, Position.DUMMY, null)
if(line=="<C>") {
chunk = parseCodeChunk(line, lines)!!
line = lines.next()
@@ -449,14 +449,14 @@ class IRFileReader {
firstline.split('=', limit = 2)[1].dropLast(1)
else
null
val chunk = IRCodeChunk(label, Position.DUMMY)
val chunk = IRCodeChunk(label, Position.DUMMY, null)
while(true) {
val line = lines.next()
if (line == "</C>")
return chunk
if (line.isBlank() || line.startsWith(';'))
continue
val result = parseIRCodeLine(line, 0, mutableMapOf())
val result = parseIRCodeLine(line, null, mutableMapOf())
result.fold(
ifLeft = {
chunk += it
@@ -667,8 +667,9 @@ data class IRInstruction(
val fpReg2: Int?=null, // 0-$ffff
val value: Int?=null, // 0-$ffff
val fpValue: Float?=null,
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
val binaryData: Collection<UByte>?=null
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
val binaryData: Collection<UByte>?=null,
var branchTarget: IRCodeChunk? = null // will be linked after loading
) {
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
// This knowledge is useful in IL assembly optimizers to see how registers are used.
@@ -66,6 +66,9 @@ class IRProgram(val name: String,
}
fun validate() {
// TODO: check that block and sub labels are also on the first chunk in said block/sub
blocks.forEach {
it.inlineAssembly.forEach { chunk ->
require(chunk.instructions.isEmpty())
@@ -182,7 +185,9 @@ abstract class IRCodeChunkBase(val label: String?, val position: Position) {
abstract fun usedRegisters(): RegistersUsed
}
class IRCodeChunk(label: String?, position: Position): IRCodeChunkBase(label, position) {
class IRCodeChunk(label: String?,
position: Position,
var next: IRCodeChunk?): IRCodeChunkBase(label, position) {
override fun isEmpty() = instructions.isEmpty()
override fun isNotEmpty() = instructions.isNotEmpty()
@@ -250,7 +255,7 @@ private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersU
if(isIR) {
assembly.lineSequence().forEach { line ->
val result = parseIRCodeLine(line.trim(), 0, mutableMapOf())
val result = parseIRCodeLine(line.trim(), null, mutableMapOf())
result.fold(
ifLeft = { it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) },
ifRight = { /* labels can be skipped */ }
+9 -7
View File
@@ -84,7 +84,7 @@ fun parseIRValue(value: String): Float {
private val instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""")
fun parseIRCodeLine(line: String, pc: Int, placeholders: MutableMap<Int, String>): Either<IRInstruction, String> {
fun parseIRCodeLine(line: String, location: Pair<IRCodeChunk, Int>?, placeholders: MutableMap<Pair<IRCodeChunk, Int>, String>): Either<IRInstruction, String> {
// Note: this function is used from multiple places:
// the IR File Reader but also the VirtualMachine itself to make sense of any inline vmasm blocks.
val labelmatch = labelPattern.matchEntire(line.trim())
@@ -124,14 +124,16 @@ fun parseIRCodeLine(line: String, pc: Int, placeholders: MutableMap<Int, String>
var operand: String?
var labelSymbol: String? = null
fun parseValueOrPlaceholder(operand: String, pc: Int, rest: String, restIndex: Int): Float? {
fun parseValueOrPlaceholder(operand: String, location: Pair<IRCodeChunk, Int>?, rest: String, restIndex: Int): Float? {
return if(operand.startsWith('_')) {
labelSymbol = rest.split(",")[restIndex].trim().drop(1)
placeholders[pc] = labelSymbol!!
if(location!=null)
placeholders[location] = labelSymbol!!
null
} else if(operand[0].isLetter()) {
labelSymbol = rest.split(",")[restIndex].trim()
placeholders[pc] = labelSymbol!!
if(location!=null)
placeholders[location] = labelSymbol!!
null
} else {
parseIRValue(operand)
@@ -145,7 +147,7 @@ fun parseIRCodeLine(line: String, pc: Int, placeholders: MutableMap<Int, String>
else if(operand[0]=='f' && operand[1]=='r')
fpReg1 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, pc, rest, 0)
value = parseValueOrPlaceholder(operand, location, rest, 0)
operands.clear()
}
if(operands.isNotEmpty()) {
@@ -155,7 +157,7 @@ fun parseIRCodeLine(line: String, pc: Int, placeholders: MutableMap<Int, String>
else if(operand[0]=='f' && operand[1]=='r')
fpReg2 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, pc, rest, 1)
value = parseValueOrPlaceholder(operand, location, rest, 1)
operands.clear()
}
if(operands.isNotEmpty()) {
@@ -165,7 +167,7 @@ fun parseIRCodeLine(line: String, pc: Int, placeholders: MutableMap<Int, String>
else if(operand[0]=='f' && operand[1]=='r')
fpReg3 = operand.substring(2).toInt()
else {
value = parseValueOrPlaceholder(operand, pc, rest, 2)
value = parseValueOrPlaceholder(operand, location, rest, 2)
operands.clear()
}
if(operands.isNotEmpty()) {
+1 -1
View File
@@ -86,7 +86,7 @@ return
<PARAMS>
uword sys.wait.jiffies
</PARAMS>
<INLINEASM IR=true POS=[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]>
<INLINEASM LABEL= IR=true POS=[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]>
loadm.w r0,sys.wait.jiffies
syscall 13
</INLINEASM>
File diff suppressed because it is too large Load Diff
+86 -84
View File
@@ -1,46 +1,48 @@
package prog8.vm
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.intermediate.*
/*
class VmProgramLoader {
private val placeholders = mutableMapOf<Pair<IRCodeChunk, Int>, String>() // program chunk+index to symbolname
private val placeholders = mutableMapOf<Int, String>() // program index to symbolname
fun load(irProgram: IRProgram, memory: Memory): List<IRInstruction> {
// at long last, allocate the variables in memory.
fun load(irProgram: IRProgram, memory: Memory): List<IRCodeChunk> {
placeholders.clear()
irProgram.validate()
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
val symbolAddresses = allocations.allocations.toMutableMap()
val programChunks = mutableListOf<IRCodeChunkBase>()
val programChunks = mutableListOf<IRCodeChunk>()
varsToMemory(irProgram, allocations, symbolAddresses, memory)
if(!irProgram.options.dontReinitGlobals)
addToProgram(irProgram.globalInits, programChunks, symbolAddresses)
if(!irProgram.options.dontReinitGlobals) {
if(irProgram.globalInits.isNotEmpty())
addToProgram(irProgram.globalInits, programChunks, symbolAddresses)
}
// make sure that if there is a "main.start" entrypoint, we jump to it
irProgram.blocks.firstOrNull()?.let {
if(it.subroutines.any { sub -> sub.name=="main.start" }) {
placeholders[program.size] = "main.start"
program += IRInstruction(Opcode.JUMP, labelSymbol = "main.start")
val chunk = IRCodeChunk(null, Position.DUMMY, null)
placeholders[Pair(chunk, 0)] = "main.start"
chunk += IRInstruction(Opcode.JUMP, labelSymbol = "main.start")
programChunks += chunk
}
}
// TODO load rest of the program
irProgram.blocks.forEach { block ->
if(block.address!=null)
throw IRParseException("blocks cannot have a load address for vm: ${block.name}")
block.inlineAssembly.forEach { addAssemblyToProgram(it, program, symbolAddresses) }
block.inlineAssembly.forEach { addAssemblyToProgram(it, programChunks, symbolAddresses) }
block.subroutines.forEach {
symbolAddresses[it.name] = program.size
it.chunks.forEach { chunk ->
when (chunk) {
is IRInlineAsmChunk -> addAssemblyToProgram(chunk, program, symbolAddresses)
is IRInlineBinaryChunk -> program += IRInstruction(Opcode.BINARYDATA, binaryData = chunk.data)
else -> addToProgram(chunk.instructions, program, symbolAddresses)
is IRInlineAsmChunk -> addAssemblyToProgram(chunk, programChunks, symbolAddresses)
is IRInlineBinaryChunk -> TODO("inline binary data not yet supported in the VM")
else -> addToProgram(chunk.instructions, programChunks, symbolAddresses)
}
}
}
@@ -48,15 +50,72 @@ class VmProgramLoader {
throw IRParseException("vm currently does not support asmsubs: ${block.asmSubroutines.first().name}")
}
pass2replaceLabelsByProgIndex(program, symbolAddresses)
pass2replaceLabelsByProgIndex(programChunks, symbolAddresses)
program.forEach {
programChunks.asSequence().flatMap { it.instructions }.forEach {
if(it.opcode in OpcodesWithAddress && it.value==null) {
throw IRParseException("instruction missing numeric value, label not replaced? $it")
}
}
return program
return programChunks
}
private fun pass2replaceLabelsByProgIndex(
chunks: MutableList<IRCodeChunk>,
symbolAddresses: MutableMap<String, Int>
) {
for((ref, label) in placeholders) {
val (chunk, line) = ref
val replacement = symbolAddresses[label]
if(replacement==null) {
// it could be an address + index: symbol+42
if('+' in label) {
val (symbol, indexStr) = label.split('+')
val index = indexStr.toInt()
val address = symbolAddresses.getValue(symbol) + index
chunk.instructions[line] = chunk.instructions[line].copy(value = address)
} else {
throw IRParseException("placeholder not found in labels: $label")
}
} else {
chunk.instructions[line] = chunk.instructions[line].copy(value = replacement)
}
}
}
private fun addToProgram(
instructions: Iterable<IRInstruction>,
program: MutableList<IRCodeChunk>,
symbolAddresses: MutableMap<String, Int>
) {
val chunk = IRCodeChunk(null, Position.DUMMY, null)
instructions.map {
it.labelSymbol?.let { symbol -> placeholders[Pair(chunk, chunk.instructions.size)]=symbol }
if(it.opcode==Opcode.SYSCALL) {
// convert IR Syscall to VM Syscall
val vmSyscall = when(it.value!!) {
IMSyscall.SORT_UBYTE.ordinal -> Syscall.SORT_UBYTE
IMSyscall.SORT_BYTE.ordinal -> Syscall.SORT_BYTE
IMSyscall.SORT_UWORD.ordinal -> Syscall.SORT_UWORD
IMSyscall.SORT_WORD.ordinal -> Syscall.SORT_WORD
IMSyscall.ANY_BYTE.ordinal -> Syscall.ANY_BYTE
IMSyscall.ANY_WORD.ordinal -> Syscall.ANY_WORD
IMSyscall.ANY_FLOAT.ordinal -> Syscall.ANY_FLOAT
IMSyscall.ALL_BYTE.ordinal -> Syscall.ALL_BYTE
IMSyscall.ALL_WORD.ordinal -> Syscall.ALL_WORD
IMSyscall.ALL_FLOAT.ordinal -> Syscall.ALL_FLOAT
IMSyscall.REVERSE_BYTES.ordinal -> Syscall.REVERSE_BYTES
IMSyscall.REVERSE_WORDS.ordinal -> Syscall.REVERSE_WORDS
IMSyscall.REVERSE_FLOATS.ordinal -> Syscall.REVERSE_FLOATS
else -> throw IRParseException("invalid IM syscall number $it")
}
chunk += it.copy(value=vmSyscall.ordinal)
} else {
chunk += it
}
}
program += chunk
}
private fun varsToMemory(
@@ -165,81 +224,24 @@ class VmProgramLoader {
}
}
private fun pass2replaceLabelsByProgIndex(
program: MutableList<IRInstruction>,
symbolAddresses: MutableMap<String, Int>
) {
for((line, label) in placeholders) {
val replacement = symbolAddresses[label]
if(replacement==null) {
// it could be an address + index: symbol+42
if('+' in label) {
val (symbol, indexStr) = label.split('+')
val index = indexStr.toInt()
val address = symbolAddresses.getValue(symbol) + index
program[line] = program[line].copy(value = address)
} else {
throw IRParseException("placeholder not found in labels: $label")
}
} else {
program[line] = program[line].copy(value = replacement)
}
}
}
private fun addToProgram(
chunks: Iterable<IRCodeChunkBase>,
program: MutableList<IRCodeChunkBase>,
symbolAddresses: MutableMap<String, Int>
) {
instructions.map {
when(it) {
is IRInstruction -> {
it.labelSymbol?.let { symbol -> placeholders[program.size]=symbol }
if(it.opcode==Opcode.SYSCALL) {
// convert IR Syscall to VM Syscall
val vmSyscall = when(it.value!!) {
IMSyscall.SORT_UBYTE.ordinal -> Syscall.SORT_UBYTE
IMSyscall.SORT_BYTE.ordinal -> Syscall.SORT_BYTE
IMSyscall.SORT_UWORD.ordinal -> Syscall.SORT_UWORD
IMSyscall.SORT_WORD.ordinal -> Syscall.SORT_WORD
IMSyscall.ANY_BYTE.ordinal -> Syscall.ANY_BYTE
IMSyscall.ANY_WORD.ordinal -> Syscall.ANY_WORD
IMSyscall.ANY_FLOAT.ordinal -> Syscall.ANY_FLOAT
IMSyscall.ALL_BYTE.ordinal -> Syscall.ALL_BYTE
IMSyscall.ALL_WORD.ordinal -> Syscall.ALL_WORD
IMSyscall.ALL_FLOAT.ordinal -> Syscall.ALL_FLOAT
IMSyscall.REVERSE_BYTES.ordinal -> Syscall.REVERSE_BYTES
IMSyscall.REVERSE_WORDS.ordinal -> Syscall.REVERSE_WORDS
IMSyscall.REVERSE_FLOATS.ordinal -> Syscall.REVERSE_FLOATS
else -> throw IRParseException("invalid IM syscall number $it")
}
program += it.copy(value=vmSyscall.ordinal)
} else {
program += it
}
}
is IRCodeLabel -> { symbolAddresses[it.name] = program.size }
}
}
}
private fun addAssemblyToProgram(
asmChunk: IRInlineAsmChunk,
program: MutableList<IRInstruction>,
chunks: MutableList<IRCodeChunk>,
symbolAddresses: MutableMap<String, Int>,
) {
if(asmChunk.isIR) {
val chunk = IRCodeChunk(asmChunk.label, asmChunk.position, null)
asmChunk.assembly.lineSequence().forEach {
val parsed = parseIRCodeLine(it.trim(), program.size, placeholders)
if (parsed is IRInstruction)
program += parsed
else if (parsed is IRCodeLabel)
symbolAddresses[parsed.name] = program.size
val parsed = parseIRCodeLine(it.trim(), Pair(chunk, chunk.instructions.size), placeholders)
parsed.fold(
ifLeft = { instruction -> chunk += instruction },
ifRight = { label -> symbolAddresses[label] = chunk.instructions.size }
)
}
chunks += chunk
} else {
throw IRParseException("vm currently does not support real inlined assembly (only IR): ${asmChunk.position}")
}
}
}
*/
+10 -9
View File
@@ -29,20 +29,20 @@ class TestVm: FunSpec( {
val vm = VirtualMachine(program)
vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe 0
vm.pcIndex shouldBe 0
vm.stepCount shouldBe 0
vm.run()
vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe 0
vm.stepCount shouldBe 1
vm.pcIndex shouldBe 0
vm.stepCount shouldBe 0
}
test("vm execution: modify memory") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(null, Position.DUMMY)
val code = IRCodeChunk(null, Position.DUMMY, null)
code += IRInstruction(Opcode.NOP)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, value=12345)
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, value=1000)
@@ -55,21 +55,22 @@ class TestVm: FunSpec( {
vm.memory.getUW(1000) shouldBe 0u
vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe 0
vm.pcIndex shouldBe 0
vm.stepCount shouldBe 0
vm.run()
vm.stepCount shouldBe 4
vm.memory.getUW(1000) shouldBe 12345u
vm.callStack.shouldBeEmpty()
vm.valueStack.shouldBeEmpty()
vm.pc shouldBe code.instructions.size-1
vm.pcIndex shouldBe code.instructions.size-1
vm.stepCount shouldBe code.instructions.size
}
test("vm asmsub not supported") {
test("vm asmbinary not supported") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, Position.DUMMY)
val code = IRCodeChunk(null, Position.DUMMY)
val code = IRCodeChunk(null, Position.DUMMY, null)
code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u))
code += IRInstruction(Opcode.RETURN)
startSub += code
@@ -81,7 +82,7 @@ class TestVm: FunSpec( {
}
}
test("vm asmbinary not supported") {
test("vm asmsub not supported") {
val program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRAsmSubroutine(