Index: clang/include/clang/Driver/Driver.h =================================================================== --- clang/include/clang/Driver/Driver.h +++ clang/include/clang/Driver/Driver.h @@ -65,7 +65,8 @@ GCCMode, GXXMode, CPPMode, - CLMode + CLMode, + FlangMode } Mode; enum SaveTempsMode { @@ -180,6 +181,10 @@ /// Whether the driver should follow cl.exe like behavior. bool IsCLMode() const { return Mode == CLMode; } + /// Whether the driver should invoke flang for fortran inputs. + /// Other modes fall back to calling gcc which in turn calls gfortran. + bool IsFlangMode() const { return Mode == FlangMode; } + /// Only print tool bindings, don't build any jobs. unsigned CCCPrintBindings : 1; @@ -534,6 +539,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,6 +134,7 @@ 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 IfsMerge; @@ -141,6 +142,7 @@ mutable std::unique_ptr OffloadWrapper; Tool *getClang() const; + Tool *getFlang() const; Tool *getAssemble() const; Tool *getLink() const; Tool *getIfsMerge() const; Index: clang/include/clang/Driver/Types.h =================================================================== --- clang/include/clang/Driver/Types.h +++ clang/include/clang/Driver/Types.h @@ -84,6 +84,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("flang", FlangMode) .Default(None)) Mode = *M; else @@ -4872,6 +4873,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) && !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 @@ -11,6 +11,7 @@ #include "ToolChains/Arch/ARM.h" #include "ToolChains/Clang.h" #include "ToolChains/InterfaceStubs.h" +#include "ToolChains/Flang.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Basic/Sanitizers.h" #include "clang/Config/config.h" @@ -151,6 +152,7 @@ {"cpp", "--driver-mode=cpp"}, {"cl", "--driver-mode=cl"}, {"++", "--driver-mode=g++"}, + {"flang", "--driver-mode=flang"}, }; for (size_t i = 0; i < llvm::array_lengthof(DriverSuffixes); ++i) { @@ -254,6 +256,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); } @@ -493,6 +501,7 @@ } Tool *ToolChain::SelectTool(const JobAction &JA) const { + if (D.IsFlangMode() && getDriver().ShouldUseFlangCompiler(JA)) return getFlang(); if (getDriver().ShouldUseClangCompiler(JA)) return getClang(); Action::ActionClass AC = JA.getKind(); if (AC == Action::AssembleJobClass && useIntegratedAs()) @@ -541,7 +550,15 @@ } types::ID ToolChain::LookupTypeForExtension(StringRef Ext) const { - return types::lookupTypeForExtension(Ext); + types::ID id = types::lookupTypeForExtension(Ext); + + // Flang always runs the preprocessor and has no notion of "preprocessed + // fortran". Here, TY_PP_Fortran is coerced to TY_Fortran to avoid treating + // them differently. + if (D.IsFlangMode() && id == types::TY_PP_Fortran) + id = types::TY_Fortran; + + return id; } bool ToolChain::HasNativeLLVMSupport() const { Index: clang/lib/Driver/ToolChains/Flang.h =================================================================== --- /dev/null +++ clang/lib/Driver/ToolChains/Flang.h @@ -0,0 +1,46 @@ +//===--- 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 "clang/Driver/Tool.h" +#include "clang/Driver/Action.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/ToolChain.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Compiler.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,79 @@ +//===-- 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/Driver/Options.h" + +#include + +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 &Triple = TC.getEffectiveTriple(); + const std::string &TripleStr = Triple.getTriple(); + + ArgStringList CmdArgs; + + CmdArgs.push_back("-fc1"); + + CmdArgs.push_back("-triple"); + CmdArgs.push_back(Args.MakeArgString(TripleStr)); + + if (isa(JA)) { + CmdArgs.push_back("-E"); + } else if (isa(JA) || isa(JA)) { + if (JA.getType() == types::TY_Nothing) { + CmdArgs.push_back("-fsyntax-only"); + } else if (JA.getType() == types::TY_AST) { + CmdArgs.push_back("-emit-ast"); + } 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 { + assert(false && "Unexpected output type!"); + } + } else if (isa(JA)) { + CmdArgs.push_back("-emit-obj"); + } else { + assert(false && "Unexpected action class for Flang tool."); + } + + if (Output.isFilename()) { + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + } else { + assert(Output.isNothing() && "Invalid output."); + } + + const InputInfo &Input = Inputs[0]; + assert(Input.isFilename() && "Invalid input."); + CmdArgs.push_back(Input.getFilename()); + + const auto& D = C.getDriver(); + const char* Exec = Args.MakeArgString(D.GetProgramPath("flang", TC)); + C.addCommand(std::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 @@ -212,6 +212,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/test/Driver/flang/Inputs/one.f90 =================================================================== --- /dev/null +++ clang/test/Driver/flang/Inputs/one.f90 @@ -0,0 +1 @@ +! This file only exists to facilitate a driver -### test. Index: clang/test/Driver/flang/Inputs/other.c =================================================================== --- /dev/null +++ clang/test/Driver/flang/Inputs/other.c @@ -0,0 +1 @@ +/* This file only exists to facilitate a driver -### test. */ Index: clang/test/Driver/flang/Inputs/two.f90 =================================================================== --- /dev/null +++ clang/test/Driver/flang/Inputs/two.f90 @@ -0,0 +1 @@ +! This file only exists to facilitate a driver -### test. Index: clang/test/Driver/flang/flang.F90 =================================================================== --- /dev/null +++ clang/test/Driver/flang/flang.F90 @@ -0,0 +1,51 @@ +! Check that flang -fc1 is invoked when in --driver-mode=flang. + +! This is a copy of flang.f90 because the driver has logic in it which +! differentiates between F90 and f90 files. Flang will not treat these files +! differently. + +! Test various output types: +! * -E +! * -fsyntax-only +! * -emit-llvm -S +! * -emit-llvm +! * -S +! * (no type specified, resulting in an object file) + +! All invocations should begin with flang -fc1, consume up to here. +! ALL-LABEL: "{{[^"]*}}flang" "-fc1" + +! Check that f90 files are not treated as "previously preprocessed" +! ... in --driver-mode=flang. +! RUN: %clang --driver-mode=flang -### -E %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-E %s +! CHECK-E-NOT: previously preprocessed input +! CHECK-E-DAG: "-E" +! CHECK-E-DAG: "-o" "-" + +! RUN: %clang --driver-mode=flang -### -emit-ast %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-AST %s +! CHECK-EMIT-AST-DAG: "-triple" +! CHECK-EMIT-AST-DAG: "-emit-ast" +! CHECK-EMIT-AST-DAG: "-o" "{{[^"]*}}.ast" + +! RUN: %clang --driver-mode=flang -### -fsyntax-only %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-SYNTAX-ONLY %s +! CHECK-SYNTAX-ONLY-NOT: "-o" +! CHECK-SYNTAX-ONLY-DAG: "-fsyntax-only" + +! RUN: %clang --driver-mode=flang -### -emit-llvm -S %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-LLVM-IR %s +! CHECK-EMIT-LLVM-IR-DAG: "-emit-llvm" +! CHECK-EMIT-LLVM-IR-DAG: "-o" "{{[^"]*}}.ll" + +! RUN: %clang --driver-mode=flang -### -emit-llvm %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-LLVM-BC %s +! CHECK-EMIT-LLVM-BC-DAG: "-emit-llvm-bc" +! CHECK-EMIT-LLVM-BC-DAG: "-o" "{{[^"]*}}.bc" + +! RUN: %clang --driver-mode=flang -### -S %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-S %s +! CHECK-S-DAG: "-S" +! CHECK-S-DAG: "-o" "{{[^"]*}}.s" + +! RUN: %clang --driver-mode=flang -### %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-OBJ %s +! CHECK-EMIT-OBJ-DAG: "-emit-obj" +! CHECK-EMIT-OBJ-DAG: "-o" "{{[^"]*}}.o" + +! Should end in the input file. +! ALL: "{{.*}}flang.F90"{{$}} Index: clang/test/Driver/flang/flang.f90 =================================================================== --- /dev/null +++ clang/test/Driver/flang/flang.f90 @@ -0,0 +1,51 @@ +! Check that flang -fc1 is invoked when in --driver-mode=flang. + +! This is a copy of flang.F90 because the driver has logic in it which +! differentiates between F90 and f90 files. Flang will not treat these files +! differently. + +! Test various output types: +! * -E +! * -fsyntax-only +! * -emit-llvm -S +! * -emit-llvm +! * -S +! * (no type specified, resulting in an object file) + +! All invocations should begin with flang -fc1, consume up to here. +! ALL-LABEL: "{{[^"]*}}flang" "-fc1" + +! Check that f90 files are not treated as "previously preprocessed" +! ... in --driver-mode=flang. +! RUN: %clang --driver-mode=flang -### -E %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-E %s +! CHECK-E-NOT: previously preprocessed input +! CHECK-E-DAG: "-E" +! CHECK-E-DAG: "-o" "-" + +! RUN: %clang --driver-mode=flang -### -emit-ast %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-AST %s +! CHECK-EMIT-AST-DAG: "-triple" +! CHECK-EMIT-AST-DAG: "-emit-ast" +! CHECK-EMIT-AST-DAG: "-o" "{{[^"]*}}.ast" + +! RUN: %clang --driver-mode=flang -### -fsyntax-only %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-SYNTAX-ONLY %s +! CHECK-SYNTAX-ONLY-NOT: "-o" +! CHECK-SYNTAX-ONLY-DAG: "-fsyntax-only" + +! RUN: %clang --driver-mode=flang -### -emit-llvm -S %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-LLVM-IR %s +! CHECK-EMIT-LLVM-IR-DAG: "-emit-llvm" +! CHECK-EMIT-LLVM-IR-DAG: "-o" "{{[^"]*}}.ll" + +! RUN: %clang --driver-mode=flang -### -emit-llvm %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-LLVM-BC %s +! CHECK-EMIT-LLVM-BC-DAG: "-emit-llvm-bc" +! CHECK-EMIT-LLVM-BC-DAG: "-o" "{{[^"]*}}.bc" + +! RUN: %clang --driver-mode=flang -### -S %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-S %s +! CHECK-S-DAG: "-S" +! CHECK-S-DAG: "-o" "{{[^"]*}}.s" + +! RUN: %clang --driver-mode=flang -### %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-OBJ %s +! CHECK-EMIT-OBJ-DAG: "-emit-obj" +! CHECK-EMIT-OBJ-DAG: "-o" "{{[^"]*}}.o" + +! Should end in the input file. +! ALL: "{{.*}}flang.f90"{{$}} Index: clang/test/Driver/flang/multiple-inputs-mixed.f90 =================================================================== --- /dev/null +++ clang/test/Driver/flang/multiple-inputs-mixed.f90 @@ -0,0 +1,7 @@ +! Check that flang can handle mixed C and fortran inputs. + +! RUN: %clang --driver-mode=flang -### -fsyntax-only %S/Inputs/one.f90 %S/Inputs/other.c 2>&1 | FileCheck --check-prefixes=CHECK-SYNTAX-ONLY %s +! CHECK-SYNTAX-ONLY-LABEL: "{{[^"]*}}flang{{[^"/]*}}" "-fc1" +! CHECK-SYNTAX-ONLY: "{{[^"]*}}/Inputs/one.f90" +! CHECK-SYNTAX-ONLY-LABEL: "{{[^"]*}}clang{{[^"/]*}}" "-cc1" +! CHECK-SYNTAX-ONLY: "{{[^"]*}}/Inputs/other.c" Index: clang/test/Driver/flang/multiple-inputs.f90 =================================================================== --- /dev/null +++ clang/test/Driver/flang/multiple-inputs.f90 @@ -0,0 +1,7 @@ +! Check that flang driver can handle multiple inputs at once. + +! RUN: %clang --driver-mode=flang -### -fsyntax-only %S/Inputs/one.f90 %S/Inputs/two.f90 2>&1 | FileCheck --check-prefixes=CHECK-SYNTAX-ONLY %s +! CHECK-SYNTAX-ONLY-LABEL: "{{[^"]*}}flang" "-fc1" +! CHECK-SYNTAX-ONLY: "{{[^"]*}}/Inputs/one.f90" +! CHECK-SYNTAX-ONLY-LABEL: "{{[^"]*}}flang" "-fc1" +! CHECK-SYNTAX-ONLY: "{{[^"]*}}/Inputs/two.f90" Index: clang/test/Driver/fortran.f95 =================================================================== --- clang/test/Driver/fortran.f95 +++ clang/test/Driver/fortran.f95 @@ -1,21 +1,22 @@ -// Check that the clang driver can invoke gcc to compile Fortran. +! Check that the clang driver can invoke gcc to compile Fortran when in +! --driver-mode=clang. This is legacy behaviour - see also --driver-mode=flang. -// RUN: %clang -target x86_64-unknown-linux-gnu -integrated-as -c %s -### 2>&1 \ -// RUN: | FileCheck --check-prefix=CHECK-OBJECT %s -// CHECK-OBJECT: gcc -// CHECK-OBJECT: "-c" -// CHECK-OBJECT: "-x" "f95" -// CHECK-OBJECT-NOT: cc1as +! RUN: %clang -target x86_64-unknown-linux-gnu -integrated-as -c %s -### 2>&1 \ +! RUN: | FileCheck --check-prefix=CHECK-OBJECT %s +! CHECK-OBJECT: gcc +! CHECK-OBJECT: "-c" +! CHECK-OBJECT: "-x" "f95" +! CHECK-OBJECT-NOT: cc1as -// RUN: %clang -target x86_64-unknown-linux-gnu -integrated-as -S %s -### 2>&1 \ -// RUN: | FileCheck --check-prefix=CHECK-ASM %s -// CHECK-ASM: gcc -// CHECK-ASM: "-S" -// CHECK-ASM: "-x" "f95" -// CHECK-ASM-NOT: cc1 +! RUN: %clang -target x86_64-unknown-linux-gnu -integrated-as -S %s -### 2>&1 \ +! RUN: | FileCheck --check-prefix=CHECK-ASM %s +! CHECK-ASM: gcc +! CHECK-ASM: "-S" +! CHECK-ASM: "-x" "f95" +! CHECK-ASM-NOT: cc1 -// RUN: %clang -Wall -target x86_64-unknown-linux-gnu -integrated-as %s -o %t -### 2>&1 | FileCheck --check-prefix=CHECK-WARN %s -// CHECK-WARN: gcc -// CHECK-WARN-NOT: "-Wall" -// CHECK-WARN: ld -// CHECK-WARN-NOT: "-Wall" +! RUN: %clang -Wall -target x86_64-unknown-linux-gnu -integrated-as %s -o %t -### 2>&1 | FileCheck --check-prefix=CHECK-WARN %s +! CHECK-WARN: gcc +! CHECK-WARN-NOT: "-Wall" +! CHECK-WARN: ld +! CHECK-WARN-NOT: "-Wall" Index: clang/test/Driver/lit.local.cfg =================================================================== --- clang/test/Driver/lit.local.cfg +++ clang/test/Driver/lit.local.cfg @@ -1,4 +1,4 @@ -config.suffixes = ['.c', '.cpp', '.h', '.m', '.mm', '.S', '.s', '.f90', '.f95', +config.suffixes = ['.c', '.cpp', '.h', '.m', '.mm', '.S', '.s', '.f90', '.F90', '.f95', '.cu', '.rs', '.cl', '.hip'] config.substitutions = list(config.substitutions) config.substitutions.insert(0,