diff --git a/CHANGELOG.md b/CHANGELOG.md index 14b21987..746c10a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,8 @@ * Fixed for-each loops with non-constant arrays. +* 8080 and LR35902: Fixed inlining of byte-sized variables into registers. + * 8080 and LR35902: fixed large stack variables. * Other bug fixes. diff --git a/src/main/scala/millfork/assembly/z80/opt/ByteVariableToRegisterOptimization.scala b/src/main/scala/millfork/assembly/z80/opt/ByteVariableToRegisterOptimization.scala index 7dbf8bce..e20279f7 100644 --- a/src/main/scala/millfork/assembly/z80/opt/ByteVariableToRegisterOptimization.scala +++ b/src/main/scala/millfork/assembly/z80/opt/ByteVariableToRegisterOptimization.scala @@ -270,6 +270,25 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] { if (target == D || target == E) fail(61) else canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe = Some(true), xs).map(add(CyclesAndBytes(10, 3))) + // TODO: other combinations: + case (_, ZLine0(LD, TwoRegisters(L, C), _)) :: (_, ZLine0(LD, TwoRegisters(H, B), _)) :: xs if addressInBc.contains(true) => + canBeInlined(vname, synced, target, addressInHl = Some(true), addressInBc, addressInDe, xs).map(add(CyclesAndBytes(8, 2))) + case (_, ZLine0(LD, TwoRegisters(H, B), _)) :: (_, ZLine0(LD, TwoRegisters(L, C), _)) :: xs if addressInBc.contains(true) => + canBeInlined(vname, synced, target, addressInHl = Some(true), addressInBc, addressInDe, xs).map(add(CyclesAndBytes(8, 2))) + case (_, ZLine0(LD, TwoRegisters(L, E), _)) :: (_, ZLine0(LD, TwoRegisters(H, D), _)) :: xs if addressInDe.contains(true) => + canBeInlined(vname, synced, target, addressInHl = Some(true), addressInBc, addressInDe, xs).map(add(CyclesAndBytes(8, 2))) + case (_, ZLine0(LD, TwoRegisters(H, D), _)) :: (_, ZLine0(LD, TwoRegisters(L, E), _)) :: xs if addressInDe.contains(true) => + canBeInlined(vname, synced, target, addressInHl = Some(true), addressInBc, addressInDe, xs).map(add(CyclesAndBytes(8, 2))) + + case (_, ZLine0(LD, TwoRegisters(C, L), _)) :: (_, ZLine0(LD, TwoRegisters(B, H), _)) :: xs if addressInHl.contains(true) => + canBeInlined(vname, synced, target, addressInHl, addressInBc = Some(true), addressInDe, xs).map(add(CyclesAndBytes(8, 2))) + case (_, ZLine0(LD, TwoRegisters(B, H), _)) :: (_, ZLine0(LD, TwoRegisters(C, L), _)) :: xs if addressInHl.contains(true) => + canBeInlined(vname, synced, target, addressInHl, addressInBc = Some(true), addressInDe, xs).map(add(CyclesAndBytes(8, 2))) + case (_, ZLine0(LD, TwoRegisters(E, L), _)) :: (_, ZLine0(LD, TwoRegisters(D, H), _)) :: xs if addressInHl.contains(true) => + canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe = Some(true), xs).map(add(CyclesAndBytes(8, 2))) + case (_, ZLine0(LD, TwoRegisters(D, H), _)) :: (_, ZLine0(LD, TwoRegisters(E, L), _)) :: xs if addressInHl.contains(true) => + canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe = Some(true), xs).map(add(CyclesAndBytes(8, 2))) + case (_, ZLine0(_, OneRegister(MEM_HL), _)) :: xs => addressInHl match { case Some(true) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs).map(add(CyclesAndBytes(3, 0))) case Some(false) => canBeInlined(vname, synced, target, addressInHl, addressInBc, addressInDe, xs) @@ -388,15 +407,34 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] { x.copy(registers = TwoRegisters(reg, target), parameter = p) :: inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs) + // TODO: other combinations + case ZLine0(LD, TwoRegisters(L, C), _) :: ZLine0(LD, TwoRegisters(H, B), _) :: xs if addressInBc => + inlineVars(vname, target, addressInHl = true, addressInBc = true, addressInDe = addressInDe, xs) + case ZLine0(LD, TwoRegisters(H, B), _) :: ZLine0(LD, TwoRegisters(L, C), _) :: xs if addressInBc => + inlineVars(vname, target, addressInHl = true, addressInBc = true, addressInDe = addressInDe, xs) + case ZLine0(LD, TwoRegisters(L, E), _) :: ZLine0(LD, TwoRegisters(H, D), _) :: xs if addressInDe => + inlineVars(vname, target, addressInHl = true, addressInBc = addressInBc, addressInDe = true, xs) + case ZLine0(LD, TwoRegisters(H, D), _) :: ZLine0(LD, TwoRegisters(L, E), _) :: xs if addressInDe => + inlineVars(vname, target, addressInHl = true, addressInBc = addressInBc, addressInDe = true, xs) + + case ZLine0(LD, TwoRegisters(C, L), _) :: ZLine0(LD, TwoRegisters(B, H), _) :: xs if addressInHl => + inlineVars(vname, target, addressInHl = true, addressInBc = true, addressInDe = addressInDe, xs) + case ZLine0(LD, TwoRegisters(B, H), _) :: ZLine0(LD, TwoRegisters(C, L), _) :: xs if addressInHl => + inlineVars(vname, target, addressInHl = true, addressInBc = true, addressInDe = addressInDe, xs) + case ZLine0(LD, TwoRegisters(E, L), _) :: ZLine0(LD, TwoRegisters(D, H), _) :: xs if addressInHl => + inlineVars(vname, target, addressInHl = true, addressInBc = addressInBc, addressInDe = true, xs) + case ZLine0(LD, TwoRegisters(D, H), _) :: ZLine0(LD, TwoRegisters(E, L), _) :: xs if addressInHl => + inlineVars(vname, target, addressInHl = true, addressInBc = addressInBc, addressInDe = true, xs) + case (x@ZLine(CALL,_,_,_,s))::xs => // TODO: this push/pull pair shouldn't prevent the inlining to the other register in the pair target match { case ZRegister.B | ZRegister.C => ZLine.register(PUSH, BC).pos(s) :: x :: ZLine.register(POP, BC).pos(s) :: - inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs) + inlineVars(vname, target, addressInHl = false, addressInBc, addressInDe = false, xs) case ZRegister.D | ZRegister.E => ZLine.register(PUSH, DE).pos(s) :: x :: ZLine.register(POP, DE).pos(s) :: - inlineVars(vname, target, addressInHl, addressInBc, addressInDe, xs) + inlineVars(vname, target, addressInHl = false, addressInBc = false, addressInDe, xs) } case x :: xs if x.changesRegister(HL) => diff --git a/src/main/scala/millfork/assembly/z80/opt/VariableLifetime.scala b/src/main/scala/millfork/assembly/z80/opt/VariableLifetime.scala index eb8849e4..bb336b13 100644 --- a/src/main/scala/millfork/assembly/z80/opt/VariableLifetime.scala +++ b/src/main/scala/millfork/assembly/z80/opt/VariableLifetime.scala @@ -1,7 +1,7 @@ package millfork.assembly.z80.opt import millfork.assembly.opt.SingleStatus -import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine, ZLine0} +import millfork.assembly.z80.{OneRegister, TwoRegisters, ZLine, ZLine0, ZOpcode} import millfork.env._ import millfork.error.ConsoleLogger import millfork.node.ZRegister @@ -14,17 +14,38 @@ object VariableLifetime { // This only works for non-stack variables. // TODO: this is also probably very wrong def apply(variableName: String, codeWithFlow: List[(FlowInfo, ZLine)]): Range = { + import ZRegister._ + import ZOpcode._ + + val pointerLoadedAt = codeWithFlow.zipWithIndex.filter{ + case ((_, ZLine0(ZOpcode.LD_16, TwoRegisters(_, IMM_16), MemoryAddressConstant(MemoryVariable(n, _, _)))), _) => n == variableName + case _ => false + }.map(_._2) + val pointerReadAt = codeWithFlow.zipWithIndex.filter{ + case ((_, ZLine0(_, TwoRegisters(MEM_HL | MEM_DE | MEM_BC, _), _)), _) => true + case ((_, ZLine0(_, TwoRegisters(_, MEM_HL | MEM_DE | MEM_BC), _)), _) => true + case ((_, ZLine0(_, OneRegister(MEM_HL | MEM_DE | MEM_BC), _)), _) => true + case ((_, ZLine0(CALL, _, _)), _) => true + case _ => false + }.map(_._2) + + val flags = codeWithFlow.map { case (_, ZLine0(_, _, MemoryAddressConstant(MemoryVariable(n, _, _)))) => n == variableName case (_, ZLine0(_, _, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1)))) => n == variableName - case (i, ZLine0(_, TwoRegisters(ZRegister.MEM_HL, _) | TwoRegisters(_, ZRegister.MEM_HL) | OneRegister(ZRegister.MEM_HL), _)) => + case (i, ZLine0(_, TwoRegisters(MEM_HL, _) | TwoRegisters(_, MEM_HL) | OneRegister(MEM_HL), _)) => i.statusBefore.hl match { case SingleStatus(MemoryAddressConstant(MemoryVariable(n, _, _))) => n == variableName case SingleStatus(CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1))) => n == variableName case _ => false } case _ => false + }.toArray + + if(pointerLoadedAt.nonEmpty) { + pointerReadAt.foreach(i => flags(i) = true) } + if (flags.forall(!_)) return Range(0, 0) var min = flags.indexOf(true) var max = flags.lastIndexOf(true) + 1