ir: moving to labeled chunks, no more IRLabel nodes

This commit is contained in:
Irmen de Jong 2022-10-07 00:34:56 +02:00
parent 2340760f53
commit 6fc89607d3
17 changed files with 997 additions and 846 deletions

View File

@ -1,8 +1,7 @@
package prog8
package prog8.code
/**
* By convention, the right side of an `Either` is used to hold successful values.
*
*/
sealed class Either<out L, out R> {

View File

@ -53,7 +53,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) {
internal fun translateFunctionCall(call: IFunctionCall, isExpression: Boolean) { // TODO remove isExpression unused parameter
// Output only the code to set up the parameters and perform the actual call
// NOTE: does NOT output the code to deal with the result values!
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!

View File

@ -5,14 +5,11 @@ import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.core.SignedDatatypes
import prog8.intermediate.IRCodeChunk
import prog8.intermediate.IRDataType
import prog8.intermediate.IRInstruction
import prog8.intermediate.Opcode
import prog8.intermediate.*
internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) {
internal fun translate(assignment: PtAssignment): IRCodeChunk {
internal fun translate(assignment: PtAssignment): IRCodeChunks {
if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
@ -22,7 +19,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
translateRegularAssign(assignment)
}
private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunk {
private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunks {
val ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
@ -48,23 +45,23 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
address: Int,
value: PtExpression,
origAssign: PtAssignment
): IRCodeChunk {
): IRCodeChunks {
val vmDt = codeGen.irType(value.type)
val code = IRCodeChunk(origAssign.position)
when(value) {
is PtIdentifier -> return code // do nothing, x=x null assignment.
is PtMachineRegister -> return code // do nothing, reg=reg null assignment
is PtIdentifier -> return emptyList() // do nothing, x=x null assignment.
is PtMachineRegister -> return emptyList() // do nothing, reg=reg null assignment
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null, value.position)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign)
is PtMemoryByte -> {
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
code // do nothing, mem=mem null assignment.
emptyList() // do nothing, mem=mem null assignment.
else {
// read and write a (i/o) memory location to itself.
val tempReg = codeGen.registers.nextFree()
val code = IRCodeChunk(null, origAssign.position)
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
code
listOf(code)
}
}
else -> return fallbackAssign(origAssign)
@ -75,25 +72,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
symbol: String,
value: PtExpression,
origAssign: PtAssignment
): IRCodeChunk {
): IRCodeChunks {
val vmDt = codeGen.irType(value.type)
val code = IRCodeChunk(origAssign.position)
when(value) {
is PtIdentifier -> return code // do nothing, x=x null assignment.
is PtMachineRegister -> return code // do nothing, reg=reg null assignment
is PtPrefix -> return inplacePrefix(value.operator, vmDt, null, symbol, value.position)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
return when(value) {
is PtIdentifier -> emptyList() // do nothing, x=x null assignment.
is PtMachineRegister -> emptyList() // do nothing, reg=reg null assignment
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 tempReg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, labelSymbol = symbol)
return code
listOf(code)
}
else -> return fallbackAssign(origAssign)
else -> fallbackAssign(origAssign)
}
}
private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunk {
private fun fallbackAssign(origAssign: PtAssignment): IRCodeChunks {
if (codeGen.options.slowCodegenWarnings)
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
return translateRegularAssign(origAssign)
@ -107,7 +105,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
knownAddress: Int?,
symbol: String?,
origAssign: PtAssignment
): IRCodeChunk {
): IRCodeChunks {
if(knownAddress!=null) {
when (operator) {
"+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand)
@ -139,8 +137,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return fallbackAssign(origAssign)
}
private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?, position: Position): IRCodeChunk {
val code= IRCodeChunk(position)
private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?, position: Position): IRCodeChunks {
val code= IRCodeChunk(null, position)
when(operator) {
"+" -> { }
"-" -> {
@ -160,17 +158,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
else -> throw AssemblyError("weird prefix operator")
}
return code
return listOf(code)
}
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunk {
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
val ident = assignment.target.identifier
val memory = assignment.target.memory
val array = assignment.target.array
val vmDt = codeGen.irType(assignment.value.type)
val code = IRCodeChunk(assignment.position)
val result = mutableListOf<IRCodeChunkBase>()
var resultRegister = -1
var resultFpRegister = -1
val zero = codeGen.isZero(assignment.value)
@ -178,20 +175,20 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
// calculate the assignment value
if (vmDt == IRDataType.FLOAT) {
resultFpRegister = codeGen.registers.nextFreeFloat()
code += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
result += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
} else {
resultRegister = if (assignment.value is PtMachineRegister) {
(assignment.value as PtMachineRegister).register
} else {
val reg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(assignment.value, reg, -1)
result += expressionEval.translateExpression(assignment.value, reg, -1)
reg
}
}
}
if(ident!=null) {
val symbol = ident.targetName.joinToString(".")
code += if(zero) {
val instruction = if(zero) {
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol)
} else {
if (vmDt == IRDataType.FLOAT)
@ -199,6 +196,8 @@ 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 }
return result
}
else if(array!=null) {
val variable = array.variable.targetName.joinToString(".")
@ -211,84 +210,90 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(array.index.type!=DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
val idxReg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(array.index, idxReg, -1)
result += expressionEval.translateExpression(array.index, idxReg, -1)
val code = IRCodeChunk(null, assignment.position)
if(zero) {
// there's no STOREZIX instruction
resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
}
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
return code
result += code
return result
}
val fixedIndex = constIntValue(array.index)
if(zero) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
code += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset")
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val indexReg = codeGen.registers.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position)
code += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable)
result += loadIndexReg(array, itemsize, indexReg)
result += IRCodeChunk(null, array.position).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
}
} else {
if(vmDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
code += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset")
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREM, vmDt, fpReg1 = resultFpRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val indexReg = codeGen.registers.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position)
code += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
result += loadIndexReg(array, itemsize, indexReg)
result += IRCodeChunk(null, array.position).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
}
} else {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset")
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = "$variable+$offset") }
result += chunk
} else {
val indexReg = codeGen.registers.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position)
code += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable)
result += loadIndexReg(array, itemsize, indexReg)
result += IRCodeChunk(null, array.position).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
}
}
}
return result
}
else if(memory!=null) {
require(vmDt== IRDataType.BYTE)
if(zero) {
if(memory.address is PtNumber) {
code += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt())
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREZM, vmDt, value=(memory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val addressReg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg)
result += expressionEval.translateExpression(memory.address, addressReg, -1)
result += IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
}
} else {
if(memory.address is PtNumber) {
code += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt())
val chunk = IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) }
result += chunk
} else {
val addressReg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg)
result += expressionEval.translateExpression(memory.address, addressReg, -1)
result += IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) }
}
}
return result
}
else
throw AssemblyError("weird assigntarget")
return code
}
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
if(itemsize==1) {
code += expressionEval.translateExpression(array.index, indexReg, -1)
}
else {
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): IRCodeChunks {
return if(itemsize==1) {
expressionEval.translateExpression(array.index, indexReg, -1)
} else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
code += expressionEval.translateExpression(mult, indexReg, -1)
expressionEval.translateExpression(mult, indexReg, -1)
}
return code
}
}

View File

@ -10,7 +10,7 @@ import prog8.intermediate.*
internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) {
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
return when(call.name) {
"any" -> funcAny(call, resultRegister)
"all" -> funcAll(call, resultRegister)
@ -25,7 +25,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"rsave",
"rsavex",
"rrestore",
"rrestorex" -> IRCodeChunk(call.position) // vm doesn't have registers to save/restore
"rrestorex" -> emptyList() // vm doesn't have registers to save/restore
"rnd" -> funcRnd(resultRegister, call.position)
"rndw" -> funcRndw(resultRegister, call.position)
"callfar" -> throw AssemblyError("callfar() is for cx16 target only")
@ -37,7 +37,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"peekw" -> funcPeekW(call, resultRegister)
"poke" -> funcPoke(call)
"pokew" -> funcPokeW(call)
"pokemon" -> IRCodeChunk(call.position)
"pokemon" -> emptyList()
"mkword" -> funcMkword(call, resultRegister)
"sort" -> funcSort(call)
"reverse" -> funcReverse(call)
@ -49,20 +49,21 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunks {
val leftRegister = codeGen.registers.nextFree()
val rightRegister = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], leftRegister, -1)
code += exprGen.translateExpression(call.args[1], rightRegister, -1)
code += IRInstruction(Opcode.CMP, codeGen.irType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
return code
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 {
it += IRInstruction(Opcode.CMP, codeGen.irType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
}
return result
}
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val code = IRCodeChunk(call.position)
val syscall =
when (array.dt) {
DataType.ARRAY_UB,
@ -72,15 +73,18 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
else -> throw IllegalArgumentException("weird type")
}
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if (resultRegister != 0)
code += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
return code
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], 0, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
if (resultRegister != 0)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
}
return result
}
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val syscall =
@ -92,98 +96,116 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_F -> IMSyscall.ALL_FLOAT
else -> throw IllegalArgumentException("weird type")
}
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=array.length)
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
if(resultRegister!=0)
code += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultRegister, reg2=0)
return code
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], 0, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
if (resultRegister != 0)
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
}
return result
}
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val sourceDt = call.args.single().type
val result = mutableListOf<IRCodeChunkBase>()
if(sourceDt!=DataType.UWORD) {
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
when (sourceDt) {
DataType.UBYTE -> {
code += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultRegister)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = resultRegister)
}
}
DataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=resultRegister)
code += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
code += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=resultRegister)
code += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultRegister)
code += IRCodeLabel(notNegativeLabel)
result += IRCodeChunk(null, call.position).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)
}
DataType.WORD -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=resultRegister)
code += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
code += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=resultRegister)
code += IRCodeLabel(notNegativeLabel)
result += IRCodeChunk(null, call.position).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)
}
else -> throw AssemblyError("weird type")
}
}
return code
return result
}
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRInstruction(Opcode.SGN, codeGen.irType(call.type), reg1=resultRegister, reg2=reg)
return code
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args.single(), reg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.SGN, codeGen.irType(call.type), reg1 = resultRegister, reg2 = reg)
}
return result
}
private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultRegister, reg2=reg)
return code
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args.single(), reg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultRegister, reg2=reg)
}
return result
}
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(null, call.position)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
code += assignRegisterTo(call.args.single(), reg)
return code
val result = mutableListOf<IRCodeChunkBase>(code)
result += assignRegisterTo(call.args.single(), reg)
return result
}
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(null, call.position)
val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
code += assignRegisterTo(call.args.single(), reg)
return code
val result = mutableListOf<IRCodeChunkBase>(code)
result += assignRegisterTo(call.args.single(), reg)
return result
}
private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=reg)
return code
result += exprGen.translateExpression(call.args.single(), reg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=reg)
}
return result
}
private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1=reg)
return code
result += exprGen.translateExpression(call.args.single(), reg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1 = reg)
}
return result
}
private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunk {
private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunks {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val syscall =
@ -193,14 +215,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
else -> throw IllegalArgumentException("weird type to reverse")
}
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=array.length)
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
return code
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], 0, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
}
return result
}
private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunk {
private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunks {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val syscall =
@ -213,153 +237,184 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
else -> throw IllegalArgumentException("weird type to sort")
}
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], 0, -1)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=array.length)
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal)
return code
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], 0, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
}
return result
}
private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
private fun funcMkword(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val msbReg = codeGen.registers.nextFree()
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], msbReg, -1)
code += exprGen.translateExpression(call.args[1], resultRegister, -1)
code += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=msbReg)
return code
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 {
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = resultRegister, reg2 = msbReg)
}
return result
}
private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.WORD, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg)
result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg)
}
}
} else {
val valueReg = codeGen.registers.nextFree()
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = valueReg, value = address)
result += exprGen.translateExpression(call.args[1], valueReg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = valueReg, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueReg, reg2 = addressReg)
result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += exprGen.translateExpression(call.args[1], valueReg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueReg, reg2 = addressReg)
}
}
}
return code
return result
}
private fun funcPoke(call: PtBuiltinFunctionCall): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcPoke(call: PtBuiltinFunctionCall): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg)
result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg)
}
}
} else {
val valueReg = codeGen.registers.nextFree()
if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueReg, value = address)
result += exprGen.translateExpression(call.args[1], valueReg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueReg, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += exprGen.translateExpression(call.args[1], valueReg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueReg, reg2 = addressReg)
}
}
}
return code
return result
}
private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = resultRegister, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultRegister, reg2 = addressReg)
}
}
return code
return result
}
private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = IRCodeChunk(call.position)
private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt()
code += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, value = address)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, value = address)
}
} else {
val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
result += exprGen.translateExpression(call.args.single(), addressReg, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultRegister, reg2 = addressReg)
}
}
return code
return result
}
private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunks {
val code = IRCodeChunk(null, position)
code += IRInstruction(Opcode.RND, IRDataType.BYTE, reg1=resultRegister)
return code
return listOf(code)
}
private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunks {
val code = IRCodeChunk(null, position)
code += IRInstruction(Opcode.RND, IRDataType.WORD, reg1=resultRegister)
return code
return listOf(code)
}
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
private fun funcMemory(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val name = (call.args[0] as PtString).value
val code = IRCodeChunk(call.position)
val code = IRCodeChunk(null, call.position)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultRegister, labelSymbol = "prog8_slabs.prog8_memoryslab_$name")
return code
return listOf(code)
}
private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
return exprGen.translateExpression(call.args.single(), resultRegister, -1)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return code
}
private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultRegister, reg2=resultRegister)
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 {
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.
return code
return result
}
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk {
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val vmDt = codeGen.irType(call.args[0].type)
val code = IRCodeChunk(call.position)
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
code += IRInstruction(opcode, vmDt, reg1=resultRegister)
code += assignRegisterTo(call.args[0], resultRegister)
return code
val result = mutableListOf<IRCodeChunkBase>()
result += exprGen.translateExpression(call.args[0], resultRegister, -1)
result += IRCodeChunk(null, call.position).also {
it += IRInstruction(opcode, vmDt, reg1 = resultRegister)
}
result += assignRegisterTo(call.args[0], resultRegister)
return result
}
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunk {
val code = IRCodeChunk(target.position)
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val code = IRCodeChunk(null, target.position)
val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position)
assignTarget.children.add(target)
assignment.children.add(assignTarget)
assignment.children.add(PtMachineRegister(register, target.type, target.position))
code += codeGen.translateNode(assignment)
return code
val result = mutableListOf<IRCodeChunkBase>(code)
result += codeGen.translateNode(assignment)
return result
}
}

View File

@ -34,9 +34,12 @@ class IRCodeGen(
if(!options.dontReinitGlobals) {
// collect global variables initializers
program.allBlocks().forEach {
val code = IRCodeChunk(it.position)
it.children.filterIsInstance<PtAssignment>().forEach { assign -> code += assignmentGen.translate(assign) }
irProg.addGlobalInits(code)
val result = mutableListOf<IRCodeChunkBase>()
it.children.filterIsInstance<PtAssignment>().forEach { assign -> result += assignmentGen.translate(assign) }
result.forEach { chunk ->
if (chunk is IRCodeChunk) irProg.addGlobalInits(chunk)
else throw AssemblyError("only expect code chunk for global inits")
}
}
}
@ -209,17 +212,17 @@ class IRCodeGen(
}
}
internal fun translateNode(node: PtNode): IRCodeChunkBase {
val code = when(node) {
is PtScopeVarsDecls -> IRCodeChunk(node.position) // vars should be looked up via symbol table
is PtVariable -> IRCodeChunk(node.position) // var should be looked up via symbol table
is PtMemMapped -> IRCodeChunk(node.position) // memmapped var should be looked up via symbol table
is PtConstant -> IRCodeChunk(node.position) // constants have all been folded into the code
internal fun translateNode(node: PtNode): IRCodeChunks {
return when(node) {
is PtScopeVarsDecls -> emptyList() // vars should be looked up via symbol table
is PtVariable -> emptyList() // var should be looked up via symbol table
is PtMemMapped -> emptyList() // memmapped var should be looked up via symbol table
is PtConstant -> emptyList() // constants have all been folded into the code
is PtAssignment -> assignmentGen.translate(node)
is PtNodeGroup -> translateGroup(node.children, node.position)
is PtNodeGroup -> translateGroup(node.children)
is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0)
is PtFunctionCall -> expressionEval.translate(node, 0, 0)
is PtNop -> IRCodeChunk(node.position)
is PtNop -> emptyList()
is PtReturn -> translate(node)
is PtJump -> translate(node)
is PtWhen -> translate(node)
@ -227,23 +230,19 @@ class IRCodeGen(
is PtIfElse -> translate(node)
is PtPostIncrDecr -> translate(node)
is PtRepeatLoop -> translate(node)
is PtLabel -> {
val chunk = IRCodeChunk(node.position)
chunk += IRCodeLabel(node.name)
return chunk
}
is PtLabel -> listOf(IRCodeChunk(node.name, node.position))
is PtBreakpoint -> {
val chunk = IRCodeChunk(node.position)
val chunk = IRCodeChunk(null, node.position)
chunk += IRInstruction(Opcode.BREAKPOINT)
return chunk
listOf(chunk)
}
is PtConditionalBranch -> translate(node)
is PtInlineAssembly -> IRInlineAsmChunk(node.assembly, node.isIR, node.position)
is PtInlineAssembly -> listOf(IRInlineAsmChunk(null, node.assembly, node.isIR, node.position))
is PtIncludeBinary -> {
val data = node.file.readBytes()
.drop(node.offset?.toInt() ?: 0)
.take(node.length?.toInt() ?: Int.MAX_VALUE)
return IRInlineBinaryChunk(data.map { it.toUByte() }, node.position)
listOf(IRInlineBinaryChunk(null, data.map { it.toUByte() }, node.position))
}
is PtAddressOf,
is PtContainmentCheck,
@ -265,14 +264,13 @@ class IRCodeGen(
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
else -> TODO("missing codegen for $node")
}
return code
}
private fun translate(branch: PtConditionalBranch): IRCodeChunk {
val code = IRCodeChunk(branch.position)
private fun translate(branch: PtConditionalBranch): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val elseLabel = createLabelName()
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
code += when(branch.condition) {
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCC, labelSymbol = elseLabel)
BranchCondition.CC -> IRInstruction(Opcode.BSTCS, labelSymbol = elseLabel)
BranchCondition.EQ, BranchCondition.Z -> IRInstruction(Opcode.BSTNE, labelSymbol = elseLabel)
@ -282,70 +280,97 @@ class IRCodeGen(
BranchCondition.VC -> IRInstruction(Opcode.BSTVC, labelSymbol = elseLabel)
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = elseLabel)
}
code += translateNode(branch.trueScope)
addInstr(result, branchIns, null, branch.position)
result += translateNode(branch.trueScope)
if(branch.falseScope.children.isNotEmpty()) {
val endLabel = createLabelName()
code += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
code += IRCodeLabel(elseLabel)
code += translateNode(branch.falseScope)
code += IRCodeLabel(endLabel)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null, branch.position)
val chunks = translateNode(branch.falseScope)
result += labelFirstChunk(chunks, elseLabel)
result += IRCodeChunk(endLabel, branch.position)
} else {
code += IRCodeLabel(elseLabel)
result += IRCodeChunk(elseLabel, branch.position)
}
return code
return result
}
private fun translate(whenStmt: PtWhen): IRCodeChunk {
val code = IRCodeChunk(whenStmt.position)
private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks {
require(chunks.isNotEmpty() && label.isNotBlank())
val newChunks = chunks.drop(1).toMutableList()
val labeledFirstChunk = when(val first=chunks[0]) {
is IRCodeChunk -> {
val newChunk = IRCodeChunk(label, first.position)
first.instructions.forEach { newChunk += it }
newChunk
}
is IRInlineAsmChunk -> {
IRInlineAsmChunk(label, first.assembly, first.isIR, first.position)
}
is IRInlineBinaryChunk -> {
IRInlineBinaryChunk(label, first.data, first.position)
}
else -> {
throw AssemblyError("invalid chunk")
}
}
newChunks.add(0, labeledFirstChunk)
return newChunks
}
private fun translate(whenStmt: PtWhen): IRCodeChunks {
if(whenStmt.choices.children.isEmpty())
return code
return emptyList()
val result = mutableListOf<IRCodeChunkBase>()
val valueReg = registers.nextFree()
val choiceReg = registers.nextFree()
val valueDt = irType(whenStmt.value.type)
code += expressionEval.translateExpression(whenStmt.value, valueReg, -1)
result += expressionEval.translateExpression(whenStmt.value, valueReg, -1)
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
val endLabel = createLabelName()
for (choice in choices) {
if(choice.isElse) {
code += translateNode(choice.statements)
result += translateNode(choice.statements)
} else {
val skipLabel = createLabelName()
val values = choice.values.children.map {it as PtNumber}
if(values.size==1) {
code += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=values[0].number.toInt())
code += IRInstruction(Opcode.BNE, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = skipLabel)
code += translateNode(choice.statements)
val chunk = IRCodeChunk(null, whenStmt.position)
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
result += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
code += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null, whenStmt.position)
} else {
val matchLabel = createLabelName()
val chunk = IRCodeChunk(null, whenStmt.position)
for (value in values) {
code += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
code += IRInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
chunk += IRInstruction(Opcode.LOAD, valueDt, reg1=choiceReg, value=value.number.toInt())
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueReg, reg2=choiceReg, labelSymbol = matchLabel)
}
code += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
code += IRCodeLabel(matchLabel)
code += translateNode(choice.statements)
chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
result += chunk
result += labelFirstChunk(translateNode(choice.statements), matchLabel)
if(choice.statements.children.last() !is PtReturn)
code += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null, whenStmt.position)
}
code += IRCodeLabel(skipLabel)
result += IRCodeChunk(skipLabel, whenStmt.position)
}
}
code += IRCodeLabel(endLabel)
return code
result += IRCodeChunk(endLabel, whenStmt.position)
return result
}
private fun translate(forLoop: PtForLoop): IRCodeChunk {
private fun translate(forLoop: PtForLoop): IRCodeChunks {
val loopvar = symbolTable.lookup(forLoop.variable.targetName) as StStaticVariable
val iterable = forLoop.iterable
val code = IRCodeChunk(forLoop.position)
val result = mutableListOf<IRCodeChunkBase>()
when(iterable) {
is PtRange -> {
if(iterable.from is PtNumber && iterable.to is PtNumber)
code += translateForInConstantRange(forLoop, loopvar)
result += translateForInConstantRange(forLoop, loopvar)
else
code += translateForInNonConstantRange(forLoop, loopvar)
result += translateForInNonConstantRange(forLoop, loopvar)
}
is PtIdentifier -> {
val symbol = iterable.targetName.joinToString(".")
@ -357,15 +382,18 @@ class IRCodeGen(
val endLabel = createLabelName()
if(iterableVar.dt==DataType.STR) {
// iterate over a zero-terminated string
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0)
code += IRCodeLabel(loopLabel)
code += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = symbol)
code += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=tmpReg, labelSymbol = endLabel)
code += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol)
code += translateNode(forLoop.statements)
code += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
code += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
code += IRCodeLabel(endLabel)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0), null, forLoop.position)
val chunk = IRCodeChunk(loopLabel, forLoop.position)
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)
jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
jumpChunk += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
result += jumpChunk
result += IRCodeChunk(endLabel, forLoop.position)
} else {
// iterate over array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
@ -373,22 +401,26 @@ class IRCodeGen(
val lengthBytes = iterableVar.length!! * elementSize
if(lengthBytes<256) {
val lengthReg = registers.nextFree()
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, value=lengthBytes)
code += IRCodeLabel(loopLabel)
code += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
code += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
code += translateNode(forLoop.statements)
code += addConstReg(IRDataType.BYTE, indexReg, elementSize, iterable.position)
code += IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel)
val chunk = IRCodeChunk(null, forLoop.position)
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)
chunk2 += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
chunk2 += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
result += chunk2
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize, iterable.position)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, reg2=lengthReg, labelSymbol = loopLabel), null, forLoop.position)
} else if(lengthBytes==256) {
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0)
code += IRCodeLabel(loopLabel)
code += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
code += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
code += translateNode(forLoop.statements)
code += addConstReg(IRDataType.BYTE, indexReg, elementSize, iterable.position)
code += IRInstruction(Opcode.BNZ, IRDataType.BYTE, reg1=indexReg, labelSymbol = loopLabel)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, value=0), null, forLoop.position)
val chunk = IRCodeChunk(loopLabel, forLoop.position)
chunk += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=symbol)
chunk += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
result += chunk
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize, iterable.position)
addInstr(result, IRInstruction(Opcode.BNZ, IRDataType.BYTE, reg1=indexReg, labelSymbol = loopLabel), null, forLoop.position)
} else {
throw AssemblyError("iterator length should never exceed 256")
}
@ -396,10 +428,10 @@ class IRCodeGen(
}
else -> throw AssemblyError("weird for iterable")
}
return code
return result
}
private fun translateForInNonConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): IRCodeChunk {
private fun translateForInNonConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): IRCodeChunks {
val iterable = forLoop.iterable as PtRange
val step = iterable.step.number.toInt()
if (step==0)
@ -409,21 +441,20 @@ class IRCodeGen(
val loopvarSymbol = loopvar.scopedName.joinToString(".")
val loopvarDt = irType(loopvar.dt)
val loopLabel = createLabelName()
val code = IRCodeChunk(forLoop.position)
val result = mutableListOf<IRCodeChunkBase>()
code += expressionEval.translateExpression(iterable.to, endvalueReg, -1)
code += expressionEval.translateExpression(iterable.from, indexReg, -1)
code += IRInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
code += IRCodeLabel(loopLabel)
code += translateNode(forLoop.statements)
code += addConstMem(loopvarDt, null, loopvarSymbol, step, iterable.position)
code += IRInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
result += expressionEval.translateExpression(iterable.to, endvalueReg, -1)
result += expressionEval.translateExpression(iterable.from, indexReg, -1)
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol), null, forLoop.position)
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
result += addConstMem(loopvarDt, null, loopvarSymbol, step, iterable.position)
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol), null, forLoop.position)
val branchOpcode = if(loopvar.dt in SignedDatatypes) Opcode.BLES else Opcode.BLE
code += IRInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel)
return code
addInstr(result, IRInstruction(branchOpcode, loopvarDt, reg1=indexReg, reg2=endvalueReg, labelSymbol=loopLabel), null, forLoop.position)
return result
}
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): IRCodeChunk {
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StStaticVariable): IRCodeChunks {
val loopLabel = createLabelName()
val loopvarSymbol = loopvar.scopedName.joinToString(".")
val indexReg = registers.nextFree()
@ -437,30 +468,33 @@ class IRCodeGen(
if(step>0 && rangeEndUntyped<rangeStart || step<0 && rangeEndUntyped>rangeStart)
throw AssemblyError("empty range")
val rangeEndWrapped = if(loopvarDt==IRDataType.BYTE) rangeEndUntyped and 255 else rangeEndUntyped and 65535
val code = IRCodeChunk(forLoop.position)
val endvalueReg: Int
val result = mutableListOf<IRCodeChunkBase>()
val chunk = IRCodeChunk(null, forLoop.position)
if(rangeEndWrapped!=0) {
endvalueReg = registers.nextFree()
code += IRInstruction(Opcode.LOAD, loopvarDt, reg1 = endvalueReg, value = rangeEndWrapped)
chunk += IRInstruction(Opcode.LOAD, loopvarDt, reg1 = endvalueReg, value = rangeEndWrapped)
} else {
endvalueReg = -1 // not used
}
code += IRInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=rangeStart)
code += IRInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
code += IRCodeLabel(loopLabel)
code += translateNode(forLoop.statements)
code += addConstMem(loopvarDt, null, loopvarSymbol, step, iterable.position)
code += IRInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
code += if(rangeEndWrapped==0) {
chunk += IRInstruction(Opcode.LOAD, loopvarDt, reg1=indexReg, value=rangeStart)
chunk += IRInstruction(Opcode.STOREM, loopvarDt, reg1=indexReg, labelSymbol=loopvarSymbol)
result += chunk
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
result += addConstMem(loopvarDt, null, loopvarSymbol, step, iterable.position)
val chunk2 = IRCodeChunk(null, forLoop.position)
chunk2 += IRInstruction(Opcode.LOADM, loopvarDt, reg1 = indexReg, labelSymbol = loopvarSymbol)
chunk2 += if(rangeEndWrapped==0) {
IRInstruction(Opcode.BNZ, loopvarDt, reg1 = indexReg, labelSymbol = loopLabel)
} else {
IRInstruction(Opcode.BNE, loopvarDt, reg1 = indexReg, reg2 = endvalueReg, labelSymbol = loopLabel)
}
return code
result += chunk2
return result
}
private fun addConstReg(dt: IRDataType, reg: Int, value: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
val code = IRCodeChunk(null, position)
when(value) {
0 -> { /* do nothing */ }
1 -> {
@ -489,7 +523,7 @@ class IRCodeGen(
}
private fun addConstMem(dt: IRDataType, knownAddress: UInt?, symbol: String?, value: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
val code = IRCodeChunk(null, position)
when(value) {
0 -> { /* do nothing */ }
1 -> {
@ -544,7 +578,7 @@ class IRCodeGen(
}
internal fun multiplyByConstFloat(fpReg: Int, factor: Float, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
val code = IRCodeChunk(null, position)
if(factor==1f)
return code
code += if(factor==0f) {
@ -556,7 +590,7 @@ class IRCodeGen(
}
internal fun multiplyByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Float, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
val code = IRCodeChunk(null, position)
if(factor==1f)
return code
if(factor==0f) {
@ -578,7 +612,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(position)
val code = IRCodeChunk(null, position)
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
@ -602,7 +636,7 @@ class IRCodeGen(
}
internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
val code = IRCodeChunk(null, position)
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
@ -641,7 +675,7 @@ class IRCodeGen(
}
internal fun divideByConstFloat(fpReg: Int, factor: Float, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
val code = IRCodeChunk(null, position)
if(factor==1f)
return code
code += if(factor==0f) {
@ -653,7 +687,7 @@ class IRCodeGen(
}
internal fun divideByConstFloatInplace(knownAddress: Int?, symbol: String?, factor: Float, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
val code = IRCodeChunk(null, position)
if(factor==1f)
return code
if(factor==0f) {
@ -675,7 +709,7 @@ class IRCodeGen(
}
internal fun divideByConst(dt: IRDataType, reg: Int, factor: Int, signed: Boolean, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
val code = IRCodeChunk(null, position)
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
@ -704,7 +738,7 @@ class IRCodeGen(
}
internal fun divideByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int, signed: Boolean, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
val code = IRCodeChunk(null, position)
if(factor==1)
return code
val pow2 = powersOfTwo.indexOf(factor)
@ -760,15 +794,15 @@ class IRCodeGen(
return code
}
private fun translate(ifElse: PtIfElse): IRCodeChunk {
private fun translate(ifElse: PtIfElse): IRCodeChunks {
if(ifElse.condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression")
val signed = ifElse.condition.left.type in arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val irDt = irType(ifElse.condition.left.type)
val code = IRCodeChunk(ifElse.position)
fun translateNonZeroComparison(): IRCodeChunk {
fun translateNonZeroComparison(): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val elseBranch = when(ifElse.condition.operator) {
"==" -> Opcode.BNE
"!=" -> Opcode.BEQ
@ -781,50 +815,49 @@ class IRCodeGen(
val leftReg = registers.nextFree()
val rightReg = registers.nextFree()
code += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
code += expressionEval.translateExpression(ifElse.condition.right, rightReg, -1)
result += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
result += expressionEval.translateExpression(ifElse.condition.right, rightReg, -1)
if(ifElse.elseScope.children.isNotEmpty()) {
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
code += IRInstruction(elseBranch, irDt, reg1=leftReg, reg2=rightReg, labelSymbol = elseLabel)
code += translateNode(ifElse.ifScope)
code += IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += IRCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope)
code += IRCodeLabel(afterIfLabel)
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, reg2=rightReg, labelSymbol = elseLabel), null, ifElse.position)
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)
} else {
// only if part
val afterIfLabel = createLabelName()
code += IRInstruction(elseBranch, irDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel)
code += translateNode(ifElse.ifScope)
code += IRCodeLabel(afterIfLabel)
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, reg2=rightReg, labelSymbol = afterIfLabel), null, ifElse.position)
result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, ifElse.position)
}
return code
return result
}
fun translateZeroComparison(): IRCodeChunk {
fun equalOrNotEqualZero(elseBranch: Opcode): IRCodeChunk {
fun translateZeroComparison(): IRCodeChunks {
fun equalOrNotEqualZero(elseBranch: Opcode): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val leftReg = registers.nextFree()
code += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
result += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
if(ifElse.elseScope.children.isNotEmpty()) {
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
code += IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = elseLabel)
code += translateNode(ifElse.ifScope)
code += IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel)
code += IRCodeLabel(elseLabel)
code += translateNode(ifElse.elseScope)
code += IRCodeLabel(afterIfLabel)
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = elseLabel), null, ifElse.position)
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)
} else {
// only if part
val afterIfLabel = createLabelName()
code += IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = afterIfLabel)
code += translateNode(ifElse.ifScope)
code += IRCodeLabel(afterIfLabel)
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = afterIfLabel), null, ifElse.position)
result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, ifElse.position)
}
return code
return result
}
return when (ifElse.condition.operator) {
@ -849,8 +882,7 @@ class IRCodeGen(
translateNonZeroComparison()
}
private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunk {
val code = IRCodeChunk(postIncrDecr.position)
private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks {
val operationMem: Opcode
val operationRegister: Opcode
when(postIncrDecr.operator) {
@ -868,19 +900,22 @@ class IRCodeGen(
val memory = postIncrDecr.target.memory
val array = postIncrDecr.target.array
val irDt = irType(postIncrDecr.target.type)
val result = mutableListOf<IRCodeChunkBase>()
if(ident!=null) {
code += IRInstruction(operationMem, irDt, labelSymbol = ident.targetName.joinToString("."))
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.targetName.joinToString(".")), null, postIncrDecr.position)
} else if(memory!=null) {
if(memory.address is PtNumber) {
val address = (memory.address as PtNumber).number.toInt()
code += IRInstruction(operationMem, irDt, value = address)
addInstr(result, IRInstruction(operationMem, irDt, value = address), null, postIncrDecr.position)
} else {
val incReg = registers.nextFree()
val addressReg = registers.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = addressReg)
code += IRInstruction(operationRegister, irDt, reg1 = incReg)
code += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = addressReg)
result += expressionEval.translateExpression(memory.address, addressReg, -1)
val chunk = IRCodeChunk(null, postIncrDecr.position)
chunk += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = addressReg)
chunk += IRInstruction(operationRegister, irDt, reg1 = incReg)
chunk += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = addressReg)
result += chunk
}
} else if (array!=null) {
val variable = array.variable.targetName.joinToString(".")
@ -888,25 +923,27 @@ class IRCodeGen(
val fixedIndex = constIntValue(array.index)
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
code += IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset")
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null, postIncrDecr.position)
} else {
val incReg = registers.nextFree()
val indexReg = registers.nextFree()
code += expressionEval.translateExpression(array.index, indexReg, -1)
code += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
code += IRInstruction(operationRegister, irDt, reg1=incReg)
code += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexReg, labelSymbol=variable)
result += expressionEval.translateExpression(array.index, indexReg, -1)
val chunk = IRCodeChunk(null, postIncrDecr.position)
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)
result += chunk
}
} else
throw AssemblyError("weird assigntarget")
return code
return result
}
private fun translate(repeat: PtRepeatLoop): IRCodeChunk {
private fun translate(repeat: PtRepeatLoop): IRCodeChunks {
when (constIntValue(repeat.count)) {
0 -> return IRCodeChunk(repeat.position)
1 -> return translateGroup(repeat.children, repeat.position)
0 -> return emptyList()
1 -> return translateGroup(repeat.children)
256 -> {
// 256 iterations can still be done with just a byte counter if you set it to zero as starting value.
repeat.children[0] = PtNumber(DataType.UBYTE, 0.0, repeat.count.position)
@ -915,22 +952,23 @@ class IRCodeGen(
val repeatLabel = createLabelName()
val skipRepeatLabel = createLabelName()
val code = IRCodeChunk(repeat.position)
val counterReg = registers.nextFree()
val irDt = irType(repeat.count.type)
code += expressionEval.translateExpression(repeat.count, counterReg, -1)
code += IRInstruction(Opcode.BZ, irDt, reg1=counterReg, labelSymbol = skipRepeatLabel)
code += IRCodeLabel(repeatLabel)
code += translateNode(repeat.statements)
code += IRInstruction(Opcode.DEC, irDt, reg1=counterReg)
code += IRInstruction(Opcode.BNZ, irDt, reg1=counterReg, labelSymbol = repeatLabel)
code += IRCodeLabel(skipRepeatLabel)
return code
val result = mutableListOf<IRCodeChunkBase>()
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)
chunk += IRInstruction(Opcode.DEC, irDt, reg1=counterReg)
chunk += IRInstruction(Opcode.BNZ, irDt, reg1=counterReg, labelSymbol = repeatLabel)
result += chunk
result += IRCodeChunk(skipRepeatLabel, repeat.position)
return result
}
private fun translate(jump: PtJump): IRCodeChunk {
val code = IRCodeChunk(jump.position)
code += if(jump.address!=null) {
private fun translate(jump: PtJump): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val instr = if(jump.address!=null) {
IRInstruction(Opcode.JUMPA, value = jump.address!!.toInt())
} else {
if (jump.generatedLabel != null)
@ -940,27 +978,28 @@ class IRCodeGen(
else
throw AssemblyError("weird jump")
}
return code
addInstr(result, instr, null, jump.position)
return result
}
private fun translateGroup(group: List<PtNode>, position: Position): IRCodeChunk {
val code = IRCodeChunk(position)
group.forEach { code += translateNode(it) }
return code
private fun translateGroup(group: List<PtNode>): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
group.forEach { result += translateNode(it) }
return result
}
private fun translate(ret: PtReturn): IRCodeChunk {
val code = IRCodeChunk(ret.position)
private fun translate(ret: PtReturn): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val value = ret.value
if(value!=null) {
// Call Convention: return value is always returned in r0 (or fr0 if float)
code += if(value.type==DataType.FLOAT)
result += if(value.type==DataType.FLOAT)
expressionEval.translateExpression(value, -1, 0)
else
expressionEval.translateExpression(value, 0, -1)
}
code += IRInstruction(Opcode.RETURN)
return code
addInstr(result, IRInstruction(Opcode.RETURN), null, ret.position)
return result
}
private fun translate(block: PtBlock): IRBlock {
@ -973,9 +1012,7 @@ class IRCodeGen(
is PtSub -> {
val sub = IRSubroutine(child.name, translate(child.parameters), child.returntype, child.position)
for (subchild in child.children) {
val translated = translateNode(subchild)
if(translated.isNotEmpty())
sub += translated
translateNode(subchild).forEach { sub += it }
}
irBlock += sub
}
@ -991,7 +1028,7 @@ class IRCodeGen(
)
}
is PtInlineAssembly -> {
irBlock += IRInlineAsmChunk(child.assembly, child.isIR, child.position)
irBlock += IRInlineAsmChunk(null, child.assembly, child.isIR, child.position)
}
else -> TODO("weird child node $child")
}
@ -1034,7 +1071,7 @@ class IRCodeGen(
return "prog8_label_gen_$labelSequenceNumber"
}
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk =
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks =
builtinFuncGen.translate(call, resultRegister)
internal fun isZero(expression: PtExpression): Boolean = expression is PtNumber && expression.number==0.0

View File

@ -34,7 +34,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
TODO: this has to be changed later...
*/
if(sub.chunks.isEmpty())
/* if(sub.chunks.isEmpty())
return
fun mayJoin(previous: IRCodeChunkBase, chunk: IRCodeChunkBase): Boolean {
@ -56,7 +56,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += sub.chunks[ix]
}
sub.chunks.clear()
sub.chunks += chunks
sub.chunks += chunks*/
}
private fun cleanupPushPop(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
@ -112,15 +112,16 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
val labelSymbol = ins.labelSymbol
if(ins.opcode== Opcode.JUMP && labelSymbol!=null) {
// remove jump/branch to label immediately below
if(idx < chunk.instructions.size-1) {
val label = chunk.instructions[idx+1] as? IRCodeLabel
if(label?.name == labelSymbol) {
chunk.instructions.removeAt(idx)
changed = true
}
}
// 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
// }
// }
}
// remove useless RETURN
if(ins.opcode == Opcode.RETURN && idx>0) {

View File

@ -6,13 +6,10 @@ import prog8.codegen.intermediate.IRPeepholeOptimizer
import prog8.intermediate.*
class TestIRPeepholeOpt: FunSpec({
fun makeIRProgram(instructions: List<IRCodeLine>): IRProgram {
fun makeIRProgram(chunks: List<IRCodeChunkBase>): IRProgram {
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val sub = IRSubroutine("main.start", emptyList(), null, Position.DUMMY)
val chunk = IRCodeChunk(Position.DUMMY)
for(instr in instructions)
chunk += instr
sub += chunk
chunks.forEach { sub += it }
block += sub
val target = VMTarget()
val options = CompilationOptions(
@ -30,7 +27,13 @@ class TestIRPeepholeOpt: FunSpec({
return prog
}
fun IRProgram.instructions(): List<IRCodeLine> = this.blocks.flatMap { it.subroutines }.flatMap { it.chunks }.flatMap { it.instructions }
fun makeIRProgram(instructions: List<IRInstruction>): IRProgram {
val chunk = IRCodeChunk(null, Position.DUMMY)
instructions.forEach { chunk += it }
return makeIRProgram(listOf(chunk))
}
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.subroutines }.flatMap { it.chunks }
test("remove nops") {
val irProg = makeIRProgram(listOf(
@ -38,33 +41,35 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.NOP),
IRInstruction(Opcode.NOP)
))
irProg.instructions().size shouldBe 3
irProg.chunks().single().instructions.size shouldBe 3
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
irProg.instructions().size shouldBe 1
irProg.chunks().single().instructions.size shouldBe 1
}
test("remove jmp to label below") {
val irProg = makeIRProgram(listOf(
IRInstruction(Opcode.JUMP, labelSymbol = "label"), // removed
IRCodeLabel("label"),
IRInstruction(Opcode.JUMP, labelSymbol = "label2"), // removed
IRInstruction(Opcode.NOP), // removed
IRCodeLabel("label2"),
IRInstruction(Opcode.JUMP, labelSymbol = "label3"),
IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=1),
IRCodeLabel("label3")
))
irProg.instructions().size shouldBe 8
val c1 = IRCodeChunk(null, Position.DUMMY)
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") // removed
val c2 = IRCodeChunk("label", Position.DUMMY)
c2 += IRInstruction(Opcode.JUMP, labelSymbol = "label2") // removed
c2 += IRInstruction(Opcode.NOP) // removed
val c3 = IRCodeChunk("label2", Position.DUMMY)
c3 += IRInstruction(Opcode.JUMP, labelSymbol = "label3")
c3 += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=1)
val c4 = IRCodeChunk("label3", Position.DUMMY)
val irProg = makeIRProgram(listOf(c1, c2, c3, c4))
irProg.chunks().size shouldBe 4
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.instructions()
lines.size shouldBe 5
(lines[0] as IRCodeLabel).name shouldBe "label"
(lines[1] as IRCodeLabel).name shouldBe "label2"
(lines[2] as IRInstruction).opcode shouldBe Opcode.JUMP
(lines[3] as IRInstruction).opcode shouldBe Opcode.INC
(lines[4] as IRCodeLabel).name shouldBe "label3"
irProg.chunks().size shouldBe 2
irProg.chunks()[0].label shouldBe "label2"
irProg.chunks()[1].label shouldBe "label3"
val instr = irProg.chunks().flatMap { it.instructions }
instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.JUMP
instr[1].opcode shouldBe Opcode.INC
}
test("remove double sec/clc") {
@ -76,12 +81,12 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.CLC),
IRInstruction(Opcode.CLC)
))
irProg.instructions().size shouldBe 6
irProg.chunks().single().instructions.size shouldBe 6
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.instructions()
lines.size shouldBe 1
(lines[0] as IRInstruction).opcode shouldBe Opcode.CLC
val instr = irProg.chunks().single().instructions
instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.CLC
}
test("push followed by pop") {
@ -91,14 +96,14 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=99),
IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=222)
))
irProg.instructions().size shouldBe 4
irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.instructions()
lines.size shouldBe 1
(lines[0] as IRInstruction).opcode shouldBe Opcode.LOADR
(lines[0] as IRInstruction).reg1 shouldBe 222
(lines[0] as IRInstruction).reg2 shouldBe 99
val instr = irProg.chunks().single().instructions
instr.size shouldBe 1
instr[0].opcode shouldBe Opcode.LOADR
instr[0].reg1 shouldBe 222
instr[0].reg2 shouldBe 99
}
test("remove useless div/mul, add/sub") {
@ -114,11 +119,10 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 0),
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 0)
))
irProg.instructions().size shouldBe 10
irProg.chunks().single().instructions.size shouldBe 10
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.instructions()
lines.size shouldBe 4
irProg.chunks().single().instructions.size shouldBe 4
}
test("replace add/sub 1 by inc/dec") {
@ -126,13 +130,13 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.ADD, IRDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.SUB, IRDataType.BYTE, reg1=42, value = 1)
))
irProg.instructions().size shouldBe 2
irProg.chunks().single().instructions.size shouldBe 2
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.instructions()
lines.size shouldBe 2
(lines[0] as IRInstruction).opcode shouldBe Opcode.INC
(lines[1] as IRInstruction).opcode shouldBe Opcode.DEC
val instr = irProg.chunks().single().instructions
instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.INC
instr[1].opcode shouldBe Opcode.DEC
}
test("remove useless and/or/xor") {
@ -146,11 +150,10 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 1),
IRInstruction(Opcode.XOR, IRDataType.BYTE, reg1=42, value = 1)
))
irProg.instructions().size shouldBe 8
irProg.chunks().single().instructions.size shouldBe 8
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.instructions()
lines.size shouldBe 4
irProg.chunks().single().instructions.size shouldBe 4
}
test("replace and/or/xor by constant number") {
@ -160,18 +163,18 @@ class TestIRPeepholeOpt: FunSpec({
IRInstruction(Opcode.OR, IRDataType.BYTE, reg1=42, value = 255),
IRInstruction(Opcode.OR, IRDataType.WORD, reg1=42, value = 65535)
))
irProg.instructions().size shouldBe 4
irProg.chunks().single().instructions.size shouldBe 4
val opt = IRPeepholeOptimizer(irProg)
opt.optimize()
val lines = irProg.instructions()
lines.size shouldBe 4
(lines[0] as IRInstruction).opcode shouldBe Opcode.LOAD
(lines[1] as IRInstruction).opcode shouldBe Opcode.LOAD
(lines[2] as IRInstruction).opcode shouldBe Opcode.LOAD
(lines[3] as IRInstruction).opcode shouldBe Opcode.LOAD
(lines[0] as IRInstruction).value shouldBe 0
(lines[1] as IRInstruction).value shouldBe 0
(lines[2] as IRInstruction).value shouldBe 255
(lines[3] as IRInstruction).value shouldBe 65535
val instr = irProg.chunks().single().instructions
instr.size shouldBe 4
instr[0].opcode shouldBe Opcode.LOAD
instr[1].opcode shouldBe Opcode.LOAD
instr[2].opcode shouldBe Opcode.LOAD
instr[3].opcode shouldBe Opcode.LOAD
instr[0].value shouldBe 0
instr[1].value shouldBe 0
instr[2].value shouldBe 255
instr[3].value shouldBe 65535
}
})

View File

@ -4,7 +4,10 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- ir: get rid of IRCodeLabel, make every label start a new code chunk, give those a 'label' property.
- ir: fix joinChunks() in the IR optimizer ?
- ir: fix program to be list of chunks
- 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
- vm: program is list of chunks, fix dispatcher
...

View File

@ -32,7 +32,7 @@ class IRFileReader {
val memorymapped = parseMemMapped(lines)
val slabs = parseSlabs(lines)
val initGlobals = parseInitGlobals(lines)
val blocks = parseBlocksUntilProgramEnd(lines, variables)
val blocks = parseBlocksUntilProgramEnd(lines)
val st = IRSymbolTable(null)
asmsymbols.forEach { (name, value) -> st.addAsmSymbol(name, value)}
@ -261,9 +261,9 @@ class IRFileReader {
if(line!="<INITGLOBALS>")
throw IRParseException("invalid INITGLOBALS")
line = lines.next()
var chunk = IRCodeChunk(Position.DUMMY)
var chunk = IRCodeChunk(null, Position.DUMMY)
if(line=="<C>") {
chunk = parseCodeChunk(line, lines)!!
chunk = parseCodeChunk(line, lines, null)!!
line = lines.next()
}
if(line!="</INITGLOBALS>")
@ -271,7 +271,7 @@ class IRFileReader {
return chunk
}
private fun parseBlocksUntilProgramEnd(lines: Iterator<String>, variables: List<StStaticVariable>): List<IRBlock> {
private fun parseBlocksUntilProgramEnd(lines: Iterator<String>): List<IRBlock> {
val blocks = mutableListOf<IRBlock>()
while(true) {
var line = lines.next()
@ -279,7 +279,7 @@ class IRFileReader {
line = lines.next()
if (line == "</PROGRAM>")
break
blocks.add(parseBlock(line, lines, variables))
blocks.add(parseBlock(line, lines))
}
return blocks
}
@ -291,7 +291,7 @@ class IRFileReader {
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
private fun parseBlock(startline: String, lines: Iterator<String>, variables: List<StStaticVariable>): IRBlock {
private fun parseBlock(startline: String, lines: Iterator<String>): IRBlock {
var line = startline
if(!line.startsWith("<BLOCK "))
throw IRParseException("invalid BLOCK")
@ -306,20 +306,20 @@ class IRFileReader {
if(line=="</BLOCK>")
return block
if(line.startsWith("<SUB ")) {
val sub = parseSubroutine(line, lines, variables)
val sub = parseSubroutine(line, lines)
block += sub
} else if(line.startsWith("<ASMSUB ")) {
val sub = parseAsmSubroutine(line, lines)
block += sub
} else if(line.startsWith("<INLINEASM ")) {
val asm = parseInlineAssembly(line, lines)
val asm = parseInlineAssembly(line, lines, null)
block += asm
} else
throw IRParseException("invalid line in BLOCK")
}
}
private fun parseInlineAssembly(startline: String, lines: Iterator<String>): IRInlineAsmChunk {
private fun parseInlineAssembly(startline: String, lines: Iterator<String>, label: String?): IRInlineAsmChunk {
// <INLINEASM IR=true POS=[examples/test.p8: line 8 col 6-9]>
val match = inlineAsmPattern.matchEntire(startline) ?: throw IRParseException("invalid INLINEASM")
val isIr = match.groupValues[1].toBoolean()
@ -330,7 +330,7 @@ class IRFileReader {
asmlines.add(line)
line = lines.next()
}
return IRInlineAsmChunk(asmlines.joinToString("\n"), isIr, pos)
return IRInlineAsmChunk(label, asmlines.joinToString("\n"), isIr, pos)
}
private fun parseAsmSubroutine(startline: String, lines: Iterator<String>): IRAsmSubroutine {
@ -352,7 +352,7 @@ class IRFileReader {
params += Pair(dt, regsf)
}
line = lines.next()
val asm = parseInlineAssembly(line, lines)
val asm = parseInlineAssembly(line, lines, null)
while(line!="</ASMSUB>")
line = lines.next()
val clobberRegs = if(clobbers.isBlank()) emptyList() else clobbers.split(',').map { CpuRegister.valueOf(it) }
@ -374,12 +374,12 @@ class IRFileReader {
)
}
private fun parseSubroutine(startline: String, lines: Iterator<String>, variables: List<StStaticVariable>): IRSubroutine {
private fun parseSubroutine(startline: String, lines: Iterator<String>): IRSubroutine {
// <SUB NAME=main.start.nested.nested2 RETURNTYPE=null POS=[examples/test.p8: line 54 col 14-16]>
val match = subPattern.matchEntire(startline) ?: throw IRParseException("invalid SUB")
val (name, returntype, pos) = match.destructured
val sub = IRSubroutine(name,
parseParameters(lines, variables),
parseParameters(lines),
if(returntype=="null") null else parseDatatype(returntype, false),
parsePosition(pos))
while(true) {
@ -387,11 +387,11 @@ class IRFileReader {
if(line=="</SUB>")
return sub
val chunk = if(line=="<C>")
parseCodeChunk(line, lines)
parseCodeChunk(line, lines, null)
else if(line.startsWith("<BYTES "))
parseBinaryBytes(line, lines)
parseBinaryBytes(line, lines, null)
else if(line.startsWith("<INLINEASM "))
parseInlineAssembly(line, lines)
parseInlineAssembly(line, lines, null)
else
throw IRParseException("invalid sub child node")
@ -406,7 +406,7 @@ class IRFileReader {
return sub
}
private fun parseBinaryBytes(startline: String, lines: Iterator<String>): IRInlineBinaryChunk {
private fun parseBinaryBytes(startline: String, lines: Iterator<String>, label: String?): IRInlineBinaryChunk {
val match = bytesPattern.matchEntire(startline) ?: throw IRParseException("invalid BYTES")
val pos = parsePosition(match.groupValues[1])
val bytes = mutableListOf<UByte>()
@ -417,10 +417,10 @@ class IRFileReader {
}
line = lines.next()
}
return IRInlineBinaryChunk(bytes, pos)
return IRInlineBinaryChunk(label, bytes, pos)
}
private fun parseParameters(lines: Iterator<String>, variables: List<StStaticVariable>): List<IRSubroutine.IRParam> {
private fun parseParameters(lines: Iterator<String>): List<IRSubroutine.IRParam> {
var line = lines.next()
if(line!="<PARAMS>")
throw IRParseException("missing PARAMS")
@ -436,21 +436,29 @@ class IRFileReader {
}
}
private fun parseCodeChunk(firstline: String, lines: Iterator<String>): IRCodeChunk? {
private fun parseCodeChunk(firstline: String, lines: Iterator<String>, label: String?): IRCodeChunk? {
if(firstline!="<C>") {
if(firstline=="</SUB>")
return null
else
throw IRParseException("invalid or empty <C>ODE chunk")
}
val chunk = IRCodeChunk(Position.DUMMY)
val chunk = IRCodeChunk(label, Position.DUMMY)
while(true) {
val line = lines.next()
if (line == "</C>")
return chunk
if (line.isBlank() || line.startsWith(';'))
continue
chunk += parseIRCodeLine(line, 0, mutableMapOf())
val result = parseIRCodeLine(line, 0, mutableMapOf())
result.fold(
ifLeft = {
chunk += it
},
ifRight = {
TODO("PROCESS LABEL $it")
}
)
}
}

View File

@ -1,7 +1,9 @@
package prog8.intermediate
import prog8.code.core.*
import java.io.BufferedWriter
import prog8.code.core.ArrayDatatypes
import prog8.code.core.DataType
import prog8.code.core.InternalCompilerException
import prog8.code.core.NumericDatatypes
import java.nio.file.Path
import kotlin.io.path.bufferedWriter
import kotlin.io.path.div
@ -25,7 +27,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
if(!irProgram.options.dontReinitGlobals) {
out.write("<C>\n")
// note: this a block of code that loads values and stores them into the global variables to reset their values.
irProgram.globalInits.forEach { out.writeLine(it) }
irProgram.globalInits.forEach { out.write(it.toString()) }
out.write("</C>\n")
}
out.write("</INITGLOBALS>\n")
@ -67,7 +69,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
throw InternalCompilerException("empty code chunk in ${it.name} ${it.position}")
chunk.instructions.forEach { instr ->
numInstr++
out.writeLine(instr)
out.write(instr.toString())
}
out.write("</C>\n")
}
@ -179,12 +181,4 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
irProgram.st.allMemorySlabs().forEach{ slab -> out.write("SLAB ${slab.name} ${slab.size} ${slab.align}\n") }
out.write("</MEMORYSLABS>\n")
}
private fun BufferedWriter.writeLine(line: IRCodeLine) {
when(line) {
is IRInstruction -> write(line.toString() + "\n")
is IRCodeLabel -> write("_${line.name}:\n")
else -> throw AssemblyError("invalid vm code line")
}
}
}

View File

@ -669,7 +669,7 @@ data class IRInstruction(
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
): IRCodeLine() {
) {
// 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.
val reg1direction: OperandDirection

View File

@ -52,7 +52,7 @@ class IRProgram(val name: String,
val encoding: IStringEncoding) {
val asmSymbols = mutableMapOf<String, String>()
val globalInits = mutableListOf<IRCodeLine>()
val globalInits = mutableListOf<IRInstruction>()
val blocks = mutableListOf<IRBlock>()
fun addGlobalInits(chunk: IRCodeChunk) = globalInits.addAll(chunk.instructions)
@ -91,10 +91,7 @@ class IRProgram(val name: String,
usedRegisters.outputFpRegs.forEach{ (reg, count) -> outputFpRegs[reg] = outputFpRegs.getValue(reg) + count }
}
globalInits.forEach {
if(it is IRInstruction)
it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs)
}
globalInits.forEach { it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) }
blocks.forEach {
it.inlineAssembly.forEach { chunk -> addUsed(chunk.usedRegisters()) }
it.subroutines.flatMap { sub->sub.chunks }.forEach { chunk -> addUsed(chunk.usedRegisters()) }
@ -171,19 +168,15 @@ class IRAsmSubroutine(
fun usedRegisters() = registersUsed
}
sealed class IRCodeLine
class IRCodeLabel(val name: String): IRCodeLine()
abstract class IRCodeChunkBase(val position: Position) {
val instructions = mutableListOf<IRCodeLine>()
abstract class IRCodeChunkBase(val label: String?, val position: Position) {
val instructions = mutableListOf<IRInstruction>()
abstract fun isEmpty(): Boolean
abstract fun isNotEmpty(): Boolean
abstract fun usedRegisters(): RegistersUsed
}
class IRCodeChunk(position: Position): IRCodeChunkBase(position) {
class IRCodeChunk(label: String?, position: Position): IRCodeChunkBase(label, position) {
override fun isEmpty() = instructions.isEmpty()
override fun isNotEmpty() = instructions.isNotEmpty()
@ -192,15 +185,12 @@ class IRCodeChunk(position: Position): IRCodeChunkBase(position) {
val inputFpRegs = mutableMapOf<Int, Int>().withDefault { 0 }
val outputRegs = mutableMapOf<Int, Int>().withDefault { 0 }
val outputFpRegs = mutableMapOf<Int, Int>().withDefault { 0 }
instructions.forEach {
if(it is IRInstruction)
it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs)
}
instructions.forEach { it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) }
return RegistersUsed(inputRegs, outputRegs, inputFpRegs, outputFpRegs)
}
operator fun plusAssign(line: IRCodeLine) {
instructions.add(line)
operator fun plusAssign(ins: IRInstruction) {
instructions.add(ins)
}
operator fun plusAssign(chunk: IRCodeChunkBase) {
@ -208,8 +198,8 @@ class IRCodeChunk(position: Position): IRCodeChunkBase(position) {
}
}
class IRInlineAsmChunk(val assembly: String, val isIR: Boolean, position: Position): IRCodeChunkBase(position) {
// note: no lines, asm is in the property
class IRInlineAsmChunk(label: String?, val assembly: String, val isIR: Boolean, position: Position): IRCodeChunkBase(label, position) {
// note: no instructions, asm is in the property
override fun isEmpty() = assembly.isBlank()
override fun isNotEmpty() = assembly.isNotBlank()
private val registersUsed by lazy { registersUsedInAssembly(isIR, assembly) }
@ -222,12 +212,15 @@ class IRInlineAsmChunk(val assembly: String, val isIR: Boolean, position: Positi
override fun usedRegisters() = registersUsed
}
class IRInlineBinaryChunk(val data: Collection<UByte>, position: Position): IRCodeChunkBase(position) {
class IRInlineBinaryChunk(label: String?, val data: Collection<UByte>, position: Position): IRCodeChunkBase(label, position) {
// note: no instructions, data is in the property
override fun isEmpty() = data.isEmpty()
override fun isNotEmpty() = data.isNotEmpty()
override fun usedRegisters() = RegistersUsed(emptyMap(), emptyMap(), emptyMap(), emptyMap())
}
typealias IRCodeChunks = List<IRCodeChunkBase>
class RegistersUsed(
// register num -> number of uses
val inputRegs: Map<Int, Int>,
@ -251,9 +244,11 @@ private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersU
if(isIR) {
assembly.lineSequence().forEach { line ->
val code = parseIRCodeLine(line.trim(), 0, mutableMapOf())
if(code is IRInstruction)
code.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs)
val result = parseIRCodeLine(line.trim(), 0, mutableMapOf())
result.fold(
ifLeft = { it.addUsedRegistersCounts(inputRegs, outputRegs, inputFpRegs, outputFpRegs) },
ifRight = { /* labels can be skipped */ }
)
}
}

View File

@ -1,7 +1,6 @@
package prog8.intermediate
import prog8.code.StMemVar
import prog8.code.StStaticVariable
import prog8.code.*
import prog8.code.core.DataType
import prog8.code.core.InternalCompilerException
@ -85,12 +84,12 @@ 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>): IRCodeLine {
fun parseIRCodeLine(line: String, pc: Int, placeholders: MutableMap<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())
if(labelmatch!=null)
return IRCodeLabel(labelmatch.groupValues[1])
return right(labelmatch.groupValues[1]) // it's a label.
val match = instructionPattern.matchEntire(line)
?: throw IRParseException("invalid IR instruction: $line")
@ -240,8 +239,8 @@ fun parseIRCodeLine(line: String, pc: Int, placeholders: MutableMap<Int, String>
"pc", "pz", "pv","pn"))
throw IRParseException("invalid cpu reg: $reg")
return IRInstruction(opcode, type, reg1, labelSymbol = reg)
return left(IRInstruction(opcode, type, reg1, labelSymbol = reg))
}
return IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue, labelSymbol = labelSymbol)
return left(IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, intValue, floatValue, labelSymbol = labelSymbol))
}

View File

@ -44,10 +44,14 @@ class VirtualMachine(irProgram: IRProgram) {
private val cx16virtualregsBaseAddress: Int
init {
program = emptyArray() // TODO
cx16virtualregsBaseAddress = 0 // TODO
/* TODO !!!
program = VmProgramLoader().load(irProgram, memory).toTypedArray()
require(program.size<=65536) {"program cannot contain more than 65536 instructions"}
require(irProgram.st.getAsmSymbols().isEmpty()) { "virtual machine can't yet process asmsymbols defined on command line" }
cx16virtualregsBaseAddress = (irProgram.st.lookup("cx16.r0") as? StMemVar)?.address?.toInt() ?: 0xff02
*/
}
fun run() {

View File

@ -3,6 +3,7 @@ package prog8.vm
import prog8.code.core.DataType
import prog8.intermediate.*
/*
class VmProgramLoader {
private val placeholders = mutableMapOf<Int, String>() // program index to symbolname
@ -13,12 +14,12 @@ class VmProgramLoader {
placeholders.clear()
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
val symbolAddresses = allocations.allocations.toMutableMap()
val program = mutableListOf<IRInstruction>()
val programChunks = mutableListOf<IRCodeChunkBase>()
varsToMemory(irProgram, allocations, symbolAddresses, memory)
if(!irProgram.options.dontReinitGlobals)
addToProgram(irProgram.globalInits, program, symbolAddresses)
addToProgram(irProgram.globalInits, programChunks, symbolAddresses)
// make sure that if there is a "main.start" entrypoint, we jump to it
irProgram.blocks.firstOrNull()?.let {
@ -187,8 +188,8 @@ class VmProgramLoader {
}
private fun addToProgram(
instructions: Iterable<IRCodeLine>,
program: MutableList<IRInstruction>,
chunks: Iterable<IRCodeChunkBase>,
program: MutableList<IRCodeChunkBase>,
symbolAddresses: MutableMap<String, Int>
) {
instructions.map {
@ -241,3 +242,4 @@ class VmProgramLoader {
}
}
}
*/

View File

@ -42,7 +42,7 @@ class TestVm: FunSpec( {
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(Position.DUMMY)
val code = IRCodeChunk(null, Position.DUMMY)
code += IRInstruction(Opcode.NOP)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, value=12345)
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, value=1000)
@ -69,7 +69,7 @@ class TestVm: FunSpec( {
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(Position.DUMMY)
val code = IRCodeChunk(null, Position.DUMMY)
code += IRInstruction(Opcode.BINARYDATA, binaryData = listOf(1u,2u,3u))
code += IRInstruction(Opcode.RETURN)
startSub += code