[cfe-dev] [LLVMdev] Removing AvailableExternal values in GlobalDCE (was Re: RFC: ThinLTO Impementation Plan)

Teresa Johnson tejohnson at google.com
Tue Jun 9 07:07:24 PDT 2015


cc: +cfe-dev for the Clang patch

Responses below, new patches attached (clang bits unchanged from last time).

On Mon, Jun 8, 2015 at 2:31 PM, Duncan P. N. Exon Smith
<dexonsmith at apple.com> wrote:
> bcc:llvmdev, +llvm-commits
>
>> On 2015 Jun 8, at 14:13, Teresa Johnson <tejohnson at google.com> wrote:
>>
>> New patches attached. Passes llvm/clang regression tests. PTAL
>> Thanks!
>> Teresa
>>
>> On Mon, Jun 8, 2015 at 1:39 PM, Duncan P. N. Exon Smith
>> <dexonsmith at apple.com> wrote:
>>>
>>>> On 2015 Jun 8, at 13:35, Eric Christopher <echristo at gmail.com> wrote:
>>>>
>>>>
>>>>
>>>> On Mon, Jun 8, 2015 at 1:33 PM Reid Kleckner <rnk at google.com> wrote:
>>>> On Mon, Jun 8, 2015 at 1:08 PM, Eric Christopher <echristo at gmail.com> wrote:
>>>> I'd rather not have it be an llvm option at all. Just construct a different set of passes...
>>>>
>>>> This would also solve the problem of needing multiple sets of options to be passed to the builder. It'd be a bit of a change (i.e. having clang do the pass setup), but I think it'd be worth it to have clang be able to explicitly say which passes it wants.
>>>>
>>>> It's also a major change and would probably need to be discussed more widely so for now I guess this is sorta ok.
>>>>
>>>> I think for now the bool on PassManagerBuilder is a good way to make progress. Eventually, we may want to customize the LTO compilation phase optimization more fully (use lower inlining threshold, defer vectorization to link time), but we can cross that bridge when we get there.
>>>>
>>>> Precisely. In case it wasn't clear I was saying this.
>>>
>>> This SGTM too.
>>
>>
>>
>> --
>> Teresa Johnson | Software Engineer | tejohnson at google.com | 408-460-2413
>> <patch.ElimAE_clang2.txt><patch.ElimAE_llvm2.txt>
>
> LLVM patch LGTM, with a few bikesheds below.
>
>> Index: include/llvm/InitializePasses.h
>> ===================================================================
>> --- include/llvm/InitializePasses.h   (revision 237590)
>> +++ include/llvm/InitializePasses.h   (working copy)
>> @@ -130,6 +130,7 @@ void initializeSanitizerCoverageModulePass(PassReg
>>  void initializeDataFlowSanitizerPass(PassRegistry&);
>>  void initializeScalarizerPass(PassRegistry&);
>>  void initializeEarlyCSELegacyPassPass(PassRegistry &);
>> +void initializeElimAvailExternPass(PassRegistry&);
>>  void initializeExpandISelPseudosPass(PassRegistry&);
>>  void initializeFunctionAttrsPass(PassRegistry&);
>>  void initializeGCMachineCodeAnalysisPass(PassRegistry&);
>> Index: include/llvm/Transforms/IPO.h
>> ===================================================================
>> --- include/llvm/Transforms/IPO.h     (revision 237590)
>> +++ include/llvm/Transforms/IPO.h     (working copy)
>> @@ -71,6 +71,12 @@ ModulePass *createGlobalOptimizerPass();
>>  ModulePass *createGlobalDCEPass();
>>
>>  //===----------------------------------------------------------------------===//
>> +/// createElimAvailExternPass - This transform is designed to eliminate
>
> This is old-style commenting.  Please skip the name of the function in
> the doxygen comment.

Fixed.

>
>> +/// available external globals (functions or global variables)
>> +///
>> +ModulePass *createElimAvailExternPass();
>
> This name isn't clear to me.  "elimavailextern" is fine as command-line
> shorthand, but I think the function should be more explicit, something
> like `createEliminateAvailableExternallyPass()`.

Went with the longer name.

>
>> +
>> +//===----------------------------------------------------------------------===//
>>  /// createGVExtractionPass - If deleteFn is true, this pass deletes
>>  /// the specified global values. Otherwise, it deletes as much of the module as
>>  /// possible, except for the global values specified.
>> Index: include/llvm/Transforms/IPO/PassManagerBuilder.h
>> ===================================================================
>> --- include/llvm/Transforms/IPO/PassManagerBuilder.h  (revision 237590)
>> +++ include/llvm/Transforms/IPO/PassManagerBuilder.h  (working copy)
>> @@ -121,6 +121,7 @@ class PassManagerBuilder {
>>    bool VerifyInput;
>>    bool VerifyOutput;
>>    bool MergeFunctions;
>> +  bool PrepareForLTO;
>>
>>  private:
>>    /// ExtensionList - This is list of all of the extensions that are registered.
>> Index: lib/Transforms/IPO/CMakeLists.txt
>> ===================================================================
>> --- lib/Transforms/IPO/CMakeLists.txt (revision 237590)
>> +++ lib/Transforms/IPO/CMakeLists.txt (working copy)
>> @@ -3,6 +3,7 @@ add_llvm_library(LLVMipo
>>    BarrierNoopPass.cpp
>>    ConstantMerge.cpp
>>    DeadArgumentElimination.cpp
>> +  ElimAvailExtern.cpp
>>    ExtractGV.cpp
>>    FunctionAttrs.cpp
>>    GlobalDCE.cpp
>> Index: lib/Transforms/IPO/ElimAvailExtern.cpp
>> ===================================================================
>> --- lib/Transforms/IPO/ElimAvailExtern.cpp    (revision 0)
>> +++ lib/Transforms/IPO/ElimAvailExtern.cpp    (working copy)
>> @@ -0,0 +1,93 @@
>> +//===-- ElimAvailExtern.cpp - DCE unreachable internal functions ----------------===//
>> +//
>> +//                     The LLVM Compiler Infrastructure
>> +//
>> +// This file is distributed under the University of Illinois Open Source
>> +// License. See LICENSE.TXT for details.
>> +//
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// This transform is designed to eliminate available external global
>> +// definitions from the program, turning them into declarations.
>> +//
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include "llvm/Transforms/IPO.h"
>> +#include "llvm/ADT/Statistic.h"
>> +#include "llvm/IR/Constants.h"
>> +#include "llvm/IR/Instructions.h"
>> +#include "llvm/IR/Module.h"
>> +#include "llvm/Transforms/Utils/CtorUtils.h"
>> +#include "llvm/Transforms/Utils/GlobalStatus.h"
>> +#include "llvm/Pass.h"
>> +using namespace llvm;
>> +
>> +#define DEBUG_TYPE "elimavailextern"
>
> Would this be better with hyphens ("elim-avail-extern")?  I don't really
> have an opinion of which is better, just asking.

Yes, I like that better, changed.

>
>> +
>> +STATISTIC(NumAliases  , "Number of global aliases removed");
>> +STATISTIC(NumFunctions, "Number of functions removed");
>> +STATISTIC(NumVariables, "Number of global variables removed");
>> +
>> +namespace {
>> +  struct ElimAvailExtern : public ModulePass {
>
> I think the class name (like the `create*()` function) should be spelled
> out more explicilty: `EliminateAvailableExternally`.

Done.

>
>> +    static char ID; // Pass identification, replacement for typeid
>> +    ElimAvailExtern() : ModulePass(ID) {
>> +      initializeElimAvailExternPass(*PassRegistry::getPassRegistry());
>> +    }
>> +
>> +    // run - Do the ElimAvailExtern pass on the specified module, optionally
>> +    // updating the specified callgraph to reflect the changes.
>> +    //
>> +    bool runOnModule(Module &M) override;
>> +  };
>> +}
>> +
>> +char ElimAvailExtern::ID = 0;
>> +INITIALIZE_PASS(ElimAvailExtern, "elimavailextern",
>
> If add hyphens to the `DEBUG_TYPE`, add hyphens here too.

Done.

>
>> +                "Eliminate Available External Globals", false, false)
>
> s/External/Externally/

Done.

>
>> +
>> +ModulePass *llvm::createElimAvailExternPass() { return new ElimAvailExtern(); }
>> +
>> +bool ElimAvailExtern::runOnModule(Module &M) {
>> +  bool Changed = false;
>> +
>> +  // Drop initializers of available externally global variables.
>> +  for (Module::global_iterator I = M.global_begin(), E = M.global_end();
>> +       I != E; ++I) {
>> +    if (!I->hasAvailableExternallyLinkage())
>> +      continue;
>> +    if (I->hasInitializer()) {
>> +      Constant *Init = I->getInitializer();
>> +      I->setInitializer(nullptr);
>> +      if (isSafeToDestroyConstant(Init))
>> +        Init->destroyConstant();
>> +    }
>> +    I->removeDeadConstantUsers();
>> +    I->setLinkage(GlobalValue::ExternalLinkage);
>> +    NumVariables++;
>> +  }
>> +
>> +  // Drop the bodies of available externally functions.
>> +  for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
>> +    if (!I->hasAvailableExternallyLinkage())
>> +      continue;
>> +    if (!I->isDeclaration())
>> +      // This will set the linkage to external
>> +      I->deleteBody();
>> +    I->removeDeadConstantUsers();
>> +    NumFunctions++;
>> +  }
>> +
>> +  // Drop targets of available externally aliases.
>> +  for (Module::alias_iterator I = M.alias_begin(), E = M.alias_end(); I != E;
>> +       ++I) {
>> +    if (!I->hasAvailableExternallyLinkage())
>> +      continue;
>> +    I->setAliasee(nullptr);
>> +    I->removeDeadConstantUsers();
>> +    I->setLinkage(GlobalValue::ExternalLinkage);
>> +    NumAliases++;
>> +  }
>> +
>> +  return Changed;
>> +}
>> Index: lib/Transforms/IPO/PassManagerBuilder.cpp
>> ===================================================================
>> --- lib/Transforms/IPO/PassManagerBuilder.cpp (revision 237590)
>> +++ lib/Transforms/IPO/PassManagerBuilder.cpp (working copy)
>> @@ -106,6 +106,7 @@ PassManagerBuilder::PassManagerBuilder() {
>>      VerifyInput = false;
>>      VerifyOutput = false;
>>      MergeFunctions = false;
>> +    PrepareForLTO = false;
>>  }
>>
>>  PassManagerBuilder::~PassManagerBuilder() {
>> @@ -407,6 +408,17 @@ void PassManagerBuilder::populateModulePassManager
>>      // GlobalOpt already deletes dead functions and globals, at -O2 try a
>>      // late pass of GlobalDCE.  It is capable of deleting dead cycles.
>>      if (OptLevel > 1) {
>> +      if (!PrepareForLTO) {
>> +        // Remove avail extern fns and globals definitions if we aren't
>> +        // compiling an object file for later LTO. For LTO we want to preserve
>> +        // these so they are eligible for inlining at link-time. Note if they
>> +        // are unreferenced they will be removed by GlobalDCE below, so
>> +        // this only impacts referenced available externally globals.
>> +        // Eventually they will be suppressed during codegen, but eliminating
>> +        // here enables more opportunity for GlobalDCE as it may make
>> +        // globals referenced by available external functions dead.
>> +        MPM.add(createElimAvailExternPass());
>> +      }
>>        MPM.add(createGlobalDCEPass());         // Remove dead fns and globals.
>>        MPM.add(createConstantMergePass());     // Merge dup global constants
>>      }
>>
>
>



-- 
Teresa Johnson | Software Engineer | tejohnson at google.com | 408-460-2413
-------------- next part --------------
Index: include/llvm/InitializePasses.h
===================================================================
--- include/llvm/InitializePasses.h	(revision 237590)
+++ include/llvm/InitializePasses.h	(working copy)
@@ -130,6 +130,7 @@ void initializeSanitizerCoverageModulePass(PassReg
 void initializeDataFlowSanitizerPass(PassRegistry&);
 void initializeScalarizerPass(PassRegistry&);
 void initializeEarlyCSELegacyPassPass(PassRegistry &);
+void initializeEliminateAvailableExternallyPass(PassRegistry&);
 void initializeExpandISelPseudosPass(PassRegistry&);
 void initializeFunctionAttrsPass(PassRegistry&);
 void initializeGCMachineCodeAnalysisPass(PassRegistry&);
Index: include/llvm/Transforms/IPO.h
===================================================================
--- include/llvm/Transforms/IPO.h	(revision 237590)
+++ include/llvm/Transforms/IPO.h	(working copy)
@@ -71,6 +71,12 @@ ModulePass *createGlobalOptimizerPass();
 ModulePass *createGlobalDCEPass();
 
 //===----------------------------------------------------------------------===//
+/// This transform is designed to eliminate available external globals
+/// (functions or global variables)
+///
+ModulePass *createEliminateAvailableExternallyPass();
+
+//===----------------------------------------------------------------------===//
 /// createGVExtractionPass - If deleteFn is true, this pass deletes
 /// the specified global values. Otherwise, it deletes as much of the module as
 /// possible, except for the global values specified.
Index: include/llvm/Transforms/IPO/PassManagerBuilder.h
===================================================================
--- include/llvm/Transforms/IPO/PassManagerBuilder.h	(revision 237590)
+++ include/llvm/Transforms/IPO/PassManagerBuilder.h	(working copy)
@@ -121,6 +121,7 @@ class PassManagerBuilder {
   bool VerifyInput;
   bool VerifyOutput;
   bool MergeFunctions;
+  bool PrepareForLTO;
 
 private:
   /// ExtensionList - This is list of all of the extensions that are registered.
Index: lib/Transforms/IPO/CMakeLists.txt
===================================================================
--- lib/Transforms/IPO/CMakeLists.txt	(revision 237590)
+++ lib/Transforms/IPO/CMakeLists.txt	(working copy)
@@ -3,6 +3,7 @@ add_llvm_library(LLVMipo
   BarrierNoopPass.cpp
   ConstantMerge.cpp
   DeadArgumentElimination.cpp
+  ElimAvailExtern.cpp
   ExtractGV.cpp
   FunctionAttrs.cpp
   GlobalDCE.cpp
Index: lib/Transforms/IPO/ElimAvailExtern.cpp
===================================================================
--- lib/Transforms/IPO/ElimAvailExtern.cpp	(revision 0)
+++ lib/Transforms/IPO/ElimAvailExtern.cpp	(working copy)
@@ -0,0 +1,96 @@
+//===-- ElimAvailExtern.cpp - DCE unreachable internal functions ----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This transform is designed to eliminate available external global
+// definitions from the program, turning them into declarations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/IPO.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Transforms/Utils/CtorUtils.h"
+#include "llvm/Transforms/Utils/GlobalStatus.h"
+#include "llvm/Pass.h"
+using namespace llvm;
+
+#define DEBUG_TYPE "elim-avail-extern"
+
+STATISTIC(NumAliases  , "Number of global aliases removed");
+STATISTIC(NumFunctions, "Number of functions removed");
+STATISTIC(NumVariables, "Number of global variables removed");
+
+namespace {
+  struct EliminateAvailableExternally : public ModulePass {
+    static char ID; // Pass identification, replacement for typeid
+    EliminateAvailableExternally() : ModulePass(ID) {
+      initializeEliminateAvailableExternallyPass(
+          *PassRegistry::getPassRegistry());
+    }
+
+    // run - Do the EliminateAvailableExternally pass on the specified module,
+    // optionally updating the specified callgraph to reflect the changes.
+    //
+    bool runOnModule(Module &M) override;
+  };
+}
+
+char EliminateAvailableExternally::ID = 0;
+INITIALIZE_PASS(EliminateAvailableExternally, "elim-avail-extern",
+                "Eliminate Available Externally Globals", false, false)
+
+ModulePass *llvm::createEliminateAvailableExternallyPass() {
+  return new EliminateAvailableExternally();
+}
+
+bool EliminateAvailableExternally::runOnModule(Module &M) {
+  bool Changed = false;
+
+  // Drop initializers of available externally global variables.
+  for (Module::global_iterator I = M.global_begin(), E = M.global_end();
+       I != E; ++I) {
+    if (!I->hasAvailableExternallyLinkage())
+      continue;
+    if (I->hasInitializer()) {
+      Constant *Init = I->getInitializer();
+      I->setInitializer(nullptr);
+      if (isSafeToDestroyConstant(Init))
+        Init->destroyConstant();
+    }
+    I->removeDeadConstantUsers();
+    I->setLinkage(GlobalValue::ExternalLinkage);
+    NumVariables++;
+  }
+
+  // Drop the bodies of available externally functions.
+  for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) {
+    if (!I->hasAvailableExternallyLinkage())
+      continue;
+    if (!I->isDeclaration())
+      // This will set the linkage to external
+      I->deleteBody();
+    I->removeDeadConstantUsers();
+    NumFunctions++;
+  }
+
+  // Drop targets of available externally aliases.
+  for (Module::alias_iterator I = M.alias_begin(), E = M.alias_end(); I != E;
+       ++I) {
+    if (!I->hasAvailableExternallyLinkage())
+      continue;
+    I->setAliasee(nullptr);
+    I->removeDeadConstantUsers();
+    I->setLinkage(GlobalValue::ExternalLinkage);
+    NumAliases++;
+  }
+
+  return Changed;
+}
Index: lib/Transforms/IPO/PassManagerBuilder.cpp
===================================================================
--- lib/Transforms/IPO/PassManagerBuilder.cpp	(revision 237590)
+++ lib/Transforms/IPO/PassManagerBuilder.cpp	(working copy)
@@ -106,6 +106,7 @@ PassManagerBuilder::PassManagerBuilder() {
     VerifyInput = false;
     VerifyOutput = false;
     MergeFunctions = false;
+    PrepareForLTO = false;
 }
 
 PassManagerBuilder::~PassManagerBuilder() {
@@ -407,6 +408,17 @@ void PassManagerBuilder::populateModulePassManager
     // GlobalOpt already deletes dead functions and globals, at -O2 try a
     // late pass of GlobalDCE.  It is capable of deleting dead cycles.
     if (OptLevel > 1) {
+      if (!PrepareForLTO) {
+        // Remove avail extern fns and globals definitions if we aren't
+        // compiling an object file for later LTO. For LTO we want to preserve
+        // these so they are eligible for inlining at link-time. Note if they
+        // are unreferenced they will be removed by GlobalDCE below, so
+        // this only impacts referenced available externally globals.
+        // Eventually they will be suppressed during codegen, but eliminating
+        // here enables more opportunity for GlobalDCE as it may make
+        // globals referenced by available external functions dead.
+        MPM.add(createEliminateAvailableExternallyPass());
+      }
       MPM.add(createGlobalDCEPass());         // Remove dead fns and globals.
       MPM.add(createConstantMergePass());     // Merge dup global constants
     }
-------------- next part --------------
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td	(revision 237590)
+++ include/clang/Driver/Options.td	(working copy)
@@ -636,7 +636,7 @@ def flat__namespace : Flag<["-"], "flat_namespace"
 def flax_vector_conversions : Flag<["-"], "flax-vector-conversions">, Group<f_Group>;
 def flimited_precision_EQ : Joined<["-"], "flimited-precision=">, Group<f_Group>;
 def flto_EQ : Joined<["-"], "flto=">, Group<clang_ignored_gcc_optimization_f_Group>;
-def flto : Flag<["-"], "flto">, Group<f_Group>;
+def flto : Flag<["-"], "flto">, Flags<[CC1Option]>, Group<f_Group>;
 def fno_lto : Flag<["-"], "fno-lto">, Group<f_Group>;
 def fmacro_backtrace_limit_EQ : Joined<["-"], "fmacro-backtrace-limit=">,
                                 Group<f_Group>, Flags<[DriverOption, CoreOption]>;
Index: include/clang/Frontend/CodeGenOptions.def
===================================================================
--- include/clang/Frontend/CodeGenOptions.def	(revision 237590)
+++ include/clang/Frontend/CodeGenOptions.def	(working copy)
@@ -67,6 +67,8 @@ CODEGENOPT(InstrumentFunctions , 1, 0) ///< Set wh
 CODEGENOPT(InstrumentForProfiling , 1, 0) ///< Set when -pg is enabled.
 CODEGENOPT(LessPreciseFPMAD  , 1, 0) ///< Enable less precise MAD instructions to
                                      ///< be generated.
+CODEGENOPT(PrepareForLTO     , 1, 0) ///< Set when -flto is enabled on the
+                                     ///< compile step.
 CODEGENOPT(MergeAllConstants , 1, 1) ///< Merge identical constants.
 CODEGENOPT(MergeFunctions    , 1, 0) ///< Set when -fmerge-functions is enabled.
 CODEGENOPT(MSVolatile        , 1, 0) ///< Set when /volatile:ms is enabled.
Index: lib/CodeGen/BackendUtil.cpp
===================================================================
--- lib/CodeGen/BackendUtil.cpp	(revision 237590)
+++ lib/CodeGen/BackendUtil.cpp	(working copy)
@@ -287,6 +287,7 @@ void EmitAssemblyHelper::CreatePasses() {
   PMBuilder.DisableUnitAtATime = !CodeGenOpts.UnitAtATime;
   PMBuilder.DisableUnrollLoops = !CodeGenOpts.UnrollLoops;
   PMBuilder.MergeFunctions = CodeGenOpts.MergeFunctions;
+  PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO;
   PMBuilder.RerollLoops = CodeGenOpts.RerollLoops;
 
   PMBuilder.addExtension(PassManagerBuilder::EP_EarlyAsPossible,
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp	(revision 237590)
+++ lib/Driver/Tools.cpp	(working copy)
@@ -2676,6 +2676,10 @@ void Clang::ConstructJob(Compilation &C, const Job
     assert((isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) &&
            "Invalid action for clang tool.");
 
+    if (JA.getType() == types::TY_LTO_IR ||
+        JA.getType() == types::TY_LTO_BC) {
+      CmdArgs.push_back("-flto");
+    }
     if (JA.getType() == types::TY_Nothing) {
       CmdArgs.push_back("-fsyntax-only");
     } else if (JA.getType() == types::TY_LLVM_IR ||
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp	(revision 237590)
+++ lib/Frontend/CompilerInvocation.cpp	(working copy)
@@ -489,6 +489,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts,
 
   Opts.MergeFunctions = Args.hasArg(OPT_fmerge_functions);
 
+  Opts.PrepareForLTO = Args.hasArg(OPT_flto);
+
   Opts.MSVolatile = Args.hasArg(OPT_fms_volatile);
 
   Opts.VectorizeBB = Args.hasArg(OPT_vectorize_slp_aggressive);
Index: test/CodeGen/available-externally-suppress.c
===================================================================
--- test/CodeGen/available-externally-suppress.c	(revision 237590)
+++ test/CodeGen/available-externally-suppress.c	(working copy)
@@ -1,6 +1,10 @@
 // RUN: %clang_cc1 -emit-llvm -o - -triple x86_64-apple-darwin10 %s | FileCheck %s
+// RUN: %clang_cc1 -O2 -fno-inline -emit-llvm -o - -triple x86_64-apple-darwin10 %s | FileCheck %s
+// RUN: %clang_cc1 -flto -O2 -fno-inline -emit-llvm -o - -triple x86_64-apple-darwin10 %s | FileCheck %s -check-prefix=LTO
 
 // Ensure that we don't emit available_externally functions at -O0.
+// Also should not emit them at -O2, unless -flto is present in which case
+// we should preserve them for link-time inlining decisions.
 int x;
 
 inline void f0(int y) { x = y; }
@@ -7,6 +11,8 @@ inline void f0(int y) { x = y; }
 
 // CHECK-LABEL: define void @test()
 // CHECK: declare void @f0(i32)
+// LTO-LABEL: define void @test()
+// LTO: define available_externally void @f0
 void test() {
   f0(17);
 }
@@ -19,9 +25,13 @@ inline int __attribute__((always_inline)) f1(int x
 }
 
 // CHECK: @test1
+// LTO: @test1
 int test1(int x) {
   // CHECK: br i1
   // CHECK-NOT: call {{.*}} @f1
   // CHECK: ret i32
+  // LTO: br i1
+  // LTO-NOT: call {{.*}} @f1
+  // LTO: ret i32
   return f1(x);
 }


More information about the cfe-dev mailing list