[llvm] [clang] [AIX][TOC] Add -mtocdata/-mno-tocdata options on AIX (PR #67999)

Zaara Syeda via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 1 13:30:44 PDT 2023


https://github.com/syzaara updated https://github.com/llvm/llvm-project/pull/67999

>From b789b57067bfe9df948a6ff2d65bf2381b23d042 Mon Sep 17 00:00:00 2001
From: Zaara Syeda <syzaara at cpap8104.rtp.raleigh.ibm.com>
Date: Thu, 28 Sep 2023 15:01:56 -0400
Subject: [PATCH 1/2] [AIX][TOC] Add -mtocdata/-mno-tocdata options on AIX

This patch enables support that the XL compiler had for AIX under
-qdatalocal/-qdataimported.
---
 clang/docs/UsersManual.rst                    | 56 ++++++++++++
 clang/include/clang/Basic/CodeGenOptions.h    |  9 ++
 .../clang/Basic/DiagnosticDriverKinds.td      |  3 +
 .../clang/Basic/DiagnosticFrontendKinds.td    |  2 +
 clang/include/clang/Driver/Options.td         | 23 +++++
 clang/lib/CodeGen/CGDecl.cpp                  |  1 +
 clang/lib/CodeGen/CodeGenModule.cpp           | 28 ++++++
 clang/lib/CodeGen/Targets/PPC.cpp             | 61 +++++++++++++
 clang/lib/Driver/ToolChains/AIX.cpp           | 89 +++++++++++++++++++
 clang/lib/Frontend/CompilerInstance.cpp       |  5 ++
 .../test/CodeGen/PowerPC/toc-data-attribute.c | 61 +++++++++++++
 .../CodeGen/PowerPC/toc-data-attribute.cpp    | 43 +++++++++
 .../CodeGen/PowerPC/toc-data-diagnostics.c    | 69 ++++++++++++++
 .../PowerPC/toc-data-structs-arrays.cpp       | 64 +++++++++++++
 clang/test/CodeGen/toc-data.c                 | 31 +++++++
 clang/test/Driver/toc-conf.c                  | 30 +++++++
 clang/test/Driver/tocdata-cc1.c               | 16 ++++
 llvm/include/llvm/ADT/STLExtras.h             | 13 +++
 llvm/lib/MC/MCSectionXCOFF.cpp                |  3 +-
 llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp   | 20 ++---
 .../CodeGen/PowerPC/toc-data-large-array.ll   | 16 ++++
 .../CodeGen/PowerPC/toc-data-struct-array.ll  | 29 ++++++
 22 files changed, 655 insertions(+), 17 deletions(-)
 create mode 100644 clang/test/CodeGen/PowerPC/toc-data-attribute.c
 create mode 100644 clang/test/CodeGen/PowerPC/toc-data-attribute.cpp
 create mode 100644 clang/test/CodeGen/PowerPC/toc-data-diagnostics.c
 create mode 100644 clang/test/CodeGen/PowerPC/toc-data-structs-arrays.cpp
 create mode 100644 clang/test/CodeGen/toc-data.c
 create mode 100644 clang/test/Driver/toc-conf.c
 create mode 100644 clang/test/Driver/tocdata-cc1.c
 create mode 100644 llvm/test/CodeGen/PowerPC/toc-data-large-array.ll
 create mode 100644 llvm/test/CodeGen/PowerPC/toc-data-struct-array.ll

diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 25bbd72c81f7a3b..ee37872771c352a 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -4016,7 +4016,63 @@ Clang expects the GCC executable "gcc.exe" compiled for
 
 AIX
 ^^^
+TOC Data Transformation
+"""""""""""""""""""""""
+TOC data transformation is off by default (``-mno-tocdata``).
+When ``-mtocdata`` is specified, the TOC data transformation will be applied to
+all suitable variables with static storage duration, including static data
+members of classes and block-scope static variables (if not marked as exceptions,
+see further below).
 
+Suitable variables must:
+
+-  have complete types
+-  be independently generated (i.e., not placed in a pool)
+-  be at most as large as a pointer
+-  not be aligned more strictly than a pointer
+-  not be structs containing flexible array members
+-  not have internal linkage
+-  not have aliases
+-  not have section attributes
+
+The TOC data transformation results in the variable, not its address,
+being placed in the TOC. This eliminates the need to load the address of the
+variable from the TOC.
+
+Note:
+If the TOC data transformation is applied to a variable whose definition
+is imported, the linker will generate fixup code for reading or writing to the
+variable.
+
+Any internal linkage variables specified to any TOC data options will be ignored.
+
+**Options:**
+
+.. option:: -mno-tocdata
+
+  This is the default behaviour. Only variables explicitly specified with
+  ``-mtocdata=`` will have the TOC data transformation applied.
+
+.. option:: -mtocdata
+
+  Apply the TOC data transformation to all suitable variables with static
+  storage duration (including static data members of classes and block-scope
+  static variables) that are not explicitly specified with ``-mno-tocdata=``.
+
+.. option:: -mno-tocdata=
+
+  Can be used in conjunction with ``-mtocdata`` to mark the comma-separated
+  list of external linkage variables, specified using their mangled names, as
+  exceptions to ``-mtocdata``.
+
+.. option:: -mtocdata=
+
+  Apply the TOC data transformation to the comma-separated list of external
+  linkage variables, specified using their mangled names, if they are suitable.
+  Emit diagnostics for all unsuitable variables specified.
+
+Default Visibility Export Mapping
+"""""""""""""""""""""""""""""""""
 The ``-mdefault-visibility-export-mapping=`` option can be used to control
 mapping of default visibility to an explicit shared object export
 (i.e. XCOFF exported visibility). Three values are provided for the option:
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 12be4e0025a7054..3af1a5d5aeec379 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -408,6 +408,15 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// List of dynamic shared object files to be loaded as pass plugins.
   std::vector<std::string> PassPlugins;
 
+  /// List of global variables explicitly specified by the user as toc-data.
+  std::vector<std::string> TocDataVarsUserSpecified;
+
+  /// List of global variables that over-ride the toc-data default.
+  std::vector<std::string> NoTocDataVars;
+
+  /// Flag for all global variables to be treated as toc-data.
+  bool AllTocData;
+
   /// Path to allowlist file specifying which objects
   /// (files, functions) should exclusively be instrumented
   /// by sanitizer coverage pass.
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 2a48c063e243ee0..736fb89b2ff5e8f 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -560,6 +560,9 @@ def warn_drv_unsupported_gpopt : Warning<
   "ignoring '-mgpopt' option as it cannot be used with %select{|the implicit"
   " usage of }0-mabicalls">,
   InGroup<UnsupportedGPOpt>;
+def warn_drv_unsupported_tocdata: Warning<
+  "ignoring '-mtocdata' as it is only supported for -mcmodel=small">,
+  InGroup<OptionIgnored>;
 def warn_drv_unsupported_sdata : Warning<
   "ignoring '-msmall-data-limit=' with -mcmodel=large for -fpic or RV64">,
   InGroup<OptionIgnored>;
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 715e0c0dc8fa84e..7269ad307eb9551 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -93,6 +93,8 @@ def err_fe_backend_error_attr :
 def warn_fe_backend_warning_attr :
   Warning<"call to '%0' declared with 'warning' attribute: %1">, BackendInfo,
   InGroup<BackendWarningAttributes>;
+def warn_toc_unsupported_type : Warning<"The -mtocdata option is ignored "
+  "for %0 because %1.">, InGroup<BackendWarningAttributes>;
 
 def err_fe_invalid_code_complete_file : Error<
     "cannot locate code-completion file %0">, DefaultFatal;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index ee4e23f335e7875..4c1bc268259f1c7 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3426,6 +3426,29 @@ def fpass_plugin_EQ : Joined<["-"], "fpass-plugin=">,
   MetaVarName<"<dsopath>">,
   HelpText<"Load pass plugin from a dynamic shared object file (only with new pass manager).">,
   MarshallingInfoStringVector<CodeGenOpts<"PassPlugins">>;
+def mtocdata : Flag<["-"], "mtocdata">,
+  Visibility<[ClangOption, CLOption, DXCOption, CC1Option]>,
+  Flags<[TargetSpecific]>,
+  HelpText<"All suitable variables will have the TOC data transformation applied.">,
+  MarshallingInfoFlag<CodeGenOpts<"AllTocData">>;
+def mno_tocdata : Flag<["-"], "mno-tocdata">,
+  Visibility<[ClangOption, CLOption, DXCOption, CC1Option]>,
+  Flags<[TargetSpecific]>,
+  HelpText<"This is the default. TOC data transformation is not applied to any"
+           "variables. Only variables specified explicitly in -mtocdata= will"
+           "have the TOC data transformation.">;
+def mtocdata_EQ : CommaJoined<["-"], "mtocdata=">,
+  Visibility<[ClangOption, CLOption, DXCOption, CC1Option]>,
+  Flags<[TargetSpecific]>,
+  HelpText<"Specifies a list of variables to which the TOC data transformation"
+           "will be applied.">,
+  MarshallingInfoStringVector<CodeGenOpts<"TocDataVarsUserSpecified">>;
+def mno_tocdata_EQ : CommaJoined<["-"], "mno-tocdata=">,
+  Visibility<[ClangOption, CLOption, DXCOption, CC1Option]>,
+  Flags<[TargetSpecific]>,
+  HelpText<"Specifies a list of variables to be exempt from the TOC data"
+           "transformation.">,
+  MarshallingInfoStringVector<CodeGenOpts<"NoTocDataVars">>;
 defm preserve_as_comments : BoolFOption<"preserve-as-comments",
   CodeGenOpts<"PreserveAsmComments">, DefaultTrue,
   NegFlag<SetFalse, [], [ClangOption, CC1Option],
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 99b94588f56f0a5..6805b514adb6d3f 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -285,6 +285,7 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl(
     setTLSMode(GV, D);
 
   setGVProperties(GV, &D);
+  getTargetCodeGenInfo().setTargetAttributes(cast<Decl>(&D), GV, *this);
 
   // Make sure the result is of the correct type.
   LangAS ExpectedAS = Ty.getAddressSpace();
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 93c3f8d6efbaade..eb00aba8a3c0756 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -622,6 +622,28 @@ static bool checkAliasedGlobal(
   return true;
 }
 
+// Emit a warning if toc-data attribute is requested for global variables that
+// have aliases and remove the toc-data attribute.
+static void checkAliasForTocData(llvm::GlobalVariable *GVar,
+                                 const CodeGenOptions &CodeGenOpts,
+                                 DiagnosticsEngine &Diags,
+                                 SourceLocation Location) {
+  if (GVar->hasAttribute("toc-data")) {
+    auto GVId = GVar->getGlobalIdentifier();
+    // Is this a global variable specified by the user as local?
+    bool UserSpecifiedTOC =
+        llvm::binary_search(CodeGenOpts.TocDataVarsUserSpecified, GVId);
+    if (UserSpecifiedTOC) {
+      Diags.Report(Location, diag::warn_toc_unsupported_type)
+          << GVId << "the variable has an alias";
+    }
+    llvm::AttributeSet CurrAttributes = GVar->getAttributes();
+    llvm::AttributeSet NewAttributes =
+        CurrAttributes.removeAttribute(GVar->getContext(), "toc-data");
+    GVar->setAttributes(NewAttributes);
+  }
+}
+
 void CodeGenModule::checkAliases() {
   // Check if the constructed aliases are well formed. It is really unfortunate
   // that we have to do this in CodeGen, but we only construct mangled names
@@ -648,6 +670,12 @@ void CodeGenModule::checkAliases() {
       continue;
     }
 
+    if (getContext().getTargetInfo().getTriple().isOSAIX())
+      if (const llvm::GlobalVariable *GVar =
+              dyn_cast<const llvm::GlobalVariable>(GV))
+        checkAliasForTocData(const_cast<llvm::GlobalVariable *>(GVar),
+                             getCodeGenOpts(), Diags, Location);
+
     llvm::Constant *Aliasee =
         IsIFunc ? cast<llvm::GlobalIFunc>(Alias)->getResolver()
                 : cast<llvm::GlobalAlias>(Alias)->getAliasee();
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index 40dddde508c1772..3032a9c1ee3c09f 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -8,6 +8,7 @@
 
 #include "ABIInfoImpl.h"
 #include "TargetInfo.h"
+#include "clang/Basic/DiagnosticFrontend.h"
 
 using namespace clang;
 using namespace clang::CodeGen;
@@ -145,6 +146,9 @@ class AIXTargetCodeGenInfo : public TargetCodeGenInfo {
 
   bool initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF,
                                llvm::Value *Address) const override;
+
+  void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
+                           CodeGen::CodeGenModule &M) const override;
 };
 } // namespace
 
@@ -265,6 +269,63 @@ bool AIXTargetCodeGenInfo::initDwarfEHRegSizeTable(
   return PPC_initDwarfEHRegSizeTable(CGF, Address, Is64Bit, /*IsAIX*/ true);
 }
 
+void AIXTargetCodeGenInfo::setTargetAttributes(
+    const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const {
+  if (auto *GVar = dyn_cast<llvm::GlobalVariable>(GV)) {
+    auto GVId = M.getMangledName(dyn_cast<NamedDecl>(D));
+
+    // Is this a global variable specified by the user as toc-data?
+    bool UserSpecifiedTOC =
+        llvm::binary_search(M.getCodeGenOpts().TocDataVarsUserSpecified, GVId);
+    // Assumes the same variable cannot be in both TocVarsUserSpecified and
+    // NoTocVars.
+    if (UserSpecifiedTOC ||
+        ((M.getCodeGenOpts().AllTocData) &&
+         !llvm::binary_search(M.getCodeGenOpts().NoTocDataVars, GVId))) {
+      const unsigned long PointerSize =
+          GV->getParent()->getDataLayout().getPointerSizeInBits() / 8;
+      ASTContext &Context = D->getASTContext();
+      auto *VarD = dyn_cast<VarDecl>(D);
+      assert(VarD && "Invalid declaration of global variable.");
+
+      unsigned Alignment = Context.toBits(Context.getDeclAlign(D)) / 8;
+      const auto *Ty = VarD->getType().getTypePtr();
+      const RecordDecl *RDecl =
+          Ty->isRecordType() ? Ty->getAs<RecordType>()->getDecl() : nullptr;
+
+      bool EmitDiagnostic = UserSpecifiedTOC && GV->hasExternalLinkage();
+      if (!Ty || Ty->isIncompleteType()) {
+        if (EmitDiagnostic)
+          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
+              << GVId << "of incomplete type";
+      } else if (RDecl && RDecl->hasFlexibleArrayMember()) {
+        if (UserSpecifiedTOC)
+          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
+              << GVId << "it contains a flexible array member";
+      } else if (VarD->getTLSKind() != VarDecl::TLS_None) {
+        if (EmitDiagnostic)
+          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
+              << GVId << "of thread local storage model";
+      } else if (PointerSize < Context.getTypeInfo(VarD->getType()).Width / 8) {
+        if (EmitDiagnostic)
+          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
+              << GVId << "variable is larger than a pointer";
+      } else if (PointerSize < Alignment) {
+        if (EmitDiagnostic)
+          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
+              << GVId << "variable is aligned wider than a pointer";
+      } else if (D->hasAttr<SectionAttr>()) {
+        if (EmitDiagnostic)
+          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
+              << GVId << "of a section attribute";
+      } else if ((GV->hasExternalLinkage() || M.getCodeGenOpts().AllTocData) &&
+                 !GV->hasInternalLinkage()) {
+        GVar->addAttribute("toc-data");
+      }
+    }
+  }
+}
+
 // PowerPC-32
 namespace {
 /// PPC32_SVR4_ABIInfo - The 32-bit PowerPC ELF (SVR4) ABI information.
diff --git a/clang/lib/Driver/ToolChains/AIX.cpp b/clang/lib/Driver/ToolChains/AIX.cpp
index 3e5ebafa15ebe1c..47f4aca199a9c2b 100644
--- a/clang/lib/Driver/ToolChains/AIX.cpp
+++ b/clang/lib/Driver/ToolChains/AIX.cpp
@@ -421,6 +421,90 @@ void AIX::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args,
   llvm_unreachable("Unexpected C++ library type; only libc++ is supported.");
 }
 
+// This function processes all the mtocdata options to build the final
+// simplified toc data options to pass to CC1.
+static void addTocDataOptions(const llvm::opt::ArgList &Args,
+                              llvm::opt::ArgStringList &CC1Args,
+                              const Driver &D) {
+
+  // Check the global toc-data setting. The default is -mno-tocdata.
+  // To enable toc-data globally, -mtocdata must be specified.
+  // Additionally, it must be last to take effect.
+  const bool TOCDataGloballyinEffect = [&Args]() {
+    if (!Args.hasArg(options::OPT_mtocdata))
+      return false;
+
+    const Arg *LastArg =
+        Args.getLastArg(options::OPT_mtocdata, options::OPT_mno_tocdata);
+    return LastArg->getOption().matches(options::OPT_mtocdata);
+  }();
+
+  // Currently only supported for small code model.
+  if (TOCDataGloballyinEffect &&
+      (Args.getLastArgValue(options::OPT_mcmodel_EQ).equals("large") ||
+       Args.getLastArgValue(options::OPT_mcmodel_EQ).equals("medium"))) {
+    D.Diag(clang::diag::warn_drv_unsupported_tocdata);
+    return;
+  }
+
+  enum TOCDataSetting {
+    AddressInTOC = 0, // Address of the symbol stored in the TOC.
+    DataInTOC = 1     // Symbol defined in the TOC.
+  };
+
+  const TOCDataSetting DefaultTocDataSetting =
+      TOCDataGloballyinEffect ? DataInTOC : AddressInTOC;
+
+  // Process the list of variables in the explicitly specified options
+  // -mtocdata= and -mno-tocdata= to see which variables are opposite to
+  // the global setting of tocdata in TOCDataGloballyinEffect.
+  // Those that have the opposite setting to TOCDataGloballyinEffect, are added
+  // to ExplicitlySpecifiedGlobals.
+  llvm::StringSet<> ExplicitlySpecifiedGlobals;
+  for (const auto Arg :
+       Args.filtered(options::OPT_mtocdata_EQ, options::OPT_mno_tocdata_EQ)) {
+    TOCDataSetting ArgTocDataSetting =
+        Arg->getOption().matches(options::OPT_mtocdata_EQ) ? DataInTOC
+                                                           : AddressInTOC;
+
+    if (ArgTocDataSetting != DefaultTocDataSetting)
+      for (const char *Val : Arg->getValues())
+        ExplicitlySpecifiedGlobals.insert(Val);
+    else
+      for (const char *Val : Arg->getValues())
+        ExplicitlySpecifiedGlobals.erase(Val);
+  }
+
+  const bool HasExplicitValues = !ExplicitlySpecifiedGlobals.empty();
+
+  auto buildExceptionList = [](const llvm::StringSet<> &ExplicitValues,
+                               const char *OptionSpelling) -> std::string {
+    std::string Option(OptionSpelling);
+    bool IsFirst = true;
+    for (const auto &E : ExplicitValues) {
+      if (!IsFirst)
+        Option += ",";
+
+      IsFirst = false;
+      Option += E.first();
+    }
+    return Option;
+  };
+
+  // Pass the final tocdata options to CC1 consisting of the default
+  // tocdata option (-mtocdata/-mno-tocdata) along with the list
+  // option (-mno-tocdata=/-mtocdata=) if there are any explicitly specified
+  // variables which would be exceptions to the default setting.
+  const char *TocDataGlobalOption =
+      TOCDataGloballyinEffect ? "-mtocdata" : "-mno-tocdata";
+  const char *TocDataListOption =
+      TOCDataGloballyinEffect ? "-mno-tocdata=" : "-mtocdata=";
+  CC1Args.push_back(TocDataGlobalOption);
+  if (HasExplicitValues)
+    CC1Args.push_back(Args.MakeArgString(llvm::Twine(
+        buildExceptionList(ExplicitlySpecifiedGlobals, TocDataListOption))));
+}
+
 void AIX::addClangTargetOptions(
     const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CC1Args,
     Action::OffloadKind DeviceOffloadingKind) const {
@@ -428,6 +512,11 @@ void AIX::addClangTargetOptions(
   Args.AddLastArg(CC1Args, options::OPT_mdefault_visibility_export_mapping_EQ);
   Args.addOptInFlag(CC1Args, options::OPT_mxcoff_roptr, options::OPT_mno_xcoff_roptr);
 
+  // Forward last mtocdata/mno_tocdata options to -cc1.
+  if (Args.hasArg(options::OPT_mtocdata_EQ, options::OPT_mno_tocdata_EQ,
+                  options::OPT_mtocdata))
+    addTocDataOptions(Args, CC1Args, getDriver());
+
   if (Args.hasFlag(options::OPT_fxl_pragma_pack,
                    options::OPT_fno_xl_pragma_pack, true))
     CC1Args.push_back("-fxl-pragma-pack");
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index d18371f21a9d86e..3c606d78fc47c51 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1035,6 +1035,11 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) {
   if (getFrontendOpts().ShowStats || !getFrontendOpts().StatsFile.empty())
     llvm::EnableStatistics(false);
 
+  // Sort vectors containing toc data and no toc data variables to facilitate
+  // binary search later.
+  llvm::sort(getCodeGenOpts().TocDataVarsUserSpecified);
+  llvm::sort(getCodeGenOpts().NoTocDataVars);
+
   for (const FrontendInputFile &FIF : getFrontendOpts().Inputs) {
     // Reset the ID tables if we are reusing the SourceManager and parsing
     // regular files.
diff --git a/clang/test/CodeGen/PowerPC/toc-data-attribute.c b/clang/test/CodeGen/PowerPC/toc-data-attribute.c
new file mode 100644
index 000000000000000..635ad53ce8c5d2d
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/toc-data-attribute.c
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix-xcoff -S -mtocdata=f,g,h,i,j,k,l,m,n,o,p -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=CHECK32 --match-full-lines
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=CHECK32 --match-full-lines
+
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix-xcoff -S -mtocdata=f,g,h,i,j,k,l,m,n,o,p -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=CHECK64 --match-full-lines
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=CHECK64 --match-full-lines
+
+extern int f;
+long long g = 5;
+const char *h = "h";
+int *i;
+int __attribute__((aligned(128))) j = 0;
+float k = 100.00;
+double l = 2.5;
+int m __attribute__((section("foo"))) = 10;
+__thread int n;
+
+extern int p[];
+
+struct SomeStruct;
+extern struct SomeStruct o;
+
+static int func_a() {
+  return g+(int)h[0]+*i+j+k+l+m+n+p[0];
+}
+
+int func_b() {
+  f = 1;
+  return func_a();
+}
+
+struct SomeStruct* getAddress(void) {
+  return &o;
+}
+
+// CHECK32: @g = global i64 5, align 8
+// CHECK32: {{.*}}  = private unnamed_addr constant [2 x i8] c"h\00", align 1
+// CHECK32: @h = global {{...*}} #0
+// CHECK32: @j = global i32 0, align 128
+// CHECK32: @k = global float 1.000000e+02, align 4 #0
+// CHECK32: @l = global double 2.500000e+00, align 8
+// CHECK32: @m = global i32 10, section "foo", align 4
+// CHECK32: @f = external global i32, align 4 #0
+// CHECK32: @o = external global %struct.SomeStruct, align 1
+// CHECK32: @i = global ptr null, align 4 #0
+// CHECK32: @n = thread_local global i32 0, align 4
+// CHECK32: @p = external global [0 x i32], align 4
+// CHECK32: attributes #0 = { "toc-data" }
+
+// CHECK64: @g = global i64 5, align 8 #0
+// CHECK64: {{.*}}  = private unnamed_addr constant [2 x i8] c"h\00", align 1
+// CHECK64: @h = global {{...*}} #0
+// CHECK64: @j = global i32 0, align 128
+// CHECK64: @k = global float 1.000000e+02, align 4 #0
+// CHECK64: @l = global double 2.500000e+00, align 8 #0
+// CHECK64: @m = global i32 10, section "foo", align 4
+// CHECK64: @f = external global i32, align 4 #0
+// CHECK64: @o = external global %struct.SomeStruct, align 1
+// CHECK64: @i = global ptr null, align 8 #0
+// CHECK64: @n = thread_local global i32 0, align 4
+// CHECK64: @p = external global [0 x i32], align 4
+// CHECK64: attributes #0 = { "toc-data" }
diff --git a/clang/test/CodeGen/PowerPC/toc-data-attribute.cpp b/clang/test/CodeGen/PowerPC/toc-data-attribute.cpp
new file mode 100644
index 000000000000000..842fd05efa5c120
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/toc-data-attribute.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 | FileCheck %s
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix-xcoff -S -mtocdata=n,_ZN11MyNamespace10myVariableE,_ZL1s,_ZZ4testvE7counter -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=CHECKIGNORE
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 | FileCheck %s
+// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix-xcoff -S -mtocdata=n,_ZN11MyNamespace10myVariableE,_ZL1s,_ZZ4testvE7counter -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=CHECKIGNORE
+
+extern int n;
+static int s = 100;
+
+inline int test() {
+    static int counter = 0;
+    counter++;
+    return counter;
+}
+
+int a () {
+    n = test();
+    return 0;
+}
+
+namespace MyNamespace {
+    int myVariable = 10;
+}
+
+int b(int x) {
+    using namespace MyNamespace;
+    return x + myVariable;
+}
+
+int c(int x) {
+  s += x;
+  return s;
+}
+
+// CHECK: @n = external global i32, align 4 #0
+// CHECK: @_ZN11MyNamespace10myVariableE = global i32 10, align 4 #0
+// CHECK-NOT: @_ZL1s = internal global i32 100, align 4 #0
+// CHECK: @_ZZ4testvE7counter = linkonce_odr global i32 0, align 4 #0
+// CHECK: attributes #0 = { "toc-data" }
+// CHECKIGNORE: @n = external global i32, align 4 #0
+// CHECKIGNORE: @_ZN11MyNamespace10myVariableE = global i32 10, align 4 #0
+// CHECKIGNORE-NOT: @_ZL1s = internal global i32 100, align 4 #0
+// CHECKIGNORE-NOT: @_ZZ4testvE7counter = linkonce_odr global i32 0, align 4 #0
+// CHECKIGNORE: attributes #0 = { "toc-data" }
diff --git a/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c b/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c
new file mode 100644
index 000000000000000..cdf4b0cf0e05b08
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c
@@ -0,0 +1,69 @@
+// REQUIRES: powerpc-registered-target
+// RUN: %clang_cc1 %s -triple=powerpc-ibm-aix-xcoff -S -mtocdata=h,g,f,e,d,c,b,a,globalOneWithAlias,globalTwoWithAlias,ll,t3 -verify -emit-llvm -o - | FileCheck %s -check-prefix=CHECK --match-full-lines
+// RUN: %clang_cc1 %s -triple=powerpc-ibm-aix-xcoff -S -mtocdata -verify=none -emit-llvm -o - | FileCheck %s -check-prefix=CHECK --match-full-lines
+
+// none-no-diagnostics
+
+struct large_struct {
+  int x;
+  short y;
+  short z;
+  char c;
+};
+
+struct large_struct a;                      // expected-warning {{The -mtocdata option is ignored for a because variable is larger than a pointer.}}
+long long b = 5;                            // expected-warning {{The -mtocdata option is ignored for b because variable is larger than a pointer.}}
+int __attribute__((aligned(128))) c = 0;    // expected-warning {{The -mtocdata option is ignored for c because variable is aligned wider than a pointer.}}
+double d = 2.5;                             // expected-warning {{The -mtocdata option is ignored for d because variable is larger than a pointer.}}
+int e __attribute__((section("foo"))) = 10; // expected-warning {{The -mtocdata option is ignored for e because of a section attribute.}}
+__thread int f;                             // expected-warning {{The -mtocdata option is ignored for f because of thread local storage model.}}
+
+struct SomeStruct;
+extern struct SomeStruct g;                 // expected-warning {{The -mtocdata option is ignored for g because of incomplete type.}}
+
+extern int h[];                             // expected-warning {{The -mtocdata option is ignored for h because of incomplete type.}}
+
+struct ty3 {
+  int A;
+  char C[];
+};
+struct ty3 t3 = { 4, "fo" }; // expected-warning {{The -mtocdata option is ignored for t3 because it contains a flexible array member.}}
+
+int globalOneWithAlias = 10;
+__attribute__((__alias__("globalOneWithAlias"))) extern int aliasOne; // expected-warning {{The -mtocdata option is ignored for globalOneWithAlias because the variable has an alias.}}
+__attribute__((__alias__("globalTwoWithAlias"))) extern int aliasTwo; // expected-warning {{The -mtocdata option is ignored for globalTwoWithAlias because the variable has an alias.}}
+int globalTwoWithAlias = 20;
+
+
+int func() {
+  return a.x+b+c+d+e+f+h[0];
+}
+
+struct SomeStruct* getAddress(void) {
+  return &g;
+}
+
+int test() {
+  return globalOneWithAlias + globalTwoWithAlias + aliasOne + aliasTwo;
+}
+
+long long test2() {
+  static long long ll = 5;
+  ll++;
+  return ll;
+}
+
+// CHECK: @b = global i64 5, align 8
+// CHECK: @c = global i32 0, align 128
+// CHECK: @d = global double 2.500000e+00, align 8
+// CHECK: @e = global i32 10, section "foo", align 4
+// CHECK: @globalOneWithAlias = global i32 10, align 4
+// CHECK: @globalTwoWithAlias = global i32 20, align 4
+// CHECK: @a = global %struct.large_struct zeroinitializer, align 4
+// CHECK: @f = thread_local global i32 0, align 4
+// CHECK: @h = external global [0 x i32], align 4
+// CHECK: @g = external global %struct.SomeStruct, align 1 
+// CHECK: @test2.ll = internal global i64 5, align 8
+// CHECK: @aliasOne = alias i32, ptr @globalOneWithAlias
+// CHECK: @aliasTwo = alias i32, ptr @globalTwoWithAlias
+// CHECK-NOT: attributes #0 = { "toc-data" }
diff --git a/clang/test/CodeGen/PowerPC/toc-data-structs-arrays.cpp b/clang/test/CodeGen/PowerPC/toc-data-structs-arrays.cpp
new file mode 100644
index 000000000000000..6f80ede29c7c79b
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/toc-data-structs-arrays.cpp
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 %s -triple powerpc-ibm-aix-xcoff -S -mtocdata=a4,a5,a8,a9,b,c,d,e,v -emit-llvm -o - 2>&1 \
+// RUN:          | FileCheck %s -check-prefixes=CHECK32 --match-full-lines
+// RUN: %clang_cc1  %s -triple powerpc-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 \
+// RUN:          | FileCheck %s -check-prefixes=CHECK32 --match-full-lines
+
+// RUN: %clang_cc1  %s -triple powerpc64-ibm-aix-xcoff -S -mtocdata=a4,a5,a8,a9,b,c,d,e,v -emit-llvm -o - 2>&1 \
+// RUN:          | FileCheck %s -check-prefixes=CHECK64 --match-full-lines
+// RUN: %clang_cc1  %s -triple powerpc64-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 \
+// RUN:          | FileCheck %s -check-prefixes=CHECK64 --match-full-lines
+
+struct size4_struct {
+  int x;
+};
+
+struct size5_struct {
+  int x;
+  char c;
+};
+
+struct size8_struct {
+  int x;
+  short y;
+  short z;
+};
+
+struct size9_struct {
+  int x;
+  short y;
+  short z;
+  char c;
+};
+
+struct size4_struct a4;
+struct size5_struct a5;
+struct size8_struct a8;
+struct size9_struct a9;
+
+short b[2];
+short c[3];
+short d[4];
+short e[5];
+
+int func_a() {
+  return a4.x+a5.x+a8.x+a9.x+b[0]+c[0]+d[0]+e[0];
+}
+
+// CHECK32: @a4 = global %struct.size4_struct zeroinitializer, align 4 #0
+// CHECK32: @a5 = global %struct.size5_struct zeroinitializer, align 4
+// CHECK32: @a8 = global %struct.size8_struct zeroinitializer, align 4
+// CHECK32: @a9 = global %struct.size9_struct zeroinitializer, align 4
+// CHECK32: @b = global [2 x i16] zeroinitializer, align 2 #0
+// CHECK32: @c = global [3 x i16] zeroinitializer, align 2
+// CHECK32: @d = global [4 x i16] zeroinitializer, align 2
+// CHECK32: @e = global [5 x i16] zeroinitializer, align 2
+// CHECK32: attributes #0 = { "toc-data" }
+
+// CHECK64: @a4 = global %struct.size4_struct zeroinitializer, align 4 #0
+// CHECK64: @a5 = global %struct.size5_struct zeroinitializer, align 4 #0
+// CHECK64: @a8 = global %struct.size8_struct zeroinitializer, align 4 #0
+// CHECK64: @a9 = global %struct.size9_struct zeroinitializer, align 4
+// CHECK64: @b = global [2 x i16] zeroinitializer, align 2 #0
+// CHECK64: @c = global [3 x i16] zeroinitializer, align 2 #0
+// CHECK64: @d = global [4 x i16] zeroinitializer, align 2 #0
+// CHECK64: attributes #0 = { "toc-data" }
diff --git a/clang/test/CodeGen/toc-data.c b/clang/test/CodeGen/toc-data.c
new file mode 100644
index 000000000000000..1490b9353f1f055
--- /dev/null
+++ b/clang/test/CodeGen/toc-data.c
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 %s -triple powerpc-unknown-aix -S -mtocdata=g1,g2,g3 -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-MIX --match-full-lines
+// RUN: %clang_cc1 %s -triple powerpc64-unkown-aix -S -mtocdata -mno-tocdata=g4,g5 -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-MIX --match-full-lines
+
+// RUN: %clang_cc1 %s -triple powerpc-unknown-aix -S -mno-tocdata -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-NOTOCDATA
+// RUN: %clang_cc1 %s -triple powerpc64-unknown-aix -S -mno-tocdata -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-NOTOCDATA
+
+// RUN: %clang_cc1 %s -triple powerpc-unknown-aix -S -mtocdata -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-TOCDATA --match-full-lines
+// RUN: %clang_cc1 %s -triple powerpc64-unknown-aix -S -mtocdata -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-TOCDATA --match-full-lines
+
+int g1, g4;
+extern int g2;
+int g3 = 0, g5 = 123;
+void func() {
+  g2 = 0;
+}
+
+// CHECK-MIX-DAG: @g3 = global i32 0, align 4 #0
+// CHECK-MIX-DAG: @g2 = external global i32, align 4 #0
+// CHECK-MIX-DAG: @g1 = global i32 0, align 4 #0
+// CHECK-MIX-DAG: @g4 = global i32 0, align 4
+// CHECK-MIX-DAG: @g5 = global i32 123, align 4
+// CHECK-MIX: attributes #0 = { "toc-data" }
+
+// CHECK-NOTOCDATA-NOT: "toc-data"
+
+// CHECK-TOCDATA-DAG: @g3 = global i32 0, align 4 #0
+// CHECK-TOCDATA-DAG: @g2 = external global i32, align 4 #0
+// CHECK-TOCDATA-DAG: @g1 = global i32 0, align 4 #0
+// CHECK-TOCDATA-DAG: @g4 = global i32 0, align 4 #0
+// CHECK-TOCDATA-DAG: @g5 = global i32 123, align 4 #0
+// CHECK-TOCDATA: attributes #0 = { "toc-data" }
diff --git a/clang/test/Driver/toc-conf.c b/clang/test/Driver/toc-conf.c
new file mode 100644
index 000000000000000..80d92ee1a90b4bc
--- /dev/null
+++ b/clang/test/Driver/toc-conf.c
@@ -0,0 +1,30 @@
+// RUN: %clang %s --target=powerpc-unknown-aix -mno-tocdata -mtocdata -mno-tocdata -### 2>&1 | FileCheck %s -check-prefix=CHECK-FLAG1
+// RUN: %clang %s --target=powerpc-unknown-aix -mno-tocdata -mtocdata -mno-tocdata -mtocdata -### 2>&1 | FileCheck %s -check-prefix=CHECK-FLAG2
+// RUN: %clang %s --target=powerpc-unknown-aix -mtocdata=g1,g2 -mno-tocdata=g2 -mtocdata=g3,g4 -mno-tocdata=g5,g1 -### 2>&1 | FileCheck %s -check-prefix=CHECK-EQCONF
+// RUN: %clang %s --target=powerpc-unknown-aix -mtocdata=g1 -mtocdata -mno-tocdata -mtocdata=g2,g3 -mno-tocdata=g4,g5,g3 -### 2>&1 | FileCheck %s -check-prefix=CHECK-CONF1
+// RUN: %clang %s --target=powerpc-unknown-aix -mno-tocdata=g1 -mno-tocdata -mtocdata -### 2>&1 | FileCheck %s -check-prefix=CHECK-CONF2
+
+int g1, g4, g5;
+extern int g2;
+int g3 = 0;
+void func() {
+  g2 = 0;
+}
+
+// CHECK-FLAG1-NOT: warning:
+// CHECK-FLAG1: "-cc1"{{.*}}" "-mno-tocdata"
+
+// CHECK-FLAG2-NOT: warning:
+// CHECK-FLAG2: "-cc1"{{.*}}" "-mtocdata"
+
+// CHECK-EQCONF-NOT: warning:
+// CHECK-EQCONF: "-cc1"{{.*}}" "-mno-tocdata"
+// CHECK-EQCONF: "-mtocdata=g3,g4"
+
+// CHECK-CONF1-NOT: warning:
+// CHECK-CONF1: "-cc1"{{.*}}" "-mno-tocdata"
+// CHECK-CONF1: "-mtocdata=g2,g1"
+
+// CHECK-CONF2-NOT: warning:
+// CHECK-CONF2: "-cc1"{{.*}}" "-mtocdata"
+// CHECK-CONF2: "-mno-tocdata=g1"
diff --git a/clang/test/Driver/tocdata-cc1.c b/clang/test/Driver/tocdata-cc1.c
new file mode 100644
index 000000000000000..fe0d97ea02db1f5
--- /dev/null
+++ b/clang/test/Driver/tocdata-cc1.c
@@ -0,0 +1,16 @@
+// RUN: %clang -### --target=powerpc-ibm-aix-xcoff -mcmodel=medium -mtocdata %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-NOTOC %s
+// RUN: %clang -### --target=powerpc-ibm-aix-xcoff -mcmodel=large -mtocdata %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-NOTOC %s
+// RUN: %clang -### --target=powerpc-ibm-aix-xcoff -mtocdata %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-TOC %s
+// RUN: %clang -### --target=powerpc64-ibm-aix-xcoff -mcmodel=medium -mtocdata %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-NOTOC %s
+// RUN: %clang -### --target=powerpc64-ibm-aix-xcoff -mcmodel=large -mtocdata %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-NOTOC %s
+// RUN: %clang -### --target=powerpc64-ibm-aix-xcoff -mtocdata %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-TOC %s
+// CHECK-NOTOC: warning: ignoring '-mtocdata' as it is only supported for -mcmodel=small
+// CHECK-NOTOC-NOT: "-cc1"{{.*}}" "-mtocdata"
+// CHECK-TOC: "-cc1"{{.*}}" "-mtocdata"
+// CHECK-TOC-NOT: warning: ignoring '-mtocdata' as it is only supported for -mcmodel=small
diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h
index 5b926864f0cc4a2..21488b8fa165474 100644
--- a/llvm/include/llvm/ADT/STLExtras.h
+++ b/llvm/include/llvm/ADT/STLExtras.h
@@ -1941,6 +1941,19 @@ auto partition(R &&Range, UnaryPredicate P) {
   return std::partition(adl_begin(Range), adl_end(Range), P);
 }
 
+/// Provide wrappers to std::binary_search which take ranges instead of having
+/// to pass begin/end explicitly.
+template <typename R, typename T> auto binary_search(R &&Range, T &&Value) {
+  return std::binary_search(adl_begin(Range), adl_end(Range),
+                            std::forward<T>(Value));
+}
+
+template <typename R, typename T, typename Compare>
+auto binary_search(R &&Range, T &&Value, Compare C) {
+  return std::binary_search(adl_begin(Range), adl_end(Range),
+                            std::forward<T>(Value), C);
+}
+
 /// Provide wrappers to std::lower_bound which take ranges instead of having to
 /// pass begin/end explicitly.
 template <typename R, typename T> auto lower_bound(R &&Range, T &&Value) {
diff --git a/llvm/lib/MC/MCSectionXCOFF.cpp b/llvm/lib/MC/MCSectionXCOFF.cpp
index 02b1b972f339bf0..ac825a1001939e8 100644
--- a/llvm/lib/MC/MCSectionXCOFF.cpp
+++ b/llvm/lib/MC/MCSectionXCOFF.cpp
@@ -82,8 +82,7 @@ void MCSectionXCOFF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
   }
 
   if (isCsect() && getMappingClass() == XCOFF::XMC_TD) {
-    assert((getKind().isBSSExtern() || getKind().isBSSLocal()) &&
-           "Unexepected section kind for toc-data");
+    assert(getKind().isBSS() && "Unexepected section kind for toc-data");
     printCsectDirective(OS);
     return;
   }
diff --git a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
index b57d185bb638b8c..9e3ed6f03b50512 100644
--- a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
@@ -535,21 +535,11 @@ static bool hasTocDataAttr(SDValue Val, unsigned PointerSize) {
   assert(GVType->isSized() && "A GlobalVariable's size must be known to be "
                               "supported by the toc data transformation.");
 
-  if (GVType->isVectorTy())
-    report_fatal_error("A GlobalVariable of Vector type is not currently "
-                       "supported by the toc data transformation.");
-
-  if (GVType->isArrayTy())
-    report_fatal_error("A GlobalVariable of Array type is not currently "
-                       "supported by the toc data transformation.");
-
-  if (GVType->isStructTy())
-    report_fatal_error("A GlobalVariable of Struct type is not currently "
-                       "supported by the toc data transformation.");
-
-  assert(GVType->getPrimitiveSizeInBits() <= PointerSize * 8 &&
-         "A GlobalVariable with size larger than a TOC entry is not currently "
-         "supported by the toc data transformation.");
+  if (GV->getParent()->getDataLayout().getTypeSizeInBits(GVType) >
+      PointerSize * 8)
+    report_fatal_error(
+        "A GlobalVariable with size larger than a TOC entry is not currently "
+        "supported by the toc data transformation.");
 
   if (GV->hasLocalLinkage() || GV->hasPrivateLinkage())
     report_fatal_error("A GlobalVariable with private or local linkage is not "
diff --git a/llvm/test/CodeGen/PowerPC/toc-data-large-array.ll b/llvm/test/CodeGen/PowerPC/toc-data-large-array.ll
new file mode 100644
index 000000000000000..90f40d9f0fec951
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/toc-data-large-array.ll
@@ -0,0 +1,16 @@
+; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff < %s 2>&1 | FileCheck %s --check-prefix CHECK-ERROR
+; RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff < %s 2>&1 | FileCheck %s --check-prefix CHECK-ERROR
+
+ at a = global [5 x i16] zeroinitializer, align 2 #0
+
+; Function Attrs: noinline
+define i16 @foo() #1 {
+entry:
+  %0 = load i16, ptr @a, align 2
+  ret i16 %0
+}
+
+attributes #0 = { "toc-data" }
+attributes #1 = { noinline }
+
+; CHECK-ERROR: LLVM ERROR: A GlobalVariable with size larger than a TOC entry is not currently supported by the toc data transformation.
diff --git a/llvm/test/CodeGen/PowerPC/toc-data-struct-array.ll b/llvm/test/CodeGen/PowerPC/toc-data-struct-array.ll
new file mode 100644
index 000000000000000..7443fe96f6f2d71
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/toc-data-struct-array.ll
@@ -0,0 +1,29 @@
+; RUN: llc -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s --check-prefix CHECK
+; RUN: llc -mtriple powerpc64-ibm-aix-xcoff < %s | FileCheck %s --check-prefix CHECK
+
+%struct.small_struct = type { i16 }
+
+ at a = global %struct.small_struct zeroinitializer, align 2 #0
+ at b = global [2 x i16] zeroinitializer, align 2 #0
+
+; Function Attrs: noinline
+define i16 @foo() #1 {
+entry:
+  %0 = load i16, ptr @a, align 2
+  %1 = load i16, ptr @b, align 2
+  %add = add nsw i16 %0, %1
+  ret i16 %add
+}
+
+attributes #0 = { "toc-data" }
+attributes #1 = { noinline }
+
+; CHECK:      .toc
+; CHECK-NEXT: .csect a[TD],2
+; CHECK-NEXT: .globl    a[TD]                           # @a
+; CHECK-NEXT: .align    1
+; CHECK-NEXT: .space    2
+; CHECK-NEXT: .csect b[TD],2
+; CHECK-NEXT: .globl    b[TD]                           # @b
+; CHECK-NEXT: .align    1
+; CHECK-NEXT: .space    4

>From 3aab6831fa6818503d6d2309e410f1f8d663071e Mon Sep 17 00:00:00 2001
From: Zaara Syeda <syzaara at cpap8104.rtp.raleigh.ibm.com>
Date: Wed, 1 Nov 2023 16:20:58 -0400
Subject: [PATCH 2/2] Address review comments

---
 clang/docs/UsersManual.rst                    |   1 +
 .../clang/Basic/DiagnosticFrontendKinds.td    |   4 +-
 clang/include/clang/Driver/Options.td         |  24 ++---
 clang/lib/CodeGen/CodeGenModule.cpp           |   4 +-
 clang/lib/CodeGen/Targets/PPC.cpp             | 100 +++++++++---------
 clang/lib/Driver/ToolChains/AIX.cpp           |   9 +-
 .../CodeGen/PowerPC/toc-data-diagnostics.c    |  22 ++--
 7 files changed, 79 insertions(+), 85 deletions(-)

diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index ee37872771c352a..b2ce494e13dda48 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -4034,6 +4034,7 @@ Suitable variables must:
 -  not have internal linkage
 -  not have aliases
 -  not have section attributes
+-  not be thread local storage
 
 The TOC data transformation results in the variable, not its address,
 being placed in the TOC. This eliminates the need to load the address of the
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 7269ad307eb9551..582fab4ae6910ef 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -93,8 +93,8 @@ def err_fe_backend_error_attr :
 def warn_fe_backend_warning_attr :
   Warning<"call to '%0' declared with 'warning' attribute: %1">, BackendInfo,
   InGroup<BackendWarningAttributes>;
-def warn_toc_unsupported_type : Warning<"The -mtocdata option is ignored "
-  "for %0 because %1.">, InGroup<BackendWarningAttributes>;
+def warn_toc_unsupported_type : Warning<"-mtocdata option is ignored "
+  "for %0 because %1">, InGroup<BackendWarningAttributes>;
 
 def err_fe_invalid_code_complete_file : Error<
     "cannot locate code-completion file %0">, DefaultFatal;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 4c1bc268259f1c7..b71b0e861233422 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3426,25 +3426,23 @@ def fpass_plugin_EQ : Joined<["-"], "fpass-plugin=">,
   MetaVarName<"<dsopath>">,
   HelpText<"Load pass plugin from a dynamic shared object file (only with new pass manager).">,
   MarshallingInfoStringVector<CodeGenOpts<"PassPlugins">>;
-def mtocdata : Flag<["-"], "mtocdata">,
-  Visibility<[ClangOption, CLOption, DXCOption, CC1Option]>,
-  Flags<[TargetSpecific]>,
-  HelpText<"All suitable variables will have the TOC data transformation applied.">,
-  MarshallingInfoFlag<CodeGenOpts<"AllTocData">>;
-def mno_tocdata : Flag<["-"], "mno-tocdata">,
-  Visibility<[ClangOption, CLOption, DXCOption, CC1Option]>,
-  Flags<[TargetSpecific]>,
-  HelpText<"This is the default. TOC data transformation is not applied to any"
-           "variables. Only variables specified explicitly in -mtocdata= will"
-           "have the TOC data transformation.">;
+defm tocdata : BoolOption<"m","tocdata",
+  CodeGenOpts<"AllTocData">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+  "All suitable variables will have the TOC data transformation applied">,
+  NegFlag<SetFalse, [], [ClangOption, CC1Option],
+  "This is the default. TOC data transformation is not applied to any"
+  "variables. Only variables specified explicitly in -mtocdata= will"
+  "have the TOC data transformation.">,
+  BothFlags<[], [ClangOption, CLOption]>>, Group<m_Group>;
 def mtocdata_EQ : CommaJoined<["-"], "mtocdata=">,
-  Visibility<[ClangOption, CLOption, DXCOption, CC1Option]>,
+  Visibility<[ClangOption, CC1Option]>,
   Flags<[TargetSpecific]>,
   HelpText<"Specifies a list of variables to which the TOC data transformation"
            "will be applied.">,
   MarshallingInfoStringVector<CodeGenOpts<"TocDataVarsUserSpecified">>;
 def mno_tocdata_EQ : CommaJoined<["-"], "mno-tocdata=">,
-  Visibility<[ClangOption, CLOption, DXCOption, CC1Option]>,
+  Visibility<[ClangOption, CC1Option]>,
   Flags<[TargetSpecific]>,
   HelpText<"Specifies a list of variables to be exempt from the TOC data"
            "transformation.">,
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index eb00aba8a3c0756..2f941bd6b4f3909 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -631,9 +631,7 @@ static void checkAliasForTocData(llvm::GlobalVariable *GVar,
   if (GVar->hasAttribute("toc-data")) {
     auto GVId = GVar->getGlobalIdentifier();
     // Is this a global variable specified by the user as local?
-    bool UserSpecifiedTOC =
-        llvm::binary_search(CodeGenOpts.TocDataVarsUserSpecified, GVId);
-    if (UserSpecifiedTOC) {
+    if ((llvm::binary_search(CodeGenOpts.TocDataVarsUserSpecified, GVId))) {
       Diags.Report(Location, diag::warn_toc_unsupported_type)
           << GVId << "the variable has an alias";
     }
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp b/clang/lib/CodeGen/Targets/PPC.cpp
index 3032a9c1ee3c09f..00bd3bc8d661c50 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -271,57 +271,55 @@ bool AIXTargetCodeGenInfo::initDwarfEHRegSizeTable(
 
 void AIXTargetCodeGenInfo::setTargetAttributes(
     const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const {
-  if (auto *GVar = dyn_cast<llvm::GlobalVariable>(GV)) {
-    auto GVId = M.getMangledName(dyn_cast<NamedDecl>(D));
-
-    // Is this a global variable specified by the user as toc-data?
-    bool UserSpecifiedTOC =
-        llvm::binary_search(M.getCodeGenOpts().TocDataVarsUserSpecified, GVId);
-    // Assumes the same variable cannot be in both TocVarsUserSpecified and
-    // NoTocVars.
-    if (UserSpecifiedTOC ||
-        ((M.getCodeGenOpts().AllTocData) &&
-         !llvm::binary_search(M.getCodeGenOpts().NoTocDataVars, GVId))) {
-      const unsigned long PointerSize =
-          GV->getParent()->getDataLayout().getPointerSizeInBits() / 8;
-      ASTContext &Context = D->getASTContext();
-      auto *VarD = dyn_cast<VarDecl>(D);
-      assert(VarD && "Invalid declaration of global variable.");
-
-      unsigned Alignment = Context.toBits(Context.getDeclAlign(D)) / 8;
-      const auto *Ty = VarD->getType().getTypePtr();
-      const RecordDecl *RDecl =
-          Ty->isRecordType() ? Ty->getAs<RecordType>()->getDecl() : nullptr;
-
-      bool EmitDiagnostic = UserSpecifiedTOC && GV->hasExternalLinkage();
-      if (!Ty || Ty->isIncompleteType()) {
-        if (EmitDiagnostic)
-          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
-              << GVId << "of incomplete type";
-      } else if (RDecl && RDecl->hasFlexibleArrayMember()) {
-        if (UserSpecifiedTOC)
-          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
-              << GVId << "it contains a flexible array member";
-      } else if (VarD->getTLSKind() != VarDecl::TLS_None) {
-        if (EmitDiagnostic)
-          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
-              << GVId << "of thread local storage model";
-      } else if (PointerSize < Context.getTypeInfo(VarD->getType()).Width / 8) {
-        if (EmitDiagnostic)
-          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
-              << GVId << "variable is larger than a pointer";
-      } else if (PointerSize < Alignment) {
-        if (EmitDiagnostic)
-          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
-              << GVId << "variable is aligned wider than a pointer";
-      } else if (D->hasAttr<SectionAttr>()) {
-        if (EmitDiagnostic)
-          M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
-              << GVId << "of a section attribute";
-      } else if ((GV->hasExternalLinkage() || M.getCodeGenOpts().AllTocData) &&
-                 !GV->hasInternalLinkage()) {
-        GVar->addAttribute("toc-data");
-      }
+  if (!isa<llvm::GlobalVariable>(GV))
+    return;
+
+  auto *GVar = dyn_cast<llvm::GlobalVariable>(GV);
+  auto GVId = M.getMangledName(dyn_cast<NamedDecl>(D));
+
+  // Is this a global variable specified by the user as toc-data?
+  bool UserSpecifiedTOC =
+      llvm::binary_search(M.getCodeGenOpts().TocDataVarsUserSpecified, GVId);
+  // Assumes the same variable cannot be in both TocVarsUserSpecified and
+  // NoTocVars.
+  if (UserSpecifiedTOC ||
+      ((M.getCodeGenOpts().AllTocData) &&
+       !llvm::binary_search(M.getCodeGenOpts().NoTocDataVars, GVId))) {
+    const unsigned long PointerSize =
+        GV->getParent()->getDataLayout().getPointerSizeInBits() / 8;
+    ASTContext &Context = D->getASTContext();
+    auto *VarD = dyn_cast<VarDecl>(D);
+    assert(VarD && "Invalid declaration of global variable.");
+
+    unsigned Alignment = Context.toBits(Context.getDeclAlign(D)) / 8;
+    const auto *Ty = VarD->getType().getTypePtr();
+    const RecordDecl *RDecl =
+        Ty->isRecordType() ? Ty->getAs<RecordType>()->getDecl() : nullptr;
+
+    bool EmitDiagnostic = UserSpecifiedTOC && GV->hasExternalLinkage();
+    auto reportUnsupportedWarning = [&](bool ShouldEmitWarning, StringRef Msg) {
+      if (ShouldEmitWarning)
+        M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type)
+            << GVId << Msg;
+    };
+    if (!Ty || Ty->isIncompleteType()) {
+      reportUnsupportedWarning(EmitDiagnostic, "of incomplete type");
+    } else if (RDecl && RDecl->hasFlexibleArrayMember()) {
+      reportUnsupportedWarning(EmitDiagnostic,
+                               "it contains a flexible array member");
+    } else if (VarD->getTLSKind() != VarDecl::TLS_None) {
+      reportUnsupportedWarning(EmitDiagnostic, "of thread local storage");
+    } else if (PointerSize < Context.getTypeInfo(VarD->getType()).Width / 8) {
+      reportUnsupportedWarning(EmitDiagnostic,
+                               "variable is larger than a pointer");
+    } else if (PointerSize < Alignment) {
+      reportUnsupportedWarning(EmitDiagnostic,
+                               "variable is aligned wider than a pointer");
+    } else if (D->hasAttr<SectionAttr>()) {
+      reportUnsupportedWarning(EmitDiagnostic, "of a section attribute");
+    } else if ((GV->hasExternalLinkage() || M.getCodeGenOpts().AllTocData) &&
+               !GV->hasInternalLinkage()) {
+      GVar->addAttribute("toc-data");
     }
   }
 }
diff --git a/clang/lib/Driver/ToolChains/AIX.cpp b/clang/lib/Driver/ToolChains/AIX.cpp
index 47f4aca199a9c2b..c0e71f7d731c817 100644
--- a/clang/lib/Driver/ToolChains/AIX.cpp
+++ b/clang/lib/Driver/ToolChains/AIX.cpp
@@ -475,10 +475,8 @@ static void addTocDataOptions(const llvm::opt::ArgList &Args,
         ExplicitlySpecifiedGlobals.erase(Val);
   }
 
-  const bool HasExplicitValues = !ExplicitlySpecifiedGlobals.empty();
-
   auto buildExceptionList = [](const llvm::StringSet<> &ExplicitValues,
-                               const char *OptionSpelling) -> std::string {
+                               const char *OptionSpelling) {
     std::string Option(OptionSpelling);
     bool IsFirst = true;
     for (const auto &E : ExplicitValues) {
@@ -497,10 +495,11 @@ static void addTocDataOptions(const llvm::opt::ArgList &Args,
   // variables which would be exceptions to the default setting.
   const char *TocDataGlobalOption =
       TOCDataGloballyinEffect ? "-mtocdata" : "-mno-tocdata";
+  CC1Args.push_back(TocDataGlobalOption);
+
   const char *TocDataListOption =
       TOCDataGloballyinEffect ? "-mno-tocdata=" : "-mtocdata=";
-  CC1Args.push_back(TocDataGlobalOption);
-  if (HasExplicitValues)
+  if (const bool HasExplicitValues = !ExplicitlySpecifiedGlobals.empty())
     CC1Args.push_back(Args.MakeArgString(llvm::Twine(
         buildExceptionList(ExplicitlySpecifiedGlobals, TocDataListOption))));
 }
diff --git a/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c b/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c
index cdf4b0cf0e05b08..9d7c949c729b81b 100644
--- a/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c
+++ b/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c
@@ -11,27 +11,27 @@ struct large_struct {
   char c;
 };
 
-struct large_struct a;                      // expected-warning {{The -mtocdata option is ignored for a because variable is larger than a pointer.}}
-long long b = 5;                            // expected-warning {{The -mtocdata option is ignored for b because variable is larger than a pointer.}}
-int __attribute__((aligned(128))) c = 0;    // expected-warning {{The -mtocdata option is ignored for c because variable is aligned wider than a pointer.}}
-double d = 2.5;                             // expected-warning {{The -mtocdata option is ignored for d because variable is larger than a pointer.}}
-int e __attribute__((section("foo"))) = 10; // expected-warning {{The -mtocdata option is ignored for e because of a section attribute.}}
-__thread int f;                             // expected-warning {{The -mtocdata option is ignored for f because of thread local storage model.}}
+struct large_struct a;                      // expected-warning {{-mtocdata option is ignored for a because variable is larger than a pointer}}
+long long b = 5;                            // expected-warning {{-mtocdata option is ignored for b because variable is larger than a pointer}}
+int __attribute__((aligned(128))) c = 0;    // expected-warning {{-mtocdata option is ignored for c because variable is aligned wider than a pointer}}
+double d = 2.5;                             // expected-warning {{-mtocdata option is ignored for d because variable is larger than a pointer}}
+int e __attribute__((section("foo"))) = 10; // expected-warning {{-mtocdata option is ignored for e because of a section attribute}}
+__thread int f;                             // expected-warning {{-mtocdata option is ignored for f because of thread local storage}}
 
 struct SomeStruct;
-extern struct SomeStruct g;                 // expected-warning {{The -mtocdata option is ignored for g because of incomplete type.}}
+extern struct SomeStruct g;                 // expected-warning {{-mtocdata option is ignored for g because of incomplete type}}
 
-extern int h[];                             // expected-warning {{The -mtocdata option is ignored for h because of incomplete type.}}
+extern int h[];                             // expected-warning {{-mtocdata option is ignored for h because of incomplete type}}
 
 struct ty3 {
   int A;
   char C[];
 };
-struct ty3 t3 = { 4, "fo" }; // expected-warning {{The -mtocdata option is ignored for t3 because it contains a flexible array member.}}
+struct ty3 t3 = { 4, "fo" }; // expected-warning {{-mtocdata option is ignored for t3 because it contains a flexible array member}}
 
 int globalOneWithAlias = 10;
-__attribute__((__alias__("globalOneWithAlias"))) extern int aliasOne; // expected-warning {{The -mtocdata option is ignored for globalOneWithAlias because the variable has an alias.}}
-__attribute__((__alias__("globalTwoWithAlias"))) extern int aliasTwo; // expected-warning {{The -mtocdata option is ignored for globalTwoWithAlias because the variable has an alias.}}
+__attribute__((__alias__("globalOneWithAlias"))) extern int aliasOne; // expected-warning {{-mtocdata option is ignored for globalOneWithAlias because the variable has an alias}}
+__attribute__((__alias__("globalTwoWithAlias"))) extern int aliasTwo; // expected-warning {{-mtocdata option is ignored for globalTwoWithAlias because the variable has an alias}}
 int globalTwoWithAlias = 20;
 
 



More information about the cfe-commits mailing list