From d0f8afa9b6d5bc8697c56f1b909cfce0e30eec9e Mon Sep 17 00:00:00 2001 From: Teresa Johnson Date: Thu, 17 Dec 2015 17:14:09 +0000 Subject: [PATCH 1/1] [ThinLTO] Metadata linking for imported functions Summary: Second patch split out from http://reviews.llvm.org/D14752. Maps metadata as a post-pass from each module when importing complete, suturing up final metadata to the temporary metadata left on the imported instructions. This entails saving the mapping from bitcode value id to temporary metadata in the importing pass, and from bitcode value id to final metadata during the metadata linking postpass. Depends on D14825. Reviewers: dexonsmith, joker.eph Subscribers: davidxl, llvm-commits, joker.eph Differential Revision: http://reviews.llvm.org/D14838 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@255909 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/IR/GVMaterializer.h | 10 + include/llvm/IR/Metadata.h | 5 +- include/llvm/IRReader/IRReader.h | 9 +- include/llvm/Linker/IRMover.h | 5 +- include/llvm/Linker/Linker.h | 13 +- include/llvm/Transforms/Utils/ValueMapper.h | 11 + lib/Bitcode/Reader/BitcodeReader.cpp | 45 +++- lib/IR/Metadata.cpp | 4 +- lib/IRReader/IRReader.cpp | 12 +- lib/Linker/IRMover.cpp | 210 +++++++++++++++--- lib/Linker/LinkModules.cpp | 33 ++- lib/Transforms/IPO/FunctionImport.cpp | 24 +- lib/Transforms/Utils/ValueMapper.cpp | 74 ++++-- .../Linker/Inputs/thinlto_funcimport_debug.ll | 38 ++++ test/Linker/thinlto_funcimport_debug.ll | 71 ++++++ .../FunctionImport/Inputs/funcimport_debug.ll | 27 +++ .../FunctionImport/funcimport_debug.ll | 45 ++++ tools/llvm-link/llvm-link.cpp | 49 +++- 18 files changed, 611 insertions(+), 74 deletions(-) create mode 100644 test/Linker/Inputs/thinlto_funcimport_debug.ll create mode 100644 test/Linker/thinlto_funcimport_debug.ll create mode 100644 test/Transforms/FunctionImport/Inputs/funcimport_debug.ll create mode 100644 test/Transforms/FunctionImport/funcimport_debug.ll diff --git a/include/llvm/IR/GVMaterializer.h b/include/llvm/IR/GVMaterializer.h index 1d6c9157f0b..992a8c8fc6f 100644 --- a/include/llvm/IR/GVMaterializer.h +++ b/include/llvm/IR/GVMaterializer.h @@ -18,12 +18,14 @@ #ifndef LLVM_IR_GVMATERIALIZER_H #define LLVM_IR_GVMATERIALIZER_H +#include "llvm/ADT/DenseMap.h" #include #include namespace llvm { class Function; class GlobalValue; +class Metadata; class Module; class StructType; @@ -56,6 +58,14 @@ public: virtual std::error_code materializeMetadata() = 0; virtual void setStripDebugInfo() = 0; + /// Client should define this interface if the mapping between metadata + /// values and value ids needs to be preserved, e.g. across materializer + /// instantiations. If OnlyTempMD is true, only those that have remained + /// temporary metadata are recorded in the map. + virtual void + saveMDValueList(DenseMap &MDValueToValIDMap, + bool OnlyTempMD) {} + virtual std::vector getIdentifiedStructTypes() const = 0; }; diff --git a/include/llvm/IR/Metadata.h b/include/llvm/IR/Metadata.h index 84c82476c4f..2e9dc457bb0 100644 --- a/include/llvm/IR/Metadata.h +++ b/include/llvm/IR/Metadata.h @@ -832,10 +832,11 @@ public: /// \brief Resolve cycles. /// /// Once all forward declarations have been resolved, force cycles to be - /// resolved. + /// resolved. If \p MDMaterialized is true, then any temporary metadata + /// is ignored, otherwise it asserts when encountering temporary metadata. /// /// \pre No operands (or operands' operands, etc.) have \a isTemporary(). - void resolveCycles(); + void resolveCycles(bool MDMaterialized = true); /// \brief Replace a temporary node with a permanent one. /// diff --git a/include/llvm/IRReader/IRReader.h b/include/llvm/IRReader/IRReader.h index 2d9ace0b62a..523cd3d6df7 100644 --- a/include/llvm/IRReader/IRReader.h +++ b/include/llvm/IRReader/IRReader.h @@ -27,10 +27,11 @@ class LLVMContext; /// If the given file holds a bitcode image, return a Module /// for it which does lazy deserialization of function bodies. Otherwise, /// attempt to parse it as LLVM Assembly and return a fully populated -/// Module. -std::unique_ptr getLazyIRFileModule(StringRef Filename, - SMDiagnostic &Err, - LLVMContext &Context); +/// Module. The ShouldLazyLoadMetadata flag is passed down to the bitcode +/// reader to optionally enable lazy metadata loading. +std::unique_ptr +getLazyIRFileModule(StringRef Filename, SMDiagnostic &Err, LLVMContext &Context, + bool ShouldLazyLoadMetadata = false); /// If the given MemoryBuffer holds a bitcode image, return a Module /// for it. Otherwise, attempt to parse it as LLVM Assembly and return diff --git a/include/llvm/Linker/IRMover.h b/include/llvm/Linker/IRMover.h index 10e6d5c0f70..a964cc4b72c 100644 --- a/include/llvm/Linker/IRMover.h +++ b/include/llvm/Linker/IRMover.h @@ -16,6 +16,7 @@ namespace llvm { class GlobalValue; +class MDNode; class Module; class StructType; class Type; @@ -60,7 +61,9 @@ public: /// Move in the provide values. The source is destroyed. /// Returns true on error. bool move(Module &Src, ArrayRef ValuesToLink, - std::function AddLazyFor); + std::function AddLazyFor, + DenseMap *ValIDToTempMDMap = nullptr, + bool IsMetadataLinkingPostpass = false); Module &getModule() { return Composite; } private: diff --git a/include/llvm/Linker/Linker.h b/include/llvm/Linker/Linker.h index eca0e9321a1..dde3f73883c 100644 --- a/include/llvm/Linker/Linker.h +++ b/include/llvm/Linker/Linker.h @@ -42,11 +42,14 @@ public: /// For ThinLTO function importing/exporting the \p FunctionInfoIndex /// is passed. If \p FunctionsToImport is provided, only the functions that /// are part of the set will be imported from the source module. + /// The \p ValIDToTempMDMap is populated by the linker when function + /// importing is performed. /// /// Returns true on error. bool linkInModule(std::unique_ptr Src, unsigned Flags = Flags::None, const FunctionInfoIndex *Index = nullptr, - DenseSet *FunctionsToImport = nullptr); + DenseSet *FunctionsToImport = nullptr, + DenseMap *ValIDToTempMDMap = nullptr); /// This exists to implement the deprecated LLVMLinkModules C api. Don't use /// for anything else. @@ -54,6 +57,14 @@ public: static bool linkModules(Module &Dest, std::unique_ptr Src, unsigned Flags = Flags::None); + + /// \brief Link metadata from \p Src into the composite. The source is + /// destroyed. + /// + /// The \p ValIDToTempMDMap sound have been populated earlier during function + /// importing from \p Src. + bool linkInMetadata(Module &Src, + DenseMap *ValIDToTempMDMap); }; /// Create a new module with exported local functions renamed and promoted diff --git a/include/llvm/Transforms/Utils/ValueMapper.h b/include/llvm/Transforms/Utils/ValueMapper.h index 5687bd21e99..7b1382854ba 100644 --- a/include/llvm/Transforms/Utils/ValueMapper.h +++ b/include/llvm/Transforms/Utils/ValueMapper.h @@ -55,6 +55,12 @@ namespace llvm { /// It is called after the mapping is recorded, so it doesn't need to worry /// about recursion. virtual void materializeInitFor(GlobalValue *New, GlobalValue *Old); + + /// If the client needs to handle temporary metadata it must implement + /// these methods. + virtual Metadata *mapTemporaryMetadata(Metadata *MD) { return nullptr; } + virtual void replaceTemporaryMetadata(const Metadata *OrigMD, + Metadata *NewMD) {} }; /// RemapFlags - These are flags that the value mapping APIs allow. @@ -78,6 +84,11 @@ namespace llvm { /// Any global values not in value map are mapped to null instead of /// mapping to self. Illegal if RF_IgnoreMissingEntries is also set. RF_NullMapMissingGlobalValues = 8, + + /// Set when there is still temporary metadata that must be handled, + /// such as when we are doing function importing and will materialize + /// and link metadata as a postpass. + RF_HaveUnmaterializedMetadata = 16, }; static inline RemapFlags operator|(RemapFlags LHS, RemapFlags RHS) { diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index 0905b5fb6d2..ff08f55d43f 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -97,6 +97,7 @@ public: class BitcodeReaderMDValueList { unsigned NumFwdRefs; bool AnyFwdRefs; + bool SavedFwdRefs; unsigned MinFwdRef; unsigned MaxFwdRef; std::vector MDValuePtrs; @@ -104,7 +105,12 @@ class BitcodeReaderMDValueList { LLVMContext &Context; public: BitcodeReaderMDValueList(LLVMContext &C) - : NumFwdRefs(0), AnyFwdRefs(false), Context(C) {} + : NumFwdRefs(0), AnyFwdRefs(false), SavedFwdRefs(false), Context(C) {} + ~BitcodeReaderMDValueList() { + // Assert that we either replaced all forward references, or saved + // them for later replacement. + assert(!NumFwdRefs || SavedFwdRefs); + } // vector compatibility methods unsigned size() const { return MDValuePtrs.size(); } @@ -115,6 +121,8 @@ public: void pop_back() { MDValuePtrs.pop_back(); } bool empty() const { return MDValuePtrs.empty(); } + void savedFwdRefs() { SavedFwdRefs = true; } + Metadata *operator[](unsigned i) const { assert(i < MDValuePtrs.size()); return MDValuePtrs[i]; @@ -274,6 +282,14 @@ public: void setStripDebugInfo() override; + /// Save the mapping between the metadata values and the corresponding + /// value id that were recorded in the MDValueList during parsing. If + /// OnlyTempMD is true, then only record those entries that are still + /// temporary metadata. This interface is used when metadata linking is + /// performed as a postpass, such as during function importing. + void saveMDValueList(DenseMap &MDValueToValIDMap, + bool OnlyTempMD) override; + private: /// Parse the "IDENTIFICATION_BLOCK_ID" block, populate the // ProducerIdentification data member, and do some basic enforcement on the @@ -1069,6 +1085,9 @@ Metadata *BitcodeReaderMDValueList::getValueFwdRef(unsigned Idx) { MinFwdRef = MaxFwdRef = Idx; } ++NumFwdRefs; + // Reset flag to ensure that we save this forward reference if we + // are delaying metadata mapping (e.g. for function importing). + SavedFwdRefs = false; // Create and return a placeholder, which will later be RAUW'd. Metadata *MD = MDNode::getTemporary(Context, None).release(); @@ -3062,6 +3081,30 @@ std::error_code BitcodeReader::materializeMetadata() { void BitcodeReader::setStripDebugInfo() { StripDebugInfo = true; } +void BitcodeReader::saveMDValueList( + DenseMap &MDValueToValIDMap, bool OnlyTempMD) { + for (unsigned ValID = 0; ValID < MDValueList.size(); ++ValID) { + Metadata *MD = MDValueList[ValID]; + auto *N = dyn_cast_or_null(MD); + // Save all values if !OnlyTempMD, otherwise just the temporary metadata. + if (!OnlyTempMD || (N && N->isTemporary())) { + // Will call this after materializing each function, in order to + // handle remapping of the function's instructions/metadata. + // See if we already have an entry in that case. + if (OnlyTempMD && MDValueToValIDMap.count(MD)) { + assert(MDValueToValIDMap[MD] == ValID && + "Inconsistent metadata value id"); + continue; + } + MDValueToValIDMap[MD] = ValID; + // Flag that we saved the forward refs (temporary metadata) for error + // checking during MDValueList destruction. + if (OnlyTempMD) + MDValueList.savedFwdRefs(); + } + } +} + /// When we see the block for a function body, remember where it is and then /// skip it. This lets us lazily deserialize the functions. std::error_code BitcodeReader::rememberAndSkipFunctionBody() { diff --git a/lib/IR/Metadata.cpp b/lib/IR/Metadata.cpp index 80f18daa79b..b1da0301ecf 100644 --- a/lib/IR/Metadata.cpp +++ b/lib/IR/Metadata.cpp @@ -517,7 +517,7 @@ void MDNode::decrementUnresolvedOperandCount() { resolve(); } -void MDNode::resolveCycles() { +void MDNode::resolveCycles(bool MDMaterialized) { if (isResolved()) return; @@ -530,6 +530,8 @@ void MDNode::resolveCycles() { if (!N) continue; + if (N->isTemporary() && !MDMaterialized) + continue; assert(!N->isTemporary() && "Expected all forward declarations to be resolved"); if (!N->isResolved()) diff --git a/lib/IRReader/IRReader.cpp b/lib/IRReader/IRReader.cpp index 43fee65db7f..9b243fc571d 100644 --- a/lib/IRReader/IRReader.cpp +++ b/lib/IRReader/IRReader.cpp @@ -31,11 +31,11 @@ static const char *const TimeIRParsingName = "Parse IR"; static std::unique_ptr getLazyIRModule(std::unique_ptr Buffer, SMDiagnostic &Err, - LLVMContext &Context) { + LLVMContext &Context, bool ShouldLazyLoadMetadata) { if (isBitcode((const unsigned char *)Buffer->getBufferStart(), (const unsigned char *)Buffer->getBufferEnd())) { - ErrorOr> ModuleOrErr = - getLazyBitcodeModule(std::move(Buffer), Context); + ErrorOr> ModuleOrErr = getLazyBitcodeModule( + std::move(Buffer), Context, ShouldLazyLoadMetadata); if (std::error_code EC = ModuleOrErr.getError()) { Err = SMDiagnostic(Buffer->getBufferIdentifier(), SourceMgr::DK_Error, EC.message()); @@ -49,7 +49,8 @@ getLazyIRModule(std::unique_ptr Buffer, SMDiagnostic &Err, std::unique_ptr llvm::getLazyIRFileModule(StringRef Filename, SMDiagnostic &Err, - LLVMContext &Context) { + LLVMContext &Context, + bool ShouldLazyLoadMetadata) { ErrorOr> FileOrErr = MemoryBuffer::getFileOrSTDIN(Filename); if (std::error_code EC = FileOrErr.getError()) { @@ -58,7 +59,8 @@ std::unique_ptr llvm::getLazyIRFileModule(StringRef Filename, return nullptr; } - return getLazyIRModule(std::move(FileOrErr.get()), Err, Context); + return getLazyIRModule(std::move(FileOrErr.get()), Err, Context, + ShouldLazyLoadMetadata); } std::unique_ptr llvm::parseIR(MemoryBufferRef Buffer, SMDiagnostic &Err, diff --git a/lib/Linker/IRMover.cpp b/lib/Linker/IRMover.cpp index 8a11a0099b5..01a0a425d8d 100644 --- a/lib/Linker/IRMover.cpp +++ b/lib/Linker/IRMover.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/GVMaterializer.h" #include "llvm/IR/TypeFinder.h" #include "llvm/Transforms/Utils/Cloning.h" using namespace llvm; @@ -349,6 +350,9 @@ public: GlobalValueMaterializer(IRLinker *ModLinker) : ModLinker(ModLinker) {} Value *materializeDeclFor(Value *V) override; void materializeInitFor(GlobalValue *New, GlobalValue *Old) override; + Metadata *mapTemporaryMetadata(Metadata *MD) override; + void replaceTemporaryMetadata(const Metadata *OrigMD, + Metadata *NewMD) override; }; class LocalValueMaterializer final : public ValueMaterializer { @@ -358,6 +362,9 @@ public: LocalValueMaterializer(IRLinker *ModLinker) : ModLinker(ModLinker) {} Value *materializeDeclFor(Value *V) override; void materializeInitFor(GlobalValue *New, GlobalValue *Old) override; + Metadata *mapTemporaryMetadata(Metadata *MD) override; + void replaceTemporaryMetadata(const Metadata *OrigMD, + Metadata *NewMD) override; }; /// This is responsible for keeping track of the state used for moving data @@ -394,6 +401,24 @@ class IRLinker { bool HasError = false; + /// Flag indicating that we are just linking metadata (after function + /// importing). + bool IsMetadataLinkingPostpass; + + /// Flags to pass to value mapper invocations. + RemapFlags ValueMapperFlags = RF_MoveDistinctMDs; + + /// Association between metadata values created during bitcode parsing and + /// the value id. Used to correlate temporary metadata created during + /// function importing with the final metadata parsed during the subsequent + /// metadata linking postpass. + DenseMap MDValueToValIDMap; + + /// Association between metadata value id and temporary metadata that + /// remains unmapped after function importing. Saved during function + /// importing and consumed during the metadata linking postpass. + DenseMap *ValIDToTempMDMap; + /// Handles cloning of a global values from the source module into /// the destination module, including setting the attributes and visibility. GlobalValue *copyGlobalValueProto(const GlobalValue *SGV, bool ForDefinition); @@ -409,6 +434,14 @@ class IRLinker { SrcM.getContext().diagnose(LinkDiagnosticInfo(DS_Warning, Message)); } + /// Check whether we should be linking metadata from the source module. + bool shouldLinkMetadata() { + // ValIDToTempMDMap will be non-null when we are importing or otherwise want + // to link metadata lazily, and then when linking the metadata. + // We only want to return true for the former case. + return ValIDToTempMDMap == nullptr || IsMetadataLinkingPostpass; + } + /// Given a global in the source module, return the global in the /// destination module that is being linked to, if any. GlobalValue *getLinkedToGlobal(const GlobalValue *SrcGV) { @@ -457,16 +490,35 @@ class IRLinker { public: IRLinker(Module &DstM, IRMover::IdentifiedStructTypeSet &Set, Module &SrcM, ArrayRef ValuesToLink, - std::function AddLazyFor) + std::function AddLazyFor, + DenseMap *ValIDToTempMDMap = nullptr, + bool IsMetadataLinkingPostpass = false) : DstM(DstM), SrcM(SrcM), AddLazyFor(AddLazyFor), TypeMap(Set), - GValMaterializer(this), LValMaterializer(this) { + GValMaterializer(this), LValMaterializer(this), + IsMetadataLinkingPostpass(IsMetadataLinkingPostpass), + ValIDToTempMDMap(ValIDToTempMDMap) { for (GlobalValue *GV : ValuesToLink) maybeAdd(GV); + + // If appropriate, tell the value mapper that it can expect to see + // temporary metadata. + if (!shouldLinkMetadata()) + ValueMapperFlags = ValueMapperFlags | RF_HaveUnmaterializedMetadata; } bool run(); Value *materializeDeclFor(Value *V, bool ForAlias); void materializeInitFor(GlobalValue *New, GlobalValue *Old, bool ForAlias); + + /// Save the mapping between the given temporary metadata and its metadata + /// value id. Used to support metadata linking as a postpass for function + /// importing. + Metadata *mapTemporaryMetadata(Metadata *MD); + + /// Replace any temporary metadata saved for the source metadata's id with + /// the new non-temporary metadata. Used when metadata linking as a postpass + /// for function importing. + void replaceTemporaryMetadata(const Metadata *OrigMD, Metadata *NewMD); }; } @@ -500,6 +552,15 @@ void GlobalValueMaterializer::materializeInitFor(GlobalValue *New, ModLinker->materializeInitFor(New, Old, false); } +Metadata *GlobalValueMaterializer::mapTemporaryMetadata(Metadata *MD) { + return ModLinker->mapTemporaryMetadata(MD); +} + +void GlobalValueMaterializer::replaceTemporaryMetadata(const Metadata *OrigMD, + Metadata *NewMD) { + ModLinker->replaceTemporaryMetadata(OrigMD, NewMD); +} + Value *LocalValueMaterializer::materializeDeclFor(Value *V) { return ModLinker->materializeDeclFor(V, true); } @@ -509,6 +570,15 @@ void LocalValueMaterializer::materializeInitFor(GlobalValue *New, ModLinker->materializeInitFor(New, Old, true); } +Metadata *LocalValueMaterializer::mapTemporaryMetadata(Metadata *MD) { + return ModLinker->mapTemporaryMetadata(MD); +} + +void LocalValueMaterializer::replaceTemporaryMetadata(const Metadata *OrigMD, + Metadata *NewMD) { + ModLinker->replaceTemporaryMetadata(OrigMD, NewMD); +} + Value *IRLinker::materializeDeclFor(Value *V, bool ForAlias) { auto *SGV = dyn_cast(V); if (!SGV) @@ -536,6 +606,51 @@ void IRLinker::materializeInitFor(GlobalValue *New, GlobalValue *Old, linkGlobalValueBody(*New, *Old); } +Metadata *IRLinker::mapTemporaryMetadata(Metadata *MD) { + if (!ValIDToTempMDMap) + return nullptr; + // If this temporary metadata has a value id recorded during function + // parsing, record that in the ValIDToTempMDMap if one was provided. + if (MDValueToValIDMap.count(MD)) { + unsigned Idx = MDValueToValIDMap[MD]; + // Check if we created a temp MD when importing a different function from + // this module. If so, reuse it the same temporary metadata, otherwise + // add this temporary metadata to the map. + if (!ValIDToTempMDMap->count(Idx)) { + MDNode *Node = cast(MD); + assert(Node->isTemporary()); + (*ValIDToTempMDMap)[Idx] = Node; + } + return (*ValIDToTempMDMap)[Idx]; + } + return nullptr; +} + +void IRLinker::replaceTemporaryMetadata(const Metadata *OrigMD, + Metadata *NewMD) { + if (!ValIDToTempMDMap) + return; +#ifndef NDEBUG + auto *N = dyn_cast_or_null(NewMD); + assert(!N || !N->isTemporary()); +#endif + // If a mapping between metadata value ids and temporary metadata + // created during function importing was provided, and the source + // metadata has a value id recorded during metadata parsing, replace + // the temporary metadata with the final mapped metadata now. + if (MDValueToValIDMap.count(OrigMD)) { + unsigned Idx = MDValueToValIDMap[OrigMD]; + // Nothing to do if we didn't need to create a temporary metadata during + // function importing. + if (!ValIDToTempMDMap->count(Idx)) + return; + MDNode *TempMD = (*ValIDToTempMDMap)[Idx]; + TempMD->replaceAllUsesWith(NewMD); + MDNode::deleteTemporary(TempMD); + ValIDToTempMDMap->erase(Idx); + } +} + /// Loop through the global variables in the src module and merge them into the /// dest module. GlobalVariable *IRLinker::copyGlobalVariableProto(const GlobalVariable *SGVar) { @@ -793,16 +908,16 @@ Constant *IRLinker::linkAppendingVarProto(GlobalVariable *DstGV, Constant *NewV; if (IsOldStructor) { auto *S = cast(V); - auto *E1 = MapValue(S->getOperand(0), ValueMap, RF_MoveDistinctMDs, + auto *E1 = MapValue(S->getOperand(0), ValueMap, ValueMapperFlags, &TypeMap, &GValMaterializer); - auto *E2 = MapValue(S->getOperand(1), ValueMap, RF_MoveDistinctMDs, + auto *E2 = MapValue(S->getOperand(1), ValueMap, ValueMapperFlags, &TypeMap, &GValMaterializer); Value *Null = Constant::getNullValue(VoidPtrTy); NewV = ConstantStruct::get(cast(EltTy), E1, E2, Null, nullptr); } else { - NewV = MapValue(V, ValueMap, RF_MoveDistinctMDs, &TypeMap, - &GValMaterializer); + NewV = + MapValue(V, ValueMap, ValueMapperFlags, &TypeMap, &GValMaterializer); } DstElements.push_back(NewV); } @@ -837,6 +952,14 @@ static bool useExistingDest(GlobalValue &SGV, GlobalValue *DGV, } bool IRLinker::shouldLink(GlobalValue *DGV, GlobalValue &SGV) { + // Already imported all the values. Just map to the Dest value + // in case it is referenced in the metadata. + if (IsMetadataLinkingPostpass) { + assert(!ValuesToLink.count(&SGV) && + "Source value unexpectedly requested for link during metadata link"); + return false; + } + if (ValuesToLink.count(&SGV)) return true; @@ -925,8 +1048,8 @@ Constant *IRLinker::linkGlobalValueProto(GlobalValue *SGV, bool ForAlias) { /// referenced are in Dest. void IRLinker::linkGlobalInit(GlobalVariable &Dst, GlobalVariable &Src) { // Figure out what the initializer looks like in the dest module. - Dst.setInitializer(MapValue(Src.getInitializer(), ValueMap, - RF_MoveDistinctMDs, &TypeMap, &GValMaterializer)); + Dst.setInitializer(MapValue(Src.getInitializer(), ValueMap, ValueMapperFlags, + &TypeMap, &GValMaterializer)); } /// Copy the source function over into the dest function and fix up references @@ -939,22 +1062,28 @@ bool IRLinker::linkFunctionBody(Function &Dst, Function &Src) { if (std::error_code EC = Src.materialize()) return emitError(EC.message()); + if (!shouldLinkMetadata()) + // This is only supported for lazy links. Do after materialization of + // a function and before remapping metadata on instructions below + // in RemapInstruction, as the saved mapping is used to handle + // the temporary metadata hanging off instructions. + SrcM.getMaterializer()->saveMDValueList(MDValueToValIDMap, true); + // Link in the prefix data. if (Src.hasPrefixData()) - Dst.setPrefixData(MapValue(Src.getPrefixData(), ValueMap, - RF_MoveDistinctMDs, &TypeMap, - &GValMaterializer)); + Dst.setPrefixData(MapValue(Src.getPrefixData(), ValueMap, ValueMapperFlags, + &TypeMap, &GValMaterializer)); // Link in the prologue data. if (Src.hasPrologueData()) Dst.setPrologueData(MapValue(Src.getPrologueData(), ValueMap, - RF_MoveDistinctMDs, &TypeMap, + ValueMapperFlags, &TypeMap, &GValMaterializer)); // Link in the personality function. if (Src.hasPersonalityFn()) Dst.setPersonalityFn(MapValue(Src.getPersonalityFn(), ValueMap, - RF_MoveDistinctMDs, &TypeMap, + ValueMapperFlags, &TypeMap, &GValMaterializer)); // Go through and convert function arguments over, remembering the mapping. @@ -971,7 +1100,7 @@ bool IRLinker::linkFunctionBody(Function &Dst, Function &Src) { SmallVector, 8> MDs; Src.getAllMetadata(MDs); for (const auto &I : MDs) - Dst.setMetadata(I.first, MapMetadata(I.second, ValueMap, RF_MoveDistinctMDs, + Dst.setMetadata(I.first, MapMetadata(I.second, ValueMap, ValueMapperFlags, &TypeMap, &GValMaterializer)); // Splice the body of the source function into the dest function. @@ -983,9 +1112,8 @@ bool IRLinker::linkFunctionBody(Function &Dst, Function &Src) { // functions and patch them up to point to the local versions. for (BasicBlock &BB : Dst) for (Instruction &I : BB) - RemapInstruction(&I, ValueMap, - RF_IgnoreMissingEntries | RF_MoveDistinctMDs, &TypeMap, - &GValMaterializer); + RemapInstruction(&I, ValueMap, RF_IgnoreMissingEntries | ValueMapperFlags, + &TypeMap, &GValMaterializer); // There is no need to map the arguments anymore. for (Argument &Arg : Src.args()) @@ -997,7 +1125,7 @@ bool IRLinker::linkFunctionBody(Function &Dst, Function &Src) { void IRLinker::linkAliasBody(GlobalAlias &Dst, GlobalAlias &Src) { Constant *Aliasee = Src.getAliasee(); - Constant *Val = MapValue(Aliasee, AliasValueMap, RF_MoveDistinctMDs, &TypeMap, + Constant *Val = MapValue(Aliasee, AliasValueMap, ValueMapperFlags, &TypeMap, &LValMaterializer); Dst.setAliasee(Val); } @@ -1024,7 +1152,7 @@ void IRLinker::linkNamedMDNodes() { // Add Src elements into Dest node. for (const MDNode *op : NMD.operands()) DestNMD->addOperand(MapMetadata( - op, ValueMap, RF_MoveDistinctMDs | RF_NullMapMissingGlobalValues, + op, ValueMap, ValueMapperFlags | RF_NullMapMissingGlobalValues, &TypeMap, &GValMaterializer)); } } @@ -1260,7 +1388,7 @@ bool IRLinker::run() { continue; assert(!GV->isDeclaration()); - MapValue(GV, ValueMap, RF_MoveDistinctMDs, &TypeMap, &GValMaterializer); + MapValue(GV, ValueMap, ValueMapperFlags, &TypeMap, &GValMaterializer); if (HasError) return true; } @@ -1272,11 +1400,39 @@ bool IRLinker::run() { // Remap all of the named MDNodes in Src into the DstM module. We do this // after linking GlobalValues so that MDNodes that reference GlobalValues // are properly remapped. - linkNamedMDNodes(); + if (shouldLinkMetadata()) { + // Even if just linking metadata we should link decls above in case + // any are referenced by metadata. IRLinker::shouldLink ensures that + // we don't actually link anything from source. + if (IsMetadataLinkingPostpass) { + // Ensure metadata materialized + if (SrcM.getMaterializer()->materializeMetadata()) + return true; + SrcM.getMaterializer()->saveMDValueList(MDValueToValIDMap, false); + } - // Merge the module flags into the DstM module. - if (linkModuleFlagsMetadata()) - return true; + linkNamedMDNodes(); + + if (IsMetadataLinkingPostpass) { + // Handle anything left in the ValIDToTempMDMap, such as metadata nodes + // not reached by the dbg.cu NamedMD (i.e. only reached from + // instructions). + // Walk the MDValueToValIDMap once to find the set of new (imported) MD + // that still has corresponding temporary metadata, and invoke metadata + // mapping on each one. + for (auto MDI : MDValueToValIDMap) { + if (!ValIDToTempMDMap->count(MDI.second)) + continue; + MapMetadata(MDI.first, ValueMap, ValueMapperFlags, &TypeMap, + &GValMaterializer); + } + assert(ValIDToTempMDMap->empty()); + } + + // Merge the module flags into the DstM module. + if (linkModuleFlagsMetadata()) + return true; + } return false; } @@ -1384,9 +1540,11 @@ IRMover::IRMover(Module &M) : Composite(M) { bool IRMover::move( Module &Src, ArrayRef ValuesToLink, - std::function AddLazyFor) { + std::function AddLazyFor, + DenseMap *ValIDToTempMDMap, + bool IsMetadataLinkingPostpass) { IRLinker TheLinker(Composite, IdentifiedStructTypes, Src, ValuesToLink, - AddLazyFor); + AddLazyFor, ValIDToTempMDMap, IsMetadataLinkingPostpass); bool RetCode = TheLinker.run(); Composite.dropTriviallyDeadConstantArrays(); return RetCode; diff --git a/lib/Linker/LinkModules.cpp b/lib/Linker/LinkModules.cpp index 9c9c0ef321f..556251092a2 100644 --- a/lib/Linker/LinkModules.cpp +++ b/lib/Linker/LinkModules.cpp @@ -48,6 +48,11 @@ class ModuleLinker { /// as part of a different backend compilation process. bool HasExportedFunctions = false; + /// Association between metadata value id and temporary metadata that + /// remains unmapped after function importing. Saved during function + /// importing and consumed during the metadata linking postpass. + DenseMap *ValIDToTempMDMap; + /// Used as the callback for lazy linking. /// The mover has just hit GV and we have to decide if it, and other members /// of the same comdat, should be linked. Every member to be linked is passed @@ -150,9 +155,10 @@ class ModuleLinker { public: ModuleLinker(IRMover &Mover, Module &SrcM, unsigned Flags, const FunctionInfoIndex *Index = nullptr, - DenseSet *FunctionsToImport = nullptr) + DenseSet *FunctionsToImport = nullptr, + DenseMap *ValIDToTempMDMap = nullptr) : Mover(Mover), SrcM(SrcM), Flags(Flags), ImportIndex(Index), - ImportFunction(FunctionsToImport) { + ImportFunction(FunctionsToImport), ValIDToTempMDMap(ValIDToTempMDMap) { assert((ImportIndex || !ImportFunction) && "Expect a FunctionInfoIndex when importing"); // If we have a FunctionInfoIndex but no function to import, @@ -161,6 +167,8 @@ public: // may be exported to another backend compilation. if (ImportIndex && !ImportFunction) HasExportedFunctions = ImportIndex->hasExportedFunctions(SrcM); + assert((ValIDToTempMDMap || !ImportFunction) && + "Function importing must provide a ValIDToTempMDMap"); } bool run(); @@ -502,6 +510,7 @@ bool ModuleLinker::getComdatResult(const Comdat *SrcC, bool ModuleLinker::shouldLinkFromSource(bool &LinkFromSrc, const GlobalValue &Dest, const GlobalValue &Src) { + // Should we unconditionally use the Src? if (shouldOverrideFromSrc()) { LinkFromSrc = true; @@ -776,7 +785,8 @@ bool ModuleLinker::run() { if (Mover.move(SrcM, ValuesToLink.getArrayRef(), [this](GlobalValue &GV, IRMover::ValueAdder Add) { addLazyFor(GV, Add); - })) + }, + ValIDToTempMDMap, false)) return true; Module &DstM = Mover.getModule(); for (auto &P : Internalize) { @@ -791,8 +801,10 @@ Linker::Linker(Module &M) : Mover(M) {} bool Linker::linkInModule(std::unique_ptr Src, unsigned Flags, const FunctionInfoIndex *Index, - DenseSet *FunctionsToImport) { - ModuleLinker TheLinker(Mover, *Src, Flags, Index, FunctionsToImport); + DenseSet *FunctionsToImport, + DenseMap *ValIDToTempMDMap) { + ModuleLinker TheLinker(Mover, *Src, Flags, Index, FunctionsToImport, + ValIDToTempMDMap); return TheLinker.run(); } @@ -801,6 +813,17 @@ bool Linker::linkInModuleForCAPI(Module &Src) { return TheLinker.run(); } +bool Linker::linkInMetadata(Module &Src, + DenseMap *ValIDToTempMDMap) { + SetVector ValuesToLink; + if (Mover.move( + Src, ValuesToLink.getArrayRef(), + [this](GlobalValue &GV, IRMover::ValueAdder Add) { assert(false); }, + ValIDToTempMDMap, true)) + return true; + return false; +} + //===----------------------------------------------------------------------===// // LinkModules entrypoint. //===----------------------------------------------------------------------===// diff --git a/lib/Transforms/IPO/FunctionImport.cpp b/lib/Transforms/IPO/FunctionImport.cpp index b8c2ea7dce2..639f15a03f5 100644 --- a/lib/Transforms/IPO/FunctionImport.cpp +++ b/lib/Transforms/IPO/FunctionImport.cpp @@ -292,6 +292,9 @@ bool FunctionImporter::importFunctions(Module &DestModule) { ModuleToFunctionsToImportMap, Index, ModuleLoaderCache); assert(Worklist.empty() && "Worklist hasn't been flushed in GetImportList"); + StringMap>> + ModuleToTempMDValsMap; + // Do the actual import of functions now, one Module at a time for (auto &FunctionsToImportPerModule : ModuleToFunctionsToImportMap) { // Get the module for the import @@ -301,13 +304,32 @@ bool FunctionImporter::importFunctions(Module &DestModule) { assert(&DestModule.getContext() == &SrcModule->getContext() && "Context mismatch"); + // Save the mapping of value ids to temporary metadata created when + // importing this function. If we have already imported from this module, + // add new temporary metadata to the existing mapping. + auto &TempMDVals = ModuleToTempMDValsMap[SrcModule->getModuleIdentifier()]; + if (!TempMDVals) + TempMDVals = llvm::make_unique>(); + // Link in the specified functions. if (TheLinker.linkInModule(std::move(SrcModule), Linker::Flags::None, - &Index, &FunctionsToImport)) + &Index, &FunctionsToImport, TempMDVals.get())) report_fatal_error("Function Import: link error"); ImportedCount += FunctionsToImport.size(); } + + // Now link in metadata for all modules from which we imported functions. + for (StringMapEntry>> &SME : + ModuleToTempMDValsMap) { + // Load the specified source module. + auto &SrcModule = ModuleLoaderCache(SME.getKey()); + + // Link in all necessary metadata from this module. + if (TheLinker.linkInMetadata(SrcModule, SME.getValue().get())) + return false; + } + DEBUG(dbgs() << "Imported " << ImportedCount << " functions for Module " << DestModule.getModuleIdentifier() << "\n"); return ImportedCount; diff --git a/lib/Transforms/Utils/ValueMapper.cpp b/lib/Transforms/Utils/ValueMapper.cpp index 00a8984845d..00ee3385981 100644 --- a/lib/Transforms/Utils/ValueMapper.cpp +++ b/lib/Transforms/Utils/ValueMapper.cpp @@ -167,13 +167,21 @@ Value *llvm::MapValue(const Value *V, ValueToValueMapTy &VM, RemapFlags Flags, } static Metadata *mapToMetadata(ValueToValueMapTy &VM, const Metadata *Key, - Metadata *Val) { + Metadata *Val, ValueMaterializer *Materializer, + RemapFlags Flags) { VM.MD()[Key].reset(Val); + if (Materializer && !(Flags & RF_HaveUnmaterializedMetadata)) { + auto *N = dyn_cast_or_null(Val); + // Need to invoke this once we have non-temporary MD. + if (!N || !N->isTemporary()) + Materializer->replaceTemporaryMetadata(Key, Val); + } return Val; } -static Metadata *mapToSelf(ValueToValueMapTy &VM, const Metadata *MD) { - return mapToMetadata(VM, MD, const_cast(MD)); +static Metadata *mapToSelf(ValueToValueMapTy &VM, const Metadata *MD, + ValueMaterializer *Materializer, RemapFlags Flags) { + return mapToMetadata(VM, MD, const_cast(MD), Materializer, Flags); } static Metadata *MapMetadataImpl(const Metadata *MD, @@ -206,10 +214,13 @@ static Metadata *mapMetadataOp(Metadata *Op, } /// Resolve uniquing cycles involving the given metadata. -static void resolveCycles(Metadata *MD) { - if (auto *N = dyn_cast_or_null(MD)) +static void resolveCycles(Metadata *MD, bool MDMaterialized) { + if (auto *N = dyn_cast_or_null(MD)) { + if (!MDMaterialized && N->isTemporary()) + return; if (!N->isResolved()) - N->resolveCycles(); + N->resolveCycles(MDMaterialized); + } } /// Remap the operands of an MDNode. @@ -238,7 +249,7 @@ static bool remapOperands(MDNode &Node, // Resolve uniquing cycles underneath distinct nodes on the fly so they // don't infect later operands. if (IsDistinct) - resolveCycles(New); + resolveCycles(New, !(Flags & RF_HaveUnmaterializedMetadata)); } } @@ -266,7 +277,7 @@ static Metadata *mapDistinctNode(const MDNode *Node, // Remap operands later. DistinctWorklist.push_back(NewMD); - return mapToMetadata(VM, Node, NewMD); + return mapToMetadata(VM, Node, NewMD, Materializer, Flags); } /// \brief Map a uniqued MDNode. @@ -277,22 +288,29 @@ static Metadata *mapUniquedNode(const MDNode *Node, ValueToValueMapTy &VM, RemapFlags Flags, ValueMapTypeRemapper *TypeMapper, ValueMaterializer *Materializer) { - assert(Node->isUniqued() && "Expected uniqued node"); + assert(((Flags & RF_HaveUnmaterializedMetadata) || Node->isUniqued()) && + "Expected uniqued node"); // Create a temporary node and map it upfront in case we have a uniquing // cycle. If necessary, this mapping will get updated by RAUW logic before // returning. auto ClonedMD = Node->clone(); - mapToMetadata(VM, Node, ClonedMD.get()); + mapToMetadata(VM, Node, ClonedMD.get(), Materializer, Flags); if (!remapOperands(*ClonedMD, DistinctWorklist, VM, Flags, TypeMapper, Materializer)) { // No operands changed, so use the original. ClonedMD->replaceAllUsesWith(const_cast(Node)); - return const_cast(Node); + // Even though replaceAllUsesWith would have replaced the value map + // entry, we need to explictly map with the final non-temporary node + // to replace any temporary metadata via the callback. + return mapToSelf(VM, Node, Materializer, Flags); } - // Uniquify the cloned node. - return MDNode::replaceWithUniqued(std::move(ClonedMD)); + // Uniquify the cloned node. Explicitly map it with the final non-temporary + // node so that replacement of temporary metadata via the callback occurs. + return mapToMetadata(VM, Node, + MDNode::replaceWithUniqued(std::move(ClonedMD)), + Materializer, Flags); } static Metadata *MapMetadataImpl(const Metadata *MD, @@ -305,18 +323,18 @@ static Metadata *MapMetadataImpl(const Metadata *MD, return NewMD; if (isa(MD)) - return mapToSelf(VM, MD); + return mapToSelf(VM, MD, Materializer, Flags); if (isa(MD)) if ((Flags & RF_NoModuleLevelChanges)) - return mapToSelf(VM, MD); + return mapToSelf(VM, MD, Materializer, Flags); if (const auto *VMD = dyn_cast(MD)) { Value *MappedV = MapValue(VMD->getValue(), VM, Flags, TypeMapper, Materializer); if (VMD->getValue() == MappedV || (!MappedV && (Flags & RF_IgnoreMissingEntries))) - return mapToSelf(VM, MD); + return mapToSelf(VM, MD, Materializer, Flags); // FIXME: This assert crashes during bootstrap, but I think it should be // correct. For now, just match behaviour from before the metadata/value @@ -325,7 +343,8 @@ static Metadata *MapMetadataImpl(const Metadata *MD, // assert((MappedV || (Flags & RF_NullMapMissingGlobalValues)) && // "Referenced metadata not in value map!"); if (MappedV) - return mapToMetadata(VM, MD, ValueAsMetadata::get(MappedV)); + return mapToMetadata(VM, MD, ValueAsMetadata::get(MappedV), Materializer, + Flags); return nullptr; } @@ -336,10 +355,25 @@ static Metadata *MapMetadataImpl(const Metadata *MD, // If this is a module-level metadata and we know that nothing at the // module level is changing, then use an identity mapping. if (Flags & RF_NoModuleLevelChanges) - return mapToSelf(VM, MD); + return mapToSelf(VM, MD, Materializer, Flags); // Require resolved nodes whenever metadata might be remapped. - assert(Node->isResolved() && "Unexpected unresolved node"); + assert(((Flags & RF_HaveUnmaterializedMetadata) || Node->isResolved()) && + "Unexpected unresolved node"); + + if (Materializer && Node->isTemporary()) { + assert(Flags & RF_HaveUnmaterializedMetadata); + Metadata *TempMD = + Materializer->mapTemporaryMetadata(const_cast(MD)); + // If the above callback returned an existing temporary node, use it + // instead of the current temporary node. This happens when earlier + // function importing passes already created and saved a temporary + // metadata node for the same value id. + if (TempMD) { + mapToMetadata(VM, MD, TempMD, Materializer, Flags); + return TempMD; + } + } if (Node->isDistinct()) return mapDistinctNode(Node, DistinctWorklist, VM, Flags, TypeMapper, @@ -363,7 +397,7 @@ Metadata *llvm::MapMetadata(const Metadata *MD, ValueToValueMapTy &VM, return NewMD; // Resolve cycles involving the entry metadata. - resolveCycles(NewMD); + resolveCycles(NewMD, !(Flags & RF_HaveUnmaterializedMetadata)); // Remap the operands of distinct MDNodes. while (!DistinctWorklist.empty()) diff --git a/test/Linker/Inputs/thinlto_funcimport_debug.ll b/test/Linker/Inputs/thinlto_funcimport_debug.ll new file mode 100644 index 00000000000..846a5ea001d --- /dev/null +++ b/test/Linker/Inputs/thinlto_funcimport_debug.ll @@ -0,0 +1,38 @@ +; ModuleID = 'dbg_main.o' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind uwtable +define i32 @main() #0 !dbg !4 { +entry: + %call = tail call i32 @func1(i32 10) #2, !dbg !11 + %call1 = tail call i32 @func2(i32 10) #2, !dbg !12 + ret i32 0, !dbg !13 +} + +declare i32 @func1(i32) #1 + +declare i32 @func2(i32) #1 + +attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!8, !9} +!llvm.ident = !{!10} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 251407) (llvm/trunk 251401)", isOptimized: true, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3) +!1 = !DIFile(filename: "dbg_main.c", directory: ".") +!2 = !{} +!3 = !{!4} +!4 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 3, type: !5, isLocal: false, isDefinition: true, scopeLine: 3, isOptimized: true, variables: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{!7} +!7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{!"clang version 3.8.0 (trunk 251407) (llvm/trunk 251401)"} +!11 = !DILocation(line: 4, column: 3, scope: !4) +!12 = !DILocation(line: 5, column: 3, scope: !4) +!13 = !DILocation(line: 6, column: 1, scope: !4) diff --git a/test/Linker/thinlto_funcimport_debug.ll b/test/Linker/thinlto_funcimport_debug.ll new file mode 100644 index 00000000000..6c33caacf9e --- /dev/null +++ b/test/Linker/thinlto_funcimport_debug.ll @@ -0,0 +1,71 @@ +; Do setup work for all below tests: generate bitcode and combined index +; RUN: llvm-as -function-summary %s -o %t.bc +; RUN: llvm-as -function-summary %p/Inputs/thinlto_funcimport_debug.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc + +; Confirm that we link the metadata for the imported module. +; RUN: llvm-link %t2.bc -functionindex=%t3.thinlto.bc -import=func1:%t.bc -S | FileCheck %s + +; CHECK: declare i32 @func2 +; CHECK: define available_externally i32 @func1 +; CHECK: distinct !DISubprogram(name: "func1" +; CHECK: distinct !DISubprogram(name: "func2" + +; ModuleID = 'dbg.o' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind readnone uwtable +define i32 @func1(i32 %n) #0 !dbg !4 { +entry: + tail call void @llvm.dbg.value(metadata i32 %n, i64 0, metadata !9, metadata !17), !dbg !18 + tail call void @llvm.dbg.value(metadata i32 5, i64 0, metadata !10, metadata !17), !dbg !19 + %cmp = icmp sgt i32 %n, 10, !dbg !20 + %. = select i1 %cmp, i32 10, i32 5, !dbg !22 + tail call void @llvm.dbg.value(metadata i32 %., i64 0, metadata !10, metadata !17), !dbg !19 + ret i32 %., !dbg !23 +} + +; Function Attrs: nounwind readnone uwtable +define i32 @func2(i32 %n) #0 !dbg !11 { +entry: + tail call void @llvm.dbg.value(metadata i32 %n, i64 0, metadata !13, metadata !17), !dbg !24 + ret i32 %n, !dbg !25 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1 + +attributes #0 = { nounwind readnone uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!14, !15} +!llvm.ident = !{!16} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 251407) (llvm/trunk 251401)", isOptimized: true, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3) +!1 = !DIFile(filename: "dbg.c", directory: ".") +!2 = !{} +!3 = !{!4, !11} +!4 = distinct !DISubprogram(name: "func1", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, variables: !8) +!5 = !DISubroutineType(types: !6) +!6 = !{!7, !7} +!7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !{!9, !10} +!9 = !DILocalVariable(name: "n", arg: 1, scope: !4, file: !1, line: 1, type: !7) +!10 = !DILocalVariable(name: "x", scope: !4, file: !1, line: 2, type: !7) +!11 = distinct !DISubprogram(name: "func2", scope: !1, file: !1, line: 8, type: !5, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped, isOptimized: true, variables: !12) +!12 = !{!13} +!13 = !DILocalVariable(name: "n", arg: 1, scope: !11, file: !1, line: 8, type: !7) +!14 = !{i32 2, !"Dwarf Version", i32 4} +!15 = !{i32 2, !"Debug Info Version", i32 3} +!16 = !{!"clang version 3.8.0 (trunk 251407) (llvm/trunk 251401)"} +!17 = !DIExpression() +!18 = !DILocation(line: 1, column: 15, scope: !4) +!19 = !DILocation(line: 2, column: 7, scope: !4) +!20 = !DILocation(line: 3, column: 9, scope: !21) +!21 = distinct !DILexicalBlock(scope: !4, file: !1, line: 3, column: 7) +!22 = !DILocation(line: 3, column: 7, scope: !4) +!23 = !DILocation(line: 5, column: 3, scope: !4) +!24 = !DILocation(line: 8, column: 15, scope: !11) +!25 = !DILocation(line: 9, column: 3, scope: !11) diff --git a/test/Transforms/FunctionImport/Inputs/funcimport_debug.ll b/test/Transforms/FunctionImport/Inputs/funcimport_debug.ll new file mode 100644 index 00000000000..35c62a26290 --- /dev/null +++ b/test/Transforms/FunctionImport/Inputs/funcimport_debug.ll @@ -0,0 +1,27 @@ +; ModuleID = 'funcimport_debug.o' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind uwtable +define void @func() #0 !dbg !4 { +entry: + ret void, !dbg !10 +} + +attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 255685) (llvm/trunk 255682)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3) +!1 = !DIFile(filename: "funcimport_debug.c", directory: ".") +!2 = !{} +!3 = !{!4} +!4 = distinct !DISubprogram(name: "func", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, variables: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{null} +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{!"clang version 3.8.0 (trunk 255685) (llvm/trunk 255682)"} +!10 = !DILocation(line: 2, column: 1, scope: !4) diff --git a/test/Transforms/FunctionImport/funcimport_debug.ll b/test/Transforms/FunctionImport/funcimport_debug.ll new file mode 100644 index 00000000000..c57b5e14af1 --- /dev/null +++ b/test/Transforms/FunctionImport/funcimport_debug.ll @@ -0,0 +1,45 @@ +; Do setup work for all below tests: generate bitcode and combined index +; RUN: llvm-as -function-summary %s -o %t.bc +; RUN: llvm-as -function-summary %p/Inputs/funcimport_debug.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc + +; Do the import now and confirm that metadata is linked for imported function. +; RUN: opt -function-import -summary-file %t3.thinlto.bc %s -S | FileCheck %s + +; CHECK: define available_externally void @func() +; CHECK: distinct !DISubprogram(name: "main" +; CHECK: distinct !DISubprogram(name: "func" + +; ModuleID = 'funcimport_debug.o' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind uwtable +define i32 @main() #0 !dbg !4 { +entry: + call void (...) @func(), !dbg !11 + ret i32 0, !dbg !12 +} + +declare void @func(...) #1 + +attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!8, !9} +!llvm.ident = !{!10} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 255685) (llvm/trunk 255682)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3) +!1 = !DIFile(filename: "funcimport_debug.c", directory: ".") +!2 = !{} +!3 = !{!4} +!4 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 2, type: !5, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: false, variables: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{!7} +!7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{!"clang version 3.8.0 (trunk 255685) (llvm/trunk 255682)"} +!11 = !DILocation(line: 3, column: 3, scope: !4) +!12 = !DILocation(line: 4, column: 1, scope: !4) diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp index 326ecba3ae9..a32383028ae 100644 --- a/tools/llvm-link/llvm-link.cpp +++ b/tools/llvm-link/llvm-link.cpp @@ -106,16 +106,21 @@ static cl::opt PreserveAssemblyUseListOrder( // Read the specified bitcode file in and return it. This routine searches the // link path for the specified file to try to find it... // -static std::unique_ptr -loadFile(const char *argv0, const std::string &FN, LLVMContext &Context) { +static std::unique_ptr loadFile(const char *argv0, + const std::string &FN, + LLVMContext &Context, + bool MaterializeMetadata = true) { SMDiagnostic Err; if (Verbose) errs() << "Loading '" << FN << "'\n"; - std::unique_ptr Result = getLazyIRFileModule(FN, Err, Context); + std::unique_ptr Result = + getLazyIRFileModule(FN, Err, Context, !MaterializeMetadata); if (!Result) Err.print(argv0, errs()); - Result->materializeMetadata(); - UpgradeDebugInfo(*Result); + if (MaterializeMetadata) { + Result->materializeMetadata(); + UpgradeDebugInfo(*Result); + } return Result; } @@ -148,6 +153,8 @@ static void diagnosticHandlerWithContext(const DiagnosticInfo &DI, void *C) { /// Import any functions requested via the -import option. static bool importFunctions(const char *argv0, LLVMContext &Context, Linker &L) { + StringMap>> + ModuleToTempMDValsMap; for (const auto &Import : Imports) { // Identify the requested function and its bitcode source file. size_t Idx = Import.find(':'); @@ -159,7 +166,7 @@ static bool importFunctions(const char *argv0, LLVMContext &Context, std::string FileName = Import.substr(Idx + 1, std::string::npos); // Load the specified source module. - std::unique_ptr M = loadFile(argv0, FileName, Context); + std::unique_ptr M = loadFile(argv0, FileName, Context, false); if (!M.get()) { errs() << argv0 << ": error loading file '" << FileName << "'\n"; return false; @@ -201,11 +208,39 @@ static bool importFunctions(const char *argv0, LLVMContext &Context, Index = std::move(IndexOrErr.get()); } + // Save the mapping of value ids to temporary metadata created when + // importing this function. If we have already imported from this module, + // add new temporary metadata to the existing mapping. + auto &TempMDVals = ModuleToTempMDValsMap[FileName]; + if (!TempMDVals) + TempMDVals = llvm::make_unique>(); + // Link in the specified function. DenseSet FunctionsToImport; FunctionsToImport.insert(F); if (L.linkInModule(std::move(M), Linker::Flags::None, Index.get(), - &FunctionsToImport)) + &FunctionsToImport, TempMDVals.get())) + return false; + } + + // Now link in metadata for all modules from which we imported functions. + for (StringMapEntry>> &SME : + ModuleToTempMDValsMap) { + // Load the specified source module. + std::unique_ptr M = loadFile(argv0, SME.getKey(), Context, true); + if (!M.get()) { + errs() << argv0 << ": error loading file '" << SME.getKey() << "'\n"; + return false; + } + + if (verifyModule(*M, &errs())) { + errs() << argv0 << ": " << SME.getKey() + << ": error: input module is broken!\n"; + return false; + } + + // Link in all necessary metadata from this module. + if (L.linkInMetadata(*M, SME.getValue().get())) return false; } return true; -- 2.34.1