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; } @@ -906,7 +906,6 @@ Expected IRLinker::linkGlobalValueProto(GlobalValue *SGV, bool ForAlias) { GlobalValue *DGV = getLinkedToGlobal(SGV); - bool ShouldLink = shouldLink(DGV, *SGV); // just missing from map @@ -1414,12 +1413,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); @@ -1432,10 +1433,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: test/Linker/Inputs/struct-mapping-baz.ll =================================================================== --- test/Linker/Inputs/struct-mapping-baz.ll +++ test/Linker/Inputs/struct-mapping-baz.ll @@ -0,0 +1,14 @@ +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 + +; Function Attrs: norecurse nounwind readonly uwtable +define %struct.Baz* @_Z6getBazv() local_unnamed_addr { +entry: + %0 = load %struct.Baz*, %struct.Baz** @gBaz, align 8 + ret %struct.Baz* %0 +} Index: test/Linker/Inputs/struct-mapping-foo.ll =================================================================== --- test/Linker/Inputs/struct-mapping-foo.ll +++ test/Linker/Inputs/struct-mapping-foo.ll @@ -0,0 +1,26 @@ +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 + +; Function Attrs: uwtable +define %struct.Foo* @_Z6getFoov() local_unnamed_addr { +entry: + %0 = load %struct.Foo*, %struct.Foo** @gFoo, align 8 + %tobool = icmp eq %struct.Foo* %0, null + br i1 %tobool, label %cond.false, label %cond.end + +cond.false: ; preds = %entry + %call = tail call %struct.Baz* @_Z6getBazv() + %f = getelementptr inbounds %struct.Baz, %struct.Baz* %call, i64 0, i32 2 + br label %cond.end + +cond.end: ; preds = %entry, %cond.false + %cond = phi %struct.Foo* [ %f, %cond.false ], [ %0, %entry ] + ret %struct.Foo* %cond +} + +declare %struct.Baz* @_Z6getBazv() local_unnamed_addr Index: test/Linker/struct-mapping.ll =================================================================== --- test/Linker/struct-mapping.ll +++ test/Linker/struct-mapping.ll @@ -0,0 +1,38 @@ +; RUN: llvm-link --initial-module=%s %p/Inputs/struct-mapping-baz.ll %p/Inputs/struct-mapping-foo.ll -S -o - | FileCheck %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. +; CHECK: %struct.Baz = type { i64, i64, %struct.Foo } + +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 +@F0 = local_unnamed_addr global %struct.Foo 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 }] + +; Function Attrs: norecurse nounwind readonly uwtable +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* @_Z6getFoov() local_unnamed_addr + +; Function Attrs: uwtable +define internal void @_GLOBAL__sub_I_main.cpp() section ".text.startup" { +entry: + %call.i.i = tail call %struct.Foo* @_Z6getFoov() + store %struct.Foo* %call.i.i, %struct.Foo** getelementptr inbounds (%struct.S, %struct.S* @instance, i64 0, i32 0), align 8 + ret void +} Index: tools/llvm-link/llvm-link.cpp =================================================================== --- tools/llvm-link/llvm-link.cpp +++ tools/llvm-link/llvm-link.cpp @@ -70,6 +70,11 @@ OutputFilename("o", cl::desc("Override output filename"), cl::init("-"), cl::value_desc("filename")); +static cl::opt +InitialModule("initial-module", + cl::desc("Link to existing destination module"), cl::init(""), + cl::value_desc("filename")); + static cl::opt Internalize("internalize", cl::desc("Internalize linked symbols")); @@ -360,7 +365,9 @@ if (!DisableDITypeMap) Context.enableDebugTypeODRUniquing(); - auto Composite = make_unique("llvm-link", Context); + auto Composite = InitialModule.empty() + ? make_unique("llvm-link", Context) + : loadFile(argv[0], InitialModule, Context); Linker L(*Composite); unsigned Flags = Linker::Flags::None;