slight optimization of repeat loop (0-256 iters) code generation on 65c02 cpu

This commit is contained in:
Irmen de Jong 2021-12-31 14:01:45 +01:00
parent 583e208c1e
commit 1e8d8e40a2
3 changed files with 79 additions and 36 deletions

View File

@ -1128,13 +1128,13 @@ class AsmGen(private val program: Program,
}
private fun translate(stmt: RepeatLoop) {
val repeatLabel = makeLabel("repeat")
val endLabel = makeLabel("repeatend")
loopEndLabels.push(endLabel)
when (stmt.iterations) {
null -> {
// endless loop
val repeatLabel = makeLabel("repeat")
out(repeatLabel)
translate(stmt.body)
jmp(repeatLabel)
@ -1146,13 +1146,11 @@ class AsmGen(private val program: Program,
throw AssemblyError("invalid number of iterations")
when {
iterations == 0 -> {}
iterations <= 256 -> {
out(" lda #${iterations and 255}")
repeatByteCountInA(iterations, repeatLabel, endLabel, stmt)
}
iterations == 1 -> translate(stmt.body)
iterations <= 256 -> repeatByteCount(iterations, stmt)
else -> {
out(" lda #<${iterations} | ldy #>${iterations}")
repeatWordCountInAY(iterations, repeatLabel, endLabel, stmt)
repeatWordCountInAY(iterations, endLabel, stmt)
}
}
}
@ -1161,12 +1159,12 @@ class AsmGen(private val program: Program,
val name = asmVariableName(stmt.iterations as IdentifierReference)
when(vardecl.datatype) {
DataType.UBYTE, DataType.BYTE -> {
assignVariableToRegister(name, RegisterOrPair.A)
repeatByteCountInA(null, repeatLabel, endLabel, stmt)
assignVariableToRegister(name, RegisterOrPair.Y)
repeatCountInY(stmt, endLabel)
}
DataType.UWORD, DataType.WORD -> {
assignVariableToRegister(name, RegisterOrPair.AY)
repeatWordCountInAY(null, repeatLabel, endLabel, stmt)
repeatWordCountInAY(null, endLabel, stmt)
}
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
}
@ -1177,12 +1175,12 @@ class AsmGen(private val program: Program,
throw AssemblyError("unknown dt")
when (dt.getOr(DataType.UNDEFINED)) {
in ByteDatatypes -> {
assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.A)
repeatByteCountInA(null, repeatLabel, endLabel, stmt)
assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.Y)
repeatCountInY(stmt, endLabel)
}
in WordDatatypes -> {
assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.AY)
repeatWordCountInAY(null, repeatLabel, endLabel, stmt)
repeatWordCountInAY(null, endLabel, stmt)
}
else -> throw AssemblyError("invalid loop expression datatype $dt")
}
@ -1192,13 +1190,14 @@ class AsmGen(private val program: Program,
loopEndLabels.pop()
}
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, stmt: RepeatLoop) {
private fun repeatWordCountInAY(constIterations: Int?, endLabel: String, stmt: RepeatLoop) {
// note: A/Y must have been loaded with the number of iterations!
if(constIterations==0)
return
// no need to explicitly test for 0 iterations as this is done in the countdown logic below
val counterVar: String = createRepeatCounterVar(DataType.UWORD, constIterations, stmt)
val repeatLabel = makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)!!
out("""
sta $counterVar
sty $counterVar+1
@ -1216,23 +1215,58 @@ $repeatLabel lda $counterVar
out(endLabel)
}
private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, stmt: RepeatLoop) {
// note: A must be loaded with the number of iterations!
if(constIterations==0)
return
if(constIterations==null)
out(" beq $endLabel ; skip loop if zero iters")
val counterVar = createRepeatCounterVar(DataType.UBYTE, constIterations, stmt)
out(" sta $counterVar")
out(repeatLabel)
translate(stmt.body)
out(" dec $counterVar | bne $repeatLabel")
if(constIterations==null)
out(endLabel)
private fun repeatByteCount(count: Int, stmt: RepeatLoop) {
require(count in 2..256)
val repeatLabel = makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
if(counterVar!=null) {
out(" lda #${count and 255} | sta $counterVar")
out(repeatLabel)
translate(stmt.body)
out(" dec $counterVar | bne $repeatLabel")
} else {
out(" ldy #${count and 255}")
out("$repeatLabel phy")
translate(stmt.body)
out(" ply | dey | bne $repeatLabel")
}
} else {
val counterVar = createRepeatCounterVar(DataType.UBYTE, false, stmt)
out(" lda #${count and 255} | sta $counterVar")
out(repeatLabel)
translate(stmt.body)
out(" dec $counterVar | bne $repeatLabel")
}
}
private fun createRepeatCounterVar(dt: DataType, constIterations: Int?, stmt: RepeatLoop): String {
private fun repeatCountInY(stmt: RepeatLoop, endLabel: String) {
// note: Y must just have been loaded with the (variable) number of loops to be performed!
val repeatLabel = makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
if(counterVar!=null) {
out(" beq $endLabel | sty $counterVar")
out(repeatLabel)
translate(stmt.body)
out(" dec $counterVar | bne $repeatLabel")
} else {
out(" beq $endLabel")
out("$repeatLabel phy")
translate(stmt.body)
out(" ply | dey | bne $repeatLabel")
}
} else {
val counterVar = createRepeatCounterVar(DataType.UBYTE, false, stmt)!!
out(" beq $endLabel | sty $counterVar")
out(repeatLabel)
translate(stmt.body)
out(" dec $counterVar | bne $repeatLabel")
}
out(endLabel)
}
private fun createRepeatCounterVar(dt: DataType, mustBeInZeropage: Boolean, stmt: RepeatLoop): String? {
val asmInfo = stmt.definingSubroutine!!.asmGenInfo
var parent = stmt.parent
while(parent !is ParentSentinel) {
@ -1245,27 +1279,33 @@ $repeatLabel lda $counterVar
if(!isNested) {
// we can re-use a counter var from the subroutine if it already has one for that datatype
val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt }
if(existingVar!=null)
return existingVar.second
if(existingVar!=null) {
if(!mustBeInZeropage || existingVar.third!=null)
return existingVar.second
}
}
val counterVar = makeLabel("repeatcounter")
val counterVar = makeLabel("counter")
when(dt) {
DataType.UBYTE -> {
if(constIterations!=null && constIterations>=16 && zeropage.hasByteAvailable()) {
if(zeropage.hasByteAvailable()) {
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, DataType.UBYTE, stmt.position, errors)
asmInfo.extraVars.add(Triple(DataType.UBYTE, counterVar, zpAddr))
} else {
if(mustBeInZeropage)
return null
asmInfo.extraVars.add(Triple(DataType.UBYTE, counterVar, null))
}
}
DataType.UWORD -> {
if(constIterations!=null && constIterations>=16 && zeropage.hasWordAvailable()) {
if(zeropage.hasWordAvailable()) {
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, stmt.position, errors)
asmInfo.extraVars.add(Triple(DataType.UWORD, counterVar, zpAddr))
} else {
if(mustBeInZeropage)
return null
asmInfo.extraVars.add(Triple(DataType.UWORD, counterVar, null))
}
}

View File

@ -3,12 +3,14 @@ TODO
For next compiler release (7.6)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
see if the 16 bit repeat code can be optimized on 65c02 a bit more as well, like the 8 bit
Need help with
^^^^^^^^^^^^^^
- c128 target: various machine specific things (free zp locations, how banking works, getting the floating point routines working, ...)
- other targets such as Atari 800XL: all required details about the machine, I have no clue whatsoever
- see the Porting Guide in the documentation for this.
Blocked by an official Commander-x16 r39 release
@ -45,6 +47,7 @@ Future
More code optimization ideas
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- if a for loop's loopvariable isn't referenced in the body, replace by a repeatloop
- automatically convert if statements that test for multiple values (if X==1 or X==2..) to if X in [1,2,..] statements, instead of just a warning
- byte typed expressions should be evaluated in the accumulator where possible, without (temp)var
for instance value = otherbyte >> 1 --> lda otherbite ; lsr a; sta value

View File

@ -1,6 +1,6 @@
#!/usr/bin/env sh
rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list a.out
rm -f *.jar *.asm *.prg *.vm.txt *.vice-mon-list *.list a.out imgui.ini
rm -rf build out
rm -rf compiler/build codeGeneration/build codeOptimizers/build compilerInterfaces/build compilerAst/build dbusCompilerService/build httpCompilerService/build parser/build