Index: llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h =================================================================== --- llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h +++ llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h @@ -85,6 +85,21 @@ : InstrsToBuild(InstrsToBuild) {} }; +/// A general function to apply to complete a combine. Used with the +/// applyMIRBuilderFn convenience combine. +/// +/// \example +/// MatchInfo = [=](MachineIRBuilder &MIB, MachineInstr &MI) { +/// MIB.buildAdd(MI.getOperand(0).getReg(), AddLHS, AddRHS); +/// MI.eraseFromParent(); +/// return true; +/// }; +/// \endexample +/// +/// If the above example is passed into applyMIRBuilderFn, then it will build +/// an add, and erase the MachineInstr. +using MIRBuilderFn = std::function; + class CombinerHelper { protected: MachineIRBuilder &Builder; @@ -380,6 +395,9 @@ /// Delete \p MI and replace all of its uses with \p Replacement. bool replaceSingleDefInstWithReg(MachineInstr &MI, Register Replacement); + /// Apply the combine described in the function \p MatchInfo. + bool applyMIRBuilderFn(MachineInstr &MI, MIRBuilderFn &MatchInfo); + /// Return true if \p MOP1 and \p MOP2 are register operands are defined by /// equivalent instructions. bool matchEqualDefs(const MachineOperand &MOP1, const MachineOperand &MOP2); @@ -465,6 +483,13 @@ bool applyCombineInsertVecElts(MachineInstr &MI, SmallVectorImpl &MatchInfo); + /// Match patterns involving a G_ICMP which uses a second, extended G_ICMP. + /// + /// E.g. + /// + /// icmp ne ([sz]ext (icmp cc, x, y)), 0) -> icmp cc, x, y + bool matchExtICmp(MachineInstr &MI, MIRBuilderFn &MatchInfo); + /// Try to transform \p MI by using all of the above /// combine functions. Returns true if changed. bool tryCombine(MachineInstr &MI); Index: llvm/include/llvm/Target/GlobalISel/Combine.td =================================================================== --- llvm/include/llvm/Target/GlobalISel/Combine.td +++ llvm/include/llvm/Target/GlobalISel/Combine.td @@ -86,6 +86,7 @@ def extending_load_matchdata : GIDefMatchData<"PreferredTuple">; def indexed_load_store_matchdata : GIDefMatchData<"IndexedLoadStoreMatchInfo">; def instruction_steps_matchdata: GIDefMatchData<"InstructionStepsMatchInfo">; +def mirbuilder_fn_matchdata: GIDefMatchData<"MIRBuilderFn">; /// The operator at the root of a GICombineRule.Match dag. def match; @@ -542,6 +543,19 @@ def insert_vec_elt_combines : GICombineGroup< [combine_insert_vec_elts_build_vector]>; +// icmp ne ([sz]ext (icmp cc, x, y)), 0) -> icmp cc, x, y +// icmp eq, ([sz]ext (icmp cc x, y)), 0) -> icmp inv(cc), x, y +// icmp ne, (zext (icmp cc, x, y)), 1) -> icmp inv(cc), x, y +// icmp eq, (zext (icmp cc, x, y)), 1) -> icmp cc, x, y +// icmp ne, (sext (icmp cc, x, y)), -1) -> icmp inv(cc), x, y +// icmp eq, (sext (icmp cc, x, y)), -1) -> icmp cc, x, y +def ext_icmp : GICombineRule< + (defs root:$root, mirbuilder_fn_matchdata:$info), + (match (wip_match_opcode G_ICMP):$root, + [{ return Helper.matchExtICmp(*${root}, ${info}); }]), + (apply [{ return Helper.applyMIRBuilderFn(*${root}, ${info}); }])>; +def icmp_combines : GICombineGroup<[ext_icmp]>; + // FIXME: These should use the custom predicate feature once it lands. def undef_combines : GICombineGroup<[undef_to_fp_zero, undef_to_int_zero, undef_to_negative_one, @@ -580,4 +594,4 @@ unmerge_merge, fabs_fabs_fold, unmerge_cst, unmerge_dead_to_trunc, unmerge_zext_to_zext, trunc_ext_fold, trunc_shl, const_combines, xor_of_and_with_same_reg, ptr_add_with_zero, - shift_immed_chain, shift_of_shifted_logic_chain]>; + shift_immed_chain, shift_of_shifted_logic_chain, icmp_combines]>; Index: llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -2553,6 +2553,12 @@ return true; } +bool CombinerHelper::applyMIRBuilderFn( + MachineInstr &MI, MIRBuilderFn &MatchInfo) { + Builder.setInstrAndDebugLoc(MI); + return MatchInfo(Builder, MI); +} + bool CombinerHelper::matchSelectSameVal(MachineInstr &MI) { assert(MI.getOpcode() == TargetOpcode::G_SELECT); // Match (cond ? x : x) @@ -3129,6 +3135,83 @@ return true; } +bool CombinerHelper::matchExtICmp(MachineInstr &MI, MIRBuilderFn &MatchInfo) { + // Fold the following: + // icmp ne ([sz]ext (icmp cc, x, y)), 0) -> icmp cc, x, y + // icmp eq, ([sz]ext (icmp cc x, y)), 0) -> icmp inv(cc), x, y + // icmp ne, (zext (icmp cc, x, y)), 1) -> icmp inv(cc), x, y + // icmp eq, (zext (icmp cc, x, y)), 1) -> icmp cc, x, y + // icmp ne, (sext (icmp cc, x, y)), -1) -> icmp inv(cc), x, y + // icmp eq, (sext (icmp cc, x, y)), -1) -> icmp cc, x, y + assert(MI.getOpcode() == TargetOpcode::G_ICMP); + + // Only do this pre-legalization. + if (LI) + return false; + LLT s1 = LLT::scalar(1); + if (MRI.getType(MI.getOperand(0).getReg()) != s1) + return false; + + auto Pred = static_cast(MI.getOperand(1).getPredicate()); + if (!ICmpInst::isEquality(Pred)) + return false; + + // Need a constant on the RHS. Don't check the value yet; we need to know the + // type of extend. + Register CstReg = MI.getOperand(3).getReg(); + int64_t Cst; + if (!mi_match(CstReg, MRI, m_ICst(Cst))) + return false; + + // We want to guarantee that we'll at least eliminate the extend. + Register ExtReg = MI.getOperand(2).getReg(); + if (!MRI.hasOneNonDBGUse(ExtReg)) + return false; + + // We're going to look for a second compare which feeds into the extend. + Register TopCmp; + bool IsSignExtended = mi_match(ExtReg, MRI, m_GSExt(m_Reg(TopCmp))); + if (!IsSignExtended && !mi_match(ExtReg, MRI, m_GZExt(m_Reg(TopCmp)))) + return false; + if (MRI.getType(TopCmp) != s1) + return false; + const auto &TLI = getTargetLowering(); + bool IsConstFalse = isConstFalseVal(TLI, Cst, /*IsVector = */ false, + /*IsFP = */ false); + if (!IsConstFalse && + !isExtendedTrueVal(TLI, Cst, /*ExtTy = */ MRI.getType(CstReg), + /* OrigTy = */ s1, + /* IsFP = */ false, IsSignExtended)) + return false; + + // Match: icmp cc, x, y + Register TopCmpLHS, TopCmpRHS; + CmpInst::Predicate TopCmpPred; + if (!mi_match( + TopCmp, MRI, + m_GICmp(m_Pred(TopCmpPred), m_Reg(TopCmpLHS), m_Reg(TopCmpRHS)))) + return false; + + MatchInfo = [=](MachineIRBuilder &MIB, MachineInstr &MI) { + // icmp ne ([sz]ext (icmp cc, x, y)), 0) -> icmp cc, x, y + // icmp eq, (zext (icmp cc, x, y)), 1) -> icmp cc, x, y + // icmp eq, (sext (icmp cc, x, y)), -1) -> icmp cc, x, y + if ((IsConstFalse && Pred == CmpInst::Predicate::ICMP_NE) || + (!IsConstFalse && Pred == CmpInst::Predicate::ICMP_EQ)) + return replaceSingleDefInstWithReg(MI, TopCmp); + + // icmp eq, ([sz]ext (icmp cc x, y)), 0) -> icmp inv(cc), x, y + // icmp ne, (zext (icmp cc, x, y)), 1) -> icmp inv(cc), x, y + // icmp ne, (sext (icmp cc, x, y)), -1) -> icmp inv(cc), x, y + Register InvTopCmp = MRI.cloneVirtualRegister(TopCmp); + MIB.buildICmp(CmpInst::getInversePredicate(TopCmpPred), InvTopCmp, + TopCmpLHS, TopCmpRHS); + return replaceSingleDefInstWithReg(MI, InvTopCmp); + }; + + return true; +} + bool CombinerHelper::tryCombine(MachineInstr &MI) { if (tryCombineCopy(MI)) return true; Index: llvm/test/CodeGen/AArch64/GlobalISel/prelegalizercombiner-ext-icmp.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/GlobalISel/prelegalizercombiner-ext-icmp.mir @@ -0,0 +1,372 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple aarch64 -run-pass=aarch64-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s + +... +--- +name: ne_zext_0 +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; icmp ne (zext (icmp cc, x, y)), 0) -> icmp cc, x, y + ; + ; CHECK-LABEL: name: ne_zext_0 + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0(s32), %reg1 + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT %top_cmp(s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 0 + %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: ne_sext_0 +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; icmp ne (sext (icmp cc, x, y)), 0) -> icmp cc, x, y + ; + ; CHECK-LABEL: name: ne_sext_0 + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0(s32), %reg1 + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT %top_cmp(s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_SEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 0 + %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: eq_zext_1 +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; icmp eq, (zext (icmp cc, x, y)), 1) -> icmp cc, x, y + ; + ; CHECK-LABEL: name: eq_zext_1 + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0(s32), %reg1 + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT %top_cmp(s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 1 + %cmp:_(s1) = G_ICMP intpred(eq), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: eq_sext_negative_1 +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; icmp eq, (sext (icmp cc, x, y)), -1) -> icmp cc, x, y + ; + ; CHECK-LABEL: name: eq_sext_negative_1 + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0(s32), %reg1 + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT %top_cmp(s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_SEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 -1 + %cmp:_(s1) = G_ICMP intpred(eq), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: eq_zext_0 +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; icmp eq, (zext (icmp cc x, y)), 0) -> icmp inv(cc), x, y + ; + ; CHECK-LABEL: name: eq_zext_0 + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(sge), %reg0(s32), %reg1 + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT [[ICMP]](s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 0 + %cmp:_(s1) = G_ICMP intpred(eq), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: eq_sext_0 +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; icmp eq, (sext (icmp cc x, y)), 0) -> icmp inv(cc), x, y + ; + ; CHECK-LABEL: name: eq_sext_0 + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(sge), %reg0(s32), %reg1 + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT [[ICMP]](s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_SEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 0 + %cmp:_(s1) = G_ICMP intpred(eq), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: ne_zext_1 +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; icmp ne, (zext (icmp cc, x, y)), 1) -> icmp inv(cc), x, y + ; + ; CHECK-LABEL: name: ne_zext_1 + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(sge), %reg0(s32), %reg1 + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT [[ICMP]](s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 1 + %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: ne_sext_negative_1 +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; icmp ne, (sext (icmp cc, x, y)), -1) -> icmp inv(cc), x, y + ; + ; CHECK-LABEL: name: ne_sext_negative_1 + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(sge), %reg0(s32), %reg1 + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT [[ICMP]](s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_SEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 -1 + %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: bad_pred +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; Need ne or eq for the root cmp. Don't combine. + ; + ; CHECK-LABEL: name: bad_pred + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0(s32), %reg1 + ; CHECK: %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + ; CHECK: %cst:_(s32) = G_CONSTANT i32 0 + ; CHECK: %cmp:_(s1) = G_ICMP intpred(sge), %ext_top_cmp(s32), %cst + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 0 + %cmp:_(s1) = G_ICMP intpred(sge), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: bad_cst_zext +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; With G_ZEXT, we need 1 or 0. Don't combine. + ; + ; CHECK-LABEL: name: bad_cst_zext + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0(s32), %reg1 + ; CHECK: %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + ; CHECK: %cst:_(s32) = G_CONSTANT i32 -1 + ; CHECK: %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 -1 + %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: bad_cst_sext +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; With G_SEXT, we need -1 or 0. Don't combine. + ; + ; CHECK-LABEL: name: bad_cst_sext + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0(s32), %reg1 + ; CHECK: %ext_top_cmp:_(s32) = G_SEXT %top_cmp(s1) + ; CHECK: %cst:_(s32) = G_CONSTANT i32 1 + ; CHECK: %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + ; CHECK: $w0 = COPY %ext_cmp(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_SEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 1 + %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + $w0 = COPY %ext_cmp(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: ext_more_than_one_use +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $w1 + ; When the extend has more than one use, don't combine. + ; + ; CHECK-LABEL: name: ext_more_than_one_use + ; CHECK: liveins: $w0, $w1 + ; CHECK: %reg0:_(s32) = COPY $w0 + ; CHECK: %reg1:_(s32) = COPY $w1 + ; CHECK: %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0(s32), %reg1 + ; CHECK: %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + ; CHECK: %cst:_(s32) = G_CONSTANT i32 0 + ; CHECK: %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + ; CHECK: %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + ; CHECK: %and:_(s32) = G_AND %ext_cmp, %ext_top_cmp + ; CHECK: $w0 = COPY %and(s32) + ; CHECK: RET_ReallyLR implicit $w0 + %reg0:_(s32) = COPY $w0 + %reg1:_(s32) = COPY $w1 + %top_cmp:_(s1) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(s32) = G_ZEXT %top_cmp(s1) + %cst:_(s32) = G_CONSTANT i32 0 + %cmp:_(s1) = G_ICMP intpred(ne), %ext_top_cmp(s32), %cst + %ext_cmp:_(s32) = G_ANYEXT %cmp(s1) + %and:_(s32) = G_AND %ext_cmp(s32), %ext_top_cmp(s32) + $w0 = COPY %and(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: vector +tracksRegLiveness: true +body: | + bb.0: + liveins: $d0, $d1 + ; Don't handle vectors. + ; + ; CHECK-LABEL: name: vector + ; CHECK: liveins: $d0, $d1 + ; CHECK: %reg0:_(<2 x s32>) = COPY $d0 + ; CHECK: %reg1:_(<2 x s32>) = COPY $d0 + ; CHECK: %top_cmp:_(<2 x s1>) = G_ICMP intpred(slt), %reg0(<2 x s32>), %reg1 + ; CHECK: %ext_top_cmp:_(<2 x s32>) = G_ZEXT %top_cmp(<2 x s1>) + ; CHECK: %cst:_(s32) = G_CONSTANT i32 0 + ; CHECK: %cst_vec:_(<2 x s32>) = G_BUILD_VECTOR %cst(s32), %cst(s32) + ; CHECK: %cmp:_(<2 x s1>) = G_ICMP intpred(ne), %ext_top_cmp(<2 x s32>), %cst_vec + ; CHECK: %ext_cmp:_(<2 x s32>) = G_ANYEXT %cmp(<2 x s1>) + ; CHECK: $d0 = COPY %ext_cmp(<2 x s32>) + ; CHECK: RET_ReallyLR implicit $d0 + %reg0:_(<2 x s32>) = COPY $d0 + %reg1:_(<2 x s32>) = COPY $d0 + %top_cmp:_(<2 x s1>) = G_ICMP intpred(slt), %reg0, %reg1 + %ext_top_cmp:_(<2 x s32>) = G_ZEXT %top_cmp(<2 x s1>) + %cst:_(s32) = G_CONSTANT i32 0 + %cst_vec:_(<2 x s32>) = G_BUILD_VECTOR %cst(s32), %cst + %cmp:_(<2 x s1>) = G_ICMP intpred(ne), %ext_top_cmp(<2 x s32>), %cst_vec + %ext_cmp:_(<2 x s32>) = G_ANYEXT %cmp(<2 x s1>) + $d0 = COPY %ext_cmp(<2 x s32>) + RET_ReallyLR implicit $d0