lower code: break -> goto after (simplifies codegen)

This commit is contained in:
Irmen de Jong 2021-12-25 21:42:50 +01:00
parent a090fe3834
commit 4da4f96669
8 changed files with 114 additions and 75 deletions

View File

@ -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")

View File

@ -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<IAstModification> {
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
}
}

View File

@ -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<IAstModification> {
throw FatalAstException("break should have been replaced by goto $breakStmt")
}
override fun before(block: Block, parent: Node): Iterable<IAstModification> {
// move all subroutines to the bottom of the block
val subs = block.statements.filterIsInstance<Subroutine>()

View File

@ -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)

View File

@ -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)

View File

@ -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<IAstModification> {
fun jumpAfter(stmt: Statement): Iterable<IAstModification> {
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
}
}
}
}

View File

@ -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() {

View File

@ -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()
}
}