peterwaller-arm created this revision.
Herald added subscribers: cfe-commits, kadircet, ilya-biryukov, mgorny.
Herald added a project: clang.

This is an early prototype, not intended for full review until after the
general approach is agreed. The purpose is to stimulate discussion on
the overall approach. Please see discussion in RFC posted to cfe-dev on
2019-06-20 titled "Adding a fortran mode to the clang driver for flang".

This patch adds a new Fortran mode. When in fortran mode, the driver
will invoke flang instead of falling back to the GCC toolchain as it
would otherwise do.

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 fortran
mode.

Along with the new mode comes a new ToolChains/Flang.cpp. As the flang
frontend option parser is still prototypical, the implementation inside
this file is not yet fleshed out. It's a straw man.

Additionally, this patch makes "bin/flang" in the clang project, a
symlink to clang. This is only there to support trying the patch out
now, and will be removed when the real thing lands.

Likewise, the patch adds a new "-fc1" by analogy with "-cc1" and tweaks
the logic to facilitate that. This change is only to demonstrate the
overall effect, and will not be present in the real patch. The action of
-fc1 is just to print its command line arguments at the moment, which is
redundant with `-###` anyway. I just wanted to see it invoke a stub
flang with my own eyes.


Repository:
  rC Clang

https://reviews.llvm.org/D63607

Files:
  clang/include/clang/Driver/Driver.h
  clang/include/clang/Driver/ToolChain.h
  clang/include/clang/Driver/Types.h
  clang/lib/Driver/CMakeLists.txt
  clang/lib/Driver/Driver.cpp
  clang/lib/Driver/ToolChain.cpp
  clang/lib/Driver/ToolChains/Flang.cpp
  clang/lib/Driver/ToolChains/Flang.h
  clang/lib/Driver/Types.cpp
  clang/tools/driver/CMakeLists.txt
  clang/tools/driver/driver.cpp

Index: clang/tools/driver/driver.cpp
===================================================================
--- clang/tools/driver/driver.cpp
+++ clang/tools/driver/driver.cpp
@@ -305,12 +305,24 @@
 
 static int ExecuteCC1Tool(ArrayRef<const char *> 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;
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/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/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<AssembleJobAction>(JA)) {
+    CmdArgs.push_back("-emit-obj");
+
+    // TODO(peterwaller-arm): Something like this:
+    // CollectArgsForIntegratedAssembler(C, Args, CmdArgs, D);
+  } else {
+    assert((isa<CompileJobAction>(JA) || isa<BackendJobAction>(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<Command>(JA, *this, Exec, CmdArgs, Inputs));
+}
+
+Flang::Flang(const ToolChain &TC)
+    : Tool("flang", "flang frontend", TC, RF_Full) {}
+
+Flang::~Flang() {}
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/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<CompileJobAction>(JA) && !isa<BackendJobAction>(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/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/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/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<Tool> Clang;
+  mutable std::unique_ptr<Tool> Flang;
   mutable std::unique_ptr<Tool> Assemble;
   mutable std::unique_ptr<Tool> Link;
   mutable std::unique_ptr<Tool> OffloadBundler;
 
   Tool *getClang() const;
+  Tool *getFlang() const;
   Tool *getAssemble() const;
   Tool *getLink() const;
   Tool *getClangAs() const;
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 <list>
 #include <map>
@@ -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; }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to