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

Zaara Syeda via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 2 08:32:10 PDT 2023


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

This patch enables support that the XL compiler had for AIX under -qdatalocal/-qdataimported.

>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] [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



More information about the llvm-commits mailing list