From d6cf8bcce006c8688359d6035677214571298a14 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 12 Dec 2018 00:15:05 +0100 Subject: [PATCH] fixed return values problem and wrong optimization into jump --- compiler/examples/numbergame-novm.p8 | 14 +----- compiler/examples/numbergame.p8 | 3 +- compiler/examples/test.p8 | 23 +++------- compiler/src/prog8/StackVmMain.kt | 2 +- compiler/src/prog8/ast/AstChecker.kt | 4 +- .../src/prog8/ast/AstIdentifiersChecker.kt | 2 + .../intermediate/IntermediateProgram.kt | 46 +++++++++++++++++-- .../prog8/optimizing/StatementOptimizer.kt | 12 ----- 8 files changed, 58 insertions(+), 48 deletions(-) diff --git a/compiler/examples/numbergame-novm.p8 b/compiler/examples/numbergame-novm.p8 index d33cd6e43..3ff9d97ec 100644 --- a/compiler/examples/numbergame-novm.p8 +++ b/compiler/examples/numbergame-novm.p8 @@ -48,8 +48,7 @@ ; c64.CHROUT('\n') if guess==secretnumber { - ending(true) - return ; @todo make return ending(true) actually work as well + return ending(true) } else { c64scr.print_string("\n\nThat is too ") if guess1 vm_write_str("es") + if attempts_left>1 + vm_write_str("es") vm_write_str(" left. What is your next guess? ") vm_input_str(guess) ubyte guessednumber = str2ubyte(guess) diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index ce5e5310c..69c84edaf 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -1,26 +1,15 @@ -%output raw -%launcher none +%import c64utils ~ main { sub start() { - ending(true) - return ; @todo make return ending(true) actually work as well - - return 99 ;@todo error message (no return values) - return 99,44 ;@todo error message (no return values) - return ending(false) ; @todo fix this, actuall needs to CALL ending even though no value is returned + return ending(true) ;; @todo fix argument passing! sub ending(success: ubyte) { - return 99 ; @todo error message (no return values) - return 99,44 ; @todo error message (no return values) - return 99,44 ; @todo should check number of return values!! - } - - sub ending2() -> ubyte { - return - return 1 - return 2, 2 ; @todo error message number of return values + c64scr.print_byte_decimal(success) + c64scr.print_byte_decimal(success) + c64scr.print_byte_decimal(success) + c64.CHROUT('\n') } } } diff --git a/compiler/src/prog8/StackVmMain.kt b/compiler/src/prog8/StackVmMain.kt index 4d1d26e7c..b3f83ae2e 100644 --- a/compiler/src/prog8/StackVmMain.kt +++ b/compiler/src/prog8/StackVmMain.kt @@ -7,7 +7,7 @@ import kotlin.system.exitProcess fun main(args: Array) { println("\nProg8 StackVM by Irmen de Jong (irmen@razorvine.net)") - // @todo software license string + // @todo decide on software license // println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n") println("**** This is a prerelease version. Please do not distribute! ****\n") diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index b3f8b5c31..794f7a2c7 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -96,9 +96,9 @@ class AstChecker(private val namespace: INameScope, if(returnStmt.values.size==1 && returnStmt.values[0] is FunctionCall) { val dt = (returnStmt.values[0] as FunctionCall).resultingDatatype(namespace, heap) if(dt!=null && expectedReturnValues.isEmpty()) - checkResult.add(SyntaxError("number of return values doesn't match subroutine return spec", returnStmt.position)) + checkResult.add(SyntaxError("invalid number of return values", returnStmt.position)) } else - checkResult.add(SyntaxError("number of return values doesn't match subroutine return spec", returnStmt.position)) + checkResult.add(SyntaxError("invalid number of return values", returnStmt.position)) } for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) { diff --git a/compiler/src/prog8/ast/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/AstIdentifiersChecker.kt index 2528ee6e6..399392953 100644 --- a/compiler/src/prog8/ast/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/AstIdentifiersChecker.kt @@ -177,6 +177,8 @@ class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor { if(returnStmt.values.isNotEmpty()) { // possibly adjust any literal values returned, into the desired returning data type val subroutine = returnStmt.definingSubroutine()!! + if(subroutine.returntypes.size!=returnStmt.values.size) + return returnStmt // mismatch in number of return values, error will be printed later. val newValues = mutableListOf() for(returnvalue in returnStmt.values.zip(subroutine.returntypes)) { val lval = returnvalue.first as? LiteralValue diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt index eefec7152..73110a564 100644 --- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt +++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt @@ -41,13 +41,53 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap optimizeDataConversionAndUselessDiscards() optimizeVariableCopying() optimizeMultipleSequentialLineInstrs() + optimizeCallReturnIntoJump() + optimizeRestoreXSaveXIntoRestoreX() // todo: optimize stackvm code more - // todo: stackvm replace rrestorex+rsavex combo by only rrestorex (note: can have label/comment inbetween) - // todo: stackvm replace call X + return (without values) combo by a jump X + optimizeRemoveNops() // must be done as the last step + optimizeMultipleSequentialLineInstrs() // once more + optimizeRemoveNops() // once more + } + + private fun optimizeRemoveNops() { // remove nops (that are not a label) - for (blk in blocks) { + for (blk in blocks) blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr } + } + + private fun optimizeRestoreXSaveXIntoRestoreX() { + // replace rrestorex+rsavex combo by only rrestorex + for(blk in blocks) { + val instructionsToReplace = mutableMapOf() + + blk.instructions.asSequence().withIndex().filter {it.value.opcode!=Opcode.LINE}.windowed(2).toList().forEach { + if(it[0].value.opcode==Opcode.RRESTOREX && it[1].value.opcode==Opcode.RSAVEX) { + instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) + } + } + + for (rins in instructionsToReplace) { + blk.instructions[rins.key] = rins.value + } + } + } + + private fun optimizeCallReturnIntoJump() { + // replaces call X followed by return, by jump X + for(blk in blocks) { + val instructionsToReplace = mutableMapOf() + + blk.instructions.asSequence().withIndex().filter {it.value.opcode!=Opcode.LINE}.windowed(2).toList().forEach { + if(it[0].value.opcode==Opcode.CALL && it[1].value.opcode==Opcode.RETURN) { + instructionsToReplace[it[1].index] = Instruction(Opcode.JUMP, callLabel = it[0].value.callLabel) + instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) + } + } + + for (rins in instructionsToReplace) { + blk.instructions[rins.key] = rins.value + } } } diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index 6c7b7183a..588de8e9f 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -67,18 +67,6 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He return super.process(functionCall) } - override fun process(returnStmt: Return): IStatement { - // if the return value is a subroutine call, replace this with a jump to the subroutine - if(returnStmt.values.size==1 && returnStmt.values[0] is FunctionCall) { - val call = returnStmt.values[0] as FunctionCall - if(call.target.targetStatement(namespace) is Subroutine) { - optimizationsDone++ - return Jump(null, call.target, null, call.position) - } - } - return super.process(returnStmt) - } - override fun process(ifStatement: IfStatement): IStatement { super.process(ifStatement) val constvalue = ifStatement.condition.constValue(namespace, heap)