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 @@ -154,6 +154,11 @@ const Instruction *CtxI, const DominatorTree *DT = nullptr, AssumptionCache *AC = nullptr); +/// This extract the Knowledge from an element of an operand bundle. +/// This is mostly for use in the assume builder. +RetainedKnowledge getKnowledgeFromBundle(CallInst &Assume, + const CallBase::BundleOpInfo &BOI); + } // namespace llvm #endif diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -31,6 +31,7 @@ class Module; class OptPassGate; template class SmallVectorImpl; +template class StringMapEntry; class SMDiagnostic; class StringRef; class Twine; @@ -105,6 +106,10 @@ /// \see LLVMContext::getOperandBundleTagID void getOperandBundleTags(SmallVectorImpl &Result) const; + /// getOrInsertBundleTag - Returns the Tag to use for an operand bundle of + /// name TagName. + StringMapEntry *getOrInsertBundleTag(StringRef TagName) const; + /// getOperandBundleTagID - Maps a bundle tag to an integer ID. Every bundle /// tag registered with an LLVMContext has an unique ID. uint32_t getOperandBundleTagID(StringRef Tag) const; 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 @@ -41,6 +41,12 @@ void salvageKnowledge(Instruction *I, AssumptionCache *AC = nullptr, DominatorTree *DT = nullptr); +/// This pass attempts to minimize the number of assume without loosing any +/// information. +struct AssumeSimplifyPass : public PassInfoMixin { + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + /// This pass will try to build an llvm.assume for every instruction in the /// function. Its main purpose is testing. struct AssumeBuilderPass : public PassInfoMixin { 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 @@ -89,11 +89,13 @@ } } -static RetainedKnowledge -getKnowledgeFromBundle(CallInst &Assume, const CallBase::BundleOpInfo &BOI) { +RetainedKnowledge +llvm::getKnowledgeFromBundle(CallInst &Assume, + const CallBase::BundleOpInfo &BOI) { RetainedKnowledge Result; Result.AttrKind = Attribute::getAttrKindFromName(BOI.Tag->getKey()); - Result.WasOn = getValueFromBundleOpInfo(Assume, BOI, ABA_WasOn); + if (bundleHasArgument(BOI, ABA_WasOn)) + Result.WasOn = getValueFromBundleOpInfo(Assume, BOI, ABA_WasOn); if (BOI.End - BOI.Begin > ABA_Argument) Result.ArgValue = cast(getValueFromBundleOpInfo(Assume, BOI, ABA_Argument)) @@ -134,7 +136,6 @@ CallInst::BundleOpInfo *Bundle = getBundleFromUse(U); if (!Bundle) return RetainedKnowledge::none(); - auto *Intr = cast(U->getUser()); RetainedKnowledge RK = getKnowledgeFromBundle(*cast(U->getUser()), *Bundle); for (auto Attr : AttrKinds) diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp --- a/llvm/lib/IR/LLVMContext.cpp +++ b/llvm/lib/IR/LLVMContext.cpp @@ -277,6 +277,11 @@ pImpl->getOperandBundleTags(Tags); } +StringMapEntry * +LLVMContext::getOrInsertBundleTag(StringRef TagName) const { + return pImpl->getOrInsertBundleTag(TagName); +} + uint32_t LLVMContext::getOperandBundleTagID(StringRef Tag) const { return pImpl->getOperandBundleTagID(Tag); } diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -165,6 +165,7 @@ FUNCTION_PASS("add-discriminators", AddDiscriminatorsPass()) FUNCTION_PASS("aggressive-instcombine", AggressiveInstCombinePass()) FUNCTION_PASS("assume-builder", AssumeBuilderPass()) +FUNCTION_PASS("assume-simplify", AssumeSimplifyPass()) FUNCTION_PASS("alignment-from-assumptions", AlignmentFromAssumptionsPass()) FUNCTION_PASS("bdce", BDCEPass()) FUNCTION_PASS("bounds-checking", BoundsCheckingPass()) 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 @@ -17,6 +17,7 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Utils/Local.h" using namespace llvm; @@ -90,7 +91,7 @@ } void addKnowledge(RetainedKnowledge RK) { - if (tryToPreserveWithoutAddingAssume(RK)) + if (RK.AttrKind == Attribute::None || tryToPreserveWithoutAddingAssume(RK)) return; MapKey Key{RK.WasOn, RK.AttrKind}; auto Lookup = AssumedKnowledgeMap.find(Key); @@ -186,6 +187,7 @@ // TODO: Add support for the other Instructions. // TODO: Maybe we should look around and merge with other llvm.assume. } + void clear() { AssumedKnowledgeMap.clear(); } }; } // namespace @@ -211,6 +213,188 @@ } } +struct AssumeSimplify { + Function &F; + AssumptionCache &AC; + DominatorTree *DT; + LLVMContext &C; + SmallDenseSet CleanupToDo; + StringMapEntry *IgnoreTag; + + AssumeSimplify(Function &F, AssumptionCache &AC, DominatorTree *DT, + LLVMContext &C) + : F(F), AC(AC), DT(DT), C(C), + IgnoreTag(C.getOrInsertBundleTag("ignore")) {} + + void RunCleanup(bool ForceCleanup) { + for (Value *V : CleanupToDo) { + if (!V) + continue; + IntrinsicInst *Assume = cast(V); + auto *Arg = dyn_cast(Assume->getOperand(0)); + if (!Arg || Arg->isZero() || + (!ForceCleanup && !isAssumeWithEmptyBundle(*Assume))) + continue; + Assume->removeFromParent(); + for (CallInst::BundleOpInfo &BOI : Assume->bundle_op_infos()) + if (BOI.Begin != BOI.End) { + auto *I = dyn_cast( + Assume->op_begin()[BOI.Begin + ABA_WasOn].get()); + Assume->op_begin()[BOI.Begin + ABA_WasOn] = nullptr; + if (!I) + continue; + if (isInstructionTriviallyDead(I)) + RecursivelyDeleteTriviallyDeadInstructions(I, nullptr, nullptr, + &AC); + } + delete Assume; + } + CleanupToDo.clear(); + } + + void dropRedundantKnowledge() { + SmallDenseMap, + SmallVector, 2>, 16> + Knowledge; + for (Value *V : AC.assumptions()) { + if (!V) + continue; + IntrinsicInst *Assume = cast(V); + for (CallInst::BundleOpInfo &BOI : Assume->bundle_op_infos()) { + if (BOI.Tag == IgnoreTag) + CleanupToDo.insert(Assume); + RetainedKnowledge RK = getKnowledgeFromBundle(*Assume, BOI); + if (!RK) + continue; + if (auto *Arg = dyn_cast_or_null(RK.WasOn)) + if (Arg->hasAttribute(RK.AttrKind)) { + if (!Attribute::doesAttrKindHaveArgument(RK.AttrKind) || + Arg->getAttribute(RK.AttrKind).getValueAsInt() >= RK.ArgValue) + goto remove; + if (isValidAssumeForContext( + Assume, &*F.getEntryBlock().getFirstInsertionPt())) + goto update; + } + { + auto &Lookup = Knowledge[{RK.WasOn, RK.AttrKind}]; + for (auto &Elem : Lookup) { + if (!isValidAssumeForContext(Elem.first, Assume, DT)) + continue; + if (Elem.second >= RK.ArgValue) + goto remove; + else if (isValidAssumeForContext(Assume, Elem.first, DT)) + goto update; + } + Lookup.push_back({Assume, RK.ArgValue}); + } + continue; + update: + Assume->op_begin()[BOI.Begin + ABA_Argument].set( + ConstantInt::get(Type::getInt64Ty(C), RK.ArgValue)); + remove: + CleanupToDo.insert(Assume); + if (BOI.Begin != BOI.End) { + Use *Arg = &Assume->op_begin()[BOI.Begin + ABA_WasOn]; + Arg->set(UndefValue::get(Arg->get()->getType())); + } + BOI.Tag = IgnoreTag; + } + } + } + + using MergeIterator = SmallVectorImpl::iterator; + + void mergeRange(BasicBlock *BB, MergeIterator Begin, MergeIterator End, + bool IsFirst) { + if (Begin == End || std::next(Begin) == End) + return; + /// Provide no additional information so that AssumeBuilderState doesn't + /// try to do any punning since it already has been done better. + AssumeBuilderState Builder(F.getParent()); + + Instruction *InsertPt = IsFirst ? BB->getFirstNonPHI() : *Begin; + for (IntrinsicInst *I : make_range(Begin, End)) { + CleanupToDo.insert(I); + for (CallInst::BundleOpInfo &BOI : I->bundle_op_infos()) { + RetainedKnowledge RK = getKnowledgeFromBundle(*I, BOI); + if (!RK) + continue; + Builder.addKnowledge(RK); + if (!RK.WasOn) + continue; + if (auto *I = dyn_cast(RK.WasOn)) + if (InsertPt->comesBefore(I)) + InsertPt = I->getNextNode(); + } + } + if (InsertPt->comesBefore(*Begin)) + for (auto It = (*Begin)->getIterator(), E = InsertPt->getIterator(); + It != E; --It) + if (!isGuaranteedToTransferExecutionToSuccessor(&*It)) { + InsertPt = It->getNextNode(); + break; + } + IntrinsicInst *MergedAssume = Builder.build(); + MergedAssume->insertBefore(InsertPt); + AC.registerAssumption(MergedAssume); + } + + void mergeAssumes() { + SmallDenseMap, 8> BBToAssume; + for (Value *V : AC.assumptions()) { + if (!V) + continue; + IntrinsicInst *Assume = cast(V); + BBToAssume[Assume->getParent()].push_back(Assume); + } + + SmallVector SplitPoints; + for (auto &Elem : BBToAssume) { + SmallVectorImpl &AssumesInBB = Elem.second; + if (AssumesInBB.size() < 2) + continue; + llvm::sort(AssumesInBB, + [](const IntrinsicInst *LHS, const IntrinsicInst *RHS) { + return LHS->comesBefore(RHS); + }); + BasicBlock::iterator It = AssumesInBB.front()->getIterator(); + BasicBlock::iterator E = AssumesInBB.back()->getIterator(); + SplitPoints.push_back(AssumesInBB.begin()); + MergeIterator LastSplit = AssumesInBB.begin(); + for (; It != E; ++It) + if (!isGuaranteedToTransferExecutionToSuccessor(&*It)) { + for (; (*LastSplit)->comesBefore(&*It); ++LastSplit) + ; + if (SplitPoints.back() != LastSplit) + SplitPoints.push_back(LastSplit); + } + SplitPoints.push_back(AssumesInBB.end()); + for (auto SplitIt = SplitPoints.begin(); + SplitIt != std::prev(SplitPoints.end()); SplitIt++) { + mergeRange(Elem.first, *SplitIt, *(SplitIt + 1), + SplitIt == SplitPoints.begin()); + } + SplitPoints.clear(); + } + } +}; + +PreservedAnalyses AssumeSimplifyPass::run(Function &F, + FunctionAnalysisManager &AM) { + if (!EnableKnowledgeRetention) + return PreservedAnalyses::all(); + AssumeSimplify AS(F, AM.getResult(F), + AM.getCachedResult(F), + F.getContext()); + + AS.dropRedundantKnowledge(); + AS.RunCleanup(false); + AS.mergeAssumes(); + AS.RunCleanup(true); + + return PreservedAnalyses::all(); +} + PreservedAnalyses AssumeBuilderPass::run(Function &F, FunctionAnalysisManager &AM) { AssumptionCache *AC = AM.getCachedResult(F); 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 @@ -3,14 +3,16 @@ ; 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 +; RUN: opt -passes='assume-builder,require,require,assume-simplify,verify' --enable-knowledge-retention -S %s | FileCheck %s --check-prefixes=FULL-SIMPLIFY target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" declare void @func(i32*, i32*) -declare void @func_cold(i32*) cold +declare void @func_cold(i32*) cold willreturn nounwind declare void @func_strbool(i32*) "no-jump-tables" declare void @func_many(i32*) "no-jump-tables" nounwind "less-precise-fpmad" willreturn norecurse declare void @func_argattr(i32* align 8, i32* nonnull) nounwind +declare void @may_throw() define void @test(i32* %P, i32* %P1, i32* %P2, i32* %P3) { ; BASIC-LABEL: @test( @@ -19,7 +21,7 @@ ; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1:%.*]], i64 12), "nonnull"(i32* [[P]]) ] ; BASIC-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) ; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12), "cold"() ] -; BASIC-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #0 +; BASIC-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #6 ; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12), "cold"() ] ; BASIC-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) ; BASIC-NEXT: call void @func(i32* [[P1]], i32* [[P]]) @@ -39,9 +41,9 @@ ; ALL-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null) ; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1:%.*]], i64 12), "nonnull"(i32* [[P]]) ] ; ALL-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) -; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12), "cold"() ] -; ALL-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #0 -; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12), "cold"() ] +; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12), "cold"(), "nounwind"(), "willreturn"() ] +; ALL-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #6 +; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12), "cold"(), "nounwind"(), "willreturn"() ] ; 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]]) @@ -61,7 +63,7 @@ ; 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 @func_cold(i32* dereferenceable(12) [[P1]]) #6 ; 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]]) @@ -82,7 +84,7 @@ ; 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 @func_cold(i32* dereferenceable(12) [[P1]]) #6 ; 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]]) @@ -97,7 +99,25 @@ ; CROSS-BLOCK-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]]) ; CROSS-BLOCK-NEXT: ret void ; - +; FULL-SIMPLIFY-LABEL: @test( +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[P:%.*]]), "dereferenceable"(i32* [[P]], i64 16) ] +; FULL-SIMPLIFY-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null) +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1:%.*]], i64 12), "ignore"(i32* undef) ] +; FULL-SIMPLIFY-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "ignore"(i32* undef, i64 12), "cold"() ] +; FULL-SIMPLIFY-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #6 +; FULL-SIMPLIFY-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) +; FULL-SIMPLIFY-NEXT: call void @func(i32* [[P1]], i32* [[P]]) +; FULL-SIMPLIFY-NEXT: call void @func_strbool(i32* [[P1]]) +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 32) ] +; FULL-SIMPLIFY-NEXT: call void @func(i32* dereferenceable(32) [[P]], i32* dereferenceable(8) [[P]]) +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8), "align"(i32* [[P2:%.*]], i64 8), "nonnull"(i32* [[P3:%.*]]) ] +; FULL-SIMPLIFY-NEXT: call void @func_many(i32* align 8 [[P1]]) +; FULL-SIMPLIFY-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]]) +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[P1]]), "ignore"(i32* undef) ] +; FULL-SIMPLIFY-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]]) +; FULL-SIMPLIFY-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 @@ -291,6 +311,39 @@ ; 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]] +; +; FULL-SIMPLIFY-LABEL: @test2( +; FULL-SIMPLIFY-NEXT: [[TMP4:%.*]] = alloca %struct.S*, align 8 +; FULL-SIMPLIFY-NEXT: [[TMP5:%.*]] = alloca i32*, align 8 +; FULL-SIMPLIFY-NEXT: [[TMP6:%.*]] = alloca i8*, align 8 +; FULL-SIMPLIFY-NEXT: [[TMP7:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +; FULL-SIMPLIFY-NEXT: store %struct.S* [[TMP0:%.*]], %struct.S** [[TMP4]], align 8 +; FULL-SIMPLIFY-NEXT: store i32* [[TMP1:%.*]], i32** [[TMP5]], align 8 +; FULL-SIMPLIFY-NEXT: store i8* [[TMP2:%.*]], i8** [[TMP6]] +; FULL-SIMPLIFY-NEXT: [[TMP8:%.*]] = load i32*, i32** [[TMP5]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP9:%.*]] = load i32, i32* [[TMP8]], align 4 +; FULL-SIMPLIFY-NEXT: [[TMP10:%.*]] = trunc i32 [[TMP9]] to i8 +; FULL-SIMPLIFY-NEXT: [[TMP11:%.*]] = load i8*, i8** [[TMP6]], align 8 +; FULL-SIMPLIFY-NEXT: store i8 [[TMP10]], i8* [[TMP11]], align 1 +; FULL-SIMPLIFY-NEXT: [[TMP12:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; FULL-SIMPLIFY-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]] +; FULL-SIMPLIFY-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* +; FULL-SIMPLIFY-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; FULL-SIMPLIFY-NEXT: [[TMP16:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP16]], i32 0, i32 0 +; FULL-SIMPLIFY-NEXT: [[TMP18:%.*]] = load i32, i32* [[TMP17]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP19:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP20:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP19]], i32 0, i32 1 +; FULL-SIMPLIFY-NEXT: [[TMP21:%.*]] = load i8, i8* [[TMP20]], align 4 +; FULL-SIMPLIFY-NEXT: [[TMP22:%.*]] = sext i8 [[TMP21]] to i32 +; FULL-SIMPLIFY-NEXT: [[TMP23:%.*]] = add nsw i32 [[TMP18]], [[TMP22]] +; FULL-SIMPLIFY-NEXT: [[TMP24:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP25:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP24]], i32 0, i32 2 +; FULL-SIMPLIFY-NEXT: [[TMP26:%.*]] = load i32*, i32** [[TMP25]], align 8 +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(%struct.S** [[TMP4]], i64 8), "nonnull"(%struct.S** [[TMP4]]), "align"(%struct.S** [[TMP4]], i64 8), "dereferenceable"(i32** [[TMP5]], i64 8), "nonnull"(i32** [[TMP5]]), "align"(i32** [[TMP5]], i64 8), "dereferenceable"(i8** [[TMP6]], i64 8), "nonnull"(i8** [[TMP6]]), "dereferenceable"(i32* [[TMP8]], i64 4), "nonnull"(i32* [[TMP8]]), "align"(i32* [[TMP8]], i64 4), "align"(i8** [[TMP6]], i64 8), "dereferenceable"(i8* [[TMP11]], i64 1), "nonnull"(i8* [[TMP11]]), "dereferenceable"(i32* [[TMP17]], i64 4), "nonnull"(i32* [[TMP17]]), "align"(i32* [[TMP17]], i64 8), "dereferenceable"(i8* [[TMP20]], i64 1), "nonnull"(i8* [[TMP20]]), "align"(i8* [[TMP20]], i64 4), "dereferenceable"(i32** [[TMP25]], i64 8), "nonnull"(i32** [[TMP25]]), "align"(i32** [[TMP25]], i64 8), "dereferenceable"(i32* [[TMP26]], i64 4), "nonnull"(i32* [[TMP26]]), "align"(i32* [[TMP26]], i64 4) ] +; FULL-SIMPLIFY-NEXT: [[TMP27:%.*]] = load i32, i32* [[TMP26]], align 4 +; FULL-SIMPLIFY-NEXT: [[TMP28:%.*]] = add nsw i32 [[TMP23]], [[TMP27]] +; FULL-SIMPLIFY-NEXT: ret i32 [[TMP28]] ; %4 = alloca %struct.S*, align 8 %5 = alloca i32*, align 8 @@ -349,6 +402,7 @@ ; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(%struct.S** [[TMP4]], i64 8), "align"(%struct.S** [[TMP4]], i64 32) ] ; BASIC-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 32 ; BASIC-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* +; BASIC-NEXT: call void @may_throw() ; BASIC-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* ; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(%struct.S** [[TMP4]], i64 8), "align"(%struct.S** [[TMP4]], i64 8) ] ; BASIC-NEXT: [[TMP16:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 @@ -396,6 +450,7 @@ ; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(%struct.S** [[TMP4]], i64 8), "align"(%struct.S** [[TMP4]], i64 32) ] ; ALL-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 32 ; ALL-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* +; ALL-NEXT: call void @may_throw() ; ALL-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* ; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(%struct.S** [[TMP4]], i64 8), "align"(%struct.S** [[TMP4]], i64 8) ] ; ALL-NEXT: [[TMP16:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 @@ -440,6 +495,7 @@ ; 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: call void @may_throw() ; 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 @@ -481,6 +537,7 @@ ; 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: call void @may_throw() ; 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 @@ -500,6 +557,41 @@ ; 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]] +; +; FULL-SIMPLIFY-LABEL: @test3( +; FULL-SIMPLIFY-NEXT: [[TMP4:%.*]] = alloca %struct.S*, align 8 +; FULL-SIMPLIFY-NEXT: [[TMP5:%.*]] = alloca i32*, align 8 +; FULL-SIMPLIFY-NEXT: [[TMP6:%.*]] = alloca i8*, align 8 +; FULL-SIMPLIFY-NEXT: [[TMP7:%.*]] = alloca [[STRUCT_S:%.*]], align 8 +; FULL-SIMPLIFY-NEXT: store %struct.S* [[TMP0:%.*]], %struct.S** [[TMP4]], align 8 +; FULL-SIMPLIFY-NEXT: store i32* [[TMP1:%.*]], i32** [[TMP5]], align 8 +; FULL-SIMPLIFY-NEXT: store i8* [[TMP2:%.*]], i8** [[TMP6]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP8:%.*]] = load i32*, i32** [[TMP5]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP9:%.*]] = load i32, i32* [[TMP8]], align 4 +; FULL-SIMPLIFY-NEXT: [[TMP10:%.*]] = trunc i32 [[TMP9]] to i8 +; FULL-SIMPLIFY-NEXT: [[TMP11:%.*]] = load i8*, i8** [[TMP6]] +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(%struct.S** [[TMP4]], i64 8), "align"(%struct.S** [[TMP4]], i64 8), "dereferenceable"(i32** [[TMP5]], i64 8), "align"(i32** [[TMP5]], i64 8), "dereferenceable"(i8** [[TMP6]], i64 8), "align"(i8** [[TMP6]], i64 8), "dereferenceable"(i32* [[TMP8]], i64 4), "align"(i32* [[TMP8]], i64 4), "dereferenceable"(i8* [[TMP11]], i64 1) ] +; FULL-SIMPLIFY-NEXT: store i8 [[TMP10]], i8* [[TMP11]], align 1 +; FULL-SIMPLIFY-NEXT: [[TMP12:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; FULL-SIMPLIFY-NEXT: [[TMP13:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 32 +; FULL-SIMPLIFY-NEXT: [[TMP14:%.*]] = bitcast %struct.S* [[TMP13]] to i8* +; FULL-SIMPLIFY-NEXT: call void @may_throw() +; FULL-SIMPLIFY-NEXT: [[TMP15:%.*]] = bitcast %struct.S* [[TMP7]] to i8* +; FULL-SIMPLIFY-NEXT: [[TMP16:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP16]], i32 0, i32 0 +; FULL-SIMPLIFY-NEXT: [[TMP18:%.*]] = load i32, i32* [[TMP17]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP19:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP20:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP19]], i32 0, i32 1 +; FULL-SIMPLIFY-NEXT: [[TMP21:%.*]] = load i8, i8* [[TMP20]], align 4 +; FULL-SIMPLIFY-NEXT: [[TMP22:%.*]] = sext i8 [[TMP21]] to i32 +; FULL-SIMPLIFY-NEXT: [[TMP23:%.*]] = add nsw i32 [[TMP18]], [[TMP22]] +; FULL-SIMPLIFY-NEXT: [[TMP24:%.*]] = load %struct.S*, %struct.S** [[TMP4]], align 8 +; FULL-SIMPLIFY-NEXT: [[TMP25:%.*]] = getelementptr inbounds [[STRUCT_S]], %struct.S* [[TMP24]], i32 0, i32 2 +; FULL-SIMPLIFY-NEXT: [[TMP26:%.*]] = load i32*, i32** [[TMP25]] +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP17]], i64 4), "align"(i32* [[TMP17]], i64 8), "dereferenceable"(i8* [[TMP20]], i64 1), "align"(i8* [[TMP20]], i64 4), "dereferenceable"(i32** [[TMP25]], i64 8), "dereferenceable"(i32* [[TMP26]], i64 4), "align"(i32* [[TMP26]], i64 4) ] +; FULL-SIMPLIFY-NEXT: [[TMP27:%.*]] = load i32, i32* [[TMP26]], align 4 +; FULL-SIMPLIFY-NEXT: [[TMP28:%.*]] = add nsw i32 [[TMP23]], [[TMP27]] +; FULL-SIMPLIFY-NEXT: ret i32 [[TMP28]] ; %4 = alloca %struct.S*, align 8 %5 = alloca i32*, align 8 @@ -516,6 +608,7 @@ %12 = bitcast %struct.S* %7 to i8* %13 = load %struct.S*, %struct.S** %4, align 32 %14 = bitcast %struct.S* %13 to i8* + call void @may_throw() %15 = bitcast %struct.S* %7 to i8* %16 = load %struct.S*, %struct.S** %4, align 8 %17 = getelementptr inbounds %struct.S, %struct.S* %16, i32 0, i32 0 @@ -629,6 +722,27 @@ ; 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 +; +; FULL-SIMPLIFY-LABEL: @_Z6squarePi( +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P:%.*]], i64 4), "nonnull"(i32* [[P]]), "align"(i32* [[P]], i64 4), "dereferenceable"(i32* [[P1:%.*]], i64 4), "nonnull"(i32* [[P1]]), "align"(i32* [[P1]], i64 8) ] +; FULL-SIMPLIFY-NEXT: store i32 0, i32* [[P]], align 4 +; FULL-SIMPLIFY-NEXT: store i32 0, i32* [[P1]], align 8 +; FULL-SIMPLIFY-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]] +; FULL-SIMPLIFY: A: +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "ignore"(i32* undef, i64 4), "ignore"(i32* undef), "align"(i32* [[P]], i64 8) ] +; FULL-SIMPLIFY-NEXT: store i32 0, i32* [[P]], align 8 +; FULL-SIMPLIFY-NEXT: store i32 0, i32* [[P1]], align 4 +; FULL-SIMPLIFY-NEXT: br i1 [[COND]], label [[C:%.*]], label [[B]] +; FULL-SIMPLIFY: B: +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "ignore"(i32* undef, i64 4), "ignore"(i32* undef), "align"(i32* [[P]], i64 8) ] +; FULL-SIMPLIFY-NEXT: store i32 0, i32* [[P]], align 8 +; FULL-SIMPLIFY-NEXT: store i32 0, i32* [[P1]], align 8 +; FULL-SIMPLIFY-NEXT: br label [[C]] +; FULL-SIMPLIFY: C: +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "ignore"(i32* undef, i64 4), "ignore"(i32* undef), "align"(i32* [[P]], i64 32) ] +; FULL-SIMPLIFY-NEXT: store i32 0, i32* [[P]], align 32 +; FULL-SIMPLIFY-NEXT: store i32 0, i32* [[P1]], align 4 +; FULL-SIMPLIFY-NEXT: ret i32 0 ; store i32 0, i32* %P, align 4 store i32 0, i32* %P1, align 8 @@ -646,3 +760,121 @@ store i32 0, i32* %P1, align 4 ret i32 0 } + +define dso_local i32 @test4A(i32* %0, i32* %1, i32 %2, i32 %3) { +; BASIC-LABEL: @test4A( +; BASIC-NEXT: [[TMP5:%.*]] = icmp ne i32* [[TMP1:%.*]], null +; BASIC-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP12:%.*]] +; BASIC: 6: +; BASIC-NEXT: [[TMP7:%.*]] = add nsw i32 [[TMP3:%.*]], [[TMP2:%.*]] +; BASIC-NEXT: call void @may_throw() +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0:%.*]], i64 4), "nonnull"(i32* [[TMP0]]), "align"(i32* [[TMP0]], i64 4) ] +; BASIC-NEXT: [[TMP8:%.*]] = load i32, i32* [[TMP0]], align 4 +; BASIC-NEXT: [[TMP9:%.*]] = add nsw i32 [[TMP7]], [[TMP8]] +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0]], i64 4), "nonnull"(i32* [[TMP0]]), "align"(i32* [[TMP0]], i64 4) ] +; BASIC-NEXT: store i32 0, i32* [[TMP0]], align 4 +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP1]], i64 4), "nonnull"(i32* [[TMP1]]), "align"(i32* [[TMP1]], i64 4) ] +; BASIC-NEXT: [[TMP10:%.*]] = load i32, i32* [[TMP1]], align 4 +; BASIC-NEXT: [[TMP11:%.*]] = add nsw i32 [[TMP9]], [[TMP10]] +; BASIC-NEXT: call void @may_throw() +; BASIC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP1]], i64 4), "nonnull"(i32* [[TMP1]]), "align"(i32* [[TMP1]], i64 4) ] +; BASIC-NEXT: store i32 [[TMP11]], i32* [[TMP1]], align 4 +; BASIC-NEXT: br label [[TMP12]] +; BASIC: 12: +; BASIC-NEXT: ret i32 0 +; +; ALL-LABEL: @test4A( +; ALL-NEXT: [[TMP5:%.*]] = icmp ne i32* [[TMP1:%.*]], null +; ALL-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP12:%.*]] +; ALL: 6: +; ALL-NEXT: [[TMP7:%.*]] = add nsw i32 [[TMP3:%.*]], [[TMP2:%.*]] +; ALL-NEXT: call void @may_throw() +; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0:%.*]], i64 4), "nonnull"(i32* [[TMP0]]), "align"(i32* [[TMP0]], i64 4) ] +; ALL-NEXT: [[TMP8:%.*]] = load i32, i32* [[TMP0]], align 4 +; ALL-NEXT: [[TMP9:%.*]] = add nsw i32 [[TMP7]], [[TMP8]] +; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0]], i64 4), "nonnull"(i32* [[TMP0]]), "align"(i32* [[TMP0]], i64 4) ] +; ALL-NEXT: store i32 0, i32* [[TMP0]], align 4 +; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP1]], i64 4), "nonnull"(i32* [[TMP1]]), "align"(i32* [[TMP1]], i64 4) ] +; ALL-NEXT: [[TMP10:%.*]] = load i32, i32* [[TMP1]], align 4 +; ALL-NEXT: [[TMP11:%.*]] = add nsw i32 [[TMP9]], [[TMP10]] +; ALL-NEXT: call void @may_throw() +; ALL-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP1]], i64 4), "nonnull"(i32* [[TMP1]]), "align"(i32* [[TMP1]], i64 4) ] +; ALL-NEXT: store i32 [[TMP11]], i32* [[TMP1]], align 4 +; ALL-NEXT: br label [[TMP12]] +; ALL: 12: +; ALL-NEXT: ret i32 0 +; +; WITH-AC-LABEL: @test4A( +; WITH-AC-NEXT: [[TMP5:%.*]] = icmp ne i32* [[TMP1:%.*]], null +; WITH-AC-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP12:%.*]] +; WITH-AC: 6: +; WITH-AC-NEXT: [[TMP7:%.*]] = add nsw i32 [[TMP3:%.*]], [[TMP2:%.*]] +; WITH-AC-NEXT: call void @may_throw() +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0:%.*]], i64 4), "nonnull"(i32* [[TMP0]]), "align"(i32* [[TMP0]], i64 4) ] +; WITH-AC-NEXT: [[TMP8:%.*]] = load i32, i32* [[TMP0]], align 4 +; WITH-AC-NEXT: [[TMP9:%.*]] = add nsw i32 [[TMP7]], [[TMP8]] +; WITH-AC-NEXT: store i32 0, i32* [[TMP0]], align 4 +; WITH-AC-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP1]], i64 4), "nonnull"(i32* [[TMP1]]), "align"(i32* [[TMP1]], i64 4) ] +; WITH-AC-NEXT: [[TMP10:%.*]] = load i32, i32* [[TMP1]], align 4 +; WITH-AC-NEXT: [[TMP11:%.*]] = add nsw i32 [[TMP9]], [[TMP10]] +; WITH-AC-NEXT: call void @may_throw() +; WITH-AC-NEXT: store i32 [[TMP11]], i32* [[TMP1]], align 4 +; WITH-AC-NEXT: br label [[TMP12]] +; WITH-AC: 12: +; WITH-AC-NEXT: ret i32 0 +; +; CROSS-BLOCK-LABEL: @test4A( +; CROSS-BLOCK-NEXT: [[TMP5:%.*]] = icmp ne i32* [[TMP1:%.*]], null +; CROSS-BLOCK-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP12:%.*]] +; CROSS-BLOCK: 6: +; CROSS-BLOCK-NEXT: [[TMP7:%.*]] = add nsw i32 [[TMP3:%.*]], [[TMP2:%.*]] +; CROSS-BLOCK-NEXT: call void @may_throw() +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0:%.*]], i64 4), "nonnull"(i32* [[TMP0]]), "align"(i32* [[TMP0]], i64 4) ] +; CROSS-BLOCK-NEXT: [[TMP8:%.*]] = load i32, i32* [[TMP0]], align 4 +; CROSS-BLOCK-NEXT: [[TMP9:%.*]] = add nsw i32 [[TMP7]], [[TMP8]] +; CROSS-BLOCK-NEXT: store i32 0, i32* [[TMP0]], align 4 +; CROSS-BLOCK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP1]], i64 4), "nonnull"(i32* [[TMP1]]), "align"(i32* [[TMP1]], i64 4) ] +; CROSS-BLOCK-NEXT: [[TMP10:%.*]] = load i32, i32* [[TMP1]], align 4 +; CROSS-BLOCK-NEXT: [[TMP11:%.*]] = add nsw i32 [[TMP9]], [[TMP10]] +; CROSS-BLOCK-NEXT: call void @may_throw() +; CROSS-BLOCK-NEXT: store i32 [[TMP11]], i32* [[TMP1]], align 4 +; CROSS-BLOCK-NEXT: br label [[TMP12]] +; CROSS-BLOCK: 12: +; CROSS-BLOCK-NEXT: ret i32 0 +; +; FULL-SIMPLIFY-LABEL: @test4A( +; FULL-SIMPLIFY-NEXT: [[TMP5:%.*]] = icmp ne i32* [[TMP1:%.*]], null +; FULL-SIMPLIFY-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[TMP12:%.*]] +; FULL-SIMPLIFY: 6: +; FULL-SIMPLIFY-NEXT: [[TMP7:%.*]] = add nsw i32 [[TMP3:%.*]], [[TMP2:%.*]] +; FULL-SIMPLIFY-NEXT: call void @may_throw() +; FULL-SIMPLIFY-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0:%.*]], i64 4), "nonnull"(i32* [[TMP0]]), "align"(i32* [[TMP0]], i64 4), "dereferenceable"(i32* [[TMP1]], i64 4), "nonnull"(i32* [[TMP1]]), "align"(i32* [[TMP1]], i64 4) ] +; FULL-SIMPLIFY-NEXT: [[TMP8:%.*]] = load i32, i32* [[TMP0]], align 4 +; FULL-SIMPLIFY-NEXT: [[TMP9:%.*]] = add nsw i32 [[TMP7]], [[TMP8]] +; FULL-SIMPLIFY-NEXT: store i32 0, i32* [[TMP0]], align 4 +; FULL-SIMPLIFY-NEXT: [[TMP10:%.*]] = load i32, i32* [[TMP1]], align 4 +; FULL-SIMPLIFY-NEXT: [[TMP11:%.*]] = add nsw i32 [[TMP9]], [[TMP10]] +; FULL-SIMPLIFY-NEXT: call void @may_throw() +; FULL-SIMPLIFY-NEXT: store i32 [[TMP11]], i32* [[TMP1]], align 4 +; FULL-SIMPLIFY-NEXT: br label [[TMP12]] +; FULL-SIMPLIFY: 12: +; FULL-SIMPLIFY-NEXT: ret i32 0 +; + %5 = icmp ne i32* %1, null + br i1 %5, label %6, label %12 + +6: ; preds = %4 + %7 = add nsw i32 %3, %2 + call void @may_throw() + %8 = load i32, i32* %0, align 4 + %9 = add nsw i32 %7, %8 + store i32 0, i32* %0, align 4 + %10 = load i32, i32* %1, align 4 + %11 = add nsw i32 %9, %10 + call void @may_throw() + store i32 %11, i32* %1, align 4 + br label %12 + +12: ; preds = %6, %4 + ret i32 0 +} diff --git a/llvm/test/Transforms/Util/assume-simplify.ll b/llvm/test/Transforms/Util/assume-simplify.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Util/assume-simplify.ll @@ -0,0 +1,177 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes='require,require,assume-simplify,verify' --enable-knowledge-retention -S %s | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +declare void @may_throw() +declare void @llvm.assume(i1) + +define i32 @test1(i32* %0, i32* %1, i32 %2, i32 %3) { +; CHECK-LABEL: @test1( +; CHECK-NEXT: [[TMP5:%.*]] = icmp ne i32 [[TMP2:%.*]], 4 +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0:%.*]], i64 4), "nonnull"(i32* [[TMP0]]) ] +; CHECK-NEXT: br i1 [[TMP5]], label [[TMP6:%.*]], label [[A:%.*]] +; CHECK: 6: +; CHECK-NEXT: [[TMP7:%.*]] = add nsw i32 [[TMP3:%.*]], [[TMP2]] +; CHECK-NEXT: call void @may_throw() +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP0]], i64 4), "align"(i32* [[TMP1:%.*]], i64 4), "nonnull"(i32* [[TMP1]]) ] +; CHECK-NEXT: [[TMP8:%.*]] = load i32, i32* [[TMP0]], align 4 +; CHECK-NEXT: [[TMP9:%.*]] = add nsw i32 [[TMP7]], [[TMP8]] +; CHECK-NEXT: store i32 0, i32* [[TMP0]], align 4 +; CHECK-NEXT: [[TMP10:%.*]] = load i32, i32* [[TMP1]], align 4 +; CHECK-NEXT: [[TMP11:%.*]] = add nsw i32 [[TMP9]], [[TMP10]] +; CHECK-NEXT: call void @may_throw() +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP1]], i64 4), "ignore"(i32* undef) ] +; CHECK-NEXT: store i32 [[TMP11]], i32* [[TMP1]], align 4 +; CHECK-NEXT: br label [[B:%.*]] +; CHECK: A: +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP0]], i64 4), "ignore"(i32* undef, i64 4), "ignore"(i32* undef) ] +; CHECK-NEXT: br label [[B]] +; CHECK: B: +; CHECK-NEXT: ret i32 0 +; + %5 = icmp ne i32 %2, 4 + call void @llvm.assume(i1 true) ["dereferenceable"(i32* %0, i64 4), "nonnull"(i32* %0) ] + br i1 %5, label %6, label %A + +6: ; preds = %4 + %7 = add nsw i32 %3, %2 + call void @may_throw() + %8 = load i32, i32* %0, align 4 + %9 = add nsw i32 %7, %8 + store i32 0, i32* %0, align 4 + call void @llvm.assume(i1 true) [ "align"(i32* %0, i64 4), "dereferenceable"(i32* %0, i64 4) ] + %10 = load i32, i32* %1, align 4 + %11 = add nsw i32 %9, %10 + call void @llvm.assume(i1 true) [ "align"(i32* %1, i64 4), "nonnull"(i32* %1) ] + call void @may_throw() + call void @llvm.assume(i1 true) [ "dereferenceable"(i32* %1, i64 4), "nonnull"(i32* %1) ] + store i32 %11, i32* %1, align 4 + br label %B + +A: + call void @llvm.assume(i1 true) [ "align"(i32* %0, i64 4), "dereferenceable"(i32* %0, i64 4), "nonnull"(i32* %0) ] + br label %B + +B: ; preds = %6, %4 + ret i32 0 +} + +define i32 @test2(i32** %0, i32* %1, i32 %2, i32 %3) { +; CHECK-LABEL: @test2( +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP1:%.*]], i64 4), "dereferenceable"(i32* [[TMP1]], i64 4), "nonnull"(i32* [[TMP1]]) ] +; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, i32* [[TMP1]], i64 0 +; CHECK-NEXT: [[TMP6:%.*]] = load i32, i32* [[TMP5]], align 4 +; CHECK-NEXT: [[TMP7:%.*]] = icmp ne i32 [[TMP6]], 0 +; CHECK-NEXT: br i1 [[TMP7]], label [[TMP8:%.*]], label [[TMP19:%.*]] +; CHECK: 8: +; CHECK-NEXT: call void @may_throw() +; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds i32, i32* [[TMP1]], i64 0 +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP9]], i64 4), "dereferenceable"(i32* [[TMP9]], i64 4), "nonnull"(i32* [[TMP9]]) ] +; CHECK-NEXT: [[TMP10:%.*]] = load i32, i32* [[TMP9]], align 4 +; CHECK-NEXT: [[TMP11:%.*]] = getelementptr inbounds i32, i32* [[TMP1]], i64 2 +; CHECK-NEXT: store i32 [[TMP10]], i32* [[TMP11]], align 4 +; CHECK-NEXT: call void @may_throw() +; CHECK-NEXT: call void @may_throw() +; CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds i32*, i32** [[TMP0:%.*]], i64 1 +; CHECK-NEXT: [[TMP13:%.*]] = load i32*, i32** [[TMP12]], align 8 +; CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds i32, i32* [[TMP13]], i64 0 +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[TMP11]], i64 4), "dereferenceable"(i32* [[TMP11]], i64 4), "nonnull"(i32* [[TMP11]]), "align"(i32* [[TMP14]], i64 4), "dereferenceable"(i32* [[TMP14]], i64 4), "nonnull"(i32* [[TMP14]]) ] +; CHECK-NEXT: [[TMP15:%.*]] = load i32, i32* [[TMP14]], align 4 +; CHECK-NEXT: [[TMP16:%.*]] = getelementptr inbounds i32*, i32** [[TMP0]], i64 1 +; CHECK-NEXT: [[TMP17:%.*]] = load i32*, i32** [[TMP16]], align 8 +; CHECK-NEXT: [[TMP18:%.*]] = getelementptr inbounds i32, i32* [[TMP17]], i64 2 +; CHECK-NEXT: store i32 [[TMP15]], i32* [[TMP18]], align 4 +; CHECK-NEXT: call void @may_throw() +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32** [[TMP0]], i64 4), "dereferenceable"(i32** [[TMP0]], i64 4), "nonnull"(i32** [[TMP0]]) ] +; CHECK-NEXT: br label [[TMP35:%.*]] +; CHECK: 19: +; CHECK-NEXT: [[TMP20:%.*]] = getelementptr inbounds i32*, i32** [[TMP0]], i64 7 +; CHECK-NEXT: [[TMP21:%.*]] = load i32*, i32** [[TMP20]], align 8 +; CHECK-NEXT: [[TMP22:%.*]] = getelementptr inbounds i32, i32* [[TMP21]], i64 0 +; CHECK-NEXT: [[TMP23:%.*]] = load i32, i32* [[TMP22]], align 4 +; CHECK-NEXT: [[TMP24:%.*]] = icmp ne i32 [[TMP23]], 0 +; CHECK-NEXT: br i1 [[TMP24]], label [[TMP25:%.*]], label [[TMP33:%.*]] +; CHECK: 25: +; CHECK-NEXT: call void @may_throw() +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32** [[TMP0]], i64 4), "dereferenceable"(i32** [[TMP0]], i64 4), "nonnull"(i32** [[TMP0]]) ] +; CHECK-NEXT: [[TMP26:%.*]] = getelementptr inbounds i32*, i32** [[TMP0]], i64 2 +; CHECK-NEXT: [[TMP27:%.*]] = load i32*, i32** [[TMP26]], align 8 +; CHECK-NEXT: [[TMP28:%.*]] = getelementptr inbounds i32, i32* [[TMP27]], i64 0 +; CHECK-NEXT: [[TMP29:%.*]] = load i32, i32* [[TMP28]], align 4 +; CHECK-NEXT: [[TMP30:%.*]] = getelementptr inbounds i32*, i32** [[TMP0]], i64 2 +; CHECK-NEXT: [[TMP31:%.*]] = load i32*, i32** [[TMP30]], align 8 +; CHECK-NEXT: [[TMP32:%.*]] = getelementptr inbounds i32, i32* [[TMP31]], i64 2 +; CHECK-NEXT: store i32 [[TMP29]], i32* [[TMP32]], align 4 +; CHECK-NEXT: call void @may_throw() +; CHECK-NEXT: br label [[TMP33]] +; CHECK: 33: +; CHECK-NEXT: br label [[TMP34:%.*]] +; CHECK: 34: +; CHECK-NEXT: br label [[TMP35]] +; CHECK: 35: +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(i32** [[TMP0]], i64 4), "dereferenceable"(i32** [[TMP0]], i64 4), "nonnull"(i32** [[TMP0]]) ] +; CHECK-NEXT: ret i32 0 +; + %5 = getelementptr inbounds i32, i32* %1, i64 0 + %6 = load i32, i32* %5, align 4 + %7 = icmp ne i32 %6, 0 + call void @llvm.assume(i1 true) [ "align"(i32* %1, i64 4), "dereferenceable"(i32* %1, i64 4) ] + call void @llvm.assume(i1 true) [ "align"(i32* %1, i64 4), "nonnull"(i32* %1) ] + br i1 %7, label %8, label %19 + +8: ; preds = %4 + call void @may_throw() + %9 = getelementptr inbounds i32, i32* %1, i64 0 + call void @llvm.assume(i1 true) [ "align"(i32* %9, i64 4), "dereferenceable"(i32* %9, i64 4), "nonnull"(i32* %9) ] + %10 = load i32, i32* %9, align 4 + %11 = getelementptr inbounds i32, i32* %1, i64 2 + store i32 %10, i32* %11, align 4 + call void @may_throw() + call void @may_throw() + call void @llvm.assume(i1 true) [ "align"(i32* %11, i64 4), "dereferenceable"(i32* %11, i64 4), "nonnull"(i32* %11) ] + %12 = getelementptr inbounds i32*, i32** %0, i64 1 + %13 = load i32*, i32** %12, align 8 + %14 = getelementptr inbounds i32, i32* %13, i64 0 + %15 = load i32, i32* %14, align 4 + call void @llvm.assume(i1 true) [ "align"(i32* %14, i64 4), "dereferenceable"(i32* %14, i64 4), "nonnull"(i32* %14) ] + %16 = getelementptr inbounds i32*, i32** %0, i64 1 + %17 = load i32*, i32** %16, align 8 + %18 = getelementptr inbounds i32, i32* %17, i64 2 + store i32 %15, i32* %18, align 4 + call void @may_throw() + call void @llvm.assume(i1 true) [ "align"(i32** %0, i64 4), "dereferenceable"(i32** %0, i64 4), "nonnull"(i32** %0) ] + br label %35 + +19: ; preds = %4 + %20 = getelementptr inbounds i32*, i32** %0, i64 7 + %21 = load i32*, i32** %20, align 8 + %22 = getelementptr inbounds i32, i32* %21, i64 0 + %23 = load i32, i32* %22, align 4 + %24 = icmp ne i32 %23, 0 + br i1 %24, label %25, label %33 + +25: ; preds = %19 + call void @may_throw() + call void @llvm.assume(i1 true) [ "align"(i32** %0, i64 4), "dereferenceable"(i32** %0, i64 4), "nonnull"(i32** %0) ] + %26 = getelementptr inbounds i32*, i32** %0, i64 2 + %27 = load i32*, i32** %26, align 8 + %28 = getelementptr inbounds i32, i32* %27, i64 0 + %29 = load i32, i32* %28, align 4 + %30 = getelementptr inbounds i32*, i32** %0, i64 2 + %31 = load i32*, i32** %30, align 8 + %32 = getelementptr inbounds i32, i32* %31, i64 2 + store i32 %29, i32* %32, align 4 + call void @may_throw() + br label %33 + +33: ; preds = %25, %19 + br label %34 + +34: ; preds = %33 + br label %35 + +35: ; preds = %34, %8 + call void @llvm.assume(i1 true) [ "align"(i32** %0, i64 4), "dereferenceable"(i32** %0, i64 4), "nonnull"(i32** %0) ] + ret i32 0 +}