diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp --- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -57,6 +57,7 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Analysis/AssumptionCache.h" @@ -109,6 +110,15 @@ #define DEBUG_TYPE "wholeprogramdevirt" +STATISTIC(NumDevirtTargets, "Number of whole program devirtualization targets"); +STATISTIC(NumSingleImpl, "Number of single implementation devirtualizations"); +STATISTIC(NumBranchFunnel, "Number of branch funnels"); +STATISTIC(NumUniformRetVal, "Number of uniform return value optimizations"); +STATISTIC(NumUniqueRetVal, "Number of unique return value optimizations"); +STATISTIC(NumVirtConstProp1Bit, + "Number of 1 bit virtual constant propagations"); +STATISTIC(NumVirtConstProp, "Number of virtual constant propagations"); + static cl::opt ClSummaryAction( "wholeprogramdevirt-summary-action", cl::desc("What to do with the summary when running this pass"), @@ -1142,6 +1152,7 @@ if (RemarksEnabled) VCallSite.emitRemark("single-impl", TheFn->stripPointerCasts()->getName(), OREGetter); + NumSingleImpl++; auto &CB = VCallSite.CB; assert(!CB.getCalledFunction() && "devirtualizing direct call?"); IRBuilder<> Builder(&CB); @@ -1247,7 +1258,7 @@ return false; // If so, update each call site to call that implementation directly. - if (RemarksEnabled) + if (RemarksEnabled || AreStatisticsEnabled()) TargetsForSlot[0].WasDevirt = true; bool IsExported = false; @@ -1318,7 +1329,7 @@ return false; // Collect functions devirtualized at least for one call site for stats. - if (PrintSummaryDevirt) + if (PrintSummaryDevirt || AreStatisticsEnabled()) DevirtTargets.insert(TheFn); auto &S = TheFn.getSummaryList()[0]; @@ -1424,6 +1435,7 @@ !FSAttr.getValueAsString().contains("+retpoline")) continue; + NumBranchFunnel++; if (RemarksEnabled) VCallSite.emitRemark("branch-funnel", JT->stripPointerCasts()->getName(), OREGetter); @@ -1515,6 +1527,7 @@ for (auto Call : CSInfo.CallSites) { if (!OptimizedCalls.insert(&Call.CB).second) continue; + NumUniformRetVal++; Call.replaceAndErase( "uniform-ret-val", FnName, RemarksEnabled, OREGetter, ConstantInt::get(cast(Call.CB.getType()), TheRetVal)); @@ -1538,7 +1551,7 @@ } applyUniformRetValOpt(CSInfo, TargetsForSlot[0].Fn->getName(), TheRetVal); - if (RemarksEnabled) + if (RemarksEnabled || AreStatisticsEnabled()) for (auto &&Target : TargetsForSlot) Target.WasDevirt = true; return true; @@ -1631,6 +1644,7 @@ B.CreateICmp(IsOne ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE, Call.VTable, B.CreateBitCast(UniqueMemberAddr, Call.VTable->getType())); Cmp = B.CreateZExt(Cmp, Call.CB.getType()); + NumUniqueRetVal++; Call.replaceAndErase("unique-ret-val", FnName, RemarksEnabled, OREGetter, Cmp); } @@ -1675,7 +1689,7 @@ UniqueMemberAddr); // Update devirtualization statistics for targets. - if (RemarksEnabled) + if (RemarksEnabled || AreStatisticsEnabled()) for (auto &&Target : TargetsForSlot) Target.WasDevirt = true; @@ -1704,11 +1718,13 @@ Value *Bits = B.CreateLoad(Int8Ty, Addr); Value *BitsAndBit = B.CreateAnd(Bits, Bit); auto IsBitSet = B.CreateICmpNE(BitsAndBit, ConstantInt::get(Int8Ty, 0)); + NumVirtConstProp1Bit++; Call.replaceAndErase("virtual-const-prop-1-bit", FnName, RemarksEnabled, OREGetter, IsBitSet); } else { Value *ValAddr = B.CreateBitCast(Addr, RetType->getPointerTo()); Value *Val = B.CreateLoad(RetType, ValAddr); + NumVirtConstProp++; Call.replaceAndErase("virtual-const-prop", FnName, RemarksEnabled, OREGetter, Val); } @@ -1794,7 +1810,7 @@ setAfterReturnValues(TargetsForSlot, AllocAfter, BitWidth, OffsetByte, OffsetBit); - if (RemarksEnabled) + if (RemarksEnabled || AreStatisticsEnabled()) for (auto &&Target : TargetsForSlot) Target.WasDevirt = true; @@ -2282,7 +2298,7 @@ } // Collect functions devirtualized at least for one call site for stats. - if (RemarksEnabled) + if (RemarksEnabled || AreStatisticsEnabled()) for (const auto &T : TargetsForSlot) if (T.WasDevirt) DevirtTargets[std::string(T.Fn->getName())] = T.Fn; @@ -2315,6 +2331,8 @@ } } + NumDevirtTargets += DevirtTargets.size(); + removeRedundantTypeTests(); // Rebuild each global we touched as part of virtual constant propagation to @@ -2406,4 +2424,6 @@ if (PrintSummaryDevirt) for (const auto &DT : DevirtTargets) errs() << "Devirtualized call to " << DT << "\n"; + + NumDevirtTargets += DevirtTargets.size(); } diff --git a/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl.ll b/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl.ll --- a/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl.ll +++ b/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl.ll @@ -1,4 +1,7 @@ -; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt %s 2>&1 | FileCheck %s +; -stats requires asserts +; REQUIRES: asserts + +; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt -stats %s 2>&1 | FileCheck %s target datalayout = "e-p:64:64" target triple = "x86_64-unknown-linux-gnu" @@ -45,3 +48,6 @@ !6 = !DILocation(line: 30, column: 32, scope: !5) !7 = distinct !DISubprogram(name: "vf", linkageName: "_ZN3vt12vfEv", scope: !1, file: !1, line: 13, isLocal: false, isDefinition: true, scopeLine: 13, flags: DIFlagPrototyped, isOptimized: false, unit: !0) !8 = !{i32 0, !"typeid"} + +; CHECK: 1 wholeprogramdevirt - Number of whole program devirtualization targets +; CHECK: 1 wholeprogramdevirt - Number of single implementation devirtualizations diff --git a/llvm/test/Transforms/WholeProgramDevirt/uniform-retval.ll b/llvm/test/Transforms/WholeProgramDevirt/uniform-retval.ll --- a/llvm/test/Transforms/WholeProgramDevirt/uniform-retval.ll +++ b/llvm/test/Transforms/WholeProgramDevirt/uniform-retval.ll @@ -1,8 +1,15 @@ -; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility %s | FileCheck %s +; -stats requires asserts +; REQUIRES: asserts + +; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt -stats %s 2>&1 | FileCheck %s target datalayout = "e-p:64:64" target triple = "x86_64-unknown-linux-gnu" +; CHECK: remark: {{.*}} uniform-ret-val: devirtualized a call to vf1 +; CHECK: remark: {{.*}} devirtualized vf1 +; CHECK: remark: {{.*}} devirtualized vf2 + @vt1 = constant [1 x i8*] [i8* bitcast (i32 (i8*)* @vf1 to i8*)], !type !0 @vt2 = constant [1 x i8*] [i8* bitcast (i32 (i8*)* @vf2 to i8*)], !type !0 @@ -34,3 +41,6 @@ declare void @llvm.assume(i1) !0 = !{i32 0, !"typeid"} + +; CHECK: 2 wholeprogramdevirt - Number of whole program devirtualization targets +; CHECK: 1 wholeprogramdevirt - Number of uniform return value optimizations diff --git a/llvm/test/Transforms/WholeProgramDevirt/unique-retval.ll b/llvm/test/Transforms/WholeProgramDevirt/unique-retval.ll --- a/llvm/test/Transforms/WholeProgramDevirt/unique-retval.ll +++ b/llvm/test/Transforms/WholeProgramDevirt/unique-retval.ll @@ -1,8 +1,16 @@ -; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility %s | FileCheck %s +; -stats requires asserts +; REQUIRES: asserts + +; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt -stats %s 2>&1 | FileCheck %s target datalayout = "e-p:64:64" target triple = "x86_64-unknown-linux-gnu" +; CHECK: remark: {{.*}} unique-ret-val: devirtualized a call to vf0 +; CHECK: remark: {{.*}} unique-ret-val: devirtualized a call to vf0 +; CHECK: remark: {{.*}} devirtualized vf0 +; CHECK: remark: {{.*}} devirtualized vf1 + @vt1 = constant [1 x i8*] [i8* bitcast (i1 (i8*)* @vf0 to i8*)], !type !0 @vt2 = constant [1 x i8*] [i8* bitcast (i1 (i8*)* @vf0 to i8*)], !type !0, !type !1 @vt3 = constant [1 x i8*] [i8* bitcast (i1 (i8*)* @vf1 to i8*)], !type !0, !type !1 @@ -55,3 +63,6 @@ !0 = !{i32 0, !"typeid1"} !1 = !{i32 0, !"typeid2"} + +; CHECK: 2 wholeprogramdevirt - Number of whole program devirtualization targets +; CHECK: 2 wholeprogramdevirt - Number of unique return value optimizations diff --git a/llvm/test/Transforms/WholeProgramDevirt/virtual-const-prop-check.ll b/llvm/test/Transforms/WholeProgramDevirt/virtual-const-prop-check.ll --- a/llvm/test/Transforms/WholeProgramDevirt/virtual-const-prop-check.ll +++ b/llvm/test/Transforms/WholeProgramDevirt/virtual-const-prop-check.ll @@ -1,4 +1,7 @@ -; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt %s 2>&1 | FileCheck %s +; -stats requires asserts +; REQUIRES: asserts + +; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt -stats %s 2>&1 | FileCheck %s ; Skipping vf0i1 is identical to setting public LTO visibility. We don't devirtualize vf0i1 and all other ; virtual call targets. ; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt -wholeprogramdevirt-skip=vf0i1 %s 2>&1 | FileCheck %s --check-prefix=SKIP @@ -162,3 +165,7 @@ ; CHECK: [[T0]] = !{i32 0, !"typeid"} !0 = !{i32 0, !"typeid"} + +; CHECK: 6 wholeprogramdevirt - Number of whole program devirtualization targets +; CHECK: 1 wholeprogramdevirt - Number of virtual constant propagations +; CHECK: 2 wholeprogramdevirt - Number of 1 bit virtual constant propagations