mirror of
https://github.com/irmen/prog8.git
synced 2026-04-21 17:16:33 +00:00
ir: fix handling of labeled chunks
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 */ }
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user