Skip to content

Commit b35b7da

Browse files
author
Andy Kaylor
committed
[PGO] Attach appropriate funclet operand bundles to value profiling instrumentation calls
Patch by Chris Chrulski When generating value profiling instrumentation, ensure the call gets the correct funclet token, otherwise WinEHPrepare will turn the call (and all subsequent instructions) into unreachable. Differential Revision: https://reviews.llvm.org/D73221
1 parent 44b865f commit b35b7da

File tree

4 files changed

+198
-4
lines changed

4 files changed

+198
-4
lines changed

llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,11 +638,19 @@ void InstrProfiling::lowerValueProfileInst(InstrProfValueProfileInst *Ind) {
638638
llvm::InstrProfValueKind::IPVK_MemOPSize);
639639
CallInst *Call = nullptr;
640640
auto *TLI = &GetTLI(*Ind->getFunction());
641+
642+
// To support value profiling calls within Windows exception handlers, funclet
643+
// information contained within operand bundles needs to be copied over to
644+
// the library call. This is required for the IR to be processed by the
645+
// WinEHPrepare pass.
646+
SmallVector<OperandBundleDef, 1> OpBundles;
647+
Ind->getOperandBundlesAsDefs(OpBundles);
641648
if (!IsRange) {
642649
Value *Args[3] = {Ind->getTargetValue(),
643650
Builder.CreateBitCast(DataVar, Builder.getInt8PtrTy()),
644651
Builder.getInt32(Index)};
645-
Call = Builder.CreateCall(getOrInsertValueProfilingCall(*M, *TLI), Args);
652+
Call = Builder.CreateCall(getOrInsertValueProfilingCall(*M, *TLI), Args,
653+
OpBundles);
646654
} else {
647655
Value *Args[6] = {
648656
Ind->getTargetValue(),
@@ -651,8 +659,8 @@ void InstrProfiling::lowerValueProfileInst(InstrProfValueProfileInst *Ind) {
651659
Builder.getInt64(MemOPSizeRangeStart),
652660
Builder.getInt64(MemOPSizeRangeLast),
653661
Builder.getInt64(MemOPSizeLarge == 0 ? INT64_MIN : MemOPSizeLarge)};
654-
Call =
655-
Builder.CreateCall(getOrInsertValueProfilingCall(*M, *TLI, true), Args);
662+
Call = Builder.CreateCall(getOrInsertValueProfilingCall(*M, *TLI, true),
663+
Args, OpBundles);
656664
}
657665
if (auto AK = TLI->getExtAttrForI32Param(false))
658666
Call->addParamAttr(2, AK);

llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "ValueProfileCollector.h"
5353
#include "llvm/ADT/APInt.h"
5454
#include "llvm/ADT/ArrayRef.h"
55+
#include "llvm/ADT/MapVector.h"
5556
#include "llvm/ADT/STLExtras.h"
5657
#include "llvm/ADT/SmallVector.h"
5758
#include "llvm/ADT/Statistic.h"
@@ -63,6 +64,7 @@
6364
#include "llvm/Analysis/BlockFrequencyInfo.h"
6465
#include "llvm/Analysis/BranchProbabilityInfo.h"
6566
#include "llvm/Analysis/CFG.h"
67+
#include "llvm/Analysis/EHPersonalities.h"
6668
#include "llvm/Analysis/LoopInfo.h"
6769
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
6870
#include "llvm/Analysis/ProfileSummaryInfo.h"
@@ -799,6 +801,37 @@ BasicBlock *FuncPGOInstrumentation<Edge, BBInfo>::getInstrBB(Edge *E) {
799801
return canInstrument(InstrBB);
800802
}
801803

804+
// When generating value profiling calls on Windows routines that make use of
805+
// handler funclets for exception processing an operand bundle needs to attached
806+
// to the called function. This routine will set \p OpBundles to contain the
807+
// funclet information, if any is needed, that should be placed on the generated
808+
// value profiling call for the value profile candidate call.
809+
static void
810+
populateEHOperandBundle(VPCandidateInfo &Cand,
811+
DenseMap<BasicBlock *, ColorVector> &BlockColors,
812+
SmallVectorImpl<OperandBundleDef> &OpBundles) {
813+
auto *OrigCall = dyn_cast<CallBase>(Cand.AnnotatedInst);
814+
if (OrigCall && !isa<IntrinsicInst>(OrigCall)) {
815+
// The instrumentation call should belong to the same funclet as a
816+
// non-intrinsic call, so just copy the operand bundle, if any exists.
817+
Optional<OperandBundleUse> ParentFunclet =
818+
OrigCall->getOperandBundle(LLVMContext::OB_funclet);
819+
if (ParentFunclet)
820+
OpBundles.emplace_back(OperandBundleDef(*ParentFunclet));
821+
} else {
822+
// Intrinsics or other instructions do not get funclet information from the
823+
// front-end. Need to use the BlockColors that was computed by the routine
824+
// colorEHFunclets to determine whether a funclet is needed.
825+
if (!BlockColors.empty()) {
826+
const ColorVector &CV = BlockColors.find(OrigCall->getParent())->second;
827+
assert(CV.size() == 1 && "non-unique color for block!");
828+
Instruction *EHPad = CV.front()->getFirstNonPHI();
829+
if (EHPad->isEHPad())
830+
OpBundles.emplace_back("funclet", EHPad);
831+
}
832+
}
833+
}
834+
802835
// Visit all edge and instrument the edges not in MST, and do value profiling.
803836
// Critical edges will be split.
804837
static void instrumentOneFunc(
@@ -839,6 +872,15 @@ static void instrumentOneFunc(
839872

840873
NumOfPGOICall += FuncInfo.ValueSites[IPVK_IndirectCallTarget].size();
841874

875+
// Intrinsic function calls do not have funclet operand bundles needed for
876+
// Windows exception handling attached to them. However, if value profiling is
877+
// inserted for one of these calls, then a funclet value will need to be set
878+
// on the instrumentation call based on the funclet coloring.
879+
DenseMap<BasicBlock *, ColorVector> BlockColors;
880+
if (F.hasPersonalityFn() &&
881+
isFuncletEHPersonality(classifyEHPersonality(F.getPersonalityFn())))
882+
BlockColors = colorEHFunclets(F);
883+
842884
// For each VP Kind, walk the VP candidates and instrument each one.
843885
for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) {
844886
unsigned SiteIndex = 0;
@@ -860,11 +902,14 @@ static void instrumentOneFunc(
860902
ToProfile = Builder.CreatePtrToInt(Cand.V, Builder.getInt64Ty());
861903
assert(ToProfile && "value profiling Value is of unexpected type");
862904

905+
SmallVector<OperandBundleDef, 1> OpBundles;
906+
populateEHOperandBundle(Cand, BlockColors, OpBundles);
863907
Builder.CreateCall(
864908
Intrinsic::getDeclaration(M, Intrinsic::instrprof_value_profile),
865909
{ConstantExpr::getBitCast(FuncInfo.FuncNameVar, I8PtrTy),
866910
Builder.getInt64(FuncInfo.FunctionHash), ToProfile,
867-
Builder.getInt32(Kind), Builder.getInt32(SiteIndex++)});
911+
Builder.getInt32(Kind), Builder.getInt32(SiteIndex++)},
912+
OpBundles);
868913
}
869914
} // IPVK_First <= Kind <= IPVK_Last
870915
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
; RUN: opt < %s -pgo-instr-gen -S | FileCheck %s --check-prefix=GEN
2+
; RUN: opt < %s -pgo-instr-gen -instrprof -S | FileCheck %s --check-prefix=LOWER
3+
4+
; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --check-prefix=GEN
5+
; RUN: opt < %s -passes=pgo-instr-gen,instrprof -S | FileCheck %s --check-prefix=LOWER
6+
7+
; This test is to verify that PGO runtime library calls get created with the
8+
; appropriate operand bundle funclet information when an indirect call
9+
; was marked with as belonging to a particular funclet.
10+
11+
; Test case based on this source:
12+
; extern void may_throw(int);
13+
;
14+
; class base {
15+
; public:
16+
; base() : x(0) {};
17+
; int get_x() const { return x; }
18+
; virtual void update() { x++; }
19+
; int x;
20+
; };
21+
;
22+
; class derived : public base {
23+
; public:
24+
; derived() {}
25+
; virtual void update() { x--; }
26+
; };
27+
;
28+
; void run(base* b, int count) {
29+
; try {
30+
; may_throw(count);
31+
; }
32+
; catch (...) {
33+
; // Virtual function call in exception handler for value profiling.
34+
; b->update();
35+
; }
36+
; }
37+
38+
%class.base = type { i32 (...)**, i32 }
39+
define dso_local void @"?run@@YAXPEAVbase@@H@Z"(%class.base* %b, i32 %count) personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) {
40+
entry:
41+
invoke void @"?may_throw@@YAXH@Z"(i32 %count)
42+
to label %try.cont unwind label %catch.dispatch
43+
44+
catch.dispatch: ; preds = %entry
45+
%tmp = catchswitch within none [label %catch] unwind to caller
46+
47+
catch: ; preds = %catch.dispatch
48+
%tmp1 = catchpad within %tmp [i8* null, i32 64, i8* null]
49+
%tmp2 = bitcast %class.base* %b to void (%class.base*)***
50+
%vtable = load void (%class.base*)**, void (%class.base*)*** %tmp2, align 8
51+
%tmp3 = load void (%class.base*)*, void (%class.base*)** %vtable, align 8
52+
call void %tmp3(%class.base* %b) [ "funclet"(token %tmp1) ]
53+
catchret from %tmp1 to label %try.cont
54+
55+
try.cont: ; preds = %catch, %entry
56+
ret void
57+
}
58+
59+
; GEN: catch:
60+
; GEN: call void @llvm.instrprof.value.profile(
61+
; GEN-SAME: [ "funclet"(token %tmp1) ]
62+
63+
; LOWER: catch:
64+
; LOWER: call void @__llvm_profile_instrument_target(
65+
; LOWER-SAME: [ "funclet"(token %tmp1) ]
66+
67+
declare dso_local void @"?may_throw@@YAXH@Z"(i32)
68+
declare dso_local i32 @__CxxFrameHandler3(...)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
; RUN: opt < %s -pgo-instr-gen -S | FileCheck %s --check-prefix=GEN
2+
; RUN: opt < %s -pgo-instr-gen -instrprof -S | FileCheck %s --check-prefix=LOWER
3+
4+
; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --check-prefix=GEN
5+
; RUN: opt < %s -passes=pgo-instr-gen,instrprof -S | FileCheck %s --check-prefix=LOWER
6+
7+
; This test is to verify that PGO runtime library calls get created with the
8+
; appropriate operand bundle funclet information when a memory intrinsic
9+
; being value profiled is called within an exception handler.
10+
11+
; Test case based on this source:
12+
; #include <memory.h>
13+
;
14+
; extern void may_throw(int);
15+
;
16+
; #define MSG "0123456789012345\0"
17+
; unsigned len = 16;
18+
; char msg[200];
19+
;
20+
; void run(int count) {
21+
; try {
22+
; may_throw(count);
23+
; }
24+
; catch (...) {
25+
; memcpy(msg, MSG, len);
26+
; throw;
27+
; }
28+
; }
29+
30+
%eh.ThrowInfo = type { i32, i32, i32, i32 }
31+
32+
$"??_C@_0BC@CABPINND@Exception?5caught?$AA?$AA@" = comdat any
33+
34+
@"?len@@3IA" = dso_local global i32 16, align 4
35+
@"?msg@@3PADA" = dso_local global [200 x i8] zeroinitializer, align 16
36+
@"??_C@_0BC@CABPINND@Exception?5caught?$AA?$AA@" = linkonce_odr dso_local unnamed_addr constant [18 x i8] c"0123456789012345\00\00", comdat, align 1
37+
38+
define dso_local void @"?run@@YAXH@Z"(i32 %count) personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) {
39+
entry:
40+
invoke void @"?may_throw@@YAXH@Z"(i32 %count)
41+
to label %try.cont unwind label %catch.dispatch
42+
43+
catch.dispatch: ; preds = %entry
44+
%tmp = catchswitch within none [label %catch] unwind to caller
45+
46+
catch: ; preds = %catch.dispatch
47+
%tmp1 = catchpad within %tmp [i8* null, i32 64, i8* null]
48+
%tmp2 = load i32, i32* @"?len@@3IA", align 4
49+
%conv = zext i32 %tmp2 to i64
50+
call void @llvm.memcpy.p0i8.p0i8.i64(
51+
i8* getelementptr inbounds ([200 x i8], [200 x i8]* @"?msg@@3PADA", i64 0, i64 0),
52+
i8* getelementptr inbounds ([18 x i8], [18 x i8]* @"??_C@_0BC@CABPINND@Exception?5caught?$AA?$AA@", i64 0, i64 0),
53+
i64 %conv, i1 false)
54+
call void @_CxxThrowException(i8* null, %eh.ThrowInfo* null) #3 [ "funclet"(token %tmp1) ]
55+
unreachable
56+
57+
try.cont: ; preds = %entry
58+
ret void
59+
}
60+
61+
; GEN: catch:
62+
; GEN: call void @llvm.instrprof.value.profile(
63+
; GEN-SAME: [ "funclet"(token %tmp1) ]
64+
65+
; LOWER: catch:
66+
; LOWER: call void @__llvm_profile_instrument_range(
67+
; LOWER-SAME: [ "funclet"(token %tmp1) ]
68+
69+
declare dso_local void @"?may_throw@@YAXH@Z"(i32)
70+
declare dso_local i32 @__CxxFrameHandler3(...)
71+
72+
declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1)
73+
declare dso_local void @_CxxThrowException(i8*, %eh.ThrowInfo*)

0 commit comments

Comments
 (0)