diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp index 2c35dd6c125bb6..bb85dde46ae2b6 100644 --- 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,9 @@ class MergeFunctions { /// analyzed again. std::vector Deferred; + /// Set of values marked as used in llvm.used and llvm.compiler.used. + SmallPtrSet Used; + #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 +411,11 @@ static bool isEligibleForMerging(Function &F) { bool MergeFunctions::runOnModule(Module &M) { bool Changed = false; + SmallVector UsedV; + collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/false); + collectUsedGlobalVariables(M, UsedV, /*CompilerUsed=*/true); + Used.insert(UsedV.begin(), UsedV.end()); + // All functions in the module, ordered by hash. Functions with a unique // hash value are easily eliminated. std::vector> @@ -453,6 +462,7 @@ bool MergeFunctions::runOnModule(Module &M) { FnTree.clear(); FNodesInTree.clear(); GlobalNumbers.clear(); + Used.clear(); return Changed; } @@ -825,7 +835,10 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) { // For better debugability, under MergeFunctionsPDI, we do not modify G's // call sites to point to F even when within the same translation unit. if (!G->isInterposable() && !MergeFunctionsPDI) { - if (G->hasGlobalUnnamedAddr()) { + // 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. + if (G->hasGlobalUnnamedAddr() && !Used.contains(G)) { // G might have been a key in our GlobalNumberState, and it's illegal // to replace a key in ValueMap with a non-global. GlobalNumbers.erase(G); diff --git a/llvm/test/Transforms/MergeFunc/merge-used.ll b/llvm/test/Transforms/MergeFunc/merge-used.ll new file mode 100644 index 00000000000000..a86e66eee3f90f --- /dev/null +++ b/llvm/test/Transforms/MergeFunc/merge-used.ll @@ -0,0 +1,35 @@ +; 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" + +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 +} + +define i32 @c(i32 %a) unnamed_addr { + %b = tail call i32 @a(i32 %a) + ret i32 %b +} + +; CHECK-LABEL: @llvm.compiler.used = appending global [1 x i8*] [i8* bitcast (i32 (i32)* @a to i8*)], section "llvm.metadata" + +; CHECK-LABEL: define i32 @b(i32 %a) unnamed_addr +; CHECK-NEXT: xor +; CHECK-NEXT: xor +; CHECK-NEXT: ret + +; CHECK-LABEL: define i32 @c(i32 %a) unnamed_addr +; CHECK-NEXT: tail call i32 @b(i32 %a) +; CHECK-NEXT: ret + +; CHECK-LABEL: define internal i32 @a(i32 %0) unnamed_addr +; CHECK-NEXT: tail call i32 @b(i32 %0) +; CHECK-NEXT: ret