Skip to content

Commit 6bf5580

Browse files
[clang][driver] Add basic --driver-mode=flang support for fortran
This patch adds a new Flang mode. When in Flang mode, the driver will invoke flang for fortran inputs instead of falling back to the GCC toolchain as it would otherwise do. The behaviour of other driver modes are left unmodified to preserve backwards compatibility. It is intended that a soon to be implemented binary in the flang project will import libclangDriver and run the clang driver in the new flang mode. Please note that since the binary invoked by the driver is under development, there will no doubt be further tweaks necessary in future commits. * Initial support is added for basic driver phases * -E, -fsyntax-only, -emit-llvm -S, -emit-llvm, -S, (none specified) * -### tests are added for all of the above * This is more than is supported by f18 so far, which will emit errors for those options which are unimplemented. * A test is added that ensures that clang gives a reasonable error message if flang is not available in the path (without -###). * Test that the driver accepts multiple inputs in --driver-mode=flang. * Test that a combination of C and Fortran inputs run both clang and flang in --driver-mode=flang. * clang/test/Driver/fortran.f95 is fixed to use the correct fortran comment character. Differential revision: https://reviews.llvm.org/D63607
1 parent a795bd9 commit 6bf5580

File tree

18 files changed

+322
-21
lines changed

18 files changed

+322
-21
lines changed

clang/include/clang/Driver/Driver.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ class Driver {
6565
GCCMode,
6666
GXXMode,
6767
CPPMode,
68-
CLMode
68+
CLMode,
69+
FlangMode
6970
} Mode;
7071

7172
enum SaveTempsMode {
@@ -180,6 +181,10 @@ class Driver {
180181
/// Whether the driver should follow cl.exe like behavior.
181182
bool IsCLMode() const { return Mode == CLMode; }
182183

184+
/// Whether the driver should invoke flang for fortran inputs.
185+
/// Other modes fall back to calling gcc which in turn calls gfortran.
186+
bool IsFlangMode() const { return Mode == FlangMode; }
187+
183188
/// Only print tool bindings, don't build any jobs.
184189
unsigned CCCPrintBindings : 1;
185190

@@ -534,6 +539,10 @@ class Driver {
534539
/// handle this action.
535540
bool ShouldUseClangCompiler(const JobAction &JA) const;
536541

542+
/// ShouldUseFlangCompiler - Should the flang compiler be used to
543+
/// handle this action.
544+
bool ShouldUseFlangCompiler(const JobAction &JA) const;
545+
537546
/// Returns true if we are performing any kind of LTO.
538547
bool isUsingLTO() const { return LTOMode != LTOK_None; }
539548

clang/include/clang/Driver/ToolChain.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,15 @@ class ToolChain {
134134
path_list ProgramPaths;
135135

136136
mutable std::unique_ptr<Tool> Clang;
137+
mutable std::unique_ptr<Tool> Flang;
137138
mutable std::unique_ptr<Tool> Assemble;
138139
mutable std::unique_ptr<Tool> Link;
139140
mutable std::unique_ptr<Tool> IfsMerge;
140141
mutable std::unique_ptr<Tool> OffloadBundler;
141142
mutable std::unique_ptr<Tool> OffloadWrapper;
142143

143144
Tool *getClang() const;
145+
Tool *getFlang() const;
144146
Tool *getAssemble() const;
145147
Tool *getLink() const;
146148
Tool *getIfsMerge() const;

clang/include/clang/Driver/Types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ namespace types {
8484
/// isObjC - Is this an "ObjC" input (Obj-C and Obj-C++ sources and headers).
8585
bool isObjC(ID Id);
8686

87+
/// isFortran - Is this a Fortran input.
88+
bool isFortran(ID Id);
89+
8790
/// isSrcFile - Is this a source file, i.e. something that still has to be
8891
/// preprocessed. The logic behind this is the same that decides if the first
8992
/// compilation phase is a preprocessing one.

clang/lib/Driver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ add_clang_library(clangDriver
4343
ToolChains/Cuda.cpp
4444
ToolChains/Darwin.cpp
4545
ToolChains/DragonFly.cpp
46+
ToolChains/Flang.cpp
4647
ToolChains/FreeBSD.cpp
4748
ToolChains/Fuchsia.cpp
4849
ToolChains/Gnu.cpp

clang/lib/Driver/Driver.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ void Driver::setDriverModeFromOption(StringRef Opt) {
178178
.Case("g++", GXXMode)
179179
.Case("cpp", CPPMode)
180180
.Case("cl", CLMode)
181+
.Case("flang", FlangMode)
181182
.Default(None))
182183
Mode = *M;
183184
else
@@ -4876,6 +4877,19 @@ bool Driver::ShouldUseClangCompiler(const JobAction &JA) const {
48764877
return true;
48774878
}
48784879

4880+
bool Driver::ShouldUseFlangCompiler(const JobAction &JA) const {
4881+
// Say "no" if there is not exactly one input of a type flang understands.
4882+
if (JA.size() != 1 ||
4883+
!types::isFortran((*JA.input_begin())->getType()))
4884+
return false;
4885+
4886+
// And say "no" if this is not a kind of action flang understands.
4887+
if (!isa<PreprocessJobAction>(JA) && !isa<CompileJobAction>(JA) && !isa<BackendJobAction>(JA))
4888+
return false;
4889+
4890+
return true;
4891+
}
4892+
48794893
/// GetReleaseVersion - Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
48804894
/// grouped values as integers. Numbers which are not provided are set to 0.
48814895
///

clang/lib/Driver/ToolChain.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "ToolChains/Arch/ARM.h"
1212
#include "ToolChains/Clang.h"
1313
#include "ToolChains/InterfaceStubs.h"
14+
#include "ToolChains/Flang.h"
1415
#include "clang/Basic/ObjCRuntime.h"
1516
#include "clang/Basic/Sanitizers.h"
1617
#include "clang/Config/config.h"
@@ -151,6 +152,7 @@ static const DriverSuffix *FindDriverSuffix(StringRef ProgName, size_t &Pos) {
151152
{"cpp", "--driver-mode=cpp"},
152153
{"cl", "--driver-mode=cl"},
153154
{"++", "--driver-mode=g++"},
155+
{"flang", "--driver-mode=flang"},
154156
};
155157

156158
for (size_t i = 0; i < llvm::array_lengthof(DriverSuffixes); ++i) {
@@ -254,6 +256,12 @@ Tool *ToolChain::getClang() const {
254256
return Clang.get();
255257
}
256258

259+
Tool *ToolChain::getFlang() const {
260+
if (!Flang)
261+
Flang.reset(new tools::Flang(*this));
262+
return Flang.get();
263+
}
264+
257265
Tool *ToolChain::buildAssembler() const {
258266
return new tools::ClangAs(*this);
259267
}
@@ -493,6 +501,7 @@ bool ToolChain::needsGCovInstrumentation(const llvm::opt::ArgList &Args) {
493501
}
494502

495503
Tool *ToolChain::SelectTool(const JobAction &JA) const {
504+
if (D.IsFlangMode() && getDriver().ShouldUseFlangCompiler(JA)) return getFlang();
496505
if (getDriver().ShouldUseClangCompiler(JA)) return getClang();
497506
Action::ActionClass AC = JA.getKind();
498507
if (AC == Action::AssembleJobClass && useIntegratedAs())
@@ -541,7 +550,15 @@ std::string ToolChain::GetLinkerPath() const {
541550
}
542551

543552
types::ID ToolChain::LookupTypeForExtension(StringRef Ext) const {
544-
return types::lookupTypeForExtension(Ext);
553+
types::ID id = types::lookupTypeForExtension(Ext);
554+
555+
// Flang always runs the preprocessor and has no notion of "preprocessed
556+
// fortran". Here, TY_PP_Fortran is coerced to TY_Fortran to avoid treating
557+
// them differently.
558+
if (D.IsFlangMode() && id == types::TY_PP_Fortran)
559+
id = types::TY_Fortran;
560+
561+
return id;
545562
}
546563

547564
bool ToolChain::HasNativeLLVMSupport() const {

clang/lib/Driver/ToolChains/Flang.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//===-- Flang.cpp - Flang+LLVM ToolChain Implementations --------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
10+
#include "Flang.h"
11+
#include "CommonArgs.h"
12+
13+
#include "clang/Driver/Options.h"
14+
15+
#include <cassert>
16+
17+
using namespace clang::driver;
18+
using namespace clang::driver::tools;
19+
using namespace clang;
20+
using namespace llvm::opt;
21+
22+
void Flang::ConstructJob(Compilation &C, const JobAction &JA,
23+
const InputInfo &Output, const InputInfoList &Inputs,
24+
const ArgList &Args, const char *LinkingOutput) const {
25+
const auto &TC = getToolChain();
26+
const llvm::Triple &Triple = TC.getEffectiveTriple();
27+
const std::string &TripleStr = Triple.getTriple();
28+
29+
ArgStringList CmdArgs;
30+
31+
CmdArgs.push_back("-fc1");
32+
33+
CmdArgs.push_back("-triple");
34+
CmdArgs.push_back(Args.MakeArgString(TripleStr));
35+
36+
if (isa<PreprocessJobAction>(JA)) {
37+
CmdArgs.push_back("-E");
38+
} else if (isa<CompileJobAction>(JA) || isa<BackendJobAction>(JA)) {
39+
if (JA.getType() == types::TY_Nothing) {
40+
CmdArgs.push_back("-fsyntax-only");
41+
} else if (JA.getType() == types::TY_AST) {
42+
CmdArgs.push_back("-emit-ast");
43+
} else if (JA.getType() == types::TY_LLVM_IR ||
44+
JA.getType() == types::TY_LTO_IR) {
45+
CmdArgs.push_back("-emit-llvm");
46+
} else if (JA.getType() == types::TY_LLVM_BC ||
47+
JA.getType() == types::TY_LTO_BC) {
48+
CmdArgs.push_back("-emit-llvm-bc");
49+
} else if (JA.getType() == types::TY_PP_Asm) {
50+
CmdArgs.push_back("-S");
51+
} else {
52+
assert(false && "Unexpected output type!");
53+
}
54+
} else if (isa<AssembleJobAction>(JA)) {
55+
CmdArgs.push_back("-emit-obj");
56+
} else {
57+
assert(false && "Unexpected action class for Flang tool.");
58+
}
59+
60+
if (Output.isFilename()) {
61+
CmdArgs.push_back("-o");
62+
CmdArgs.push_back(Output.getFilename());
63+
} else {
64+
assert(Output.isNothing() && "Invalid output.");
65+
}
66+
67+
const InputInfo &Input = Inputs[0];
68+
assert(Input.isFilename() && "Invalid input.");
69+
CmdArgs.push_back(Input.getFilename());
70+
71+
const auto& D = C.getDriver();
72+
const char* Exec = Args.MakeArgString(D.GetProgramPath("flang", TC));
73+
C.addCommand(std::make_unique<Command>(JA, *this, Exec, CmdArgs, Inputs));
74+
}
75+
76+
Flang::Flang(const ToolChain &TC)
77+
: Tool("flang", "flang frontend", TC, RF_Full) {}
78+
79+
Flang::~Flang() {}

clang/lib/Driver/ToolChains/Flang.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===--- Flang.h - Flang Tool and ToolChain Implementations ====-*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_FLANG_H
10+
#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_FLANG_H
11+
12+
#include "clang/Driver/Tool.h"
13+
#include "clang/Driver/Action.h"
14+
#include "clang/Driver/Compilation.h"
15+
#include "clang/Driver/ToolChain.h"
16+
#include "llvm/Option/ArgList.h"
17+
#include "llvm/Support/Compiler.h"
18+
19+
namespace clang {
20+
namespace driver {
21+
22+
namespace tools {
23+
24+
/// Flang compiler tool.
25+
class LLVM_LIBRARY_VISIBILITY Flang : public Tool {
26+
public:
27+
Flang(const ToolChain &TC);
28+
~Flang() override;
29+
30+
bool hasGoodDiagnostics() const override { return true; }
31+
bool hasIntegratedAssembler() const override { return true; }
32+
bool hasIntegratedCPP() const override { return true; }
33+
bool canEmitIR() const override { return true; }
34+
35+
void ConstructJob(Compilation &C, const JobAction &JA,
36+
const InputInfo &Output, const InputInfoList &Inputs,
37+
const llvm::opt::ArgList &TCArgs,
38+
const char *LinkingOutput) const override;
39+
};
40+
41+
} // end namespace tools
42+
43+
} // end namespace driver
44+
} // end namespace clang
45+
46+
#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_FLANG_H

clang/lib/Driver/Types.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,16 @@ bool types::isHIP(ID Id) {
212212
}
213213
}
214214

215+
bool types::isFortran(ID Id) {
216+
switch (Id) {
217+
default:
218+
return false;
219+
220+
case TY_Fortran: case TY_PP_Fortran:
221+
return true;
222+
}
223+
}
224+
215225
bool types::isSrcFile(ID Id) {
216226
return Id != TY_Object && getPreprocessedType(Id) != TY_INVALID;
217227
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
! This file only exists to facilitate a driver -### test.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* This file only exists to facilitate a driver -### test. */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
! This file only exists to facilitate a driver -### test.

clang/test/Driver/flang/flang.F90

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
! Check that flang -fc1 is invoked when in --driver-mode=flang.
2+
3+
! This is a copy of flang.f90 because the driver has logic in it which
4+
! differentiates between F90 and f90 files. Flang will not treat these files
5+
! differently.
6+
7+
! Test various output types:
8+
! * -E
9+
! * -fsyntax-only
10+
! * -emit-llvm -S
11+
! * -emit-llvm
12+
! * -S
13+
! * (no type specified, resulting in an object file)
14+
15+
! All invocations should begin with flang -fc1, consume up to here.
16+
! ALL-LABEL: "{{[^"]*}}flang" "-fc1"
17+
18+
! Check that f90 files are not treated as "previously preprocessed"
19+
! ... in --driver-mode=flang.
20+
! RUN: %clang --driver-mode=flang -### -E %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-E %s
21+
! CHECK-E-NOT: previously preprocessed input
22+
! CHECK-E-DAG: "-E"
23+
! CHECK-E-DAG: "-o" "-"
24+
25+
! RUN: %clang --driver-mode=flang -### -emit-ast %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-AST %s
26+
! CHECK-EMIT-AST-DAG: "-triple"
27+
! CHECK-EMIT-AST-DAG: "-emit-ast"
28+
! CHECK-EMIT-AST-DAG: "-o" "{{[^"]*}}.ast"
29+
30+
! RUN: %clang --driver-mode=flang -### -fsyntax-only %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-SYNTAX-ONLY %s
31+
! CHECK-SYNTAX-ONLY-NOT: "-o"
32+
! CHECK-SYNTAX-ONLY-DAG: "-fsyntax-only"
33+
34+
! RUN: %clang --driver-mode=flang -### -emit-llvm -S %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-LLVM-IR %s
35+
! CHECK-EMIT-LLVM-IR-DAG: "-emit-llvm"
36+
! CHECK-EMIT-LLVM-IR-DAG: "-o" "{{[^"]*}}.ll"
37+
38+
! RUN: %clang --driver-mode=flang -### -emit-llvm %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-LLVM-BC %s
39+
! CHECK-EMIT-LLVM-BC-DAG: "-emit-llvm-bc"
40+
! CHECK-EMIT-LLVM-BC-DAG: "-o" "{{[^"]*}}.bc"
41+
42+
! RUN: %clang --driver-mode=flang -### -S %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-S %s
43+
! CHECK-S-DAG: "-S"
44+
! CHECK-S-DAG: "-o" "{{[^"]*}}.s"
45+
46+
! RUN: %clang --driver-mode=flang -### %s 2>&1 | FileCheck --check-prefixes=ALL,CHECK-EMIT-OBJ %s
47+
! CHECK-EMIT-OBJ-DAG: "-emit-obj"
48+
! CHECK-EMIT-OBJ-DAG: "-o" "{{[^"]*}}.o"
49+
50+
! Should end in the input file.
51+
! ALL: "{{.*}}flang.F90"{{$}}

0 commit comments

Comments
 (0)