diff --git a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt index 7a32f956d..b8854c108 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/BuiltinFuncGen.kt @@ -8,9 +8,9 @@ import prog8.vm.Opcode import prog8.vm.Syscall import prog8.vm.VmDataType -internal class BuiltinFuncGen(val codeGen: CodeGen, val exprGen: ExpressionGen) { +internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen: ExpressionGen) { - fun translate(call: PtBuiltinFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk { + fun translate(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk { val code = VmCodeChunk() when(call.name) { "syscall" -> { @@ -20,19 +20,19 @@ internal class BuiltinFuncGen(val codeGen: CodeGen, val exprGen: ExpressionGen) "syscall1" -> { code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0)) val callNr = (call.args[0] as PtNumber).number.toInt() - code += exprGen.translateExpression(call.args[1], 0, regUsage) + code += exprGen.translateExpression(call.args[1], 0) code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr)) code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0)) } "syscall2" -> { code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0)) code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)) - while(regUsage.firstFree<2) { - regUsage.nextFree() + while(codeGen.vmRegisters.peekNext()<2) { + codeGen.vmRegisters.nextFree() } val callNr = (call.args[0] as PtNumber).number.toInt() - code += exprGen.translateExpression(call.args[1], 0, regUsage) - code += exprGen.translateExpression(call.args[2], 1, regUsage) + code += exprGen.translateExpression(call.args[1], 0) + code += exprGen.translateExpression(call.args[2], 1) code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr)) code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1)) code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0)) @@ -41,25 +41,25 @@ internal class BuiltinFuncGen(val codeGen: CodeGen, val exprGen: ExpressionGen) code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1 = 0)) code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 1)) code += VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1 = 2)) - while(regUsage.firstFree<3) { - regUsage.nextFree() + while(codeGen.vmRegisters.peekNext()<3) { + codeGen.vmRegisters.nextFree() } val callNr = (call.args[0] as PtNumber).number.toInt() - code += exprGen.translateExpression(call.args[1], 0, regUsage) - code += exprGen.translateExpression(call.args[2], 1, regUsage) - code += exprGen.translateExpression(call.args[3], 2, regUsage) + code += exprGen.translateExpression(call.args[1], 0) + code += exprGen.translateExpression(call.args[2], 1) + code += exprGen.translateExpression(call.args[3], 2) code += VmCodeInstruction(Instruction(Opcode.SYSCALL, value=callNr)) code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 2)) code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.WORD, reg1 = 1)) code += VmCodeInstruction(Instruction(Opcode.POP, VmDataType.BYTE, reg1 = 0)) } "msb" -> { - code += exprGen.translateExpression(call.args.single(), resultRegister, regUsage) + code += exprGen.translateExpression(call.args.single(), resultRegister) code += VmCodeInstruction(Instruction(Opcode.SWAP, VmDataType.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. } "lsb" -> { - code += exprGen.translateExpression(call.args.single(), resultRegister, regUsage) + code += exprGen.translateExpression(call.args.single(), resultRegister) // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. } "memory" -> { @@ -83,27 +83,27 @@ internal class BuiltinFuncGen(val codeGen: CodeGen, val exprGen: ExpressionGen) code += VmCodeInstruction(Instruction(Opcode.LOADR, VmDataType.BYTE, reg1=resultRegister, reg2=0)) } "peek" -> { - val addressReg = regUsage.nextFree() - code += exprGen.translateExpression(call.args.single(), addressReg, regUsage) + val addressReg = codeGen.vmRegisters.nextFree() + code += exprGen.translateExpression(call.args.single(), addressReg) code += VmCodeInstruction(Instruction(Opcode.LOADI, VmDataType.BYTE, reg1 = resultRegister, reg2=addressReg)) } "peekw" -> { - val addressReg = regUsage.nextFree() - code += exprGen.translateExpression(call.args.single(), addressReg, regUsage) + val addressReg = codeGen.vmRegisters.nextFree() + code += exprGen.translateExpression(call.args.single(), addressReg) code += VmCodeInstruction(Instruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg)) } "mkword" -> { - val msbReg = regUsage.nextFree() - val lsbReg = regUsage.nextFree() - code += exprGen.translateExpression(call.args[0], msbReg, regUsage) - code += exprGen.translateExpression(call.args[1], lsbReg, regUsage) + val msbReg = codeGen.vmRegisters.nextFree() + val lsbReg = codeGen.vmRegisters.nextFree() + code += exprGen.translateExpression(call.args[0], msbReg) + code += exprGen.translateExpression(call.args[1], lsbReg) code += VmCodeInstruction(Instruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg, reg3=lsbReg)) } else -> { TODO("builtinfunc ${call.name}") // code += VmCodeInstruction(Instruction(Opcode.NOP)) // for (arg in call.args) { -// code += translateExpression(arg, resultRegister, regUsage) +// code += translateExpression(arg, resultRegister) // code += when(arg.type) { // in ByteDatatypes -> VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.BYTE, reg1=resultRegister)) // in WordDatatypes -> VmCodeInstruction(Instruction(Opcode.PUSH, VmDataType.WORD, reg1=resultRegister)) diff --git a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt index 47ef7d59a..8cf082f28 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/CodeGen.kt @@ -7,6 +7,23 @@ import prog8.code.core.* import prog8.vm.Instruction import prog8.vm.Opcode import prog8.vm.VmDataType +import java.lang.Math.pow +import kotlin.math.pow + + +internal class VmRegisterPool() { + private var firstFree: Int=3 // registers 0,1,2 are reserved + + fun peekNext() = firstFree + + fun nextFree(): Int { + val result = firstFree + firstFree++ + if(firstFree>65535) + throw AssemblyError("out of virtual registers") + return result + } +} class CodeGen(internal val program: PtProgram, @@ -18,6 +35,7 @@ class CodeGen(internal val program: PtProgram, internal val allocations = VariableAllocator(symbolTable, program, errors) private val expressionEval = ExpressionGen(this) private val builtinFuncGen = BuiltinFuncGen(this, expressionEval) + internal val vmRegisters = VmRegisterPool() init { if(options.dontReinitGlobals) @@ -30,40 +48,39 @@ class CodeGen(internal val program: PtProgram, // collect global variables initializers program.allBlocks().forEach { val code = VmCodeChunk() - it.children.filterIsInstance().forEach { assign -> code += translate(assign, RegisterUsage(0)) } + it.children.filterIsInstance().forEach { assign -> code += translate(assign) } vmprog.addGlobalInits(code) } - val regUsage = RegisterUsage(0) for (block in program.allBlocks()) { - vmprog.addBlock(translate(block, regUsage)) + vmprog.addBlock(translate(block)) } return vmprog } - private fun translateNode(node: PtNode, regUsage: RegisterUsage): VmCodeChunk { + private fun translateNode(node: PtNode): VmCodeChunk { val code = when(node) { - is PtBlock -> translate(node, regUsage) - is PtSub -> translate(node, regUsage) + is PtBlock -> translate(node) + is PtSub -> translate(node) is PtScopeVarsDecls -> VmCodeChunk() // vars should be looked up via symbol table is PtVariable -> VmCodeChunk() // var should be looked up via symbol table is PtMemMapped -> VmCodeChunk() // memmapped var should be looked up via symbol table is PtConstant -> VmCodeChunk() // constants have all been folded into the code - is PtAssignment -> translate(node, regUsage) - is PtNodeGroup -> translateGroup(node.children, regUsage) - is PtBuiltinFunctionCall -> translateBuiltinFunc(node, regUsage.nextFree(), regUsage) - is PtFunctionCall -> expressionEval.translate(node, regUsage.nextFree(), regUsage) + is PtAssignment -> translate(node) + is PtNodeGroup -> translateGroup(node.children) + is PtBuiltinFunctionCall -> translateBuiltinFunc(node, 0) + is PtFunctionCall -> expressionEval.translate(node, 0) is PtNop -> VmCodeChunk() is PtReturn -> translate(node) is PtJump -> translate(node) is PtWhen -> TODO("when") - is PtPipe -> expressionEval.translate(node, regUsage.nextFree(), regUsage) - is PtForLoop -> translate(node, regUsage) - is PtIfElse -> translate(node, regUsage) - is PtPostIncrDecr -> translate(node, regUsage) - is PtRepeatLoop -> translate(node, regUsage) + is PtPipe -> expressionEval.translate(node, 0) + is PtForLoop -> translate(node) + is PtIfElse -> translate(node) + is PtPostIncrDecr -> translate(node) + is PtRepeatLoop -> translate(node) is PtLabel -> VmCodeChunk(VmCodeLabel(node.scopedName)) is PtBreakpoint -> VmCodeChunk(VmCodeInstruction(Instruction(Opcode.BREAKPOINT))) is PtAddressOf, @@ -88,35 +105,113 @@ class CodeGen(internal val program: PtProgram, is PtConditionalBranch -> throw AssemblyError("conditional branches not supported in vm target due to lack of cpu flags ${node.position}") else -> TODO("missing codegen for $node") } - code.lines.add(0, VmCodeComment(node.position.toString())) + if(code.lines.isNotEmpty()) + code.lines.add(0, VmCodeComment(node.position.toString())) return code } - private fun translate(forLoop: PtForLoop, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(forLoop: PtForLoop): VmCodeChunk { val loopvar = symbolTable.lookup(forLoop.variable.targetName) as StStaticVariable val iterable = forLoop.iterable val code = VmCodeChunk() when(iterable) { is PtRange -> { - println("forloop ${loopvar.dt} ${loopvar.scopedName} in range ${iterable} ") + TODO("forloop ${loopvar.dt} ${loopvar.scopedName} in range ${iterable} ") iterable.printIndented(0) - TODO() + TODO("forloop over range") } is PtIdentifier -> { - val address = allocations.get(iterable.targetName) - val variable = symbolTable.lookup(iterable.targetName) as StStaticVariable - val length = variable.length!! - println("forloop ${loopvar.dt} ${loopvar.scopedName} in $iterable @${address.toHex()} ${length}") - val indexReg = regUsage.nextFree() - val loopvarReg = regUsage.nextFree() - TODO() + val arrayAddress = allocations.get(iterable.targetName) + val iterableVar = symbolTable.lookup(iterable.targetName) as StStaticVariable + val loopvarAddress = allocations.get(loopvar.scopedName) + val indexReg = vmRegisters.nextFree() + val loopLabel = createLabelName() + val endLabel = createLabelName() + if(iterableVar.dt==DataType.STR) { + // iterate over a zero-terminated string + code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)) + code += VmCodeLabel(loopLabel) + code += VmCodeInstruction(Instruction(Opcode.LOADX, VmDataType.BYTE, reg1=0, reg2=indexReg, value = arrayAddress)) + code += VmCodeInstruction(Instruction(Opcode.BZ, VmDataType.BYTE, reg1=0, symbol = endLabel)) + code += VmCodeInstruction(Instruction(Opcode.STOREM, VmDataType.BYTE, reg1=0, value = loopvarAddress)) + code += translateNode(forLoop.statements) + code += VmCodeInstruction(Instruction(Opcode.INC, VmDataType.BYTE, reg1=indexReg)) + code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = loopLabel)) + code += VmCodeLabel(endLabel) + } else { + // iterate over array + val elementDt = ArrayToElementTypes.getValue(iterable.type) + val elementSize = program.memsizer.memorySize(elementDt).toUInt() + val lengthBytes = iterableVar.length!! * elementSize.toInt() + val lengthReg = vmRegisters.nextFree() + /* + index = 0 + _loop: + if index==(length * ) goto _end + loopvar = read_mem(iterable+index) + + index += + goto _loop + _end: ... + */ + code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=indexReg, value=0)) + code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=lengthReg, value=lengthBytes)) + code += VmCodeLabel(loopLabel) + code += VmCodeInstruction(Instruction(Opcode.BEQ, VmDataType.BYTE, reg1=indexReg, reg2=lengthReg, symbol = endLabel)) + code += VmCodeInstruction(Instruction(Opcode.LOADX, vmType(elementDt), reg1=0, reg2=indexReg, value=arrayAddress)) + code += VmCodeInstruction(Instruction(Opcode.STOREM, vmType(elementDt), reg1=0, value = loopvarAddress)) + code += translateNode(forLoop.statements) + code += addConst(VmDataType.BYTE, indexReg, elementSize) + code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = loopLabel)) + code += VmCodeLabel(endLabel) + } } else -> throw AssemblyError("weird for iterable") } return code } - private fun translate(ifElse: PtIfElse, regUsage: RegisterUsage): VmCodeChunk { + private fun addConst(dt: VmDataType, reg: Int, value: UInt): VmCodeChunk { + val code = VmCodeChunk() + when(value) { + 0u -> { /* do nothing */ } + 1u -> { + code += VmCodeInstruction(Instruction(Opcode.INC, dt, reg1=reg)) + } + else -> { + val valueReg = vmRegisters.nextFree() + code += VmCodeInstruction(Instruction(Opcode.LOAD, dt, reg1=valueReg, value=value.toInt())) + code += VmCodeInstruction(Instruction(Opcode.ADD, dt, reg1=reg, reg2=reg, reg3=valueReg)) + } + } + return code + } + + private val powersOfTwo = (0..16).map { 2.0.pow(it.toDouble()).toInt() } + + private fun multiplyByConst(dt: VmDataType, reg: Int, factor: UInt): VmCodeChunk { + val code = VmCodeChunk() + val pow2 = powersOfTwo.indexOf(factor.toInt()) + if(pow2>=1) { + // just shift bits + code += VmCodeInstruction(Instruction(Opcode.LSL, dt, reg1=reg, reg2=reg, reg3=pow2)) + } else { + when(factor) { + 0u -> { + code += VmCodeInstruction(Instruction(Opcode.LOAD, dt, reg1=reg, value=0)) + } + 1u -> { /* do nothing */ } + else -> { + val factorReg = vmRegisters.nextFree() + code += VmCodeInstruction(Instruction(Opcode.LOAD, dt, reg1=factorReg, value=factor.toInt())) + code += VmCodeInstruction(Instruction(Opcode.MUL, dt, reg1=reg, reg2=reg, reg3=factorReg)) + } + } + } + return code + } + + private fun translate(ifElse: PtIfElse): VmCodeChunk { var branch = Opcode.BZ var condition = ifElse.condition @@ -133,31 +228,31 @@ class CodeGen(internal val program: PtProgram, } } - val conditionReg = regUsage.nextFree() + val conditionReg = vmRegisters.nextFree() val vmDt = vmType(condition.type) val code = VmCodeChunk() - code += expressionEval.translateExpression(condition, conditionReg, regUsage) + code += expressionEval.translateExpression(condition, conditionReg) if(ifElse.elseScope.children.isNotEmpty()) { // if and else parts val elseLabel = createLabelName() val afterIfLabel = createLabelName() code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg, symbol = elseLabel)) - code += translateNode(ifElse.ifScope, regUsage) + code += translateNode(ifElse.ifScope) code += VmCodeInstruction(Instruction(Opcode.JUMP, symbol = afterIfLabel)) code += VmCodeLabel(elseLabel) - code += translateNode(ifElse.elseScope, regUsage) + code += translateNode(ifElse.elseScope) code += VmCodeLabel(afterIfLabel) } else { // only if part val afterIfLabel = createLabelName() code += VmCodeInstruction(Instruction(branch, vmDt, reg1=conditionReg, symbol = afterIfLabel)) - code += translateNode(ifElse.ifScope, regUsage) + code += translateNode(ifElse.ifScope) code += VmCodeLabel(afterIfLabel) } return code } - private fun translate(postIncrDecr: PtPostIncrDecr, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(postIncrDecr: PtPostIncrDecr): VmCodeChunk { val code = VmCodeChunk() val operation = when(postIncrDecr.operator) { "++" -> Opcode.INC @@ -168,15 +263,15 @@ class CodeGen(internal val program: PtProgram, val memory = postIncrDecr.target.memory val array = postIncrDecr.target.array val vmDt = vmType(postIncrDecr.target.type) - val resultReg = regUsage.nextFree() + val resultReg = vmRegisters.nextFree() if(ident!=null) { val address = allocations.get(ident.targetName) code += VmCodeInstruction(Instruction(Opcode.LOADM, vmDt, reg1=resultReg, value = address)) code += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg)) code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=resultReg, value = address)) } else if(memory!=null) { - val addressReg = regUsage.nextFree() - code += expressionEval.translateExpression(memory.address, addressReg, regUsage) + val addressReg = vmRegisters.nextFree() + code += expressionEval.translateExpression(memory.address, addressReg) code += VmCodeInstruction(Instruction(Opcode.LOADI, vmDt, reg1=resultReg, reg2=addressReg)) code += VmCodeInstruction(Instruction(operation, vmDt, reg1=resultReg)) code += VmCodeInstruction(Instruction(Opcode.STOREI, vmDt, reg1=resultReg, reg2=addressReg)) @@ -188,23 +283,23 @@ class CodeGen(internal val program: PtProgram, return code } - private fun translate(repeat: PtRepeatLoop, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(repeat: PtRepeatLoop): VmCodeChunk { if((repeat.count as? PtNumber)?.number==0.0) return VmCodeChunk() if((repeat.count as? PtNumber)?.number==1.0) - return translateGroup(repeat.children, regUsage) + return translateGroup(repeat.children) if((repeat.count as? PtNumber)?.number==256.0) { // 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) } val code = VmCodeChunk() - val counterReg = regUsage.nextFree() + val counterReg = vmRegisters.nextFree() val vmDt = vmType(repeat.count.type) - code += expressionEval.translateExpression(repeat.count, counterReg, regUsage) + code += expressionEval.translateExpression(repeat.count, counterReg) val repeatLabel = createLabelName() code += VmCodeLabel(repeatLabel) - code += translateNode(repeat.statements, regUsage) + code += translateNode(repeat.statements) code += VmCodeInstruction(Instruction(Opcode.DEC, vmDt, reg1=counterReg)) code += VmCodeInstruction(Instruction(Opcode.BNZ, vmDt, reg1=counterReg, symbol = repeatLabel)) return code @@ -223,18 +318,18 @@ class CodeGen(internal val program: PtProgram, return code } - private fun translateGroup(group: List, regUsage: RegisterUsage): VmCodeChunk { + private fun translateGroup(group: List): VmCodeChunk { val code = VmCodeChunk() - group.forEach { code += translateNode(it, regUsage) } + group.forEach { code += translateNode(it) } return code } - private fun translate(assignment: PtAssignment, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(assignment: PtAssignment): VmCodeChunk { // TODO can in-place assignments (assignment.augmentable = true) be optimized more? val code = VmCodeChunk() - val resultRegister = regUsage.nextFree() - code += expressionEval.translateExpression(assignment.value, resultRegister, regUsage) + val resultRegister = vmRegisters.nextFree() + code += expressionEval.translateExpression(assignment.value, resultRegister) val ident = assignment.target.identifier val memory = assignment.target.memory val array = assignment.target.array @@ -252,11 +347,11 @@ class CodeGen(internal val program: PtProgram, val vmDtArrayIdx = vmType(array.type) if(fixedIndex!=null) { variableAddr += fixedIndex*itemsize - code += VmCodeInstruction(Instruction(Opcode.LOADM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr)) + code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDtArrayIdx, reg1 = resultRegister, value=variableAddr)) } else { - val indexReg = regUsage.nextFree() - code += expressionEval.translateExpression(array.index, indexReg, regUsage) - code += VmCodeInstruction(Instruction(Opcode.LOADX, vmDtArrayIdx, reg1 = resultRegister, reg2=indexReg, value=variableAddr)) + val indexReg = vmRegisters.nextFree() + code += expressionEval.translateExpression(array.index, indexReg) + code += VmCodeInstruction(Instruction(Opcode.STOREX, vmDtArrayIdx, reg1 = resultRegister, reg2=indexReg, value=variableAddr)) } } else if(memory!=null) { @@ -264,8 +359,8 @@ class CodeGen(internal val program: PtProgram, if(memory.address is PtNumber) { Instruction(Opcode.STOREM, vmDt, reg1=resultRegister, value=(memory.address as PtNumber).number.toInt()) } else { - val addressRegister = regUsage.nextFree() - code += expressionEval.translateExpression(assignment.value, addressRegister, regUsage) + val addressRegister = vmRegisters.nextFree() + code += expressionEval.translateExpression(assignment.value, addressRegister) Instruction(Opcode.STOREI, vmDt, reg1=resultRegister, reg2=addressRegister) } code += VmCodeInstruction(ins) @@ -280,30 +375,30 @@ class CodeGen(internal val program: PtProgram, val value = ret.value if(value!=null) { // Call Convention: return value is always returned in r0 - code += expressionEval.translateExpression(value, 0, RegisterUsage(1)) + code += expressionEval.translateExpression(value, 0) } code += VmCodeInstruction(Instruction(Opcode.RETURN)) return code } - private fun translate(sub: PtSub, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(sub: PtSub): VmCodeChunk { // TODO actually inline subroutines marked as inline val code = VmCodeChunk() code += VmCodeComment("SUB: ${sub.scopedName} -> ${sub.returntype}") code += VmCodeLabel(sub.scopedName) for (child in sub.children) { - code += translateNode(child, regUsage) + code += translateNode(child) } code += VmCodeComment("SUB-END '${sub.name}'") return code } - private fun translate(block: PtBlock, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(block: PtBlock): VmCodeChunk { val code = VmCodeChunk() code += VmCodeComment("BLOCK '${block.name}' addr=${block.address} lib=${block.library}") for (child in block.children) { if(child !is PtAssignment) // global variable initialization is done elsewhere - code += translateNode(child, regUsage) + code += translateNode(child) } code += VmCodeComment("BLOCK-END '${block.name}'") return code @@ -327,6 +422,6 @@ class CodeGen(internal val program: PtProgram, return listOf("generated$labelSequenceNumber") } - internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk = - builtinFuncGen.translate(call, resultRegister, regUsage) + internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk = + builtinFuncGen.translate(call, resultRegister) } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt index a21463e1d..859b1e1a7 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/ExpressionGen.kt @@ -12,18 +12,9 @@ import prog8.vm.Opcode import prog8.vm.VmDataType -internal class RegisterUsage(var firstFree: Int=0) { - fun nextFree(): Int { - val result = firstFree - firstFree++ - return result - } -} - - -internal class ExpressionGen(val codeGen: CodeGen) { - fun translateExpression(expr: PtExpression, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk { - require(regUsage.firstFree > resultRegister) +internal class ExpressionGen(private val codeGen: CodeGen) { + fun translateExpression(expr: PtExpression, resultRegister: Int): VmCodeChunk { + require(codeGen.vmRegisters.peekNext() > resultRegister) val code = VmCodeChunk() val vmDt = codeGen.vmType(expr.type) @@ -46,18 +37,18 @@ internal class ExpressionGen(val codeGen: CodeGen) { code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=mem)) } is PtMemoryByte -> { - val addressRegister = regUsage.nextFree() - val addressExprCode = translateExpression(expr.address, addressRegister, regUsage) + val addressRegister = codeGen.vmRegisters.nextFree() + val addressExprCode = translateExpression(expr.address, addressRegister) code += addressExprCode } - is PtTypeCast -> code += translate(expr, resultRegister, regUsage) - is PtPrefix -> code += translate(expr, resultRegister, regUsage) - is PtArrayIndexer -> code += translate(expr, resultRegister, regUsage) - is PtBinaryExpression -> code += translate(expr, resultRegister, regUsage) - is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister, regUsage) - is PtFunctionCall -> code += translate(expr, resultRegister, regUsage) - is PtContainmentCheck -> code += translate(expr, resultRegister, regUsage) - is PtPipe -> code += translate(expr, resultRegister, regUsage) + is PtTypeCast -> code += translate(expr, resultRegister) + is PtPrefix -> code += translate(expr, resultRegister) + is PtArrayIndexer -> code += translate(expr, resultRegister) + is PtBinaryExpression -> code += translate(expr, resultRegister) + is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister) + is PtFunctionCall -> code += translate(expr, resultRegister) + is PtContainmentCheck -> code += translate(expr, resultRegister) + is PtPipe -> code += translate(expr, resultRegister) is PtRange, is PtArray, is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression") @@ -66,34 +57,34 @@ internal class ExpressionGen(val codeGen: CodeGen) { return code } - internal fun translate(pipe: PtPipe, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk { + internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk { TODO("Not yet implemented: pipe expression") } - private fun translate(check: PtContainmentCheck, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(check: PtContainmentCheck, resultRegister: Int): VmCodeChunk { val code = VmCodeChunk() - code += translateExpression(check.element, resultRegister, regUsage) // load the element to check in resultRegister + code += translateExpression(check.element, resultRegister) // load the element to check in resultRegister val iterable = codeGen.symbolTable.flat.getValue(check.iterable.targetName) as StStaticVariable when(iterable.dt) { DataType.STR -> { val call = PtFunctionCall(listOf("prog8_lib", "string_contains"), false, DataType.UBYTE, check.position) call.children.add(check.element) call.children.add(check.iterable) - code += translate(call, resultRegister, regUsage) + code += translate(call, resultRegister) } DataType.ARRAY_UB, DataType.ARRAY_B -> { val call = PtFunctionCall(listOf("prog8_lib", "bytearray_contains"), false, DataType.UBYTE, check.position) call.children.add(check.element) call.children.add(check.iterable) call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position)) - code += translate(call, resultRegister, regUsage) + code += translate(call, resultRegister) } DataType.ARRAY_UW, DataType.ARRAY_W -> { val call = PtFunctionCall(listOf("prog8_lib", "wordarray_contains"), false, DataType.UBYTE, check.position) call.children.add(check.element) call.children.add(check.iterable) call.children.add(PtNumber(DataType.UBYTE, iterable.length!!.toDouble(), iterable.position)) - code += translate(call, resultRegister, regUsage) + code += translate(call, resultRegister) } DataType.ARRAY_F -> TODO("containment check in float-array") else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.targetName}") @@ -101,14 +92,14 @@ internal class ExpressionGen(val codeGen: CodeGen) { return code } - private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(arrayIx: PtArrayIndexer, resultRegister: Int): VmCodeChunk { val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type) val vmDt = codeGen.vmType(arrayIx.type) val code = VmCodeChunk() - val idxReg = regUsage.nextFree() - code += translateExpression(arrayIx.index, idxReg, regUsage) + val idxReg = codeGen.vmRegisters.nextFree() + code += translateExpression(arrayIx.index, idxReg) if(eltSize>1) { - val factorReg = regUsage.nextFree() + val factorReg = codeGen.vmRegisters.nextFree() code += VmCodeInstruction(Instruction(Opcode.LOAD, VmDataType.BYTE, reg1=factorReg, value=eltSize)) code += VmCodeInstruction(Instruction(Opcode.MUL, VmDataType.BYTE, reg1=idxReg, reg2=factorReg)) } @@ -117,9 +108,9 @@ internal class ExpressionGen(val codeGen: CodeGen) { return code } - private fun translate(expr: PtPrefix, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(expr: PtPrefix, resultRegister: Int): VmCodeChunk { val code = VmCodeChunk() - code += translateExpression(expr.value, resultRegister, regUsage) + code += translateExpression(expr.value, resultRegister) val vmDt = codeGen.vmType(expr.type) when(expr.operator) { "+" -> { } @@ -127,7 +118,7 @@ internal class ExpressionGen(val codeGen: CodeGen) { code += VmCodeInstruction(Instruction(Opcode.NEG, vmDt, reg1=resultRegister)) } "~" -> { - val regMask = regUsage.nextFree() + val regMask = codeGen.vmRegisters.nextFree() val mask = if(vmDt==VmDataType.BYTE) 0x00ff else 0xffff code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=mask)) code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)) @@ -137,7 +128,7 @@ internal class ExpressionGen(val codeGen: CodeGen) { code += VmCodeInstruction(Instruction(Opcode.BZ, vmDt, reg1=resultRegister, symbol = label)) code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=resultRegister, value=1)) code += VmCodeLabel(label) - val regMask = regUsage.nextFree() + val regMask = codeGen.vmRegisters.nextFree() code += VmCodeInstruction(Instruction(Opcode.LOAD, vmDt, reg1=regMask, value=1)) code += VmCodeInstruction(Instruction(Opcode.XOR, vmDt, reg1=resultRegister, reg2=resultRegister, reg3=regMask)) } @@ -146,11 +137,11 @@ internal class ExpressionGen(val codeGen: CodeGen) { return code } - private fun translate(cast: PtTypeCast, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(cast: PtTypeCast, resultRegister: Int): VmCodeChunk { val code = VmCodeChunk() if(cast.type==cast.value.type) return code - code += translateExpression(cast.value, resultRegister, regUsage) + code += translateExpression(cast.value, resultRegister) when(cast.type) { DataType.UBYTE -> { when(cast.value.type) { @@ -223,14 +214,14 @@ internal class ExpressionGen(val codeGen: CodeGen) { return code } - private fun translate(binExpr: PtBinaryExpression, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk { + private fun translate(binExpr: PtBinaryExpression, resultRegister: Int): VmCodeChunk { val code = VmCodeChunk() - val leftResultReg = regUsage.nextFree() - val rightResultReg = regUsage.nextFree() + val leftResultReg = codeGen.vmRegisters.nextFree() + val rightResultReg = codeGen.vmRegisters.nextFree() // TODO: optimized codegen when left or right operand is known 0 or 1 or whatever. But only if this would result in a different opcode such as ADD 1 -> INC, MUL 1 -> NOP // actually optimizing the code should not be done here but in a tailored code optimizer step. - val leftCode = translateExpression(binExpr.left, leftResultReg, regUsage) - val rightCode = translateExpression(binExpr.right, rightResultReg, regUsage) + val leftCode = translateExpression(binExpr.left, leftResultReg) + val rightCode = translateExpression(binExpr.right, rightResultReg) code += leftCode code += rightCode val vmDt = codeGen.vmType(binExpr.left.type) @@ -294,12 +285,12 @@ internal class ExpressionGen(val codeGen: CodeGen) { return code } - fun translate(fcall: PtFunctionCall, resultRegister: Int, regUsage: RegisterUsage): VmCodeChunk { + fun translate(fcall: PtFunctionCall, resultRegister: Int): VmCodeChunk { val subroutine = codeGen.symbolTable.flat.getValue(fcall.functionName) as StSub val code = VmCodeChunk() for ((arg, parameter) in fcall.args.zip(subroutine.parameters)) { - val argReg = regUsage.nextFree() - code += translateExpression(arg, argReg, regUsage) + val argReg = codeGen.vmRegisters.nextFree() + code += translateExpression(arg, argReg) val vmDt = codeGen.vmType(parameter.type) val mem = codeGen.allocations.get(fcall.functionName + parameter.name) code += VmCodeInstruction(Instruction(Opcode.STOREM, vmDt, reg1=argReg, value=mem)) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 94b8b8a8c..71321dcdf 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,7 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- vm codegen: fix primes endless loop stuck on '2' +- vm: memory: reading/writing of words seems buggy. add tests+fix - x16: check additional FP lib changes https://github.com/commanderx16/x16-rom/commit/ae608673f0210953172d6837acfbb231d62ddbd1 and https://github.com/commanderx16/x16-docs/commit/21238aedc641da91df88e04c4ce9bf3324a3c12d - x16: check joystick support (petaxian, own stuff) because of api change in r39 kernal https://github.com/commanderx16/x16-docs/blob/master/Commander%20X16%20Programmer's%20Reference%20Guide.md#function-name-joystick_get diff --git a/examples/test.p8 b/examples/test.p8 index bdb7e3ed0..1a2e8b1bc 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,39 +4,35 @@ main { sub start() { - ubyte x1 = $ea - ubyte x2 = $31 - uword uw = mkword(x1, $99) - txt.print_uwhex(uw, true) - txt.nl() - uw = mkword(x1, x2) - txt.print_uwhex(uw, true) + txt.clear_screen() + txt.print("Welcome to a prog8 pixel shader :-)\n") + uword ww = 0 + ubyte bc + uword wc + + for bc in "irmen" { + txt.chrout(bc) + ww++ + } + txt.print_uw(ww) ; 5 txt.nl() - sys.exit(99) + for bc in [10,11,12] { + txt.print_ub(bc) + txt.spc() + ww++ + } + txt.print_uw(ww) ; 8 + txt.nl() + + for wc in [4096,8192,16384] { + txt.print_uw(wc) + txt.spc() + ww++ + } + txt.print_uw(ww) ; 11 + txt.nl() -; txt.clear_screen() -; txt.print("Welcome to a prog8 pixel shader :-)\n") -; uword ww = 0 -; ubyte bc -; uword wc -; -; for bc in "irmen" { -; ; +5 -> 17 -; txt.chrout(bc) -; ww++ -; } -; txt.print_uw(ww) -; txt.nl() -; -; for wc in [1000,1111,1222] { -; txt.print_uw(wc) -; txt.spc() -; ww++ ; +3 -> 20 -; } -; txt.print_uw(ww) -; txt.nl() -; ; for bc in 10 to 20 step 3 { ; ; 10,13,16,19 -> 4 ; ww++ @@ -50,8 +46,8 @@ main { ; txt.print_uw(ww) ; txt.nl() ; -; -; sys.exit(99) + + sys.exit(99) ; the "pixelshader": diff --git a/virtualmachine/src/prog8/vm/Memory.kt b/virtualmachine/src/prog8/vm/Memory.kt index fcfcf5ce4..8c356458e 100644 --- a/virtualmachine/src/prog8/vm/Memory.kt +++ b/virtualmachine/src/prog8/vm/Memory.kt @@ -27,22 +27,22 @@ class Memory { } fun getUW(address: Int): UShort { - return (256u*mem[address] + mem[address+1]).toUShort() + return (256u*mem[address+1] + mem[address]).toUShort() } fun getSW(address: Int): Short { - return (mem[address].toInt()*256 + mem[address+1].toInt()).toShort() + return (mem[address+1].toInt()*256 + mem[address].toInt()).toShort() } fun setUW(address: Int, value: UShort) { - mem[address] = (value.toInt() ushr 8).toUByte() - mem[address+1] = value.toUByte() + mem[address+1] = (value.toInt() ushr 8).toUByte() + mem[address] = value.toUByte() } fun setSW(address: Int, value: Short) { val uv = value.toUShort() - mem[address] = (uv.toInt() ushr 8).toUByte() - mem[address+1] = uv.toUByte() + mem[address+1] = (uv.toInt() ushr 8).toUByte() + mem[address] = uv.toUByte() } // for now, no LONG 32-bits and no FLOAT support.