diff --git a/llvm/lib/Target/X86/X86InstrInfo.h b/llvm/lib/Target/X86/X86InstrInfo.h --- a/llvm/lib/Target/X86/X86InstrInfo.h +++ b/llvm/lib/Target/X86/X86InstrInfo.h @@ -629,6 +629,16 @@ unsigned &SrcOpIdx1, unsigned &SrcOpIdx2, bool IsIntrinsic = false) const; + + /// Check whether the first instruction, whose only + /// purpose is to update flags, can be made redundant. + /// CMPrr can be made redundant by SUBrr if the operands are the same. + /// This function can be extended later on. + /// SrcReg, SrcRegs: register operands for FlagI. + /// ImmValue: immediate for FlagI if it takes an immediate. + bool isRedundantFlagInstr(const MachineInstr &FlagI, Register SrcReg, + Register SrcReg2, int64_t ImmMask, int64_t ImmValue, + const MachineInstr &OI, bool *IsSwapped) const; }; } // namespace llvm diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp --- a/llvm/lib/Target/X86/X86InstrInfo.cpp +++ b/llvm/lib/Target/X86/X86InstrInfo.cpp @@ -4016,42 +4016,72 @@ return false; } -/// Check whether the first instruction, whose only -/// purpose is to update flags, can be made redundant. -/// CMPrr can be made redundant by SUBrr if the operands are the same. -/// This function can be extended later on. -/// SrcReg, SrcRegs: register operands for FlagI. -/// ImmValue: immediate for FlagI if it takes an immediate. -inline static bool isRedundantFlagInstr(const MachineInstr &FlagI, +bool X86InstrInfo::isRedundantFlagInstr(const MachineInstr &FlagI, Register SrcReg, Register SrcReg2, int64_t ImmMask, int64_t ImmValue, - const MachineInstr &OI) { - if (((FlagI.getOpcode() == X86::CMP64rr && OI.getOpcode() == X86::SUB64rr) || - (FlagI.getOpcode() == X86::CMP32rr && OI.getOpcode() == X86::SUB32rr) || - (FlagI.getOpcode() == X86::CMP16rr && OI.getOpcode() == X86::SUB16rr) || - (FlagI.getOpcode() == X86::CMP8rr && OI.getOpcode() == X86::SUB8rr)) && - ((OI.getOperand(1).getReg() == SrcReg && - OI.getOperand(2).getReg() == SrcReg2) || - (OI.getOperand(1).getReg() == SrcReg2 && - OI.getOperand(2).getReg() == SrcReg))) - return true; - - if (ImmMask != 0 && - ((FlagI.getOpcode() == X86::CMP64ri32 && - OI.getOpcode() == X86::SUB64ri32) || - (FlagI.getOpcode() == X86::CMP64ri8 && - OI.getOpcode() == X86::SUB64ri8) || - (FlagI.getOpcode() == X86::CMP32ri && OI.getOpcode() == X86::SUB32ri) || - (FlagI.getOpcode() == X86::CMP32ri8 && - OI.getOpcode() == X86::SUB32ri8) || - (FlagI.getOpcode() == X86::CMP16ri && OI.getOpcode() == X86::SUB16ri) || - (FlagI.getOpcode() == X86::CMP16ri8 && - OI.getOpcode() == X86::SUB16ri8) || - (FlagI.getOpcode() == X86::CMP8ri && OI.getOpcode() == X86::SUB8ri)) && - OI.getOperand(1).getReg() == SrcReg && - OI.getOperand(2).getImm() == ImmValue) - return true; - return false; + const MachineInstr &OI, + bool *IsSwapped) const { + switch (OI.getOpcode()) { + case X86::CMP64rr: + case X86::CMP32rr: + case X86::CMP16rr: + case X86::CMP8rr: + case X86::SUB64rr: + case X86::SUB32rr: + case X86::SUB16rr: + case X86::SUB8rr: { + Register OISrcReg; + Register OISrcReg2; + int64_t OIMask; + int64_t OIValue; + if (!analyzeCompare(OI, OISrcReg, OISrcReg2, OIMask, OIValue) || + OIMask != ImmMask || OIValue != ImmValue) + return false; + if (SrcReg == OISrcReg && SrcReg2 == OISrcReg2) { + *IsSwapped = false; + return true; + } + if (SrcReg == OISrcReg2 && SrcReg2 == OISrcReg) { + *IsSwapped = true; + return true; + } + return false; + } + case X86::CMP64ri32: + case X86::CMP64ri8: + case X86::CMP32ri: + case X86::CMP32ri8: + case X86::CMP16ri: + case X86::CMP16ri8: + case X86::CMP8ri: + case X86::SUB64ri32: + case X86::SUB64ri8: + case X86::SUB32ri: + case X86::SUB32ri8: + case X86::SUB16ri: + case X86::SUB16ri8: + case X86::SUB8ri: + case X86::TEST64rr: + case X86::TEST32rr: + case X86::TEST16rr: + case X86::TEST8rr: { + if (ImmMask != 0) { + Register OISrcReg; + Register OISrcReg2; + int64_t OIMask; + int64_t OIValue; + if (analyzeCompare(OI, OISrcReg, OISrcReg2, OIMask, OIValue) && + SrcReg == OISrcReg && ImmMask == OIMask && OIValue == ImmValue) { + assert(SrcReg2 == X86::NoRegister && OISrcReg2 == X86::NoRegister && + "should not have 2nd register"); + return true; + } + } + return FlagI.isIdenticalTo(OI); + } + default: + return false; + } } /// Check whether the definition can be converted @@ -4286,6 +4316,7 @@ bool NoSignFlag = false; bool ClearsOverflowFlag = false; bool ShouldUpdateCC = false; + bool IsSwapped = false; X86::CondCode NewCC = X86::COND_INVALID; // Search backward from CmpInstr for the next instruction defining EFLAGS. @@ -4293,6 +4324,8 @@ MachineBasicBlock &CmpMBB = *CmpInstr.getParent(); MachineBasicBlock::reverse_iterator From = std::next(MachineBasicBlock::reverse_iterator(CmpInstr)); + if (SrcReg2.isPhysical()) + return false; MachineInstr *SrcRegDef = MRI->getVRegDef(SrcReg); assert(SrcRegDef && "Must have a definition (SSA)"); for (MachineBasicBlock *MBB = &CmpMBB;;) { @@ -4334,8 +4367,8 @@ // sub x, y or cmp x, y // ... // EFLAGS not changed // cmp x, y // <-- can be removed - if (!IsCmpZero && isRedundantFlagInstr(CmpInstr, SrcReg, SrcReg2, - CmpMask, CmpValue, Inst)) { + if (isRedundantFlagInstr(CmpInstr, SrcReg, SrcReg2, CmpMask, CmpValue, + Inst, &IsSwapped)) { Sub = &Inst; break; } @@ -4365,10 +4398,6 @@ From = MBB->rbegin(); } - bool IsSwapped = - (SrcReg2 != 0 && Sub && Sub->getOperand(1).getReg() == SrcReg2 && - Sub->getOperand(2).getReg() == SrcReg); - // Scan forward from the instruction after CmpInstr for uses of EFLAGS. // It is safe to remove CmpInstr if EFLAGS is redefined or killed. // If we are done with the basic block, we need to check whether EFLAGS is @@ -4391,7 +4420,7 @@ // EFLAGS is used by this instruction. X86::CondCode OldCC = X86::COND_INVALID; - if (IsCmpZero || IsSwapped) { + if (MI || IsSwapped) { // We decode the condition code from opcode. if (Instr.isBranch()) OldCC = X86::getCondFromBranch(Instr); @@ -4403,7 +4432,7 @@ if (OldCC == X86::COND_INVALID) return false; } X86::CondCode ReplacementCC = X86::COND_INVALID; - if (IsCmpZero) { + if (MI) { switch (OldCC) { default: break; case X86::COND_A: case X86::COND_AE: @@ -4461,14 +4490,15 @@ // If EFLAGS is not killed nor re-defined, we should check whether it is // live-out. If it is live-out, do not optimize. - if ((IsCmpZero || IsSwapped) && !IsSafe) { + if ((MI || IsSwapped) && !IsSafe) { for (MachineBasicBlock *Successor : CmpMBB.successors()) if (Successor->isLiveIn(X86::EFLAGS)) return false; } // The instruction to be updated is either Sub or MI. - Sub = IsCmpZero ? MI : Sub; + assert(MI == nullptr || Sub == nullptr); + Sub = MI != nullptr ? MI : Sub; MachineBasicBlock *SubBB = Sub->getParent(); // Move Movr0Inst to the appropriate place before Sub. if (Movr0Inst) { diff --git a/llvm/test/CodeGen/X86/2007-02-16-BranchFold.ll b/llvm/test/CodeGen/X86/2007-02-16-BranchFold.ll --- a/llvm/test/CodeGen/X86/2007-02-16-BranchFold.ll +++ b/llvm/test/CodeGen/X86/2007-02-16-BranchFold.ll @@ -67,7 +67,6 @@ ; CHECK-NEXT: LBB0_6: ## %NodeBlock ; CHECK-NEXT: js LBB0_9 ; CHECK-NEXT: ## %bb.7: ## %LeafBlock1 -; CHECK-NEXT: testl %eax, %eax ; CHECK-NEXT: jne LBB0_3 ; CHECK-NEXT: ## %bb.8: ## %bb12.i.i935.exitStub ; CHECK-NEXT: movl %edi, (%esi) diff --git a/llvm/test/CodeGen/X86/optimize-compare.mir b/llvm/test/CodeGen/X86/optimize-compare.mir --- a/llvm/test/CodeGen/X86/optimize-compare.mir +++ b/llvm/test/CodeGen/X86/optimize-compare.mir @@ -203,3 +203,179 @@ CMP32rr %0, %1, implicit-def $eflags $bl = SETCCr 2, implicit $eflags ... +--- +name: opt_redundant_flags_cmp_cmp +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_cmp_cmp + ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $esi + ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr32 = COPY $edi + ; CHECK-NEXT: CMP32rr [[COPY]], [[COPY1]], implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 2, implicit $eflags + ; CHECK-NEXT: $bl = SETCCr 7, implicit $eflags + %0:gr32 = COPY $esi + %1:gr32 = COPY $edi + CMP32rr %0, %1, implicit-def $eflags + $cl = SETCCr 2, implicit $eflags + ; 2nd CMP should be removed. + CMP32rr %1, %0, implicit-def $eflags + $bl = SETCCr 2, implicit $eflags +... +--- +name: opt_redundant_flags_cmp_cmp_2 +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_cmp_cmp_2 + ; CHECK: [[COPY:%[0-9]+]]:gr64 = COPY $rsi + ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr64 = COPY $rdi + ; CHECK-NEXT: CMP64ri8 [[COPY]], 15, implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 2, implicit $eflags + ; CHECK-NEXT: $bl = SETCCr 2, implicit $eflags + %0:gr64 = COPY $rsi + %1:gr64 = COPY $rdi + CMP64ri8 %0, 15, implicit-def $eflags + $cl = SETCCr 2, implicit $eflags + ; 2nd CMP should be removed. + CMP64ri8 %0, 15, implicit-def $eflags + $bl = SETCCr 2, implicit $eflags +... +--- +name: opt_redundant_flags_test_test +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_test_test + ; CHECK: [[COPY:%[0-9]+]]:gr16 = COPY $ax + ; CHECK-NEXT: TEST16rr [[COPY]], [[COPY]], implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 2, implicit $eflags + ; CHECK-NEXT: $bl = SETCCr 2, implicit $eflags + %0:gr16 = COPY $ax + TEST16rr %0, %0, implicit-def $eflags + $cl = SETCCr 2, implicit $eflags + ; 2nd CMP should be removed. + TEST16rr %0, %0, implicit-def $eflags + $bl = SETCCr 2, implicit $eflags +... +--- +name: opt_redundant_flags_cmp_sub +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_cmp_sub + ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $esi + ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr32 = COPY $edi + ; CHECK-NEXT: CMP32rr [[COPY]], [[COPY1]], implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 2, implicit $eflags + ; CHECK-NEXT: $bl = SETCCr 7, implicit $eflags + %0:gr32 = COPY $esi + %1:gr32 = COPY $edi + CMP32rr %0, %1, implicit-def $eflags + $cl = SETCCr 2, implicit $eflags + ; SUB should be removed. + dead %2:gr32 = SUB32rr %1, %0, implicit-def $eflags + $bl = SETCCr 2, implicit $eflags +... +--- +name: opt_redundant_flags_cmp_sub_2 +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_cmp_sub_2 + ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $esi + ; CHECK-NEXT: CMP32ri [[COPY]], -12345, implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 2, implicit $eflags + ; CHECK-NEXT: $bl = SETCCr 2, implicit $eflags + %0:gr32 = COPY $esi + CMP32ri %0, -12345, implicit-def $eflags + $cl = SETCCr 2, implicit $eflags + ; SUB should be removed + dead %2:gr32 = SUB32ri %0, -12345, implicit-def $eflags + $bl = SETCCr 2, implicit $eflags +... +--- +name: opt_redundant_flags_cmp_sub_noopt +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_cmp_sub_noopt + ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $esi + ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr32 = COPY $edi + ; CHECK-NEXT: CMP32rr [[COPY]], [[COPY1]], implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 2, implicit $eflags + ; CHECK-NEXT: [[SUB32rr:%[0-9]+]]:gr32 = SUB32rr [[COPY]], [[COPY1]], implicit-def $eflags + ; CHECK-NEXT: $rdx = COPY [[SUB32rr]] + ; CHECK-NEXT: $bl = SETCCr 2, implicit $eflags + %0:gr32 = COPY $esi + %1:gr32 = COPY $edi + CMP32rr %0, %1, implicit-def $eflags + $cl = SETCCr 2, implicit $eflags + ; cannot optimize the SUB because the result value is used. + %2:gr32 = SUB32rr %0, %1, implicit-def $eflags + $rdx = COPY %2 + $bl = SETCCr 2, implicit $eflags +... +--- +name: opt_redundant_flags_cmp_test +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_cmp_test + ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $esi + ; CHECK-NEXT: CMP32ri8 [[COPY]], 0, implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 2, implicit $eflags + ; CHECK-NEXT: $bl = SETCCr 2, implicit $eflags + %0:gr32 = COPY $esi + CMP32ri8 %0, 0, implicit-def $eflags + $cl = SETCCr 2, implicit $eflags + ; TEST should be removed + TEST32rr %0, %0, implicit-def $eflags + $bl = SETCCr 2, implicit $eflags +... +--- +name: opt_redundant_flags_test_cmp +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_test_cmp + ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $esi + ; CHECK-NEXT: TEST32rr [[COPY]], [[COPY]], implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 2, implicit $eflags + ; CHECK-NEXT: $bl = SETCCr 2, implicit $eflags + %0:gr32 = COPY $esi + TEST32rr %0, %0, implicit-def $eflags + $cl = SETCCr 2, implicit $eflags + ; TEST should be removed + CMP32ri8 %0, 0, implicit-def $eflags + $bl = SETCCr 2, implicit $eflags +... +--- +name: opt_redundant_flags_cmp_addr +stack: + - { id: 0, size: 4, alignment: 4 } +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_cmp_addr + ; CHECK: [[COPY:%[0-9]+]]:gr64 = COPY $rsi + ; CHECK-NEXT: CMP64ri32 [[COPY]], @opt_redundant_flags_cmp_addr + 4, implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 7, implicit $eflags + ; CHECK-NEXT: $cl = SETCCr 3, implicit $eflags + %0:gr64 = COPY $rsi + CMP64ri32 %0, @opt_redundant_flags_cmp_addr + 4, implicit-def $eflags + $cl = SETCCr 7, implicit $eflags + ; CMP should be removed + CMP64ri32 %0, @opt_redundant_flags_cmp_addr + 4, implicit-def $eflags + $cl = SETCCr 3, implicit $eflags +... +--- +name: opt_redundant_flags_cmp_addr_noopt +stack: + - { id: 0, size: 4, alignment: 4 } +body: | + bb.0: + ; CHECK-LABEL: name: opt_redundant_flags_cmp_addr_noopt + ; CHECK: [[COPY:%[0-9]+]]:gr64 = COPY $rsi + ; CHECK-NEXT: CMP64ri32 [[COPY]], @opt_redundant_flags_cmp_addr_noopt + 24, implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 7, implicit $eflags + ; CHECK-NEXT: CMP64ri32 [[COPY]], 24, implicit-def $eflags + ; CHECK-NEXT: $cl = SETCCr 3, implicit $eflags + %0:gr64 = COPY $rsi + CMP64ri32 %0, @opt_redundant_flags_cmp_addr_noopt + 24, implicit-def $eflags + $cl = SETCCr 7, implicit $eflags + ; CMP should not be removed + CMP64ri32 %0, 24, implicit-def $eflags + $cl = SETCCr 3, implicit $eflags +... diff --git a/llvm/test/CodeGen/X86/postalloc-coalescing.ll b/llvm/test/CodeGen/X86/postalloc-coalescing.ll --- a/llvm/test/CodeGen/X86/postalloc-coalescing.ll +++ b/llvm/test/CodeGen/X86/postalloc-coalescing.ll @@ -15,7 +15,6 @@ ; CHECK-NEXT: jne .LBB0_1 ; CHECK-NEXT: .LBB0_3: # %bb158 ; CHECK-NEXT: movb %al, 0 -; CHECK-NEXT: cmpl $-1, %eax ; CHECK-NEXT: xorl %eax, %eax ; CHECK-NEXT: retl entry: