mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +00:00
fix for loop iteration Y register clobbering
This commit is contained in:
parent
26233d5409
commit
b01deb2170
@ -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)
|
||||
|
@ -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')
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user