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 @@ -6,17 +6,29 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "assume-queries" + #include "llvm/Analysis/AssumeBundleQueries.h" +#include "llvm/ADT/Statistic.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/PatternMatch.h" +#include "llvm/Support/DebugCounter.h" using namespace llvm; using namespace llvm::PatternMatch; +STATISTIC(NumAssumeQueries, "Number of Queries into an assume assume bundles"); +STATISTIC( + NumUsefullAssumeQueries, + "Number of Queries into an assume assume bundles that were satisfied"); + +DEBUG_COUNTER(AssumeQueryCounter, "assume-queries-counter", + "Controls which assumes gets created"); + static bool bundleHasArgument(const CallBase::BundleOpInfo &BOI, unsigned Idx) { return BOI.End - BOI.Begin > Idx; } @@ -158,6 +170,9 @@ function_ref Filter) { + NumAssumeQueries++; + if (!DebugCounter::shouldExecute(AssumeQueryCounter)) + return RetainedKnowledge::none(); if (AC) { for (AssumptionCache::ResultElem &Elem : AC->assumptionsFor(V)) { IntrinsicInst *II = cast_or_null(Elem.Assume); @@ -166,20 +181,24 @@ if (RetainedKnowledge RK = getKnowledgeFromBundle( *II, II->bundle_op_info_begin()[Elem.Index])) if (is_contained(AttrKinds, RK.AttrKind) && - Filter(RK, II, &II->bundle_op_info_begin()[Elem.Index])) + Filter(RK, II, &II->bundle_op_info_begin()[Elem.Index])) { + NumUsefullAssumeQueries++; return RK; + } } return RetainedKnowledge::none(); } - for (auto &U : V->uses()) { + for (const auto &U : V->uses()) { 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)) + Filter(RK, cast(U.getUser()), Bundle)) { + NumUsefullAssumeQueries++; return RK; + } } return RetainedKnowledge::none(); } 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 @@ -6,9 +6,12 @@ // //===----------------------------------------------------------------------===// +#define DEBUG_TYPE "assume-builder" + #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/Statistic.h" #include "llvm/Analysis/AssumeBundleQueries.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/ValueTracking.h" @@ -19,6 +22,7 @@ #include "llvm/IR/Module.h" #include "llvm/InitializePasses.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/DebugCounter.h" #include "llvm/Transforms/Utils/Local.h" using namespace llvm; @@ -33,6 +37,16 @@ cl::desc( "enable preservation of attributes throughout code transformation")); +STATISTIC(NumAssumeBuilt, "Number of assume built by the assume builder"); +STATISTIC(NumBundlesInAssumes, "Total number of Bundles in the assume built"); +STATISTIC(NumAssumesMerged, + "Number of assume merged by the assume simplify pass"); +STATISTIC(NumAssumesRemoved, + "Number of assume removed by the assume simplify pass"); + +DEBUG_COUNTER(BuildAssumeCounter, "assume-builder-counter", + "Controls which assumes gets created"); + namespace { bool isUsefullToPreserve(Attribute::AttrKind Kind) { @@ -204,6 +218,8 @@ IntrinsicInst *build() { if (AssumedKnowledgeMap.empty()) return nullptr; + if (!DebugCounter::shouldExecute(BuildAssumeCounter)) + return nullptr; Function *FnAssume = Intrinsic::getDeclaration(M, Intrinsic::assume); LLVMContext &C = M->getContext(); SmallVector OpBundle; @@ -220,7 +236,9 @@ OpBundle.push_back(OperandBundleDefT( std::string(Attribute::getNameFromAttrKind(MapElem.first.second)), Args)); + NumBundlesInAssumes++; } + NumAssumeBuilt++; return cast(CallInst::Create( FnAssume, ArrayRef({ConstantInt::getTrue(C)}), OpBundle)); } @@ -328,6 +346,10 @@ (!ForceCleanup && !isAssumeWithEmptyBundle(*Assume))) continue; MadeChange = true; + if (ForceCleanup) + NumAssumesMerged++; + else + NumAssumesRemoved++; Assume->eraseFromParent(); } CleanupToDo.clear(); diff --git a/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll b/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/ValueTracking/assume-queries-counter.ll @@ -0,0 +1,110 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine --debug-counter=assume-queries-counter-skip=0,assume-queries-counter-count=1 -S | FileCheck %s --check-prefixes=SAME,COUNTER1 +; RUN: opt < %s -instcombine --debug-counter=assume-queries-counter-skip=1,assume-queries-counter-count=2 -S | FileCheck %s --check-prefixes=SAME,COUNTER2 +; RUN: opt < %s -instcombine --debug-counter=assume-queries-counter-skip=2,assume-queries-counter-count=1 -S | FileCheck %s --check-prefixes=SAME,COUNTER3 + +declare i1 @get_val() +declare void @llvm.assume(i1) + +define dso_local i1 @test1(i32* readonly %0) { +; COUNTER1-LABEL: @test1( +; COUNTER1-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[TMP0:%.*]]) ] +; COUNTER1-NEXT: ret i1 false +; +; COUNTER2-LABEL: @test1( +; COUNTER2-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[TMP0:%.*]]) ] +; COUNTER2-NEXT: [[TMP2:%.*]] = icmp eq i32* [[TMP0]], null +; COUNTER2-NEXT: ret i1 [[TMP2]] +; +; COUNTER3-LABEL: @test1( +; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[TMP0:%.*]]) ] +; COUNTER3-NEXT: [[TMP2:%.*]] = icmp eq i32* [[TMP0]], null +; COUNTER3-NEXT: ret i1 [[TMP2]] +; + call void @llvm.assume(i1 true) ["nonnull"(i32* %0)] + %2 = icmp eq i32* %0, null + ret i1 %2 +} + +define dso_local i1 @test2(i32* readonly %0) { +; COUNTER1-LABEL: @test2( +; COUNTER1-NEXT: [[TMP2:%.*]] = icmp eq i32* [[TMP0:%.*]], null +; COUNTER1-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[TMP0]]) ] +; COUNTER1-NEXT: ret i1 [[TMP2]] +; +; COUNTER2-LABEL: @test2( +; COUNTER2-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[TMP0:%.*]]) ] +; COUNTER2-NEXT: ret i1 false +; +; COUNTER3-LABEL: @test2( +; COUNTER3-NEXT: [[TMP2:%.*]] = icmp eq i32* [[TMP0:%.*]], null +; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[TMP0]]) ] +; COUNTER3-NEXT: ret i1 [[TMP2]] +; + %2 = icmp eq i32* %0, null + call void @llvm.assume(i1 true) ["nonnull"(i32* %0)] + ret i1 %2 +} + +define dso_local i32 @test4(i32* readonly %0, i1 %cond) { +; COUNTER1-LABEL: @test4( +; COUNTER1-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0:%.*]], i32 4) ] +; COUNTER1-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]] +; COUNTER1: B: +; COUNTER1-NEXT: br label [[A]] +; COUNTER1: A: +; COUNTER1-NEXT: [[TMP2:%.*]] = icmp eq i32* [[TMP0]], null +; COUNTER1-NEXT: br i1 [[TMP2]], label [[TMP5:%.*]], label [[TMP3:%.*]] +; COUNTER1: 3: +; COUNTER1-NEXT: [[TMP4:%.*]] = load i32, i32* [[TMP0]], align 4 +; COUNTER1-NEXT: br label [[TMP5]] +; COUNTER1: 5: +; COUNTER1-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP4]], [[TMP3]] ], [ 0, [[A]] ] +; COUNTER1-NEXT: ret i32 [[TMP6]] +; +; COUNTER2-LABEL: @test4( +; COUNTER2-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0:%.*]], i32 4) ] +; COUNTER2-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]] +; COUNTER2: B: +; COUNTER2-NEXT: br label [[A]] +; COUNTER2: A: +; COUNTER2-NEXT: br i1 false, label [[TMP4:%.*]], label [[TMP2:%.*]] +; COUNTER2: 2: +; COUNTER2-NEXT: [[TMP3:%.*]] = load i32, i32* [[TMP0]], align 4 +; COUNTER2-NEXT: br label [[TMP4]] +; COUNTER2: 4: +; COUNTER2-NEXT: [[TMP5:%.*]] = phi i32 [ [[TMP3]], [[TMP2]] ], [ 0, [[A]] ] +; COUNTER2-NEXT: ret i32 [[TMP5]] +; +; COUNTER3-LABEL: @test4( +; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[TMP0:%.*]], i32 4) ] +; COUNTER3-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]] +; COUNTER3: B: +; COUNTER3-NEXT: br label [[A]] +; COUNTER3: A: +; COUNTER3-NEXT: br i1 false, label [[TMP4:%.*]], label [[TMP2:%.*]] +; COUNTER3: 2: +; COUNTER3-NEXT: [[TMP3:%.*]] = load i32, i32* [[TMP0]], align 4 +; COUNTER3-NEXT: br label [[TMP4]] +; COUNTER3: 4: +; COUNTER3-NEXT: [[TMP5:%.*]] = phi i32 [ [[TMP3]], [[TMP2]] ], [ 0, [[A]] ] +; COUNTER3-NEXT: ret i32 [[TMP5]] +; + call void @llvm.assume(i1 true) ["dereferenceable"(i32* %0, i32 4)] + br i1 %cond, label %A, label %B + +B: + br label %A + +A: + %2 = icmp eq i32* %0, null + br i1 %2, label %5, label %3 + +3: ; preds = %1 + %4 = load i32, i32* %0, align 4 + br label %5 + +5: ; preds = %1, %3 + %6 = phi i32 [ %4, %3 ], [ 0, %A ] + ret i32 %6 +} diff --git a/llvm/test/Transforms/Util/assume-builder-counter.ll b/llvm/test/Transforms/Util/assume-builder-counter.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Util/assume-builder-counter.ll @@ -0,0 +1,79 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature +; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --debug-counter=assume-builder-counter-skip=5,assume-builder-counter-count=1 -S %s | FileCheck %s --check-prefixes=COUNTER1 +; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --debug-counter=assume-builder-counter-skip=1,assume-builder-counter-count=3 -S %s | FileCheck %s --check-prefixes=COUNTER2 +; RUN: opt -passes='assume-builder,verify' --enable-knowledge-retention --debug-counter=assume-builder-counter-skip=2,assume-builder-counter-count=200 -S %s | FileCheck %s --check-prefixes=COUNTER3 + +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 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) { +; COUNTER1-LABEL: define {{[^@]+}}@test +; COUNTER1-SAME: (i32* [[P:%.*]], i32* [[P1:%.*]], i32* [[P2:%.*]], i32* [[P3:%.*]]) +; COUNTER1-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null) +; COUNTER1-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) +; COUNTER1-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #5 +; COUNTER1-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) +; COUNTER1-NEXT: call void @func(i32* [[P1]], i32* [[P]]) +; COUNTER1-NEXT: call void @func_strbool(i32* [[P1]]) +; COUNTER1-NEXT: call void @func(i32* dereferenceable(32) [[P]], i32* dereferenceable(8) [[P]]) +; COUNTER1-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8) ] +; COUNTER1-NEXT: call void @func_many(i32* align 8 [[P1]]) +; COUNTER1-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]]) +; COUNTER1-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]]) +; COUNTER1-NEXT: ret void +; +; COUNTER2-LABEL: define {{[^@]+}}@test +; COUNTER2-SAME: (i32* [[P:%.*]], i32* [[P1:%.*]], i32* [[P2:%.*]], i32* [[P3:%.*]]) +; COUNTER2-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null) +; COUNTER2-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12), "nonnull"(i32* [[P]]) ] +; COUNTER2-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) +; COUNTER2-NEXT: call void @llvm.assume(i1 true) [ "cold"() ] +; COUNTER2-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #5 +; COUNTER2-NEXT: call void @llvm.assume(i1 true) [ "cold"() ] +; COUNTER2-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) +; COUNTER2-NEXT: call void @func(i32* [[P1]], i32* [[P]]) +; COUNTER2-NEXT: call void @func_strbool(i32* [[P1]]) +; COUNTER2-NEXT: call void @func(i32* dereferenceable(32) [[P]], i32* dereferenceable(8) [[P]]) +; COUNTER2-NEXT: call void @func_many(i32* align 8 [[P1]]) +; COUNTER2-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]]) +; COUNTER2-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]]) +; COUNTER2-NEXT: ret void +; +; COUNTER3-LABEL: define {{[^@]+}}@test +; COUNTER3-SAME: (i32* [[P:%.*]], i32* [[P1:%.*]], i32* [[P2:%.*]], i32* [[P3:%.*]]) +; COUNTER3-NEXT: call void @func(i32* nonnull dereferenceable(16) [[P]], i32* null) +; COUNTER3-NEXT: call void @func(i32* dereferenceable(12) [[P1]], i32* nonnull [[P]]) +; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P1]], i64 12), "cold"() ] +; COUNTER3-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) #5 +; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "cold"() ] +; COUNTER3-NEXT: call void @func_cold(i32* dereferenceable(12) [[P1]]) +; COUNTER3-NEXT: call void @func(i32* [[P1]], i32* [[P]]) +; COUNTER3-NEXT: call void @func_strbool(i32* [[P1]]) +; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(i32* [[P]], i64 32) ] +; COUNTER3-NEXT: call void @func(i32* dereferenceable(32) [[P]], i32* dereferenceable(8) [[P]]) +; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P1]], i64 8) ] +; COUNTER3-NEXT: call void @func_many(i32* align 8 [[P1]]) +; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "align"(i32* [[P2]], i64 8), "nonnull"(i32* [[P3]]) ] +; COUNTER3-NEXT: call void @func_argattr(i32* [[P2]], i32* [[P3]]) +; COUNTER3-NEXT: call void @llvm.assume(i1 true) [ "nonnull"(i32* [[P1]]), "nonnull"(i32* [[P]]) ] +; COUNTER3-NEXT: call void @func(i32* nonnull [[P1]], i32* nonnull [[P]]) +; COUNTER3-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(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) + ret void +}