diff --git a/llvm/lib/Linker/IRMover.cpp b/llvm/lib/Linker/IRMover.cpp --- a/llvm/lib/Linker/IRMover.cpp +++ b/llvm/lib/Linker/IRMover.cpp @@ -520,6 +520,8 @@ void prepareCompileUnitsForImport(); void linkNamedMDNodes(); + Error preMaterializeFunctions(); + public: IRLinker(Module &DstM, MDMapT &SharedMDs, IRMover::IdentifiedStructTypeSet &Set, std::unique_ptr SrcM, @@ -1098,6 +1100,7 @@ assert(Dst.isDeclaration() && !Src.isDeclaration()); // Materialize if needed. + // TODO: can this be removed? if (Error Err = Src.materialize()) return Err; @@ -1516,6 +1519,13 @@ computeTypeMapping(); std::reverse(Worklist.begin(), Worklist.end()); + + // Materialize all Functions in Worklist and that will be added Lazily, prior + // to remapping otherwise blockaddress Constants may fail to materialize + // during IR moving. + if (Error E = preMaterializeFunctions()) + return E; + while (!Worklist.empty()) { GlobalValue *GV = Worklist.back(); Worklist.pop_back(); @@ -1578,6 +1588,34 @@ return linkModuleFlagsMetadata(); } +Error IRLinker::preMaterializeFunctions() { + // Make a copy of the Worklist. + SmallVector MaterializeList(Worklist.begin(), + Worklist.end()); + while (!MaterializeList.empty()) { + GlobalValue *GV = MaterializeList.pop_back_val(); + if (auto *F = dyn_cast(GV)) { + if (!F->isDeclaration()) + if (Error E = F->materialize()) + return E; + // Once materialized, check all operands for references to other + // Functions that have not yet been materialized. + for (BasicBlock &BB : *F) + for (Instruction &I : BB) + for (Use &U : I.operands()) + if (auto *Callee = dyn_cast(U)) { + // TODO: shouldLink? + if (!Callee->isMaterializable()) + continue; + if (Error E = Callee->materialize()) + return E; + MaterializeList.push_back(Callee); + } + } + } + return Error::success(); +} + IRMover::StructTypeKeyInfo::KeyTy::KeyTy(ArrayRef E, bool P) : ETypes(E), IsPacked(P) {} diff --git a/llvm/test/Linker/blockaddress.ll b/llvm/test/Linker/blockaddress.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Linker/blockaddress.ll @@ -0,0 +1,125 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-link %t.bc -S | FileCheck %s + +declare void @f(i8*) + +; Test that a blockaddress in @y referring to %label in @x can be moved when @y +; appears after @x. +define void @x() { + br label %label + +label: + call void @y() + ret void +} + +define void @y() { +; CHECK: define void @y() { +; CHECK-NEXT: call void @f(i8* blockaddress(@x, %label)) + call void @f(i8* blockaddress(@x, %label)) + ret void +} + +; Test that a blockaddress in @a referring to %label in @b can be moved when @a +; appears before @b. +define void @a() { +; CHECK: define void @a() { +; CHECK-NEXT: call void @f(i8* blockaddress(@b, %label)) + call void @f(i8* blockaddress(@b, %label)) + ret void +} + +define void @b() { + br label %label + +label: + call void @a() + ret void +} + +; Test that @c and @d can both have blockaddress Constants that refer to one +; another. + +define void @c() { +; CHECK: define void @c() { +; CHECK-NEXT: br label %label +; CHECK-EMPTY: +; CHECK-NEXT: label: +; CHECK-NEXT: call void @f(i8* blockaddress(@d, %label)) + br label %label + +label: + call void @f(i8* blockaddress(@d, %label)) + ret void +} + +define void @d() { +; CHECK: define void @d() { +; CHECK-NEXT: br label %label +; CHECK-EMPTY: +; CHECK-NEXT: label: +; CHECK-NEXT: call void @f(i8* blockaddress(@c, %label)) + br label %label + +label: + call void @f(i8* blockaddress(@c, %label)) + ret void +} + +; Test that Functions added to IRLinker's Worklist member lazily (linkonce_odr) +; aren't susceptible to the the same issues as @x/@y above. +define void @parsed() { + br label %label + +label: + ret void +} + +define linkonce_odr void @lazy() { +; CHECK: define linkonce_odr void @lazy() { +; CHECK-NEXT: br label %label +; CHECK-EMPTY: +; CHECK-NEXT: label: +; CHECK-NEXT: call void @f(i8* blockaddress(@parsed, %label)) + br label %label + +label: + call void @f(i8* blockaddress(@parsed, %label)) + ret void +} + +define void @parsed2() { + call void @lazy() + ret void +} + +; Same test as @lazy, just with one more level of lazy parsed functions. +define void @parsed3() { + br label %label + +label: + ret void +} + +define linkonce_odr void @lazy1() { +; CHECK: define linkonce_odr void @lazy1() { +; CHECK-NEXT: br label %label +; CHECK-EMPTY: +; CHECK-NEXT: label: +; CHECK-NEXT: call void @f(i8* blockaddress(@parsed3, %label)) + br label %label + +label: + call void @f(i8* blockaddress(@parsed3, %label)) + ret void +} + +define linkonce_odr void @lazy2() { + call void @lazy1() + ret void +} + +define void @parsed4() { + call void @lazy2() + ret void +}