mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-29 11:46:06 +00:00
Recommit "[ThinLTO] Add correctness check for RO/WO variable import"
ValueInfo has user-defined 'operator bool' which allows incorrect implicit conversion to GlobalValue::GUID (which is unsigned long). This causes bugs which are hard to track and should be removed in future.
This commit is contained in:
parent
5f0c3bad2f
commit
3d708bf5c2
@ -59,7 +59,7 @@ void thinLTOResolvePrevailingInIndex(
|
|||||||
/// must apply the changes to the Module via thinLTOInternalizeModule.
|
/// must apply the changes to the Module via thinLTOInternalizeModule.
|
||||||
void thinLTOInternalizeAndPromoteInIndex(
|
void thinLTOInternalizeAndPromoteInIndex(
|
||||||
ModuleSummaryIndex &Index,
|
ModuleSummaryIndex &Index,
|
||||||
function_ref<bool(StringRef, GlobalValue::GUID)> isExported,
|
function_ref<bool(StringRef, ValueInfo)> isExported,
|
||||||
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
||||||
isPrevailing);
|
isPrevailing);
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ public:
|
|||||||
using ImportMapTy = StringMap<FunctionsToImportTy>;
|
using ImportMapTy = StringMap<FunctionsToImportTy>;
|
||||||
|
|
||||||
/// The set contains an entry for every global value the module exports.
|
/// The set contains an entry for every global value the module exports.
|
||||||
using ExportSetTy = std::unordered_set<GlobalValue::GUID>;
|
using ExportSetTy = DenseSet<ValueInfo>;
|
||||||
|
|
||||||
/// A function of this type is used to load modules referenced by the index.
|
/// A function of this type is used to load modules referenced by the index.
|
||||||
using ModuleLoaderTy =
|
using ModuleLoaderTy =
|
||||||
|
@ -251,7 +251,7 @@ void runWholeProgramDevirtOnIndex(
|
|||||||
/// devirt target names for any locals that were exported.
|
/// devirt target names for any locals that were exported.
|
||||||
void updateIndexWPDForExports(
|
void updateIndexWPDForExports(
|
||||||
ModuleSummaryIndex &Summary,
|
ModuleSummaryIndex &Summary,
|
||||||
function_ref<bool(StringRef, GlobalValue::GUID)> isExported,
|
function_ref<bool(StringRef, ValueInfo)> isExported,
|
||||||
std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap);
|
std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap);
|
||||||
|
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
@ -147,9 +147,11 @@ void llvm::computeLTOCacheKey(
|
|||||||
// Include the hash for the current module
|
// Include the hash for the current module
|
||||||
auto ModHash = Index.getModuleHash(ModuleID);
|
auto ModHash = Index.getModuleHash(ModuleID);
|
||||||
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));
|
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&ModHash[0], sizeof(ModHash)));
|
||||||
for (auto F : ExportList)
|
for (const auto &VI : ExportList) {
|
||||||
|
auto GUID = VI.getGUID();
|
||||||
// The export list can impact the internalization, be conservative here
|
// The export list can impact the internalization, be conservative here
|
||||||
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&F, sizeof(F)));
|
Hasher.update(ArrayRef<uint8_t>((uint8_t *)&GUID, sizeof(GUID)));
|
||||||
|
}
|
||||||
|
|
||||||
// Include the hash for every module we import functions from. The set of
|
// Include the hash for every module we import functions from. The set of
|
||||||
// imported symbols for each module may affect code generation and is
|
// imported symbols for each module may affect code generation and is
|
||||||
@ -384,12 +386,12 @@ static bool isWeakObjectWithRWAccess(GlobalValueSummary *GVS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void thinLTOInternalizeAndPromoteGUID(
|
static void thinLTOInternalizeAndPromoteGUID(
|
||||||
GlobalValueSummaryList &GVSummaryList, GlobalValue::GUID GUID,
|
GlobalValueSummaryList &GVSummaryList, ValueInfo VI,
|
||||||
function_ref<bool(StringRef, GlobalValue::GUID)> isExported,
|
function_ref<bool(StringRef, ValueInfo)> isExported,
|
||||||
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
||||||
isPrevailing) {
|
isPrevailing) {
|
||||||
for (auto &S : GVSummaryList) {
|
for (auto &S : GVSummaryList) {
|
||||||
if (isExported(S->modulePath(), GUID)) {
|
if (isExported(S->modulePath(), VI)) {
|
||||||
if (GlobalValue::isLocalLinkage(S->linkage()))
|
if (GlobalValue::isLocalLinkage(S->linkage()))
|
||||||
S->setLinkage(GlobalValue::ExternalLinkage);
|
S->setLinkage(GlobalValue::ExternalLinkage);
|
||||||
} else if (EnableLTOInternalization &&
|
} else if (EnableLTOInternalization &&
|
||||||
@ -397,7 +399,7 @@ static void thinLTOInternalizeAndPromoteGUID(
|
|||||||
// doesn't resolve them.
|
// doesn't resolve them.
|
||||||
!GlobalValue::isLocalLinkage(S->linkage()) &&
|
!GlobalValue::isLocalLinkage(S->linkage()) &&
|
||||||
(!GlobalValue::isInterposableLinkage(S->linkage()) ||
|
(!GlobalValue::isInterposableLinkage(S->linkage()) ||
|
||||||
isPrevailing(GUID, S.get())) &&
|
isPrevailing(VI.getGUID(), S.get())) &&
|
||||||
S->linkage() != GlobalValue::AppendingLinkage &&
|
S->linkage() != GlobalValue::AppendingLinkage &&
|
||||||
// We can't internalize available_externally globals because this
|
// We can't internalize available_externally globals because this
|
||||||
// can break function pointer equality.
|
// can break function pointer equality.
|
||||||
@ -416,12 +418,12 @@ static void thinLTOInternalizeAndPromoteGUID(
|
|||||||
// as external and non-exported values as internal.
|
// as external and non-exported values as internal.
|
||||||
void llvm::thinLTOInternalizeAndPromoteInIndex(
|
void llvm::thinLTOInternalizeAndPromoteInIndex(
|
||||||
ModuleSummaryIndex &Index,
|
ModuleSummaryIndex &Index,
|
||||||
function_ref<bool(StringRef, GlobalValue::GUID)> isExported,
|
function_ref<bool(StringRef, ValueInfo)> isExported,
|
||||||
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
|
||||||
isPrevailing) {
|
isPrevailing) {
|
||||||
for (auto &I : Index)
|
for (auto &I : Index)
|
||||||
thinLTOInternalizeAndPromoteGUID(I.second.SummaryList, I.first, isExported,
|
thinLTOInternalizeAndPromoteGUID(
|
||||||
isPrevailing);
|
I.second.SummaryList, Index.getValueInfo(I), isExported, isPrevailing);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires a destructor for std::vector<InputModule>.
|
// Requires a destructor for std::vector<InputModule>.
|
||||||
@ -1331,11 +1333,10 @@ Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
|
|||||||
ExportedGUIDs.insert(
|
ExportedGUIDs.insert(
|
||||||
GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Def)));
|
GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Def)));
|
||||||
|
|
||||||
auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
|
auto isExported = [&](StringRef ModuleIdentifier, ValueInfo VI) {
|
||||||
const auto &ExportList = ExportLists.find(ModuleIdentifier);
|
const auto &ExportList = ExportLists.find(ModuleIdentifier);
|
||||||
return (ExportList != ExportLists.end() &&
|
return (ExportList != ExportLists.end() && ExportList->second.count(VI)) ||
|
||||||
ExportList->second.count(GUID)) ||
|
ExportedGUIDs.count(VI.getGUID());
|
||||||
ExportedGUIDs.count(GUID);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update local devirtualized targets that were exported by cross-module
|
// Update local devirtualized targets that were exported by cross-module
|
||||||
|
@ -590,11 +590,10 @@ struct IsExported {
|
|||||||
const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols)
|
const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols)
|
||||||
: ExportLists(ExportLists), GUIDPreservedSymbols(GUIDPreservedSymbols) {}
|
: ExportLists(ExportLists), GUIDPreservedSymbols(GUIDPreservedSymbols) {}
|
||||||
|
|
||||||
bool operator()(StringRef ModuleIdentifier, GlobalValue::GUID GUID) const {
|
bool operator()(StringRef ModuleIdentifier, ValueInfo VI) const {
|
||||||
const auto &ExportList = ExportLists.find(ModuleIdentifier);
|
const auto &ExportList = ExportLists.find(ModuleIdentifier);
|
||||||
return (ExportList != ExportLists.end() &&
|
return (ExportList != ExportLists.end() && ExportList->second.count(VI)) ||
|
||||||
ExportList->second.count(GUID)) ||
|
GUIDPreservedSymbols.count(VI.getGUID());
|
||||||
GUIDPreservedSymbols.count(GUID);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -308,7 +308,7 @@ static void computeImportForReferencedGlobals(
|
|||||||
|
|
||||||
auto MarkExported = [&](const ValueInfo &VI, const GlobalValueSummary *S) {
|
auto MarkExported = [&](const ValueInfo &VI, const GlobalValueSummary *S) {
|
||||||
if (ExportLists)
|
if (ExportLists)
|
||||||
(*ExportLists)[S->modulePath()].insert(VI.getGUID());
|
(*ExportLists)[S->modulePath()].insert(VI);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto &RefSummary : VI.getSummaryList())
|
for (auto &RefSummary : VI.getSummaryList())
|
||||||
@ -497,7 +497,7 @@ static void computeImportForFunction(
|
|||||||
// Make exports in the source module.
|
// Make exports in the source module.
|
||||||
if (ExportLists) {
|
if (ExportLists) {
|
||||||
auto &ExportList = (*ExportLists)[ExportModulePath];
|
auto &ExportList = (*ExportLists)[ExportModulePath];
|
||||||
ExportList.insert(VI.getGUID());
|
ExportList.insert(VI);
|
||||||
if (!PreviouslyImported) {
|
if (!PreviouslyImported) {
|
||||||
// This is the first time this function was exported from its source
|
// This is the first time this function was exported from its source
|
||||||
// module, so mark all functions and globals it references as exported
|
// module, so mark all functions and globals it references as exported
|
||||||
@ -505,14 +505,11 @@ static void computeImportForFunction(
|
|||||||
// For efficiency, we unconditionally add all the referenced GUIDs
|
// For efficiency, we unconditionally add all the referenced GUIDs
|
||||||
// to the ExportList for this module, and will prune out any not
|
// to the ExportList for this module, and will prune out any not
|
||||||
// defined in the module later in a single pass.
|
// defined in the module later in a single pass.
|
||||||
for (auto &Edge : ResolvedCalleeSummary->calls()) {
|
for (auto &Edge : ResolvedCalleeSummary->calls())
|
||||||
auto CalleeGUID = Edge.first.getGUID();
|
ExportList.insert(Edge.first);
|
||||||
ExportList.insert(CalleeGUID);
|
|
||||||
}
|
for (auto &Ref : ResolvedCalleeSummary->refs())
|
||||||
for (auto &Ref : ResolvedCalleeSummary->refs()) {
|
ExportList.insert(Ref);
|
||||||
auto GUID = Ref.getGUID();
|
|
||||||
ExportList.insert(GUID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,6 +627,37 @@ static unsigned numGlobalVarSummaries(const ModuleSummaryIndex &Index,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static bool
|
||||||
|
checkVariableImport(const ModuleSummaryIndex &Index,
|
||||||
|
StringMap<FunctionImporter::ImportMapTy> &ImportLists,
|
||||||
|
StringMap<FunctionImporter::ExportSetTy> &ExportLists) {
|
||||||
|
|
||||||
|
DenseSet<GlobalValue::GUID> FlattenedImports;
|
||||||
|
|
||||||
|
for (auto &ImportPerModule : ImportLists)
|
||||||
|
for (auto &ExportPerModule : ImportPerModule.second)
|
||||||
|
FlattenedImports.insert(ExportPerModule.second.begin(),
|
||||||
|
ExportPerModule.second.end());
|
||||||
|
|
||||||
|
// Checks that all GUIDs of read/writeonly vars we see in export lists
|
||||||
|
// are also in the import lists. Otherwise we my face linker undefs,
|
||||||
|
// because readonly and writeonly vars are internalized in their
|
||||||
|
// source modules.
|
||||||
|
auto IsReadOrWriteOnlyVar = [&](StringRef ModulePath, const ValueInfo &VI) {
|
||||||
|
auto *GVS = dyn_cast_or_null<GlobalVarSummary>(
|
||||||
|
Index.findSummaryInModule(VI, ModulePath));
|
||||||
|
return GVS && (Index.isReadOnly(GVS) || Index.isWriteOnly(GVS));
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &ExportPerModule : ExportLists)
|
||||||
|
for (auto &VI : ExportPerModule.second)
|
||||||
|
if (!FlattenedImports.count(VI.getGUID()) &&
|
||||||
|
IsReadOrWriteOnlyVar(ExportPerModule.first(), VI))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute all the import and export for every module using the Index.
|
/// Compute all the import and export for every module using the Index.
|
||||||
void llvm::ComputeCrossModuleImport(
|
void llvm::ComputeCrossModuleImport(
|
||||||
const ModuleSummaryIndex &Index,
|
const ModuleSummaryIndex &Index,
|
||||||
@ -655,13 +683,14 @@ void llvm::ComputeCrossModuleImport(
|
|||||||
const auto &DefinedGVSummaries =
|
const auto &DefinedGVSummaries =
|
||||||
ModuleToDefinedGVSummaries.lookup(ELI.first());
|
ModuleToDefinedGVSummaries.lookup(ELI.first());
|
||||||
for (auto EI = ELI.second.begin(); EI != ELI.second.end();) {
|
for (auto EI = ELI.second.begin(); EI != ELI.second.end();) {
|
||||||
if (!DefinedGVSummaries.count(*EI))
|
if (!DefinedGVSummaries.count(EI->getGUID()))
|
||||||
EI = ELI.second.erase(EI);
|
ELI.second.erase(EI++);
|
||||||
else
|
else
|
||||||
++EI;
|
++EI;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(checkVariableImport(Index, ImportLists, ExportLists));
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
LLVM_DEBUG(dbgs() << "Import/Export lists for " << ImportLists.size()
|
LLVM_DEBUG(dbgs() << "Import/Export lists for " << ImportLists.size()
|
||||||
<< " modules:\n");
|
<< " modules:\n");
|
||||||
|
@ -711,7 +711,7 @@ void runWholeProgramDevirtOnIndex(
|
|||||||
|
|
||||||
void updateIndexWPDForExports(
|
void updateIndexWPDForExports(
|
||||||
ModuleSummaryIndex &Summary,
|
ModuleSummaryIndex &Summary,
|
||||||
function_ref<bool(StringRef, GlobalValue::GUID)> isExported,
|
function_ref<bool(StringRef, ValueInfo)> isExported,
|
||||||
std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) {
|
std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) {
|
||||||
for (auto &T : LocalWPDTargetsMap) {
|
for (auto &T : LocalWPDTargetsMap) {
|
||||||
auto &VI = T.first;
|
auto &VI = T.first;
|
||||||
@ -719,7 +719,7 @@ void updateIndexWPDForExports(
|
|||||||
assert(VI.getSummaryList().size() == 1 &&
|
assert(VI.getSummaryList().size() == 1 &&
|
||||||
"Devirt of local target has more than one copy");
|
"Devirt of local target has more than one copy");
|
||||||
auto &S = VI.getSummaryList()[0];
|
auto &S = VI.getSummaryList()[0];
|
||||||
if (!isExported(S->modulePath(), VI.getGUID()))
|
if (!isExported(S->modulePath(), VI))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// It's been exported by a cross module import.
|
// It's been exported by a cross module import.
|
||||||
|
@ -250,12 +250,12 @@ void FunctionImportGlobalProcessing::processGlobalForThinLTO(GlobalValue &GV) {
|
|||||||
// contains summaries from the source modules if they are being imported.
|
// contains summaries from the source modules if they are being imported.
|
||||||
// We might have a non-null VI and get here even in that case if the name
|
// We might have a non-null VI and get here even in that case if the name
|
||||||
// matches one in this module (e.g. weak or appending linkage).
|
// matches one in this module (e.g. weak or appending linkage).
|
||||||
auto* GVS = dyn_cast_or_null<GlobalVarSummary>(
|
auto *GVS = dyn_cast_or_null<GlobalVarSummary>(
|
||||||
ImportIndex.findSummaryInModule(VI, M.getModuleIdentifier()));
|
ImportIndex.findSummaryInModule(VI, M.getModuleIdentifier()));
|
||||||
if (GVS &&
|
if (GVS &&
|
||||||
(ImportIndex.isReadOnly(GVS) || ImportIndex.isWriteOnly(GVS))) {
|
(ImportIndex.isReadOnly(GVS) || ImportIndex.isWriteOnly(GVS))) {
|
||||||
V->addAttribute("thinlto-internalize");
|
V->addAttribute("thinlto-internalize");
|
||||||
// Objects referenced by writeonly GV initializer should not be
|
// Objects referenced by writeonly GV initializer should not be
|
||||||
// promoted, because there is no any kind of read access to them
|
// promoted, because there is no any kind of read access to them
|
||||||
// on behalf of this writeonly GV. To avoid promotion we convert
|
// on behalf of this writeonly GV. To avoid promotion we convert
|
||||||
// GV initializer to 'zeroinitializer'. This effectively drops
|
// GV initializer to 'zeroinitializer'. This effectively drops
|
||||||
|
Loading…
x
Reference in New Issue
Block a user