-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[ThinLTO][TypeProf] Import local-linkage global var for mod1:func_foo-> mod2:local-var edge #100448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1be94de
d0cc8ba
78fe701
d47ce4e
eff8695
7486f9d
5163477
c677c57
d6ce694
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -174,6 +174,17 @@ static cl::opt<std::string> WorkloadDefinitions( | |
"}"), | ||
cl::Hidden); | ||
|
||
static cl::opt<bool> ImportAssumeUniqueLocal( | ||
"import-assume-unique-local", cl::init(false), | ||
cl::desc( | ||
"By default, a local-linkage global variable won't be imported in the " | ||
"edge mod1:func -> mod2:local-var (from value profiles) since compiler " | ||
"cannot assume mod2 is compiled with full path which gives local-var a " | ||
"program-wide unique GUID. Set this option to true will help cross " | ||
"module import of such variables. This is only safe if the compiler " | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am trying to learn more about this space. Can you elaborate on "This is only safe if the compiler user specify the full module path." How does user specify it? I looked at the test and it's not clear to me from it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In a monolithic repository (https://research.google/pubs/why-google-stores-billions-of-lines-of-code-in-a-single-repository/) of source code, user specifies the full path in the compile options. In practice, bazel (https://bazel.build/) configures the build rules and guarantees full path is used. Users invoke bazel to compile an executable. Using the folder structure below as an example [1],
The purpose of the test is to illustrate and provide coverage for profile-gen and its use, so I saved the extra work to use full path there. [1]
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test program itself is correct as there are only two files and they don't have conflict names. I'm open to update it to use the full path if that is preferred or better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI, I double checked whether compiler correctness can rely on the fact that full file paths are used. My takeaway is that bazel doesn't fail the executable compilation if local paths are used (if any in a unconventional way) so I don't plan to turn on Yet still There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotcha, thx for explanation. |
||
"user specify the full module path."), | ||
cl::Hidden); | ||
|
||
namespace llvm { | ||
extern cl::opt<bool> EnableMemProfContextDisambiguation; | ||
} | ||
|
@@ -196,6 +207,23 @@ static std::unique_ptr<Module> loadFile(const std::string &FileName, | |
return Result; | ||
} | ||
|
||
static bool shouldSkipLocalInAnotherModule(const GlobalVarSummary *RefSummary, | ||
size_t NumDefs, | ||
StringRef ImporterModule) { | ||
// We can import a local from another module if all inputs are compiled | ||
// with full paths or when there is one definition. | ||
if (ImportAssumeUniqueLocal || NumDefs == 1) | ||
return false; | ||
// In other cases, make sure we import the copy in the caller's module if the | ||
// referenced value has local linkage. The only time a local variable can | ||
// share an entry in the index is if there is a local with the same name in | ||
// another module that had the same source file name (in a different | ||
// directory), where each was compiled in their own directory so there was not | ||
// distinguishing path. | ||
return GlobalValue::isLocalLinkage(RefSummary->linkage()) && | ||
RefSummary->modulePath() != ImporterModule; | ||
} | ||
|
||
/// Given a list of possible callee implementation for a call site, qualify the | ||
/// legality of importing each. The return is a range of pairs. Each pair | ||
/// corresponds to a candidate. The first value is the ImportFailureReason for | ||
|
@@ -228,19 +256,21 @@ static auto qualifyCalleeCandidates( | |
if (!Summary) | ||
return {FunctionImporter::ImportFailureReason::GlobalVar, GVSummary}; | ||
|
||
// If this is a local function, make sure we import the copy | ||
// in the caller's module. The only time a local function can | ||
// share an entry in the index is if there is a local with the same name | ||
// in another module that had the same source file name (in a different | ||
// directory), where each was compiled in their own directory so there | ||
// was not distinguishing path. | ||
// However, do the import from another module if there is only one | ||
// entry in the list - in that case this must be a reference due | ||
// to indirect call profile data, since a function pointer can point to | ||
// a local in another module. | ||
if (GlobalValue::isLocalLinkage(Summary->linkage()) && | ||
CalleeSummaryList.size() > 1 && | ||
Summary->modulePath() != CallerModulePath) | ||
// If this is a local function, make sure we import the copy in the | ||
// caller's module. The only time a local function can share an entry in | ||
// the index is if there is a local with the same name in another module | ||
// that had the same source file name (in a different directory), where | ||
// each was compiled in their own directory so there was not | ||
// distinguishing path. | ||
// If the local function is from another module, it must be a reference | ||
// due to indirect call profile data since a function pointer can point | ||
// to a local in another module. Do the import from another module if | ||
// there is only one entry in the list or when all files in the program | ||
// are compiled with full path - in both cases the local function has | ||
// unique PGO name and GUID. | ||
if (shouldSkipLocalInAnotherModule(dyn_cast<GlobalVarSummary>(Summary), | ||
CalleeSummaryList.size(), | ||
CallerModulePath)) | ||
return { | ||
FunctionImporter::ImportFailureReason::LocalLinkageNotInModule, | ||
GVSummary}; | ||
|
@@ -359,18 +389,6 @@ class GlobalsImporter final { | |
|
||
LLVM_DEBUG(dbgs() << " ref -> " << VI << "\n"); | ||
|
||
// If this is a local variable, make sure we import the copy | ||
// in the caller's module. The only time a local variable can | ||
// share an entry in the index is if there is a local with the same name | ||
// in another module that had the same source file name (in a different | ||
// directory), where each was compiled in their own directory so there | ||
// was not distinguishing path. | ||
auto LocalNotInModule = | ||
[&](const GlobalValueSummary *RefSummary) -> bool { | ||
return GlobalValue::isLocalLinkage(RefSummary->linkage()) && | ||
RefSummary->modulePath() != Summary.modulePath(); | ||
}; | ||
|
||
for (const auto &RefSummary : VI.getSummaryList()) { | ||
const auto *GVS = dyn_cast<GlobalVarSummary>(RefSummary.get()); | ||
// Functions could be referenced by global vars - e.g. a vtable; but we | ||
|
@@ -379,7 +397,8 @@ class GlobalsImporter final { | |
// based on profile information). Should we decide to handle them here, | ||
// we can refactor accordingly at that time. | ||
if (!GVS || !Index.canImportGlobalVar(GVS, /* AnalyzeRefs */ true) || | ||
LocalNotInModule(GVS)) | ||
shouldSkipLocalInAnotherModule(GVS, VI.getSummaryList().size(), | ||
Summary.modulePath())) | ||
continue; | ||
|
||
// If there isn't an entry for GUID, insert <GUID, Definition> pair. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can the local var be promoted to global to enable importing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As shown in the updated test, with this option on, when the local-var gets imported, it will have a global name
<vtable>.llvm.<mod-hash>
.