From 4a47e15b1c360fc23156f34913075372e7a9fa75 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 31 Oct 2024 22:19:04 +0100 Subject: [PATCH] fix IR if expression sometimes lacking a cmpi after calculation of the condition value VM/IR: add a returni immediate value return instruction to replace certain returnr's --- .../prog8/codegen/intermediate/IRCodeGen.kt | 39 ++++++++---- .../intermediate/IRPeepholeOptimizer.kt | 2 +- docs/source/todo.rst | 5 +- examples/test.p8 | 62 ++++++++++++++----- .../src/prog8/intermediate/IRInstructions.kt | 9 ++- .../src/prog8/intermediate/IRProgram.kt | 2 +- virtualmachine/src/prog8/vm/VirtualMachine.kt | 41 ++++++++++++ 7 files changed, 124 insertions(+), 36 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index e0561375e..c67353e4f 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -1395,12 +1395,14 @@ class IRCodeGen( private fun ifWithElse_IntegerCond(ifElse: PtIfElse): List { val result = mutableListOf() - fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode) { + fun translateSimple(condition: PtExpression, jumpFalseOpcode: Opcode, addCmpiZero: Boolean) { if(condition is PtBuiltinFunctionCall && condition.name.startsWith("prog8_ifelse_bittest_")) throw AssemblyError("IR codegen doesn't have special instructions for dedicated BIT tests and should just still use normal AND") val tr = expressionEval.translateExpression(condition) + if(addCmpiZero) + tr.chunks.last().instructions.add(IRInstruction(Opcode.CMPI, tr.dt, reg1 = tr.resultReg, immediate = 0)) result += tr.chunks if(ifElse.hasElse()) { val elseLabel = createLabelName() @@ -1420,7 +1422,7 @@ class IRCodeGen( fun translateBinExpr(condition: PtBinaryExpression) { if(condition.operator in LogicalOperators) - return translateSimple(condition, Opcode.BSTEQ) + return translateSimple(condition, Opcode.BSTEQ, false) val signed = condition.left.type in SignedDatatypes val elseBranchFirstReg: Int @@ -1559,18 +1561,21 @@ class IRCodeGen( when(val cond=ifElse.condition) { is PtBool -> { // normally this will be optimized away, but not with -noopt - translateSimple(cond, Opcode.BSTEQ) + translateSimple(cond, Opcode.BSTEQ, false) } is PtTypeCast -> { require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes) - translateSimple(cond, Opcode.BSTEQ) + translateSimple(cond, Opcode.BSTEQ, false) } - is PtIdentifier, is PtArrayIndexer, is PtBuiltinFunctionCall, is PtFunctionCall, is PtContainmentCheck -> { - translateSimple(cond, Opcode.BSTEQ) + is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> { + translateSimple(cond, Opcode.BSTEQ, false) + } + is PtBuiltinFunctionCall, is PtFunctionCall -> { + translateSimple(cond, Opcode.BSTEQ, true) } is PtPrefix -> { require(cond.operator=="not") - translateSimple(cond.value, Opcode.BSTNE) + translateSimple(cond.value, Opcode.BSTNE, false) } is PtBinaryExpression -> { translateBinExpr(cond) @@ -1648,14 +1653,22 @@ class IRCodeGen( addInstr(result, IRInstruction(Opcode.RETURN), null) } else { if(value.type==DataType.FLOAT) { - val tr = expressionEval.translateExpression(value) - addToResult(result, tr, -1, tr.resultFpReg) - addInstr(result, IRInstruction(Opcode.RETURNR, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null) + if(value is PtNumber) { + addInstr(result, IRInstruction(Opcode.RETURNI, IRDataType.FLOAT, immediateFp = value.number), null) + } else { + val tr = expressionEval.translateExpression(value) + addToResult(result, tr, -1, tr.resultFpReg) + addInstr(result, IRInstruction(Opcode.RETURNR, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null) + } } else { - val tr = expressionEval.translateExpression(value) - addToResult(result, tr, tr.resultReg, -1) - addInstr(result, IRInstruction(Opcode.RETURNR, irType(value.type) , reg1=tr.resultReg), null) + if(value.asConstInteger()!=null) { + addInstr(result, IRInstruction(Opcode.RETURNI, irType(value.type), immediate = value.asConstInteger()), null) + } else { + val tr = expressionEval.translateExpression(value) + addToResult(result, tr, tr.resultReg, -1) + addInstr(result, IRInstruction(Opcode.RETURNR, irType(value.type), reg1 = tr.resultReg), null) + } } } return result diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt index 140fe8012..08cb6c91c 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRPeepholeOptimizer.kt @@ -299,7 +299,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) { } // remove useless RETURN - if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR)) { + if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR || ins.opcode==Opcode.RETURNI)) { val previous = chunk.instructions[idx-1] if(previous.opcode in OpcodesThatJump) { chunk.instructions.removeAt(idx) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 29e917743..4e5414493 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,9 +1,8 @@ TODO ==== -VM/IR: fix return value passing from %ir routines? (diskio.exists routine always returns true) - -make better zsmkit example +replace zsound example by a zsmkit example +contribute a short how-to to the zsmkit repo for building a suitable blob write a howto for integrating third party library code like zsmkit and vtui Improve register load order in subroutine call args assignments: diff --git a/examples/test.p8 b/examples/test.p8 index 775cf173d..c64a2067f 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,30 +1,60 @@ -%import diskio %import textio -;%import gfx_hires4 +%import diskio %option no_sysinit %zeropage basicsafe main { sub start() { - txt.print_bool(diskio.exists("doesntexist.prg")) + txt.print_ub(exists_byte("test.prg")) + txt.spc() + txt.print_ub(exists_byte("doesntexist.xxx")) txt.nl() - txt.print_bool(diskio.exists("test.prg")) + txt.print_bool(exists_bool("test.prg")) + txt.spc() + txt.print_bool(exists_bool("doesntexist.xxx")) txt.nl() + txt.print_bool(exists1("test.prg")) + txt.spc() + txt.print_bool(exists1("doesntexist.xxx")) + txt.nl() + txt.print_bool(exists2("test.prg")) + txt.spc() + txt.print_bool(exists2("doesntexist.xxx")) + txt.nl() + } - diskio.f_open_w("dump.bin") - diskio.f_close_w() - diskio.f_close_w() - diskio.f_close_w() - diskio.f_close_w() + sub exists1(str filename) -> bool { + ; -- returns true if the given file exists on the disk, otherwise false + if exists_byte(filename)!=0 { + return true + } + return false + } -; gfx_hires4.graphics_mode() -; gfx_hires4.circle(300, 250, 200, 3) -; gfx_hires4.rect(320, 10, 20, 200, 3) -; gfx_hires4.fill(310, 310, 2) -; -; repeat { -; } + sub exists2(str filename) -> bool { + ; -- returns true if the given file exists on the disk, otherwise false + if exists_bool(filename) { + return true + } + return false + } + + + sub exists_bool(str name) -> bool { + %ir {{ + loadm.w r65535,main.exists_bool.name + syscall 52 (r65535.w): r0.b + returnr.b r0 + }} + } + + sub exists_byte(str name) -> ubyte { + %ir {{ + loadm.w r65535,main.exists_byte.name + syscall 52 (r65535.w): r0.b + returnr.b r0 + }} } } diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 86277809a..3315b4382 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -82,6 +82,7 @@ syscall number (argument register list) [: resultreg.type] Always preceded by parameter setup and preparecall instructions 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 +returni number - like return, but also returns the immediate value to the caller @@ -289,6 +290,7 @@ enum class Opcode { SYSCALL, RETURN, RETURNR, + RETURNI, BSTCC, BSTCS, @@ -428,7 +430,8 @@ val OpcodesThatJump = arrayOf( Opcode.JUMP, Opcode.JUMPI, Opcode.RETURN, - Opcode.RETURNR + Opcode.RETURNR, + Opcode.RETURNI ) val OpcodesThatBranch = arrayOf( @@ -436,6 +439,7 @@ val OpcodesThatBranch = arrayOf( Opcode.JUMPI, Opcode.RETURN, Opcode.RETURNR, + Opcode.RETURNI, Opcode.CALLI, Opcode.CALL, Opcode.SYSCALL, @@ -647,7 +651,8 @@ val instructionFormats = mutableMapOf( Opcode.CALL to InstructionFormat.from("N,call"), Opcode.SYSCALL to InstructionFormat.from("N,syscall"), Opcode.RETURN to InstructionFormat.from("N"), - Opcode.RETURNR to InstructionFormat.from("BW,>r1 | F,>fr1"), + Opcode.RETURNR to InstructionFormat.from("BW, InsSYSCALL(ins) Opcode.RETURN -> InsRETURN() Opcode.RETURNR -> InsRETURNR(ins) + Opcode.RETURNI -> InsRETURNI(ins) Opcode.BSTCC -> InsBSTCC(ins) Opcode.BSTCS -> InsBSTCS(ins) Opcode.BSTEQ -> InsBSTEQ(ins) @@ -656,6 +657,46 @@ class VirtualMachine(irProgram: IRProgram) { } } + private fun InsRETURNI(i: IRInstruction) { + if(callStack.isEmpty()) + exit(0) + else { + val context = callStack.removeLast() + val returns = context.fcallSpec.returns + when (i.type!!) { + IRDataType.BYTE -> { + if(returns.isNotEmpty()) + registers.setUB(returns.single().registerNum, i.immediate!!.toUByte()) + else { + val callInstr = context.returnChunk.instructions[context.returnIndex-1] + if(callInstr.opcode!=Opcode.CALL) + throw IllegalArgumentException("missing return value reg") + } + } + IRDataType.WORD -> { + if(returns.isNotEmpty()) + registers.setUW(returns.single().registerNum, i.immediate!!.toUShort()) + else { + val callInstr = context.returnChunk.instructions[context.returnIndex-1] + if(callInstr.opcode!=Opcode.CALL) + throw IllegalArgumentException("missing return value reg") + } + } + IRDataType.FLOAT -> { + if(returns.isNotEmpty()) + registers.setFloat(returns.single().registerNum, i.immediateFp!!) + else { + val callInstr = context.returnChunk.instructions[context.returnIndex-1] + if(callInstr.opcode!=Opcode.CALL) + throw IllegalArgumentException("missing return value reg") + } + } + } + pcChunk = context.returnChunk + pcIndex = context.returnIndex + } + } + private fun InsRETURNR(i: IRInstruction) { if(callStack.isEmpty()) exit(0)