diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp index 15d2e10aa0f08..a301f9f9f122c 100644 --- a/llvm/lib/Target/X86/X86InstrInfo.cpp +++ b/llvm/lib/Target/X86/X86InstrInfo.cpp @@ -1031,7 +1031,8 @@ static bool findRedundantFlagInstr(MachineInstr &CmpInstr, MachineInstr &CmpValDefInstr, const MachineRegisterInfo *MRI, MachineInstr **AndInstr, const TargetRegisterInfo *TRI, const X86Subtarget &ST, - bool &NoSignFlag, bool &ClearsOverflowFlag) { + bool &NoSignFlag, bool &ClearsOverflowFlag, + bool &ClearsCarryFlag) { if (!(CmpValDefInstr.getOpcode() == X86::SUBREG_TO_REG && CmpInstr.getOpcode() == X86::TEST64rr) && !(CmpValDefInstr.getOpcode() == X86::COPY && @@ -1136,6 +1137,7 @@ findRedundantFlagInstr(MachineInstr &CmpInstr, MachineInstr &CmpValDefInstr, NoSignFlag = true; // ClearsOverflowFlag is true for AND operation (no surprise). ClearsOverflowFlag = true; + ClearsCarryFlag = true; return true; } return false; @@ -4989,9 +4991,11 @@ bool X86InstrInfo::isRedundantFlagInstr(const MachineInstr &FlagI, /// Check whether the definition can be converted /// to remove a comparison against zero. inline static bool isDefConvertible(const MachineInstr &MI, bool &NoSignFlag, - bool &ClearsOverflowFlag) { + bool &ClearsOverflowFlag, + bool &ClearsCarryFlag) { NoSignFlag = false; ClearsOverflowFlag = false; + ClearsCarryFlag = false; // "ELF Handling for Thread-Local Storage" specifies that x86-64 GOTTPOFF, and // i386 GOTNTPOFF/INDNTPOFF relocations can convert an ADD to a LEA during @@ -5155,6 +5159,9 @@ inline static bool isDefConvertible(const MachineInstr &MI, bool &NoSignFlag, CASE_ND(OR32rm) CASE_ND(OR16rm) CASE_ND(OR8rm) + ClearsOverflowFlag = true; + ClearsCarryFlag = true; + return true; case X86::ANDN32rr: case X86::ANDN32rm: case X86::ANDN64rr: @@ -5348,6 +5355,7 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, SmallVector, 4> InstsToUpdate; bool NoSignFlag = false; bool ClearsOverflowFlag = false; + bool ClearsCarryFlag = false; bool ShouldUpdateCC = false; bool IsSwapped = false; bool HasNF = Subtarget.hasNF(); @@ -5368,7 +5376,8 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, // testl %eax, %eax // <-- can be removed if (&Inst == SrcRegDef) { if (IsCmpZero && - isDefConvertible(Inst, NoSignFlag, ClearsOverflowFlag)) { + isDefConvertible(Inst, NoSignFlag, ClearsOverflowFlag, + ClearsCarryFlag)) { MI = &Inst; break; } @@ -5389,7 +5398,8 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, MachineInstr *AndInstr = nullptr; if (IsCmpZero && findRedundantFlagInstr(CmpInstr, Inst, MRI, &AndInstr, TRI, - Subtarget, NoSignFlag, ClearsOverflowFlag)) { + Subtarget, NoSignFlag, ClearsOverflowFlag, + ClearsCarryFlag)) { assert(AndInstr != nullptr && X86::isAND(AndInstr->getOpcode())); MI = AndInstr; break; @@ -5504,8 +5514,10 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, case X86::COND_AE: case X86::COND_B: case X86::COND_BE: - // CF is used, we can't perform this optimization. - return false; + // If CF is used, the instruction needs to clear it like CmpZero does. + if (!ClearsCarryFlag) + return false; + break; case X86::COND_G: case X86::COND_GE: case X86::COND_L: diff --git a/llvm/test/CodeGen/X86/optimize-compare.mir b/llvm/test/CodeGen/X86/optimize-compare.mir index 36ab851a85153..165ca406df6c5 100644 --- a/llvm/test/CodeGen/X86/optimize-compare.mir +++ b/llvm/test/CodeGen/X86/optimize-compare.mir @@ -16,6 +16,53 @@ body: | $al = SETCCr 5, implicit $eflags ... --- +name: opt_zerocmp_logical_carry_cc +body: | + bb.0: + ; CHECK-LABEL: name: opt_zerocmp_logical_carry_cc + ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $esi + ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr32 = COPY $edi + ; CHECK-NEXT: [[OR32rr:%[0-9]+]]:gr32 = OR32rr [[COPY]], [[COPY1]], implicit-def $eflags + ; CHECK-NEXT: $al = SETCCr 7, implicit $eflags + %0:gr32 = COPY $esi + %1:gr32 = COPY $edi + %2:gr32 = OR32rr %0, %1, implicit-def dead $eflags + ; TEST should be removed. OR clears CF like TEST and sets the same ZF. + TEST32rr %2, %2, implicit-def $eflags + $al = SETCCr 7, implicit $eflags +... +--- +name: opt_zerocmp_logical_carry_cc_subreg +body: | + bb.0: + ; CHECK-LABEL: name: opt_zerocmp_logical_carry_cc_subreg + ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $esi + ; CHECK-NEXT: [[AND32ri:%[0-9]+]]:gr32 = AND32ri [[COPY]], i32 255, implicit-def $eflags + ; CHECK-NEXT: [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG [[AND32ri]], %subreg.sub_32bit + ; CHECK-NEXT: $al = SETCCr 7, implicit $eflags + %0:gr32 = COPY $esi + %1:gr32 = AND32ri %0, i32 255, implicit-def dead $eflags + %2:gr64 = SUBREG_TO_REG %1, %subreg.sub_32bit + ; TEST should be removed. AND clears CF like TEST and sets the same ZF. + TEST64rr %2, %2, implicit-def $eflags + $al = SETCCr 7, implicit $eflags +... +--- +name: opt_zerocmp_arith_carry_cc_noopt +body: | + bb.0: + ; CHECK-LABEL: name: opt_zerocmp_arith_carry_cc_noopt + ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $esi + ; CHECK-NEXT: [[ADD32ri:%[0-9]+]]:gr32 = ADD32ri [[COPY]], i32 1, implicit-def dead $eflags + ; CHECK-NEXT: TEST32rr [[ADD32ri]], [[ADD32ri]], implicit-def $eflags + ; CHECK-NEXT: $al = SETCCr 7, implicit $eflags + %0:gr32 = COPY $esi + %1:gr32 = ADD32ri %0, i32 1, implicit-def dead $eflags + ; TEST should not be removed. ADD does not clear CF like TEST. + TEST32rr %1, %1, implicit-def $eflags + $al = SETCCr 7, implicit $eflags +... +--- name: opt_zerocmp_1 body: | bb.0: