Index: llvm/lib/Transforms/Scalar/MergeICmps.cpp =================================================================== --- llvm/lib/Transforms/Scalar/MergeICmps.cpp +++ llvm/lib/Transforms/Scalar/MergeICmps.cpp @@ -331,10 +331,10 @@ // Visit the given comparison block. If this is a comparison between two valid // BCE atoms, returns the comparison. -std::optional visitCmpBlock(Value *const Val, - BasicBlock *const Block, - const BasicBlock *const PhiBlock, - BaseIdentifier &BaseId) { +std::optional +visitCmpBlock(Value *const Baseline, ICmpInst::Predicate &Predicate, + Value *const Val, BasicBlock *const Block, + const BasicBlock *const PhiBlock, BaseIdentifier &BaseId) { if (Block->empty()) return std::nullopt; auto *const BranchI = dyn_cast(Block->getTerminator()); @@ -349,15 +349,27 @@ // that this does not mean that this is the last incoming value, blocks // can be reordered). Cond = Val; - ExpectedPredicate = ICmpInst::ICMP_EQ; + const auto *const ConstBase = cast(Baseline); + assert(ConstBase->getType()->isIntegerTy(1) && + "Select condition is not an i1?"); + ExpectedPredicate = + ConstBase->isOne() ? ICmpInst::ICMP_NE : ICmpInst::ICMP_EQ; + + // Remember the correct predicate. + Predicate = ExpectedPredicate; } else { + // All the incoming values must be consistent. + if (Baseline != Val) + return std::nullopt; // In this case, we expect a constant incoming value (the comparison is // chained). const auto *const Const = cast(Val); - LLVM_DEBUG(dbgs() << "const\n"); - if (!Const->isZero()) + assert(Const->getType()->isIntegerTy(1) && + "Incoming value is not an i1?"); + LLVM_DEBUG(dbgs() << "const i1 value\n"); + if (!Const->isZero() && !Const->isOne()) return std::nullopt; - LLVM_DEBUG(dbgs() << "false\n"); + LLVM_DEBUG(dbgs() << *Const << "\n"); assert(BranchI->getNumSuccessors() == 2 && "expecting a cond branch"); BasicBlock *const FalseBlock = BranchI->getSuccessor(1); Cond = BranchI->getCondition(); @@ -418,6 +430,8 @@ std::vector MergedBlocks_; // The original entry block (before sorting); BasicBlock *EntryBlock_; + // Remember the predicate type of the chain. + ICmpInst::Predicate Predicate_; }; static bool areContiguous(const BCECmpBlock &First, const BCECmpBlock &Second) { @@ -476,10 +490,13 @@ // Now look inside blocks to check for BCE comparisons. std::vector Comparisons; BaseIdentifier BaseId; + Value *const Baseline = Phi.getIncomingValueForBlock(Blocks[0]); + Predicate_ = CmpInst::BAD_ICMP_PREDICATE; for (BasicBlock *const Block : Blocks) { assert(Block && "invalid block"); - std::optional Comparison = visitCmpBlock( - Phi.getIncomingValueForBlock(Block), Block, Phi.getParent(), BaseId); + std::optional Comparison = + visitCmpBlock(Baseline, Predicate_, Phi.getIncomingValueForBlock(Block), + Block, Phi.getParent(), BaseId); if (!Comparison) { LLVM_DEBUG(dbgs() << "chain with invalid BCECmpBlock, no merge.\n"); return; @@ -598,12 +615,28 @@ }; } // namespace +// Check whether the current comparisons is the last comparisons. +// Only the last comparisons chain include the last link (unconditionla jmp). +// Due to reordering of contiguous comparison blocks, the unconditionla +// comparison of a chain maybe not placed at the end of the array comparisons. +static bool isLastLinkComparison(ArrayRef Comparisons) { + for (int i = 0; i < Comparisons.size(); i++) { + BasicBlock *const BB = Comparisons[i].BB; + auto *const BranchI = cast(BB->getTerminator()); + // Only the last comparisons chain include the unconditionla jmp + if (BranchI->isUnconditional()) + return true; + } + return false; +} + // Merges the given contiguous comparison blocks into one memcmp block. static BasicBlock *mergeComparisons(ArrayRef Comparisons, BasicBlock *const InsertBefore, BasicBlock *const NextCmpBlock, PHINode &Phi, const TargetLibraryInfo &TLI, - AliasAnalysis &AA, DomTreeUpdater &DTU) { + AliasAnalysis &AA, DomTreeUpdater &DTU, + ICmpInst::Predicate Predicate) { assert(!Comparisons.empty() && "merging zero comparisons"); LLVMContext &Context = NextCmpBlock->getContext(); const BCECmpBlock &FirstCmp = Comparisons[0]; @@ -624,7 +657,7 @@ else Rhs = FirstCmp.Rhs().LoadI->getPointerOperand(); - Value *IsEqual = nullptr; + Value *ICmpValue = nullptr; LLVM_DEBUG(dbgs() << "Merging " << Comparisons.size() << " comparisons -> " << BB->getName() << "\n"); @@ -638,6 +671,11 @@ ToSplit->split(BB, AA); } + // For a Icmp chain, the Predicate is record the last link in the chain of + // comparisons. When we spilt the chain The new spilted chain of comparisons + // is end with ICMP_EQ. + ICmpInst::Predicate Pred = + isLastLinkComparison(Comparisons) ? Predicate : ICmpInst::ICMP_EQ; if (Comparisons.size() == 1) { LLVM_DEBUG(dbgs() << "Only one comparison, updating branches\n"); // Use clone to keep the metadata @@ -646,7 +684,7 @@ LhsLoad->replaceUsesOfWith(LhsLoad->getOperand(0), Lhs); RhsLoad->replaceUsesOfWith(RhsLoad->getOperand(0), Rhs); // There are no blocks to merge, just do the comparison. - IsEqual = Builder.CreateICmpEQ(LhsLoad, RhsLoad); + ICmpValue = Builder.CreateICmp(Pred, LhsLoad, RhsLoad); } else { const unsigned TotalSizeBits = std::accumulate( Comparisons.begin(), Comparisons.end(), 0u, @@ -662,8 +700,8 @@ Lhs, Rhs, ConstantInt::get(Builder.getIntNTy(SizeTBits), TotalSizeBits / 8), Builder, DL, &TLI); - IsEqual = Builder.CreateICmpEQ( - MemCmpCall, ConstantInt::get(Builder.getIntNTy(IntBits), 0)); + ICmpValue = Builder.CreateICmp( + Pred, MemCmpCall, ConstantInt::get(Builder.getIntNTy(IntBits), 0)); } BasicBlock *const PhiBB = Phi.getParent(); @@ -671,12 +709,15 @@ if (NextCmpBlock == PhiBB) { // Continue to phi, passing it the comparison result. Builder.CreateBr(PhiBB); - Phi.addIncoming(IsEqual, BB); + Phi.addIncoming(ICmpValue, BB); DTU.applyUpdates({{DominatorTree::Insert, BB, PhiBB}}); } else { // Continue to next block if equal, exit to phi else. - Builder.CreateCondBr(IsEqual, NextCmpBlock, PhiBB); - Phi.addIncoming(ConstantInt::getFalse(Context), BB); + Builder.CreateCondBr(ICmpValue, NextCmpBlock, PhiBB); + Value *ConstantVal = Predicate == CmpInst::ICMP_NE + ? ConstantInt::getTrue(Context) + : ConstantInt::getFalse(Context); + Phi.addIncoming(ConstantVal, BB); DTU.applyUpdates({{DominatorTree::Insert, BB, NextCmpBlock}, {DominatorTree::Insert, BB, PhiBB}}); } @@ -693,9 +734,11 @@ // so that the next block is always available to branch to. BasicBlock *InsertBefore = EntryBlock_; BasicBlock *NextCmpBlock = Phi_.getParent(); + assert(Predicate_ != CmpInst::BAD_ICMP_PREDICATE && + "Got the chain of comparisons"); for (const auto &Blocks : reverse(MergedBlocks_)) { InsertBefore = NextCmpBlock = mergeComparisons( - Blocks, InsertBefore, NextCmpBlock, Phi_, TLI, AA, DTU); + Blocks, InsertBefore, NextCmpBlock, Phi_, TLI, AA, DTU, Predicate_); } // Replace the original cmp chain with the new cmp chain by pointing all Index: llvm/test/Transforms/MergeICmps/X86/pr59740.ll =================================================================== --- llvm/test/Transforms/MergeICmps/X86/pr59740.ll +++ llvm/test/Transforms/MergeICmps/X86/pr59740.ll @@ -8,35 +8,12 @@ define noundef i1 @full_sequent_ne(ptr nocapture readonly align 1 dereferenceable(4) %s0, ptr nocapture readonly align 1 dereferenceable(4) %s1) { ; CHECK-LABEL: @full_sequent_ne( -; CHECK-NEXT: bb0: -; CHECK-NEXT: [[V0:%.*]] = load i8, ptr [[S0:%.*]], align 1 -; CHECK-NEXT: [[V1:%.*]] = load i8, ptr [[S1:%.*]], align 1 -; CHECK-NEXT: [[CMP0:%.*]] = icmp eq i8 [[V0]], [[V1]] -; CHECK-NEXT: br i1 [[CMP0]], label [[BB1:%.*]], label [[BB4:%.*]] -; CHECK: bb1: -; CHECK-NEXT: [[S2:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], ptr [[S0]], i64 0, i32 1 -; CHECK-NEXT: [[V2:%.*]] = load i8, ptr [[S2]], align 1 -; CHECK-NEXT: [[S3:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S1]], i64 0, i32 1 -; CHECK-NEXT: [[V3:%.*]] = load i8, ptr [[S3]], align 1 -; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[V2]], [[V3]] -; CHECK-NEXT: br i1 [[CMP1]], label [[BB2:%.*]], label [[BB4]] -; CHECK: bb2: -; CHECK-NEXT: [[S4:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S0]], i64 0, i32 2 -; CHECK-NEXT: [[V4:%.*]] = load i8, ptr [[S4]], align 1 -; CHECK-NEXT: [[S5:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S1]], i64 0, i32 2 -; CHECK-NEXT: [[V5:%.*]] = load i8, ptr [[S5]], align 1 -; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[V4]], [[V5]] -; CHECK-NEXT: br i1 [[CMP2]], label [[BB3:%.*]], label [[BB4]] -; CHECK: bb3: -; CHECK-NEXT: [[S6:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S0]], i64 0, i32 3 -; CHECK-NEXT: [[V6:%.*]] = load i8, ptr [[S6]], align 1 -; CHECK-NEXT: [[S7:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[S1]], i64 0, i32 3 -; CHECK-NEXT: [[V7:%.*]] = load i8, ptr [[S7]], align 1 -; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i8 [[V6]], [[V7]] -; CHECK-NEXT: br label [[BB4]] +; CHECK-NEXT: "bb0+bb1+bb2+bb3": +; CHECK-NEXT: [[MEMCMP:%.*]] = call i32 @memcmp(ptr [[S0:%.*]], ptr [[S1:%.*]], i64 4) +; CHECK-NEXT: [[TMP0:%.*]] = icmp ne i32 [[MEMCMP]], 0 +; CHECK-NEXT: br label [[BB4:%.*]] ; CHECK: bb4: -; CHECK-NEXT: [[CMP:%.*]] = phi i1 [ true, [[BB0:%.*]] ], [ true, [[BB1]] ], [ true, [[BB2]] ], [ [[CMP3]], [[BB3]] ] -; CHECK-NEXT: ret i1 [[CMP]] +; CHECK-NEXT: ret i1 [[TMP0]] ; bb0: %v0 = load i8, ptr %s0, align 1 @@ -159,27 +136,19 @@ ; https://alive2.llvm.org/ce/z/sL5Uz6 define noundef i1 @partial_sequent_ne(ptr nocapture readonly dereferenceable(16) %s0, ptr nocapture readonly dereferenceable(16) %s1) { ; CHECK-LABEL: @partial_sequent_ne( -; CHECK-NEXT: bb0: -; CHECK-NEXT: [[V0:%.*]] = load i32, ptr [[S0:%.*]], align 8 -; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[S1:%.*]], align 8 -; CHECK-NEXT: [[CMP0:%.*]] = icmp eq i32 [[V0]], [[V1]] -; CHECK-NEXT: br i1 [[CMP0]], label [[BB1:%.*]], label [[BB3:%.*]] -; CHECK: bb1: -; CHECK-NEXT: [[S2:%.*]] = getelementptr inbounds [[STRUCT1_S:%.*]], ptr [[S0]], i64 0, i32 2 -; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[S2]], align 8 -; CHECK-NEXT: [[S3:%.*]] = getelementptr inbounds [[STRUCT1_S]], ptr [[S1]], i64 0, i32 2 -; CHECK-NEXT: [[V3:%.*]] = load i32, ptr [[S3]], align 8 -; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[V2]], [[V3]] -; CHECK-NEXT: br i1 [[CMP1]], label [[BB2:%.*]], label [[BB3]] -; CHECK: bb2: -; CHECK-NEXT: [[S6:%.*]] = getelementptr inbounds [[STRUCT1_S]], ptr [[S0]], i64 0, i32 3 -; CHECK-NEXT: [[V6:%.*]] = load i8, ptr [[S6]], align 1 -; CHECK-NEXT: [[S7:%.*]] = getelementptr inbounds [[STRUCT1_S]], ptr [[S1]], i64 0, i32 3 -; CHECK-NEXT: [[V7:%.*]] = load i8, ptr [[S7]], align 1 -; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i8 [[V6]], [[V7]] +; CHECK-NEXT: bb01: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[S0:%.*]], align 8 +; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[S1:%.*]], align 8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP0]], [[TMP1]] +; CHECK-NEXT: br i1 [[TMP2]], label %"bb1+bb2", label [[BB3:%.*]] +; CHECK: "bb1+bb2": +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [[STRUCT1_S:%.*]], ptr [[S0]], i64 0, i32 2 +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds [[STRUCT1_S]], ptr [[S1]], i64 0, i32 2 +; CHECK-NEXT: [[MEMCMP:%.*]] = call i32 @memcmp(ptr [[TMP3]], ptr [[TMP4]], i64 5) +; CHECK-NEXT: [[TMP5:%.*]] = icmp ne i32 [[MEMCMP]], 0 ; CHECK-NEXT: br label [[BB3]] ; CHECK: bb3: -; CHECK-NEXT: [[CMP:%.*]] = phi i1 [ true, [[BB0:%.*]] ], [ true, [[BB1]] ], [ [[CMP3]], [[BB2]] ] +; CHECK-NEXT: [[CMP:%.*]] = phi i1 [ [[TMP5]], %"bb1+bb2" ], [ true, [[BB01:%.*]] ] ; CHECK-NEXT: ret i1 [[CMP]] ; bb0: @@ -212,34 +181,26 @@ ; https://alive2.llvm.org/ce/z/EQtb_S define i1 @WebrtcVideoStats(ptr nocapture noundef dereferenceable(16) %S0, ptr nocapture noundef dereferenceable(16) %S1) { ; CHECK-LABEL: @WebrtcVideoStats( -; CHECK-NEXT: bb0: -; CHECK-NEXT: [[V0:%.*]] = load i8, ptr [[S0:%.*]], align 4 -; CHECK-NEXT: [[V1:%.*]] = load i8, ptr [[S1:%.*]], align 4 -; CHECK-NEXT: [[CMP0:%.*]] = icmp eq i8 [[V0]], [[V1]] -; CHECK-NEXT: br i1 [[CMP0]], label [[BB1:%.*]], label [[BB4:%.*]] -; CHECK: bb1: -; CHECK-NEXT: [[BASE2:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S0]], i64 0, i32 1 -; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[BASE2]], align 4 -; CHECK-NEXT: [[BASE3:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S1]], i64 0, i32 1 -; CHECK-NEXT: [[V3:%.*]] = load i32, ptr [[BASE3]], align 4 -; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[V2]], [[V3]] -; CHECK-NEXT: br i1 [[CMP1]], label [[BB2:%.*]], label [[BB4]] -; CHECK: bb2: -; CHECK-NEXT: [[BASE4:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S0]], i64 0, i32 2 -; CHECK-NEXT: [[V4:%.*]] = load i8, ptr [[BASE4]], align 4 -; CHECK-NEXT: [[BASE5:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S1]], i64 0, i32 2 -; CHECK-NEXT: [[V5:%.*]] = load i8, ptr [[BASE5]], align 4 -; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[V4]], [[V5]] -; CHECK-NEXT: br i1 [[CMP2]], label [[BB3:%.*]], label [[BB4]] -; CHECK: bb3: -; CHECK-NEXT: [[BASE6:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S0]], i64 0, i32 3 -; CHECK-NEXT: [[V6:%.*]] = load i32, ptr [[BASE6]], align 4 -; CHECK-NEXT: [[BASE7:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S1]], i64 0, i32 3 -; CHECK-NEXT: [[V7:%.*]] = load i32, ptr [[BASE7]], align 4 -; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i32 [[V6]], [[V7]] +; CHECK-NEXT: bb02: +; CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[S0:%.*]], align 4 +; CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[S1:%.*]], align 4 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]] +; CHECK-NEXT: br i1 [[TMP2]], label %"bb1+bb2", label [[BB4:%.*]] +; CHECK: "bb1+bb2": +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S0]], i64 0, i32 1 +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S1]], i64 0, i32 1 +; CHECK-NEXT: [[MEMCMP:%.*]] = call i32 @memcmp(ptr [[TMP3]], ptr [[TMP4]], i64 5) +; CHECK-NEXT: [[TMP5:%.*]] = icmp eq i32 [[MEMCMP]], 0 +; CHECK-NEXT: br i1 [[TMP5]], label [[BB31:%.*]], label [[BB4]] +; CHECK: bb31: +; CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S0]], i64 0, i32 3 +; CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds %"struct.media::WebrtcVideoStatsDB::VideoDescKey", ptr [[S1]], i64 0, i32 3 +; CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP6]], align 4 +; CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[TMP7]], align 4 +; CHECK-NEXT: [[TMP10:%.*]] = icmp ne i32 [[TMP8]], [[TMP9]] ; CHECK-NEXT: br label [[BB4]] ; CHECK: bb4: -; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[CMP3]], [[BB3]] ], [ true, [[BB2]] ], [ true, [[BB1]] ], [ true, [[BB0:%.*]] ] +; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP10]], [[BB31]] ], [ true, %"bb1+bb2" ], [ true, [[BB02:%.*]] ] ; CHECK-NEXT: ret i1 [[RESULT]] ; bb0: @@ -279,33 +240,18 @@ ; the comparison link sequence will be reordered according the memory offset, ; and the unconditionla comparison will not at the end of the chain. -define i1 @full_revert_order_ne(ptr nocapture noundef nonnull readonly dereferenceable(24) %0, ptr nocapture noundef nonnull readonly dereferenceable(24) %1) { +define i1 @full_revert_order_ne(ptr nocapture noundef nonnull readonly dereferenceable(24) %0, ; CHECK-LABEL: @full_revert_order_ne( -; CHECK-NEXT: bb0: -; CHECK-NEXT: [[S0:%.*]] = getelementptr inbounds [[C:%.*]], ptr [[TMP0:%.*]], i64 0, i32 2 -; CHECK-NEXT: [[V0:%.*]] = load i32, ptr [[S0]], align 4 -; CHECK-NEXT: [[S1:%.*]] = getelementptr inbounds [[C]], ptr [[TMP1:%.*]], i64 0, i32 2 -; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[S1]], align 4 -; CHECK-NEXT: [[CMP0:%.*]] = icmp eq i32 [[V0]], [[V1]] -; CHECK-NEXT: br i1 [[CMP0]], label [[BB1:%.*]], label [[BB3:%.*]] -; CHECK: bb1: -; CHECK-NEXT: [[S2:%.*]] = getelementptr inbounds [[C]], ptr [[TMP0]], i64 0, i32 1 -; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[S2]], align 4 -; CHECK-NEXT: [[S3:%.*]] = getelementptr inbounds [[C]], ptr [[TMP1]], i64 0, i32 1 -; CHECK-NEXT: [[V3:%.*]] = load i32, ptr [[S3]], align 4 -; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[V2]], [[V3]] -; CHECK-NEXT: br i1 [[CMP1]], label [[BB2:%.*]], label [[BB3]] -; CHECK: bb2: -; CHECK-NEXT: [[S4:%.*]] = getelementptr inbounds [[C]], ptr [[TMP0]], i64 0, i32 0 -; CHECK-NEXT: [[V4:%.*]] = load i32, ptr [[S4]], align 4 -; CHECK-NEXT: [[S5:%.*]] = getelementptr inbounds [[C]], ptr [[TMP1]], i64 0, i32 0 -; CHECK-NEXT: [[V5:%.*]] = load i32, ptr [[S5]], align 4 -; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i32 [[V4]], [[V5]] -; CHECK-NEXT: br label [[BB3]] +; CHECK-NEXT: "bb2+bb1+bb0": +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[C:%.*]], ptr [[TMP0:%.*]], i64 0, i32 0 +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [[C]], ptr [[TMP1:%.*]], i64 0, i32 0 +; CHECK-NEXT: [[MEMCMP:%.*]] = call i32 @memcmp(ptr [[TMP2]], ptr [[TMP3]], i64 12) +; CHECK-NEXT: [[TMP4:%.*]] = icmp ne i32 [[MEMCMP]], 0 +; CHECK-NEXT: br label [[BB3:%.*]] ; CHECK: bb3: -; CHECK-NEXT: [[CMP3:%.*]] = phi i1 [ true, [[BB1]] ], [ true, [[BB0:%.*]] ], [ [[CMP2]], [[BB2]] ] -; CHECK-NEXT: ret i1 [[CMP3]] +; CHECK-NEXT: ret i1 [[TMP4]] ; + ptr nocapture noundef nonnull readonly dereferenceable(24) %1) { bb0: %s0 = getelementptr inbounds %"c", ptr %0, i64 0, i32 2 %v0 = load i32, ptr %s0, align 4