[clang] 048a0c2 - [clang] Support Unified LTO Bitcode Frontend

Matthew Voss via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 11 15:14:38 PDT 2023


Author: Matthew Voss
Date: 2023-07-11T15:13:57-07:00
New Revision: 048a0c246908291c82d2f4531d3df45a4c4a8a18

URL: https://github.com/llvm/llvm-project/commit/048a0c246908291c82d2f4531d3df45a4c4a8a18
DIFF: https://github.com/llvm/llvm-project/commit/048a0c246908291c82d2f4531d3df45a4c4a8a18.diff

LOG: [clang] Support Unified LTO Bitcode Frontend

The unified LTO pipeline creates a single LTO bitcode structure that can
be used by Thin or Full LTO. This means that the LTO mode can be chosen
at link time and that all LTO bitcode produced by the pipeline is
compatible, from an optimization perspective. This makes the behavior of
LTO a bit more predictable by normalizing the set of LTO features
supported by each LTO bitcode file.

Example usage:

  # Compile and link. Select regular LTO at link time.
  clang -flto -funified-lto -fuse-ld=lld foo.c

  # Compile and link. Select ThinLTO at link time.
  clang -flto=thin -funified-lto -fuse-ld=lld foo.c

  # Link separately, using ThinLTO.
  clang -c -flto -funified-lto foo.c  # -flto={full,thin} are identical in
  terms of compilation actions
  clang -flto=thin -fuse-ld=lld foo.o  # pass --lto=thin to ld.lld

  # Link separately, using regular LTO.
  clang -c -flto -funified-lto foo.c
  clang -flto -fuse-ld=lld foo.o  # pass --lto=full to ld.lld

The RFC discussing the details and rational for this change is here:
https://discourse.llvm.org/t/rfc-a-unified-lto-bitcode-frontend/61774

Added: 
    clang/test/CodeGen/asan-unified-lto.ll
    clang/test/CodeGen/unified-lto-pipeline.c
    clang/test/CodeGenCXX/unified-cfi-lto.cpp
    clang/test/Driver/unified-lto.c

Modified: 
    clang/include/clang/Basic/CodeGenOptions.def
    clang/include/clang/Driver/Options.td
    clang/lib/CodeGen/BackendUtil.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Driver/ToolChains/PS4CPU.cpp
    clang/lib/Frontend/CompilerInvocation.cpp
    clang/test/CodeGen/emit-summary-index.c
    clang/test/Driver/whole-program-vtables.c

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 5b18f45a04ab1a..a03447ca2931e2 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -168,6 +168,7 @@ CODEGENOPT(EnableSplitLTOUnit, 1, 0) ///< Enable LTO unit splitting to support
 				     /// CFI and traditional whole program
 				     /// devirtualization that require whole
 				     /// program IR support.
+CODEGENOPT(UnifiedLTO, 1, 0) ///< Use the unified LTO pipeline.
 CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can
                                               ///< be used with an incremental
                                               ///< linker.

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index c5230d11baeddf..aacdf09bec1e6f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2341,6 +2341,11 @@ def flto_EQ_auto : Flag<["-"], "flto=auto">, Group<f_Group>,
   Alias<flto_EQ>, AliasArgs<["full"]>, HelpText<"Enable LTO in 'full' mode">;
 def flto : Flag<["-"], "flto">, Flags<[CoreOption, CC1Option, FC1Option, FlangOption]>, Group<f_Group>,
   Alias<flto_EQ>, AliasArgs<["full"]>, HelpText<"Enable LTO in 'full' mode">;
+defm unified_lto : BoolFOption<"unified-lto",
+  CodeGenOpts<"UnifiedLTO">, DefaultFalse,
+  PosFlag<SetTrue, [], "Use the unified LTO pipeline">,
+  NegFlag<SetFalse, [], "Use distinct LTO pipelines">,
+  BothFlags<[CC1Option], "">>;
 def fno_lto : Flag<["-"], "fno-lto">, Flags<[CoreOption, CC1Option]>, Group<f_Group>,
   HelpText<"Disable LTO mode (default)">;
 def foffload_lto_EQ : Joined<["-"], "foffload-lto=">, Flags<[CoreOption]>, Group<f_Group>,

diff  --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 06af08023d1be9..1b0c249f440899 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -831,6 +831,7 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
   // Only enable CGProfilePass when using integrated assembler, since
   // non-integrated assemblers don't recognize .cgprofile section.
   PTO.CallGraphProfile = !CodeGenOpts.DisableIntegratedAS;
+  PTO.UnifiedLTO = CodeGenOpts.UnifiedLTO;
 
   LoopAnalysisManager LAM;
   FunctionAnalysisManager FAM;
@@ -1010,7 +1011,7 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
           });
     }
 
-    if (IsThinLTO) {
+    if (IsThinLTO || (IsLTO && CodeGenOpts.UnifiedLTO)) {
       MPM = PB.buildThinLTOPreLinkDefaultPipeline(Level);
     } else if (IsLTO) {
       MPM = PB.buildLTOPreLinkDefaultPipeline(Level);
@@ -1036,8 +1037,10 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
           if (!ThinLinkOS)
             return;
         }
-        MPM.addPass(ThinLTOBitcodeWriterPass(*OS, ThinLinkOS ? &ThinLinkOS->os()
-                                                             : nullptr));
+        if (CodeGenOpts.UnifiedLTO)
+          TheModule->addModuleFlag(Module::Error, "UnifiedLTO", uint32_t(1));
+        MPM.addPass(ThinLTOBitcodeWriterPass(
+            *OS, ThinLinkOS ? &ThinLinkOS->os() : nullptr));
       } else {
         MPM.addPass(PrintModulePass(*OS, "", CodeGenOpts.EmitLLVMUseLists,
                                     /*EmitLTOSummary=*/true));
@@ -1048,11 +1051,13 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
       // targets
       bool EmitLTOSummary = shouldEmitRegularLTOSummary();
       if (EmitLTOSummary) {
-        if (!TheModule->getModuleFlag("ThinLTO"))
+        if (!TheModule->getModuleFlag("ThinLTO") && !CodeGenOpts.UnifiedLTO)
           TheModule->addModuleFlag(Module::Error, "ThinLTO", uint32_t(0));
         if (!TheModule->getModuleFlag("EnableSplitLTOUnit"))
           TheModule->addModuleFlag(Module::Error, "EnableSplitLTOUnit",
                                    uint32_t(1));
+        if (CodeGenOpts.UnifiedLTO)
+          TheModule->addModuleFlag(Module::Error, "UnifiedLTO", uint32_t(1));
       }
       if (Action == Backend_EmitBC)
         MPM.addPass(BitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists,

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 5c7e248c286a1b..890b99d916f116 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4776,6 +4776,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   // Select the appropriate action.
   RewriteKind rewriteKind = RK_None;
 
+  bool UnifiedLTO = false;
+  if (IsUsingLTO) {
+    UnifiedLTO = Args.hasFlag(options::OPT_funified_lto,
+                              options::OPT_fno_unified_lto, false);
+    if (UnifiedLTO)
+      CmdArgs.push_back("-funified-lto");
+  }
+
   // If CollectArgsForIntegratedAssembler() isn't called below, claim the args
   // it claims when not running an assembler. Otherwise, clang would emit
   // "argument unused" warnings for assembler flags when e.g. adding "-E" to
@@ -4920,7 +4928,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
         assert(LTOMode == LTOK_Full || LTOMode == LTOK_Thin);
         CmdArgs.push_back(Args.MakeArgString(
             Twine("-flto=") + (LTOMode == LTOK_Thin ? "thin" : "full")));
-        CmdArgs.push_back("-flto-unit");
+        // PS4 uses the legacy LTO API, which does not support some of the
+        // features enabled by -flto-unit.
+        if ((RawTriple.getOS() != llvm::Triple::PS4) ||
+            (D.getLTOMode() == LTOK_Full) || !UnifiedLTO)
+          CmdArgs.push_back("-flto-unit");
       }
     }
   }
@@ -7309,17 +7321,24 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   }
 
   if (WholeProgramVTables) {
-    // Propagate -fwhole-program-vtables if this is an LTO compile.
-    if (IsUsingLTO)
-      CmdArgs.push_back("-fwhole-program-vtables");
+    // PS4 uses the legacy LTO API, which does not support this feature in
+    // ThinLTO mode.
+    bool IsPS4 = getToolChain().getTriple().isPS4();
+
     // Check if we passed LTO options but they were suppressed because this is a
     // device offloading action, or we passed device offload LTO options which
     // were suppressed because this is not the device offload action.
+    // Check if we are using PS4 in regular LTO mode.
     // Otherwise, issue an error.
-    else if (!D.isUsingLTO(!IsDeviceOffloadAction))
+    if ((!IsUsingLTO && !D.isUsingLTO(!IsDeviceOffloadAction)) ||
+        (IsPS4 && !UnifiedLTO && (D.getLTOMode() != LTOK_Full)))
       D.Diag(diag::err_drv_argument_only_allowed_with)
           << "-fwhole-program-vtables"
-          << "-flto";
+          << ((IsPS4 && !UnifiedLTO) ? "-flto=full" : "-flto");
+
+    // Propagate -fwhole-program-vtables if this is an LTO compile.
+    if (IsUsingLTO)
+      CmdArgs.push_back("-fwhole-program-vtables");
   }
 
   bool DefaultsSplitLTOUnit =

diff  --git a/clang/lib/Driver/ToolChains/PS4CPU.cpp b/clang/lib/Driver/ToolChains/PS4CPU.cpp
index 2a35fdfdfedac3..37006dc5d7ed8b 100644
--- a/clang/lib/Driver/ToolChains/PS4CPU.cpp
+++ b/clang/lib/Driver/ToolChains/PS4CPU.cpp
@@ -204,6 +204,13 @@ void tools::PScpu::Linker::ConstructJob(Compilation &C, const JobAction &JA,
   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs))
     TC.addSanitizerArgs(Args, CmdArgs, "-l", "");
 
+  if (D.isUsingLTO() && Args.hasArg(options::OPT_funified_lto)) {
+    if (D.getLTOMode() == LTOK_Thin)
+      CmdArgs.push_back("--lto=thin");
+    else if (D.getLTOMode() == LTOK_Full)
+      CmdArgs.push_back("--lto=full");
+  }
+
   Args.AddAllArgs(CmdArgs, options::OPT_L);
   Args.AddAllArgs(CmdArgs, options::OPT_T_Group);
   Args.AddAllArgs(CmdArgs, options::OPT_s);

diff  --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 5360bbe8780923..1fba91bed04141 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1767,6 +1767,8 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
       Opts.PrepareForThinLTO = true;
     else if (S != "full")
       Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << S;
+    if (Args.hasArg(OPT_funified_lto))
+      Opts.PrepareForThinLTO = true;
   }
   if (Arg *A = Args.getLastArg(OPT_fthinlto_index_EQ)) {
     if (IK.getLanguage() != Language::LLVM_IR)

diff  --git a/clang/test/CodeGen/asan-unified-lto.ll b/clang/test/CodeGen/asan-unified-lto.ll
new file mode 100644
index 00000000000000..7b790d49e3fdbd
--- /dev/null
+++ b/clang/test/CodeGen/asan-unified-lto.ll
@@ -0,0 +1,18 @@
+; Verify that in the cases of explict distinct LTO piplines,
+; explicit unified LTO pipelines, and the default LTO pipeline,
+; there is no crash and the anonoymous global is named
+; as expected.
+
+; RUN: %clang_cc1 -emit-llvm-bc -O1 -flto -fsanitize=address -o - -x ir < %s | llvm-dis -o - | FileCheck %s
+; RUN: %clang_cc1 -emit-llvm-bc -O1 -flto -funified-lto -fsanitize=address -o - -x ir < %s | llvm-dis -o - | FileCheck %s
+; CHECK: @anon.3ee0898e5200a57350fed5485ae5d237
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-linux-gnu"
+
+ at .str = private unnamed_addr constant [5 x i8] c"none\00", align 1
+
+define ptr @f() {
+  %ptr = getelementptr inbounds [5 x i8], ptr @.str, i32 0, i32 0
+  ret ptr %ptr
+}

diff  --git a/clang/test/CodeGen/emit-summary-index.c b/clang/test/CodeGen/emit-summary-index.c
index c407934e8f4baf..4a18831c895474 100644
--- a/clang/test/CodeGen/emit-summary-index.c
+++ b/clang/test/CodeGen/emit-summary-index.c
@@ -14,4 +14,10 @@
 // RUN: %clang_cc1 -flto -triple x86_64-pc-linux-gnu -emit-llvm-bc -disable-llvm-passes < %s -o %t.bc
 // RUN: %clang_cc1 -flto -triple x86_64-pc-linux-gnu -emit-llvm-bc -x ir < %t.bc | llvm-bcanalyzer -dump | FileCheck --check-prefix=LTOINDEX %s
 
+/// Check that emitting bitcode works for Unified LTO, when either LTO mode is specified
+// RUN: %clang_cc1 -flto=thin -funified-lto -emit-llvm-bc < %s | llvm-bcanalyzer -dump | FileCheck --check-prefix=UNITHIN %s
+// RUN: %clang_cc1 -flto -funified-lto -emit-llvm-bc < %s | llvm-bcanalyzer -dump | FileCheck --check-prefix=UNITHIN %s
+
+// UNITHIN: <GLOBALVAL_SUMMARY_BLOCK
+
 int main(void) {}

diff  --git a/clang/test/CodeGen/unified-lto-pipeline.c b/clang/test/CodeGen/unified-lto-pipeline.c
new file mode 100644
index 00000000000000..88bdfa6a634a46
--- /dev/null
+++ b/clang/test/CodeGen/unified-lto-pipeline.c
@@ -0,0 +1,17 @@
+// RUN: %clang --target=x86_64-unknown-unknown-linux -Xclang -fdebug-pass-manager -flto=thin -funified-lto -O2 -c %s -o %t.0 2>&1 | FileCheck --check-prefix=THIN %s
+// RUN: mv %t.0 %t.1
+// RUN: %clang --target=x86_64-unknown-unknown-linux -Xclang -fdebug-pass-manager -flto=full -funified-lto -O2 -c %s -o %t.0 2>&1 | FileCheck --check-prefix=THIN %s
+// RUN: %clang --target=x86_64-unknown-unknown-linux -Xclang -fdebug-pass-manager -flto=thin -O2 -c %s -o %t.2 2>&1 | FileCheck --check-prefix=THIN %s
+// RUN: mv %t.2 %t.3
+// RUN: %clang --target=x86_64-unknown-unknown-linux -Xclang -fdebug-pass-manager -flto=full -O2 -c %s -o %t.2 2>&1 | FileCheck --check-prefix=FULL %s
+// RUN: cmp %t.0 %t.1
+// THIN: ThinLTOBitcodeWriterPass
+// FULL-NOT: ThinLTOBitcodeWriterPass
+
+int foo() {
+  return 2 + 2;
+}
+
+int bar() {
+  return foo() + 1;
+}

diff  --git a/clang/test/CodeGenCXX/unified-cfi-lto.cpp b/clang/test/CodeGenCXX/unified-cfi-lto.cpp
new file mode 100644
index 00000000000000..cc3f594533ae1b
--- /dev/null
+++ b/clang/test/CodeGenCXX/unified-cfi-lto.cpp
@@ -0,0 +1,22 @@
+// Ensure that the frontend adds the proper metadata when CFI is
+// enabled.
+// RUN: %clang --target=x86_64-scei-ps4 -funified-lto -flto -fsanitize=cfi -fvisibility=hidden -c %s -o %t.o
+// RUN: llvm-dis %t.o -o %t1
+// RUN: FileCheck <%t1.0 %s
+
+typedef int (*FuncPtr)();
+
+int a() { return 1; }
+int b() { return 2; }
+int c() { return 3; }
+
+FuncPtr func[3] = {a,b,c};
+
+int
+main(int argc, char *argv[]) {
+  // CHECK: call i1 @llvm.type.test
+  return func[argc]();
+  // CHECK-LABEL: trap
+}
+
+// CHECK: typeTests:

diff  --git a/clang/test/Driver/unified-lto.c b/clang/test/Driver/unified-lto.c
new file mode 100644
index 00000000000000..e16affe2c5efda
--- /dev/null
+++ b/clang/test/Driver/unified-lto.c
@@ -0,0 +1,12 @@
+// RUN: %clang --target=x86_64-unknown-linux -### %s -flto=full -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s
+// RUN: %clang --target=x86_64-unknown-linux -### %s -flto=thin -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s
+// RUN: %clang --target=x86_64-scei-ps4 -### %s -flto=full -funified-lto 2>&1 | FileCheck --check-prefix=UNIT %s
+// RUN: %clang --target=x86_64-scei-ps4 -### %s -flto=thin -funified-lto 2>&1 | FileCheck --check-prefix=NOUNIT %s
+
+// UNIT: "-flto-unit"
+// NOUNIT-NOT: "-flto-unit"
+
+// RUN: %clang --target=x86_64-sie-prospero -### %s -funified-lto 2>&1 | FileCheck --check-prefix=NOUNILTO %s
+// NOUNILTO: clang: warning: argument unused during compilation: '-funified-lto'
+// NOUNILTO: "-cc1"
+// NOUNILTO-NOT: "-funified-lto

diff  --git a/clang/test/Driver/whole-program-vtables.c b/clang/test/Driver/whole-program-vtables.c
index de0c606594c1f2..19986ec3eb7c0a 100644
--- a/clang/test/Driver/whole-program-vtables.c
+++ b/clang/test/Driver/whole-program-vtables.c
@@ -6,6 +6,10 @@
 // RUN: %clang_cl --target=x86_64-pc-win32 -fwhole-program-vtables -flto -### -- %s 2>&1 | FileCheck --check-prefix=LTO %s
 // LTO: "-fwhole-program-vtables"
 
+/// -funified-lto does not imply -flto, so we still get an error that fwhole-program-vtables has no effect without -flto
+// RUN: %clang --target=x86_64-pc-linux-gnu -fwhole-program-vtables -funified-lto -### %s 2>&1 | FileCheck --check-prefix=NO-LTO %s
+// RUN: %clang --target=x86_64-pc-linux-gnu -fwhole-program-vtables -fno-unified-lto -### %s 2>&1 | FileCheck --check-prefix=NO-LTO %s
+
 // RUN: %clang -target x86_64-unknown-linux -fwhole-program-vtables -fno-whole-program-vtables -flto -### %s 2>&1 | FileCheck --check-prefix=LTO-DISABLE %s
 // RUN: %clang_cl --target=x86_64-pc-win32 -fwhole-program-vtables -fno-whole-program-vtables -flto -### -- %s 2>&1 | FileCheck --check-prefix=LTO-DISABLE %s
 // LTO-DISABLE-NOT: "-fwhole-program-vtables"


        


More information about the cfe-commits mailing list