fix for loop iteration Y register clobbering

This commit is contained in:
Irmen de Jong 2018-12-28 02:08:39 +01:00
parent 26233d5409
commit b01deb2170
9 changed files with 82 additions and 13 deletions

View File

@ -88,6 +88,7 @@
}
; draw all edges of the object
; @todo crashes with array index out of bounds in stackvm
for uword edge in edges {
ubyte e_from = msb(edge)
ubyte e_to = lsb(edge)

View File

@ -1,12 +1,49 @@
%import c64utils
%option enable_floats
~ main {
;@todo implement the various byte/word division routines.
ubyte[3] ubarray = [11,55,222]
byte[3] barray = [-11,-22,-33]
uword[3] uwarray = [111,2222,55555]
word[3] warray = [-111,-222,-555]
str text = "hello\n"
sub start() {
c64scr.print_ub(X)
c64.CHROUT('\n')
c64scr.print("loop str\n")
for ubyte c in text {
c64scr.print(" c ")
c64scr.print_ub(c)
c64.CHROUT('\n')
}
c64scr.print("loop ub\n")
for ubyte ub in ubarray{
c64scr.print(" ub ")
c64scr.print_ub(ub)
c64.CHROUT('\n')
}
; c64scr.print("loop b\n") ; @todo allow signed loopvars
; for byte b in barray { ; @todo loop doesn't end because of register clobbering??
; c64scr.print(" b ")
; c64scr.print_b(b)
; c64.CHROUT('\n')
; }
errorloop:
c64scr.print("loop uw\n")
for uword uw in uwarray { ; @todo loop doesn't end because of register clobbering
c64scr.print(" uw ")
c64scr.print_uw(uw)
c64.CHROUT('\n')
}
ending:
c64scr.print("\nending\n")
c64scr.print_ub(X)
c64.CHROUT('\n')
}
}

View File

@ -1656,6 +1656,10 @@ class ForLoop(val loopRegister: Register?,
override fun toString(): String {
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
}
companion object {
const val iteratorLoopcounterVarname = "prog8forloopcounter"
}
}

View File

@ -151,8 +151,10 @@ class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
}
override fun process(forLoop: ForLoop): IStatement {
// if the for loop has a decltype, it means to declare the loopvar inside the loop body
// If the for loop has a decltype, it means to declare the loopvar inside the loop body
// rather than reusing an already declared loopvar from an outer scope.
// For loops that loop over an interable variable (instead of a range of numbers) get an
// additional interation count variable in their scope.
if(forLoop.loopRegister!=null) {
if(forLoop.decltype!=null)
checkResult.add(SyntaxError("register loop variables cannot be explicitly declared with a datatype", forLoop.position))
@ -164,14 +166,28 @@ class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
DataType.UBYTE, DataType.UWORD -> {
val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
if(existing==null) {
// create the local scoped for loop variable itself
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, null, varName, null, forLoop.loopVar.position)
vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl)
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
}
}
DataType.BYTE, DataType.WORD -> {
checkResult.add(SyntaxError("loop variables can only be unsigned byte or unsigned word", forLoop.position)) // TODO allow signed loopvars
}
null -> {}
else -> checkResult.add(SyntaxError("loop variables can only be an unsigned byte or unsigned word", forLoop.position)) // TODO loops over signed values
else -> checkResult.add(SyntaxError("loop variables can only be a byte or word", forLoop.position))
}
if(forLoop.iterable !is RangeExpr) {
val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
if(existing==null) {
// create loop iteration counter variable
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, null, ForLoop.iteratorLoopcounterVarname, null, forLoop.loopVar.position)
vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl)
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
}
}
}
return super.process(forLoop)

View File

@ -1682,7 +1682,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
AssignTarget(loop.loopRegister, null, null, loop.position)
else
AssignTarget(null, loop.loopVar!!.copy(), null, loop.position)
val arrayspec = ArraySpec(RegisterExpr(Register.Y, loop.position), loop.position)
val arrayspec = ArraySpec(IdentifierReference(listOf(ForLoop.iteratorLoopcounterVarname), loop.position), loop.position)
val assignLv = Assignment(
listOf(assignTarget), null,
ArrayIndexedExpression((loop.iterable as IdentifierReference).copy(), arrayspec, loop.position),
@ -1691,11 +1691,13 @@ private class StatementTranslator(private val prog: IntermediateProgram,
translate(assignLv)
translate(loop.body)
prog.label(continueLabel)
prog.instr(opcodeIncvar(zero.type), callLabel = "Y")
val loopCounterVar = loop.body.getLabelOrVariable(ForLoop.iteratorLoopcounterVarname) as VarDecl
prog.instr(opcodeIncvar(zero.type), callLabel = loopCounterVar.scopedname)
// TODO: optimize edge cases if last value = 255 or 0 (for bytes) etc. to avoid PUSH_BYTE / SUB opcodes and make use of the wrapping around of the value.
prog.instr(opcodePush(zero.type), Value(zero.type, numElements))
prog.instr(opcodePushvar(zero.type), callLabel = "Y")
prog.instr(opcodePushvar(zero.type), callLabel = loopCounterVar.scopedname)
prog.instr(opcodeSub(zero.type))
if(zero.type==DataType.UWORD)
prog.instr(Opcode.JNZW, callLabel = loopLabel)

View File

@ -42,7 +42,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
optimizeVariableCopying()
optimizeMultipleSequentialLineInstrs()
optimizeCallReturnIntoJump()
optimizeRestoreXSaveXIntoRestoreX()
optimizeRestoreXYSaveXYIntoRestoreXY()
// todo: optimize stackvm code more
optimizeRemoveNops() // must be done as the last step
@ -56,8 +56,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr }
}
private fun optimizeRestoreXSaveXIntoRestoreX() {
// replace rrestorex+rsavex combo by only rrestorex
private fun optimizeRestoreXYSaveXYIntoRestoreXY() {
// replace rrestorex/y+rsavex/y combo by only rrestorex/y
for(blk in blocks) {
val instructionsToReplace = mutableMapOf<Int, Instruction>()
@ -65,6 +65,9 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
if(it[0].value.opcode==Opcode.RRESTOREX && it[1].value.opcode==Opcode.RSAVEX) {
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
else if(it[0].value.opcode==Opcode.RRESTOREY && it[1].value.opcode==Opcode.RSAVEY) {
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
}
}
for (rins in instructionsToReplace) {

View File

@ -240,8 +240,10 @@ enum class Opcode {
CLI, // clear irq-disable status flag
RSAVE, // save all internal registers and status flags
RSAVEX, // save just X (the evaluation stack pointer)
RSAVEY, // save just Y (used in for loops for instance)
RRESTORE, // restore all internal registers and status flags
RRESTOREX, // restore just X (the evaluation stack pointer)
RRESTOREY, // restore just Y (used in for loops for instance)
NOP, // do nothing
BREAKPOINT, // breakpoint
TERMINATE, // end the program

View File

@ -432,8 +432,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
// restore all registers and cpu status flag
" pla | tay | pla | tax | pla | plp"
}
Opcode.RSAVEX -> " stx ${C64Zeropage.SCRATCH_REG_X}"
Opcode.RSAVEX -> " stx ${C64Zeropage.SCRATCH_REG_X}" // TODO on stack instead, to allow nested calls?
Opcode.RRESTOREX -> " ldx ${C64Zeropage.SCRATCH_REG_X}"
Opcode.RSAVEY -> " tya | pha"
Opcode.RRESTOREY -> " pla | tay"
Opcode.DISCARD_BYTE -> " inx"
Opcode.DISCARD_WORD -> " inx"
Opcode.DISCARD_FLOAT -> " inx | inx | inx"

View File

@ -1338,7 +1338,9 @@ class StackVm(private var traceOutputFile: String?) {
P_irqd = evalstack.pop().asBooleanValue
}
Opcode.RSAVEX -> evalstack.push(variables["X"])
Opcode.RSAVEY -> evalstack.push(variables["Y"])
Opcode.RRESTOREX -> variables["X"] = evalstack.pop()
Opcode.RRESTOREY -> variables["Y"] = evalstack.pop()
Opcode.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code")
Opcode.PUSH_ADDR_HEAPVAR -> {
val heapId = variables[ins.callLabel]!!.heapId