From f2bb238e9b542eb8435d2193c92d982117c3f9ec Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 24 Mar 2020 19:37:54 +0100 Subject: [PATCH] cleaned up various ast checks/mutations --- compiler/src/prog8/ast/base/Extensions.kt | 10 +-- .../src/prog8/ast/processing/AstChecker.kt | 20 ++++-- .../ast/processing/StatementReorderer.kt | 51 ------------- .../prog8/ast/processing/TypecastsAdder.kt | 9 ++- compiler/src/prog8/compiler/Main.kt | 4 +- ...er.kt => AsmVariableAndReturnsPreparer.kt} | 7 +- .../target/c64/codegen/ExpressionsAsmGen.kt | 2 - .../optimizer/ConstantFoldingOptimizer.kt | 71 ------------------- .../src/prog8/optimizer/StatementOptimizer.kt | 17 ----- docs/source/todo.rst | 3 - examples/test.p8 | 16 ++++- 11 files changed, 45 insertions(+), 165 deletions(-) rename compiler/src/prog8/compiler/target/{AsmVariablePreparer.kt => AsmVariableAndReturnsPreparer.kt} (87%) diff --git a/compiler/src/prog8/ast/base/Extensions.kt b/compiler/src/prog8/ast/base/Extensions.kt index dfb2988d8..09febce79 100644 --- a/compiler/src/prog8/ast/base/Extensions.kt +++ b/compiler/src/prog8/ast/base/Extensions.kt @@ -4,7 +4,7 @@ import prog8.ast.Module import prog8.ast.Program import prog8.ast.processing.* import prog8.compiler.CompilationOptions -import prog8.compiler.target.AsmVariablePreparer +import prog8.compiler.target.AsmVariableAndReturnsPreparer import prog8.optimizer.FlattenAnonymousScopesAndNopRemover @@ -13,10 +13,10 @@ internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: Err checker.visit(this) } -internal fun Program.prepareAsmVariables(errors: ErrorReporter) { - val mover = AsmVariablePreparer(this, errors) - mover.visit(this) - mover.applyModifications() +internal fun Program.prepareAsmVariablesAndReturns(errors: ErrorReporter) { + val fixer = AsmVariableAndReturnsPreparer(this, errors) + fixer.visit(this) + fixer.applyModifications() } internal fun Program.reorderStatements() { diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index c0c83400a..75bbdf31d 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -413,9 +413,11 @@ internal class AstChecker(private val program: Program, } } } - val targetDt = assignTarget.inferType(program, assignment).typeOrElse(DataType.STR) - if(targetDt in IterableDatatypes) - errors.err("cannot assign to a string or array type", assignTarget.position) + +// target type check is already done at the Assignment: +// val targetDt = assignTarget.inferType(program, assignment).typeOrElse(DataType.STR) +// if(targetDt in IterableDatatypes) +// errors.err("cannot assign to a string or array type", assignTarget.position) if (assignment is Assignment) { @@ -1054,11 +1056,15 @@ internal class AstChecker(private val program: Program, for((index, stmt) in statements.withIndex()) { if(stmt is FunctionCallStatement && stmt.target.nameInSource.last()=="exit" - && index < statements.lastIndex) - errors.warn("unreachable code, exit call above never returns", statements[index+1].position) + && index < statements.lastIndex) { + println("STMT AFTER EXIT ${statements[index+1]}") // TODO fix message if next stmt is not a regular stmt + errors.warn("unreachable code, exit call above never returns", statements[index + 1].position) + } - if(stmt is Return && index < statements.lastIndex) - errors.warn("unreachable code, return statement above", statements[index+1].position) + if(stmt is Return && index < statements.lastIndex) { + println("STMT AFTER RETURN ${statements[index+1]}") // TODO fix message if next stmt is not a regular stmt + errors.warn("unreachable code, return statement above", statements[index + 1].position) + } stmt.accept(this) } diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index 1abd46677..43ec41a57 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -25,11 +25,9 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option") - private val addReturns = mutableListOf>() private val addVardecls = mutableMapOf>() override fun visit(module: Module) { - addReturns.clear() addVardecls.clear() super.visit(module) @@ -59,13 +57,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi module.statements.removeAll(directives) module.statements.addAll(0, directives) - // add new statements - for(pos in addReturns) { - val returnStmt = Return(null, pos.first.position) - returnStmt.linkParents(pos.first as Node) - pos.first.statements.add(pos.second, returnStmt) - } - for((where, decls) in addVardecls) { where.statements.addAll(0, decls) decls.forEach { it.linkParents(where as Node) } @@ -93,23 +84,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi } } - // make sure there is a 'return' in front of the first subroutine - // (if it isn't the first statement in the block itself, and isn't the program's entrypoint) - if(numSubroutinesAtEnd>0 && block.statements.size > (numSubroutinesAtEnd+1)) { - val firstSub = block.statements[block.statements.size - numSubroutinesAtEnd] as Subroutine - if(firstSub.name != "start" && block.name != "main") { - val stmtBeforeFirstSub = block.statements[block.statements.size - numSubroutinesAtEnd - 1] - if (stmtBeforeFirstSub !is Return - && stmtBeforeFirstSub !is Jump - && stmtBeforeFirstSub !is Subroutine - && stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) { - val ret = Return(null, stmtBeforeFirstSub.position) - ret.linkParents(block) - block.statements.add(block.statements.size - numSubroutinesAtEnd, ret) - } - } - } - val varDecls = block.statements.filterIsInstance() block.statements.removeAll(varDecls) block.statements.addAll(0, varDecls) @@ -124,19 +98,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi override fun visit(subroutine: Subroutine): Statement { super.visit(subroutine) - val scope = subroutine.definingScope() - if(scope is Subroutine) { - for(stmt in scope.statements.withIndex()) { - if(stmt.index>0 && stmt.value===subroutine) { - val precedingStmt = scope.statements[stmt.index-1] - if(precedingStmt !is Jump && precedingStmt !is Subroutine) { - // insert a return statement before a nested subroutine, to avoid falling trough inside the subroutine - addReturns.add(Pair(scope, stmt.index)) - } - } - } - } - val varDecls = subroutine.statements.filterIsInstance() subroutine.statements.removeAll(varDecls) subroutine.statements.addAll(0, varDecls) @@ -144,18 +105,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi subroutine.statements.removeAll(directives) subroutine.statements.addAll(0, directives) - if(subroutine.returntypes.isEmpty()) { - // add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine. - // and if an assembly block doesn't contain a rts/rti - if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) { - if (subroutine.statements.lastOrNull {it !is VarDecl } !is Return) { - val returnStmt = Return(null, subroutine.position) - returnStmt.linkParents(subroutine) - subroutine.statements.add(returnStmt) - } - } - } - return subroutine } diff --git a/compiler/src/prog8/ast/processing/TypecastsAdder.kt b/compiler/src/prog8/ast/processing/TypecastsAdder.kt index 59a7d1a63..7f1f6e148 100644 --- a/compiler/src/prog8/ast/processing/TypecastsAdder.kt +++ b/compiler/src/prog8/ast/processing/TypecastsAdder.kt @@ -45,11 +45,10 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke val targettype = targetItype.typeOrElse(DataType.STRUCT) val valuetype = valueItype.typeOrElse(DataType.STRUCT) if (valuetype != targettype) { - if (valuetype isAssignableTo targettype) - return listOf(IAstModification.ReplaceNode( - assignment.value, - TypecastExpression(assignment.value, targettype, true, assignment.value.position), - assignment)) + return listOf(IAstModification.ReplaceNode( + assignment.value, + TypecastExpression(assignment.value, targettype, true, assignment.value.position), + assignment)) } } return emptyList() diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index 8726be7ad..424ad15ff 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -40,7 +40,7 @@ fun compileProgram(filepath: Path, if (optimize) optimizeAst(programAst, errors) postprocessAst(programAst, errors, compilationOptions) - // printAst(programAst) + printAst(programAst) // TODO if(writeAssembly) programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions) } @@ -179,7 +179,7 @@ private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: optimize: Boolean, compilerOptions: CompilationOptions): String { // asm generation directly from the Ast, val zeropage = CompilationTarget.machine.getZeropage(compilerOptions) - programAst.prepareAsmVariables(errors) + programAst.prepareAsmVariablesAndReturns(errors) errors.handle() val assembly = CompilationTarget.asmGenerator( programAst, diff --git a/compiler/src/prog8/compiler/target/AsmVariablePreparer.kt b/compiler/src/prog8/compiler/target/AsmVariableAndReturnsPreparer.kt similarity index 87% rename from compiler/src/prog8/compiler/target/AsmVariablePreparer.kt rename to compiler/src/prog8/compiler/target/AsmVariableAndReturnsPreparer.kt index c02aab120..adaaed6fe 100644 --- a/compiler/src/prog8/compiler/target/AsmVariablePreparer.kt +++ b/compiler/src/prog8/compiler/target/AsmVariableAndReturnsPreparer.kt @@ -11,7 +11,7 @@ import prog8.ast.processing.IAstModification import prog8.ast.statements.* -class AsmVariablePreparer(val program: Program, val errors: ErrorReporter): AstWalker() { +class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorReporter): AstWalker() { override fun after(decl: VarDecl, parent: Node): Iterable { if(decl.value==null && decl.type==VarDeclType.VAR && decl.datatype in NumericDatatypes) { @@ -49,4 +49,9 @@ class AsmVariablePreparer(val program: Program, val errors: ErrorReporter): AstW } return emptyList() } + + override fun after(subroutine: Subroutine, parent: Node): Iterable { + TODO("insert Return statements at the required places such as at the end of a subroutine if they're missing") + return emptyList() + } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt index e74137e1d..b447d4373 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/ExpressionsAsmGen.kt @@ -199,7 +199,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge when(expr.operator) { ">>" -> { // bit-shifts are always by a constant number (for now) - // TODO for everything except UBYTE, if shifting > 2 bits, use a subroutine translateExpression(expr.left) val amount = expr.right.constValue(program)!!.number.toInt() when (leftDt) { @@ -239,7 +238,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge } "<<" -> { // bit-shifts are always by a constant number (for now) - // TODO for the word types, if shifting > 3 bits, use a subroutine translateExpression(expr.left) val amount = expr.right.constValue(program)!!.number.toInt() if (leftDt in ByteDatatypes) { diff --git a/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt b/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt index ee4976c88..ef77e5171 100644 --- a/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt +++ b/compiler/src/prog8/optimizer/ConstantFoldingOptimizer.kt @@ -8,7 +8,6 @@ import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.statements.* import prog8.compiler.target.CompilationTarget import prog8.functions.BuiltinFunctions -import kotlin.math.floor internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : IAstModifyingVisitor { @@ -620,74 +619,4 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va } return array } - - - // TODO: type casts are already done elsewhere, remove all this?: - override fun visit(assignment: Assignment): Statement { - super.visit(assignment) - val lv = assignment.value as? NumericLiteralValue - if(lv!=null) { - // see if we can promote/convert a literal value to the required datatype - val idt = assignment.target.inferType(program, assignment) - if(!idt.isKnown) - return assignment - when(idt.typeOrElse(DataType.STRUCT)) { - DataType.UWORD -> { - // we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535, - if(lv.type== DataType.UBYTE) - assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position) - else if(lv.type== DataType.BYTE && lv.number.toInt()>=0) - assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position) - else if(lv.type== DataType.WORD && lv.number.toInt()>=0) - assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position) - else if(lv.type== DataType.FLOAT) { - val d = lv.number.toDouble() - if(floor(d)==d && d>=0 && d<=65535) - assignment.value = NumericLiteralValue(DataType.UWORD, floor(d).toInt(), lv.position) - } - } - DataType.UBYTE -> { - // we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255, - if(lv.type== DataType.UWORD && lv.number.toInt() <= 255) - assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position) - else if(lv.type== DataType.BYTE && lv.number.toInt() >=0) - assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position) - else if(lv.type== DataType.FLOAT) { - val d = lv.number.toDouble() - if(floor(d)==d && d >=0 && d<=255) - assignment.value = NumericLiteralValue(DataType.UBYTE, floor(d).toShort(), lv.position) - } - } - DataType.BYTE -> { - // we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127 - if(lv.type== DataType.UWORD && lv.number.toInt() <= 127) - assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position) - else if(lv.type== DataType.UBYTE && lv.number.toInt() <= 127) - assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position) - else if(lv.type== DataType.FLOAT) { - val d = lv.number.toDouble() - if(floor(d)==d && d>=0 && d<=127) - assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position) - } - } - DataType.WORD -> { - // we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767, - if(lv.type== DataType.UBYTE || lv.type== DataType.BYTE) - assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position) - else if(lv.type== DataType.UWORD && lv.number.toInt() <= 32767) - assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position) - else if(lv.type== DataType.FLOAT) { - val d = lv.number.toDouble() - if(floor(d)==d && d>=-32768 && d<=32767) - assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position) - } - } - DataType.FLOAT -> { - assignment.value = NumericLiteralValue(DataType.FLOAT, lv.number.toDouble(), lv.position) - } - else -> {} - } - } - return assignment - } } diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index a546bcbc2..b8ef9c198 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -115,7 +115,6 @@ internal class StatementOptimizer(private val program: Program, return NopStatement.insteadOf(subroutine) } - visitStatements(subroutine.statements) return subroutine } @@ -548,7 +547,6 @@ internal class StatementOptimizer(private val program: Program, if(linesToRemove.isNotEmpty()) { linesToRemove.reversed().forEach{scope.statements.removeAt(it)} } - visitStatements(scope.statements) return super.visit(scope) } @@ -561,21 +559,6 @@ internal class StatementOptimizer(private val program: Program, return super.visit(label) } - - - private fun visitStatements(statements: MutableList) { - // TODO remove all unreachable code statements after call to exit() or a return - // this is not yet correct because we still have nested subroutines -// val exitCallIndex = statements.indexOfFirst { it is FunctionCallStatement && it.target.nameInSource.last()=="exit" } -// if(exitCallIndex>=0) { -// while(exitCallIndex < statements.lastIndex) { -// val stmt = statements[exitCallIndex+1] -// println("after exit() removing: $stmt") -// statements.removeAt(exitCallIndex+1) -// } -// } - } - } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 927636343..4ce07a91c 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -26,9 +26,6 @@ Memory Block Operations integrated in language? array/string memory block operations? -- vector inc/dec/add/sub/mul/div...? (on array or string): - ``arrayvar++ / arrayvar-- / arrayvar += 2 / arrayvar -= 2 / arrayvar *= 3 / arrayvar /= 3`` - - array operations copy (from another array with the same length), shift-N(left,right), rotate-N(left,right) clear (set whole array to the given value, default 0) diff --git a/examples/test.p8 b/examples/test.p8 index d0fa31ba2..632f52c43 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,6 +6,20 @@ main { sub start() { - c64scr.print("ubyte shift left\n") + byte v1 + byte v2 + + bla() + exit(4) + v1 = 100 + v2 = 127 + A=5 + return + + sub bla () { + A=99 + } + + } }