llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-llvm-transforms 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
