Index: clang/docs/UsersManual.rst =================================================================== --- clang/docs/UsersManual.rst +++ clang/docs/UsersManual.rst @@ -3156,6 +3156,35 @@ .. _c: +Mark TOC Data / Not TOC Data (AIX-specific) +------------------------------------------- + +The user may specify which global variables they wish to be treated as TOC +data and which they don't. The effects of the toc-data attribute only take +effect on AIX and when the symbol is independently-generated (e.g. not +placed in a pool). +Symbols with external linkage can be referenced in the -mtocdata,-mno-tocdata +options using their mangled names. + +.. option:: -mtocdata= + + Mark the specified global variables as "toc-data" if valid, or emit + diagnostics otherwise. + +.. option:: -mno-tocdata= + + Specifies which variables are execptions to the toc-data trasformation. + +.. option:: -mtocdata + + Mark all global variables not explicitly specified with -mno-tocdata as + toc-data. + +.. option:: -mno-tocdata + + Only explicitly specified variables will be treated as toc-data. + This is the default behaviour. + C Language Features =================== Index: clang/include/clang/Basic/CodeGenOptions.h =================================================================== --- clang/include/clang/Basic/CodeGenOptions.h +++ clang/include/clang/Basic/CodeGenOptions.h @@ -403,6 +403,15 @@ /// List of dynamic shared object files to be loaded as pass plugins. std::vector PassPlugins; + /// List of global variables explicitly specified by the user as toc-data. + std::vector TocDataVarsUserSpecified; + + /// List of global variables that over-ride the toc-data default. + std::vector 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. Index: clang/include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticDriverKinds.td +++ clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -554,6 +554,9 @@ "ignoring '-mgpopt' option as it cannot be used with %select{|the implicit" " usage of }0-mabicalls">, InGroup; +def warn_drv_unsupported_tocdata: Warning< + "ignoring '-mtocdata' with -mcmodel-large, -mcmodel=large">, + InGroup; def warn_drv_unsupported_sdata : Warning< "ignoring '-msmall-data-limit=' with -mcmodel=large for -fpic or RV64">, InGroup; Index: clang/include/clang/Basic/DiagnosticFrontendKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -93,6 +93,8 @@ def warn_fe_backend_warning_attr : Warning<"call to '%0' declared with 'warning' attribute: %1">, BackendInfo, InGroup; +def warn_toc_unsupported_type : Warning<"The -mtocdata option is ignored " + "for %0 because %1.">, InGroup; def err_fe_invalid_code_complete_file : Error< "cannot locate code-completion file %0">, DefaultFatal; Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -2945,6 +2945,19 @@ Group, Flags<[CC1Option,FlangOption,FC1Option]>, MetaVarName<"">, HelpText<"Load pass plugin from a dynamic shared object file (only with new pass manager).">, MarshallingInfoStringVector>; +def mtocdata_EQ : CommaJoined<["-"], "mtocdata=">, + Flags<[CC1Option,TargetSpecific]>, + HelpText<"Specifies which variables can be treated as toc-data">, + MarshallingInfoStringVector>; +def mtocdata : Flag<["-"], "mtocdata">, Flags<[CC1Option,TargetSpecific]>, + HelpText<"All variables can be treated as toc-data">, + MarshallingInfoFlag>; +def mno_tocdata_EQ : CommaJoined<["-"], "mno-tocdata=">, + Flags<[CC1Option,TargetSpecific]>, + HelpText<"Specifies which variables are exempt from the toc-data trasformation">, + MarshallingInfoStringVector>; +def mno_tocdata : Flag<["-"], "mno-tocdata">, Flags<[CC1Option,TargetSpecific]>, + HelpText<"Only explicitly specified variables will be treated as toc-data">; defm preserve_as_comments : BoolFOption<"preserve-as-comments", CodeGenOpts<"PreserveAsmComments">, DefaultTrue, NegFlag, Index: clang/lib/CodeGen/Targets/PPC.cpp =================================================================== --- clang/lib/CodeGen/Targets/PPC.cpp +++ clang/lib/CodeGen/Targets/PPC.cpp @@ -145,6 +145,9 @@ 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 +268,53 @@ 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(GV)) { + auto GVId = GVar->getGlobalIdentifier(); + + // Is this a global variable specified by the user as toc-data? + bool UserSpecifiedTOC = + llvm::binary_search(M.getCodeGenOpts().TocDataVarsUserSpecified, GVId); + 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(D); + assert(VarD && "Invalid declaration of global variable."); + + unsigned Alignment = Context.toBits(Context.getDeclAlign(D)) / 8; + const auto *Ty = VarD->getType().getTypePtr(); + + if (!Ty || Ty->isIncompleteType()) { + if (UserSpecifiedTOC) + M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type) + << GVId << "of incomplete type"; + } else if (VarD->getTLSKind() != VarDecl::TLS_None) { + if (UserSpecifiedTOC) + 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 (UserSpecifiedTOC) + M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type) + << GVId << "variable is larger than a pointer"; + } else if (PointerSize < Alignment) { + if (UserSpecifiedTOC) + M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type) + << GVId << "variable is aligned wider than a pointer"; + } else if (D->hasAttr()) { + if (UserSpecifiedTOC) + M.getDiags().Report(D->getLocation(), diag::warn_toc_unsupported_type) + << GVId << "of a section attribute"; + } else { + GVar->addAttribute("toc-data"); + } + } + } +} + // PowerPC-32 namespace { /// PPC32_SVR4_ABIInfo - The 32-bit PowerPC ELF (SVR4) ABI information. Index: clang/lib/Driver/ToolChains/AIX.cpp =================================================================== --- clang/lib/Driver/ToolChains/AIX.cpp +++ clang/lib/Driver/ToolChains/AIX.cpp @@ -418,10 +418,84 @@ void AIX::addClangTargetOptions( const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CC1Args, Action::OffloadKind DeviceOffloadingKind) const { + const Driver &D = getDriver(); + 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, options::OPT_mno_tocdata)) { + // The default behavior is -mno-tocdata. We need an explicit + // -mtocdata to enable toc-data attribute, and it must be last to take + // effect. + const bool TocDataInEffect = [&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); + }(); + + if (TocDataInEffect && + (Args.getLastArgValue(options::OPT_mcmodel_EQ).equals("large") || + Args.getLastArgValue(options::OPT_mcmodel_EQ).equals("medium"))) { + D.Diag(diag::warn_drv_unsupported_tocdata); + } else { + enum TOCDataOptionVal { + AddressInTOC = 0, // Address of the symbol stored in the TOC. + DataInTOC = 1 // Symbol defined in the TOC. + }; + + const TOCDataOptionVal ExplicitOptionDefaultOverride = + TocDataInEffect ? AddressInTOC : DataInTOC; + + llvm::StringSet<> ExplicitlySpecifiedGlobals; + for (const auto Arg : Args.filtered(options::OPT_mtocdata_EQ, + options::OPT_mno_tocdata_EQ)) { + TOCDataOptionVal OptionType = + Arg->getOption().matches(options::OPT_mtocdata_EQ) ? DataInTOC + : AddressInTOC; + for (const char *Val : Arg->getValues()) { + if (OptionType == ExplicitOptionDefaultOverride) + ExplicitlySpecifiedGlobals.insert(Val); + else + 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; + }; + + if (TocDataInEffect) { + CC1Args.push_back("-mtocdata"); + if (HasExplicitValues) + CC1Args.push_back(Args.MakeArgString(Twine(buildExceptionList( + ExplicitlySpecifiedGlobals, "-mno-tocdata=")))); + } else { + CC1Args.push_back("-mno-tocdata"); + if (HasExplicitValues) + CC1Args.push_back(Args.MakeArgString(Twine( + buildExceptionList(ExplicitlySpecifiedGlobals, "-mtocdata=")))); + } + } + } + if (Args.hasFlag(options::OPT_fxl_pragma_pack, options::OPT_fno_xl_pragma_pack, true)) CC1Args.push_back("-fxl-pragma-pack"); Index: clang/lib/Frontend/CompilerInstance.cpp =================================================================== --- clang/lib/Frontend/CompilerInstance.cpp +++ clang/lib/Frontend/CompilerInstance.cpp @@ -1039,6 +1039,11 @@ 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. Index: llvm/include/llvm/ADT/STLExtras.h =================================================================== --- llvm/include/llvm/ADT/STLExtras.h +++ llvm/include/llvm/ADT/STLExtras.h @@ -1951,6 +1951,19 @@ 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 auto binary_search(R &&Range, T &&Value) { + return std::binary_search(adl_begin(Range), adl_end(Range), + std::forward(Value)); +} + +template +auto binary_search(R &&Range, T &&Value, Compare C) { + return std::binary_search(adl_begin(Range), adl_end(Range), + std::forward(Value), C); +} + /// Provide wrappers to std::lower_bound which take ranges instead of having to /// pass begin/end explicitly. template auto lower_bound(R &&Range, T &&Value) { Index: llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp =================================================================== --- llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp +++ llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp @@ -533,21 +533,11 @@ 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 "