diff --git a/llvm/include/llvm/Analysis/AssumeBundleQueries.h b/llvm/include/llvm/Analysis/AssumeBundleQueries.h --- a/llvm/include/llvm/Analysis/AssumeBundleQueries.h +++ b/llvm/include/llvm/Analysis/AssumeBundleQueries.h @@ -142,8 +142,9 @@ RetainedKnowledge getKnowledgeForValue( const Value *V, ArrayRef AttrKinds, AssumptionCache *AC = nullptr, - function_ref Filter = - [](RetainedKnowledge, Instruction *) { return true; }); + function_ref + Filter = [](auto...) { return true; }); /// Return a valid Knowledge associated to the Value V if its Attribute kind is /// in AttrKinds and the knowledge is suitable to be used in the context of diff --git a/llvm/include/llvm/Transforms/Utils/AssumeBundleBuilder.h b/llvm/include/llvm/Transforms/Utils/AssumeBundleBuilder.h --- a/llvm/include/llvm/Transforms/Utils/AssumeBundleBuilder.h +++ b/llvm/include/llvm/Transforms/Utils/AssumeBundleBuilder.h @@ -23,6 +23,7 @@ namespace llvm { class IntrinsicInst; class AssumptionCache; +class DominatorTree; /// Build a call to llvm.assume to preserve informations that can be derived /// from the given instruction. @@ -33,7 +34,12 @@ /// Calls BuildAssumeFromInst and if the resulting llvm.assume is valid insert /// if before I. This is usually what need to be done to salvage the knowledge /// contained in the instruction I. -void salvageKnowledge(Instruction *I, AssumptionCache *AC = nullptr); +/// The AssumptionCache must be provided if it is available or the cache may +/// become silently be invalid. +/// The DominatorTree can optionally be provided to enable cross-block +/// reasoning. +void salvageKnowledge(Instruction *I, AssumptionCache *AC = nullptr, + DominatorTree *DT = nullptr); /// This pass will try to build an llvm.assume for every instruction in the /// function. Its main purpose is testing. diff --git a/llvm/lib/Analysis/AssumeBundleQueries.cpp b/llvm/lib/Analysis/AssumeBundleQueries.cpp --- a/llvm/lib/Analysis/AssumeBundleQueries.cpp +++ b/llvm/lib/Analysis/AssumeBundleQueries.cpp @@ -120,25 +120,35 @@ }); } +static CallInst::BundleOpInfo *getBundleFromUse(const Use *U) { + auto *Intr = dyn_cast(U->getUser()); + if (!match(U->getUser(), + m_Intrinsic(m_Unless(m_Specific(U->get()))))) + return nullptr; + return &Intr->getBundleOpInfoForOperand(U->getOperandNo()); +} + RetainedKnowledge llvm::getKnowledgeFromUse(const Use *U, ArrayRef AttrKinds) { - if (!match(U->getUser(), - m_Intrinsic(m_Unless(m_Specific(U->get()))))) + CallInst::BundleOpInfo* Bundle = getBundleFromUse(U); + if (!Bundle) return RetainedKnowledge::none(); - auto *Intr = cast(U->getUser()); RetainedKnowledge RK = - getKnowledgeFromOperandInAssume(*Intr, U->getOperandNo()); + getKnowledgeFromBundle(*cast(U->getUser()), *Bundle); for (auto Attr : AttrKinds) if (Attr == RK.AttrKind) return RK; return RetainedKnowledge::none(); } -RetainedKnowledge llvm::getKnowledgeForValue( - const Value *V, ArrayRef AttrKinds, - AssumptionCache *AC, - function_ref Filter) { +RetainedKnowledge +llvm::getKnowledgeForValue(const Value *V, + ArrayRef AttrKinds, + AssumptionCache *AC, + function_ref + Filter) { if (AC) { #ifndef NDEBUG RetainedKnowledge RKCheck = @@ -150,7 +160,8 @@ continue; if (RetainedKnowledge RK = getKnowledgeFromBundle( *II, II->bundle_op_info_begin()[Elem.Index])) - if (is_contained(AttrKinds, RK.AttrKind) && Filter(RK, II)) { + if (is_contained(AttrKinds, RK.AttrKind) && + Filter(RK, II, &II->bundle_op_info_begin()[Elem.Index])) { assert(!!RKCheck && "invalid Assumption cache"); return RK; } @@ -159,8 +170,13 @@ return RetainedKnowledge::none(); } for (auto &U : V->uses()) { - if (RetainedKnowledge RK = getKnowledgeFromUse(&U, AttrKinds)) - if (Filter(RK, cast(U.getUser()))) + CallInst::BundleOpInfo* Bundle = getBundleFromUse(&U); + if (!Bundle) + continue; + if (RetainedKnowledge RK = + getKnowledgeFromBundle(*cast(U.getUser()), *Bundle)) + if (is_contained(AttrKinds, RK.AttrKind) && + Filter(RK, cast(U.getUser()), Bundle)) return RK; } return RetainedKnowledge::none(); @@ -170,7 +186,7 @@ const Value *V, ArrayRef AttrKinds, const Instruction *CtxI, const DominatorTree *DT, AssumptionCache *AC) { return getKnowledgeForValue(V, AttrKinds, AC, - [&](RetainedKnowledge, Instruction *I) { + [&](auto, Instruction *I, auto) { return isValidAssumeForContext(I, CtxI, DT); }); } diff --git a/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp b/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp --- a/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp +++ b/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp @@ -9,7 +9,8 @@ #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Analysis/AssumeBundleQueries.h" #include "llvm/Analysis/AssumptionCache.h" -#include "llvm/ADT/DenseSet.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/IntrinsicInst.h" @@ -74,10 +75,45 @@ using MapKey = std::pair; SmallDenseMap AssumedKnowledgeMap; Instruction *InsertBeforeInstruction = nullptr; + AssumptionCache* AC = nullptr; + DominatorTree* DT = nullptr; - AssumeBuilderState(Module *M) : M(M) {} + AssumeBuilderState(Module *M, Instruction *I = nullptr, + AssumptionCache *AC = nullptr, DominatorTree *DT = nullptr) + : M(M), InsertBeforeInstruction(I), AC(AC), DT(DT) {} + + bool tryToPreserveWithoutAddingAssume(RetainedKnowledge RK) { + if (!InsertBeforeInstruction || !AC || !RK.WasOn) + return false; + bool HasBeenPreserved = false; + Use* ToUpdate = nullptr; + getKnowledgeForValue( + RK.WasOn, {RK.AttrKind}, AC, + [&](RetainedKnowledge RKOther, Instruction *Assume, + const CallInst::BundleOpInfo *Bundle) { + if (!isValidAssumeForContext(Assume, InsertBeforeInstruction, DT)) + return false; + if (RKOther.ArgValue >= RK.ArgValue) { + HasBeenPreserved = true; + return true; + } else if (isValidAssumeForContext(InsertBeforeInstruction, Assume, + DT)) { + HasBeenPreserved = true; + IntrinsicInst *Intr = cast(Assume); + ToUpdate = &Intr->op_begin()[Bundle->Begin + ABA_Argument]; + return true; + } + return false; + }); + if (ToUpdate) + ToUpdate->set( + ConstantInt::get(Type::getInt64Ty(M->getContext()), RK.ArgValue)); + return HasBeenPreserved; + } void addKnowledge(RetainedKnowledge RK) { + if (tryToPreserveWithoutAddingAssume(RK)) + return; MapKey Key{RK.WasOn, RK.AttrKind}; auto Lookup = AssumedKnowledgeMap.find(Key); if (Lookup == AssumedKnowledgeMap.end()) { @@ -185,11 +221,10 @@ return Builder.build(); } -void llvm::salvageKnowledge(Instruction *I, AssumptionCache *AC) { +void llvm::salvageKnowledge(Instruction *I, AssumptionCache *AC, DominatorTree* DT) { if (!EnableKnowledgeRetention) return; - AssumeBuilderState Builder(I->getModule()); - Builder.InsertBeforeInstruction = I; + AssumeBuilderState Builder(I->getModule(), I, AC, DT); Builder.addInstruction(I); if (IntrinsicInst *Intr = Builder.build()) { Intr->insertBefore(I); @@ -200,8 +235,9 @@ PreservedAnalyses AssumeBuilderPass::run(Function &F, FunctionAnalysisManager &AM) { + AssumptionCache* AC = AM.getCachedResult(F); + DominatorTree* DT = AM.getCachedResult(F); for (Instruction &I : instructions(F)) - if (Instruction *Assume = buildAssumeFromInst(&I)) - Assume->insertBefore(&I); + salvageKnowledge(&I, AC, DT); return PreservedAnalyses::all(); } diff --git a/llvm/test/Analysis/BasicAA/featuretest.ll b/llvm/test/Analysis/BasicAA/featuretest.ll --- a/llvm/test/Analysis/BasicAA/featuretest.ll +++ b/llvm/test/Analysis/BasicAA/featuretest.ll @@ -36,7 +36,6 @@ ; USE_ASSUME-NEXT: call void @external(i32* nonnull [[ARRAY11_SUB]]) ; USE_ASSUME-NEXT: call void @external(i32* nonnull [[ARRAY22_SUB]]) ; USE_ASSUME-NEXT: [[POINTER:%.*]] = getelementptr [100 x i32], [100 x i32]* [[ARRAY11]], i64 0, i64 [[A:%.*]] -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[POINTER]], i64 4), "nonnull"(i32* [[POINTER]]) ] ; USE_ASSUME-NEXT: [[POINTER2:%.*]] = getelementptr [200 x i32], [200 x i32]* [[ARRAY22]], i64 0, i64 [[B:%.*]] ; USE_ASSUME-NEXT: store i32 7, i32* [[POINTER2]], align 4 ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[POINTER]], i64 4), "nonnull"(i32* [[POINTER]]) ] @@ -78,7 +77,6 @@ ; USE_ASSUME-NEXT: call void @external(i32* nonnull [[ARRAY1_SUB]]) ; USE_ASSUME-NEXT: [[P1:%.*]] = getelementptr inbounds [100 x i32], [100 x i32]* [[ARRAY1]], i64 0, i64 7 ; USE_ASSUME-NEXT: [[P2:%.*]] = getelementptr inbounds [100 x i32], [100 x i32]* [[ARRAY1]], i64 0, i64 6 -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] ; USE_ASSUME-NEXT: store i32 1, i32* [[P2]], align 4 ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] ; USE_ASSUME-NEXT: ret i32 0 @@ -105,8 +103,7 @@ ; NO_ASSUME-NEXT: ret i32 0 ; ; USE_ASSUME-LABEL: @gep_distance_test( -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[A:%.*]], i64 4), "nonnull"(i32* [[A]]) ] -; USE_ASSUME-NEXT: [[B:%.*]] = getelementptr i32, i32* [[A]], i64 2 +; USE_ASSUME-NEXT: [[B:%.*]] = getelementptr i32, i32* [[A:%.*]], i64 2 ; USE_ASSUME-NEXT: store i32 7, i32* [[B]], align 4 ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[A]], i64 4), "nonnull"(i32* [[A]]) ] ; USE_ASSUME-NEXT: ret i32 0 @@ -129,7 +126,6 @@ ; ; USE_ASSUME-LABEL: @gep_distance_test2( ; USE_ASSUME-NEXT: [[A1:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[A:%.*]], i64 0, i32 0 -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[A1]], i64 4), "nonnull"(i32* [[A1]]) ] ; USE_ASSUME-NEXT: [[B:%.*]] = getelementptr { i32, i32 }, { i32, i32 }* [[A]], i64 [[DISTANCE:%.*]], i32 1 ; USE_ASSUME-NEXT: store i32 7, i32* [[B]], align 4 ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[A1]], i64 4), "nonnull"(i32* [[A1]]) ] @@ -154,8 +150,7 @@ ; NO_ASSUME-NEXT: ret i32 0 ; ; USE_ASSUME-LABEL: @gep_distance_test3( -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[A:%.*]], i64 4), "nonnull"(i32* [[A]]) ] -; USE_ASSUME-NEXT: [[C1:%.*]] = getelementptr i32, i32* [[A]], i64 1 +; USE_ASSUME-NEXT: [[C1:%.*]] = getelementptr i32, i32* [[A:%.*]], i64 1 ; USE_ASSUME-NEXT: [[C:%.*]] = bitcast i32* [[C1]] to i8* ; USE_ASSUME-NEXT: store i8 42, i8* [[C]], align 1 ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[A]], i64 4), "nonnull"(i32* [[A]]) ] @@ -181,7 +176,6 @@ ; USE_ASSUME-LABEL: @constexpr_test( ; USE_ASSUME-NEXT: [[X:%.*]] = alloca i32, align 4 ; USE_ASSUME-NEXT: call void @external(i32* nonnull [[X]]) -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[X]], i64 4), "nonnull"(i32* [[X]]) ] ; USE_ASSUME-NEXT: store i32 5, i32* getelementptr inbounds ({ i32 }, { i32 }* @Global, i64 0, i32 0), align 4 ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[X]], i64 4), "nonnull"(i32* [[X]]) ] ; USE_ASSUME-NEXT: ret i32 0 diff --git a/llvm/test/Transforms/EarlyCSE/guards.ll b/llvm/test/Transforms/EarlyCSE/guards.ll --- a/llvm/test/Transforms/EarlyCSE/guards.ll +++ b/llvm/test/Transforms/EarlyCSE/guards.ll @@ -229,7 +229,6 @@ ; USE_ASSUME-NEXT: store i32 100, i32* [[PTR:%.*]] ; USE_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ] ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] ; USE_ASSUME-NEXT: store i32 400, i32* [[PTR]] ; USE_ASSUME-NEXT: ret void ; @@ -271,7 +270,6 @@ ; USE_ASSUME-NEXT: store i32 100, i32* [[PTR:%.*]] ; USE_ASSUME-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[CMP]]) [ "deopt"() ] ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] ; USE_ASSUME-NEXT: store i32 400, i32* [[PTR]] ; USE_ASSUME-NEXT: br i1 [[C:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] ; USE_ASSUME: if.true: @@ -410,8 +408,6 @@ ; USE_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] ; USE_ASSUME-NEXT: call void @llvm.assume(i1 [[CMP]]) ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR:%.*]], i64 4), "nonnull"(i32* [[PTR]]) ] -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] ; USE_ASSUME-NEXT: store i32 400, i32* [[PTR]] ; USE_ASSUME-NEXT: ret void ; @@ -452,8 +448,6 @@ ; USE_ASSUME-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]] ; USE_ASSUME-NEXT: call void @llvm.assume(i1 [[CMP]]) ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR:%.*]], i64 4), "nonnull"(i32* [[PTR]]) ] -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] ; USE_ASSUME-NEXT: store i32 400, i32* [[PTR]] ; USE_ASSUME-NEXT: br i1 [[C:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]] ; USE_ASSUME: if.true: diff --git a/llvm/test/Transforms/EarlyCSE/invariant-loads.ll b/llvm/test/Transforms/EarlyCSE/invariant-loads.ll --- a/llvm/test/Transforms/EarlyCSE/invariant-loads.ll +++ b/llvm/test/Transforms/EarlyCSE/invariant-loads.ll @@ -18,7 +18,6 @@ ; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] ; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[PTR]], i64 4), "nonnull"(i32* [[PTR]]) ] ; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[VAL0]]) ; USE_ASSUME-NEXT: ret void ; @@ -194,7 +193,6 @@ ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] ; USE_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]] ; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]]) -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] ; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) ; USE_ASSUME-NEXT: ret void ; @@ -225,7 +223,6 @@ ; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] ; USE_ASSUME-NEXT: [[ADD:%.*]] = add i32 [[V1]], [[V1]] ; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[ADD]]) -; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] ; USE_ASSUME-NEXT: call void @clobber_and_use(i32 [[V1]]) ; USE_ASSUME-NEXT: ret void ; diff --git a/llvm/test/Transforms/Util/assume-builder.ll b/llvm/test/Transforms/Util/assume-builder.ll --- a/llvm/test/Transforms/Util/assume-builder.ll +++ b/llvm/test/Transforms/Util/assume-builder.ll @@ -1,6 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention -S %s | FileCheck %s --check-prefixes=BASIC ; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --assume-preserve-all -S %s | FileCheck %s --check-prefixes=ALL +; RUN: opt -passes='require,assume-builder,verify' --enable-knowledge-retention -S %s | FileCheck %s --check-prefixes=WITH-AC +; RUN: opt -passes='require,require,assume-builder,verify' --enable-knowledge-retention -S %s | FileCheck %s --check-prefixes=CROSS-BLOCK target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" @@ -22,8 +24,8 @@ ; BASIC-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) ; BASIC-NEXT: call void @func(i32* [[P1]], i32* [[P]]) ; BASIC-NEXT: call void @func_strbool(i32* [[P1]]) -; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 16) ] -; BASIC-NEXT: call void @func(i32* dereferenceable(16) [[P]], i32* dereferenceable(8) [[P]]) +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 32) ] +; BASIC-NEXT: call void @func(i32* dereferenceable(32) [[P]], i32* dereferenceable(8) [[P]]) ; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8) ] ; BASIC-NEXT: call void @func_many(i32* align 8 [[P1]]) ; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P2:%.*]], i64 8), "nonnull"(i32* [[P3:%.*]]) ] @@ -43,8 +45,8 @@ ; ALL-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) ; ALL-NEXT: call void @func(i32* [[P1]], i32* [[P]]) ; ALL-NEXT: call void @func_strbool(i32* [[P1]]) -; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 16) ] -; ALL-NEXT: call void @func(i32* dereferenceable(16) [[P]], i32* dereferenceable(8) [[P]]) +; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 32) ] +; ALL-NEXT: call void @func(i32* dereferenceable(32) [[P]], i32* dereferenceable(8) [[P]]) ; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8), "norecurse"(), "nounwind"(), "willreturn"() ] ; ALL-NEXT: call void @func_many(i32* align 8 [[P1]]) ; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P2:%.*]], i64 8), "nonnull"(i32* [[P3:%.*]]), "nounwind"() ] @@ -53,13 +55,56 @@ ; ALL-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]]) ; ALL-NEXT: ret void ; +; WITH-AC-LABEL: @test( +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P:%.*]], i64 16), "nonnull"(i32* [[P]]) ] +; WITH-AC-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null) +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1:%.*]], i64 12) ] +; WITH-AC-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "cold"() ] +; WITH-AC-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #0 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "cold"() ] +; WITH-AC-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) +; WITH-AC-NEXT: call void @func(i32* [[P1]], i32* [[P]]) +; WITH-AC-NEXT: call void @func_strbool(i32* [[P1]]) +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 32) ] +; WITH-AC-NEXT: call void @func(i32* dereferenceable(32) [[P]], i32* dereferenceable(8) [[P]]) +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8) ] +; WITH-AC-NEXT: call void @func_many(i32* align 8 [[P1]]) +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P2:%.*]], i64 8), "nonnull"(i32* [[P3:%.*]]) ] +; WITH-AC-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]]) +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[P1]]) ] +; WITH-AC-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]]) +; WITH-AC-NEXT: ret void +; +; CROSS-BLOCK-LABEL: @test( +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P:%.*]], i64 16), "nonnull"(i32* [[P]]) ] +; CROSS-BLOCK-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null) +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1:%.*]], i64 12) ] +; CROSS-BLOCK-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "cold"() ] +; CROSS-BLOCK-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #0 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "cold"() ] +; CROSS-BLOCK-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) +; CROSS-BLOCK-NEXT: call void @func(i32* [[P1]], i32* [[P]]) +; CROSS-BLOCK-NEXT: call void @func_strbool(i32* [[P1]]) +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 32) ] +; CROSS-BLOCK-NEXT: call void @func(i32* dereferenceable(32) [[P]], i32* dereferenceable(8) [[P]]) +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8) ] +; CROSS-BLOCK-NEXT: call void @func_many(i32* align 8 [[P1]]) +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P2:%.*]], i64 8), "nonnull"(i32* [[P3:%.*]]) ] +; CROSS-BLOCK-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]]) +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[P1]]) ] +; CROSS-BLOCK-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]]) +; CROSS-BLOCK-NEXT: ret void +; + call void @func(i32* nonnull dereferenceable(16) %P, i32* null) call void @func(i32* dereferenceable(12) %P1, i32* nonnull %P) call void @func_cold(i32* dereferenceable(12) %P1) cold call void @func_cold(i32* dereferenceable(12) %P1) call void @func(i32* %P1, i32* %P) call void @func_strbool(i32* %P1) - call void @func(i32* dereferenceable(16) %P, i32* dereferenceable(8) %P) + call void @func(i32* dereferenceable(32) %P, i32* dereferenceable(8) %P) call void @func_many(i32* align 8 %P1) call void @func_argattr(i32* %P2, i32* %P3) call void @func(i32* nonnull %P1, i32* nonnull %P) @@ -163,6 +208,90 @@ ; ALL-NEXT: [[TMP28:%.*]] = add nsw i32 [[TMP23]], [[TMP27]] ; ALL-NEXT: ret i32 [[TMP28]] ; +; WITH-AC-LABEL: @test2( +; WITH-AC-NEXT: [[TMP4:%.*]] = alloca %struct.S*, align 8 +; WITH-AC-NEXT: [[TMP5:%.*]] = alloca i32*, align 8 +; WITH-AC-NEXT: [[TMP6:%.*]] = alloca i8*, align 8 +; WITH-AC-NEXT: [[TMP7:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 8), "dereferenceable"(%struct.S** [[TMP4]], i64 8), "nonnull"(%struct.S** [[TMP4]]) ] +; WITH-AC-NEXT: store %struct.S* [[TMP0:%.*]], %struct.S** [[TMP4]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32** [[TMP5]], i64 8), "dereferenceable"(i32** [[TMP5]], i64 8), "nonnull"(i32** [[TMP5]]) ] +; WITH-AC-NEXT: store i32* [[TMP1:%.*]], i32** [[TMP5]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8** [[TMP6]], i64 8), "nonnull"(i8** [[TMP6]]) ] +; WITH-AC-NEXT: store i8* [[TMP2:%.*]], i8** [[TMP6]] +; WITH-AC-NEXT: [[TMP8:%.*]] = load i32*, i32** [[TMP5]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP8]], i64 4), "dereferenceable"(i32* [[TMP8]], i64 4), "nonnull"(i32* [[TMP8]]) ] +; WITH-AC-NEXT: [[TMP9:%.*]] = load i32, i32* [[TMP8]], align 4 +; WITH-AC-NEXT: [[TMP10:%.*]] = trunc i32 [[TMP9]] to i8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i8** [[TMP6]], i64 8) ] +; WITH-AC-NEXT: [[TMP11:%.*]] = load i8*, i8** [[TMP6]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* [[TMP11]], i64 1), "nonnull"(i8* [[TMP11]]) ] +; WITH-AC-NEXT: store i8 [[TMP10]], i8* [[TMP11]], align 1 +; WITH-AC-NEXT: [[TMP12:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; WITH-AC-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]] +; WITH-AC-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* +; WITH-AC-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; WITH-AC-NEXT: [[TMP16:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; WITH-AC-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP16]], i32 0, i32 0 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP17]], i64 8), "dereferenceable"(i32* [[TMP17]], i64 4), "nonnull"(i32* [[TMP17]]) ] +; WITH-AC-NEXT: [[TMP18:%.*]] = load i32, i32* [[TMP17]], align 8 +; WITH-AC-NEXT: [[TMP19:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; WITH-AC-NEXT: [[TMP20:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP19]], i32 0, i32 1 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i8* [[TMP20]], i64 4), "dereferenceable"(i8* [[TMP20]], i64 1), "nonnull"(i8* [[TMP20]]) ] +; WITH-AC-NEXT: [[TMP21:%.*]] = load i8, i8* [[TMP20]], align 4 +; WITH-AC-NEXT: [[TMP22:%.*]] = sext i8 [[TMP21]] to i32 +; WITH-AC-NEXT: [[TMP23:%.*]] = add nsw i32 [[TMP18]], [[TMP22]] +; WITH-AC-NEXT: [[TMP24:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; WITH-AC-NEXT: [[TMP25:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP24]], i32 0, i32 2 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32** [[TMP25]], i64 8), "dereferenceable"(i32** [[TMP25]], i64 8), "nonnull"(i32** [[TMP25]]) ] +; WITH-AC-NEXT: [[TMP26:%.*]] = load i32*, i32** [[TMP25]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP26]], i64 4), "dereferenceable"(i32* [[TMP26]], i64 4), "nonnull"(i32* [[TMP26]]) ] +; WITH-AC-NEXT: [[TMP27:%.*]] = load i32, i32* [[TMP26]], align 4 +; WITH-AC-NEXT: [[TMP28:%.*]] = add nsw i32 [[TMP23]], [[TMP27]] +; WITH-AC-NEXT: ret i32 [[TMP28]] +; +; CROSS-BLOCK-LABEL: @test2( +; CROSS-BLOCK-NEXT: [[TMP4:%.*]] = alloca %struct.S*, align 8 +; CROSS-BLOCK-NEXT: [[TMP5:%.*]] = alloca i32*, align 8 +; CROSS-BLOCK-NEXT: [[TMP6:%.*]] = alloca i8*, align 8 +; CROSS-BLOCK-NEXT: [[TMP7:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 8), "dereferenceable"(%struct.S** [[TMP4]], i64 8), "nonnull"(%struct.S** [[TMP4]]) ] +; CROSS-BLOCK-NEXT: store %struct.S* [[TMP0:%.*]], %struct.S** [[TMP4]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32** [[TMP5]], i64 8), "dereferenceable"(i32** [[TMP5]], i64 8), "nonnull"(i32** [[TMP5]]) ] +; CROSS-BLOCK-NEXT: store i32* [[TMP1:%.*]], i32** [[TMP5]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8** [[TMP6]], i64 8), "nonnull"(i8** [[TMP6]]) ] +; CROSS-BLOCK-NEXT: store i8* [[TMP2:%.*]], i8** [[TMP6]] +; CROSS-BLOCK-NEXT: [[TMP8:%.*]] = load i32*, i32** [[TMP5]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP8]], i64 4), "dereferenceable"(i32* [[TMP8]], i64 4), "nonnull"(i32* [[TMP8]]) ] +; CROSS-BLOCK-NEXT: [[TMP9:%.*]] = load i32, i32* [[TMP8]], align 4 +; CROSS-BLOCK-NEXT: [[TMP10:%.*]] = trunc i32 [[TMP9]] to i8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i8** [[TMP6]], i64 8) ] +; CROSS-BLOCK-NEXT: [[TMP11:%.*]] = load i8*, i8** [[TMP6]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* [[TMP11]], i64 1), "nonnull"(i8* [[TMP11]]) ] +; CROSS-BLOCK-NEXT: store i8 [[TMP10]], i8* [[TMP11]], align 1 +; CROSS-BLOCK-NEXT: [[TMP12:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; CROSS-BLOCK-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]] +; CROSS-BLOCK-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* +; CROSS-BLOCK-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; CROSS-BLOCK-NEXT: [[TMP16:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; CROSS-BLOCK-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP16]], i32 0, i32 0 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP17]], i64 8), "dereferenceable"(i32* [[TMP17]], i64 4), "nonnull"(i32* [[TMP17]]) ] +; CROSS-BLOCK-NEXT: [[TMP18:%.*]] = load i32, i32* [[TMP17]], align 8 +; CROSS-BLOCK-NEXT: [[TMP19:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; CROSS-BLOCK-NEXT: [[TMP20:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP19]], i32 0, i32 1 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i8* [[TMP20]], i64 4), "dereferenceable"(i8* [[TMP20]], i64 1), "nonnull"(i8* [[TMP20]]) ] +; CROSS-BLOCK-NEXT: [[TMP21:%.*]] = load i8, i8* [[TMP20]], align 4 +; CROSS-BLOCK-NEXT: [[TMP22:%.*]] = sext i8 [[TMP21]] to i32 +; CROSS-BLOCK-NEXT: [[TMP23:%.*]] = add nsw i32 [[TMP18]], [[TMP22]] +; CROSS-BLOCK-NEXT: [[TMP24:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; CROSS-BLOCK-NEXT: [[TMP25:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP24]], i32 0, i32 2 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32** [[TMP25]], i64 8), "dereferenceable"(i32** [[TMP25]], i64 8), "nonnull"(i32** [[TMP25]]) ] +; CROSS-BLOCK-NEXT: [[TMP26:%.*]] = load i32*, i32** [[TMP25]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP26]], i64 4), "dereferenceable"(i32* [[TMP26]], i64 4), "nonnull"(i32* [[TMP26]]) ] +; CROSS-BLOCK-NEXT: [[TMP27:%.*]] = load i32, i32* [[TMP26]], align 4 +; CROSS-BLOCK-NEXT: [[TMP28:%.*]] = add nsw i32 [[TMP23]], [[TMP27]] +; CROSS-BLOCK-NEXT: ret i32 [[TMP28]] +; %4 = alloca %struct.S*, align 8 %5 = alloca i32*, align 8 %6 = alloca i8*, align 8 @@ -217,8 +346,8 @@ ; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* [[TMP11]], i64 1) ] ; BASIC-NEXT: store i8 [[TMP10]], i8* [[TMP11]], align 1 ; BASIC-NEXT: [[TMP12:%.*]] = bitcast %struct.S* [[TMP7]] to i8* -; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 8), "dereferenceable"(%struct.S** [[TMP4]], i64 8) ] -; BASIC-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 32), "dereferenceable"(%struct.S** [[TMP4]], i64 8) ] +; BASIC-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 32 ; BASIC-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* ; BASIC-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* ; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 8), "dereferenceable"(%struct.S** [[TMP4]], i64 8) ] @@ -264,8 +393,8 @@ ; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* [[TMP11]], i64 1) ] ; ALL-NEXT: store i8 [[TMP10]], i8* [[TMP11]], align 1 ; ALL-NEXT: [[TMP12:%.*]] = bitcast %struct.S* [[TMP7]] to i8* -; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 8), "dereferenceable"(%struct.S** [[TMP4]], i64 8) ] -; ALL-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 32), "dereferenceable"(%struct.S** [[TMP4]], i64 8) ] +; ALL-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 32 ; ALL-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* ; ALL-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* ; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 8), "dereferenceable"(%struct.S** [[TMP4]], i64 8) ] @@ -290,6 +419,88 @@ ; ALL-NEXT: [[TMP28:%.*]] = add nsw i32 [[TMP23]], [[TMP27]] ; ALL-NEXT: ret i32 [[TMP28]] ; +; WITH-AC-LABEL: @test3( +; WITH-AC-NEXT: [[TMP4:%.*]] = alloca %struct.S*, align 8 +; WITH-AC-NEXT: [[TMP5:%.*]] = alloca i32*, align 8 +; WITH-AC-NEXT: [[TMP6:%.*]] = alloca i8*, align 8 +; WITH-AC-NEXT: [[TMP7:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 32), "dereferenceable"(%struct.S** [[TMP4]], i64 8) ] +; WITH-AC-NEXT: store %struct.S* [[TMP0:%.*]], %struct.S** [[TMP4]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32** [[TMP5]], i64 8), "dereferenceable"(i32** [[TMP5]], i64 8) ] +; WITH-AC-NEXT: store i32* [[TMP1:%.*]], i32** [[TMP5]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i8** [[TMP6]], i64 8), "dereferenceable"(i8** [[TMP6]], i64 8) ] +; WITH-AC-NEXT: store i8* [[TMP2:%.*]], i8** [[TMP6]], align 8 +; WITH-AC-NEXT: [[TMP8:%.*]] = load i32*, i32** [[TMP5]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP8]], i64 4), "dereferenceable"(i32* [[TMP8]], i64 4) ] +; WITH-AC-NEXT: [[TMP9:%.*]] = load i32, i32* [[TMP8]], align 4 +; WITH-AC-NEXT: [[TMP10:%.*]] = trunc i32 [[TMP9]] to i8 +; WITH-AC-NEXT: [[TMP11:%.*]] = load i8*, i8** [[TMP6]] +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* [[TMP11]], i64 1) ] +; WITH-AC-NEXT: store i8 [[TMP10]], i8* [[TMP11]], align 1 +; WITH-AC-NEXT: [[TMP12:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; WITH-AC-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 32 +; WITH-AC-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* +; WITH-AC-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; WITH-AC-NEXT: [[TMP16:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; WITH-AC-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP16]], i32 0, i32 0 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP17]], i64 8), "dereferenceable"(i32* [[TMP17]], i64 4) ] +; WITH-AC-NEXT: [[TMP18:%.*]] = load i32, i32* [[TMP17]], align 8 +; WITH-AC-NEXT: [[TMP19:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; WITH-AC-NEXT: [[TMP20:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP19]], i32 0, i32 1 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i8* [[TMP20]], i64 4), "dereferenceable"(i8* [[TMP20]], i64 1) ] +; WITH-AC-NEXT: [[TMP21:%.*]] = load i8, i8* [[TMP20]], align 4 +; WITH-AC-NEXT: [[TMP22:%.*]] = sext i8 [[TMP21]] to i32 +; WITH-AC-NEXT: [[TMP23:%.*]] = add nsw i32 [[TMP18]], [[TMP22]] +; WITH-AC-NEXT: [[TMP24:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; WITH-AC-NEXT: [[TMP25:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP24]], i32 0, i32 2 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32** [[TMP25]], i64 8) ] +; WITH-AC-NEXT: [[TMP26:%.*]] = load i32*, i32** [[TMP25]] +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP26]], i64 4), "dereferenceable"(i32* [[TMP26]], i64 4) ] +; WITH-AC-NEXT: [[TMP27:%.*]] = load i32, i32* [[TMP26]], align 4 +; WITH-AC-NEXT: [[TMP28:%.*]] = add nsw i32 [[TMP23]], [[TMP27]] +; WITH-AC-NEXT: ret i32 [[TMP28]] +; +; CROSS-BLOCK-LABEL: @test3( +; CROSS-BLOCK-NEXT: [[TMP4:%.*]] = alloca %struct.S*, align 8 +; CROSS-BLOCK-NEXT: [[TMP5:%.*]] = alloca i32*, align 8 +; CROSS-BLOCK-NEXT: [[TMP6:%.*]] = alloca i8*, align 8 +; CROSS-BLOCK-NEXT: [[TMP7:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(%struct.S** [[TMP4]], i64 32), "dereferenceable"(%struct.S** [[TMP4]], i64 8) ] +; CROSS-BLOCK-NEXT: store %struct.S* [[TMP0:%.*]], %struct.S** [[TMP4]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32** [[TMP5]], i64 8), "dereferenceable"(i32** [[TMP5]], i64 8) ] +; CROSS-BLOCK-NEXT: store i32* [[TMP1:%.*]], i32** [[TMP5]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i8** [[TMP6]], i64 8), "dereferenceable"(i8** [[TMP6]], i64 8) ] +; CROSS-BLOCK-NEXT: store i8* [[TMP2:%.*]], i8** [[TMP6]], align 8 +; CROSS-BLOCK-NEXT: [[TMP8:%.*]] = load i32*, i32** [[TMP5]], align 8 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP8]], i64 4), "dereferenceable"(i32* [[TMP8]], i64 4) ] +; CROSS-BLOCK-NEXT: [[TMP9:%.*]] = load i32, i32* [[TMP8]], align 4 +; CROSS-BLOCK-NEXT: [[TMP10:%.*]] = trunc i32 [[TMP9]] to i8 +; CROSS-BLOCK-NEXT: [[TMP11:%.*]] = load i8*, i8** [[TMP6]] +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* [[TMP11]], i64 1) ] +; CROSS-BLOCK-NEXT: store i8 [[TMP10]], i8* [[TMP11]], align 1 +; CROSS-BLOCK-NEXT: [[TMP12:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; CROSS-BLOCK-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 32 +; CROSS-BLOCK-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* +; CROSS-BLOCK-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; CROSS-BLOCK-NEXT: [[TMP16:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; CROSS-BLOCK-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP16]], i32 0, i32 0 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP17]], i64 8), "dereferenceable"(i32* [[TMP17]], i64 4) ] +; CROSS-BLOCK-NEXT: [[TMP18:%.*]] = load i32, i32* [[TMP17]], align 8 +; CROSS-BLOCK-NEXT: [[TMP19:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; CROSS-BLOCK-NEXT: [[TMP20:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP19]], i32 0, i32 1 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i8* [[TMP20]], i64 4), "dereferenceable"(i8* [[TMP20]], i64 1) ] +; CROSS-BLOCK-NEXT: [[TMP21:%.*]] = load i8, i8* [[TMP20]], align 4 +; CROSS-BLOCK-NEXT: [[TMP22:%.*]] = sext i8 [[TMP21]] to i32 +; CROSS-BLOCK-NEXT: [[TMP23:%.*]] = add nsw i32 [[TMP18]], [[TMP22]] +; CROSS-BLOCK-NEXT: [[TMP24:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; CROSS-BLOCK-NEXT: [[TMP25:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP24]], i32 0, i32 2 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32** [[TMP25]], i64 8) ] +; CROSS-BLOCK-NEXT: [[TMP26:%.*]] = load i32*, i32** [[TMP25]] +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP26]], i64 4), "dereferenceable"(i32* [[TMP26]], i64 4) ] +; CROSS-BLOCK-NEXT: [[TMP27:%.*]] = load i32, i32* [[TMP26]], align 4 +; CROSS-BLOCK-NEXT: [[TMP28:%.*]] = add nsw i32 [[TMP23]], [[TMP27]] +; CROSS-BLOCK-NEXT: ret i32 [[TMP28]] +; %4 = alloca %struct.S*, align 8 %5 = alloca i32*, align 8 %6 = alloca i8*, align 8 @@ -303,7 +514,7 @@ %11 = load i8*, i8** %6 store i8 %10, i8* %11, align 1 %12 = bitcast %struct.S* %7 to i8* - %13 = load %struct.S*, %struct.S** %4, align 8 + %13 = load %struct.S*, %struct.S** %4, align 32 %14 = bitcast %struct.S* %13 to i8* %15 = bitcast %struct.S* %7 to i8* %16 = load %struct.S*, %struct.S** %4, align 8 @@ -321,3 +532,117 @@ %28 = add nsw i32 %23, %27 ret i32 %28 } + +define dso_local i32 @_Z6squarePi(i32* %P, i32* %P1, i1 %cond) { +; BASIC-LABEL: @_Z6squarePi( +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P:%.*]], i64 4), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; BASIC-NEXT: store i32 0, i32* [[P]], align 4 +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1:%.*]], i64 8), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; BASIC-NEXT: store i32 0, i32* [[P1]], align 8 +; BASIC-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]] +; BASIC: A: +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 8), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; BASIC-NEXT: store i32 0, i32* [[P]], align 8 +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 4), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; BASIC-NEXT: store i32 0, i32* [[P1]], align 4 +; BASIC-NEXT: br i1 [[COND]], label [[C:%.*]], label [[B]] +; BASIC: B: +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 8), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; BASIC-NEXT: store i32 0, i32* [[P]], align 8 +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; BASIC-NEXT: store i32 0, i32* [[P1]], align 8 +; BASIC-NEXT: br label [[C]] +; BASIC: C: +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 32), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; BASIC-NEXT: store i32 0, i32* [[P]], align 32 +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 4), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; BASIC-NEXT: store i32 0, i32* [[P1]], align 4 +; BASIC-NEXT: ret i32 0 +; +; ALL-LABEL: @_Z6squarePi( +; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P:%.*]], i64 4), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; ALL-NEXT: store i32 0, i32* [[P]], align 4 +; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1:%.*]], i64 8), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; ALL-NEXT: store i32 0, i32* [[P1]], align 8 +; ALL-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]] +; ALL: A: +; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 8), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; ALL-NEXT: store i32 0, i32* [[P]], align 8 +; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 4), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; ALL-NEXT: store i32 0, i32* [[P1]], align 4 +; ALL-NEXT: br i1 [[COND]], label [[C:%.*]], label [[B]] +; ALL: B: +; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 8), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; ALL-NEXT: store i32 0, i32* [[P]], align 8 +; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; ALL-NEXT: store i32 0, i32* [[P1]], align 8 +; ALL-NEXT: br label [[C]] +; ALL: C: +; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 32), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; ALL-NEXT: store i32 0, i32* [[P]], align 32 +; ALL-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 4), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; ALL-NEXT: store i32 0, i32* [[P1]], align 4 +; ALL-NEXT: ret i32 0 +; +; WITH-AC-LABEL: @_Z6squarePi( +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P:%.*]], i64 4), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; WITH-AC-NEXT: store i32 0, i32* [[P]], align 4 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1:%.*]], i64 8), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; WITH-AC-NEXT: store i32 0, i32* [[P1]], align 8 +; WITH-AC-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]] +; WITH-AC: A: +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 8) ] +; WITH-AC-NEXT: store i32 0, i32* [[P]], align 8 +; WITH-AC-NEXT: store i32 0, i32* [[P1]], align 4 +; WITH-AC-NEXT: br i1 [[COND]], label [[C:%.*]], label [[B]] +; WITH-AC: B: +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 8), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; WITH-AC-NEXT: store i32 0, i32* [[P]], align 8 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; WITH-AC-NEXT: store i32 0, i32* [[P1]], align 8 +; WITH-AC-NEXT: br label [[C]] +; WITH-AC: C: +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 32), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; WITH-AC-NEXT: store i32 0, i32* [[P]], align 32 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 4), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; WITH-AC-NEXT: store i32 0, i32* [[P1]], align 4 +; WITH-AC-NEXT: ret i32 0 +; +; CROSS-BLOCK-LABEL: @_Z6squarePi( +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P:%.*]], i64 4), "dereferenceable"(i32* [[P]], i64 4), "nonnull"(i32* [[P]]) ] +; CROSS-BLOCK-NEXT: store i32 0, i32* [[P]], align 4 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1:%.*]], i64 8), "dereferenceable"(i32* [[P1]], i64 4), "nonnull"(i32* [[P1]]) ] +; CROSS-BLOCK-NEXT: store i32 0, i32* [[P1]], align 8 +; CROSS-BLOCK-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]] +; CROSS-BLOCK: A: +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 8) ] +; CROSS-BLOCK-NEXT: store i32 0, i32* [[P]], align 8 +; CROSS-BLOCK-NEXT: store i32 0, i32* [[P1]], align 4 +; CROSS-BLOCK-NEXT: br i1 [[COND]], label [[C:%.*]], label [[B]] +; CROSS-BLOCK: B: +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 8) ] +; CROSS-BLOCK-NEXT: store i32 0, i32* [[P]], align 8 +; CROSS-BLOCK-NEXT: store i32 0, i32* [[P1]], align 8 +; CROSS-BLOCK-NEXT: br label [[C]] +; CROSS-BLOCK: C: +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P]], i64 32) ] +; CROSS-BLOCK-NEXT: store i32 0, i32* [[P]], align 32 +; CROSS-BLOCK-NEXT: store i32 0, i32* [[P1]], align 4 +; CROSS-BLOCK-NEXT: ret i32 0 +; + store i32 0, i32* %P, align 4 + store i32 0, i32* %P1, align 8 + br i1 %cond, label %A, label %B +A: + store i32 0, i32* %P, align 8 + store i32 0, i32* %P1, align 4 + br i1 %cond, label %C, label %B +B: + store i32 0, i32* %P, align 8 + store i32 0, i32* %P1, align 8 + br label %C +C: + store i32 0, i32* %P, align 32 + store i32 0, i32* %P1, align 4 + ret i32 0 +}