Skip to content

Commit 3cfaea2

Browse files
committed
Reland "[pgo] Avoid introducing relocations by using private alias"
In many cases, we can use an alias to avoid a symbolic relocations, instead of using the public, interposable symbol. When the instrumented function is in a COMDAT, we can use a hidden alias, and still avoid references to discarded sections. This disables the failing runtime test on Windows, since the compiler options (-fPIC) are unsupported on that platform. Reviewed By: phosek Differential Revision: https://reviews.llvm.org/D137982
1 parent 8c0e401 commit 3cfaea2

File tree

4 files changed

+199
-3
lines changed

4 files changed

+199
-3
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Check that instrprof does not introduce references to discarded sections when
2+
// using comdats.
3+
//
4+
// Occasionally, it is possible that the same function can be compiled in
5+
// different TUs with slightly different linkages, e.g., due to different
6+
// compiler options. However, if these are comdat functions, a single
7+
// implementation will be chosen at link time. we want to ensure that the
8+
// profiling data does not contain a reference to the discarded section.
9+
10+
// UNSUPPORTED: windows
11+
12+
// RUN: mkdir -p %t.d
13+
// RUN: %clangxx_pgogen -O2 -fPIC -ffunction-sections -fdata-sections -c %s -o %t.d/a1.o -DOBJECT_1 -mllvm -disable-preinline
14+
// RUN: %clangxx_pgogen -O2 -fPIC -ffunction-sections -fdata-sections -c %s -o %t.d/a2.o
15+
// RUN: %clangxx -fPIC -shared -o %t.d/liba.so %t.d/a1.o %t.d/a2.o 2>&1 | FileCheck %s --allow-empty
16+
17+
// Ensure that we don't get an error when linking
18+
// CHECK-NOT: relocation refers to a discarded section: .text._ZN1CIiE1fEi
19+
20+
template <typename T> struct C {
21+
void f(T x);
22+
int g(T x) {
23+
f(x);
24+
return v;
25+
}
26+
int v;
27+
};
28+
29+
template <typename T>
30+
#ifdef OBJECT_1
31+
__attribute__((weak))
32+
#else
33+
__attribute__((noinline))
34+
#endif
35+
void C<T>::f(T x) {
36+
v += x;
37+
}
38+
39+
#ifdef OBJECT_1
40+
int foo() {
41+
C<int> c;
42+
c.f(1);
43+
return c.g(2);
44+
}
45+
#else
46+
int bar() {
47+
C<int> c;
48+
c.f(3);
49+
return c.g(4);
50+
}
51+
#endif

llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,36 @@ static inline bool shouldRecordFunctionAddr(Function *F) {
823823
return F->hasAddressTaken() || F->hasLinkOnceLinkage();
824824
}
825825

826+
static inline Constant *getFuncAddrForProfData(Function *Fn) {
827+
auto *Int8PtrTy = Type::getInt8PtrTy(Fn->getContext());
828+
// Store a nullptr in __llvm_profd, if we shouldn't use a real address
829+
if (!shouldRecordFunctionAddr(Fn))
830+
return ConstantPointerNull::get(Int8PtrTy);
831+
832+
// If we can't use an alias, we must use the public symbol, even though this
833+
// may require a symbolic relocation. When the function has local linkage, we
834+
// can use the symbol directly without introducing relocations.
835+
if (Fn->isDeclarationForLinker() || Fn->hasLocalLinkage())
836+
return ConstantExpr::getBitCast(Fn, Int8PtrTy);
837+
838+
// When possible use a private alias to avoid symbolic relocations.
839+
auto *GA = GlobalAlias::create(GlobalValue::LinkageTypes::PrivateLinkage,
840+
Fn->getName(), Fn);
841+
842+
// When the instrumented function is a COMDAT function, we cannot use a
843+
// private alias. If we did, we would create reference to a local label in
844+
// this function's section. If this version of the function isn't selected by
845+
// the linker, then the metadata would introduce a reference to a discarded
846+
// section. So, for COMDAT functions, we need to adjust the linkage of the
847+
// alias. Using hidden visibility avoids a dynamic relocation and an entry in
848+
// the dynamic symbol table.
849+
if (Fn->hasComdat()) {
850+
GA->setLinkage(Fn->getLinkage());
851+
GA->setVisibility(GlobalValue::VisibilityTypes::HiddenVisibility);
852+
}
853+
return ConstantExpr::getBitCast(GA, Int8PtrTy);
854+
}
855+
826856
static bool needsRuntimeRegistrationOfSectionRange(const Triple &TT) {
827857
// Don't do this for Darwin. compiler-rt uses linker magic.
828858
if (TT.isOSDarwin())
@@ -1014,9 +1044,7 @@ InstrProfiling::getOrCreateRegionCounters(InstrProfInstBase *Inc) {
10141044
};
10151045
auto *DataTy = StructType::get(Ctx, makeArrayRef(DataTypes));
10161046

1017-
Constant *FunctionAddr = shouldRecordFunctionAddr(Fn)
1018-
? ConstantExpr::getBitCast(Fn, Int8PtrTy)
1019-
: ConstantPointerNull::get(Int8PtrTy);
1047+
Constant *FunctionAddr = getFuncAddrForProfData(Fn);
10201048

10211049
Constant *Int16ArrayVals[IPVK_Last + 1];
10221050
for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind)

llvm/test/Transforms/PGOProfile/comdat.ll

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
$linkonceodr = comdat any
66
$weakodr = comdat any
7+
$weak = comdat any
8+
$linkonce = comdat any
79

810
;; profc/profd have hash suffixes. This definition doesn't have value profiling,
911
;; so definitions with the same name in other modules must have the same CFG and
@@ -27,3 +29,32 @@ define linkonce_odr void @linkonceodr() comdat {
2729
define weak_odr void @weakodr() comdat {
2830
ret void
2931
}
32+
33+
;; weak in a comdat is not renamed. There is no guarantee that definitions in
34+
;; other modules don't have value profiling. profd should be conservatively
35+
;; non-private to prevent a caller from referencing a non-prevailing profd,
36+
;; causing a linker error.
37+
; ELF: @__profc_weak = weak hidden global {{.*}} comdat, align 8
38+
; ELF: @__profd_weak = weak hidden global {{.*}} comdat($__profc_weak), align 8
39+
; COFF: @__profc_weak = weak hidden global {{.*}} comdat, align 8
40+
; COFF: @__profd_weak = weak hidden global {{.*}} comdat, align 8
41+
define weak void @weak() comdat {
42+
ret void
43+
}
44+
45+
;; profc/profd have hash suffixes. This definition doesn't have value profiling,
46+
;; so definitions with the same name in other modules must have the same CFG and
47+
;; cannot have value profiling, either. profd can be made private for ELF.
48+
; ELF: @__profc_linkonce.[[#]] = linkonce hidden global {{.*}} comdat, align 8
49+
; ELF: @__profd_linkonce.[[#]] = private global {{.*}} comdat($__profc_linkonce.[[#]]), align 8
50+
; COFF: @__profc_linkonce.[[#]] = linkonce hidden global {{.*}} comdat, align 8
51+
; COFF: @__profd_linkonce.[[#]] = linkonce hidden global {{.*}} comdat, align 8
52+
define linkonce void @linkonce() comdat {
53+
ret void
54+
}
55+
56+
; Check that comdat aliases are hidden for all linkage types
57+
; ELF: @linkonceodr.1 = linkonce_odr hidden alias void (), ptr @linkonceodr
58+
; ELF: @weakodr.2 = weak_odr hidden alias void (), ptr @weakodr
59+
; ELF: @weak.3 = weak hidden alias void (), ptr @weak
60+
; ELF: @linkonce.4 = linkonce hidden alias void (), ptr @linkonce
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
2+
; RUN: opt -S -passes=pgo-instr-gen,instrprof < %s | FileCheck %s
3+
4+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
5+
target triple = "x86_64-unknown-linux-gnu"
6+
7+
;; Test that we use private aliases to reference function addresses inside profile data
8+
; CHECK: @__profd_foo = private global {{.*}}, ptr @foo.1,
9+
; CHECK-NOT: @__profd_foo = private global {{.*}}, ptr @foo,
10+
; CHECK: @[[__PROFC_WEAK:[a-zA-Z0-9_$"\\.-]+]] = weak hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
11+
; CHECK: @[[__PROFD_WEAK:[a-zA-Z0-9_$"\\.-]+]] = private global { i64, i64, i64, ptr, ptr, i32, [2 x i16] } { i64 -5028622335731970946, i64 742261418966908927, i64 sub (i64 ptrtoint (ptr @__profc_weak to i64), i64 ptrtoint (ptr @__profd_weak to i64)), ptr @weak.2, ptr null, i32 1, [2 x i16] zeroinitializer }, section "__llvm_prf_data", comdat($__profc_weak), align 8
12+
; CHECK: @[[__PROFC_LINKONCE:[a-zA-Z0-9_$"\\.-]+]] = linkonce hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
13+
; CHECK: @[[__PROFD_LINKONCE:[a-zA-Z0-9_$"\\.-]+]] = private global { i64, i64, i64, ptr, ptr, i32, [2 x i16] } { i64 -121947654961992603, i64 742261418966908927, i64 sub (i64 ptrtoint (ptr @__profc_linkonce to i64), i64 ptrtoint (ptr @__profd_linkonce to i64)), ptr @linkonce.3, ptr null, i32 1, [2 x i16] zeroinitializer }, section "__llvm_prf_data", comdat($__profc_linkonce), align 8
14+
; CHECK: @[[__PROFC_WEAKODR:[a-zA-Z0-9_$"\\.-]+]] = weak_odr hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
15+
; CHECK: @[[__PROFD_WEAKODR:[a-zA-Z0-9_$"\\.-]+]] = private global { i64, i64, i64, ptr, ptr, i32, [2 x i16] } { i64 -4807837289933096997, i64 742261418966908927, i64 sub (i64 ptrtoint (ptr @__profc_weakodr to i64), i64 ptrtoint (ptr @__profd_weakodr to i64)), ptr @weakodr.4, ptr null, i32 1, [2 x i16] zeroinitializer }, section "__llvm_prf_data", comdat($__profc_weakodr), align 8
16+
; CHECK: @[[__PROFC_LINKONCEODR:[a-zA-Z0-9_$"\\.-]+]] = linkonce_odr hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
17+
; CHECK: @[[__PROFD_LINKONCEODR:[a-zA-Z0-9_$"\\.-]+]] = private global { i64, i64, i64, ptr, ptr, i32, [2 x i16] } { i64 4214081367395809689, i64 742261418966908927, i64 sub (i64 ptrtoint (ptr @__profc_linkonceodr to i64), i64 ptrtoint (ptr @__profd_linkonceodr to i64)), ptr @linkonceodr.5, ptr null, i32 1, [2 x i16] zeroinitializer }, section "__llvm_prf_data", comdat($__profc_linkonceodr), align 8
18+
; CHECK: @[[__PROFC_AVAILABLE_EXTERNALLY_742261418966908927:[a-zA-Z0-9_$"\\.-]+]] = linkonce_odr hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
19+
; CHECK: @[[__PROFD_AVAILABLE_EXTERNALLY_742261418966908927:[a-zA-Z0-9_$"\\.-]+]] = private global { i64, i64, i64, ptr, ptr, i32, [2 x i16] } { i64 -8510055422695886042, i64 742261418966908927, i64 sub (i64 ptrtoint (ptr @__profc_available_externally.742261418966908927 to i64), i64 ptrtoint (ptr @__profd_available_externally.742261418966908927 to i64)), ptr null, ptr null, i32 1, [2 x i16] zeroinitializer }, section "__llvm_prf_data", comdat($__profc_available_externally.742261418966908927), align 8
20+
21+
;; Ensure when not instrumenting a non-comdat function, then if we generate an
22+
;; alias, then it is private. We check comdat versions in comdat.ll
23+
; CHECK: @foo.1 = private alias i32 (i32), ptr @foo
24+
; CHECK: @[[WEAK_2:[a-zA-Z0-9_$"\\.-]+]] = private alias void (), ptr @weak
25+
; CHECK: @[[LINKONCE_3:[a-zA-Z0-9_$"\\.-]+]] = private alias void (), ptr @linkonce
26+
; CHECK: @[[WEAKODR_4:[a-zA-Z0-9_$"\\.-]+]] = private alias void (), ptr @weakodr
27+
; CHECK: @[[LINKONCEODR_5:[a-zA-Z0-9_$"\\.-]+]] = private alias void (), ptr @linkonceodr
28+
29+
;; We should never generate an alias for available_externally functions
30+
; CHECK-NOT: @[[AVAILABLE_EXTERNALLY_6:[a-zA-Z0-9_$"\\.-]+]] = private alias void (), ptr @available_externally
31+
32+
define i32 @foo(i32 %0) {
33+
; CHECK-LABEL: @foo(
34+
; CHECK-NEXT: entry:
35+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_foo, align 8
36+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
37+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_foo, align 8
38+
; CHECK-NEXT: ret i32 0
39+
entry:
40+
ret i32 0
41+
}
42+
43+
define weak void @weak() {
44+
; CHECK-LABEL: @weak(
45+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_weak, align 8
46+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
47+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_weak, align 8
48+
; CHECK-NEXT: ret void
49+
ret void
50+
}
51+
52+
define linkonce void @linkonce() {
53+
; CHECK-LABEL: @linkonce(
54+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_linkonce, align 8
55+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
56+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_linkonce, align 8
57+
; CHECK-NEXT: ret void
58+
ret void
59+
}
60+
61+
define weak_odr void @weakodr() {
62+
; CHECK-LABEL: @weakodr(
63+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_weakodr, align 8
64+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
65+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_weakodr, align 8
66+
; CHECK-NEXT: ret void
67+
ret void
68+
}
69+
70+
define linkonce_odr void @linkonceodr() {
71+
; CHECK-LABEL: @linkonceodr(
72+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_linkonceodr, align 8
73+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
74+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_linkonceodr, align 8
75+
; CHECK-NEXT: ret void
76+
ret void
77+
}
78+
79+
define available_externally void @available_externally(){
80+
; CHECK-LABEL: @available_externally(
81+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_available_externally.742261418966908927, align 8
82+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
83+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_available_externally.742261418966908927, align 8
84+
; CHECK-NEXT: ret void
85+
ret void
86+
}

0 commit comments

Comments
 (0)