diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 8eaa5b7cd..1b83c4353 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -78,7 +78,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) val bankTr = exprGen.translateExpression(call.args[0]) val addressTr = exprGen.translateExpression(call.args[1]) val argumentwordTr = exprGen.translateExpression(call.args[2]) @@ -91,7 +90,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) val bankTr = exprGen.translateExpression(call.args[0]) val addressTr = exprGen.translateExpression(call.args[1]) val argumentA = exprGen.translateExpression(call.args[2]) @@ -143,7 +141,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val left = exprGen.translateExpression(call.args[0]) val right = exprGen.translateExpression(call.args[1]) addToResult(result, left, left.resultReg, -1) @@ -278,7 +275,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) val type = irType(call.type) val valueTr = exprGen.translateExpression(call.args[0]) val minimumTr = exprGen.translateExpression(call.args[1]) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 35739e4ec..7f4eaf79d 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -289,7 +289,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val haystackVar = check.haystackHeapVar!! when { haystackVar.type.isString -> { - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val elementTr = translateExpression(check.needle) addToResult(result, elementTr, elementTr.resultReg, -1) val iterableTr = translateExpression(haystackVar) @@ -300,7 +299,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) } haystackVar.type.isByteArray -> { - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) val elementTr = translateExpression(check.needle) addToResult(result, elementTr, elementTr.resultReg, -1) val iterableTr = translateExpression(haystackVar) @@ -314,7 +312,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) } haystackVar.type.isWordArray -> { - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) val elementTr = translateExpression(check.needle) addToResult(result, elementTr, elementTr.resultReg, -1) val iterableTr = translateExpression(haystackVar) @@ -328,7 +325,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) } haystackVar.type.isFloatArray -> { - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) val elementTr = translateExpression(check.needle) addToResult(result, elementTr, -1, elementTr.resultFpReg) val iterableTr = translateExpression(haystackVar) @@ -618,7 +614,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { when (callTarget) { is StSub -> { val result = mutableListOf() - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null) // assign the arguments val argRegisters = mutableListOf() for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { @@ -670,7 +665,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } is StExtSub -> { val result = mutableListOf() - addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null) // assign the arguments val argRegisters = mutableListOf() for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 80848addd..479ba2626 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -48,6 +48,7 @@ class IRCodeGen( irProg.linkChunks() irProg.convertAsmChunks() + // the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks. val optimizer = IRPeepholeOptimizer(irProg) optimizer.optimize(options.optimize, errors) irProg.validate() diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index 51a3bdb77..8f48d567b 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { } private fun optimizeOnlyJoinChunks() { + // this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list irprog.foreachSub { sub -> joinChunks(sub) removeEmptyChunks(sub) diff --git a/codeGenIntermediate/test/TestVmCodeGen.kt b/codeGenIntermediate/test/TestVmCodeGen.kt index 6a0fecd44..6b219ab44 100644 --- a/codeGenIntermediate/test/TestVmCodeGen.kt +++ b/codeGenIntermediate/test/TestVmCodeGen.kt @@ -554,11 +554,8 @@ class TestVmCodeGen: FunSpec({ val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks irChunks.size shouldBe 1 - irChunks[0].instructions.size shouldBe 2 - val preparecallInstr = irChunks[0].instructions[0] - preparecallInstr.opcode shouldBe Opcode.PREPARECALL - preparecallInstr.immediate shouldBe 0 - val callInstr = irChunks[0].instructions[1] + irChunks[0].instructions.size shouldBe 1 + val callInstr = irChunks[0].instructions[0] callInstr.opcode shouldBe Opcode.CALL callInstr.address shouldBe 0x5000 } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index d1915e9e3..3bc17cf4a 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -31,6 +31,7 @@ Future Things and Ideas IR/VM ----- +- BUG Key main.start.the_loop is missing in the map when compiling shell into IR. See test.p8 in commit that added this line here - getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!) - fix call() return value handling - proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg diff --git a/examples/test.p8 b/examples/test.p8 index 15eb43713..b351a520e 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,10 +1,15 @@ main { - ubyte @shared banknumber - extsub @bank 10 $C04B = otherbank() clobbers(A,X,Y) - extsub @bank banknumber $C04B = otherbankvar() clobbers(A,X,Y) - sub start() { - otherbank() - otherbankvar() + nmi_handler() + cx16.r0++ + the_loop: + repeat { + cx16.r0++ + } + } + + sub nmi_handler() {;forcefully kills the running process and returns to the shell prompt. + cx16.r0++ + goto main.start.the_loop } } diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index ef9e0ca15..55c5b8e0f 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -71,7 +71,6 @@ CONTROL FLOW ------------ jump location - continue running at instruction at 'location' (label/memory address) jumpi reg1 - continue running at memory address in reg1 (indirect jump) -preparecall numparams - indicator that the next instructions are the param setup and function call/syscall with parameters, does nothing by itself calli reg1 - calls a subroutine (without arguments and without return valus) at memory addres in reg1 (indirect jsr) call label(argument register list) [: resultreg.type] - calls a subroutine with the given arguments and return value (optional). @@ -80,13 +79,13 @@ call label(argument register list) [: resultreg.type] If the call is to a rom-routine, 'label' will be a hexadecimal address instead such as $ffd2 If the arguments should be passed in CPU registers, they'll have a @REGISTER postfix. For example: call $ffd2(r5.b@A) - Always preceded by parameter setup and preparecall instructions + Always preceded by parameter setup callfar bank, address Call a subroutine at the given memory address, in the given RAM/ROM bank (switches both banks at the same time) callfarvb reg1 address Call a subroutine at the given memory address, in the RAM/ROM bank in reg1.b (switches both banks at the same time) syscall number (argument register list) [: resultreg.type] - do a systemcall identified by number, result value(s) are pushed on value stack by the syscall code so will be POPped off into the given resultregister if any. - Always preceded by parameter setup and preparecall instructions. + Always preceded by parameter setup All register types (arguments + result register) are ALWAYS WORDS. return - restore last saved instruction location and continue at that instruction. No return value. returnr reg1 - like return, but also returns the value in reg1 to the caller @@ -283,7 +282,6 @@ enum class Opcode { JUMP, JUMPI, - PREPARECALL, CALLI, CALL, CALLFAR, @@ -626,7 +624,6 @@ val instructionFormats = mutableMapOf( Opcode.STOREHFACONE to InstructionFormat.from("F, + chunk.instructions.forEach { instr -> if(instr.labelSymbol!=null && instr.opcode in OpcodesThatBranch) { if(instr.opcode==Opcode.JUMPI) { when(val pointervar = st.lookup(instr.labelSymbol)!!) { @@ -229,26 +229,6 @@ class IRProgram(val name: String, else if(!instr.labelSymbol.startsWith('$') && !instr.labelSymbol.first().isDigit()) require(instr.branchTarget != null) { "branching instruction to label should have branchTarget set" } } - - if(instr.opcode==Opcode.PREPARECALL) { - var i = index+1 - var instr2 = chunk.instructions[i] - val registers = mutableSetOf() - while(instr2.opcode!=Opcode.SYSCALL && instr2.opcode!=Opcode.CALL && i InsSTOREHFACONE(ins) Opcode.JUMP -> InsJUMP(ins) Opcode.JUMPI -> InsJUMPI(ins) - Opcode.PREPARECALL -> nextPc() Opcode.CALLI -> throw IllegalArgumentException("VM cannot run code from memory bytes") Opcode.CALL -> InsCALL(ins) Opcode.CALLFAR, Opcode.CALLFARVB -> throw IllegalArgumentException("VM cannot run code from another ram/rombank")