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

This patch enables support that the XL compiler had for AIX under
-qdatalocal/-qdataimported.
This commit is contained in:
Zaara Syeda
2024-03-13 10:26:31 -04:00
committed by GitHub
parent e77324decf
commit 37b5eb0a0a
25 changed files with 716 additions and 36 deletions

View File

@@ -4227,7 +4227,68 @@ 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
- 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
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.
When multiple toc-data options are used, the last option used has the affect.
For example: -mno-tocdata=g5,g1 -mtocdata=g1,g2 -mno-tocdata=g2 -mtocdata=g3,g4
results in -mtocdata=g1,g3,g4
Names of variables not having external linkage 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:

View File

@@ -404,6 +404,15 @@ public:
/// List of pass builder callbacks.
std::vector<std::function<void(llvm::PassBuilder &)>> PassBuilderCallbacks;
/// 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.

View File

@@ -587,6 +587,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>;

View File

@@ -94,6 +94,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<"-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;

View File

@@ -3609,6 +3609,27 @@ 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">>;
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<[TargetSpecific], [ClangOption, CLOption]>>, Group<m_Group>;
def mtocdata_EQ : CommaJoined<["-"], "mtocdata=">,
Visibility<[ClangOption, CC1Option]>,
Flags<[TargetSpecific]>, Group<m_Group>,
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, CC1Option]>,
Flags<[TargetSpecific]>, Group<m_Group>,
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],

View File

@@ -284,6 +284,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();

View File

@@ -626,6 +626,26 @@ 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->getName();
// Is this a global variable specified by the user as local?
if ((llvm::binary_search(CodeGenOpts.TocDataVarsUserSpecified, GVId))) {
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
@@ -652,6 +672,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();

View File

@@ -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 @@ public:
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,61 @@ 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 (!isa<llvm::GlobalVariable>(GV))
return;
auto *GVar = dyn_cast<llvm::GlobalVariable>(GV);
auto GVId = GV->getName();
// 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;
auto *VarD = dyn_cast<VarDecl>(D);
assert(VarD && "Invalid declaration of global variable.");
ASTContext &Context = D->getASTContext();
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,
"variable has a section attribute");
else if (GV->hasExternalLinkage() ||
(M.getCodeGenOpts().AllTocData && !GV->hasLocalLinkage()))
GVar->addAttribute("toc-data");
}
}
// PowerPC-32
namespace {
/// PPC32_SVR4_ABIInfo - The 32-bit PowerPC ELF (SVR4) ABI information.

View File

@@ -433,6 +433,88 @@ 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 (const Arg *LastArg =
Args.getLastArg(options::OPT_mtocdata, options::OPT_mno_tocdata))
return LastArg->getOption().matches(options::OPT_mtocdata);
else
return false;
}();
// 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);
}
auto buildExceptionList = [](const llvm::StringSet<> &ExplicitValues,
const char *OptionSpelling) {
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";
CC1Args.push_back(TocDataGlobalOption);
const char *TocDataListOption =
TOCDataGloballyinEffect ? "-mno-tocdata=" : "-mtocdata=";
if (!ExplicitlySpecifiedGlobals.empty())
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 {
@@ -440,6 +522,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");

View File

@@ -1047,6 +1047,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.

View File

@@ -0,0 +1,50 @@
// 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=COMMON,CHECK32 --match-full-lines
// RUN: %clang_cc1 %s -triple powerpc-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=COMMON,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=COMMON,CHECK64 --match-full-lines
// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=COMMON,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
// CHECK64: @g = global i64 5, align 8 #0
// COMMON: {{.*}} = private unnamed_addr constant [2 x i8] c"h\00", align 1
// COMMON: @h = global {{...*}} #0
// COMMON: @j = global i32 0, align 128
// COMMON: @k = global float 1.000000e+02, align 4 #0
// CHECK32: @l = global double 2.500000e+00, align 8
// CHECK64: @l = global double 2.500000e+00, align 8 #0
// COMMON: @m = global i32 10, section "foo", align 4
// COMMON: @f = external global i32, align 4 #0
// COMMON: @o = external global %struct.SomeStruct, align 1
// CHECK32: @i = global ptr null, align 4 #0
// CHECK64: @i = global ptr null, align 8 #0
// COMMON: @n = thread_local global i32 0, align 4
// COMMON: @p = external global [0 x i32], align 4
// COMMON: attributes #0 = { "toc-data" }

View File

@@ -0,0 +1,39 @@
// RUN: %clang_cc1 %s -triple powerpc-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=COMMON,ALLTOC
// RUN: %clang_cc1 %s -triple powerpc-ibm-aix-xcoff -S -mtocdata=n,_ZN11MyNamespace10myVariableE,_ZL1s,_ZZ4testvE7counter -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=COMMON,TOCLIST
// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix-xcoff -S -mtocdata -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=COMMON,ALLTOC
// RUN: %clang_cc1 %s -triple powerpc64-ibm-aix-xcoff -S -mtocdata=n,_ZN11MyNamespace10myVariableE,_ZL1s,_ZZ4testvE7counter -emit-llvm -o - 2>&1 | FileCheck %s -check-prefixes=COMMON,TOCLIST
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;
}
// COMMON: @n = external global i32, align 4 #0
// COMMON: @_ZN11MyNamespace10myVariableE = global i32 10, align 4 #0
// COMMON-NOT: @_ZL1s = internal global i32 100, align 4 #0
// ALLTOC: @_ZZ4testvE7counter = linkonce_odr global i32 0, align 4 #0
// TOCLIST-NOT: @_ZZ4testvE7counter = linkonce_odr global i32 0, align 4 #0
// COMMON: attributes #0 = { "toc-data" }

View File

@@ -0,0 +1,68 @@
// 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 {{-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 variable has 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 {{-mtocdata option is ignored for g 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 {{-mtocdata option is ignored for t3 because it contains a flexible array member}}
int globalOneWithAlias = 10;
__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;
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" }

View File

@@ -0,0 +1,65 @@
// 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: @e = global [5 x i16] zeroinitializer, align 2
// CHECK64: attributes #0 = { "toc-data" }

View File

@@ -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"

View File

@@ -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

View File

@@ -1945,6 +1945,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) {

View File

@@ -87,8 +87,7 @@ void MCSectionXCOFF::printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
if (getKind().isCommon() && !getKind().isBSSLocal())
return;
assert((getKind().isBSSExtern() || getKind().isBSSLocal()) &&
"Unexepected section kind for toc-data");
assert(getKind().isBSS() && "Unexpected section kind for toc-data");
printCsectDirective(OS);
return;
}

View File

@@ -2659,6 +2659,8 @@ void PPCAIXAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
// If the Global Variable has the toc-data attribute, it needs to be emitted
// when we emit the .toc section.
if (GV->hasAttribute("toc-data")) {
unsigned PointerSize = GV->getParent()->getDataLayout().getPointerSize();
Subtarget->tocDataChecks(PointerSize, GV);
TOCDataGlobalVars.push_back(GV);
return;
}

View File

@@ -521,40 +521,6 @@ static bool hasTocDataAttr(SDValue Val, unsigned PointerSize) {
if (!GV->hasAttribute("toc-data"))
return false;
// TODO: These asserts should be updated as more support for the toc data
// transformation is added (struct support, etc.).
assert(
PointerSize >= GV->getAlign().valueOrOne().value() &&
"GlobalVariables with an alignment requirement stricter than TOC entry "
"size not supported by the toc data transformation.");
Type *GVType = GV->getValueType();
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->hasPrivateLinkage())
report_fatal_error("A GlobalVariable with private linkage is not "
"currently supported by the toc data transformation.");
return true;
}

View File

@@ -185,6 +185,28 @@ bool PPCSubtarget::enableSubRegLiveness() const {
return UseSubRegLiveness;
}
void PPCSubtarget::tocDataChecks(unsigned PointerSize,
const GlobalVariable *GV) const {
// TODO: These asserts should be updated as more support for the toc data
// transformation is added (struct support, etc.).
assert(
PointerSize >= GV->getAlign().valueOrOne().value() &&
"GlobalVariables with an alignment requirement stricter than TOC entry "
"size not supported by the toc data transformation.");
Type *GVType = GV->getValueType();
assert(GVType->isSized() && "A GlobalVariable's size must be known to be "
"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->hasPrivateLinkage())
report_fatal_error("A GlobalVariable with private linkage is not "
"currently supported by the toc data transformation.");
}
bool PPCSubtarget::isGVIndirectSymbol(const GlobalValue *GV) const {
// Large code model always uses the TOC even for local symbols.
if (TM.getCodeModel() == CodeModel::Large)

View File

@@ -245,6 +245,8 @@ public:
/// True if the GV will be accessed via an indirect symbol.
bool isGVIndirectSymbol(const GlobalValue *GV) const;
void tocDataChecks(unsigned PointerSize, const GlobalVariable *GV) const;
/// True if the ABI is descriptor based.
bool usesFunctionDescriptors() const {
// Both 32-bit and 64-bit AIX are descriptor based. For ELF only the 64-bit

View File

@@ -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
@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.

View File

@@ -0,0 +1,8 @@
; 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
@a = global [5 x i16] zeroinitializer, align 2 #0
attributes #0 = { "toc-data" }
; CHECK-ERROR: LLVM ERROR: A GlobalVariable with size larger than a TOC entry is not currently supported by the toc data transformation.

View File

@@ -0,0 +1,110 @@
; 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
; RUN: llc -filetype=obj -mtriple powerpc-ibm-aix-xcoff < %s -o %t32.o
; RUN: llvm-readobj %t32.o --syms | FileCheck %s --check-prefix=OBJ32
; RUN: llc -filetype=obj -mtriple powerpc64-ibm-aix-xcoff < %s -o %t64.o
; RUN: llvm-readobj %t64.o --syms | FileCheck %s --check-prefix=OBJ64
%struct.small_struct = type { i16 }
@a = global %struct.small_struct zeroinitializer, align 2 #0
@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
; OBJ32: Symbol {
; OBJ32: Name: a
; OBJ32-NEXT: Value (RelocatableAddress): 0x3C
; OBJ32-NEXT: Section: .data
; OBJ32-NEXT: Type: 0x0
; OBJ32-NEXT: StorageClass: C_EXT (0x2)
; OBJ32-NEXT: NumberOfAuxEntries: 1
; OBJ32-NEXT: CSECT Auxiliary Entry {
; OBJ32-NEXT: Index: {{[0-9]+}}
; OBJ32-NEXT: SectionLen: 2
; OBJ32-NEXT: ParameterHashIndex: 0x0
; OBJ32-NEXT: TypeChkSectNum: 0x0
; OBJ32-NEXT: SymbolAlignmentLog2: 2
; OBJ32-NEXT: SymbolType: XTY_SD (0x1)
; OBJ32-NEXT: StorageMappingClass: XMC_TD (0x10)
; OBJ32-NEXT: StabInfoIndex: 0x0
; OBJ32-NEXT: StabSectNum: 0x0
; OBJ32-NEXT: }
; OBJ32-NEXT: }
; OBJ32-NEXT: Symbol {
; OBJ32: Name: b
; OBJ32-NEXT: Value (RelocatableAddress): 0x40
; OBJ32-NEXT: Section: .data
; OBJ32-NEXT: Type: 0x0
; OBJ32-NEXT: StorageClass: C_EXT (0x2)
; OBJ32-NEXT: NumberOfAuxEntries: 1
; OBJ32-NEXT: CSECT Auxiliary Entry {
; OBJ32-NEXT: Index: {{[0-9]+}}
; OBJ32-NEXT: SectionLen: 4
; OBJ32-NEXT: ParameterHashIndex: 0x0
; OBJ32-NEXT: TypeChkSectNum: 0x0
; OBJ32-NEXT: SymbolAlignmentLog2: 2
; OBJ32-NEXT: SymbolType: XTY_SD (0x1)
; OBJ32-NEXT: StorageMappingClass: XMC_TD (0x10)
; OBJ32-NEXT: StabInfoIndex: 0x0
; OBJ32-NEXT: StabSectNum: 0x0
; OBJ32-NEXT: }
; OBJ32-NEXT: }
; OBJ64: Symbol {
; OBJ64: Name: a
; OBJ64-NEXT: Value (RelocatableAddress): 0x48
; OBJ64-NEXT: Section: .data
; OBJ64-NEXT: Type: 0x0
; OBJ64-NEXT: StorageClass: C_EXT (0x2)
; OBJ64-NEXT: NumberOfAuxEntries: 1
; OBJ64-NEXT: CSECT Auxiliary Entry {
; OBJ64-NEXT: Index: {{[0-9]+}}
; OBJ64-NEXT: SectionLen: 2
; OBJ64-NEXT: ParameterHashIndex: 0x0
; OBJ64-NEXT: TypeChkSectNum: 0x0
; OBJ64-NEXT: SymbolAlignmentLog2: 2
; OBJ64-NEXT: SymbolType: XTY_SD (0x1)
; OBJ64-NEXT: StorageMappingClass: XMC_TD (0x10)
; OBJ64-NEXT: Auxiliary Type: AUX_CSECT (0xFB)
; OBJ64-NEXT: }
; OBJ64-NEXT: }
; OBJ64-NEXT: Symbol {
; OBJ64: Name: b
; OBJ64-NEXT: Value (RelocatableAddress): 0x4C
; OBJ64-NEXT: Section: .data
; OBJ64-NEXT: Type: 0x0
; OBJ64-NEXT: StorageClass: C_EXT (0x2)
; OBJ64-NEXT: NumberOfAuxEntries: 1
; OBJ64-NEXT: CSECT Auxiliary Entry {
; OBJ64-NEXT: Index: {{[0-9]+}}
; OBJ64-NEXT: SectionLen: 4
; OBJ64-NEXT: ParameterHashIndex: 0x0
; OBJ64-NEXT: TypeChkSectNum: 0x0
; OBJ64-NEXT: SymbolAlignmentLog2: 2
; OBJ64-NEXT: SymbolType: XTY_SD (0x1)
; OBJ64-NEXT: StorageMappingClass: XMC_TD (0x10)
; OBJ64-NEXT: Auxiliary Type: AUX_CSECT (0xFB)
; OBJ64-NEXT: }
; OBJ64-NEXT: }