mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 19:54:26 +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
|
; draw all edges of the object
|
||||||
|
; @todo crashes with array index out of bounds in stackvm
|
||||||
for uword edge in edges {
|
for uword edge in edges {
|
||||||
ubyte e_from = msb(edge)
|
ubyte e_from = msb(edge)
|
||||||
ubyte e_to = lsb(edge)
|
ubyte e_to = lsb(edge)
|
||||||
|
@ -1,12 +1,49 @@
|
|||||||
%import c64utils
|
%import c64utils
|
||||||
%option enable_floats
|
|
||||||
|
|
||||||
|
|
||||||
~ main {
|
~ 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 {
|
override fun toString(): String {
|
||||||
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
|
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 {
|
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.
|
// 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.loopRegister!=null) {
|
||||||
if(forLoop.decltype!=null)
|
if(forLoop.decltype!=null)
|
||||||
checkResult.add(SyntaxError("register loop variables cannot be explicitly declared with a datatype", forLoop.position))
|
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 -> {
|
DataType.UBYTE, DataType.UWORD -> {
|
||||||
val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
|
val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
|
||||||
if(existing==null) {
|
if(existing==null) {
|
||||||
|
// create the local scoped for loop variable itself
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, null, varName, null, forLoop.loopVar.position)
|
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, null, varName, null, forLoop.loopVar.position)
|
||||||
vardecl.linkParents(forLoop.body)
|
vardecl.linkParents(forLoop.body)
|
||||||
forLoop.body.statements.add(0, vardecl)
|
forLoop.body.statements.add(0, vardecl)
|
||||||
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
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 -> {}
|
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)
|
return super.process(forLoop)
|
||||||
|
@ -1682,7 +1682,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
|||||||
AssignTarget(loop.loopRegister, null, null, loop.position)
|
AssignTarget(loop.loopRegister, null, null, loop.position)
|
||||||
else
|
else
|
||||||
AssignTarget(null, loop.loopVar!!.copy(), null, loop.position)
|
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(
|
val assignLv = Assignment(
|
||||||
listOf(assignTarget), null,
|
listOf(assignTarget), null,
|
||||||
ArrayIndexedExpression((loop.iterable as IdentifierReference).copy(), arrayspec, loop.position),
|
ArrayIndexedExpression((loop.iterable as IdentifierReference).copy(), arrayspec, loop.position),
|
||||||
@ -1691,11 +1691,13 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
|||||||
translate(assignLv)
|
translate(assignLv)
|
||||||
translate(loop.body)
|
translate(loop.body)
|
||||||
prog.label(continueLabel)
|
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.
|
// 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(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))
|
prog.instr(opcodeSub(zero.type))
|
||||||
if(zero.type==DataType.UWORD)
|
if(zero.type==DataType.UWORD)
|
||||||
prog.instr(Opcode.JNZW, callLabel = loopLabel)
|
prog.instr(Opcode.JNZW, callLabel = loopLabel)
|
||||||
|
@ -42,7 +42,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
|||||||
optimizeVariableCopying()
|
optimizeVariableCopying()
|
||||||
optimizeMultipleSequentialLineInstrs()
|
optimizeMultipleSequentialLineInstrs()
|
||||||
optimizeCallReturnIntoJump()
|
optimizeCallReturnIntoJump()
|
||||||
optimizeRestoreXSaveXIntoRestoreX()
|
optimizeRestoreXYSaveXYIntoRestoreXY()
|
||||||
// todo: optimize stackvm code more
|
// todo: optimize stackvm code more
|
||||||
|
|
||||||
optimizeRemoveNops() // must be done as the last step
|
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 }
|
blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeRestoreXSaveXIntoRestoreX() {
|
private fun optimizeRestoreXYSaveXYIntoRestoreXY() {
|
||||||
// replace rrestorex+rsavex combo by only rrestorex
|
// replace rrestorex/y+rsavex/y combo by only rrestorex/y
|
||||||
for(blk in blocks) {
|
for(blk in blocks) {
|
||||||
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
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) {
|
if(it[0].value.opcode==Opcode.RRESTOREX && it[1].value.opcode==Opcode.RSAVEX) {
|
||||||
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
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) {
|
for (rins in instructionsToReplace) {
|
||||||
|
@ -240,8 +240,10 @@ enum class Opcode {
|
|||||||
CLI, // clear irq-disable status flag
|
CLI, // clear irq-disable status flag
|
||||||
RSAVE, // save all internal registers and status flags
|
RSAVE, // save all internal registers and status flags
|
||||||
RSAVEX, // save just X (the evaluation stack pointer)
|
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
|
RRESTORE, // restore all internal registers and status flags
|
||||||
RRESTOREX, // restore just X (the evaluation stack pointer)
|
RRESTOREX, // restore just X (the evaluation stack pointer)
|
||||||
|
RRESTOREY, // restore just Y (used in for loops for instance)
|
||||||
NOP, // do nothing
|
NOP, // do nothing
|
||||||
BREAKPOINT, // breakpoint
|
BREAKPOINT, // breakpoint
|
||||||
TERMINATE, // end the program
|
TERMINATE, // end the program
|
||||||
|
@ -432,8 +432,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
// restore all registers and cpu status flag
|
// restore all registers and cpu status flag
|
||||||
" pla | tay | pla | tax | pla | plp"
|
" 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.RRESTOREX -> " ldx ${C64Zeropage.SCRATCH_REG_X}"
|
||||||
|
Opcode.RSAVEY -> " tya | pha"
|
||||||
|
Opcode.RRESTOREY -> " pla | tay"
|
||||||
Opcode.DISCARD_BYTE -> " inx"
|
Opcode.DISCARD_BYTE -> " inx"
|
||||||
Opcode.DISCARD_WORD -> " inx"
|
Opcode.DISCARD_WORD -> " inx"
|
||||||
Opcode.DISCARD_FLOAT -> " inx | inx | inx"
|
Opcode.DISCARD_FLOAT -> " inx | inx | inx"
|
||||||
|
@ -1338,7 +1338,9 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
P_irqd = evalstack.pop().asBooleanValue
|
P_irqd = evalstack.pop().asBooleanValue
|
||||||
}
|
}
|
||||||
Opcode.RSAVEX -> evalstack.push(variables["X"])
|
Opcode.RSAVEX -> evalstack.push(variables["X"])
|
||||||
|
Opcode.RSAVEY -> evalstack.push(variables["Y"])
|
||||||
Opcode.RRESTOREX -> variables["X"] = evalstack.pop()
|
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.INLINE_ASSEMBLY -> throw VmExecutionException("stackVm doesn't support executing inline assembly code")
|
||||||
Opcode.PUSH_ADDR_HEAPVAR -> {
|
Opcode.PUSH_ADDR_HEAPVAR -> {
|
||||||
val heapId = variables[ins.callLabel]!!.heapId
|
val heapId = variables[ins.callLabel]!!.heapId
|
||||||
|
Loading…
Reference in New Issue
Block a user