diff --git a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index eb7fbde2d..27dd25c4c 100644 --- a/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/codeGeneration/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -781,18 +781,14 @@ class AsmGen(private val program: Program, is BranchStatement -> translate(stmt) is IfStatement -> translate(stmt) is ForLoop -> forloopsAsmGen.translate(stmt) - is Break -> { - if(loopEndLabels.isEmpty()) - throw AssemblyError("break statement out of context ${stmt.position}") - jmp(loopEndLabels.peek()) - } is WhileLoop -> translate(stmt) is RepeatLoop -> translate(stmt) is UntilLoop -> translate(stmt) is WhenStatement -> translate(stmt) - is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?") is AnonymousScope -> translate(stmt) + is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore") is Block -> throw AssemblyError("block should have been handled elsewhere") + is Break -> throw AssemblyError("break should have been replaced by goto") else -> throw AssemblyError("missing asm translation for $stmt") } } @@ -1366,34 +1362,12 @@ $repeatLabel lda $counterVar out(" $instruction ${getJumpTarget(jump)}") translate(stmt.elsepart) } else { - val truePartIsJustBreak = stmt.truepart.statements.firstOrNull() is Break - val elsePartIsJustBreak = stmt.elsepart.statements.firstOrNull() is Break if(stmt.elsepart.isEmpty()) { - if(truePartIsJustBreak) { - // branch with just a break (jump out of loop) - val instruction = branchInstruction(stmt.condition, false) - val loopEndLabel = loopEndLabels.peek() - out(" $instruction $loopEndLabel") - } else { - val instruction = branchInstruction(stmt.condition, true) - val elseLabel = makeLabel("branch_else") - out(" $instruction $elseLabel") - translate(stmt.truepart) - out(elseLabel) - } - } - else if(truePartIsJustBreak) { - // branch with just a break (jump out of loop) - val instruction = branchInstruction(stmt.condition, false) - val loopEndLabel = loopEndLabels.peek() - out(" $instruction $loopEndLabel") - translate(stmt.elsepart) - } else if(elsePartIsJustBreak) { - // branch with just a break (jump out of loop) but true/false inverted val instruction = branchInstruction(stmt.condition, true) - val loopEndLabel = loopEndLabels.peek() - out(" $instruction $loopEndLabel") + val elseLabel = makeLabel("branch_else") + out(" $instruction $elseLabel") translate(stmt.truepart) + out(elseLabel) } else { val instruction = branchInstruction(stmt.condition, true) val elseLabel = makeLabel("branch_else") diff --git a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt index 787bc095f..494ba04f7 100644 --- a/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt +++ b/codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt @@ -6,7 +6,6 @@ import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification -import prog8.ast.walk.IAstVisitor import prog8.compilerinterface.ICompilationTarget import prog8.compilerinterface.IErrorReporter import prog8.compilerinterface.size @@ -228,15 +227,14 @@ class StatementOptimizer(private val program: Program, override fun before(untilLoop: UntilLoop, parent: Node): Iterable { val constvalue = untilLoop.condition.constValue(program) if(constvalue!=null) { - if(constvalue.asBooleanValue) { - // always true -> keep only the statement block (if there are no break statements) + return if(constvalue.asBooleanValue) { + // always true -> keep only the statement block errors.warn("condition is always true", untilLoop.condition.position) - if(!hasBreak(untilLoop.body)) - return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent)) + listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent)) } else { // always false val forever = RepeatLoop(null, untilLoop.body, untilLoop.position) - return listOf(IAstModification.ReplaceNode(untilLoop, forever, parent)) + listOf(IAstModification.ReplaceNode(untilLoop, forever, parent)) } } return noModifications @@ -474,25 +472,4 @@ class StatementOptimizer(private val program: Program, return noModifications } - - private fun hasBreak(scope: IStatementContainer): Boolean { - - class Searcher: IAstVisitor - { - var count=0 - - override fun visit(breakStmt: Break) { - count++ - } - } - - val s=Searcher() - for(stmt in scope.statements) { - stmt.accept(s) - if(s.count>0) - return true - } - return s.count > 0 - } - } diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index d2ac36898..cf132c36c 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -31,6 +31,10 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, private val o varsList.add(decl.name to decl) } + override fun before(breakStmt: Break, parent: Node): Iterable { + throw FatalAstException("break should have been replaced by goto $breakStmt") + } + override fun before(block: Block, parent: Node): Iterable { // move all subroutines to the bottom of the block val subs = block.statements.filterIsInstance() diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 435cb3f34..9f90b282b 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -76,8 +76,8 @@ fun compileProgram(args: CompilerArguments): CompilationResult { ) postprocessAst(program, args.errors, compilationOptions) -// println("*********** AST BEFORE ASSEMBLYGEN *************") -// printProgram(program) + println("*********** AST BEFORE ASSEMBLYGEN *************") + printProgram(program) if (args.writeAssembly) { when (val result = writeAssembly(program, args.errors, args.outputDir, args.quietAssembler, compilationOptions)) { @@ -267,6 +267,8 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions errors.report() program.reorderStatements(errors, compilerOptions) errors.report() + program.desugaring(errors) + errors.report() program.addTypecasts(errors, compilerOptions) errors.report() program.variousCleanups(program, errors) @@ -295,11 +297,11 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e if (optsDone1 + optsDone2 + optsDone3 == 0) break } - errors.report() } private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) { + program.desugaring(errors) program.addTypecasts(errors, compilerOptions) errors.report() program.variousCleanups(program, errors) diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index 0b7c6118b..e1028c5e6 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -59,6 +59,12 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp caster.applyModifications() } +fun Program.desugaring(errors: IErrorReporter): Int { + val desugar = CodeDesugarer(this, errors) + desugar.visit(this) + return desugar.applyModifications() +} + internal fun Program.verifyFunctionArgTypes() { val fixer = VerifyFunctionArgTypes(this) fixer.visit(this) diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt new file mode 100644 index 000000000..032b04c42 --- /dev/null +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -0,0 +1,59 @@ +package prog8.compiler.astprocessing + +import prog8.ast.IStatementContainer +import prog8.ast.Node +import prog8.ast.Program +import prog8.ast.base.ParentSentinel +import prog8.ast.expressions.IdentifierReference +import prog8.ast.statements.* +import prog8.ast.walk.AstWalker +import prog8.ast.walk.IAstModification +import prog8.compilerinterface.* + + +internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() { + + // Some more code shuffling to simplify the Ast that the codegenerator has to process. + // Several changes have already been done by the StatementReorderer ! + // But the ones here are simpler and are repeated once again after all optimization steps + // have been performed (because those could re-introduce nodes that have to be desugared) + // + // List of modifications: + // - replace 'break' statements by a goto + generated after label. + + + private var generatedLabelSequenceNumber: Int = 0 + private val generatedLabelPrefix = "prog8_label_" + + private fun makeLabel(postfix: String): String { + generatedLabelSequenceNumber++ + return "${generatedLabelPrefix}${generatedLabelSequenceNumber}_$postfix" + } + + override fun before(breakStmt: Break, parent: Node): Iterable { + fun jumpAfter(stmt: Statement): Iterable { + val labelName = makeLabel("after") + val ident = IdentifierReference(listOf(labelName), breakStmt.position) + return listOf( + IAstModification.ReplaceNode(breakStmt, Jump(null, ident, null, breakStmt.position), parent), + IAstModification.InsertAfter(stmt, Label(labelName, breakStmt.position), stmt.parent as IStatementContainer) + ) + } + + var partof = parent + while(true) { + when (partof) { + is Subroutine, is Block, is ParentSentinel -> { + errors.err("break in wrong scope", breakStmt.position) + return noModifications + } + is ForLoop, + is RepeatLoop, + is UntilLoop, + is WhileLoop -> return jumpAfter(partof as Statement) + else -> partof = partof.parent + } + } + } + +} diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 2d5add6c6..03d11b61f 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -8,10 +8,8 @@ import prog8.ast.walk.AstWalker import prog8.ast.walk.IAstModification import prog8.compilerinterface.BuiltinFunctions import prog8.compilerinterface.CompilationOptions -import prog8.compilerinterface.ICompilationTarget import prog8.compilerinterface.IErrorReporter - internal class StatementReorderer(val program: Program, val errors: IErrorReporter, private val options: CompilationOptions) : AstWalker() { diff --git a/examples/test.p8 b/examples/test.p8 index 839f4456a..b1f07e8e4 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,19 +1,38 @@ - +%import textio +%zeropage basicsafe main { - ubyte @shared joy_info - sub start() { - void pushing_start() - } + ubyte @shared xx + repeat { + xx++ + if xx==10 + break + } + txt.print_ub(xx) + txt.nl() - sub pushing_start() -> ubyte { - joy_info++ - return not c64.READST() - } + while xx<50 { + xx++ + if xx==40 + break + } + txt.print_ub(xx) + txt.nl() - sub derp(ubyte aa) -> ubyte { - aa++ - return aa*2 + do { + xx++ + if xx==80 + break + } until xx>100 + txt.print_ub(xx) + txt.nl() + + for xx in 0 to 25 { + if xx==20 + break + } + txt.print_ub(xx) + txt.nl() } }