diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp --- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp +++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp @@ -120,6 +120,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Utils/FunctionComparator.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" #include #include #include @@ -225,6 +226,10 @@ /// analyzed again. std::vector Deferred; + /// Set of values marked as used in llvm.used and llvm.compiler.used. + SmallPtrSet Used; + SmallPtrSet CompilerUsed; + #ifndef NDEBUG /// Checks the rules of order relation introduced among functions set. /// Returns true, if check has been passed, and false if failed. @@ -407,6 +412,14 @@ bool MergeFunctions::runOnModule(Module &M) { bool Changed = false; + // Keep track of functions referred to by llvm.used/llvm.compiler.used. + SmallVector UsedV; + collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/false); + Used.insert(UsedV.begin(), UsedV.end()); + SmallVector CompilerUsedV; + collectUsedGlobalVariables(M, CompilerUsedV, /*CompilerUsed=*/true); + CompilerUsed.insert(CompilerUsedV.begin(), CompilerUsedV.end()); + // All functions in the module, ordered by hash. Functions with a unique // hash value are easily eliminated. std::vector> @@ -453,6 +466,8 @@ FnTree.clear(); FNodesInTree.clear(); GlobalNumbers.clear(); + Used.clear(); + CompilerUsed.clear(); return Changed; } @@ -833,6 +848,18 @@ Constant *BitcastF = ConstantExpr::getBitCast(F, G->getType()); removeUsers(G); G->replaceAllUsesWith(BitcastF); + + // Functions referred to by llvm.used/llvm.compiler.used are special: + // there are uses of the symbol name that are not visible to LLVM, + // usually from inline asm. We need to handle these by keeping the old + // symbol in the used list as an alias or thunk. + // + // RAUW will have removed references to G from the existing list, make + // sure to add it back. + if (Used.contains(G)) + appendToUsed(*G->getParent(), {G}); + if (CompilerUsed.contains(G)) + appendToCompilerUsed(*G->getParent(), {G}); } else { // Redirect direct callers of G to F. (See note on MergeFunctionsPDI // above). diff --git a/llvm/test/Transforms/MergeFunc/merge-used.ll b/llvm/test/Transforms/MergeFunc/merge-used.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/MergeFunc/merge-used.ll @@ -0,0 +1,18 @@ +; RUN: opt -S -mergefunc < %s | FileCheck %s + +@llvm.compiler.used = appending global [1 x i8*] [i8* bitcast (i32 (i32)* @a to i8*)], section "llvm.metadata" + +; CHECK: @a +; CHECK: @b + +define internal i32 @a(i32 %a) unnamed_addr { + %b = xor i32 %a, 0 + %c = xor i32 %b, 0 + ret i32 %c +} + +define i32 @b(i32 %a) unnamed_addr { + %b = xor i32 %a, 0 + %c = xor i32 %b, 0 + ret i32 %c +}