Skip to content

Commit c587171

Browse files
Reland "[PGO][GlobalValue][LTO]In GlobalValues::getGlobalIdentifier, use semicolon as delimiter for local-linkage varibles. " (#75860)
Fixed build-bot failures caught by post-submit tests 1) Add the list of command line tools needed by new compiler-rt test into dependency. 2) Use `starts_with` to replace deprecated `startswith`. **Original commit message** Commit fe05193 (phab D156569), IRPGO names uses format `[<filepath>;]<linkage-name>` while prior format is `[<filepath>:<mangled-name>`. The format change would break the use case demonstrated in (updated) `llvm/test/Transforms/PGOProfile/thinlto_indirect_call_promotion.ll` and `compiler-rt/test/profile/instrprof-thinlto-indirect-call-promotion.cpp` This patch changes `GlobalValues::getGlobalIdentifer` to use the semicolon. To elaborate on the scenario how things break without this PR 1. IRPGO raw profiles stores (compressed) IRPGO names of functions in one section, and per-function profile data in another section. The [NameRef](https://github.com/llvm/llvm-project/blob/fc715e4cd942612a091097339841733757b53824/compiler-rt/include/profile/InstrProfData.inc#L72) field in per-function profile data is the MD5 hash of IRPGO names. 2. When raw profiles are converted to indexed format profiles, the profiled address is [mapped](https://github.com/llvm/llvm-project/blob/fc715e4cd942612a091097339841733757b53824/llvm/lib/ProfileData/InstrProf.cpp#L876-L885) to the MD5 hash of the callee. 3. In `pgo-instr-use` thin-lto prelink pipeline, MD5 hash of IRPGO names will be [annotated](https://github.com/llvm/llvm-project/blob/fc715e4cd942612a091097339841733757b53824/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp#L1707) as value profiles, and used to import indirect-call-prom candidates. If the annotated MD5 hash is computed from the new format while import uses the prior format, the callee cannot be imported. * `compiler-rt/test/profile/instrprof-thinlto-indirect-call-promotion.cpp` is added to have an end-to-end test. * `llvm/test/Transforms/PGOProfile/thinlto_indirect_call_promotion.ll` is updated to have better test coverage from another aspect (as runtime tests are more sensitive to the environment and may be skipped by some contributors)
1 parent 47db1e2 commit c587171

18 files changed

+362
-127
lines changed

compiler-rt/test/profile/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ set(PROFILE_TESTSUITES)
66
set(PROFILE_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} compiler-rt-headers)
77
list(APPEND PROFILE_TEST_DEPS profile)
88
if(NOT COMPILER_RT_STANDALONE_BUILD)
9-
list(APPEND PROFILE_TEST_DEPS llvm-profdata llvm-cov)
9+
list(APPEND PROFILE_TEST_DEPS llvm-cov llvm-dis llvm-lto llvm-profdata opt)
1010
if(NOT APPLE AND COMPILER_RT_HAS_LLD AND "lld" IN_LIST LLVM_ENABLE_PROJECTS)
1111
list(APPEND PROFILE_TEST_DEPS lld)
1212
endif()
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// This is a regression test for ThinLTO indirect-call-promotion when candidate
2+
// callees need to be imported from another IR module. In the C++ test case,
3+
// `main` calls `global_func` which is defined in another module. `global_func`
4+
// has two indirect callees, one has external linkage and one has local linkage.
5+
// All three functions should be imported into the IR module of main.
6+
7+
// What the test does:
8+
// - Generate raw profiles from executables and convert it to indexed profiles.
9+
// During the conversion, a profiled callee address in raw profiles will be
10+
// converted to function hash in indexed profiles.
11+
// - Run IRPGO profile use and ThinTLO prelink pipeline and get LLVM bitcodes
12+
// for both cpp files in the C++ test case.
13+
// - Generate ThinLTO summary file with LLVM bitcodes, and run `function-import` pass.
14+
// - Run `pgo-icall-prom` pass for the IR module which needs to import callees.
15+
16+
// Use lld as linker for more robust test. We need to REQUIRE LLVMgold.so for
17+
// LTO if default linker is GNU ld or gold anyway.
18+
// REQUIRES: lld-available
19+
20+
// Test should fail where linkage-name and mangled-name diverges, see issue https://github.com/llvm/llvm-project/issues/74565).
21+
// Currently, this name divergence happens on Mach-O object file format, or on
22+
// many (but not all) 32-bit Windows systems.
23+
//
24+
// XFAIL: system-darwin
25+
//
26+
// Mark 32-bit Windows as UNSUPPORTED for now as opposed to XFAIL. This test
27+
// should fail on many (but not all) 32-bit Windows systems and succeed on the
28+
// rest. The flexibility in triple string parsing makes it tricky to capture
29+
// both sets accurately. i[3-9]86 specifies arch as Triple::ArchType::x86, (win32|windows)
30+
// specifies OS as Triple::OS::Win32
31+
//
32+
// UNSUPPORTED: target={{i.86.*windows.*}}
33+
34+
// RUN: rm -rf %t && split-file %s %t && cd %t
35+
36+
// Do setup work for all below tests.
37+
// Generate raw profiles from real programs and convert it into indexed profiles.
38+
// Use clangxx_pgogen for IR level instrumentation for C++.
39+
// RUN: %clangxx_pgogen -fuse-ld=lld -O2 lib.cpp main.cpp -o main
40+
// RUN: env LLVM_PROFILE_FILE=main.profraw %run ./main
41+
// RUN: llvm-profdata merge main.profraw -o main.profdata
42+
43+
// Use profile on lib and get bitcode, test that local function callee0 has
44+
// expected !PGOFuncName metadata and external function callee1 doesn't have
45+
// !PGOFuncName metadata. Explicitly skip ICP pass to test ICP happens as
46+
// expected in the IR module that imports functions from lib.
47+
// RUN: %clang -mllvm -disable-icp -fprofile-use=main.profdata -flto=thin -O2 -c lib.cpp -o lib.bc
48+
// RUN: llvm-dis lib.bc -o - | FileCheck %s --check-prefix=PGOName
49+
50+
// Use profile on main and get bitcode.
51+
// RUN: %clang -fprofile-use=main.profdata -flto=thin -O2 -c main.cpp -o main.bc
52+
53+
// Run llvm-lto to get summary file.
54+
// RUN: llvm-lto -thinlto -o summary main.bc lib.bc
55+
56+
// Test the imports of functions. Default import thresholds would work but do
57+
// explicit override to be more futureproof. Note all functions have one basic
58+
// block with a function-entry-count of one, so they are actually hot functions
59+
// per default profile summary hotness cutoff.
60+
// RUN: opt -passes=function-import -import-instr-limit=100 -import-cold-multiplier=1 -summary-file summary.thinlto.bc main.bc -o main.import.bc -print-imports 2>&1 | FileCheck %s --check-prefix=IMPORTS
61+
// Test that '_Z11global_funcv' has indirect calls annotated with value profiles.
62+
// RUN: llvm-dis main.import.bc -o - | FileCheck %s --check-prefix=IR
63+
64+
// Test that both candidates are ICP'ed and there is no `!VP` in the IR.
65+
// RUN: opt main.import.bc -icp-lto -passes=pgo-icall-prom -S -pass-remarks=pgo-icall-prom 2>&1 | FileCheck %s --check-prefixes=ICP-IR,ICP-REMARK --implicit-check-not="!VP"
66+
67+
// IMPORTS: main.cpp: Import _Z7callee1v
68+
// IMPORTS: main.cpp: Import _ZL7callee0v.llvm.[[#]]
69+
// IMPORTS: main.cpp: Import _Z11global_funcv
70+
71+
// PGOName: define {{(dso_local )?}}void @_Z7callee1v() #[[#]] !prof ![[#]] {
72+
// PGOName: define internal void @_ZL7callee0v() #[[#]] !prof ![[#]] !PGOFuncName ![[#MD:]] {
73+
// PGOName: ![[#MD]] = !{!"{{.*}}lib.cpp;_ZL7callee0v"}
74+
75+
// IR-LABEL: define available_externally {{.*}} void @_Z11global_funcv() {{.*}} !prof ![[#]] {
76+
// IR-NEXT: entry:
77+
// IR-NEXT: %0 = load ptr, ptr @calleeAddrs
78+
// IR-NEXT: tail call void %0(), !prof ![[#PROF1:]]
79+
// IR-NEXT: %1 = load ptr, ptr getelementptr inbounds ([2 x ptr], ptr @calleeAddrs,
80+
// IR-NEXT: tail call void %1(), !prof ![[#PROF2:]]
81+
82+
// The GUID of indirect callee is the MD5 hash of `/path/to/lib.cpp;_ZL7callee0v`
83+
// that depends on the directory. Use [[#]] for its MD5 hash.
84+
// Use {{.*}} for integer types so the test works on 32-bit and 64-bit systems.
85+
// IR: ![[#PROF1]] = !{!"VP", i32 0, {{.*}} 1, {{.*}} [[#]], {{.*}} 1}
86+
// IR: ![[#PROF2]] = !{!"VP", i32 0, {{.*}} 1, {{.*}} -3993653843325621743, {{.*}} 1}
87+
88+
// ICP-REMARK: Promote indirect call to _ZL7callee0v.llvm.[[#]] with count 1 out of 1
89+
// ICP-REMARK: Promote indirect call to _Z7callee1v with count 1 out of 1
90+
91+
// ICP-IR: br i1 %[[#]], label %if.true.direct_targ, label %if.false.orig_indirect, !prof ![[#BRANCH_WEIGHT1:]]
92+
// ICP-IR: br i1 %[[#]], label %if.true.direct_targ1, label %if.false.orig_indirect2, !prof ![[#BRANCH_WEIGHT1]]
93+
// ICP-IR: ![[#BRANCH_WEIGHT1]] = !{!"branch_weights", i32 1, i32 0}
94+
95+
//--- lib.h
96+
void global_func();
97+
98+
//--- lib.cpp
99+
#include "lib.h"
100+
static void callee0() {}
101+
void callee1() {}
102+
typedef void (*FPT)();
103+
FPT calleeAddrs[] = {callee0, callee1};
104+
// `global_func`` might call one of two indirect callees. callee0 has internal
105+
// linkage and callee1 has external linkage.
106+
void global_func() {
107+
FPT fp = calleeAddrs[0];
108+
fp();
109+
fp = calleeAddrs[1];
110+
fp();
111+
}
112+
113+
//--- main.cpp
114+
#include "lib.h"
115+
int main() { global_func(); }

llvm/include/llvm/IR/GlobalValue.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ namespace Intrinsic {
4141
typedef unsigned ID;
4242
} // end namespace Intrinsic
4343

44+
// Choose ';' as the delimiter. ':' was used once but it doesn't work well for
45+
// Objective-C functions which commonly have :'s in their names.
46+
inline constexpr char kGlobalIdentifierDelimiter = ';';
47+
4448
class GlobalValue : public Constant {
4549
public:
4650
/// An enumeration for the kinds of linkage for global values.

llvm/include/llvm/ProfileData/InstrProf.h

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ inline StringRef getInstrProfCounterBiasVarName() {
171171
/// Return the marker used to separate PGO names during serialization.
172172
inline StringRef getInstrProfNameSeparator() { return "\01"; }
173173

174+
/// Please use getIRPGOFuncName for LLVM IR instrumentation. This function is
175+
/// for front-end (Clang, etc) instrumentation.
174176
/// Return the modified name for function \c F suitable to be
175177
/// used the key for profile lookup. Variable \c InLTO indicates if this
176178
/// is called in LTO optimization passes.
@@ -196,20 +198,22 @@ std::string getIRPGOFuncName(const Function &F, bool InLTO = false);
196198
std::pair<StringRef, StringRef> getParsedIRPGOFuncName(StringRef IRPGOFuncName);
197199

198200
/// Return the name of the global variable used to store a function
199-
/// name in PGO instrumentation. \c FuncName is the name of the function
200-
/// returned by the \c getPGOFuncName call.
201+
/// name in PGO instrumentation. \c FuncName is the IRPGO function name
202+
/// (returned by \c getIRPGOFuncName) for LLVM IR instrumentation and PGO
203+
/// function name (returned by \c getPGOFuncName) for front-end instrumentation.
201204
std::string getPGOFuncNameVarName(StringRef FuncName,
202205
GlobalValue::LinkageTypes Linkage);
203206

204207
/// Create and return the global variable for function name used in PGO
205-
/// instrumentation. \c FuncName is the name of the function returned
206-
/// by \c getPGOFuncName call.
208+
/// instrumentation. \c FuncName is the IRPGO function name (returned by
209+
/// \c getIRPGOFuncName) for LLVM IR instrumentation and PGO function name
210+
/// (returned by \c getPGOFuncName) for front-end instrumentation.
207211
GlobalVariable *createPGOFuncNameVar(Function &F, StringRef PGOFuncName);
208212

209213
/// Create and return the global variable for function name used in PGO
210-
/// instrumentation. /// \c FuncName is the name of the function
211-
/// returned by \c getPGOFuncName call, \c M is the owning module,
212-
/// and \c Linkage is the linkage of the instrumented function.
214+
/// instrumentation. \c FuncName is the IRPGO function name (returned by
215+
/// \c getIRPGOFuncName) for LLVM IR instrumentation and PGO function name
216+
/// (returned by \c getPGOFuncName) for front-end instrumentation.
213217
GlobalVariable *createPGOFuncNameVar(Module &M,
214218
GlobalValue::LinkageTypes Linkage,
215219
StringRef PGOFuncName);
@@ -417,11 +421,11 @@ uint64_t ComputeHash(StringRef K);
417421

418422
} // end namespace IndexedInstrProf
419423

420-
/// A symbol table used for function PGO name look-up with keys
424+
/// A symbol table used for function [IR]PGO name look-up with keys
421425
/// (such as pointers, md5hash values) to the function. A function's
422-
/// PGO name or name's md5hash are used in retrieving the profile
423-
/// data of the function. See \c getPGOFuncName() method for details
424-
/// on how PGO name is formed.
426+
/// [IR]PGO name or name's md5hash are used in retrieving the profile
427+
/// data of the function. See \c getIRPGOFuncName() and \c getPGOFuncName
428+
/// methods for details how [IR]PGO name is formed.
425429
class InstrProfSymtab {
426430
public:
427431
using AddrHashMap = std::vector<std::pair<uint64_t, uint64_t>>;

llvm/lib/IR/Globals.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,25 +144,27 @@ void GlobalObject::copyAttributesFrom(const GlobalObject *Src) {
144144
std::string GlobalValue::getGlobalIdentifier(StringRef Name,
145145
GlobalValue::LinkageTypes Linkage,
146146
StringRef FileName) {
147-
148147
// Value names may be prefixed with a binary '1' to indicate
149148
// that the backend should not modify the symbols due to any platform
150149
// naming convention. Do not include that '1' in the PGO profile name.
151150
if (Name[0] == '\1')
152151
Name = Name.substr(1);
153152

154-
std::string NewName = std::string(Name);
153+
std::string GlobalName;
155154
if (llvm::GlobalValue::isLocalLinkage(Linkage)) {
156155
// For local symbols, prepend the main file name to distinguish them.
157156
// Do not include the full path in the file name since there's no guarantee
158157
// that it will stay the same, e.g., if the files are checked out from
159158
// version control in different locations.
160159
if (FileName.empty())
161-
NewName = NewName.insert(0, "<unknown>:");
160+
GlobalName += "<unknown>";
162161
else
163-
NewName = NewName.insert(0, FileName.str() + ":");
162+
GlobalName += FileName;
163+
164+
GlobalName += kGlobalIdentifierDelimiter;
164165
}
165-
return NewName;
166+
GlobalName += Name;
167+
return GlobalName;
166168
}
167169

168170
std::string GlobalValue::getGlobalIdentifier() const {

llvm/lib/ProfileData/InstrProf.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,27 @@ std::string InstrProfError::message() const {
246246

247247
char InstrProfError::ID = 0;
248248

249-
std::string getPGOFuncName(StringRef RawFuncName,
250-
GlobalValue::LinkageTypes Linkage,
249+
std::string getPGOFuncName(StringRef Name, GlobalValue::LinkageTypes Linkage,
251250
StringRef FileName,
252251
uint64_t Version LLVM_ATTRIBUTE_UNUSED) {
253-
return GlobalValue::getGlobalIdentifier(RawFuncName, Linkage, FileName);
252+
// Value names may be prefixed with a binary '1' to indicate
253+
// that the backend should not modify the symbols due to any platform
254+
// naming convention. Do not include that '1' in the PGO profile name.
255+
if (Name[0] == '\1')
256+
Name = Name.substr(1);
257+
258+
std::string NewName = std::string(Name);
259+
if (llvm::GlobalValue::isLocalLinkage(Linkage)) {
260+
// For local symbols, prepend the main file name to distinguish them.
261+
// Do not include the full path in the file name since there's no guarantee
262+
// that it will stay the same, e.g., if the files are checked out from
263+
// version control in different locations.
264+
if (FileName.empty())
265+
NewName = NewName.insert(0, "<unknown>:");
266+
else
267+
NewName = NewName.insert(0, FileName.str() + ":");
268+
}
269+
return NewName;
254270
}
255271

256272
// Strip NumPrefix level of directory name from PathNameStr. If the number of
@@ -300,12 +316,10 @@ getIRPGONameForGlobalObject(const GlobalObject &GO,
300316
GlobalValue::LinkageTypes Linkage,
301317
StringRef FileName) {
302318
SmallString<64> Name;
303-
if (llvm::GlobalValue::isLocalLinkage(Linkage)) {
304-
Name.append(FileName.empty() ? "<unknown>" : FileName);
305-
Name.append(";");
306-
}
319+
// FIXME: Mangler's handling is kept outside of `getGlobalIdentifier` for now.
320+
// For more details please check issue #74565.
307321
Mangler().getNameWithPrefix(Name, &GO, /*CannotUsePrivateLabel=*/true);
308-
return Name.str().str();
322+
return GlobalValue::getGlobalIdentifier(Name, Linkage, FileName);
309323
}
310324

311325
static std::optional<std::string> lookupPGONameFromMetadata(MDNode *MD) {
@@ -352,6 +366,9 @@ std::string getIRPGOFuncName(const Function &F, bool InLTO) {
352366
return getIRPGOObjectName(F, InLTO, getPGOFuncNameMetadata(F));
353367
}
354368

369+
// Please use getIRPGOFuncName for LLVM IR instrumentation. This function is
370+
// for front-end (Clang, etc) instrumentation.
371+
// The implementation is kept for profile matching from older profiles.
355372
// This is similar to `getIRPGOFuncName` except that this function calls
356373
// 'getPGOFuncName' to get a name and `getIRPGOFuncName` calls
357374
// 'getIRPGONameForGlobalObject'. See the difference between two callees in the
@@ -384,7 +401,8 @@ getParsedIRPGOFuncName(StringRef IRPGOFuncName) {
384401
StringRef getFuncNameWithoutPrefix(StringRef PGOFuncName, StringRef FileName) {
385402
if (FileName.empty())
386403
return PGOFuncName;
387-
// Drop the file name including ':'. See also getPGOFuncName.
404+
// Drop the file name including ':' or ';'. See getIRPGONameForGlobalObject as
405+
// well.
388406
if (PGOFuncName.starts_with(FileName))
389407
PGOFuncName = PGOFuncName.drop_front(FileName.size() + 1);
390408
return PGOFuncName;

llvm/lib/ProfileData/InstrProfReader.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,12 +1008,13 @@ class llvm::InstrProfReaderItaniumRemapper
10081008

10091009
/// Extract the original function name from a PGO function name.
10101010
static StringRef extractName(StringRef Name) {
1011-
// We can have multiple :-separated pieces; there can be pieces both
1012-
// before and after the mangled name. Find the first part that starts
1013-
// with '_Z'; we'll assume that's the mangled name we want.
1011+
// We can have multiple pieces separated by kGlobalIdentifierDelimiter (
1012+
// semicolon now and colon in older profiles); there can be pieces both
1013+
// before and after the mangled name. Find the first part that starts with
1014+
// '_Z'; we'll assume that's the mangled name we want.
10141015
std::pair<StringRef, StringRef> Parts = {StringRef(), Name};
10151016
while (true) {
1016-
Parts = Parts.second.split(':');
1017+
Parts = Parts.second.split(kGlobalIdentifierDelimiter);
10171018
if (Parts.first.starts_with("_Z"))
10181019
return Parts.first;
10191020
if (Parts.second.empty())

llvm/test/Bitcode/thinlto-function-summary-originalnames.ll

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
; COMBINED: <GLOBALVAL_SUMMARY_BLOCK
77
; COMBINED-NEXT: <VERSION
88
; COMBINED-NEXT: <FLAGS
9-
; COMBINED-NEXT: <VALUE_GUID {{.*}} op1=4947176790635855146/>
10-
; COMBINED-NEXT: <VALUE_GUID {{.*}} op1=-6591587165810580810/>
11-
; COMBINED-NEXT: <VALUE_GUID {{.*}} op1=-4377693495213223786/>
9+
; COMBINED-NEXT: <VALUE_GUID {{.*}} op1=686735765308251824/>
10+
; COMBINED-NEXT: <VALUE_GUID {{.*}} op1=4507502870619175775/>
11+
; COMBINED-NEXT: <VALUE_GUID {{.*}} op1=-8118561185538785069/>
1212
; COMBINED-DAG: <COMBINED_PROFILE{{ }}
13-
; COMBINED-DAG: <COMBINED_ORIGINAL_NAME op0=6699318081062747564/>
14-
; COMBINED-DAG: <COMBINED_GLOBALVAR_INIT_REFS
1513
; COMBINED-DAG: <COMBINED_ORIGINAL_NAME op0=-2012135647395072713/>
14+
; COMBINED-DAG: <COMBINED_GLOBALVAR_INIT_REFS
15+
; COMBINED-DAG: <COMBINED_ORIGINAL_NAME op0=6699318081062747564/>
1616
; COMBINED-DAG: <COMBINED_ALIAS
1717
; COMBINED-DAG: <COMBINED_ORIGINAL_NAME op0=-4170563161550796836/>
1818
; COMBINED-NEXT: </GLOBALVAL_SUMMARY_BLOCK>

0 commit comments

Comments
 (0)