@@ -142,6 +142,11 @@ class Z80MachineLateOptimization : public MachineFunctionPass {
142142 // after SBC A,A: A is a member of {0, 0xFF} and Z = (A == 0), so OR A,A is redundant
143143 bool ZFlagReflectsAZero = false ;
144144
145+ // track redundant A<->reg copies, after "ld X, a", X mirrors A
146+ // if A hasnt changed when we see "ld a, X", we can eliminate the copy
147+ // AMirroredInReg = X means the value currently in A is also in X
148+ MCRegister AMirroredInReg = Z80 ::NoRegister;
149+
145150 template <typename ... Args> void assign (MCRegister Reg, Args &&...args) {
146151 RegVals[Reg] = RegVal (std::forward<Args>(args)..., Reg, *TRI );
147152 }
@@ -452,6 +457,7 @@ bool Z80MachineLateOptimization::runOnMachineFunction(MachineFunction &MF) {
452457 LiveUnits.addLiveIns (MBB );
453458 clobberAll ();
454459 ZFlagReflectsAZero = false ;
460+ AMirroredInReg = Z80 ::NoRegister;
455461 for (MachineBasicBlock::iterator I = MBB .begin (), E = MBB .end (); I != E;) {
456462 MachineInstrBuilder MIB (MF , I);
457463 LiveUnits.stepForward (*I);
@@ -624,6 +630,45 @@ bool Z80MachineLateOptimization::runOnMachineFunction(MachineFunction &MF) {
624630 continue ;
625631 }
626632 break ;
633+ // redundant copy elimination: track COPY from A and eliminate COPY back if A unchanged
634+ // this runs before pseudo expansion, so we see COPY, not LD8gg/xx/yy
635+ case TargetOpcode::COPY : {
636+ MCRegister CopyDst = MIB ->getOperand (0 ).getReg ();
637+ MCRegister CopySrc = MIB ->getOperand (1 ).getReg ();
638+ // only handle 8-bit A-related copies
639+ if (!Z80 ::R8RegClass.contains (CopyDst) || !Z80 ::R8RegClass.contains (CopySrc))
640+ break ;
641+ // if copying TO A from the register that mirrors A, the copy is redundant
642+ if (CopyDst == Z80 ::A && CopySrc == AMirroredInReg) {
643+ LLVM_DEBUG (dbgs () << " Erasing redundant copy to A from "
644+ << TRI ->getName (CopySrc) << " (A mirrors "
645+ << TRI ->getName (AMirroredInReg) << " ): " ;
646+ MIB ->dump ());
647+ MIB ->eraseFromParent ();
648+ Changed = true ;
649+ continue ;
650+ }
651+ // if copying FROM A to X, record that X now mirrors A
652+ if (CopySrc == Z80 ::A) {
653+ AMirroredInReg = CopyDst;
654+ }
655+ break ;
656+ }
657+ // 8 bit ALU pseudos: ADD8_gisel, SUB8_gisel, etc. have an explicit 8 bit def
658+ // and implicit def A (because Z80 ALU ops always write to A)
659+ // after these instructions, A and the dest register hold the same value
660+ case Z80 ::ADD8_gisel:
661+ case Z80 ::SUB8_gisel:
662+ case Z80 ::AND8_gisel:
663+ case Z80 ::OR8_gisel:
664+ case Z80 ::XOR8_gisel: {
665+ MCRegister ExplicitDst = MIB ->getOperand (0 ).getReg ();
666+ // after ALU pseudo, explicit dest and A have the same value if dest is not A itself, record that dest mirrors A
667+ if (ExplicitDst != Z80 ::A) {
668+ AMirroredInReg = ExplicitDst;
669+ }
670+ break ;
671+ }
627672 case Z80 ::Sub16ao:
628673 case Z80 ::Sub24ao:
629674 case Z80 ::Cmp16ao:
@@ -734,16 +779,46 @@ bool Z80MachineLateOptimization::runOnMachineFunction(MachineFunction &MF) {
734779 }
735780 }
736781
737- // after SBC A,A, Z flag correctly reflects (A == 0)
738- // track this so we can eliminate redundant OR A,A
782+ // SBC A,A sets Z=(A==0). INC/DEC A preserve this. Track to eliminate redundant OR A,A
739783 unsigned Opc = MIB ->getOpcode ();
740784 if (Opc == Z80 ::SBC8ar && MIB ->getOperand (0 ).getReg () == Z80 ::A) {
741785 ZFlagReflectsAZero = true ;
786+ } else if (ZFlagReflectsAZero &&
787+ (Opc == Z80 ::INC8r || Opc == Z80 ::DEC8r) &&
788+ MIB ->getOperand (0 ).getReg () == Z80 ::A) {
789+ // INC/DEC A preserves Z=(A==0)
742790 } else if (ClobberedA || ClobberedF) {
743- // if A or F is modified by something other than SBC A,A, reset tracking
744791 ZFlagReflectsAZero = false ;
745792 }
746793
794+ // invalidate A<->reg mirroring if A or mirrored reg is clobbered (except by tracked ops)
795+ if (ClobberedA) {
796+ bool isCopyFromA = Opc == TargetOpcode::COPY &&
797+ MIB ->getOperand (1 ).getReg () == Z80 ::A;
798+ bool isALUPseudo = (Opc == Z80 ::ADD8_gisel || Opc == Z80 ::SUB8_gisel ||
799+ Opc == Z80 ::AND8_gisel || Opc == Z80 ::OR8_gisel ||
800+ Opc == Z80 ::XOR8_gisel);
801+ if (!isCopyFromA && !isALUPseudo)
802+ AMirroredInReg = Z80 ::NoRegister;
803+ }
804+ if (AMirroredInReg != Z80 ::NoRegister) {
805+ bool isALUPseudoOrCopy = (Opc == Z80 ::ADD8_gisel || Opc == Z80 ::SUB8_gisel ||
806+ Opc == Z80 ::AND8_gisel || Opc == Z80 ::OR8_gisel ||
807+ Opc == Z80 ::XOR8_gisel || Opc == TargetOpcode::COPY );
808+ if (!isALUPseudoOrCopy) {
809+ for (MachineOperand &MO : MIB ->operands ()) {
810+ if (MO .isReg () && MO .isDef ()) {
811+ for (MCRegAliasIterator AI (MO .getReg (), TRI , true ); AI .isValid (); ++AI ) {
812+ if (*AI == AMirroredInReg) {
813+ AMirroredInReg = Z80 ::NoRegister;
814+ break ;
815+ }
816+ }
817+ }
818+ }
819+ }
820+ }
821+
747822 // Apply KnownFlags after clobbering defs.
748823 if (KnownFlags)
749824 assign (Z80 ::F, KnownFlags.getKnownVal (KnownFlagsVal),
0 commit comments