diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c782a12..feccc126 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ * 6502: Fixed optimizations using index registers. +* Fixed volatile-related bugs. + * Fixed optimizations removing jumps to jumps. * Fixed optimizations removing pointless stores to local variables. diff --git a/src/main/scala/millfork/assembly/mos/opt/VariableToRegisterOptimization.scala b/src/main/scala/millfork/assembly/mos/opt/VariableToRegisterOptimization.scala index 0da05e8b..a73edc10 100644 --- a/src/main/scala/millfork/assembly/mos/opt/VariableToRegisterOptimization.scala +++ b/src/main/scala/millfork/assembly/mos/opt/VariableToRegisterOptimization.scala @@ -987,21 +987,6 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine] // these TAY's may get optimized away by a different optimization tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY) :: _) - case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs - if th.name == vx && LdxAddrModes(am) => - // these TXA's may get optimized away by a different optimization - tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDX, am, param).pos(s1, s2) :: AssemblyLine.implied(TXA).pos(s1, s2) :: _) - - case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs - if th.name == vy && LdyAddrModes(am) => - // these TYA's may get optimized away by a different optimization - tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDY, am, param).pos(s1, s2) :: AssemblyLine.implied(TYA).pos(s1, s2) :: _) - - case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs - if th.name == vz && LdzAddrModes(am) => - // these TZA's may get optimized away by a different optimization - tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDZ, am, param).pos(s1, s2) :: AssemblyLine.implied(TZA).pos(s1, s2) :: _) - case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs if th.name == vx && CpxyzAddrModes(am) && isNot(vx, param) => // ditto @@ -1045,6 +1030,21 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine] if th.name == va => tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY).pos(s) :: _) + case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs + if th.name == vx && LdxAddrModes(am) => + // these TXA's may get optimized away by a different optimization + tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDX, am, param).pos(s1, s2) :: AssemblyLine.implied(TXA).pos(s1, s2) :: _) + + case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs + if th.name == vy && LdyAddrModes(am) => + // these TYA's may get optimized away by a different optimization + tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDY, am, param).pos(s1, s2) :: AssemblyLine.implied(TYA).pos(s1, s2) :: _) + + case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs + if th.name == vz && LdzAddrModes(am) => + // these TZA's may get optimized away by a different optimization + tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDZ, am, param).pos(s1, s2) :: AssemblyLine.implied(TZA).pos(s1, s2) :: _) + case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs if th.name == vx => tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _) diff --git a/src/main/scala/millfork/compiler/mos/MosExpressionCompiler.scala b/src/main/scala/millfork/compiler/mos/MosExpressionCompiler.scala index 3db1d849..9d4a09c5 100644 --- a/src/main/scala/millfork/compiler/mos/MosExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/mos/MosExpressionCompiler.scala @@ -1,6 +1,7 @@ package millfork.compiler.mos import millfork.CompilationFlag +import millfork.assembly.Elidability import millfork.assembly.mos.AddrMode._ import millfork.assembly.mos.Opcode._ import millfork.assembly.mos._ @@ -38,23 +39,24 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] { AssemblyLine(LDA, Immediate, expr.hiByte), AssemblyLine(LDY, Immediate, expr.loByte)) case m: VariableInMemory => + val elidability = if (m.isVolatile) Elidability.Volatile else Elidability.Elidable val addrMode = if (m.zeropage) ZeroPage else Absolute val addr = m.toAddress m.typ.size match { case 0 => Nil case 1 => List( AssemblyLine(LDA, Immediate, expr.loByte), - AssemblyLine(STA, addrMode, addr)) + AssemblyLine(STA, addrMode, addr, elidability = elidability)) case 2 => if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) { AssemblyLine.accu16 :: AssemblyLine(LDA_W, WordImmediate, expr) ::(AssemblyLine.variable(ctx, STA_W, m) :+ AssemblyLine.accu8) } else List( AssemblyLine(LDA, Immediate, expr.loByte), - AssemblyLine(STA, addrMode, addr), + AssemblyLine(STA, addrMode, addr, elidability = elidability), AssemblyLine(LDA, Immediate, expr.hiByte), - AssemblyLine(STA, addrMode, addr + 1)) + AssemblyLine(STA, addrMode, addr + 1, elidability = elidability)) case s => List.tabulate(s)(i => List( AssemblyLine(LDA, Immediate, expr.subbyte(i)), - AssemblyLine(STA, addrMode, addr + i))).flatten + AssemblyLine(STA, addrMode, addr + i, elidability = elidability))).flatten } case m@StackVariable(_, t, offset) => t.size match { diff --git a/src/test/scala/millfork/test/VolatileSuite.scala b/src/test/scala/millfork/test/VolatileSuite.scala new file mode 100644 index 00000000..2bef73b3 --- /dev/null +++ b/src/test/scala/millfork/test/VolatileSuite.scala @@ -0,0 +1,37 @@ +package millfork.test + +import millfork.Cpu +import millfork.test.emu.{EmuBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuSuperOptimizedRun, EmuUltraBenchmarkRun} +import org.scalatest.{FunSuite, Matchers} + +/** + * @author Karol Stasiak + */ +class VolatileSuite extends FunSuite with Matchers { + + test("Basic volatile test") { + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)( + """ + | word addr @$c000 + | volatile byte output @$c0ea + | byte thing + | void main () { + | f(55) + | addr = f.addr + | } + | noinline void f(byte x) { + | output = 5 + | thing = x + | output = x + | } + """.stripMargin) { m => + m.readByte(0xc0ea) should equal(55) + var f = m.readWord(0xc000) + var count = 0 + do { + if (m.readWord(f) == 0xc0ea) count +=1 + f += 1 + } while (count < 2) + } + } +}