Index: llvm/include/llvm/Transforms/Utils/NoAliasUtils.h =================================================================== --- llvm/include/llvm/Transforms/Utils/NoAliasUtils.h +++ llvm/include/llvm/Transforms/Utils/NoAliasUtils.h @@ -13,6 +13,15 @@ #ifndef LLVM_TRANSFORMS_UTILS_NOALIASUTILS_H #define LLVM_TRANSFORMS_UTILS_NOALIASUTILS_H +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" + namespace llvm { class Function; Index: llvm/lib/Transforms/Utils/CloneFunction.cpp =================================================================== --- llvm/lib/Transforms/Utils/CloneFunction.cpp +++ llvm/lib/Transforms/Utils/CloneFunction.cpp @@ -991,9 +991,31 @@ return nullptr; }; - if (auto *Decl = dyn_cast(I)) + if (auto *Decl = dyn_cast(I)) { if (auto *NewScopeList = CloneScopeList(Decl->getScopeList())) Decl->setScopeList(NewScopeList); + } else if (auto *II = dyn_cast(I)) { + auto ID = II->getIntrinsicID(); + if (ID == Intrinsic::noalias || ID == Intrinsic::provenance_noalias || + ID == Intrinsic::noalias_decl || ID == Intrinsic::noalias_copy_guard) { + int NoAliasScope = 0; + if (ID == Intrinsic::noalias) + NoAliasScope = Intrinsic::NoAliasScopeArg; + if (ID == Intrinsic::provenance_noalias) + NoAliasScope = Intrinsic::ProvenanceNoAliasScopeArg; + if (ID == Intrinsic::noalias_decl) + NoAliasScope = Intrinsic::NoAliasDeclScopeArg; + if (ID == Intrinsic::noalias_copy_guard) + NoAliasScope = Intrinsic::NoAliasCopyGuardScopeArg; + + if (auto *NewScopeList = CloneScopeList( + cast(cast(II->getOperand(NoAliasScope)) + ->getMetadata()))) { + II->setOperand(NoAliasScope, + MetadataAsValue::get(II->getContext(), NewScopeList)); + } + } + } auto replaceWhenNeeded = [&](unsigned MD_ID) { if (const MDNode *CSNoAlias = I->getMetadata(MD_ID)) @@ -1047,6 +1069,13 @@ for (Instruction &I : *BB) if (auto *Decl = dyn_cast(&I)) NoAliasDeclScopes.push_back(Decl->getScopeList()); + else if (auto CB = dyn_cast(&I)) { + if (CB->getIntrinsicID() == Intrinsic::noalias_decl) + NoAliasDeclScopes.push_back( + dyn_cast(cast( + I.getOperand(Intrinsic::NoAliasDeclScopeArg)) + ->getMetadata())); + } } void llvm::identifyNoAliasScopesToClone( @@ -1055,4 +1084,10 @@ for (Instruction &I : make_range(Start, End)) if (auto *Decl = dyn_cast(&I)) NoAliasDeclScopes.push_back(Decl->getScopeList()); + else if (auto CB = dyn_cast(&I)) { + if (CB->getIntrinsicID() == Intrinsic::noalias_decl) + NoAliasDeclScopes.push_back(dyn_cast( + cast(I.getOperand(Intrinsic::NoAliasDeclScopeArg)) + ->getMetadata())); + } } Index: llvm/lib/Transforms/Utils/LoopUnroll.cpp =================================================================== --- llvm/lib/Transforms/Utils/LoopUnroll.cpp +++ llvm/lib/Transforms/Utils/LoopUnroll.cpp @@ -62,6 +62,7 @@ #include "llvm/Transforms/Utils/LoopPeel.h" #include "llvm/Transforms/Utils/LoopSimplify.h" #include "llvm/Transforms/Utils/LoopUtils.h" +#include "llvm/Transforms/Utils/NoAliasUtils.h" #include "llvm/Transforms/Utils/SimplifyIndVar.h" #include "llvm/Transforms/Utils/UnrollLoop.h" #include "llvm/Transforms/Utils/ValueMapper.h" @@ -589,6 +590,45 @@ SmallVector LoopLocalNoAliasDeclScopes; identifyNoAliasScopesToClone(L->getBlocks(), LoopLocalNoAliasDeclScopes); + if (!LoopLocalNoAliasDeclScopes.empty() && (ULO.Count > 1)) { + // We have loop local llvm.noalias.decl. If they are used by code that is + // considered to be outside the loop, the must go through the latch block. + // We duplicate the llvm.noalias.decl to the latch block, so that migrated + // code can still locally benefit from the noalias information. But it will + // get disconnected from the information inside the loop body itself. + SmallVector UniqueExitBlocks; + L->getUniqueExitBlocks(UniqueExitBlocks); + for (auto *EB : UniqueExitBlocks) { + SmallVector, 6> ClonedNoAlias; + + for (auto &PN : EB->phis()) { + Value *V = PN.getIncomingValue(0); + while (true) { + if (IntrinsicInst *II = dyn_cast(V)) { + if (II->getIntrinsicID() == Intrinsic::noalias_decl) { + ClonedNoAlias.emplace_back(II->clone(), &PN); + } + } else if (PHINode *PN2 = dyn_cast(V)) { + if (PN2->getNumIncomingValues() == 1) { + // look through phi ( phi (.. ) ) in case of nested loops + V = PN2->getIncomingValue(0); + continue; + } + } + break; + } + } + + auto IP = EB->getFirstInsertionPt(); + for (auto P : ClonedNoAlias) { + EB->getInstList().insert(IP, P.first); + } + for (auto P : ClonedNoAlias) { + P.second->replaceAllUsesWith(P.first); + } + } + } + for (unsigned It = 1; It != ULO.Count; ++It) { SmallVector NewBlocks; SmallDenseMap NewLoops; @@ -681,15 +721,25 @@ AC->registerAssumption(II); { - // Identify what other metadata depends on the cloned version. After - // cloning, replace the metadata with the corrected version for both - // memory instructions and noalias intrinsics. + // Phase2: identify what other metadata depends on the cloned version + // Phase3: after cloning, replace the metadata with the corrected version + // for both memory instructions and noalias intrinsics std::string ext = (Twine("It") + Twine(It)).str(); cloneAndAdaptNoAliasScopes(LoopLocalNoAliasDeclScopes, NewBlocks, Header->getContext(), ext); } } + if (!LoopLocalNoAliasDeclScopes.empty() && (ULO.Count > 1)) { + // Also adapt the scopes of the first iteration to avoid any + // conflicts with instructions outside the loop using the + // scopes. Those have already been taken care of by + // duplicating the llvm.noalias.decl. + std::vector OldBlocks(BlockBegin, BlockEnd); + cloneAndAdaptNoAliasScopes(LoopLocalNoAliasDeclScopes, OldBlocks, + Header->getContext(), "It0"); + } + // Loop over the PHI nodes in the original block, setting incoming values. for (PHINode *PN : OrigPHINode) { if (CompletelyUnroll) { Index: llvm/test/Transforms/LoopUnroll/noalias.ll =================================================================== --- llvm/test/Transforms/LoopUnroll/noalias.ll +++ llvm/test/Transforms/LoopUnroll/noalias.ll @@ -6,20 +6,20 @@ ; CHECK-NEXT: start: ; CHECK-NEXT: br label [[BODY:%.*]] ; CHECK: body: -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !0) +; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META0:![0-9]+]]) ; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[ADDR1:%.*]], align 4, !alias.scope !0 ; CHECK-NEXT: store i32 [[X]], i32* [[ADDR2:%.*]], align 4, !noalias !0 ; CHECK-NEXT: [[ADDR1I_1:%.*]] = getelementptr inbounds i32, i32* [[ADDR1]], i32 1 ; CHECK-NEXT: [[ADDR2I_1:%.*]] = getelementptr inbounds i32, i32* [[ADDR2]], i32 1 -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !3) +; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META3:![0-9]+]]) ; CHECK-NEXT: [[X_1:%.*]] = load i32, i32* [[ADDR1I_1]], align 4, !alias.scope !3 ; CHECK-NEXT: store i32 [[X_1]], i32* [[ADDR2I_1]], align 4, !noalias !3 -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !5) +; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META5:![0-9]+]]) ; CHECK-NEXT: [[X_2:%.*]] = load i32, i32* [[ADDR1]], align 4, !alias.scope !5 ; CHECK-NEXT: store i32 [[X_2]], i32* [[ADDR2]], align 4, !noalias !5 ; CHECK-NEXT: [[ADDR1I_3:%.*]] = getelementptr inbounds i32, i32* [[ADDR1]], i32 1 ; CHECK-NEXT: [[ADDR2I_3:%.*]] = getelementptr inbounds i32, i32* [[ADDR2]], i32 1 -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !7) +; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META7:![0-9]+]]) ; CHECK-NEXT: [[X_3:%.*]] = load i32, i32* [[ADDR1I_3]], align 4, !alias.scope !7 ; CHECK-NEXT: store i32 [[X_3]], i32* [[ADDR2I_3]], align 4, !noalias !7 ; CHECK-NEXT: ret void @@ -48,21 +48,21 @@ define void @test_outside(i32* %addr1, i32* %addr2) { ; CHECK-LABEL: @test_outside( ; CHECK-NEXT: start: -; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata !0) +; CHECK-NEXT: call void @llvm.experimental.noalias.scope.decl(metadata [[META9:![0-9]+]]) ; CHECK-NEXT: br label [[BODY:%.*]] ; CHECK: body: -; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[ADDR1:%.*]], align 4, !alias.scope !0 -; CHECK-NEXT: store i32 [[X]], i32* [[ADDR2:%.*]], align 4, !noalias !0 +; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[ADDR1:%.*]], align 4, !alias.scope !9 +; CHECK-NEXT: store i32 [[X]], i32* [[ADDR2:%.*]], align 4, !noalias !9 ; CHECK-NEXT: [[ADDR1I_1:%.*]] = getelementptr inbounds i32, i32* [[ADDR1]], i32 1 ; CHECK-NEXT: [[ADDR2I_1:%.*]] = getelementptr inbounds i32, i32* [[ADDR2]], i32 1 -; CHECK-NEXT: [[X_1:%.*]] = load i32, i32* [[ADDR1I_1]], align 4, !alias.scope !0 -; CHECK-NEXT: store i32 [[X_1]], i32* [[ADDR2I_1]], align 4, !noalias !0 -; CHECK-NEXT: [[X_2:%.*]] = load i32, i32* [[ADDR1]], align 4, !alias.scope !0 -; CHECK-NEXT: store i32 [[X_2]], i32* [[ADDR2]], align 4, !noalias !0 +; CHECK-NEXT: [[X_1:%.*]] = load i32, i32* [[ADDR1I_1]], align 4, !alias.scope !9 +; CHECK-NEXT: store i32 [[X_1]], i32* [[ADDR2I_1]], align 4, !noalias !9 +; CHECK-NEXT: [[X_2:%.*]] = load i32, i32* [[ADDR1]], align 4, !alias.scope !9 +; CHECK-NEXT: store i32 [[X_2]], i32* [[ADDR2]], align 4, !noalias !9 ; CHECK-NEXT: [[ADDR1I_3:%.*]] = getelementptr inbounds i32, i32* [[ADDR1]], i32 1 ; CHECK-NEXT: [[ADDR2I_3:%.*]] = getelementptr inbounds i32, i32* [[ADDR2]], i32 1 -; CHECK-NEXT: [[X_3:%.*]] = load i32, i32* [[ADDR1I_3]], align 4, !alias.scope !0 -; CHECK-NEXT: store i32 [[X_3]], i32* [[ADDR2I_3]], align 4, !noalias !0 +; CHECK-NEXT: [[X_3:%.*]] = load i32, i32* [[ADDR1I_3]], align 4, !alias.scope !9 +; CHECK-NEXT: store i32 [[X_3]], i32* [[ADDR2I_3]], align 4, !noalias !9 ; CHECK-NEXT: ret void ; start: @@ -93,11 +93,13 @@ !2 = !{!1} ; CHECK: !0 = !{!1} -; CHECK: !1 = distinct !{!1, !2} -; CHECK: !2 = distinct !{!2} -; CHECK: !3 = !{!4} -; CHECK: !4 = distinct !{!4, !2, !"It1"} -; CHECK: !5 = !{!6} -; CHECK: !6 = distinct !{!6, !2, !"It2"} -; CHECK: !7 = !{!8} -; CHECK: !8 = distinct !{!8, !2, !"It3"} +; CHECK-NEXT: !1 = distinct !{!1, !2, !"It0"} +; CHECK-NEXT: !2 = distinct !{!2} +; CHECK-NEXT: !3 = !{!4} +; CHECK-NEXT: !4 = distinct !{!4, !2, !"It1"} +; CHECK-NEXT: !5 = !{!6} +; CHECK-NEXT: !6 = distinct !{!6, !2, !"It2"} +; CHECK-NEXT: !7 = !{!8} +; CHECK-NEXT: !8 = distinct !{!8, !2, !"It3"} +; CHECK-NEXT: !9 = !{!10} +; CHECK-NEXT: !10 = distinct !{!10, !2} Index: llvm/test/Transforms/LoopUnroll/noalias2.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/LoopUnroll/noalias2.ll @@ -0,0 +1,196 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -S -unroll-runtime -unroll-count=2 -loop-unroll | FileCheck %s + +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind +define dso_local void @test_loop_unroll_01(i32* nocapture %_pA) local_unnamed_addr #0 { +; CHECK-LABEL: @test_loop_unroll_01( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata [[META2:![0-9]+]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* [[_PA:%.*]], i8* [[TMP0]], i32** null, i32** undef, i32 0, metadata [[META2]]), !tbaa [[TBAA5:![0-9]+]], !noalias !2 +; CHECK-NEXT: br label [[FOR_BODY:%.*]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; CHECK: for.body: +; CHECK-NEXT: [[I_06:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC_1:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, i32* [[_PA]], i32 [[I_06]] +; CHECK-NEXT: store i32 [[I_06]], i32* [[ARRAYIDX]], ptr_provenance i32* [[TMP1]], align 4, !tbaa [[TBAA9:![0-9]+]], !noalias !2 +; CHECK-NEXT: [[INC:%.*]] = add nuw nsw i32 [[I_06]], 1 +; CHECK-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr inbounds i32, i32* [[_PA]], i32 [[INC]] +; CHECK-NEXT: store i32 [[INC]], i32* [[ARRAYIDX_1]], ptr_provenance i32* [[TMP1]], align 4, !tbaa [[TBAA9]], !noalias !2 +; CHECK-NEXT: [[INC_1]] = add nuw nsw i32 [[INC]], 1 +; CHECK-NEXT: [[EXITCOND_1:%.*]] = icmp eq i32 [[INC_1]], 4 +; CHECK-NEXT: br i1 [[EXITCOND_1]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]], !llvm.loop [[LOOP11:![0-9]+]] +; +entry: + %0 = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !2) + %1 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %_pA, i8* %0, i32** null, i32** undef, i32 0, metadata !2), !tbaa !5, !noalias !2 + br label %for.body + +for.cond.cleanup: ; preds = %for.body + ret void + +for.body: ; preds = %for.body, %entry + %i.06 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %arrayidx = getelementptr inbounds i32, i32* %_pA, i32 %i.06 + store i32 %i.06, i32* %arrayidx, ptr_provenance i32* %1, align 4, !tbaa !9, !noalias !2 + %inc = add nuw nsw i32 %i.06, 1 + %exitcond = icmp eq i32 %inc, 4 + br i1 %exitcond, label %for.cond.cleanup, label %for.body +} + +; Function Attrs: argmemonly nounwind +declare i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32**, i32, metadata) #1 + +; Function Attrs: nounwind +define dso_local void @test_loop_unroll_02(i32* nocapture %_pA) local_unnamed_addr #0 { +; CHECK-LABEL: @test_loop_unroll_02( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[FOR_BODY:%.*]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; CHECK: for.body: +; CHECK-NEXT: [[I_06:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC_1:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[TMP0:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata [[META13:![0-9]+]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* [[_PA:%.*]], i8* [[TMP0]], i32** null, i32** undef, i32 0, metadata [[META13]]), !tbaa [[TBAA5]], !noalias !13 +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, i32* [[_PA]], i32 [[I_06]] +; CHECK-NEXT: store i32 [[I_06]], i32* [[ARRAYIDX]], ptr_provenance i32* [[TMP1]], align 4, !tbaa [[TBAA9]], !noalias !13 +; CHECK-NEXT: [[INC:%.*]] = add nuw nsw i32 [[I_06]], 1 +; CHECK-NEXT: [[TMP2:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata [[META16:![0-9]+]]) +; CHECK-NEXT: [[TMP3:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* [[_PA]], i8* [[TMP2]], i32** null, i32** undef, i32 0, metadata [[META16]]), !tbaa [[TBAA5]], !noalias !16 +; CHECK-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr inbounds i32, i32* [[_PA]], i32 [[INC]] +; CHECK-NEXT: store i32 [[INC]], i32* [[ARRAYIDX_1]], ptr_provenance i32* [[TMP3]], align 4, !tbaa [[TBAA9]], !noalias !16 +; CHECK-NEXT: [[INC_1]] = add nuw nsw i32 [[INC]], 1 +; CHECK-NEXT: [[EXITCOND_1:%.*]] = icmp eq i32 [[INC_1]], 4 +; CHECK-NEXT: br i1 [[EXITCOND_1]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]], !llvm.loop [[LOOP18:![0-9]+]] +; +entry: + br label %for.body + +for.cond.cleanup: ; preds = %for.body + ret void + +for.body: ; preds = %for.body, %entry + %i.06 = phi i32 [ 0, %entry ], [ %inc, %for.body ] + %0 = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !11) + %1 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %_pA, i8* %0, i32** null, i32** undef, i32 0, metadata !11), !tbaa !5, !noalias !11 + %arrayidx = getelementptr inbounds i32, i32* %_pA, i32 %i.06 + store i32 %i.06, i32* %arrayidx, ptr_provenance i32* %1, align 4, !tbaa !9, !noalias !11 + %inc = add nuw nsw i32 %i.06, 1 + %exitcond = icmp eq i32 %inc, 4 + br i1 %exitcond, label %for.cond.cleanup, label %for.body +} + +; Function Attrs: nounwind +define dso_local void @test_loop_unroll_03(i32* nocapture %_pA, i1 %c) local_unnamed_addr #0 { +; CHECK-LABEL: @test_loop_unroll_03( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[FOR_BODY:%.*]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: ret void +; CHECK: for.body: +; CHECK-NEXT: [[I_06:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC_1:%.*]], [[FOR_BODY1_1:%.*]] ] +; CHECK-NEXT: [[TMP0:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata [[META19:![0-9]+]]) +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, i32* [[_PA:%.*]], i32 [[I_06]] +; CHECK-NEXT: br i1 [[C:%.*]], label [[ALT_EXIT:%.*]], label [[FOR_BODY1:%.*]] +; CHECK: for.body1: +; CHECK-NEXT: store i32 1, i32* [[ARRAYIDX]], align 4, !tbaa [[TBAA9]], !noalias !19 +; CHECK-NEXT: [[INC:%.*]] = add nuw nsw i32 [[I_06]], 1 +; CHECK-NEXT: [[TMP1:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata [[META22:![0-9]+]]) +; CHECK-NEXT: [[ARRAYIDX_1:%.*]] = getelementptr inbounds i32, i32* [[_PA]], i32 [[INC]] +; CHECK-NEXT: br i1 [[C]], label [[ALT_EXIT]], label [[FOR_BODY1_1]] +; CHECK: alt.exit: +; CHECK-NEXT: [[I_06_LCSSA:%.*]] = phi i32 [ [[I_06]], [[FOR_BODY]] ], [ [[INC]], [[FOR_BODY1]] ] +; CHECK-NEXT: [[DOTLCSSA:%.*]] = phi i8* [ [[TMP0]], [[FOR_BODY]] ], [ [[TMP1]], [[FOR_BODY1]] ] +; CHECK-NEXT: [[ARRAYIDX_LCSSA:%.*]] = phi i32* [ [[ARRAYIDX]], [[FOR_BODY]] ], [ [[ARRAYIDX_1]], [[FOR_BODY1]] ] +; CHECK-NEXT: [[TMP2:%.*]] = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata [[META24:![0-9]+]]) +; CHECK-NEXT: [[S:%.*]] = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* [[_PA]], i8* [[TMP2]], i32** null, i32** undef, i32 0, metadata [[META24]]), !tbaa [[TBAA5]], !noalias !24 +; CHECK-NEXT: store i32 [[I_06_LCSSA]], i32* [[ARRAYIDX_LCSSA]], ptr_provenance i32* [[S]], align 4, !tbaa [[TBAA9]], !noalias !24 +; CHECK-NEXT: ret void +; CHECK: for.body1.1: +; CHECK-NEXT: store i32 1, i32* [[ARRAYIDX_1]], align 4, !tbaa [[TBAA9]], !noalias !22 +; CHECK-NEXT: [[INC_1]] = add nuw nsw i32 [[INC]], 1 +; CHECK-NEXT: [[EXITCOND_1:%.*]] = icmp eq i32 [[INC_1]], 4 +; CHECK-NEXT: br i1 [[EXITCOND_1]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]], !llvm.loop [[LOOP26:![0-9]+]] +; +entry: + br label %for.body + +for.cond.cleanup: ; preds = %for.body + ret void + +for.body: ; preds = %for.body, %entry + %i.06 = phi i32 [ 0, %entry ], [ %inc, %for.body1 ] + %0 = tail call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i32** null, i32 0, metadata !14) + %arrayidx = getelementptr inbounds i32, i32* %_pA, i32 %i.06 + br i1 %c, label %alt.exit, label %for.body1 + +for.body1: + store i32 1, i32* %arrayidx, align 4, !tbaa !9, !noalias !14 + %inc = add nuw nsw i32 %i.06, 1 + %exitcond = icmp eq i32 %inc, 4 + br i1 %exitcond, label %for.cond.cleanup, label %for.body + +alt.exit: + %s = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32* %_pA, i8* %0, i32** null, i32** undef, i32 0, metadata !14), !tbaa !5, !noalias !14 + store i32 %i.06, i32* %arrayidx, ptr_provenance i32* %s, align 4, !tbaa !9, !noalias !14 + ret void +} + +; Function Attrs: nounwind readnone speculatable +declare i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i32(i32*, i8*, i32**, i32**, i32, metadata) #2 + +attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind } +attributes #2 = { nounwind readnone speculatable } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang"} +!2 = !{!3} +!3 = distinct !{!3, !4, !"test_loop_unroll_01: pA"} +!4 = distinct !{!4, !"test_loop_unroll_01"} +!5 = !{!6, !6, i64 0, i64 4} +!6 = !{!7, i64 4, !"any pointer"} +!7 = !{!8, i64 1, !"omnipotent char"} +!8 = !{!"Simple C/C++ TBAA"} +!9 = !{!10, !10, i64 0, i64 4} +!10 = !{!7, i64 4, !"int"} +!11 = !{!12} +!12 = distinct !{!12, !13, !"test_loop_unroll_02: pA"} +!13 = distinct !{!13, !"test_loop_unroll_02"} +!14 = !{!15} +!15 = distinct !{!15, !16, !"test_loop_unroll_03: pA"} +!16 = distinct !{!16, !"test_loop_unroll_03"} + +; CHECK: !0 = !{i32 1, !"wchar_size", i32 4} +; CHECK: !1 = !{!"clang"} +; CHECK: [[META2]] = !{!3} +; CHECK: !3 = distinct !{!3, !4, !"test_loop_unroll_01: pA"} +; CHECK: !4 = distinct !{!4, !"test_loop_unroll_01"} +; CHECK: !5 = !{!6, !6, i64 0, i64 4} +; CHECK: !6 = !{!7, i64 4, !"any pointer"} +; CHECK: !7 = !{!8, i64 1, !"omnipotent char"} +; CHECK: !8 = !{!"Simple C/C++ TBAA"} +; CHECK: !9 = !{!10, !10, i64 0, i64 4} +; CHECK: !10 = !{!7, i64 4, !"int"} +; CHECK: !11 = distinct !{!11, !12} +; CHECK: !12 = !{!"llvm.loop.unroll.disable"} +; CHECK: [[META13]] = !{!14} +; CHECK: !14 = distinct !{!14, !15, !"test_loop_unroll_02: pA:It0"} +; CHECK: !15 = distinct !{!15, !"test_loop_unroll_02"} +; CHECK: [[META16]] = !{!17} +; CHECK: !17 = distinct !{!17, !15, !"test_loop_unroll_02: pA:It1"} +; CHECK: !18 = distinct !{!18, !12} +; CHECK: [[META19]] = !{!20} +; CHECK: !20 = distinct !{!20, !21, !"test_loop_unroll_03: pA:It0"} +; CHECK: !21 = distinct !{!21, !"test_loop_unroll_03"} +; CHECK: [[META22]] = !{!23} +; CHECK: !23 = distinct !{!23, !21, !"test_loop_unroll_03: pA:It1"} +; CHECK: [[META24]] = !{!25} +; CHECK: !25 = distinct !{!25, !21, !"test_loop_unroll_03: pA"} +; CHECK: !26 = distinct !{!26, !12}