llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-analysis

Author: Kuba (Brecka) Mracek (kubamracek)

<details>
<summary>Changes</summary>

This is being discussed in an RFC: 
https://discourse.llvm.org/t/rfc-optimizing-linkonce-odr-functions-frontend-hints-inlining-and-internal-cloning/92423

Some more context is in this PR: 
https://github.com/llvm/llvm-project/pull/228074

This adds, a (currently off-by-default) pass that can perform internalization 
of linkonce_odr functions. Three modes:
- off ... default
- likely-module-local ... only when the frontend (Clang) annotates the function 
as coming from a .cpp file and not a .h file
  - see https://github.com/llvm/llvm-project/pull/228074 and the RFC detailing 
this idea
- all ... for experimentation and for reference, it's useful to be able to 
assess how much full internalization shifts performance and codesize

Currently, this PR includes the commits from 
https://github.com/llvm/llvm-project/pull/228074. I don't know how to make a 
stacked PR 😄 

---

Patch is 21.99 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/229402.diff


12 Files Affected:

- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+19) 
- (added) clang/test/CodeGenCXX/frontend-hint-likely-module-local.cpp (+56) 
- (added) llvm/include/llvm/Transforms/IPO/InternalizeLinkOnceODR.h (+38) 
- (modified) llvm/lib/Analysis/InlineCost.cpp (+8-2) 
- (modified) llvm/lib/Passes/PassBuilder.cpp (+1) 
- (modified) llvm/lib/Passes/PassBuilderPipelines.cpp (+5) 
- (modified) llvm/lib/Passes/PassRegistry.def (+1) 
- (modified) llvm/lib/Transforms/IPO/CMakeLists.txt (+1) 
- (added) llvm/lib/Transforms/IPO/InternalizeLinkOnceODR.cpp (+121) 
- (added) llvm/test/Transforms/Inline/frontend-hint-likely-module-local.ll 
(+95) 
- (added) llvm/test/Transforms/InternalizeLinkOnceODR/basic.ll (+82) 
- (added) llvm/test/Transforms/InternalizeLinkOnceODR/pipeline.ll (+23) 


``````````diff
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index d19d64e2e1c60..011aae06effef 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -94,6 +94,12 @@ static llvm::cl::opt<bool> LimitedCoverage(
     "limited-coverage-experimental", llvm::cl::Hidden,
     llvm::cl::desc("Emit limited coverage mapping information 
(experimental)"));
 
+static llvm::cl::opt<bool> EmitModuleLocalHints(
+    "clang-emit-module-local-hints", llvm::cl::Hidden,
+    llvm::cl::init(false),
+    llvm::cl::desc("Mark inline and template functions defined in the main "
+                   "source file with \"frontend-hint-likely-module-local\""));
+
 static const char AnnotationSection[] = "llvm.metadata";
 static constexpr auto ErrnoTBAAMDName = "llvm.errno.tbaa";
 
@@ -3296,6 +3302,19 @@ void 
CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
   if (CodeGenOpts.DisableOutlining || D->hasAttr<NoOutlineAttr>())
     B.addAttribute(llvm::Attribute::NoOutline);
 
+  // An inline function or template instantiation defined in the main source
+  // file is (ignoring unusual setups like #include-ing a .cpp file) not
+  // referenced or emitted by any other translation unit.
+  if (EmitModuleLocalHints) {
+    const auto *FD = dyn_cast<FunctionDecl>(D);
+    const SourceManager &SM = getContext().getSourceManager();
+    // D->getLocation(), for a template instantiation, is the location of the
+    // template's definition, not instantiation.
+    bool InMainFile = SM.isInMainFile(D->getLocation());
+    if (FD && (FD->isInlined() || FD->isTemplateInstantiation()) && InMainFile)
+      B.addAttribute("frontend-hint-likely-module-local");
+  }
+
   F->addFnAttrs(B);
 
   llvm::MaybeAlign ExplicitAlignment;
diff --git a/clang/test/CodeGenCXX/frontend-hint-likely-module-local.cpp 
b/clang/test/CodeGenCXX/frontend-hint-likely-module-local.cpp
new file mode 100644
index 0000000000000..3bbea1ce840e2
--- /dev/null
+++ b/clang/test/CodeGenCXX/frontend-hint-likely-module-local.cpp
@@ -0,0 +1,56 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -std=c++20 -emit-llvm 
-mllvm -clang-emit-module-local-hints -o %t/on.ll %t/main.cpp
+// RUN: FileCheck %s < %t/on.ll
+// RUN: FileCheck %s --check-prefix=PLAIN < %t/on.ll
+// RUN: FileCheck %s --check-prefix=HDR < %t/on.ll
+// RUN: FileCheck %s --check-prefix=HDRTMPL < %t/on.ll
+// RUN: %clang_cc1 -triple arm64-apple-macosx14.0.0 -std=c++20 -emit-llvm -o - 
%t/main.cpp | FileCheck %s --check-prefix=OFF
+
+//--- hdr.h
+inline int hdr_inline(int x) { return x * 3; }
+template <class F> int hdr_call(F f) { return f(); }
+
+//--- main.cpp
+#include "hdr.h"
+
+struct S { int m(int x) { return x - 1; } };
+int main_plain(int x) { return x + 1; }
+inline int main_inline(int x) { return x * 7; }
+template <class T> int main_tmpl(T x) { return hdr_call([&] { return x * 2; 
}); }
+
+int use(int x) {
+  return main_plain(x) + main_inline(x) + main_tmpl(x) + hdr_inline(x) +
+         S().m(x);
+}
+
+// Inline functions, template instantiations and lambdas defined in main.cpp.
+// CHECK-DAG: define{{.*}} i32 @_Z11main_inlinei({{.*}}) #[[INL:[0-9]+]]
+// CHECK-DAG: define{{.*}} i32 @_Z9main_tmplIiEiT_({{.*}}) #[[TMPL:[0-9]+]]
+// CHECK-DAG: define{{.*}} i32 @_ZZ9main_tmplIiEiT_ENKUlvE_clEv({{.*}}) 
#[[LAMBDA:[0-9]+]]
+// CHECK-DAG: define{{.*}} i32 @_ZN1S1mEi({{.*}}) #[[MEMBER:[0-9]+]]
+// CHECK-DAG: attributes #[[INL]] = { 
{{.*}}"frontend-hint-likely-module-local"{{.*}} }
+// CHECK-DAG: attributes #[[TMPL]] = { 
{{.*}}"frontend-hint-likely-module-local"{{.*}} }
+// CHECK-DAG: attributes #[[LAMBDA]] = { 
{{.*}}"frontend-hint-likely-module-local"{{.*}} }
+// CHECK-DAG: attributes #[[MEMBER]] = { 
{{.*}}"frontend-hint-likely-module-local"{{.*}} }
+
+// Non-inline functions in main.cpp.
+// PLAIN-DAG: define{{.*}} i32 @_Z10main_plaini({{.*}}) #[[PLAIN:[0-9]+]]
+// PLAIN-DAG: define{{.*}} i32 @_Z3usei({{.*}}) #[[PLAIN]]
+// PLAIN: attributes #[[PLAIN]] = {
+// PLAIN-NOT: frontend-hint-likely-module-local
+// PLAIN-SAME: }
+
+// Inline function defined in hdr.h.
+// HDR: define{{.*}} i32 @_Z10hdr_inlinei({{.*}}) #[[HDR:[0-9]+]]
+// HDR: attributes #[[HDR]] = {
+// HDR-NOT: frontend-hint-likely-module-local
+// HDR-SAME: }
+
+// Template defined in hdr.h, instantiated (with a main.cpp lambda) in 
main.cpp.
+// HDRTMPL: define{{.*}} i32 @_Z8hdr_call{{.*}}) #[[HDRTMPL:[0-9]+]]
+// HDRTMPL: attributes #[[HDRTMPL]] = {
+// HDRTMPL-NOT: frontend-hint-likely-module-local
+// HDRTMPL-SAME: }
+
+// OFF-NOT: frontend-hint-likely-module-local
diff --git a/llvm/include/llvm/Transforms/IPO/InternalizeLinkOnceODR.h 
b/llvm/include/llvm/Transforms/IPO/InternalizeLinkOnceODR.h
new file mode 100644
index 0000000000000..e2df918b66f2c
--- /dev/null
+++ b/llvm/include/llvm/Transforms/IPO/InternalizeLinkOnceODR.h
@@ -0,0 +1,38 @@
+//===- InternalizeLinkOnceODR.h - Clone linkonce_odr functions 
------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_IPO_INTERNALIZELINKONCEODR_H
+#define LLVM_TRANSFORMS_IPO_INTERNALIZELINKONCEODR_H
+
+#include "llvm/IR/PassManager.h"
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+class Module;
+
+/// Clone and internalize linkonce_odr functions in the current TU (when 
asked):
+/// * -enable-linkonce-odr-internalization=off ... default, nothing
+/// * -enable-linkonce-odr-internalization=likely-module-local ... when marked
+///   with "frontend-hint-likely-module-local" function attribute
+/// * -enable-linkonce-odr-internalization=all ... on all linkonce_odr 
functions
+///
+/// Changing calls in the current TU to instead use a private copy of a
+/// linkonce_odr function is allowed, as long as nothing relies on the address
+/// of the function, so only direct calls are redirected, other uses keep
+/// referring to the original. When all uses of a function are direct calls, 
the
+/// internalization is done in-place.
+class InternalizeLinkOnceODRPass
+    : public OptionalPassInfoMixin<InternalizeLinkOnceODRPass> {
+public:
+  LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_IPO_INTERNALIZELINKONCEODR_H
diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp
index 0a540c1b8fb67..d9d9b9401be01 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -1247,10 +1247,16 @@ class InlineCostCallAnalyzer final : public 
CallAnalyzer {
 };
 
 // Return true if CB is the sole call to local function Callee.
+//
+// A linkonce_odr function marked "frontend-hint-likely-module-local" counts as
+// local too: no other module is expected to reference it, so we expect
+// inlining the sole call to turn the original dead.
 static bool isSoleCallToLocalFunction(const CallBase &CB,
                                       const Function &Callee) {
-  return Callee.hasLocalLinkage() && Callee.hasOneLiveUse() &&
-         &Callee == CB.getCalledFunction();
+  bool Local = Callee.hasLocalLinkage() ||
+               (Callee.hasLinkOnceODRLinkage() &&
+                Callee.hasFnAttribute("frontend-hint-likely-module-local"));
+  return Local && Callee.hasOneLiveUse() && &Callee == CB.getCalledFunction();
 }
 
 class InlineCostFeaturesAnalyzer final : public CallAnalyzer {
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 30a6f75a1b86d..602870246d0dc 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -258,6 +258,7 @@
 #include "llvm/Transforms/IPO/InferFunctionAttrs.h"
 #include "llvm/Transforms/IPO/Instrumentor.h"
 #include "llvm/Transforms/IPO/Internalize.h"
+#include "llvm/Transforms/IPO/InternalizeLinkOnceODR.h"
 #include "llvm/Transforms/IPO/LowerTypeTests.h"
 #include "llvm/Transforms/IPO/MemProfContextDisambiguation.h"
 #include "llvm/Transforms/IPO/MergeFunctions.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp 
b/llvm/lib/Passes/PassBuilderPipelines.cpp
index bd37fffc8e991..73c3d7540fe2c 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -64,6 +64,7 @@
 #include "llvm/Transforms/IPO/InferFunctionAttrs.h"
 #include "llvm/Transforms/IPO/Inliner.h"
 #include "llvm/Transforms/IPO/Instrumentor.h"
+#include "llvm/Transforms/IPO/InternalizeLinkOnceODR.h"
 #include "llvm/Transforms/IPO/LowerTypeTests.h"
 #include "llvm/Transforms/IPO/MemProfContextDisambiguation.h"
 #include "llvm/Transforms/IPO/MergeFunctions.h"
@@ -1222,6 +1223,10 @@ 
PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
 
   invokePipelineEarlySimplificationEPCallbacks(MPM, Level, Phase);
 
+  // Give linkonce_odr functions internal copies before the interprocedural
+  // passes that require local linkage (IPSCCP, GlobalOpt, DeadArgElim, etc.).
+  MPM.addPass(InternalizeLinkOnceODRPass());
+
   // Interprocedural constant propagation now that basic cleanup has occurred
   // and prior to optimizing globals.
   // FIXME: This position in the pipeline hasn't been carefully considered in
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index af4ce5029551d..8cd25201f76f8 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -107,6 +107,7 @@ MODULE_PASS("ctx-instr-lower", PGOCtxProfLoweringPass())
 MODULE_PASS("print<ctx-prof-analysis>", CtxProfAnalysisPrinterPass(errs()))
 MODULE_PASS("invalidate<all>", InvalidateAllAnalysesPass())
 MODULE_PASS("instrumentor", InstrumentorPass())
+MODULE_PASS("internalize-linkonce-odr", InternalizeLinkOnceODRPass())
 MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass())
 MODULE_PASS("lower-emutls", LowerEmuTLSPass())
 MODULE_PASS("lower-global-dtors", LowerGlobalDtorsPass())
diff --git a/llvm/lib/Transforms/IPO/CMakeLists.txt 
b/llvm/lib/Transforms/IPO/CMakeLists.txt
index fd791f9fe86f8..e67a25e755014 100644
--- a/llvm/lib/Transforms/IPO/CMakeLists.txt
+++ b/llvm/lib/Transforms/IPO/CMakeLists.txt
@@ -31,6 +31,7 @@ add_llvm_component_library(LLVMipo
   InstrumentorConfigFile.cpp
   InstrumentorStubPrinter.cpp
   Internalize.cpp
+  InternalizeLinkOnceODR.cpp
   LowerTypeTests.cpp
   MemProfContextDisambiguation.cpp
   MergeFunctions.cpp
diff --git a/llvm/lib/Transforms/IPO/InternalizeLinkOnceODR.cpp 
b/llvm/lib/Transforms/IPO/InternalizeLinkOnceODR.cpp
new file mode 100644
index 0000000000000..b176abbdeea9d
--- /dev/null
+++ b/llvm/lib/Transforms/IPO/InternalizeLinkOnceODR.cpp
@@ -0,0 +1,121 @@
+//===- InternalizeLinkOnceODR.cpp - Clone linkonce_odr functions 
----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/IPO/InternalizeLinkOnceODR.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/Comdat.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/ValueMapper.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "internalize-linkonce-odr"
+
+STATISTIC(NumInternalized, "Number of linkonce_odr functions made internal");
+STATISTIC(NumCloned, "Number of internal clones of linkonce_odr functions");
+STATISTIC(NumCallsRedirected, "Number of direct calls redirected to a clone");
+
+enum class LinkOnceODRInternalization { Off, LikelyModuleLocal, All };
+
+static cl::opt<LinkOnceODRInternalization> EnableLinkOnceODRInternalization(
+    "enable-linkonce-odr-internalization", cl::Hidden,
+    cl::init(LinkOnceODRInternalization::Off),
+    cl::desc("Clone internal copies of linkonce_odr functions"),
+    cl::values(clEnumValN(LinkOnceODRInternalization::Off, "off", "never"),
+               clEnumValN(LinkOnceODRInternalization::LikelyModuleLocal,
+                          "likely-module-local",
+                          "when TU-local hint is present"),
+               clEnumValN(LinkOnceODRInternalization::All, "all",
+                          "all linkonce_odr functions")));
+
+static bool isCandidate(const Function &F) {
+  if (F.isDeclaration() || !F.hasLinkOnceODRLinkage())
+    return false;
+  return EnableLinkOnceODRInternalization == LinkOnceODRInternalization::All ||
+         F.hasFnAttribute("frontend-hint-likely-module-local");
+}
+
+static bool isDirectCall(const Use &U) {
+  const auto *CB = dyn_cast<CallBase>(U.getUser());
+  return CB && CB->isCallee(&U);
+}
+
+// Whether a copy of F can be made. Duplicating it must not duplicate anything
+// that has to be unique, like labels of block addresses or of inline asm.
+static bool isCloneable(const Function &F) {
+  if (F.hasFnAttribute(Attribute::NoDuplicate))
+    return false;
+  for (const BasicBlock &BB : F)
+    if (BB.hasAddressTaken())
+      return false;
+  for (const Instruction &I : instructions(F))
+    if (const auto *CB = dyn_cast<CallBase>(&I))
+      if (CB->isInlineAsm() || CB->cannotDuplicate())
+        return false;
+  return true;
+}
+
+PreservedAnalyses InternalizeLinkOnceODRPass::run(Module &M,
+                                                  ModuleAnalysisManager &) {
+  if (EnableLinkOnceODRInternalization == LinkOnceODRInternalization::Off)
+    return PreservedAnalyses::all();
+
+  SmallVector<Function *, 16> Candidates;
+  for (Function &F : M)
+    if (isCandidate(F))
+      Candidates.push_back(&F);
+
+  bool Changed = false;
+  for (Function *F : Candidates) {
+    bool HasDirectCalls = false, HasOtherUses = false;
+    for (const Use &U : F->uses()) {
+      if (isDirectCall(U))
+        HasDirectCalls = true;
+      else
+        HasOtherUses = true;
+    }
+    if (!HasDirectCalls)
+      continue;
+
+    // (1) Make local in-place when allowed (only direct calls, alone in 
comdat)
+    const Comdat *C = F->getComdat();
+    if (!HasOtherUses && (!C || C->getUsers().size() == 1)) {
+      F->setLinkage(GlobalValue::InternalLinkage);
+      F->setComdat(nullptr);
+      ++NumInternalized;
+      Changed = true;
+      continue;
+    }
+
+    if (!isCloneable(*F))
+      continue;
+
+    // (2) Clone and redirect (direct calls only)
+    ValueToValueMapTy VMap;
+    Function *Clone = CloneFunction(F, VMap);
+    Clone->setName(F->getName() + ".internal");
+    Clone->setLinkage(GlobalValue::InternalLinkage);
+    Clone->setComdat(nullptr);
+    Clone->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
+    for (Use &U : make_early_inc_range(F->uses())) {
+      if (isDirectCall(U)) {
+        U.set(Clone);
+        ++NumCallsRedirected;
+      }
+    }
+    ++NumCloned;
+    Changed = true;
+  }
+
+  return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
+}
diff --git a/llvm/test/Transforms/Inline/frontend-hint-likely-module-local.ll 
b/llvm/test/Transforms/Inline/frontend-hint-likely-module-local.ll
new file mode 100644
index 0000000000000..de9aa101b2615
--- /dev/null
+++ b/llvm/test/Transforms/Inline/frontend-hint-likely-module-local.ll
@@ -0,0 +1,95 @@
+; RUN: opt -passes=inline -inline-threshold=0 -S < %s | FileCheck %s
+
+; A linkonce_odr function marked "frontend-hint-likely-module-local" gets the
+; last-call-to-static bonus for its sole call, like a local function would.
+
+; CHECK-LABEL: define i32 @caller(
+; CHECK-NOT: call i32 @hint_once(
+; CHECK-COUNT-2: call i32 @hint_twice(
+; CHECK: call i32 @nohint_once(
+; CHECK: call i32 @hint_external_once(
+; CHECK: ret i32
+define i32 @caller(i32 %x) {
+  %a = call i32 @hint_once(i32 %x)
+  %b1 = call i32 @hint_twice(i32 %a)
+  %b2 = call i32 @hint_twice(i32 %b1)
+  %c = call i32 @nohint_once(i32 %b2)
+  %d = call i32 @hint_external_once(i32 %c)
+  ret i32 %d
+}
+
+define linkonce_odr i32 @hint_once(i32 %x) "frontend-hint-likely-module-local" 
{
+  %v1 = mul i32 %x, 3
+  %v2 = mul i32 %v1, 5
+  %v3 = mul i32 %v2, 7
+  %v4 = mul i32 %v3, 11
+  %v5 = mul i32 %v4, 13
+  %v6 = mul i32 %v5, 17
+  %v7 = mul i32 %v6, 19
+  %v8 = mul i32 %v7, 23
+  %v9 = mul i32 %v8, 29
+  %v10 = mul i32 %v9, 31
+  %v11 = mul i32 %v10, 37
+  %v12 = mul i32 %v11, 41
+  %v13 = mul i32 %v12, 43
+  %v14 = mul i32 %v13, 47
+  %v15 = mul i32 %v14, 53
+  ret i32 %v15
+}
+
+define linkonce_odr i32 @hint_twice(i32 %x) 
"frontend-hint-likely-module-local" {
+  %v1 = mul i32 %x, 3
+  %v2 = mul i32 %v1, 5
+  %v3 = mul i32 %v2, 7
+  %v4 = mul i32 %v3, 11
+  %v5 = mul i32 %v4, 13
+  %v6 = mul i32 %v5, 17
+  %v7 = mul i32 %v6, 19
+  %v8 = mul i32 %v7, 23
+  %v9 = mul i32 %v8, 29
+  %v10 = mul i32 %v9, 31
+  %v11 = mul i32 %v10, 37
+  %v12 = mul i32 %v11, 41
+  %v13 = mul i32 %v12, 43
+  %v14 = mul i32 %v13, 47
+  %v15 = mul i32 %v14, 53
+  ret i32 %v15
+}
+
+define linkonce_odr i32 @nohint_once(i32 %x) {
+  %v1 = mul i32 %x, 3
+  %v2 = mul i32 %v1, 5
+  %v3 = mul i32 %v2, 7
+  %v4 = mul i32 %v3, 11
+  %v5 = mul i32 %v4, 13
+  %v6 = mul i32 %v5, 17
+  %v7 = mul i32 %v6, 19
+  %v8 = mul i32 %v7, 23
+  %v9 = mul i32 %v8, 29
+  %v10 = mul i32 %v9, 31
+  %v11 = mul i32 %v10, 37
+  %v12 = mul i32 %v11, 41
+  %v13 = mul i32 %v12, 43
+  %v14 = mul i32 %v13, 47
+  %v15 = mul i32 %v14, 53
+  ret i32 %v15
+}
+
+define i32 @hint_external_once(i32 %x) "frontend-hint-likely-module-local" {
+  %v1 = mul i32 %x, 3
+  %v2 = mul i32 %v1, 5
+  %v3 = mul i32 %v2, 7
+  %v4 = mul i32 %v3, 11
+  %v5 = mul i32 %v4, 13
+  %v6 = mul i32 %v5, 17
+  %v7 = mul i32 %v6, 19
+  %v8 = mul i32 %v7, 23
+  %v9 = mul i32 %v8, 29
+  %v10 = mul i32 %v9, 31
+  %v11 = mul i32 %v10, 37
+  %v12 = mul i32 %v11, 41
+  %v13 = mul i32 %v12, 43
+  %v14 = mul i32 %v13, 47
+  %v15 = mul i32 %v14, 53
+  ret i32 %v15
+}
diff --git a/llvm/test/Transforms/InternalizeLinkOnceODR/basic.ll 
b/llvm/test/Transforms/InternalizeLinkOnceODR/basic.ll
new file mode 100644
index 0000000000000..ef6599d3f8c86
--- /dev/null
+++ b/llvm/test/Transforms/InternalizeLinkOnceODR/basic.ll
@@ -0,0 +1,82 @@
+; RUN: opt -passes=internalize-linkonce-odr 
-enable-linkonce-odr-internalization=likely-module-local -S < %s | FileCheck %s 
--check-prefixes=CHECK,MAIN
+; RUN: opt -passes=internalize-linkonce-odr 
-enable-linkonce-odr-internalization=all -S < %s | FileCheck %s 
--check-prefixes=CHECK,ALL
+
+$only_calls = comdat any
+$shared = comdat any
+
+; Only direct calls: made internal in place, removed from its comdat.
+; CHECK: define internal i32 @only_calls(i32 %x) #0 {
+define linkonce_odr hidden i32 @only_calls(i32 %x) #0 comdat {
+  %r = add i32 %x, 1
+  ret i32 %r
+}
+
+; Address taken: the original stays for that use, all direct calls (including
+; the recursive ones in the original and in the clone) go to an internal clone.
+; CHECK: define linkonce_odr i32 @address_taken(i32 %x) #0 {
+; CHECK-NEXT: call i32 @address_taken.internal(
+define linkonce_odr i32 @address_taken(i32 %x) #0 {
+  %r = call i32 @address_taken(i32 %x)
+  ret i32 %r
+}
+
+; Shares its comdat with a global: cannot leave the comdat, so it is cloned.
+; CHECK: define linkonce_odr i32 @in_shared_comdat(i32 %x) #0 comdat($shared) {
+@shared_var = linkonce_odr global i32 0, comdat($shared)
+define linkonce_odr i32 @in_shared_comdat(i32 %x) #0 comdat($shared) {
+  ret i32 %x
+}
+
+; Not marked likely module-local: only with =all.
+; MAIN: define linkonce_odr i32 @from_header(i32 %x) {
+; ALL: define internal i32 @from_header(i32 %x) {
+define linkonce_odr i32 @from_header(i32 %x) {
+  ret i32 %x
+}
+
+; Not linkonce_odr: untouched.
+; CHECK: define weak_odr i32 @explicit_instantiation(i32 %x) #0 {
+define weak_odr i32 @explicit_instantiation(i32 %x) #0 {
+  ret i32 %x
+}
+
+; Address taken and not cloneable (inline asm may define labels): untouched.
+; CHECK: define linkonce_odr void @has_asm() #0 {
+; CHECK-NOT: @has_asm.internal
+define linkonce_odr void @has_asm() #0 {
+  call void asm sideeffect "nop", ""()
+  ret void
+}
+
+; No calls: nothing to do.
+; CHECK: define linkonce_odr void @never_called() #0 {
+define linkonce_odr void @never_called() #0 {
+  ret void
+}
+
+...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/229402
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to