removed 'continue' statement to be able to generate more optimized loop assembly code. started with for loop optimizations

This commit is contained in:
Irmen de Jong
2020-08-17 18:42:02 +02:00
parent f14dda4eca
commit c0887b5f08
16 changed files with 437 additions and 201 deletions

View File

@@ -305,10 +305,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
output(postIncrDecr.operator) output(postIncrDecr.operator)
} }
override fun visit(contStmt: Continue) {
output("continue")
}
override fun visit(breakStmt: Break) { override fun visit(breakStmt: Break) {
output("break") output("break")
} }

View File

@@ -217,9 +217,6 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
val breakstmt = breakstmt()?.toAst() val breakstmt = breakstmt()?.toAst()
if(breakstmt!=null) return breakstmt if(breakstmt!=null) return breakstmt
val continuestmt = continuestmt()?.toAst()
if(continuestmt!=null) return continuestmt
val whenstmt = whenstmt()?.toAst() val whenstmt = whenstmt()?.toAst()
if(whenstmt!=null) return whenstmt if(whenstmt!=null) return whenstmt
@@ -593,8 +590,6 @@ private fun prog8Parser.ForloopContext.toAst(): ForLoop {
return ForLoop(loopvar, iterable, scope, toPosition()) return ForLoop(loopvar, iterable, scope, toPosition())
} }
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition()) private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop { private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {

View File

@@ -88,7 +88,6 @@ abstract class AstWalker {
open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList() open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList() open fun before(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList() open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList() open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
@@ -130,7 +129,6 @@ abstract class AstWalker {
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList() open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList() open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList() open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList() open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList() open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList() open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
@@ -309,11 +307,6 @@ abstract class AstWalker {
track(after(postIncrDecr, parent), postIncrDecr, parent) track(after(postIncrDecr, parent), postIncrDecr, parent)
} }
fun visit(contStmt: Continue, parent: Node) {
track(before(contStmt, parent), contStmt, parent)
track(after(contStmt, parent), contStmt, parent)
}
fun visit(breakStmt: Break, parent: Node) { fun visit(breakStmt: Break, parent: Node) {
track(before(breakStmt, parent), breakStmt, parent) track(before(breakStmt, parent), breakStmt, parent)
track(after(breakStmt, parent), breakStmt, parent) track(after(breakStmt, parent), breakStmt, parent)

View File

@@ -95,9 +95,6 @@ interface IAstVisitor {
postIncrDecr.target.accept(this) postIncrDecr.target.accept(this)
} }
fun visit(contStmt: Continue) {
}
fun visit(breakStmt: Break) { fun visit(breakStmt: Break) {
} }

View File

@@ -148,18 +148,6 @@ class ReturnFromIrq(override val position: Position) : Return(null, position) {
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
} }
class Continue(override val position: Position) : Statement() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent=parent
}
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
}
class Break(override val position: Position) : Statement() { class Break(override val position: Position) : Statement() {
override lateinit var parent: Node override lateinit var parent: Node

View File

@@ -43,13 +43,11 @@ internal class AsmGen(private val program: Program,
private val assignmentAsmGen = AssignmentAsmGen(program, errors, this) private val assignmentAsmGen = AssignmentAsmGen(program, errors, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this) private val expressionsAsmGen = ExpressionsAsmGen(program, this)
internal val loopEndLabels = ArrayDeque<String>() internal val loopEndLabels = ArrayDeque<String>()
internal val loopContinueLabels = ArrayDeque<String>()
internal val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>() internal val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>()
override fun compileToAssembly(optimize: Boolean): IAssemblyProgram { override fun compileToAssembly(optimize: Boolean): IAssemblyProgram {
assemblyLines.clear() assemblyLines.clear()
loopEndLabels.clear() loopEndLabels.clear()
loopContinueLabels.clear()
println("Generating assembly code... ") println("Generating assembly code... ")
@@ -638,7 +636,6 @@ internal class AsmGen(private val program: Program,
is BranchStatement -> translate(stmt) is BranchStatement -> translate(stmt)
is IfStatement -> translate(stmt) is IfStatement -> translate(stmt)
is ForLoop -> forloopsAsmGen.translate(stmt) is ForLoop -> forloopsAsmGen.translate(stmt)
is Continue -> out(" jmp ${loopContinueLabels.peek()}")
is Break -> out(" jmp ${loopEndLabels.peek()}") is Break -> out(" jmp ${loopEndLabels.peek()}")
is WhileLoop -> translate(stmt) is WhileLoop -> translate(stmt)
is RepeatLoop -> translate(stmt) is RepeatLoop -> translate(stmt)
@@ -674,11 +671,11 @@ internal class AsmGen(private val program: Program,
} }
private fun translate(stmt: RepeatLoop) { private fun translate(stmt: RepeatLoop) {
// TODO more optimized code possible now that continue is gone?
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend") val endLabel = makeLabel("repeatend")
val counterLabel = makeLabel("repeatcounter") val counterLabel = makeLabel("repeatcounter")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
loopContinueLabels.push(repeatLabel)
when (stmt.iterations) { when (stmt.iterations) {
null -> { null -> {
@@ -737,7 +734,6 @@ internal class AsmGen(private val program: Program,
} }
loopEndLabels.pop() loopEndLabels.pop()
loopContinueLabels.pop()
} }
private fun repeatWordCountInAY(counterVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) { private fun repeatWordCountInAY(counterVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
@@ -778,10 +774,10 @@ $endLabel""")
} }
private fun translate(stmt: WhileLoop) { private fun translate(stmt: WhileLoop) {
// TODO more optimized code possible now that continue is gone?
val whileLabel = makeLabel("while") val whileLabel = makeLabel("while")
val endLabel = makeLabel("whileend") val endLabel = makeLabel("whileend")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
loopContinueLabels.push(whileLabel)
out(whileLabel) out(whileLabel)
expressionsAsmGen.translateExpression(stmt.condition) expressionsAsmGen.translateExpression(stmt.condition)
val conditionDt = stmt.condition.inferType(program) val conditionDt = stmt.condition.inferType(program)
@@ -802,14 +798,13 @@ $endLabel""")
out(" jmp $whileLabel") out(" jmp $whileLabel")
out(endLabel) out(endLabel)
loopEndLabels.pop() loopEndLabels.pop()
loopContinueLabels.pop()
} }
private fun translate(stmt: UntilLoop) { private fun translate(stmt: UntilLoop) {
// TODO more optimized code possible now that continue is gone?
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend") val endLabel = makeLabel("repeatend")
loopEndLabels.push(endLabel) loopEndLabels.push(endLabel)
loopContinueLabels.push(repeatLabel)
out(repeatLabel) out(repeatLabel)
translate(stmt.body) translate(stmt.body)
expressionsAsmGen.translateExpression(stmt.untilCondition) expressionsAsmGen.translateExpression(stmt.untilCondition)
@@ -829,7 +824,6 @@ $endLabel""")
} }
out(endLabel) out(endLabel)
loopEndLabels.pop() loopEndLabels.pop()
loopContinueLabels.pop()
} }
private fun translate(stmt: WhenStatement) { private fun translate(stmt: WhenStatement) {

View File

@@ -14,9 +14,8 @@ import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.toHex import prog8.compiler.toHex
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
// todo optimized loop code for common simple cases 0..N, N..0, >=1..N , N..>=1 where N<=255
// todo choose more efficient comparisons to avoid needless lda's // todo choose more efficient comparisons to avoid needless lda's
// todo optimize common case when step == 2 or -2 // todo optimized code for step == 2 or -2
// todo allocate loop counter variable dynamically, preferrably on zeropage // todo allocate loop counter variable dynamically, preferrably on zeropage
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
@@ -42,11 +41,11 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
} }
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) { private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
// TODO more optimized code possible now that continue is gone?
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
val continueLabel = asmgen.makeLabel("for_continue")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
asmgen.loopContinueLabels.push(continueLabel)
val stepsize=range.step.constValue(program)!!.number.toInt() val stepsize=range.step.constValue(program)!!.number.toInt()
when(iterableDt) { when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
@@ -66,7 +65,7 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel lda $varname lda $varname
cmp $ESTACK_LO_PLUS1_HEX,x cmp $ESTACK_LO_PLUS1_HEX,x
beq $endLabel beq $endLabel
$incdec $varname $incdec $varname
@@ -88,7 +87,7 @@ $endLabel inx""")
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel lda $varname""") lda $varname""")
if(stepsize>0) { if(stepsize>0) {
asmgen.out(""" asmgen.out("""
clc clc
@@ -248,15 +247,13 @@ $endLabel inx""")
} }
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
asmgen.loopContinueLabels.pop()
} }
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) { private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
// TODO more optimized code possible now that continue is gone?
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
val continueLabel = asmgen.makeLabel("for_continue")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
asmgen.loopContinueLabels.push(continueLabel)
val iterableName = asmgen.asmIdentifierName(ident) val iterableName = asmgen.asmIdentifierName(ident)
val decl = ident.targetVarDecl(program.namespace)!! val decl = ident.targetVarDecl(program.namespace)!!
when(iterableDt) { when(iterableDt) {
@@ -271,7 +268,7 @@ $loopLabel lda ${65535.toHex()} ; modified
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}") asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel inc $loopLabel+1 inc $loopLabel+1
bne $loopLabel bne $loopLabel
inc $loopLabel+2 inc $loopLabel+2
bne $loopLabel bne $loopLabel
@@ -293,7 +290,7 @@ $modifiedLabel lda ${65535.toHex()},y ; modified""")
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}") asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel ldy $counterLabel ldy $counterLabel
iny iny
cpy #${length and 255} cpy #${length and 255}
beq $endLabel beq $endLabel
@@ -325,7 +322,7 @@ $modifiedLabel2 lda ${65535.toHex()},y ; modified
sta $loopvarName+1""") sta $loopvarName+1""")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel ldy $counterLabel ldy $counterLabel
iny iny
iny iny
cpy #${length and 255} cpy #${length and 255}
@@ -340,57 +337,34 @@ $endLabel""")
else -> throw AssemblyError("can't iterate over $iterableDt") else -> throw AssemblyError("can't iterate over $iterableDt")
} }
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
asmgen.loopContinueLabels.pop()
} }
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) { private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
// TODO: optimize loop code when the range is < 256 iterations if (range.isEmpty() || range.step==0)
if (range.isEmpty()) throw AssemblyError("empty range or step 0")
throw AssemblyError("empty range") if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
if(range.step==1 && range.first>=0 && range.last <= 255 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
if(range.step==-1 && range.first<=255 && range.first >=0 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range)
}
if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
if(range.step==1 && range.first>=0 && range.last <= 255 && range.last>range.first) return translateForSimpleWordRange255Asc(stmt, range)
if(range.step==-1 && range.first<=255 && range.first >=0 && range.last<range.first) return translateForSimpleWordRange255Desc(stmt, range)
if(range.step==1 && range.first>=0 && range.last <= 65535 && range.last>range.first) return translateForSimpleWordRange65535Asc(stmt, range)
if(range.step==-1 && range.first<=65535 && range.first >=0 && range.last<range.first) return translateForSimpleWordRange65535Desc(stmt, range)
}
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
val continueLabel = asmgen.makeLabel("for_continue")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
asmgen.loopContinueLabels.push(continueLabel) // TODO more optimized code possible now that continue is gone?
when(iterableDt) { when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> { DataType.ARRAY_B, DataType.ARRAY_UB -> {
// loop over byte range via loopvar, step > 1 or < -1
val counterLabel = asmgen.makeLabel("for_counter") val counterLabel = asmgen.makeLabel("for_counter")
// loop over byte range via loopvar
val varname = asmgen.asmIdentifierName(stmt.loopVar) val varname = asmgen.asmIdentifierName(stmt.loopVar)
when { when {
range.step==1 -> { range.step==1 || range.step==-1 -> {
// step = 1 throw AssemblyError("step 1 and -1 should have been handled specifically")
asmgen.out("""
lda #${range.first}
sta $varname
lda #${range.last-range.first+1 and 255}
sta $counterLabel
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
inc $varname
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
}
range.step==-1 -> {
// step = -1
asmgen.out("""
lda #${range.first}
sta $varname
lda #${range.first-range.last+1 and 255}
sta $counterLabel
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel dec $counterLabel
beq $endLabel
dec $varname
jmp $loopLabel
$counterLabel .byte 0
$endLabel""")
} }
range.step >= 2 -> { range.step >= 2 -> {
// step >= 2 // step >= 2
@@ -402,7 +376,7 @@ $endLabel""")
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel dec $counterLabel dec $counterLabel
beq $endLabel beq $endLabel
lda $varname lda $varname
clc clc
@@ -422,7 +396,7 @@ $endLabel""")
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel dec $counterLabel dec $counterLabel
beq $endLabel beq $endLabel
lda $varname lda $varname
sec sec
@@ -435,56 +409,59 @@ $endLabel""")
} }
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { DataType.ARRAY_W, DataType.ARRAY_UW -> {
// loop over word range via loopvar // loop over word range via loopvar, step > 1 or < -1
val varname = asmgen.asmIdentifierName(stmt.loopVar) val varname = asmgen.asmIdentifierName(stmt.loopVar)
when { when {
range.step == 1 -> { range.step==1 || range.step==-1 -> {
// word, step = 1 throw AssemblyError("step 1 and -1 should have been handled specifically")
val lastValue = range.last+1
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel inc $varname
bne +
inc $varname+1
+ lda $varname
cmp #<$lastValue
bne +
lda $varname+1
cmp #>$lastValue
beq $endLabel
+ jmp $loopLabel
$endLabel""")
}
range.step == -1 -> {
// word, step = 1
val lastValue = range.last-1
asmgen.out("""
lda #<${range.first}
ldy #>${range.first}
sta $varname
sty $varname+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
$continueLabel lda $varname
bne +
dec $varname+1
+ dec $varname
lda $varname
cmp #<$lastValue
bne +
lda $varname+1
cmp #>$lastValue
beq $endLabel
+ jmp $loopLabel
$endLabel""")
} }
// range.step == 1 -> {
// // word, step = 1
// val lastValue = range.last+1
// asmgen.out("""
// lda #<${range.first}
// ldy #>${range.first}
// sta $varname
// sty $varname+1
//$loopLabel""")
// asmgen.translate(stmt.body)
// asmgen.out("""
// inc $varname
// bne +
// inc $varname+1
//+ lda $varname
// cmp #<$lastValue
// bne +
// lda $varname+1
// cmp #>$lastValue
// beq $endLabel
//+ jmp $loopLabel
//$endLabel""")
// }
// range.step == -1 -> {
// // word, step = 1
// val lastValue = range.last-1
// asmgen.out("""
// lda #<${range.first}
// ldy #>${range.first}
// sta $varname
// sty $varname+1
//$loopLabel""")
// asmgen.translate(stmt.body)
// asmgen.out("""
// lda $varname
// bne +
// dec $varname+1
//+ dec $varname
// lda $varname
// cmp #<$lastValue
// bne +
// lda $varname+1
// cmp #>$lastValue
// beq $endLabel
//+ jmp $loopLabel
//$endLabel""")
// }
range.step >= 2 -> { range.step >= 2 -> {
// word, step >= 2 // word, step >= 2
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence // note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
@@ -497,7 +474,7 @@ $endLabel""")
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel clc clc
lda $varname lda $varname
adc #<${range.step} adc #<${range.step}
sta $varname sta $varname
@@ -525,7 +502,7 @@ $endLabel""")
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.body) asmgen.translate(stmt.body)
asmgen.out(""" asmgen.out("""
$continueLabel sec sec
lda $varname lda $varname
sbc #<${range.step.absoluteValue} sbc #<${range.step.absoluteValue}
sta $varname sta $varname
@@ -546,7 +523,102 @@ $endLabel""")
else -> throw AssemblyError("range expression can only be byte or word") else -> throw AssemblyError("range expression can only be byte or word")
} }
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
asmgen.loopContinueLabels.pop() }
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmIdentifierName(stmt.loopVar)
if (range.last == 255) {
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
inc $varname
bne $loopLabel
$endLabel""")
} else {
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
lda $varname
cmp #${range.last}
beq $endLabel
inc $varname
bne $loopLabel
$endLabel""")
}
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val varname = asmgen.asmIdentifierName(stmt.loopVar)
when (range.last) {
0 -> {
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
lda $varname
beq $endLabel
dec $varname
jmp $loopLabel
$endLabel""")
}
1 -> {
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
dec $varname
bne $loopLabel
$endLabel""")
}
else -> {
asmgen.out("""
lda #${range.first}
sta $varname
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
lda $varname
cmp #${range.last}
beq $endLabel
dec $varname
bne $loopLabel
$endLabel""")
}
}
asmgen.loopEndLabels.pop()
}
private fun translateForSimpleWordRange255Asc(stmt: ForLoop, range: IntProgression) {
TODO("Not yet implemented")
}
private fun translateForSimpleWordRange255Desc(stmt: ForLoop, range: IntProgression) {
TODO("Not yet implemented")
}
private fun translateForSimpleWordRange65535Asc(stmt: ForLoop, range: IntProgression) {
TODO("Not yet implemented")
}
private fun translateForSimpleWordRange65535Desc(stmt: ForLoop, range: IntProgression) {
TODO("Not yet implemented")
} }
} }

View File

@@ -14,11 +14,6 @@ import prog8.functions.BuiltinFunctions
import kotlin.math.floor import kotlin.math.floor
/*
TODO: remove unreachable code after return and exit()
*/
internal class StatementOptimizer(private val program: Program, internal class StatementOptimizer(private val program: Program,
private val errors: ErrorReporter) : AstWalker() { private val errors: ErrorReporter) : AstWalker() {
@@ -257,9 +252,9 @@ internal class StatementOptimizer(private val program: Program,
val constvalue = untilLoop.untilCondition.constValue(program) val constvalue = untilLoop.untilCondition.constValue(program)
if(constvalue!=null) { if(constvalue!=null) {
if(constvalue.asBooleanValue) { if(constvalue.asBooleanValue) {
// always true -> keep only the statement block (if there are no continue and break statements) // always true -> keep only the statement block (if there are no break statements)
errors.warn("condition is always true", untilLoop.untilCondition.position) errors.warn("condition is always true", untilLoop.untilCondition.position)
if(!hasContinueOrBreak(untilLoop.body)) if(!hasBreak(untilLoop.body))
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent)) return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
} else { } else {
// always false // always false
@@ -495,7 +490,7 @@ internal class StatementOptimizer(private val program: Program,
return linesToRemove return linesToRemove
} }
private fun hasContinueOrBreak(scope: INameScope): Boolean { private fun hasBreak(scope: INameScope): Boolean {
class Searcher: IAstVisitor class Searcher: IAstVisitor
{ {
@@ -504,10 +499,6 @@ internal class StatementOptimizer(private val program: Program,
override fun visit(breakStmt: Break) { override fun visit(breakStmt: Break) {
count++ count++
} }
override fun visit(contStmt: Continue) {
count++
}
} }
val s=Searcher() val s=Searcher()

View File

@@ -6,6 +6,9 @@ import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification import prog8.ast.processing.IAstModification
import prog8.ast.statements.Block import prog8.ast.statements.Block
/*
TODO: remove unreachable code after return and exit()
*/
internal class UnusedCodeRemover: AstWalker() { internal class UnusedCodeRemover: AstWalker() {

View File

@@ -397,6 +397,8 @@ The *repeat* loop is used as a short notation of a for loop where the loop varia
You can also create loops by using the ``goto`` statement, but this should usually be avoided. You can also create loops by using the ``goto`` statement, but this should usually be avoided.
Breaking out of a loop prematurely is possible with the ``break`` statement.
.. attention:: .. attention::
The value of the loop variable after executing the loop *is undefined*. Don't use it immediately The value of the loop variable after executing the loop *is undefined*. Don't use it immediately
after the loop without first assigning a new value to it! after the loop without first assigning a new value to it!

View File

@@ -559,7 +559,6 @@ You can use a single statement, or a statement block like in the example below::
for <loopvar> in <expression> [ step <amount> ] { for <loopvar> in <expression> [ step <amount> ] {
; do something... ; do something...
break ; break out of the loop break ; break out of the loop
continue ; immediately enter next iteration
} }
For example, this is a for loop using a byte variable ``i``, defined before, to loop over a certain range of numbers:: For example, this is a for loop using a byte variable ``i``, defined before, to loop over a certain range of numbers::
@@ -592,7 +591,6 @@ You can use a single statement, or a statement block like in the example below::
while <condition> { while <condition> {
; do something... ; do something...
break ; break out of the loop break ; break out of the loop
continue ; immediately enter next iteration
} }
@@ -605,7 +603,6 @@ You can use a single statement, or a statement block like in the example below::
do { do {
; do something... ; do something...
break ; break out of the loop break ; break out of the loop
continue ; immediately enter next iteration
} until <condition> } until <condition>

View File

@@ -2,9 +2,10 @@
TODO TODO
==== ====
- finalize (most) of the still missing "new" assignment asm code generation - get rid of all TODO's ;-)
- aliases for imported symbols for example perhaps '%alias print = c64scr.print' - allow declaring arrays on specific memory location and page-aligned
- option to load library files from a directory instead of the embedded ones (easier library development/debugging) - option to load the built-inlibrary files from a directory instead of the embedded ones (for easier library development/debugging)
- aliases for imported symbols for example perhaps '%alias print = c64scr.print' ?
- investigate support for 8bitguy's Commander X16 platform https://www.commanderx16.com and https://github.com/commanderx16/x16-docs - investigate support for 8bitguy's Commander X16 platform https://www.commanderx16.com and https://github.com/commanderx16/x16-docs
- see if we can group some errors together for instance the (now single) errors about unidentified symbols - see if we can group some errors together for instance the (now single) errors about unidentified symbols

View File

@@ -0,0 +1,74 @@
%import c64utils
main {
const uword rom = $e000
sub sumrom() -> uword {
uword p = rom
uword s = 0
ubyte i
repeat $20 {
for i in 0 to $ff {
s += @(p+i)
}
p += $100
}
return s
}
sub start() {
benchcommon.begin()
ubyte i
for i in 0 to 5 {
c64scr.print_uw(sumrom())
c64.CHROUT('\n')
}
benchcommon.end()
}
}
benchcommon {
ubyte last_time0 = 0
ubyte last_time1 = 0
ubyte last_time2 = 0
ubyte time_start0 = 0
ubyte time_start1 = 0
ubyte time_start2 = 0
asmsub read_time () clobbers(A,X,Y) {
%asm {{
jsr $FFDE
sta last_time0
stx last_time1
sty last_time2
rts
}}
}
sub begin() {
benchcommon.read_time()
benchcommon.time_start0 = benchcommon.last_time0
benchcommon.time_start1 = benchcommon.last_time1
benchcommon.time_start2 = benchcommon.last_time2
}
sub end() {
benchcommon.read_time()
c64scr.print_ubhex(benchcommon.time_start2, false)
c64scr.print_ubhex(benchcommon.time_start1, false)
c64scr.print_ubhex(benchcommon.time_start0, false)
c64.CHROUT('\n')
c64scr.print_ubhex(benchcommon.last_time2, false)
c64scr.print_ubhex(benchcommon.last_time1, false)
c64scr.print_ubhex(benchcommon.last_time0, false)
c64.CHROUT('\n')
void c64scr.input_chars($c000)
}
}

View File

@@ -0,0 +1,88 @@
%import c64utils
main {
const uword COUNT = 16384
const uword SQRT_COUNT = 128
const uword Sieve = $4000
sub sieve_round() {
uword S
ubyte I = 2
memset(Sieve, COUNT, 0)
while I < SQRT_COUNT {
if @(Sieve + I) == 0 {
S = Sieve + (I << 1)
while S < Sieve + COUNT {
@(S) = 1
S += I
}
}
I ++
}
}
sub start() {
benchcommon.begin()
sieve_round()
sieve_round()
sieve_round()
sieve_round()
sieve_round()
sieve_round()
sieve_round()
sieve_round()
sieve_round()
sieve_round()
benchcommon.end()
}
}
benchcommon {
ubyte last_time0 = 0
ubyte last_time1 = 0
ubyte last_time2 = 0
ubyte time_start0 = 0
ubyte time_start1 = 0
ubyte time_start2 = 0
asmsub read_time () clobbers(A,X,Y) {
%asm {{
jsr $FFDE
sta last_time0
stx last_time1
sty last_time2
rts
}}
}
sub begin() {
benchcommon.read_time()
benchcommon.time_start0 = benchcommon.last_time0
benchcommon.time_start1 = benchcommon.last_time1
benchcommon.time_start2 = benchcommon.last_time2
}
sub end() {
benchcommon.read_time()
c64scr.print_ubhex(benchcommon.time_start2, false)
c64scr.print_ubhex(benchcommon.time_start1, false)
c64scr.print_ubhex(benchcommon.time_start0, false)
c64.CHROUT('\n')
c64scr.print_ubhex(benchcommon.last_time2, false)
c64scr.print_ubhex(benchcommon.last_time1, false)
c64scr.print_ubhex(benchcommon.last_time0, false)
c64.CHROUT('\n')
void c64scr.input_chars($c000)
}
}

View File

@@ -2,49 +2,97 @@
%import c64utils %import c64utils
%import c64flt %import c64flt
%zeropage basicsafe %zeropage basicsafe
%option enable_floats
main { main {
sub start() { sub start() {
ubyte counterb
uword counterw
ubyte wv
ubyte wv2
wv = wv + wv + wv for counterb in 0 to 10 {
c64scr.print_ub(counterb)
c64.CHROUT(',')
}
c64.CHROUT('\n')
; wv *= wv2 for counterb in 10 to 30 {
; c64scr.print_ub(counterb)
; wv += 10 c64.CHROUT(',')
; wv += 20 }
; wv += 30 c64.CHROUT('\n')
;
; wv += 1 + wv2
; wv += 2 + wv2
; wv += 3 + wv2
;
; wv += wv2 + 1
; wv += wv2 + 2
; wv += wv2 + 3
;
; wv = wv + 1 + wv2
; wv = wv + 2 + wv2
; wv = wv + 3 + wv2
;
; wv = 1 + wv2 + wv
; wv = 2 + wv2 + wv
; wv = 3 + wv2 + wv
;
; wv = wv + wv2 + 1
; wv = wv + wv2 + 2
; wv = wv + wv2 + 3
;
; wv = wv2 + 1 + wv
; wv = wv2 + 2 + wv
; wv = wv2 + 3 + wv
wv = wv2 + wv + 1 for counterb in 250 to 255 {
wv = wv2 + wv + 2 c64scr.print_ub(counterb)
wv = wv2 + wv + 3 c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterb in 10 to 0 step -1 {
c64scr.print_ub(counterb)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterb in 10 to 1 step -1 {
c64scr.print_ub(counterb)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterb in 30 to 10 step -1 {
c64scr.print_ub(counterb)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterb in 255 to 250 step -1 {
c64scr.print_ub(counterb)
c64.CHROUT(',')
}
c64.CHROUT('\n')
c64.CHROUT('\n')
for counterw in 0 to 10 {
c64scr.print_uw(counterw)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterw in 10 to 30 {
c64scr.print_uw(counterw)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterw in 250 to 255 {
c64scr.print_uw(counterw)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterw in 10 to 0 step -1 {
c64scr.print_uw(counterw)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterw in 10 to 1 step -1 {
c64scr.print_uw(counterw)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterw in 30 to 10 step -1 {
c64scr.print_uw(counterw)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for counterw in 255 to 250 step -1 {
c64scr.print_uw(counterw)
c64.CHROUT(',')
}
c64.CHROUT('\n')
} }
} }

View File

@@ -98,7 +98,6 @@ statement :
| repeatloop | repeatloop
| whenstmt | whenstmt
| breakstmt | breakstmt
| continuestmt
| labeldef | labeldef
; ;
@@ -214,8 +213,6 @@ returnstmt : 'return' expression? ;
breakstmt : 'break'; breakstmt : 'break';
continuestmt: 'continue';
identifier : NAME ; identifier : NAME ;
scoped_identifier : NAME ('.' NAME)* ; scoped_identifier : NAME ('.' NAME)* ;