Index: include/llvm/Linker/IRMover.h =================================================================== --- include/llvm/Linker/IRMover.h +++ include/llvm/Linker/IRMover.h @@ -52,11 +52,16 @@ // The set of identified but non opaque structures in the composite module. DenseSet NonOpaqueStructTypes; + // Map between structure type name and instance. Used in findNonOpaque + // to correctly map imported global variable type during ThinLTO import + // phase. + DenseMap NonOpaqueStructNameMap; public: void addNonOpaque(StructType *Ty); void switchToNonOpaque(StructType *Ty); void addOpaque(StructType *Ty); - StructType *findNonOpaque(ArrayRef ETypes, bool IsPacked); + StructType *findNonOpaque(ArrayRef ETypes, bool IsPacked, + StringRef Name); bool hasType(StructType *Ty); }; Index: lib/Linker/IRMover.cpp =================================================================== --- lib/Linker/IRMover.cpp +++ lib/Linker/IRMover.cpp @@ -319,7 +319,7 @@ } if (StructType *OldT = - DstStructTypesSet.findNonOpaque(ElementTypes, IsPacked)) { + DstStructTypesSet.findNonOpaque(ElementTypes, IsPacked, STy->getName())) { STy->setName(""); return *Entry = OldT; } @@ -682,6 +682,14 @@ return NewGV; } +static StringRef getTypeNamePrefix(StringRef Name) { + size_t DotPos = Name.rfind('.'); + return (DotPos == 0 || DotPos == StringRef::npos || Name.back() == '.' || + !isdigit(static_cast(Name[DotPos + 1]))) + ? Name + : Name.substr(0, DotPos); +} + /// Loop over all of the linked values to compute type mappings. For example, /// if we link "extern Foo *x" and "Foo *x = NULL", then we have two struct /// types 'Foo' but one got renamed when the module was loaded into the same @@ -729,14 +737,12 @@ } // Check to see if there is a dot in the name followed by a digit. - size_t DotPos = ST->getName().rfind('.'); - if (DotPos == 0 || DotPos == StringRef::npos || - ST->getName().back() == '.' || - !isdigit(static_cast(ST->getName()[DotPos + 1]))) + auto STTypePrefix = getTypeNamePrefix(ST->getName()); + if (STTypePrefix.size()== ST->getName().size()) continue; // Check to see if the destination module has a struct with the prefix name. - StructType *DST = DstM.getTypeByName(ST->getName().substr(0, DotPos)); + StructType *DST = DstM.getTypeByName(STTypePrefix); if (!DST) continue; @@ -901,7 +907,6 @@ Expected IRLinker::linkGlobalValueProto(GlobalValue *SGV, bool ForAlias) { GlobalValue *DGV = getLinkedToGlobal(SGV); - bool ShouldLink = shouldLink(DGV, *SGV); // just missing from map @@ -1046,14 +1051,11 @@ ValueMap.MD()[CU->getRawEnumTypes()].reset(nullptr); ValueMap.MD()[CU->getRawMacros()].reset(nullptr); ValueMap.MD()[CU->getRawRetainedTypes()].reset(nullptr); - // If we ever start importing global variable defs, we'll need to - // add their DIGlobalVariable to the globals list on the imported - // DICompileUnit. Confirm none are imported, and then we can - // map the list of global variables to nullptr. - assert(none_of( - ValuesToLink, - [](const GlobalValue *GV) { return isa(GV); }) && - "Unexpected importing of a GlobalVariable definition"); + + // We import global variables only temporarily in order for instcombine + // and globalopt to perform constant folding and static constructor + // evaluation. After that elim-avail-extern will covert imported globals + // back to declarations, so we don't need debug info for them. ValueMap.MD()[CU->getRawGlobalVariables()].reset(nullptr); // Imported entities only need to be mapped in if they have local @@ -1409,12 +1411,14 @@ void IRMover::IdentifiedStructTypeSet::addNonOpaque(StructType *Ty) { assert(!Ty->isOpaque()); + if (Ty->hasName()) + NonOpaqueStructNameMap.insert({getTypeNamePrefix(Ty->getName()), Ty}); + NonOpaqueStructTypes.insert(Ty); } void IRMover::IdentifiedStructTypeSet::switchToNonOpaque(StructType *Ty) { - assert(!Ty->isOpaque()); - NonOpaqueStructTypes.insert(Ty); + addNonOpaque(Ty); bool Removed = OpaqueStructTypes.erase(Ty); (void)Removed; assert(Removed); @@ -1427,10 +1431,16 @@ StructType * IRMover::IdentifiedStructTypeSet::findNonOpaque(ArrayRef ETypes, - bool IsPacked) { + bool IsPacked, StringRef Name) { IRMover::StructTypeKeyInfo::KeyTy Key(ETypes, IsPacked); auto I = NonOpaqueStructTypes.find_as(Key); - return I == NonOpaqueStructTypes.end() ? nullptr : *I; + if (I == NonOpaqueStructTypes.end()) + return nullptr; + auto NI = NonOpaqueStructNameMap.find(getTypeNamePrefix(Name)); + if (NI != NonOpaqueStructNameMap.end() && + IRMover::StructTypeKeyInfo::KeyTy((*NI).second) == Key) + return (*NI).second; + return *I; } bool IRMover::IdentifiedStructTypeSet::hasType(StructType *Ty) { Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -238,6 +238,33 @@ return Index.getValueInfo(GUID); } +static void computeImportForReferencedGlobals( + const FunctionSummary &Summary, const GVSummaryMapTy &DefinedGVSummaries, + FunctionImporter::ImportMapTy &ImportList, + StringMap *ExportLists) { + if (Summary.modulePath().empty()) + return; + + for (auto &VI : Summary.refs()) { + if (DefinedGVSummaries.count(VI.getGUID())) { + DEBUG(dbgs() << "Ref ignored! Target already in destination module.\n"); + continue; + } + + DEBUG(dbgs() << " ref -> " << VI.getGUID() << "\n"); + + for (auto &RefSummary : VI.getSummaryList()) + if (RefSummary->getSummaryKind() == GlobalValueSummary::GlobalVarKind && + !RefSummary->modulePath().empty() && + RefSummary->linkage() == GlobalValue::ExternalLinkage && + RefSummary->refs().empty()) { + ImportList[RefSummary->modulePath()][VI.getGUID()] = 1; + if (ExportLists) + (*ExportLists)[RefSummary->modulePath()].insert(VI.getGUID()); + } + } +} + /// Compute the list of functions to import for a given caller. Mark these /// imported functions and the symbols they reference in their source module as /// exported from their source module. @@ -247,6 +274,8 @@ SmallVectorImpl &Worklist, FunctionImporter::ImportMapTy &ImportList, StringMap *ExportLists = nullptr) { + computeImportForReferencedGlobals(Summary, DefinedGVSummaries, ImportList, + ExportLists); for (auto &Edge : Summary.calls()) { ValueInfo VI = Edge.first; DEBUG(dbgs() << " edge -> " << VI.getGUID() << " Threshold:" << Threshold Index: test/ThinLTO/X86/Inputs/globals-import-cf-baz.ll =================================================================== --- test/ThinLTO/X86/Inputs/globals-import-cf-baz.ll +++ test/ThinLTO/X86/Inputs/globals-import-cf-baz.ll @@ -0,0 +1,4 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@baz = local_unnamed_addr constant i32 10, align 4 Index: test/ThinLTO/X86/Inputs/globals-import-eval-baz.ll =================================================================== --- test/ThinLTO/X86/Inputs/globals-import-eval-baz.ll +++ test/ThinLTO/X86/Inputs/globals-import-eval-baz.ll @@ -0,0 +1,7 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +%struct.Baz = type { i64, i64, %struct.Foo } +%struct.Foo = type { i64, i64 } + +@gBaz = local_unnamed_addr global %struct.Baz* null, align 8 Index: test/ThinLTO/X86/Inputs/globals-import-eval-foo.ll =================================================================== --- test/ThinLTO/X86/Inputs/globals-import-eval-foo.ll +++ test/ThinLTO/X86/Inputs/globals-import-eval-foo.ll @@ -0,0 +1,18 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +%struct.Foo = type { i64, i64 } +%struct.Baz = type { i64, i64, %struct.Foo } + +@gFoo = local_unnamed_addr global %struct.Foo* null, align 8 +@gBaz = external local_unnamed_addr global %struct.Baz*, align 8 + +define %struct.Foo* @getFoo() local_unnamed_addr { +entry: + %0 = load %struct.Foo*, %struct.Foo** @gFoo, align 8 + %tobool = icmp eq %struct.Foo* %0, null + %1 = load %struct.Baz*, %struct.Baz** @gBaz, align 8 + %f = getelementptr inbounds %struct.Baz, %struct.Baz* %1, i64 0, i32 2 + %cond = select i1 %tobool, %struct.Foo* %f, %struct.Foo* %0 + ret %struct.Foo* %cond +} Index: test/ThinLTO/X86/globals-import-const-fold.ll =================================================================== --- test/ThinLTO/X86/globals-import-const-fold.ll +++ test/ThinLTO/X86/globals-import-const-fold.ll @@ -0,0 +1,23 @@ +; RUN: opt -module-summary %s -o %t1.bc +; RUN: opt -module-summary %p/Inputs/globals-import-cf-baz.ll -o %t2.bc +; RUN: llvm-lto -thinlto-action=thinlink %t1.bc %t2.bc -o %t3.index.bc + +; RUN: llvm-lto -thinlto-action=import %t1.bc %t2.bc -thinlto-index=%t3.index.bc +; RUN: llvm-dis %t1.bc.thinlto.imported.bc -o - | FileCheck --check-prefix=IMPORT %s +; RUN: llvm-lto -thinlto-action=optimize %t1.bc.thinlto.imported.bc -o %t1.bc.thinlto.opt.bc +; RUN: llvm-dis %t1.bc.thinlto.opt.bc -o - | FileCheck --check-prefix=OPTIMIZE %s + +; IMPORT: @baz = available_externally local_unnamed_addr constant i32 10 + +; OPTIMIZE: define i32 @main() +; OPTIMIZE-NEXT: ret i32 10 + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@baz = external local_unnamed_addr constant i32, align 4 + +define i32 @main() local_unnamed_addr { + %1 = load i32, i32* @baz, align 4 + ret i32 %1 +} Index: test/ThinLTO/X86/globals-import-eval.ll =================================================================== --- test/ThinLTO/X86/globals-import-eval.ll +++ test/ThinLTO/X86/globals-import-eval.ll @@ -0,0 +1,46 @@ +; RUN: opt -module-summary %s -o %t1.bc +; RUN: opt -module-summary %p/Inputs/globals-import-eval-foo.ll -o %t2.bc +; RUN: opt -module-summary %p/Inputs/globals-import-eval-baz.ll -o %t3.bc +; RUN: llvm-lto -thinlto-action=thinlink %t1.bc %t2.bc %t3.bc -o %t4.index.bc + +; RUN: llvm-lto -thinlto-action=import %t1.bc %t2.bc %t3.bc -thinlto-index=%t4.index.bc +; RUN: llvm-dis %t1.bc.thinlto.imported.bc -o - | FileCheck --check-prefix=IMPORT %s +; RUN: llvm-lto -thinlto-action=optimize %t1.bc.thinlto.imported.bc -o %t1.bc.thinlto.opt.bc +; RUN: llvm-dis %t1.bc.thinlto.opt.bc -o - | FileCheck --check-prefix=OPTIMIZE %s + +; Here we check that new type mapping algorithm correctly mapped type of internal +; member of struct.Baz to struct.Foo. Without it we'd map that type to struct.Bar, because +; it is recursively isomorphic to struct.Foo and is defined first in source file. +; IMPORT: %struct.Baz = type { i64, i64, %struct.Foo } + +; Here we check that static construct has been optimized out +; OPTIMIZE-NOT: _GLOBAL__sub_I_main.cpp + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +%struct.Bar = type { i64, i64 } +%struct.S = type { %struct.Foo* } +%struct.Foo = type { i64, i64 } + +@B0 = local_unnamed_addr global %struct.Bar zeroinitializer, align 8 +@instance = local_unnamed_addr global %struct.S zeroinitializer, align 8 +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_main.cpp, i8* null }] + +define i32 @main() local_unnamed_addr { +entry: + %0 = load %struct.Foo*, %struct.Foo** getelementptr inbounds (%struct.S, %struct.S* @instance, i64 0, i32 0), align 8 + %a = getelementptr inbounds %struct.Foo, %struct.Foo* %0, i64 0, i32 0 + %1 = load i64, i64* %a, align 8 + %conv = trunc i64 %1 to i32 + ret i32 %conv +} + +declare %struct.Foo* @getFoo() local_unnamed_addr + +define internal void @_GLOBAL__sub_I_main.cpp() section ".text.startup" { +entry: + %call.i.i = tail call %struct.Foo* @getFoo() + store %struct.Foo* %call.i.i, %struct.Foo** getelementptr inbounds (%struct.S, %struct.S* @instance, i64 0, i32 0), align 8 + ret void +} Index: test/Transforms/FunctionImport/funcimport.ll =================================================================== --- test/Transforms/FunctionImport/funcimport.ll +++ test/Transforms/FunctionImport/funcimport.ll @@ -106,7 +106,7 @@ declare void @variadic(...) ; INSTLIMDEF-DAG: Import globalfunc2 -; INSTLIMDEF-DAG: 13 function-import - Number of functions imported +; INSTLIMDEF-DAG: 14 function-import - Number of functions imported ; CHECK-DAG: !0 = !{!"{{.*}}/Inputs/funcimport.ll"} ; The actual GUID values will depend on path to test.