Index: clang/include/clang/Driver/Driver.h =================================================================== --- clang/include/clang/Driver/Driver.h +++ clang/include/clang/Driver/Driver.h @@ -20,6 +20,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/Path.h" #include #include @@ -65,7 +66,8 @@ GCCMode, GXXMode, CPPMode, - CLMode + CLMode, + FortranMode, } Mode; enum SaveTempsMode { @@ -120,6 +122,8 @@ /// The original path to the clang executable. std::string ClangExecutable; + // DONOTSUBMIT(peterwaller-arm): See patch. + mutable std::string FlangExecutable; /// Target and driver mode components extracted from clang executable name. ParsedClangName ClangNameParts; @@ -180,6 +184,9 @@ /// Whether the driver should follow cl.exe like behavior. bool IsCLMode() const { return Mode == CLMode; } + /// Whether the driver should follow gfortran like behavior. + bool IsFortranMode() const { return Mode == FortranMode; } + /// Only print tool bindings, don't build any jobs. unsigned CCCPrintBindings : 1; @@ -312,6 +319,22 @@ std::string getTargetTriple() const { return TargetTriple; } + /// Get the path to the main flang executable. Lifetime of pointer is equal to + /// that of driver. + const char *getFlangProgramPath() const { + // DONOTSUBMIT(peterwaller-arm): This is only needed if flang is a symlink + // to clang, which won't be the case in the real implementation. For some + // reason getClangProgramPath dereferences the symlink and then we can't see + // the path we were invoked by. + if (FlangExecutable.size() == 0) { + SmallString<256> Result; + llvm::sys::path::append(Result, Dir, "flang"); + FlangExecutable = Result.str(); + } + + return FlangExecutable.c_str(); + } + /// Get the path to the main clang executable. const char *getClangProgramPath() const { return ClangExecutable.c_str(); @@ -529,6 +552,10 @@ /// handle this action. bool ShouldUseClangCompiler(const JobAction &JA) const; + /// ShouldUseFlangCompiler - Should the flang compiler be used to + /// handle this action. + bool ShouldUseFlangCompiler(const JobAction &JA) const; + /// Returns true if we are performing any kind of LTO. bool isUsingLTO() const { return LTOMode != LTOK_None; } Index: clang/include/clang/Driver/ToolChain.h =================================================================== --- clang/include/clang/Driver/ToolChain.h +++ clang/include/clang/Driver/ToolChain.h @@ -134,11 +134,13 @@ path_list ProgramPaths; mutable std::unique_ptr Clang; + mutable std::unique_ptr Flang; mutable std::unique_ptr Assemble; mutable std::unique_ptr Link; mutable std::unique_ptr OffloadBundler; Tool *getClang() const; + Tool *getFlang() const; Tool *getAssemble() const; Tool *getLink() const; Tool *getClangAs() const; Index: clang/include/clang/Driver/Types.h =================================================================== --- clang/include/clang/Driver/Types.h +++ clang/include/clang/Driver/Types.h @@ -82,6 +82,9 @@ /// isObjC - Is this an "ObjC" input (Obj-C and Obj-C++ sources and headers). bool isObjC(ID Id); + /// isFortran - Is this a Fortran input. + bool isFortran(ID Id); + /// isSrcFile - Is this a source file, i.e. something that still has to be /// preprocessed. The logic behind this is the same that decides if the first /// compilation phase is a preprocessing one. Index: clang/lib/Driver/CMakeLists.txt =================================================================== --- clang/lib/Driver/CMakeLists.txt +++ clang/lib/Driver/CMakeLists.txt @@ -42,6 +42,7 @@ ToolChains/Cuda.cpp ToolChains/Darwin.cpp ToolChains/DragonFly.cpp + ToolChains/Flang.cpp ToolChains/FreeBSD.cpp ToolChains/Fuchsia.cpp ToolChains/Gnu.cpp Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -177,6 +177,7 @@ .Case("g++", GXXMode) .Case("cpp", CPPMode) .Case("cl", CLMode) + .Case("fortran", FortranMode) .Default(None)) Mode = *M; else @@ -4744,6 +4745,19 @@ return true; } +bool Driver::ShouldUseFlangCompiler(const JobAction &JA) const { + // Say "no" if there is not exactly one input of a type flang understands. + if (JA.size() != 1 || + !types::isFortran((*JA.input_begin())->getType())) + return false; + + // And say "no" if this is not a kind of action flang understands. + if (!isa(JA) && !isa(JA)) + return false; + + return true; +} + /// GetReleaseVersion - Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the /// grouped values as integers. Numbers which are not provided are set to 0. /// Index: clang/lib/Driver/ToolChain.cpp =================================================================== --- clang/lib/Driver/ToolChain.cpp +++ clang/lib/Driver/ToolChain.cpp @@ -10,6 +10,7 @@ #include "InputInfo.h" #include "ToolChains/Arch/ARM.h" #include "ToolChains/Clang.h" +#include "ToolChains/Flang.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Basic/Sanitizers.h" #include "clang/Config/config.h" @@ -150,6 +151,7 @@ {"cpp", "--driver-mode=cpp"}, {"cl", "--driver-mode=cl"}, {"++", "--driver-mode=g++"}, + {"flang", "--driver-mode=fortran"}, }; for (size_t i = 0; i < llvm::array_lengthof(DriverSuffixes); ++i) { @@ -253,6 +255,12 @@ return Clang.get(); } +Tool *ToolChain::getFlang() const { + if (!Flang) + Flang.reset(new tools::Flang(*this)); + return Flang.get(); +} + Tool *ToolChain::buildAssembler() const { return new tools::ClangAs(*this); } @@ -309,6 +317,9 @@ case Action::MigrateJobClass: case Action::VerifyPCHJobClass: case Action::BackendJobClass: + if (D.IsFortranMode()) { + return getFlang(); + } return getClang(); case Action::OffloadBundlingJobClass: @@ -471,6 +482,7 @@ } Tool *ToolChain::SelectTool(const JobAction &JA) const { + if (D.IsFortranMode() && getDriver().ShouldUseFlangCompiler(JA)) return getFlang(); if (getDriver().ShouldUseClangCompiler(JA)) return getClang(); Action::ActionClass AC = JA.getKind(); if (AC == Action::AssembleJobClass && useIntegratedAs()) Index: clang/lib/Driver/ToolChains/Flang.h =================================================================== --- /dev/null +++ clang/lib/Driver/ToolChains/Flang.h @@ -0,0 +1,48 @@ +//===--- Flang.h - Flang Tool and ToolChain Implementations ====-*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_FLANG_H +#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_FLANG_H + +#include "MSVC.h" +#include "clang/Basic/DebugInfoOptions.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Tool.h" +#include "clang/Driver/Types.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace driver { + +namespace tools { + +/// Flang compiler tool. +class LLVM_LIBRARY_VISIBILITY Flang : public Tool { +public: + Flang(const ToolChain &TC); + ~Flang() override; + + bool hasGoodDiagnostics() const override { return true; } + bool hasIntegratedAssembler() const override { return true; } + bool hasIntegratedCPP() const override { return true; } + bool canEmitIR() const override { return true; } + + 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 +} // end namespace clang + +#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_FLANG_H Index: clang/lib/Driver/ToolChains/Flang.cpp =================================================================== --- /dev/null +++ clang/lib/Driver/ToolChains/Flang.cpp @@ -0,0 +1,102 @@ +//===-- Flang.cpp - Flang+LLVM ToolChain Implementations --------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Flang.h" +#include "CommonArgs.h" + +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/Version.h" +#include "clang/Driver/Distro.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/SanitizerArgs.h" +#include "clang/Driver/XRayArgs.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/TargetParser.h" +#include "llvm/Support/YAMLParser.h" + +using namespace clang::driver; +using namespace clang::driver::tools; +using namespace clang; +using namespace llvm::opt; + +void Flang::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const ArgList &Args, const char *LinkingOutput) const { + const auto &TC = getToolChain(); + const llvm::Triple &RawTriple = TC.getTriple(); + const llvm::Triple &Triple = TC.getEffectiveTriple(); + const std::string &TripleStr = Triple.getTriple(); + + const Driver &D = TC.getDriver(); + ArgStringList CmdArgs; + + CmdArgs.push_back("-fc1"); + + // Add the "effective" target triple. + CmdArgs.push_back("-triple"); + CmdArgs.push_back(Args.MakeArgString(TripleStr)); + + if (isa(JA)) { + CmdArgs.push_back("-emit-obj"); + + // TODO(peterwaller-arm): Something like this: + // CollectArgsForIntegratedAssembler(C, Args, CmdArgs, D); + } else { + assert((isa(JA) || isa(JA)) && + "Invalid action for flang tool."); + if (JA.getType() == types::TY_Nothing) { + CmdArgs.push_back("-fsyntax-only"); + } else if (JA.getType() == types::TY_LLVM_IR || + JA.getType() == types::TY_LTO_IR) { + CmdArgs.push_back("-emit-llvm"); + } else if (JA.getType() == types::TY_LLVM_BC || + JA.getType() == types::TY_LTO_BC) { + CmdArgs.push_back("-emit-llvm-bc"); + } else if (JA.getType() == types::TY_PP_Asm) { + CmdArgs.push_back("-S"); + } else if (JA.getType() == types::TY_AST) { + // TODO(peterwaller-arm): Can this be used for emitting fortran ast? + } else { + assert(JA.getType() == types::TY_PP_Asm && "Unexpected output type!"); + } + + // Preserve use-list order by default when emitting bitcode, so that + // loading the bitcode up in 'opt' or 'llc' and running passes gives the + // same result as running passes here. For LTO, we don't need to preserve + // the use-list order, since serialization to bitcode is part of the flow. + if (JA.getType() == types::TY_LLVM_BC) + CmdArgs.push_back("-emit-llvm-uselists"); + } + + Args.AddAllArgs(CmdArgs, options::OPT_R_Group); // Includes -Rpass- + Args.AddAllArgs(CmdArgs, options::OPT_gfortran_Group); // fortran options passthrough. + + assert(Output.isFilename() && "Unexpected lipo output."); + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + + const InputInfo &Input = Inputs[0]; + assert(Input.isFilename() && "Invalid input."); + CmdArgs.push_back(Input.getFilename()); + + const auto *Exec = getToolChain().getDriver().getFlangProgramPath(); + C.addCommand(llvm::make_unique(JA, *this, Exec, CmdArgs, Inputs)); +} + +Flang::Flang(const ToolChain &TC) + : Tool("flang", "flang frontend", TC, RF_Full) {} + +Flang::~Flang() {} Index: clang/lib/Driver/Types.cpp =================================================================== --- clang/lib/Driver/Types.cpp +++ clang/lib/Driver/Types.cpp @@ -187,6 +187,16 @@ } } +bool types::isFortran(ID Id) { + switch (Id) { + default: + return false; + + case TY_Fortran: case TY_PP_Fortran: + return true; + } +} + bool types::isSrcFile(ID Id) { return Id != TY_Object && getPreprocessedType(Id) != TY_INVALID; } Index: clang/tools/driver/CMakeLists.txt =================================================================== --- clang/tools/driver/CMakeLists.txt +++ clang/tools/driver/CMakeLists.txt @@ -63,7 +63,7 @@ add_dependencies(clang clang-resource-headers) if(NOT CLANG_LINKS_TO_CREATE) - set(CLANG_LINKS_TO_CREATE clang++ clang-cl clang-cpp) + set(CLANG_LINKS_TO_CREATE clang++ clang-cl clang-cpp flang) endif() foreach(link ${CLANG_LINKS_TO_CREATE}) Index: clang/tools/driver/driver.cpp =================================================================== --- clang/tools/driver/driver.cpp +++ clang/tools/driver/driver.cpp @@ -305,12 +305,24 @@ static int ExecuteCC1Tool(ArrayRef argv, StringRef Tool) { void *GetExecutablePathVP = (void *)(intptr_t) GetExecutablePath; - if (Tool == "") + // DONOTSUBMIT(peterwaller-arm): Please note that changes to this logic are + // only to facilitate demonstrating that -fc1 is being invoked, and will not + // be submitted in a future implementation. + if (Tool == "-cc1") return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); - if (Tool == "as") + if (Tool == "-cc1as") return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP); - if (Tool == "gen-reproducer") + if (Tool == "-cc1gen-reproducer") return cc1gen_reproducer_main(argv.slice(2), argv[0], GetExecutablePathVP); + if (Tool == "-fc1") { + llvm::errs() << "invoked flang frontend: "; + for (auto arg : argv) { + llvm::errs() << arg << " "; + } + llvm::errs() << "\n"; + return 1; + } + // Reject unknown tools. llvm::errs() << "error: unknown integrated tool '" << Tool << "'. " @@ -372,13 +384,18 @@ // file. auto FirstArg = std::find_if(argv.begin() + 1, argv.end(), [](const char *A) { return A != nullptr; }); - if (FirstArg != argv.end() && StringRef(*FirstArg).startswith("-cc1")) { + // DONOTSUBMIT(peterwaller-arm): Please note that changes to this logic are + // only to facilitate demonstrating that -fc1 is being invoked, and will not + // be submitted in a future implementation. + if (FirstArg != argv.end() && ( + StringRef(*FirstArg).startswith("-cc1") || StringRef(*FirstArg).startswith("-fc1") + )) { // If -cc1 came from a response file, remove the EOL sentinels. if (MarkEOLs) { auto newEnd = std::remove(argv.begin(), argv.end(), nullptr); argv.resize(newEnd - argv.begin()); } - return ExecuteCC1Tool(argv, argv[1] + 4); + return ExecuteCC1Tool(argv, argv[1]); } bool CanonicalPrefixes = true;