[AssumeBundles] Prevent generation of some redundant assumes

Summary: with this patch the assume salvageKnowledge will not generate assume if all knowledge is already available in an assume with valid context. assume bulider can also in some cases update an existing assume with better information.

Reviewers: jdoerfert

Reviewed By: jdoerfert

Subscribers: hiraditya, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D78014
This commit is contained in:
Tyker 2020-05-10 18:20:34 +02:00
parent 7f22ceeaae
commit 821a0f23d8
8 changed files with 418 additions and 49 deletions

View File

@ -142,8 +142,9 @@ RetainedKnowledge getKnowledgeFromUse(const Use *U,
RetainedKnowledge getKnowledgeForValue(
const Value *V, ArrayRef<Attribute::AttrKind> AttrKinds,
AssumptionCache *AC = nullptr,
function_ref<bool(RetainedKnowledge, Instruction *)> Filter =
[](RetainedKnowledge, Instruction *) { return true; });
function_ref<bool(RetainedKnowledge, Instruction *,
const CallBase::BundleOpInfo *)>
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

View File

@ -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 @@ IntrinsicInst *buildAssumeFromInst(Instruction *I);
/// 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.

View File

@ -120,25 +120,35 @@ bool llvm::isAssumeWithEmptyBundle(CallInst &CI) {
});
}
static CallInst::BundleOpInfo *getBundleFromUse(const Use *U) {
auto *Intr = dyn_cast<IntrinsicInst>(U->getUser());
if (!match(U->getUser(),
m_Intrinsic<Intrinsic::assume>(m_Unless(m_Specific(U->get())))))
return nullptr;
return &Intr->getBundleOpInfoForOperand(U->getOperandNo());
}
RetainedKnowledge
llvm::getKnowledgeFromUse(const Use *U,
ArrayRef<Attribute::AttrKind> AttrKinds) {
if (!match(U->getUser(),
m_Intrinsic<Intrinsic::assume>(m_Unless(m_Specific(U->get())))))
CallInst::BundleOpInfo* Bundle = getBundleFromUse(U);
if (!Bundle)
return RetainedKnowledge::none();
auto *Intr = cast<IntrinsicInst>(U->getUser());
RetainedKnowledge RK =
getKnowledgeFromOperandInAssume(*Intr, U->getOperandNo());
getKnowledgeFromBundle(*cast<CallInst>(U->getUser()), *Bundle);
for (auto Attr : AttrKinds)
if (Attr == RK.AttrKind)
return RK;
return RetainedKnowledge::none();
}
RetainedKnowledge llvm::getKnowledgeForValue(
const Value *V, ArrayRef<Attribute::AttrKind> AttrKinds,
AssumptionCache *AC,
function_ref<bool(RetainedKnowledge, Instruction *)> Filter) {
RetainedKnowledge
llvm::getKnowledgeForValue(const Value *V,
ArrayRef<Attribute::AttrKind> AttrKinds,
AssumptionCache *AC,
function_ref<bool(RetainedKnowledge, Instruction *,
const CallBase::BundleOpInfo *)>
Filter) {
if (AC) {
#ifndef NDEBUG
RetainedKnowledge RKCheck =
@ -150,7 +160,8 @@ RetainedKnowledge llvm::getKnowledgeForValue(
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 @@ RetainedKnowledge llvm::getKnowledgeForValue(
return RetainedKnowledge::none();
}
for (auto &U : V->uses()) {
if (RetainedKnowledge RK = getKnowledgeFromUse(&U, AttrKinds))
if (Filter(RK, cast<Instruction>(U.getUser())))
CallInst::BundleOpInfo* Bundle = getBundleFromUse(&U);
if (!Bundle)
continue;
if (RetainedKnowledge RK =
getKnowledgeFromBundle(*cast<CallInst>(U.getUser()), *Bundle))
if (is_contained(AttrKinds, RK.AttrKind) &&
Filter(RK, cast<Instruction>(U.getUser()), Bundle))
return RK;
}
return RetainedKnowledge::none();
@ -170,7 +186,7 @@ RetainedKnowledge llvm::getKnowledgeValidInContext(
const Value *V, ArrayRef<Attribute::AttrKind> 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);
});
}

View File

@ -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 @@ struct AssumeBuilderState {
using MapKey = std::pair<Value *, Attribute::AttrKind>;
SmallDenseMap<MapKey, unsigned, 8> 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<IntrinsicInst>(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 @@ IntrinsicInst *llvm::buildAssumeFromInst(Instruction *I) {
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 @@ void llvm::salvageKnowledge(Instruction *I, AssumptionCache *AC) {
PreservedAnalyses AssumeBuilderPass::run(Function &F,
FunctionAnalysisManager &AM) {
AssumptionCache* AC = AM.getCachedResult<AssumptionAnalysis>(F);
DominatorTree* DT = AM.getCachedResult<DominatorTreeAnalysis>(F);
for (Instruction &I : instructions(F))
if (Instruction *Assume = buildAssumeFromInst(&I))
Assume->insertBefore(&I);
salvageKnowledge(&I, AC, DT);
return PreservedAnalyses::all();
}

View File

@ -36,7 +36,6 @@ define i32 @different_array_test(i64 %A, i64 %B) {
; 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 @@ define i32 @constant_array_index_test() {
; 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 @@ define i32 @gep_distance_test(i32* %A) {
; 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 @@ define i32 @gep_distance_test2({i32,i32}* %A, i64 %distance) {
;
; 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 @@ define i32 @gep_distance_test3(i32 * %A) {
; 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 @@ define i32 @constexpr_test() {
; 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

View File

@ -229,7 +229,6 @@ define void @test08(i32 %a, i32 %b, i32* %ptr) {
; 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 @@ define void @test09(i32 %a, i32 %b, i1 %c, i32* %ptr) {
; 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 @@ define void @test13(i32 %a, i32 %b, i32* %ptr) {
; 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 @@ define void @test14(i32 %a, i32 %b, i1 %c, i32* %ptr) {
; 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:

View File

@ -18,7 +18,6 @@ define void @f_0(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: 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 @@ define void @test_scope_start_without_load(i32* %p) {
; 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 @@ define void @test_scope_restart(i32* %p) {
; 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
;

View File

@ -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<assumptions>,assume-builder,verify' --enable-knowledge-retention -S %s | FileCheck %s --check-prefixes=WITH-AC
; RUN: opt -passes='require<domtree>,require<assumptions>,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 @@ define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {
; 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 @@ define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {
; 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 @@ define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) {
; 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)
@ -162,6 +207,90 @@ define i32 @test2(%struct.S* %0, i32* %1, i8* %2) {
; ALL-NEXT: [[TMP27:%.*]] = load i32, i32* [[TMP26]], align 4
; 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
@ -217,8 +346,8 @@ define i32 @test3(%struct.S* %0, i32* %1, i8* %2) "null-pointer-is-valid"="true"
; 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 @@ define i32 @test3(%struct.S* %0, i32* %1, i8* %2) "null-pointer-is-valid"="true"
; 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) ]
@ -289,6 +418,88 @@ define i32 @test3(%struct.S* %0, i32* %1, i8* %2) "null-pointer-is-valid"="true"
; ALL-NEXT: [[TMP27:%.*]] = load i32, i32* [[TMP26]], align 4
; 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
@ -303,7 +514,7 @@ define i32 @test3(%struct.S* %0, i32* %1, i8* %2) "null-pointer-is-valid"="true"
%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 @@ define i32 @test3(%struct.S* %0, i32* %1, i8* %2) "null-pointer-is-valid"="true"
%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
}