Skip to content

Commit 6e5cbc0

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. Previous versions of this patch allowed the compiler to name the generated alias, but that would only be valid when the functions were local. Since the alias may be used across TUs we use a more deterministic naming convention, and add a .local suffix to the alias name just as we do for relative vtables aliases. This should be safe to land after an incorrect LLD assertion was removed in https://reviews.llvm.org/rG20894a478da224bdd69c91a22a5175b28bc08ed9 which caused assertion failures in LLD on Mac. Reviewed By: phosek Differential Revision: https://reviews.llvm.org/D137982
1 parent 6538808 commit 6e5cbc0

File tree

4 files changed

+200
-3
lines changed

4 files changed

+200
-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: target={{.*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_pgogen -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() + ".local", 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, ArrayRef(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.local = linkonce_odr hidden alias void (), ptr @linkonceodr
58+
; ELF: @weakodr.local = weak_odr hidden alias void (), ptr @weakodr
59+
; ELF: @weak.local = weak hidden alias void (), ptr @weak
60+
; ELF: @linkonce.local = linkonce hidden alias void (), ptr @linkonce
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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.local,
9+
; CHECK-NOT: @__profd_foo = private global {{.*}}, ptr @foo,
10+
11+
; CHECK: @[[__PROFC_WEAK:[a-zA-Z0-9_$"\\.-]+]] = weak hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
12+
; 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.local, ptr null, i32 1, [2 x i16] zeroinitializer }, section "__llvm_prf_data", comdat($__profc_weak), align 8
13+
; CHECK: @[[__PROFC_LINKONCE:[a-zA-Z0-9_$"\\.-]+]] = linkonce hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
14+
; 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.local, ptr null, i32 1, [2 x i16] zeroinitializer }, section "__llvm_prf_data", comdat($__profc_linkonce), align 8
15+
; CHECK: @[[__PROFC_WEAKODR:[a-zA-Z0-9_$"\\.-]+]] = weak_odr hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
16+
; 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.local, ptr null, i32 1, [2 x i16] zeroinitializer }, section "__llvm_prf_data", comdat($__profc_weakodr), align 8
17+
; CHECK: @[[__PROFC_LINKONCEODR:[a-zA-Z0-9_$"\\.-]+]] = linkonce_odr hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
18+
; 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.local, ptr null, i32 1, [2 x i16] zeroinitializer }, section "__llvm_prf_data", comdat($__profc_linkonceodr), align 8
19+
; CHECK: @[[__PROFC_AVAILABLE_EXTERNALLY_742261418966908927:[a-zA-Z0-9_$"\\.-]+]] = linkonce_odr hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", comdat, align 8
20+
; 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
21+
22+
;; Ensure when not instrumenting a non-comdat function, then if we generate an
23+
;; alias, then it is private. We check comdat versions in comdat.ll
24+
; CHECK: @foo.local = private alias i32 (i32), ptr @foo
25+
; CHECK: @[[WEAK_2:[a-zA-Z0-9_$"\\.-]+]].local = private alias void (), ptr @weak
26+
; CHECK: @[[LINKONCE_3:[a-zA-Z0-9_$"\\.-]+]].local = private alias void (), ptr @linkonce
27+
; CHECK: @[[WEAKODR_4:[a-zA-Z0-9_$"\\.-]+]].local = private alias void (), ptr @weakodr
28+
; CHECK: @[[LINKONCEODR_5:[a-zA-Z0-9_$"\\.-]+]].local = private alias void (), ptr @linkonceodr
29+
30+
;; We should never generate an alias for available_externally functions
31+
; CHECK-NOT: @[[AVAILABLE_EXTERNALLY_6:[a-zA-Z0-9_$"\\.-]+]] = private alias void (), ptr @available_externally
32+
33+
define i32 @foo(i32 %0) {
34+
; CHECK-LABEL: @foo(
35+
; CHECK-NEXT: entry:
36+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_foo, align 8
37+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
38+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_foo, align 8
39+
; CHECK-NEXT: ret i32 0
40+
entry:
41+
ret i32 0
42+
}
43+
44+
define weak void @weak() {
45+
; CHECK-LABEL: @weak(
46+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_weak, align 8
47+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
48+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_weak, align 8
49+
; CHECK-NEXT: ret void
50+
ret void
51+
}
52+
53+
define linkonce void @linkonce() {
54+
; CHECK-LABEL: @linkonce(
55+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_linkonce, align 8
56+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
57+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_linkonce, align 8
58+
; CHECK-NEXT: ret void
59+
ret void
60+
}
61+
62+
define weak_odr void @weakodr() {
63+
; CHECK-LABEL: @weakodr(
64+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_weakodr, align 8
65+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
66+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_weakodr, align 8
67+
; CHECK-NEXT: ret void
68+
ret void
69+
}
70+
71+
define linkonce_odr void @linkonceodr() {
72+
; CHECK-LABEL: @linkonceodr(
73+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_linkonceodr, align 8
74+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
75+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_linkonceodr, align 8
76+
; CHECK-NEXT: ret void
77+
ret void
78+
}
79+
80+
define available_externally void @available_externally(){
81+
; CHECK-LABEL: @available_externally(
82+
; CHECK-NEXT: [[PGOCOUNT:%.*]] = load i64, ptr @__profc_available_externally.742261418966908927, align 8
83+
; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[PGOCOUNT]], 1
84+
; CHECK-NEXT: store i64 [[TMP1]], ptr @__profc_available_externally.742261418966908927, align 8
85+
; CHECK-NEXT: ret void
86+
ret void
87+
}

0 commit comments

Comments
 (0)