diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -126,6 +126,11 @@ // "none": Disable sections/labels for basic blocks. std::string BBSections; + // If set, overwrite the default value of MCAsmInfo::BinutilsVersion. "future" + // means that features not implemented by any known release can be used. If + // DisableIntegratedAS is specified, this also affects assembly output. + std::string BinutilsVersion; + enum class FramePointerKind { None, // Omit all frame pointers. NonLeaf, // Keep non-leaf frame pointers. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3304,6 +3304,10 @@ def fprofile_dir : Joined<["-"], "fprofile-dir=">, Group; +def fbinutils_version_EQ : Joined<["-"], "fbinutils-version=">, Group, + HelpText<"Produced object files can use ELF features only supported by ld newer than the specified version. If" + "-fno-integrated-as is specified, produced assembly can use newer ELF features. " + "'future' means that features not implemented by any known release can be used">; def fuse_ld_EQ : Joined<["-"], "fuse-ld=">, Group, Flags<[CoreOption, LinkOption]>; def ld_path_EQ : Joined<["--"], "ld-path=">, Group; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -464,6 +464,8 @@ break; } + Options.BinutilsVersion = + llvm::TargetMachine::parseBinutilsVersion(CodeGenOpts.BinutilsVersion); Options.UseInitArray = CodeGenOpts.UseInitArray; Options.DisableIntegratedAS = CodeGenOpts.DisableIntegratedAS; Options.CompressDebugSections = CodeGenOpts.getCompressDebugSections(); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4635,6 +4635,21 @@ IsIntegratedAssemblerDefault)) CmdArgs.push_back("-fno-verbose-asm"); + // Parse 'future' or '$major.$minor'. + if (Arg *A = Args.getLastArg(options::OPT_fbinutils_version_EQ)) { + StringRef V = A->getValue(); + int Num; + if (V == "future") + A->render(Args, CmdArgs); + else if (!V.consumeInteger(10, Num) && + (V.empty() || (V.consume_front(".") && + !V.consumeInteger(10, Num) && V.empty()))) + A->render(Args, CmdArgs); + else + D.Diag(diag::err_drv_invalid_argument_to_option) + << A->getValue() << A->getOption().getName(); + } + if (!TC.useIntegratedAs()) CmdArgs.push_back("-no-integrated-as"); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -859,6 +859,8 @@ (Opts.OptimizationLevel > 1)); Opts.RerollLoops = Args.hasArg(OPT_freroll_loops); + Opts.BinutilsVersion = + std::string(Args.getLastArgValue(OPT_fbinutils_version_EQ)); Opts.DisableIntegratedAS = Args.hasArg(OPT_fno_integrated_as); Opts.Autolink = !Args.hasArg(OPT_fno_autolink); Opts.SampleProfileFile = diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -384,6 +384,10 @@ //===--- Integrated Assembler Information ----------------------------===// + // Generated object files can only use features support by GNU ld of this + // binutils version. + std::pair BinutilsVersion = {2, 26}; + /// Should we use the integrated assembler? /// The integrated assembler should be enabled by default (by the /// constructors) when failing to parse a valid piece of assembly (inline @@ -649,9 +653,18 @@ return InitialFrameState; } + void setBinutilsVersion(std::pair Value) { + BinutilsVersion = Value; + } + /// Return true if assembly (inline or otherwise) should be parsed. bool useIntegratedAssembler() const { return UseIntegratedAssembler; } + bool useIntegratedASOrGAS(int Major, int Minor) const { + return UseIntegratedAssembler || + BinutilsVersion >= std::make_pair(Major, Minor); + } + /// Set whether assembly (inline or otherwise) should be parsed. virtual void setUseIntegratedAssembler(bool Value) { UseIntegratedAssembler = Value; diff --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h --- a/llvm/include/llvm/Target/TargetMachine.h +++ b/llvm/include/llvm/Target/TargetMachine.h @@ -332,6 +332,8 @@ /// The integer bit size to use for SjLj based exception handling. static constexpr unsigned DefaultSjLjDataSize = 32; virtual unsigned getSjLjDataSize() const { return DefaultSjLjDataSize; } + + static std::pair parseBinutilsVersion(StringRef Version); }; /// This class describes a target machine that is implemented with the LLVM diff --git a/llvm/include/llvm/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h --- a/llvm/include/llvm/Target/TargetOptions.h +++ b/llvm/include/llvm/Target/TargetOptions.h @@ -134,6 +134,9 @@ /// optimization should be disabled for the given machine function. bool DisableFramePointerElim(const MachineFunction &MF) const; + enum { FutureBinutilsVersion = 99 }; + std::pair BinutilsVersion{0, 0}; + /// UnsafeFPMath - This flag is enabled when the /// -enable-unsafe-fp-math flag is specified on the command line. When /// this flag is off (the default), the code generator is not allowed to diff --git a/llvm/lib/CodeGen/LLVMTargetMachine.cpp b/llvm/lib/CodeGen/LLVMTargetMachine.cpp --- a/llvm/lib/CodeGen/LLVMTargetMachine.cpp +++ b/llvm/lib/CodeGen/LLVMTargetMachine.cpp @@ -58,6 +58,9 @@ "Make sure you include the correct TargetSelect.h" "and that InitializeAllTargetMCs() is being invoked!"); + if (Options.BinutilsVersion.first > 0) + TmpAsmInfo->setBinutilsVersion(Options.BinutilsVersion); + if (Options.DisableIntegratedAS) TmpAsmInfo->setUseIntegratedAssembler(false); diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -684,7 +684,7 @@ UniqueID = NextUniqueID++; Flags |= ELF::SHF_LINK_ORDER; } else { - if (getContext().getAsmInfo()->useIntegratedAssembler()) { + if (getContext().getAsmInfo()->useIntegratedASOrGAS(2, 35)) { // Symbols must be placed into sections with compatible entry // sizes. Generate unique sections for symbols that have not // been assigned to compatible sections. @@ -735,8 +735,8 @@ assert(Section->getLinkedToSymbol() == LinkedToSym && "Associated symbol mismatch between sections"); - if (!getContext().getAsmInfo()->useIntegratedAssembler()) { - // If we are not using the integrated assembler then this symbol might have + if (!getContext().getAsmInfo()->useIntegratedASOrGAS(2, 35)) { + // If we are using GNU as before 2.35, then this symbol might have // been placed in an incompatible mergeable section. Emit an error if this // is the case to avoid creating broken output. if ((Section->getFlags() & ELF::SHF_MERGE) && diff --git a/llvm/lib/Target/TargetMachine.cpp b/llvm/lib/Target/TargetMachine.cpp --- a/llvm/lib/Target/TargetMachine.cpp +++ b/llvm/lib/Target/TargetMachine.cpp @@ -281,3 +281,12 @@ return TargetIRAnalysis( [this](const Function &F) { return this->getTargetTransformInfo(F); }); } + +std::pair TargetMachine::parseBinutilsVersion(StringRef Version) { + if (Version == "future") + return {(int)TargetOptions::FutureBinutilsVersion, 0}; + std::pair Ret; + if (!Version.consumeInteger(10, Ret.first) && Version.consume_front(".")) + Version.consumeInteger(10, Ret.second); + return Ret; +} diff --git a/llvm/test/CodeGen/X86/explicit-section-mergeable.ll b/llvm/test/CodeGen/X86/explicit-section-mergeable.ll --- a/llvm/test/CodeGen/X86/explicit-section-mergeable.ll +++ b/llvm/test/CodeGen/X86/explicit-section-mergeable.ll @@ -282,15 +282,19 @@ ;; --no-integrated-as avoids the use of ",unique," for compatibility with older binutils. ;; Error if an incompatible symbol is explicitly placed into a mergeable section. -; RUN: not llc < %s -mtriple=x86_64 --no-integrated-as 2>&1 \ +; RUN: not llc < %s -mtriple=x86_64 --no-integrated-as -binutils-version=2.34 2>&1 \ ; RUN: | FileCheck %s --check-prefix=NO-I-AS-ERR ; NO-I-AS-ERR: error: Symbol 'explicit_default_1' from module '' required a section with entry-size=0 but was placed in section '.rodata.cst16' with entry-size=16: Explicit assignment by pragma or attribute of an incompatible symbol to this section? ; NO-I-AS-ERR: error: Symbol 'explicit_default_4' from module '' required a section with entry-size=0 but was placed in section '.debug_str' with entry-size=1: Explicit assignment by pragma or attribute of an incompatible symbol to this section? ; NO-I-AS-ERR: error: Symbol 'explicit_implicit_2' from module '' required a section with entry-size=0 but was placed in section '.rodata.str1.1' with entry-size=1: Explicit assignment by pragma or attribute of an incompatible symbol to this section? ; NO-I-AS-ERR: error: Symbol 'explicit_implicit_4' from module '' required a section with entry-size=0 but was placed in section '.rodata.str1.1' with entry-size=1: Explicit assignment by pragma or attribute of an incompatible symbol to this section? +;; For GNU as before 2.35, ;; Don't create mergeable sections for globals with an explicit section name. ; RUN: echo '@explicit = unnamed_addr constant [2 x i16] [i16 1, i16 1], section ".explicit"' > %t.no_i_as.ll -; RUN: llc < %t.no_i_as.ll -mtriple=x86_64 --no-integrated-as 2>&1 \ -; RUN: | FileCheck %s --check-prefix=NO-I-AS -; NO-I-AS: .section .explicit,"a",@progbits +; RUN: llc < %t.no_i_as.ll -mtriple=x86_64 --no-integrated-as -binutils-version=2.34 2>&1 \ +; RUN: | FileCheck %s --check-prefix=NO-I-AS-OLD +; NO-I-AS-OLD: .section .explicit,"a",@progbits +; RUN: llc < %t.no_i_as.ll -mtriple=x86_64 --no-integrated-as -binutils-version=2.35 2>&1 \ +; RUN: | FileCheck %s --check-prefix=NO-I-AS-NEW +; NO-I-AS-NEW: .section .explicit,"aM",@progbits,4,unique,1 diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp --- a/llvm/tools/llc/llc.cpp +++ b/llvm/tools/llc/llc.cpp @@ -81,6 +81,14 @@ cl::value_desc("N"), cl::desc("Repeat compilation N times for timing")); +static cl::opt BinutilsVersion( + "binutils-version", cl::Hidden, + cl::desc( + "Used ELF features need to be supported by GNU ld " + "from the specified binutils version. 'future' means that " + "features not implemented by any known release can be used. If " + "-no-integrated-as is specified, this also affects assembly output")); + static cl::opt NoIntegratedAssembler("no-integrated-as", cl::Hidden, cl::desc("Disable integrated assembler")); @@ -425,6 +433,8 @@ } TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(); + Options.BinutilsVersion = + TargetMachine::parseBinutilsVersion(BinutilsVersion); Options.DisableIntegratedAS = NoIntegratedAssembler; Options.MCOptions.ShowMCEncoding = ShowMCEncoding; Options.MCOptions.MCUseDwarfDirectory = EnableDwarfDirectory;