Index: clang/lib/CodeGen/BackendUtil.cpp =================================================================== --- clang/lib/CodeGen/BackendUtil.cpp +++ clang/lib/CodeGen/BackendUtil.cpp @@ -81,6 +81,7 @@ #include "llvm/Transforms/Scalar/GVN.h" #include "llvm/Transforms/Scalar/LowerMatrixIntrinsics.h" #include "llvm/Transforms/Utils.h" +#include "llvm/Transforms/Utils/CGSectionFuncComdatCreator.h" #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/Debugify.h" #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" @@ -1415,6 +1416,10 @@ MPM.addPass(createModuleToFunctionPassAdaptor(MemProfilerPass())); MPM.addPass(ModuleMemProfilerPass()); } + + if (CodeGenOpts.CallGraphSection) { + MPM.addPass(CGSectionFuncComdatCreatorPass()); + } } // FIXME: We still use the legacy pass manager to do code generation. We Index: llvm/include/llvm/Transforms/Utils/CGSectionFuncComdatCreator.h =================================================================== --- /dev/null +++ llvm/include/llvm/Transforms/Utils/CGSectionFuncComdatCreator.h @@ -0,0 +1,32 @@ +//===-- CGSectionFuncComdatCreator.h - CG func comdat creator ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass creates comdats for functions whose symbols will be referenced +// from the call graph section. These comdats are used to create the call graph +// sections, so that, the sections can get discarded by the linker if the +// functions get removed. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_UTILS_CGSECTIONFUNCCOMDATCREATOR_H +#define LLVM_TRANSFORMS_UTILS_CGSECTIONFUNCCOMDATCREATOR_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class CGSectionFuncComdatCreatorPass + : public PassInfoMixin { +public: + PreservedAnalyses run(Module &, ModuleAnalysisManager &); +}; + +} // namespace llvm + +#endif // LLVM_TRANSFORMS_UTILS_CGSECTIONFUNCCOMDATCREATOR_H + Index: llvm/lib/Passes/PassBuilder.cpp =================================================================== --- llvm/lib/Passes/PassBuilder.cpp +++ llvm/lib/Passes/PassBuilder.cpp @@ -211,6 +211,7 @@ #include "llvm/Transforms/Utils/AddDiscriminators.h" #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/BreakCriticalEdges.h" +#include "llvm/Transforms/Utils/CGSectionFuncComdatCreator.h" #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h" #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" Index: llvm/lib/Passes/PassRegistry.def =================================================================== --- llvm/lib/Passes/PassRegistry.def +++ llvm/lib/Passes/PassRegistry.def @@ -45,6 +45,7 @@ MODULE_PASS("attributor", AttributorPass()) MODULE_PASS("annotation2metadata", Annotation2MetadataPass()) MODULE_PASS("openmp-opt", OpenMPOptPass()) +MODULE_PASS("cg-section-func-comdat-creator", CGSectionFuncComdatCreatorPass()) MODULE_PASS("called-value-propagation", CalledValuePropagationPass()) MODULE_PASS("canonicalize-aliases", CanonicalizeAliasesPass()) MODULE_PASS("cg-profile", CGProfilePass()) Index: llvm/lib/Transforms/Utils/CGSectionFuncComdatCreator.cpp =================================================================== --- /dev/null +++ llvm/lib/Transforms/Utils/CGSectionFuncComdatCreator.cpp @@ -0,0 +1,98 @@ +//===-- CGSectionFuncComdatCreator.cpp - CG section func comdat creator ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass creates comdats for functions whose symbols will be referenced +// from the call graph section. These comdats are used to create the call graph +// sections, so that, the sections can get discarded by the linker if the +// functions get removed. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/CGSectionFuncComdatCreator.h" + +#include "llvm/ADT/Triple.h" +#include "llvm/IR/Instructions.h" + +namespace llvm { + +namespace { +class ModuleCGSectionFuncComdatCreator { +public: + bool instrumentModule(Module &); + +private: + bool createFunctionComdat(Function &F, const Triple &T) const; + bool hasIndirectCalls(const Function &F) const; + bool isTargetToIndirectCalls(const Function &F) const; +}; + +bool ModuleCGSectionFuncComdatCreator::createFunctionComdat( + Function &F, const Triple &T) const { + if (auto *Comdat = F.getComdat()) + return false; + assert(F.hasName()); + Module *M = F.getParent(); + + // Make a new comdat for the function. Use the "no duplicates" selection kind + // if the object file format supports it. For COFF we restrict it to non-weak + // symbols. + Comdat *C = M->getOrInsertComdat(F.getName()); + if (T.isOSBinFormatELF() || (T.isOSBinFormatCOFF() && !F.isWeakForLinker())) + C->setSelectionKind(Comdat::NoDuplicates); + F.setComdat(C); + return true; +} + +bool ModuleCGSectionFuncComdatCreator::hasIndirectCalls( + const Function &F) const { + for (const auto &I : F) + if (const CallInst *CI = dyn_cast(&I)) + if (CI->isIndirectCall()) + return true; + return false; +} + +bool ModuleCGSectionFuncComdatCreator::isTargetToIndirectCalls( + const Function &F) const { + return !F.hasLocalLinkage() || + F.hasAddressTaken(nullptr, + /* IgnoreCallbackUses */ true, + /* IgnoreAssumeLikeCalls */ true, + /* IgnoreLLVMUsed */ false); +} + +bool ModuleCGSectionFuncComdatCreator::instrumentModule(Module &M) { + Triple TargetTriple = Triple(M.getTargetTriple()); + + bool CreatedComdats = false; + + for (Function &F : M) { + if (isTargetToIndirectCalls(F) || hasIndirectCalls(F)) { + if (TargetTriple.supportsCOMDAT() && !F.isInterposable() && + !F.isDeclarationForLinker()) { + CreatedComdats |= createFunctionComdat(F, TargetTriple); + } + } + } + + return CreatedComdats; +} + +} // namespace + +PreservedAnalyses +CGSectionFuncComdatCreatorPass::run(Module &M, ModuleAnalysisManager &AM) { + ModuleCGSectionFuncComdatCreator ModuleCG; + if (ModuleCG.instrumentModule(M)) { + return PreservedAnalyses::none(); + } + return PreservedAnalyses::all(); +} + +} // namespace llvm + Index: llvm/lib/Transforms/Utils/CMakeLists.txt =================================================================== --- llvm/lib/Transforms/Utils/CMakeLists.txt +++ llvm/lib/Transforms/Utils/CMakeLists.txt @@ -7,6 +7,7 @@ BreakCriticalEdges.cpp BuildLibCalls.cpp BypassSlowDivision.cpp + CGSectionFuncComdatCreator.cpp CallPromotionUtils.cpp CallGraphUpdater.cpp CanonicalizeAliases.cpp Index: llvm/test/Transforms/Util/create-function-comdats.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Util/create-function-comdats.ll @@ -0,0 +1,21 @@ +; Tests that we create function comdats properly and only for those with no +; comdats. + +; RUN: opt -passes='cg-section-func-comdat-creator' -S %s | FileCheck %s + +; CHECK: $f = comdat noduplicates +; CHECK: $g = comdat any +$g = comdat any + +; CHECK: define dso_local void @f() comdat { +define dso_local void @f() { +entry: + ret void +} + +; CHECK: define dso_local void @g() comdat { +define dso_local void @g() comdat { +entry: + ret void +} +