diff --git a/clang/include/clang/Driver/Options.h b/clang/include/clang/Driver/Options.h --- a/clang/include/clang/Driver/Options.h +++ b/clang/include/clang/Driver/Options.h @@ -34,10 +34,11 @@ LinkOption = (1 << 13), FlangOption = (1 << 14), FC1Option = (1 << 15), - FlangOnlyOption = (1 << 16), - DXCOption = (1 << 17), - CLDXCOption = (1 << 18), - Ignored = (1 << 19), + FC1AsOption = (1 << 16), + FlangOnlyOption = (1 << 17), + DXCOption = (1 << 18), + CLDXCOption = (1 << 19), + Ignored = (1 << 20), }; enum ID { 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 @@ -75,6 +75,9 @@ // FC1Option - This option should be accepted by flang -fc1. def FC1Option : OptionFlag; +// FC1AsOption - This option should be accepted by flang -fc1as. +def FC1AsOption : OptionFlag; + // A short name to show in documentation. The name will be interpreted as rST. class DocName { string DocName = name; } @@ -696,7 +699,7 @@ HelpText<"Restrict all prior -I flags to double-quoted inclusion and " "remove current directory from include path">; def I : JoinedOrSeparate<["-"], "I">, Group, - Flags<[CC1Option,CC1AsOption,FlangOption,FC1Option]>, MetaVarName<"">, + Flags<[CC1Option,CC1AsOption,FlangOption,FC1AsOption,FC1Option]>, MetaVarName<"">, HelpText<"Add directory to the end of the list of include search paths">, DocBrief<[{Add directory to include search path. For C++ inputs, if there are multiple -I options, these directories are searched @@ -3300,7 +3303,7 @@ Flags<[NoXarchOption]>, HelpText<"Restore the default behavior of not embedding source text in DWARF debug sections">; def headerpad__max__install__names : Joined<["-"], "headerpad_max_install_names">; -def help : Flag<["-", "--"], "help">, Flags<[CC1Option,CC1AsOption, FC1Option, +def help : Flag<["-", "--"], "help">, Flags<[CC1Option,CC1AsOption, FC1Option, FC1AsOption, FlangOption]>, HelpText<"Display available options">, MarshallingInfoFlag>; def ibuiltininc : Flag<["-"], "ibuiltininc">, @@ -3475,7 +3478,7 @@ def mkernel : Flag<["-"], "mkernel">, Group; def mlinker_version_EQ : Joined<["-"], "mlinker-version=">, Flags<[NoXarchOption]>; -def mllvm : Separate<["-"], "mllvm">,Flags<[CC1Option,CC1AsOption,CoreOption,FC1Option,FlangOption]>, +def mllvm : Separate<["-"], "mllvm">,Flags<[CC1Option,CC1AsOption,CoreOption,FC1Option,FC1AsOption,FlangOption]>, HelpText<"Additional arguments to forward to LLVM's option processing">, MarshallingInfoStringVector>; def : Joined<["-"], "mllvm=">, Flags<[CoreOption,FlangOption]>, Alias, @@ -4127,7 +4130,7 @@ def nostdlibxx : Flag<["-"], "nostdlib++">; def object : Flag<["-"], "object">; def o : JoinedOrSeparate<["-"], "o">, Flags<[NoXarchOption, - CC1Option, CC1AsOption, FC1Option, FlangOption]>, + CC1Option, CC1AsOption, FC1Option, FC1AsOption, FlangOption]>, HelpText<"Write output to ">, MetaVarName<"">, MarshallingInfoString>; def object_file_name_EQ : Joined<["-"], "object-file-name=">, Flags<[CC1Option, CC1AsOption, CoreOption]>, @@ -5166,7 +5169,7 @@ // Target Options (cc1 + cc1as + fc1) //===----------------------------------------------------------------------===// -let Flags = [CC1Option, CC1AsOption, FC1Option, NoDriverOption] in { +let Flags = [CC1Option, CC1AsOption, FC1Option, FC1AsOption, NoDriverOption] in { def target_cpu : Separate<["-"], "target-cpu">, HelpText<"Target a specific cpu type">, @@ -5179,7 +5182,7 @@ MarshallingInfoString, "llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple())">, AlwaysEmit, Normalizer<"normalizeTriple">; -} // let Flags = [CC1Option, CC1ASOption, FC1Option, NoDriverOption] +} // let Flags = [CC1Option, CC1ASOption, FC1Option, FC1AsOption, NoDriverOption] //===----------------------------------------------------------------------===// // Target Options (other) @@ -5372,7 +5375,7 @@ // CodeGen Options //===----------------------------------------------------------------------===// -let Flags = [CC1Option, CC1AsOption, FC1Option, NoDriverOption] in { +let Flags = [CC1Option, CC1AsOption, FC1Option, FC1AsOption, NoDriverOption] in { def mrelocation_model : Separate<["-"], "mrelocation-model">, HelpText<"The relocation model to use">, Values<"static,pic,ropi,rwpi,ropi-rwpi,dynamic-no-pic">, @@ -5380,7 +5383,7 @@ NormalizedValues<["Static", "PIC_", "ROPI", "RWPI", "ROPI_RWPI", "DynamicNoPIC"]>, MarshallingInfoEnum, "PIC_">; -} // let Flags = [CC1Option, CC1AsOption, FC1Option, NoDriverOption] +} // let Flags = [CC1Option, CC1AsOption, FC1Option, FC1AsOption, NoDriverOption] let Flags = [CC1Option, CC1AsOption, NoDriverOption] in { @@ -6124,7 +6127,7 @@ def version : Flag<["-"], "version">, HelpText<"Print the compiler version">, - Flags<[CC1Option, CC1AsOption, FC1Option, NoDriverOption]>, + Flags<[CC1Option, CC1AsOption, FC1Option, FC1AsOption, NoDriverOption]>, MarshallingInfoFlag>; def main_file_name : Separate<["-"], "main-file-name">, @@ -6511,7 +6514,7 @@ // cc1as-only Options //===----------------------------------------------------------------------===// -let Flags = [CC1AsOption, NoDriverOption] in { +let Flags = [CC1AsOption, FC1AsOption, NoDriverOption] in { // Language Options def n : Flag<["-"], "n">, @@ -6536,7 +6539,7 @@ def defsym : Separate<["-"], "defsym">, HelpText<"Define a value for a symbol">; -} // let Flags = [CC1AsOption] +} // let Flags = [CC1AsOption, FC1AsOption, NoDriverOption] //===----------------------------------------------------------------------===// // clang-cl Options diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h --- a/clang/include/clang/Driver/ToolChain.h +++ b/clang/include/clang/Driver/ToolChain.h @@ -166,6 +166,7 @@ Tool *getStaticLibTool() const; Tool *getIfsMerge() const; Tool *getClangAs() const; + Tool *getFlangAs() const; Tool *getOffloadBundler() const; Tool *getOffloadPackager() const; Tool *getLinkerWrapper() const; diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -363,6 +363,12 @@ return Assemble.get(); } +Tool *ToolChain::getFlangAs() const { + if (!Assemble) + Assemble.reset(new tools::FlangAs(*this)); + return Assemble.get(); +} + Tool *ToolChain::getLink() const { if (!Link) Link.reset(buildLinker()); @@ -652,8 +658,12 @@ if (D.IsFlangMode() && getDriver().ShouldUseFlangCompiler(JA)) return getFlang(); if (getDriver().ShouldUseClangCompiler(JA)) return getClang(); Action::ActionClass AC = JA.getKind(); - if (AC == Action::AssembleJobClass && useIntegratedAs()) + if (AC == Action::AssembleJobClass && useIntegratedAs()) { + if (D.IsFlangMode()) + return getFlangAs(); return getClangAs(); + } + return getTool(AC); } diff --git a/clang/lib/Driver/ToolChains/Flang.h b/clang/lib/Driver/ToolChains/Flang.h --- a/clang/lib/Driver/ToolChains/Flang.h +++ b/clang/lib/Driver/ToolChains/Flang.h @@ -79,6 +79,22 @@ const char *LinkingOutput) const override; }; +/// Flang integrated assembler tool. +class LLVM_LIBRARY_VISIBILITY FlangAs : public Tool { +public: + FlangAs(const ToolChain &TC) + : Tool("flang::as", "flang integrated assembler", TC) {} + void AddX86TargetArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; + bool hasIntegratedAssembler() const override { return false; } + bool hasIntegratedCPP() const override { return false; } + + void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const override; +}; + } // end namespace tools } // end namespace driver diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -11,6 +11,7 @@ #include "CommonArgs.h" #include "clang/Driver/Options.h" +#include "llvm/Support/VirtualFileSystem.h" #include @@ -357,3 +358,143 @@ Flang::Flang(const ToolChain &TC) : Tool("flang-new", "flang frontend", TC) {} Flang::~Flang() {} + +// Begin FlangAs + +void FlangAs::AddX86TargetArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + addX86AlignBranchArgs(getToolChain().getDriver(), Args, CmdArgs, + /*IsLTO=*/false); + + if (Arg *A = Args.getLastArg(options::OPT_masm_EQ)) { + StringRef Value = A->getValue(); + if (Value == "intel" || Value == "att") { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back(Args.MakeArgString("-x86-asm-syntax=" + Value)); + } else { + getToolChain().getDriver().Diag(diag::err_drv_unsupported_option_argument) + << A->getSpelling() << Value; + } + } +} + +void FlangAs::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const ArgList &Args, + const char *LinkingOutput) const { + ArgStringList CmdArgs; + + assert(Inputs.size() == 1 && "Unexpected number of inputs."); + const InputInfo &Input = Inputs[0]; + + const llvm::Triple &Triple = getToolChain().getEffectiveTriple(); + const std::string &TripleStr = Triple.getTriple(); + const auto &D = getToolChain().getDriver(); + + // Don't warn about "clang -w -c foo.s" + Args.ClaimAllArgs(options::OPT_w); + // and "clang -emit-llvm -c foo.s" + Args.ClaimAllArgs(options::OPT_emit_llvm); + + claimNoWarnArgs(Args); + + // Invoke ourselves in -cc1as mode. + // + // FIXME: Implement custom jobs for internal actions. + CmdArgs.push_back("-fc1as"); + + // Add the "effective" target triple. + CmdArgs.push_back("-triple"); + CmdArgs.push_back(Args.MakeArgString(TripleStr)); + + getToolChain().addClangCC1ASTargetOptions(Args, CmdArgs); + + // Set the output mode, we currently only expect to be used as a real + // assembler. + CmdArgs.push_back("-filetype"); + CmdArgs.push_back("obj"); + + // Add the target cpu + std::string CPU = getCPUName(D, Args, Triple, /*FromAs*/ true); + if (!CPU.empty()) { + CmdArgs.push_back("-target-cpu"); + CmdArgs.push_back(Args.MakeArgString(CPU)); + } + + // Add the target features + getTargetFeatures(D, Triple, Args, CmdArgs, true); + + // Ignore explicit -force_cpusubtype_ALL option. + (void)Args.hasArg(options::OPT_force__cpusubtype__ALL); + + // Pass along any -I options so we get proper .include search paths. + Args.AddAllArgs(CmdArgs, options::OPT_I_Group); + + // Handle -fPIC et al -- the relocation-model affects the assembler + // for some targets. + llvm::Reloc::Model RelocationModel; + unsigned PICLevel; + bool IsPIE; + std::tie(RelocationModel, PICLevel, IsPIE) = + ParsePICArgs(getToolChain(), Args); + + const char *RMName = RelocationModelName(RelocationModel); + if (RMName) { + CmdArgs.push_back("-mrelocation-model"); + CmdArgs.push_back(RMName); + } + + // Add target specific flags. + switch (getToolChain().getArch()) { + default: + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + AddX86TargetArgs(Args, CmdArgs); + break; + + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + // This isn't in AddARMTargetArgs because we want to do this for assembly + // only, not C/C++. + if (Args.hasFlag(options::OPT_mdefault_build_attributes, + options::OPT_mno_default_build_attributes, true)) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-arm-add-build-attributes"); + } + break; + + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + case llvm::Triple::aarch64_be: + if (Args.hasArg(options::OPT_mmark_bti_property)) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-aarch64-mark-bti-property"); + } + break; + } + + Args.ClaimAllArgs(options::OPT_W_Group); + + assert(Output.isFilename() && "Unexpected lipo output."); + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + + assert(Input.isFilename() && "Invalid input."); + CmdArgs.push_back(Input.getFilename()); + + const char *Exec = getToolChain().getDriver().getClangProgramPath(); + if (D.CC1Main && !D.CCGenDiagnostics) { + // Invoke cc1as directly in this process. + C.addCommand(std::make_unique(JA, *this, + ResponseFileSupport::AtFileUTF8(), + Exec, CmdArgs, Inputs, Output)); + } else { + C.addCommand(std::make_unique(JA, *this, + ResponseFileSupport::AtFileUTF8(), + Exec, CmdArgs, Inputs, Output)); + } +} diff --git a/flang/test/Driver/asm-code-gen-x86.s b/flang/test/Driver/asm-code-gen-x86.s new file mode 100644 --- /dev/null +++ b/flang/test/Driver/asm-code-gen-x86.s @@ -0,0 +1,12 @@ +// Test -emit-obj (X86) + +// RUN: %flang -c %s -o - \ +// RUN: | llvm-objdump -d - | FileCheck %s +// RUN: %flang_fc1as -triple x86_64-pc-linux-gnu -filetype obj %s -o - \ +// RUN: | llvm-objdump -d - | FileCheck %s + +// CHECK: retq + +.globl main +main: + ret diff --git a/flang/test/lit.cfg.py b/flang/test/lit.cfg.py --- a/flang/test/lit.cfg.py +++ b/flang/test/lit.cfg.py @@ -28,7 +28,7 @@ config.suffixes = ['.c', '.cpp', '.f', '.F', '.ff', '.FOR', '.for', '.f77', '.f90', '.F90', '.ff90', '.f95', '.F95', '.ff95', '.fpp', '.FPP', '.cuf' '.CUF', '.f18', '.F18', '.f03', '.F03', '.f08', '.F08', - '.ll', '.fir', '.mlir'] + '.ll', '.fir', '.mlir', '.s', '.S'] config.substitutions.append(('%PATH%', config.environment['PATH'])) config.substitutions.append(('%llvmshlibdir', config.llvm_shlib_dir)) @@ -90,9 +90,11 @@ # For each occurrence of a flang tool name, replace it with the full path to # the build directory holding that tool. tools = [ - ToolSubst('%flang', command=FindTool('flang-new'), unresolved='fatal'), + ToolSubst('%flang', command=FindTool('flang-new'), unresolved='fatal'), ToolSubst('%flang_fc1', command=FindTool('flang-new'), extra_args=['-fc1'], - unresolved='fatal')] + unresolved='fatal'), + ToolSubst('%flang_fc1as', command=FindTool('flang-new'), extra_args=['-fc1as'], + unresolved='fatal')] # Flang has several unimplemented features. TODO messages are used to mark and fail if these # features are exercised. TODOs exit with an error in non-assert builds but in assert builds diff --git a/flang/tools/flang-driver/CMakeLists.txt b/flang/tools/flang-driver/CMakeLists.txt --- a/flang/tools/flang-driver/CMakeLists.txt +++ b/flang/tools/flang-driver/CMakeLists.txt @@ -14,6 +14,7 @@ add_flang_tool(flang-new driver.cpp fc1_main.cpp + fc1as_main.cpp DEPENDS # These libraries are used in the linker invocation generated by the driver @@ -32,8 +33,12 @@ clang_target_link_libraries(flang-new PRIVATE - clangDriver clangBasic + clangCodeGen + clangDriver + clangFrontend + clangFrontendTool + clangSerialization ) option(FLANG_PLUGIN_SUPPORT "Build Flang with plugin support." ON) diff --git a/flang/tools/flang-driver/driver.cpp b/flang/tools/flang-driver/driver.cpp --- a/flang/tools/flang-driver/driver.cpp +++ b/flang/tools/flang-driver/driver.cpp @@ -35,6 +35,8 @@ // main frontend method. Lives inside fc1_main.cpp extern int fc1_main(llvm::ArrayRef argv, const char *argv0); +extern int fc1as_main(llvm::ArrayRef argv, const char *argv0); + std::string getExecutablePath(const char *argv0) { // This just needs to be some symbol in the binary void *p = (void *)(intptr_t)getExecutablePath; @@ -64,6 +66,8 @@ llvm::StringRef tool = argV[1]; if (tool == "-fc1") return fc1_main(llvm::ArrayRef(argV).slice(2), argV[0]); + if (tool == "-fc1as") + return fc1as_main(llvm::ArrayRef(argV).slice(2), argV[0]); // Reject unknown tools. // ATM it only supports fc1. Any fc1[*] is rejected. diff --git a/flang/tools/flang-driver/fc1as_main.cpp b/flang/tools/flang-driver/fc1as_main.cpp new file mode 100644 --- /dev/null +++ b/flang/tools/flang-driver/fc1as_main.cpp @@ -0,0 +1,672 @@ +//===-- fc1as_main.cpp - Flang Assembler ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the flang -fc1as functionality, which implements +// the direct interface to the LLVM MC based assembler. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +using namespace clang; +using namespace clang::driver; +using namespace clang::driver::options; +using namespace llvm; +using namespace llvm::opt; + +namespace { + +/// Helper class for representing a single invocation of the assembler. +struct AssemblerInvocation { + /// @name Target Options + /// @{ + + /// The name of the target triple to assemble for. + std::string triple; + + /// If given, the name of the target CPU to determine which instructions + /// are legal. + std::string cpu; + + /// The list of target specific features to enable or disable -- this should + /// be a list of strings starting with '+' or '-'. + std::vector features; + + /// The list of symbol definitions. + std::vector symbolDefs; + + /// @} + /// @name Language Options + /// @{ + + std::vector includePaths; + unsigned noInitialTextSection : 1; + unsigned saveTemporaryLabels : 1; + unsigned genDwarfForAssembly : 1; + unsigned relaxElfRelocations : 1; + unsigned dwarf64 : 1; + unsigned dwarfVersion; + std::string dwarfDebugFlags; + std::string dwarfDebugProducer; + std::string debugCompilationDir; + std::map debugPrefixMap; + llvm::DebugCompressionType compressDebugSections = + llvm::DebugCompressionType::None; + std::string mainFileName; + std::string splitDwarfOutput; + + /// @} + /// @name Frontend Options + /// @{ + + std::string inputFile; + std::vector llvmArgs; + std::string outputPath; + enum FileType { + FT_Asm, ///< Assembly (.s) output, transliterate mode. + FT_Null, ///< No output, for timing purposes. + FT_Obj ///< Object file output. + }; + FileType outputType; + unsigned showHelp : 1; + unsigned showVersion : 1; + + /// @} + /// @name Transliterate Options + /// @{ + + unsigned outputAsmVariant; + unsigned showEncoding : 1; + unsigned showInst : 1; + + /// @} + /// @name Assembler Options + /// @{ + + unsigned relaxAll : 1; + unsigned noExecStack : 1; + unsigned fatalWarnings : 1; + unsigned noWarn : 1; + unsigned noTypeCheck : 1; + unsigned incrementalLinkerCompatible : 1; + unsigned embedBitcode : 1; + + /// Whether to emit DWARF unwind info. + EmitDwarfUnwindType emitDwarfUnwind; + + /// The name of the relocation model to use. + std::string relocationModel; + + /// The ABI targeted by the backend. Specified using -target-abi. Empty + /// otherwise. + std::string targetAbi; + + /// Darwin target variant triple, the variant of the deployment target + /// for which the code is being compiled. + std::optional darwinTargetVariantTriple; + + /// The version of the darwin target variant SDK which was used during the + /// compilation + llvm::VersionTuple darwinTargetVariantSdkVersion; + + /// The name of a file to use with \c .secure_log_unique directives. + std::string asSecureLogFile; + /// @} + +public: + AssemblerInvocation() { + triple = ""; + noInitialTextSection = 0; + inputFile = "-"; + outputPath = "-"; + outputType = FT_Asm; + outputAsmVariant = 0; + showInst = 0; + showEncoding = 0; + relaxAll = 0; + noExecStack = 0; + fatalWarnings = 0; + noWarn = 0; + noTypeCheck = 0; + incrementalLinkerCompatible = 0; + dwarf64 = 0; + dwarfVersion = 0; + embedBitcode = 0; + emitDwarfUnwind = EmitDwarfUnwindType::Default; + } + + static bool createFromArgs(AssemblerInvocation &res, + ArrayRef argv, + DiagnosticsEngine &diags); +}; + +} // namespace + +bool AssemblerInvocation::createFromArgs(AssemblerInvocation &opts, + ArrayRef argv, + DiagnosticsEngine &diags) { + bool success = true; + + // Parse the arguments. + const OptTable &optTbl = getDriverOptTable(); + + const unsigned includedFlagsBitmask = options::FC1AsOption; + unsigned missingArgIndex, missingArgCount; + InputArgList args = optTbl.ParseArgs(argv, missingArgIndex, missingArgCount, + includedFlagsBitmask); + + // Check for missing argument error. + if (missingArgCount) { + diags.Report(diag::err_drv_missing_argument) + << args.getArgString(missingArgIndex) << missingArgCount; + success = false; + } + + // Issue errors on unknown arguments. + for (const Arg *a : args.filtered(OPT_UNKNOWN)) { + auto argString = a->getAsString(args); + std::string nearest; + if (optTbl.findNearest(argString, nearest, includedFlagsBitmask) > 1) + diags.Report(diag::err_drv_unknown_argument) << argString; + else + diags.Report(diag::err_drv_unknown_argument_with_suggestion) + << argString << nearest; + success = false; + } + + // Construct the invocation. + + // Target Options + opts.triple = llvm::Triple::normalize(args.getLastArgValue(OPT_triple)); + if (Arg *a = args.getLastArg(options::OPT_darwin_target_variant_triple)) + opts.darwinTargetVariantTriple = llvm::Triple(a->getValue()); + if (Arg *a = args.getLastArg(OPT_darwin_target_variant_sdk_version_EQ)) { + VersionTuple version; + if (version.tryParse(a->getValue())) + diags.Report(diag::err_drv_invalid_value) + << a->getAsString(args) << a->getValue(); + else + opts.darwinTargetVariantSdkVersion = version; + } + + opts.cpu = std::string(args.getLastArgValue(OPT_target_cpu)); + opts.features = args.getAllArgValues(OPT_target_feature); + + // Use the default target triple if unspecified. + if (opts.triple.empty()) + opts.triple = llvm::sys::getDefaultTargetTriple(); + + // Language Options + opts.includePaths = args.getAllArgValues(OPT_I); + opts.noInitialTextSection = args.hasArg(OPT_n); + opts.saveTemporaryLabels = args.hasArg(OPT_msave_temp_labels); + // Any DebugInfoKind implies GenDwarfForAssembly. + opts.genDwarfForAssembly = args.hasArg(OPT_debug_info_kind_EQ); + + if (const Arg *a = args.getLastArg(OPT_compress_debug_sections_EQ)) { + opts.compressDebugSections = + llvm::StringSwitch(a->getValue()) + .Case("none", llvm::DebugCompressionType::None) + .Case("zlib", llvm::DebugCompressionType::Zlib) + .Case("zstd", llvm::DebugCompressionType::Zstd) + .Default(llvm::DebugCompressionType::None); + } + + opts.relaxElfRelocations = !args.hasArg(OPT_mrelax_relocations_no); + if (auto *dwarfFormatArg = args.getLastArg(OPT_gdwarf64, OPT_gdwarf32)) + opts.dwarf64 = dwarfFormatArg->getOption().matches(OPT_gdwarf64); + opts.dwarfVersion = getLastArgIntValue(args, OPT_dwarf_version_EQ, 2, diags); + opts.dwarfDebugFlags = + std::string(args.getLastArgValue(OPT_dwarf_debug_flags)); + opts.dwarfDebugProducer = + std::string(args.getLastArgValue(OPT_dwarf_debug_producer)); + if (const Arg *a = args.getLastArg(options::OPT_ffile_compilation_dir_EQ, + options::OPT_fdebug_compilation_dir_EQ)) + opts.debugCompilationDir = a->getValue(); + opts.mainFileName = std::string(args.getLastArgValue(OPT_main_file_name)); + + for (const auto &arg : args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) { + auto split = StringRef(arg).split('='); + opts.debugPrefixMap.insert( + {std::string(split.first), std::string(split.second)}); + } + + // Frontend Options + if (args.hasArg(OPT_INPUT)) { + bool first = true; + for (const Arg *a : args.filtered(OPT_INPUT)) { + if (first) { + opts.inputFile = a->getValue(); + first = false; + } else { + diags.Report(diag::err_drv_unknown_argument) << a->getAsString(args); + success = false; + } + } + } + opts.llvmArgs = args.getAllArgValues(OPT_mllvm); + opts.outputPath = std::string(args.getLastArgValue(OPT_o)); + opts.splitDwarfOutput = + std::string(args.getLastArgValue(OPT_split_dwarf_output)); + if (Arg *a = args.getLastArg(OPT_filetype)) { + StringRef name = a->getValue(); + unsigned outputType = StringSwitch(name) + .Case("asm", FT_Asm) + .Case("null", FT_Null) + .Case("obj", FT_Obj) + .Default(~0U); + if (outputType == ~0U) { + diags.Report(diag::err_drv_invalid_value) << a->getAsString(args) << name; + success = false; + } else + opts.outputType = FileType(outputType); + } + opts.showHelp = args.hasArg(OPT_help); + opts.showVersion = args.hasArg(OPT_version); + + // Transliterate Options + opts.outputAsmVariant = + getLastArgIntValue(args, OPT_output_asm_variant, 0, diags); + opts.showEncoding = args.hasArg(OPT_show_encoding); + opts.showInst = args.hasArg(OPT_show_inst); + + // Assemble Options + opts.relaxAll = args.hasArg(OPT_mrelax_all); + opts.noExecStack = args.hasArg(OPT_mno_exec_stack); + opts.fatalWarnings = args.hasArg(OPT_massembler_fatal_warnings); + opts.noWarn = args.hasArg(OPT_massembler_no_warn); + opts.noTypeCheck = args.hasArg(OPT_mno_type_check); + opts.relocationModel = + std::string(args.getLastArgValue(OPT_mrelocation_model, "pic")); + opts.targetAbi = std::string(args.getLastArgValue(OPT_target_abi)); + opts.incrementalLinkerCompatible = + args.hasArg(OPT_mincremental_linker_compatible); + opts.symbolDefs = args.getAllArgValues(OPT_defsym); + + // EmbedBitcode Option. If -fembed-bitcode is enabled, set the flag. + // EmbedBitcode behaves the same for all embed options for assembly files. + if (auto *a = args.getLastArg(OPT_fembed_bitcode_EQ)) { + opts.embedBitcode = llvm::StringSwitch(a->getValue()) + .Case("all", 1) + .Case("bitcode", 1) + .Case("marker", 1) + .Default(0); + } + + if (auto *a = args.getLastArg(OPT_femit_dwarf_unwind_EQ)) { + opts.emitDwarfUnwind = + llvm::StringSwitch(a->getValue()) + .Case("always", EmitDwarfUnwindType::Always) + .Case("no-compact-unwind", EmitDwarfUnwindType::NoCompactUnwind) + .Case("default", EmitDwarfUnwindType::Default); + } + + opts.asSecureLogFile = args.getLastArgValue(OPT_as_secure_log_file); + + return success; +} + +static std::unique_ptr +getOutputStream(StringRef path, DiagnosticsEngine &diags, bool binary) { + // Make sure that the Out file gets unlinked from the disk if we get a + // SIGINT. + if (path != "-") + sys::RemoveFileOnSignal(path); + + std::error_code ec; + auto out = std::make_unique( + path, ec, (binary ? sys::fs::OF_None : sys::fs::OF_TextWithCRLF)); + if (ec) { + diags.Report(diag::err_fe_unable_to_open_output) << path << ec.message(); + return nullptr; + } + + return out; +} + +static bool executeAssemblerImpl(AssemblerInvocation &opts, + DiagnosticsEngine &diags) { + // Get the target specific parser. + std::string error; + const Target *theTarget = TargetRegistry::lookupTarget(opts.triple, error); + if (!theTarget) + return diags.Report(diag::err_target_unknown_triple) << opts.triple; + + ErrorOr> buffer = + MemoryBuffer::getFileOrSTDIN(opts.inputFile, /*IsText=*/true); + + if (std::error_code ec = buffer.getError()) { + error = ec.message(); + return diags.Report(diag::err_fe_error_reading) << opts.inputFile; + } + + SourceMgr srcMgr; + + // Tell SrcMgr about this buffer, which is what the parser will pick up. + unsigned bufferIndex = srcMgr.AddNewSourceBuffer(std::move(*buffer), SMLoc()); + + // Record the location of the include directories so that the lexer can find + // it later. + srcMgr.setIncludeDirs(opts.includePaths); + + std::unique_ptr mri(theTarget->createMCRegInfo(opts.triple)); + assert(mri && "Unable to create target register info!"); + + MCTargetOptions mcOptions; + mcOptions.EmitDwarfUnwind = opts.emitDwarfUnwind; + mcOptions.AsSecureLogFile = opts.asSecureLogFile; + + std::unique_ptr mai( + theTarget->createMCAsmInfo(*mri, opts.triple, mcOptions)); + assert(mai && "Unable to create target asm info!"); + + // Ensure MCAsmInfo initialization occurs before any use, otherwise sections + // may be created with a combination of default and explicit settings. + mai->setCompressDebugSections(opts.compressDebugSections); + + mai->setRelaxELFRelocations(opts.relaxElfRelocations); + + bool isBinary = opts.outputType == AssemblerInvocation::FT_Obj; + if (opts.outputPath.empty()) + opts.outputPath = "-"; + std::unique_ptr fdos = + getOutputStream(opts.outputPath, diags, isBinary); + if (!fdos) + return true; + std::unique_ptr dwoOs; + if (!opts.splitDwarfOutput.empty()) + dwoOs = getOutputStream(opts.splitDwarfOutput, diags, isBinary); + + // Build up the feature string from the target feature list. + std::string fs = llvm::join(opts.features, ","); + + std::unique_ptr sti( + theTarget->createMCSubtargetInfo(opts.triple, opts.cpu, fs)); + assert(sti && "Unable to create subtarget info!"); + + MCContext ctx(Triple(opts.triple), mai.get(), mri.get(), sti.get(), &srcMgr, + &mcOptions); + + bool pic = false; + if (opts.relocationModel == "static") { + pic = false; + } else if (opts.relocationModel == "pic") { + pic = true; + } else { + assert(opts.relocationModel == "dynamic-no-pic" && "Invalid PIC model!"); + pic = false; + } + + // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and + // MCObjectFileInfo needs a MCContext reference in order to initialize itself. + std::unique_ptr mofi( + theTarget->createMCObjectFileInfo(ctx, pic)); + if (opts.darwinTargetVariantTriple) + mofi->setDarwinTargetVariantTriple(*opts.darwinTargetVariantTriple); + if (!opts.darwinTargetVariantSdkVersion.empty()) + mofi->setDarwinTargetVariantSDKVersion(opts.darwinTargetVariantSdkVersion); + ctx.setObjectFileInfo(mofi.get()); + + if (opts.saveTemporaryLabels) + ctx.setAllowTemporaryLabels(false); + if (opts.genDwarfForAssembly) + ctx.setGenDwarfForAssembly(true); + if (!opts.dwarfDebugFlags.empty()) + ctx.setDwarfDebugFlags(StringRef(opts.dwarfDebugFlags)); + if (!opts.dwarfDebugProducer.empty()) + ctx.setDwarfDebugProducer(StringRef(opts.dwarfDebugProducer)); + if (!opts.debugCompilationDir.empty()) + ctx.setCompilationDir(opts.debugCompilationDir); + else { + // If no compilation dir is set, try to use the current directory. + SmallString<128> cwd; + if (!sys::fs::current_path(cwd)) + ctx.setCompilationDir(cwd); + } + if (!opts.debugPrefixMap.empty()) + for (const auto &kv : opts.debugPrefixMap) + ctx.addDebugPrefixMapEntry(kv.first, kv.second); + if (!opts.mainFileName.empty()) + ctx.setMainFileName(StringRef(opts.mainFileName)); + ctx.setDwarfFormat(opts.dwarf64 ? dwarf::DWARF64 : dwarf::DWARF32); + ctx.setDwarfVersion(opts.dwarfVersion); + if (opts.genDwarfForAssembly) + ctx.setGenDwarfRootFile(opts.inputFile, + srcMgr.getMemoryBuffer(bufferIndex)->getBuffer()); + + std::unique_ptr str; + + std::unique_ptr mcii(theTarget->createMCInstrInfo()); + assert(mcii && "Unable to create instruction info!"); + + raw_pwrite_stream *out = fdos.get(); + std::unique_ptr bos; + + mcOptions.MCNoWarn = opts.noWarn; + mcOptions.MCFatalWarnings = opts.fatalWarnings; + mcOptions.MCNoTypeCheck = opts.noTypeCheck; + mcOptions.ABIName = opts.targetAbi; + + // FIXME: There is a bit of code duplication with addPassesToEmitFile. + if (opts.outputType == AssemblerInvocation::FT_Asm) { + MCInstPrinter *ip = theTarget->createMCInstPrinter( + llvm::Triple(opts.triple), opts.outputAsmVariant, *mai, *mcii, *mri); + + std::unique_ptr ce; + if (opts.showEncoding) + ce.reset(theTarget->createMCCodeEmitter(*mcii, ctx)); + std::unique_ptr mab( + theTarget->createMCAsmBackend(*sti, *mri, mcOptions)); + + auto fOut = std::make_unique(*out); + str.reset(theTarget->createAsmStreamer( + ctx, std::move(fOut), /*asmverbose*/ true, + /*useDwarfDirectory*/ true, ip, std::move(ce), std::move(mab), + opts.showInst)); + } else if (opts.outputType == AssemblerInvocation::FT_Null) { + str.reset(createNullStreamer(ctx)); + } else { + assert(opts.outputType == AssemblerInvocation::FT_Obj && + "Invalid file type!"); + if (!fdos->supportsSeeking()) { + bos = std::make_unique(*fdos); + out = bos.get(); + } + + std::unique_ptr ce( + theTarget->createMCCodeEmitter(*mcii, ctx)); + std::unique_ptr mab( + theTarget->createMCAsmBackend(*sti, *mri, mcOptions)); + assert(mab && "Unable to create asm backend!"); + + std::unique_ptr ow = + dwoOs ? mab->createDwoObjectWriter(*out, *dwoOs) + : mab->createObjectWriter(*out); + + Triple t(opts.triple); + str.reset(theTarget->createMCObjectStreamer( + t, ctx, std::move(mab), std::move(ow), std::move(ce), *sti, + opts.relaxAll, opts.incrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ true)); + str.get()->initSections(opts.noExecStack, *sti); + } + + // When -fembed-bitcode is passed to flang_as, a 1-byte marker + // is emitted in __LLVM,__asm section if the object file is MachO format. + if (opts.embedBitcode && ctx.getObjectFileType() == MCContext::IsMachO) { + MCSection *asmLabel = ctx.getMachOSection( + "__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly()); + str.get()->switchSection(asmLabel); + str.get()->emitZeros(1); + } + + // Assembly to object compilation should leverage assembly info. + str->setUseAssemblerInfoForParsing(true); + + bool failed = false; + + std::unique_ptr parser( + createMCAsmParser(srcMgr, ctx, *str.get(), *mai)); + + // FIXME: init MCTargetOptions from sanitizer flags here. + std::unique_ptr tap( + theTarget->createMCAsmParser(*sti, *parser, *mcii, mcOptions)); + if (!tap) + failed = diags.Report(diag::err_target_unknown_triple) << opts.triple; + + // Set values for symbols, if any. + for (auto &s : opts.symbolDefs) { + auto pair = StringRef(s).split('='); + auto sym = pair.first; + auto val = pair.second; + int64_t value; + // We have already error checked this in the driver. + val.getAsInteger(0, value); + ctx.setSymbolValue(parser->getStreamer(), sym, value); + } + + if (!failed) { + parser->setTargetParser(*tap.get()); + failed = parser->Run(opts.noInitialTextSection); + } + + return failed; +} + +static bool executeAssembler(AssemblerInvocation &opts, + DiagnosticsEngine &diags) { + bool failed = executeAssemblerImpl(opts, diags); + + // Delete output file if there were errors. + if (failed) { + if (opts.outputPath != "-") + sys::fs::remove(opts.outputPath); + if (!opts.splitDwarfOutput.empty() && opts.splitDwarfOutput != "-") + sys::fs::remove(opts.splitDwarfOutput); + } + + return failed; +} + +static void llvmErrorHandler(void *userData, const char *message, + bool genCrashDiag) { + DiagnosticsEngine &diags = *static_cast(userData); + + diags.Report(diag::err_fe_error_backend) << message; + + // We cannot recover from llvm errors. + sys::Process::Exit(1); +} + +int fc1as_main(ArrayRef argv, const char *argv0) { + // Initialize targets and assembly printers/parsers. + InitializeAllTargetInfos(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); + + // Construct our diagnostic client. + IntrusiveRefCntPtr diagOpts = new DiagnosticOptions(); + TextDiagnosticPrinter *diagClient = + new TextDiagnosticPrinter(errs(), &*diagOpts); + diagClient->setPrefix("flang -fc1as"); + IntrusiveRefCntPtr diagId(new DiagnosticIDs()); + DiagnosticsEngine diags(diagId, &*diagOpts, diagClient); + + // Set an error handler, so that any LLVM backend diagnostics go through our + // error handler. + ScopedFatalErrorHandler fatalErrorHandler(llvmErrorHandler, + static_cast(&diags)); + + // Parse the arguments. + AssemblerInvocation asmInv; + if (!AssemblerInvocation::createFromArgs(asmInv, argv, diags)) + return 1; + + if (asmInv.showHelp) { + getDriverOptTable().printHelp( + llvm::outs(), "flang -fc1as [options] file...", + "Flang Integrated Assembler", + /*Include=*/driver::options::FC1AsOption, /*Exclude=*/0, + /*ShowAllAliases=*/false); + return 0; + } + + // Honor -version. + // + // FIXME: Use a better -version message? + if (asmInv.showVersion) { + llvm::cl::PrintVersionMessage(); + return 0; + } + + // Honor -mllvm. + // + // FIXME: Remove this, one day. + if (!asmInv.llvmArgs.empty()) { + unsigned numArgs = asmInv.llvmArgs.size(); + auto args = std::make_unique(numArgs + 2); + args[0] = "flang (LLVM option parsing)"; + for (unsigned i = 0; i != numArgs; ++i) + args[i + 1] = asmInv.llvmArgs[i].c_str(); + args[numArgs + 1] = nullptr; + llvm::cl::ParseCommandLineOptions(numArgs + 1, args.get()); + } + + // Execute the invocation, unless there were parsing errors. + bool failed = diags.hasErrorOccurred() || executeAssembler(asmInv, diags); + + // If any timers were active but haven't been destroyed yet, print their + // results now. + TimerGroup::printAll(errs()); + TimerGroup::clearAll(); + + return !!failed; +}