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. * By convention, the right side of an `Either` is used to hold successful values.
*
*/ */
sealed class Either<out L, out R> { 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==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes) || (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 // 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 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!! // 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.DataType
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.core.SignedDatatypes import prog8.code.core.SignedDatatypes
import prog8.intermediate.IRCodeChunk import prog8.intermediate.*
import prog8.intermediate.IRDataType
import prog8.intermediate.IRInstruction
import prog8.intermediate.Opcode
internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) { 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) if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister") 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) translateRegularAssign(assignment)
} }
private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunk { private fun translateInplaceAssign(assignment: PtAssignment): IRCodeChunks {
val ident = assignment.target.identifier val ident = assignment.target.identifier
val memory = assignment.target.memory val memory = assignment.target.memory
val array = assignment.target.array val array = assignment.target.array
@ -48,23 +45,23 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
address: Int, address: Int,
value: PtExpression, value: PtExpression,
origAssign: PtAssignment origAssign: PtAssignment
): IRCodeChunk { ): IRCodeChunks {
val vmDt = codeGen.irType(value.type) val vmDt = codeGen.irType(value.type)
val code = IRCodeChunk(origAssign.position)
when(value) { when(value) {
is PtIdentifier -> return code // do nothing, x=x null assignment. is PtIdentifier -> return emptyList() // do nothing, x=x null assignment.
is PtMachineRegister -> return code // do nothing, reg=reg null assignment is PtMachineRegister -> return emptyList() // do nothing, reg=reg null assignment
is PtPrefix -> return inplacePrefix(value.operator, vmDt, address, null, value.position) 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 PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, address, null, origAssign)
is PtMemoryByte -> { is PtMemoryByte -> {
return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt())) return if (!codeGen.options.compTarget.machine.isIOAddress(address.toUInt()))
code // do nothing, mem=mem null assignment. emptyList() // do nothing, mem=mem null assignment.
else { else {
// read and write a (i/o) memory location to itself. // read and write a (i/o) memory location to itself.
val tempReg = codeGen.registers.nextFree() val tempReg = codeGen.registers.nextFree()
val code = IRCodeChunk(null, origAssign.position)
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address) code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, value = address)
code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address) code += IRInstruction(Opcode.STOREM, vmDt, reg1 = tempReg, value = address)
code listOf(code)
} }
} }
else -> return fallbackAssign(origAssign) else -> return fallbackAssign(origAssign)
@ -75,25 +72,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
symbol: String, symbol: String,
value: PtExpression, value: PtExpression,
origAssign: PtAssignment origAssign: PtAssignment
): IRCodeChunk { ): IRCodeChunks {
val vmDt = codeGen.irType(value.type) val vmDt = codeGen.irType(value.type)
val code = IRCodeChunk(origAssign.position) return when(value) {
when(value) { is PtIdentifier -> emptyList() // do nothing, x=x null assignment.
is PtIdentifier -> return code // do nothing, x=x null assignment. is PtMachineRegister -> emptyList() // do nothing, reg=reg null assignment
is PtMachineRegister -> return code // do nothing, reg=reg null assignment is PtPrefix -> inplacePrefix(value.operator, vmDt, null, symbol, value.position)
is PtPrefix -> return inplacePrefix(value.operator, vmDt, null, symbol, value.position) is PtBinaryExpression -> inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
is PtBinaryExpression -> return inplaceBinexpr(value.operator, value.right, vmDt, value.type in SignedDatatypes, null, symbol, origAssign)
is PtMemoryByte -> { is PtMemoryByte -> {
val code = IRCodeChunk(null, origAssign.position)
val tempReg = codeGen.registers.nextFree() val tempReg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol) code += IRInstruction(Opcode.LOADM, vmDt, reg1 = tempReg, labelSymbol = symbol)
code += IRInstruction(Opcode.STOREM, 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) if (codeGen.options.slowCodegenWarnings)
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position) codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
return translateRegularAssign(origAssign) return translateRegularAssign(origAssign)
@ -107,7 +105,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
knownAddress: Int?, knownAddress: Int?,
symbol: String?, symbol: String?,
origAssign: PtAssignment origAssign: PtAssignment
): IRCodeChunk { ): IRCodeChunks {
if(knownAddress!=null) { if(knownAddress!=null) {
when (operator) { when (operator) {
"+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand) "+" -> return expressionEval.operatorPlusInplace(knownAddress, null, vmDt, operand)
@ -139,8 +137,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return fallbackAssign(origAssign) return fallbackAssign(origAssign)
} }
private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?, position: Position): IRCodeChunk { private fun inplacePrefix(operator: String, vmDt: IRDataType, knownAddress: Int?, addressSymbol: String?, position: Position): IRCodeChunks {
val code= IRCodeChunk(position) val code= IRCodeChunk(null, position)
when(operator) { when(operator) {
"+" -> { } "+" -> { }
"-" -> { "-" -> {
@ -160,17 +158,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} }
else -> throw AssemblyError("weird prefix operator") 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. // note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
val ident = assignment.target.identifier val ident = assignment.target.identifier
val memory = assignment.target.memory val memory = assignment.target.memory
val array = assignment.target.array val array = assignment.target.array
val vmDt = codeGen.irType(assignment.value.type) val vmDt = codeGen.irType(assignment.value.type)
val result = mutableListOf<IRCodeChunkBase>()
val code = IRCodeChunk(assignment.position)
var resultRegister = -1 var resultRegister = -1
var resultFpRegister = -1 var resultFpRegister = -1
val zero = codeGen.isZero(assignment.value) val zero = codeGen.isZero(assignment.value)
@ -178,20 +175,20 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
// calculate the assignment value // calculate the assignment value
if (vmDt == IRDataType.FLOAT) { if (vmDt == IRDataType.FLOAT) {
resultFpRegister = codeGen.registers.nextFreeFloat() resultFpRegister = codeGen.registers.nextFreeFloat()
code += expressionEval.translateExpression(assignment.value, -1, resultFpRegister) result += expressionEval.translateExpression(assignment.value, -1, resultFpRegister)
} else { } else {
resultRegister = if (assignment.value is PtMachineRegister) { resultRegister = if (assignment.value is PtMachineRegister) {
(assignment.value as PtMachineRegister).register (assignment.value as PtMachineRegister).register
} else { } else {
val reg = codeGen.registers.nextFree() val reg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(assignment.value, reg, -1) result += expressionEval.translateExpression(assignment.value, reg, -1)
reg reg
} }
} }
} }
if(ident!=null) { if(ident!=null) {
val symbol = ident.targetName.joinToString(".") val symbol = ident.targetName.joinToString(".")
code += if(zero) { val instruction = if(zero) {
IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.STOREZM, vmDt, labelSymbol = symbol)
} else { } else {
if (vmDt == IRDataType.FLOAT) if (vmDt == IRDataType.FLOAT)
@ -199,6 +196,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
else else
IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol) IRInstruction(Opcode.STOREM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
} }
result += IRCodeChunk(null, ident.position).also { it += instruction }
return result
} }
else if(array!=null) { else if(array!=null) {
val variable = array.variable.targetName.joinToString(".") 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) if(array.index.type!=DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index") throw AssemblyError("non-array var indexing requires bytes index")
val idxReg = codeGen.registers.nextFree() 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) { if(zero) {
// there's no STOREZIX instruction // there's no STOREZIX instruction
resultRegister = codeGen.registers.nextFree() resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0) code += IRInstruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=0)
} }
code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable) code += IRInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, labelSymbol = variable)
return code result += code
return result
} }
val fixedIndex = constIntValue(array.index) val fixedIndex = constIntValue(array.index)
if(zero) { if(zero) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize 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 { } else {
val indexReg = codeGen.registers.nextFree() val indexReg = codeGen.registers.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position) result += loadIndexReg(array, itemsize, indexReg)
code += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) result += IRCodeChunk(null, array.position).also { it += IRInstruction(Opcode.STOREZX, vmDt, reg1=indexReg, labelSymbol = variable) }
} }
} else { } else {
if(vmDt== IRDataType.FLOAT) { if(vmDt== IRDataType.FLOAT) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize 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 { } else {
val indexReg = codeGen.registers.nextFree() val indexReg = codeGen.registers.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position) result += loadIndexReg(array, itemsize, indexReg)
code += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) result += IRCodeChunk(null, array.position).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
} }
} else { } else {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize 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 { } else {
val indexReg = codeGen.registers.nextFree() val indexReg = codeGen.registers.nextFree()
code += loadIndexReg(array, itemsize, indexReg, array.position) result += loadIndexReg(array, itemsize, indexReg)
code += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) result += IRCodeChunk(null, array.position).also { it += IRInstruction(Opcode.STOREX, vmDt, reg1 = resultRegister, reg2=indexReg, labelSymbol = variable) }
} }
} }
} }
return result
} }
else if(memory!=null) { else if(memory!=null) {
require(vmDt== IRDataType.BYTE) require(vmDt== IRDataType.BYTE)
if(zero) { if(zero) {
if(memory.address is PtNumber) { 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 { } else {
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1) result += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) result += IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREZI, vmDt, reg1=addressReg) }
} }
} else { } else {
if(memory.address is PtNumber) { 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 { } else {
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
code += expressionEval.translateExpression(memory.address, addressReg, -1) result += expressionEval.translateExpression(memory.address, addressReg, -1)
code += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) result += IRCodeChunk(null, assignment.position).also { it += IRInstruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressReg) }
} }
} }
return result
} }
else else
throw AssemblyError("weird assigntarget") throw AssemblyError("weird assigntarget")
return code
} }
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int, position: Position): IRCodeChunk { private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int, indexReg: Int): IRCodeChunks {
val code = IRCodeChunk(position) return if(itemsize==1) {
if(itemsize==1) { expressionEval.translateExpression(array.index, indexReg, -1)
code += expressionEval.translateExpression(array.index, indexReg, -1) } else {
}
else {
val mult = PtBinaryExpression("*", DataType.UBYTE, array.position) val mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position) 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) { 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) { return when(call.name) {
"any" -> funcAny(call, resultRegister) "any" -> funcAny(call, resultRegister)
"all" -> funcAll(call, resultRegister) "all" -> funcAll(call, resultRegister)
@ -25,7 +25,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"rsave", "rsave",
"rsavex", "rsavex",
"rrestore", "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) "rnd" -> funcRnd(resultRegister, call.position)
"rndw" -> funcRndw(resultRegister, call.position) "rndw" -> funcRndw(resultRegister, call.position)
"callfar" -> throw AssemblyError("callfar() is for cx16 target only") "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) "peekw" -> funcPeekW(call, resultRegister)
"poke" -> funcPoke(call) "poke" -> funcPoke(call)
"pokew" -> funcPokeW(call) "pokew" -> funcPokeW(call)
"pokemon" -> IRCodeChunk(call.position) "pokemon" -> emptyList()
"mkword" -> funcMkword(call, resultRegister) "mkword" -> funcMkword(call, resultRegister)
"sort" -> funcSort(call) "sort" -> funcSort(call)
"reverse" -> funcReverse(call) "reverse" -> funcReverse(call)
@ -49,20 +49,21 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
} }
private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcCmp(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position)
val leftRegister = codeGen.registers.nextFree() val leftRegister = codeGen.registers.nextFree()
val rightRegister = codeGen.registers.nextFree() val rightRegister = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], leftRegister, -1) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[1], rightRegister, -1) result += exprGen.translateExpression(call.args[0], leftRegister, -1)
code += IRInstruction(Opcode.CMP, codeGen.irType(call.args[0].type), reg1=leftRegister, reg2=rightRegister) result += exprGen.translateExpression(call.args[1], rightRegister, -1)
return code 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 arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val code = IRCodeChunk(call.position)
val syscall = val syscall =
when (array.dt) { when (array.dt) {
DataType.ARRAY_UB, DataType.ARRAY_UB,
@ -72,15 +73,18 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_F -> IMSyscall.ANY_FLOAT DataType.ARRAY_F -> IMSyscall.ANY_FLOAT
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
code += exprGen.translateExpression(call.args[0], 0, -1) val result = mutableListOf<IRCodeChunkBase>()
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length) result += exprGen.translateExpression(call.args[0], 0, -1)
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal) result += IRCodeChunk(null, call.position).also {
if (resultRegister != 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
code += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0) it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
return code 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 arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val syscall = val syscall =
@ -92,98 +96,116 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_F -> IMSyscall.ALL_FLOAT DataType.ARRAY_F -> IMSyscall.ALL_FLOAT
else -> throw IllegalArgumentException("weird type") else -> throw IllegalArgumentException("weird type")
} }
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], 0, -1) result += exprGen.translateExpression(call.args[0], 0, -1)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=array.length) result += IRCodeChunk(null, call.position).also {
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
if(resultRegister!=0) it += IRInstruction(Opcode.SYSCALL, value = syscall.ordinal)
code += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=resultRegister, reg2=0) if (resultRegister != 0)
return code it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1 = resultRegister, reg2 = 0)
}
return result
} }
private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcAbs(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position)
val sourceDt = call.args.single().type val sourceDt = call.args.single().type
val result = mutableListOf<IRCodeChunkBase>()
if(sourceDt!=DataType.UWORD) { if(sourceDt!=DataType.UWORD) {
code += exprGen.translateExpression(call.args[0], resultRegister, -1) result += exprGen.translateExpression(call.args[0], resultRegister, -1)
when (sourceDt) { when (sourceDt) {
DataType.UBYTE -> { 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 -> { DataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree() val compareReg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=resultRegister) result += IRCodeChunk(null, call.position).also {
code += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80) it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=resultRegister)
code += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, value=0x80)
code += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=resultRegister) it += IRInstruction(Opcode.BZ, IRDataType.BYTE, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultRegister) it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=resultRegister)
code += IRCodeLabel(notNegativeLabel) it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultRegister)
}
result += IRCodeChunk(notNegativeLabel, call.position)
} }
DataType.WORD -> { DataType.WORD -> {
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree() val compareReg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=resultRegister) result += IRCodeChunk(null, call.position).also {
code += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000) it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=resultRegister)
code += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, value=0x8000)
code += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=resultRegister) it += IRInstruction(Opcode.BZ, IRDataType.WORD, reg1=compareReg, labelSymbol = notNegativeLabel)
code += IRCodeLabel(notNegativeLabel) it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=resultRegister)
}
result += IRCodeChunk(notNegativeLabel, call.position)
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
return code return result
} }
private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunk { private fun funcSgn(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position)
val reg = codeGen.registers.nextFree() val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) val result = mutableListOf<IRCodeChunkBase>()
code += IRInstruction(Opcode.SGN, codeGen.irType(call.type), reg1=resultRegister, reg2=reg) result += exprGen.translateExpression(call.args.single(), reg, -1)
return code 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 { private fun funcSqrt16(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position)
val reg = codeGen.registers.nextFree() val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) val result = mutableListOf<IRCodeChunkBase>()
code += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultRegister, reg2=reg) result += exprGen.translateExpression(call.args.single(), reg, -1)
return code result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultRegister, reg2=reg)
}
return result
} }
private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPop(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val code = IRCodeChunk(null, call.position)
val reg = codeGen.registers.nextFree() val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg) code += IRInstruction(Opcode.POP, IRDataType.BYTE, reg1=reg)
code += assignRegisterTo(call.args.single(), reg) val result = mutableListOf<IRCodeChunkBase>(code)
return code result += assignRegisterTo(call.args.single(), reg)
return result
} }
private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPopw(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val code = IRCodeChunk(null, call.position)
val reg = codeGen.registers.nextFree() val reg = codeGen.registers.nextFree()
code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg) code += IRInstruction(Opcode.POP, IRDataType.WORD, reg1=reg)
code += assignRegisterTo(call.args.single(), reg) val result = mutableListOf<IRCodeChunkBase>(code)
return code result += assignRegisterTo(call.args.single(), reg)
return result
} }
private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPush(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
val reg = codeGen.registers.nextFree() val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) result += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=reg) result += IRCodeChunk(null, call.position).also {
return code it += IRInstruction(Opcode.PUSH, IRDataType.BYTE, reg1=reg)
}
return result
} }
private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPushw(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
val reg = codeGen.registers.nextFree() val reg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), reg, -1) result += exprGen.translateExpression(call.args.single(), reg, -1)
code += IRInstruction(Opcode.PUSH, IRDataType.WORD, reg1=reg) result += IRCodeChunk(null, call.position).also {
return code 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 arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val syscall = val syscall =
@ -193,14 +215,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
else -> throw IllegalArgumentException("weird type to reverse") else -> throw IllegalArgumentException("weird type to reverse")
} }
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], 0, -1) result += exprGen.translateExpression(call.args[0], 0, -1)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=array.length) result += IRCodeChunk(null, call.position).also {
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
return code 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 arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
val syscall = 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") DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
else -> throw IllegalArgumentException("weird type to sort") else -> throw IllegalArgumentException("weird type to sort")
} }
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], 0, -1) result += exprGen.translateExpression(call.args[0], 0, -1)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=1, value=array.length) result += IRCodeChunk(null, call.position).also {
code += IRInstruction(Opcode.SYSCALL, value=syscall.ordinal) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = 1, value = array.length)
return code 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 msbReg = codeGen.registers.nextFree()
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], msbReg, -1) result += exprGen.translateExpression(call.args[0], msbReg, -1)
code += exprGen.translateExpression(call.args[1], resultRegister, -1) result += exprGen.translateExpression(call.args[1], resultRegister, -1)
code += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=msbReg) result += IRCodeChunk(null, call.position).also {
return code it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = resultRegister, reg2 = msbReg)
}
return result
} }
private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunk { private fun funcPokeW(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isZero(call.args[1])) { if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() 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 { } else {
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
code += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg) result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg)
}
} }
} else { } else {
val valueReg = codeGen.registers.nextFree() val valueReg = codeGen.registers.nextFree()
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1) result += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = valueReg, value = address) result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1 = valueReg, value = address)
}
} else { } else {
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1) result += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRInstruction(Opcode.STOREI, IRDataType.WORD, reg1 = valueReg, reg2 = addressReg) 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 { private fun funcPoke(call: PtBuiltinFunctionCall): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
if(codeGen.isZero(call.args[1])) { if(codeGen.isZero(call.args[1])) {
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() 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 { } else {
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
code += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg) result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg)
}
} }
} else { } else {
val valueReg = codeGen.registers.nextFree() val valueReg = codeGen.registers.nextFree()
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
code += exprGen.translateExpression(call.args[1], valueReg, -1) result += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueReg, value = address) result += IRCodeChunk(null, call.position).also {
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueReg, value = address)
}
} else { } else {
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
code += exprGen.translateExpression(call.args[1], valueReg, -1) result += exprGen.translateExpression(call.args[1], valueReg, -1)
code += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueReg, reg2 = addressReg) 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 { private fun funcPeekW(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
if(call.args[0] is PtNumber) { if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() 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 { } else {
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1) result += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = resultRegister, reg2 = addressReg) 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 { private fun funcPeek(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
if(call.args[0] is PtNumber) { if(call.args[0] is PtNumber) {
val address = (call.args[0] as PtNumber).number.toInt() 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 { } else {
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
code += exprGen.translateExpression(call.args.single(), addressReg, -1) result += exprGen.translateExpression(call.args.single(), addressReg, -1)
code += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultRegister, reg2 = addressReg) 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 { private fun funcRnd(resultRegister: Int, position: Position): IRCodeChunks {
val code = IRCodeChunk(position) val code = IRCodeChunk(null, position)
code += IRInstruction(Opcode.RND, IRDataType.BYTE, reg1=resultRegister) code += IRInstruction(Opcode.RND, IRDataType.BYTE, reg1=resultRegister)
return code return listOf(code)
} }
private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunk { private fun funcRndw(resultRegister: Int, position: Position): IRCodeChunks {
val code = IRCodeChunk(position) val code = IRCodeChunk(null, position)
code += IRInstruction(Opcode.RND, IRDataType.WORD, reg1=resultRegister) 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 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") 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 { private fun funcLsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) return exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += 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. // 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 { private fun funcMsb(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks {
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args.single(), resultRegister, -1) result += exprGen.translateExpression(call.args.single(), resultRegister, -1)
code += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultRegister, reg2=resultRegister) 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. // 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 vmDt = codeGen.irType(call.args[0].type)
val code = IRCodeChunk(call.position) val result = mutableListOf<IRCodeChunkBase>()
code += exprGen.translateExpression(call.args[0], resultRegister, -1) result += exprGen.translateExpression(call.args[0], resultRegister, -1)
code += IRInstruction(opcode, vmDt, reg1=resultRegister) result += IRCodeChunk(null, call.position).also {
code += assignRegisterTo(call.args[0], resultRegister) it += IRInstruction(opcode, vmDt, reg1 = resultRegister)
return code }
result += assignRegisterTo(call.args[0], resultRegister)
return result
} }
private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunk { private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks {
val code = IRCodeChunk(target.position) val code = IRCodeChunk(null, target.position)
val assignment = PtAssignment(target.position) val assignment = PtAssignment(target.position)
val assignTarget = PtAssignTarget(target.position) val assignTarget = PtAssignTarget(target.position)
assignTarget.children.add(target) assignTarget.children.add(target)
assignment.children.add(assignTarget) assignment.children.add(assignTarget)
assignment.children.add(PtMachineRegister(register, target.type, target.position)) assignment.children.add(PtMachineRegister(register, target.type, target.position))
code += codeGen.translateNode(assignment) val result = mutableListOf<IRCodeChunkBase>(code)
return code result += codeGen.translateNode(assignment)
return result
} }
} }

View File

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

View File

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

View File

@ -4,7 +4,10 @@ TODO
For next release For next release
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
- ir: get rid of IRCodeLabel, make every label start a new code chunk, give those a 'label' property. - 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 memorymapped = parseMemMapped(lines)
val slabs = parseSlabs(lines) val slabs = parseSlabs(lines)
val initGlobals = parseInitGlobals(lines) val initGlobals = parseInitGlobals(lines)
val blocks = parseBlocksUntilProgramEnd(lines, variables) val blocks = parseBlocksUntilProgramEnd(lines)
val st = IRSymbolTable(null) val st = IRSymbolTable(null)
asmsymbols.forEach { (name, value) -> st.addAsmSymbol(name, value)} asmsymbols.forEach { (name, value) -> st.addAsmSymbol(name, value)}
@ -261,9 +261,9 @@ class IRFileReader {
if(line!="<INITGLOBALS>") if(line!="<INITGLOBALS>")
throw IRParseException("invalid INITGLOBALS") throw IRParseException("invalid INITGLOBALS")
line = lines.next() line = lines.next()
var chunk = IRCodeChunk(Position.DUMMY) var chunk = IRCodeChunk(null, Position.DUMMY)
if(line=="<C>") { if(line=="<C>") {
chunk = parseCodeChunk(line, lines)!! chunk = parseCodeChunk(line, lines, null)!!
line = lines.next() line = lines.next()
} }
if(line!="</INITGLOBALS>") if(line!="</INITGLOBALS>")
@ -271,7 +271,7 @@ class IRFileReader {
return chunk 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>() val blocks = mutableListOf<IRBlock>()
while(true) { while(true) {
var line = lines.next() var line = lines.next()
@ -279,7 +279,7 @@ class IRFileReader {
line = lines.next() line = lines.next()
if (line == "</PROGRAM>") if (line == "</PROGRAM>")
break break
blocks.add(parseBlock(line, lines, variables)) blocks.add(parseBlock(line, lines))
} }
return blocks return blocks
} }
@ -291,7 +291,7 @@ class IRFileReader {
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>") private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]") 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 var line = startline
if(!line.startsWith("<BLOCK ")) if(!line.startsWith("<BLOCK "))
throw IRParseException("invalid BLOCK") throw IRParseException("invalid BLOCK")
@ -306,20 +306,20 @@ class IRFileReader {
if(line=="</BLOCK>") if(line=="</BLOCK>")
return block return block
if(line.startsWith("<SUB ")) { if(line.startsWith("<SUB ")) {
val sub = parseSubroutine(line, lines, variables) val sub = parseSubroutine(line, lines)
block += sub block += sub
} else if(line.startsWith("<ASMSUB ")) { } else if(line.startsWith("<ASMSUB ")) {
val sub = parseAsmSubroutine(line, lines) val sub = parseAsmSubroutine(line, lines)
block += sub block += sub
} else if(line.startsWith("<INLINEASM ")) { } else if(line.startsWith("<INLINEASM ")) {
val asm = parseInlineAssembly(line, lines) val asm = parseInlineAssembly(line, lines, null)
block += asm block += asm
} else } else
throw IRParseException("invalid line in BLOCK") 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]> // <INLINEASM IR=true POS=[examples/test.p8: line 8 col 6-9]>
val match = inlineAsmPattern.matchEntire(startline) ?: throw IRParseException("invalid INLINEASM") val match = inlineAsmPattern.matchEntire(startline) ?: throw IRParseException("invalid INLINEASM")
val isIr = match.groupValues[1].toBoolean() val isIr = match.groupValues[1].toBoolean()
@ -330,7 +330,7 @@ class IRFileReader {
asmlines.add(line) asmlines.add(line)
line = lines.next() 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 { private fun parseAsmSubroutine(startline: String, lines: Iterator<String>): IRAsmSubroutine {
@ -352,7 +352,7 @@ class IRFileReader {
params += Pair(dt, regsf) params += Pair(dt, regsf)
} }
line = lines.next() line = lines.next()
val asm = parseInlineAssembly(line, lines) val asm = parseInlineAssembly(line, lines, null)
while(line!="</ASMSUB>") while(line!="</ASMSUB>")
line = lines.next() line = lines.next()
val clobberRegs = if(clobbers.isBlank()) emptyList() else clobbers.split(',').map { CpuRegister.valueOf(it) } 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]> // <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 match = subPattern.matchEntire(startline) ?: throw IRParseException("invalid SUB")
val (name, returntype, pos) = match.destructured val (name, returntype, pos) = match.destructured
val sub = IRSubroutine(name, val sub = IRSubroutine(name,
parseParameters(lines, variables), parseParameters(lines),
if(returntype=="null") null else parseDatatype(returntype, false), if(returntype=="null") null else parseDatatype(returntype, false),
parsePosition(pos)) parsePosition(pos))
while(true) { while(true) {
@ -387,11 +387,11 @@ class IRFileReader {
if(line=="</SUB>") if(line=="</SUB>")
return sub return sub
val chunk = if(line=="<C>") val chunk = if(line=="<C>")
parseCodeChunk(line, lines) parseCodeChunk(line, lines, null)
else if(line.startsWith("<BYTES ")) else if(line.startsWith("<BYTES "))
parseBinaryBytes(line, lines) parseBinaryBytes(line, lines, null)
else if(line.startsWith("<INLINEASM ")) else if(line.startsWith("<INLINEASM "))
parseInlineAssembly(line, lines) parseInlineAssembly(line, lines, null)
else else
throw IRParseException("invalid sub child node") throw IRParseException("invalid sub child node")
@ -406,7 +406,7 @@ class IRFileReader {
return sub 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 match = bytesPattern.matchEntire(startline) ?: throw IRParseException("invalid BYTES")
val pos = parsePosition(match.groupValues[1]) val pos = parsePosition(match.groupValues[1])
val bytes = mutableListOf<UByte>() val bytes = mutableListOf<UByte>()
@ -417,10 +417,10 @@ class IRFileReader {
} }
line = lines.next() 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() var line = lines.next()
if(line!="<PARAMS>") if(line!="<PARAMS>")
throw IRParseException("missing 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!="<C>") {
if(firstline=="</SUB>") if(firstline=="</SUB>")
return null return null
else else
throw IRParseException("invalid or empty <C>ODE chunk") throw IRParseException("invalid or empty <C>ODE chunk")
} }
val chunk = IRCodeChunk(Position.DUMMY) val chunk = IRCodeChunk(label, Position.DUMMY)
while(true) { while(true) {
val line = lines.next() val line = lines.next()
if (line == "</C>") if (line == "</C>")
return chunk return chunk
if (line.isBlank() || line.startsWith(';')) if (line.isBlank() || line.startsWith(';'))
continue 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 package prog8.intermediate
import prog8.code.core.* import prog8.code.core.ArrayDatatypes
import java.io.BufferedWriter import prog8.code.core.DataType
import prog8.code.core.InternalCompilerException
import prog8.code.core.NumericDatatypes
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.bufferedWriter import kotlin.io.path.bufferedWriter
import kotlin.io.path.div import kotlin.io.path.div
@ -25,7 +27,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
if(!irProgram.options.dontReinitGlobals) { if(!irProgram.options.dontReinitGlobals) {
out.write("<C>\n") out.write("<C>\n")
// note: this a block of code that loads values and stores them into the global variables to reset their values. // 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("</C>\n")
} }
out.write("</INITGLOBALS>\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}") throw InternalCompilerException("empty code chunk in ${it.name} ${it.position}")
chunk.instructions.forEach { instr -> chunk.instructions.forEach { instr ->
numInstr++ numInstr++
out.writeLine(instr) out.write(instr.toString())
} }
out.write("</C>\n") 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") } irProgram.st.allMemorySlabs().forEach{ slab -> out.write("SLAB ${slab.name} ${slab.size} ${slab.align}\n") }
out.write("</MEMORYSLABS>\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 fpValue: Float?=null,
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!) val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
val binaryData: Collection<UByte>?=null val binaryData: Collection<UByte>?=null
): IRCodeLine() { ) {
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT) // 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. // This knowledge is useful in IL assembly optimizers to see how registers are used.
val reg1direction: OperandDirection val reg1direction: OperandDirection

View File

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

View File

@ -1,7 +1,6 @@
package prog8.intermediate package prog8.intermediate
import prog8.code.StMemVar import prog8.code.*
import prog8.code.StStaticVariable
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.InternalCompilerException 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 instructionPattern = Regex("""([a-z]+)(\.b|\.w|\.f)?(.*)""", RegexOption.IGNORE_CASE)
private val labelPattern = Regex("""_([a-zA-Z\d\._]+):""") 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: // 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. // the IR File Reader but also the VirtualMachine itself to make sense of any inline vmasm blocks.
val labelmatch = labelPattern.matchEntire(line.trim()) val labelmatch = labelPattern.matchEntire(line.trim())
if(labelmatch!=null) if(labelmatch!=null)
return IRCodeLabel(labelmatch.groupValues[1]) return right(labelmatch.groupValues[1]) // it's a label.
val match = instructionPattern.matchEntire(line) val match = instructionPattern.matchEntire(line)
?: throw IRParseException("invalid IR instruction: $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")) "pc", "pz", "pv","pn"))
throw IRParseException("invalid cpu reg: $reg") 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 private val cx16virtualregsBaseAddress: Int
init { init {
program = emptyArray() // TODO
cx16virtualregsBaseAddress = 0 // TODO
/* TODO !!!
program = VmProgramLoader().load(irProgram, memory).toTypedArray() program = VmProgramLoader().load(irProgram, memory).toTypedArray()
require(program.size<=65536) {"program cannot contain more than 65536 instructions"} 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" } 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 cx16virtualregsBaseAddress = (irProgram.st.lookup("cx16.r0") as? StMemVar)?.address?.toInt() ?: 0xff02
*/
} }
fun run() { fun run() {

View File

@ -3,6 +3,7 @@ package prog8.vm
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.intermediate.* import prog8.intermediate.*
/*
class VmProgramLoader { class VmProgramLoader {
private val placeholders = mutableMapOf<Int, String>() // program index to symbolname private val placeholders = mutableMapOf<Int, String>() // program index to symbolname
@ -13,12 +14,12 @@ class VmProgramLoader {
placeholders.clear() placeholders.clear()
val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget) val allocations = VmVariableAllocator(irProgram.st, irProgram.encoding, irProgram.options.compTarget)
val symbolAddresses = allocations.allocations.toMutableMap() val symbolAddresses = allocations.allocations.toMutableMap()
val program = mutableListOf<IRInstruction>() val programChunks = mutableListOf<IRCodeChunkBase>()
varsToMemory(irProgram, allocations, symbolAddresses, memory) varsToMemory(irProgram, allocations, symbolAddresses, memory)
if(!irProgram.options.dontReinitGlobals) 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 // make sure that if there is a "main.start" entrypoint, we jump to it
irProgram.blocks.firstOrNull()?.let { irProgram.blocks.firstOrNull()?.let {
@ -187,8 +188,8 @@ class VmProgramLoader {
} }
private fun addToProgram( private fun addToProgram(
instructions: Iterable<IRCodeLine>, chunks: Iterable<IRCodeChunkBase>,
program: MutableList<IRInstruction>, program: MutableList<IRCodeChunkBase>,
symbolAddresses: MutableMap<String, Int> symbolAddresses: MutableMap<String, Int>
) { ) {
instructions.map { 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 program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, 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.NOP)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, value=12345) code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=1, value=12345)
code += IRInstruction(Opcode.STOREM, IRDataType.WORD, reg1=1, value=1000) 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 program = IRProgram("test", IRSymbolTable(null), getTestOptions(), VMTarget())
val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val block = IRBlock("testmain", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRSubroutine("testmain.testsub", emptyList(), null, 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.BINARYDATA, binaryData = listOf(1u,2u,3u))
code += IRInstruction(Opcode.RETURN) code += IRInstruction(Opcode.RETURN)
startSub += code startSub += code