File: driver.cpp

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (192 lines) | stat: -rw-r--r-- 7,194 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//===-- driver.cpp - Flang Driver -----------------------------------------===//
//
// 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 driver; it is a thin wrapper
// for functionality in the Driver flang library.
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//

#include "clang/Driver/Driver.h"
#include "flang/Frontend/CompilerInvocation.h"
#include "flang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Driver/Compilation.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"
#include <stdlib.h>

using llvm::StringRef;

// main frontend method. Lives inside fc1_main.cpp
extern int fc1_main(llvm::ArrayRef<const char *> 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;
  return llvm::sys::fs::getMainExecutable(argv0, p);
}

// This lets us create the DiagnosticsEngine with a properly-filled-out
// DiagnosticOptions instance
static clang::DiagnosticOptions *
createAndPopulateDiagOpts(llvm::ArrayRef<const char *> argv) {
  auto *diagOpts = new clang::DiagnosticOptions;

  // Ignore missingArgCount and the return value of ParseDiagnosticArgs.
  // Any errors that would be diagnosed here will also be diagnosed later,
  // when the DiagnosticsEngine actually exists.
  unsigned missingArgIndex, missingArgCount;
  llvm::opt::InputArgList args = clang::driver::getDriverOptTable().ParseArgs(
      argv.slice(1), missingArgIndex, missingArgCount,
      /*FlagsToInclude=*/clang::driver::options::FlangOption);

  (void)Fortran::frontend::parseDiagnosticArgs(*diagOpts, args);

  return diagOpts;
}

static int executeFC1Tool(llvm::SmallVectorImpl<const char *> &argV) {
  llvm::StringRef tool = argV[1];
  if (tool == "-fc1")
    return fc1_main(llvm::ArrayRef(argV).slice(2), argV[0]);

  // Reject unknown tools.
  // ATM it only supports fc1. Any fc1[*] is rejected.
  llvm::errs() << "error: unknown integrated tool '" << tool << "'. "
               << "Valid tools include '-fc1'.\n";
  return 1;
}

static void ExpandResponseFiles(llvm::StringSaver &saver,
                                llvm::SmallVectorImpl<const char *> &args) {
  // We're defaulting to the GNU syntax, since we don't have a CL mode.
  llvm::cl::TokenizerCallback tokenizer = &llvm::cl::TokenizeGNUCommandLine;
  llvm::cl::ExpansionContext ExpCtx(saver.getAllocator(), tokenizer);
  if (llvm::Error Err = ExpCtx.expandResponseFiles(args)) {
    llvm::errs() << toString(std::move(Err)) << '\n';
  }
}

int main(int argc, const char **argv) {

  // Initialize variables to call the driver
  llvm::InitLLVM x(argc, argv);
  llvm::SmallVector<const char *, 256> args(argv, argv + argc);

  clang::driver::ParsedClangName targetandMode("flang", "--driver-mode=flang");
  std::string driverPath = getExecutablePath(args[0]);

  llvm::BumpPtrAllocator a;
  llvm::StringSaver saver(a);
  ExpandResponseFiles(saver, args);

  // Check if flang-new is in the frontend mode
  auto firstArg = std::find_if(args.begin() + 1, args.end(),
                               [](const char *a) { return a != nullptr; });
  if (firstArg != args.end()) {
    if (llvm::StringRef(args[1]).startswith("-cc1")) {
      llvm::errs() << "error: unknown integrated tool '" << args[1] << "'. "
                   << "Valid tools include '-fc1'.\n";
      return 1;
    }
    // Call flang-new frontend
    if (llvm::StringRef(args[1]).startswith("-fc1")) {
      return executeFC1Tool(args);
    }
  }

  // Not in the frontend mode - continue in the compiler driver mode.

  // Create DiagnosticsEngine for the compiler driver
  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> diagOpts =
      createAndPopulateDiagOpts(args);
  llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
      new clang::DiagnosticIDs());
  Fortran::frontend::TextDiagnosticPrinter *diagClient =
      new Fortran::frontend::TextDiagnosticPrinter(llvm::errs(), &*diagOpts);

  diagClient->setPrefix(
      std::string(llvm::sys::path::stem(getExecutablePath(args[0]))));

  clang::DiagnosticsEngine diags(diagID, &*diagOpts, diagClient);

  // Prepare the driver
  clang::driver::Driver theDriver(driverPath,
                                  llvm::sys::getDefaultTargetTriple(), diags,
                                  "flang LLVM compiler");
  theDriver.setTargetAndMode(targetandMode);
  std::unique_ptr<clang::driver::Compilation> c(
      theDriver.BuildCompilation(args));
  llvm::SmallVector<std::pair<int, const clang::driver::Command *>, 4>
      failingCommands;

  // Set the environment variable, FLANG_COMPILER_OPTIONS_STRING, to contain all
  // the compiler options. This is intended for the frontend driver,
  // flang-new -fc1, to enable the implementation of the COMPILER_OPTIONS
  // intrinsic. To this end, the frontend driver requires the list of the
  // original compiler options, which is not available through other means.
  // TODO: This way of passing information between the compiler and frontend
  // drivers is discouraged. We should find a better way not involving env
  // variables.
  std::string compilerOptsGathered;
  llvm::raw_string_ostream os(compilerOptsGathered);
  for (int i = 0; i < argc; ++i) {
    os << argv[i];
    if (i < argc - 1) {
      os << ' ';
    }
  }
#ifdef _WIN32
  _putenv_s("FLANG_COMPILER_OPTIONS_STRING", compilerOptsGathered.c_str());
#else
  setenv("FLANG_COMPILER_OPTIONS_STRING", compilerOptsGathered.c_str(), 1);
#endif

  // Run the driver
  int res = 1;
  bool isCrash = false;
  res = theDriver.ExecuteCompilation(*c, failingCommands);

  for (const auto &p : failingCommands) {
    int commandRes = p.first;
    const clang::driver::Command *failingCommand = p.second;
    if (!res)
      res = commandRes;

    // If result status is < 0 (e.g. when sys::ExecuteAndWait returns -1),
    // then the driver command signalled an error. On Windows, abort will
    // return an exit code of 3. In these cases, generate additional diagnostic
    // information if possible.
    isCrash = commandRes < 0;
#ifdef _WIN32
    isCrash |= commandRes == 3;
#endif
    if (isCrash) {
      theDriver.generateCompilationDiagnostics(*c, *failingCommand);
      break;
    }
  }

  diags.getClient()->finish();

  // If we have multiple failing commands, we return the result of the first
  // failing command.
  return res;
}