Skip to content

Commit

Permalink
[AIX][TOC] Add -mtocdata/-mno-tocdata options on AIX (#67999)
Browse files Browse the repository at this point in the history
This patch enables support that the XL compiler had for AIX under
-qdatalocal/-qdataimported.
  • Loading branch information
syzaara committed Mar 13, 2024
1 parent e77324d commit 37b5eb0
Show file tree
Hide file tree
Showing 25 changed files with 716 additions and 36 deletions.
61 changes: 61 additions & 0 deletions clang/docs/UsersManual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,15 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// 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.
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
21 changes: 21 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
26 changes: 26 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand Down
59 changes: 59 additions & 0 deletions clang/lib/CodeGen/Targets/PPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "ABIInfoImpl.h"
#include "TargetInfo.h"
#include "clang/Basic/DiagnosticFrontend.h"

using namespace clang;
using namespace clang::CodeGen;
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
87 changes: 87 additions & 0 deletions clang/lib/Driver/ToolChains/AIX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,13 +433,100 @@ 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 {
Args.AddLastArg(CC1Args, options::OPT_mignore_xcoff_visibility);
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");
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Frontend/CompilerInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 37b5eb0

Please sign in to comment.