brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.7 KiB · 58901c6 Raw
275 lines · cpp
1//===--- FrontendAction.cpp -----------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/10//11//===----------------------------------------------------------------------===//12 13#include "flang/Frontend/FrontendAction.h"14#include "flang/Frontend/CompilerInstance.h"15#include "flang/Frontend/FrontendActions.h"16#include "flang/Frontend/FrontendOptions.h"17#include "flang/Frontend/FrontendPluginRegistry.h"18#include "flang/Parser/parsing.h"19#include "clang/Basic/DiagnosticFrontend.h"20#include "llvm/Support/Errc.h"21#include "llvm/Support/VirtualFileSystem.h"22 23using namespace Fortran::frontend;24 25LLVM_INSTANTIATE_REGISTRY(FrontendPluginRegistry)26 27void FrontendAction::setCurrentInput(const FrontendInputFile &input) {28  this->currentInput = input;29}30 31// Call this method if BeginSourceFile fails.32// Deallocate compiler instance, input and output descriptors33static void beginSourceFileCleanUp(FrontendAction &fa, CompilerInstance &ci) {34  ci.clearOutputFiles(/*EraseFiles=*/true);35  fa.setCurrentInput(FrontendInputFile());36  fa.setInstance(nullptr);37}38 39bool FrontendAction::beginSourceFile(CompilerInstance &ci,40                                     const FrontendInputFile &realInput) {41 42  FrontendInputFile input(realInput);43 44  // Return immediately if the input file does not exist or is not a file. Note45  // that we cannot check this for input from stdin.46  if (input.getFile() != "-") {47    if (!llvm::sys::fs::is_regular_file(input.getFile())) {48      // Create an diagnostic ID to report49      unsigned diagID;50      if (llvm::vfs::getRealFileSystem()->exists(input.getFile())) {51        ci.getDiagnostics().Report(clang::diag::err_fe_error_reading)52            << input.getFile() << "not a regular file";53        diagID = ci.getDiagnostics().getCustomDiagID(54            clang::DiagnosticsEngine::Error, "%0 is not a regular file");55      } else {56        diagID = ci.getDiagnostics().getCustomDiagID(57            clang::DiagnosticsEngine::Error, "%0 does not exist");58      }59 60      // Report the diagnostic and return61      ci.getDiagnostics().Report(diagID) << input.getFile();62      beginSourceFileCleanUp(*this, ci);63      return false;64    }65  }66 67  assert(!instance && "Already processing a source file!");68  assert(!realInput.isEmpty() && "Unexpected empty filename!");69  setCurrentInput(realInput);70  setInstance(&ci);71 72  if (!ci.hasAllSources()) {73    beginSourceFileCleanUp(*this, ci);74    return false;75  }76 77  auto &invoc = ci.getInvocation();78 79  // Include command-line and predefined preprocessor macros. Use either:80  //  * `-cpp/-nocpp`, or81  //  * the file extension (if the user didn't express any preference)82  // to decide whether to include them or not.83  if ((invoc.getPreprocessorOpts().macrosFlag == PPMacrosFlag::Include) ||84      (invoc.getPreprocessorOpts().macrosFlag == PPMacrosFlag::Unknown &&85       getCurrentInput().getMustBePreprocessed())) {86    invoc.setDefaultPredefinitions();87    invoc.collectMacroDefinitions();88  }89 90  if (!invoc.getFortranOpts().features.IsEnabled(91          Fortran::common::LanguageFeature::CUDA)) {92    // Enable CUDA Fortran if source file is *.cuf/*.CUF and not already93    // enabled.94    invoc.getFortranOpts().features.Enable(95        Fortran::common::LanguageFeature::CUDA,96        getCurrentInput().getIsCUDAFortran());97  }98 99  // -fpreprocess-include-lines100  invoc.getFortranOpts().expandIncludeLinesInPreprocessedOutput =101      invoc.getPreprocessorOpts().preprocessIncludeLines;102 103  // Decide between fixed and free form (if the user didn't express any104  // preference, use the file extension to decide)105  if (invoc.getFrontendOpts().fortranForm == FortranForm::Unknown) {106    invoc.getFortranOpts().isFixedForm = getCurrentInput().getIsFixedForm();107  }108 109  if (!beginSourceFileAction()) {110    beginSourceFileCleanUp(*this, ci);111    return false;112  }113 114  return true;115}116 117bool FrontendAction::shouldEraseOutputFiles() {118  return getInstance().getDiagnostics().hasErrorOccurred();119}120 121llvm::Error FrontendAction::execute() {122  executeAction();123 124  return llvm::Error::success();125}126 127void FrontendAction::endSourceFile() {128  CompilerInstance &ci = getInstance();129 130  // Cleanup the output streams, and erase the output files if instructed by the131  // FrontendAction.132  ci.clearOutputFiles(/*EraseFiles=*/shouldEraseOutputFiles());133 134  setInstance(nullptr);135  setCurrentInput(FrontendInputFile());136}137 138bool FrontendAction::runPrescan() {139  CompilerInstance &ci = this->getInstance();140  std::string currentInputPath{getCurrentFileOrBufferName()};141  Fortran::parser::Options parserOptions = ci.getInvocation().getFortranOpts();142 143  if (ci.getInvocation().getFrontendOpts().fortranForm ==144      FortranForm::Unknown) {145    // Switch between fixed and free form format based on the input file146    // extension.147    //148    // Ideally we should have all Fortran options set before entering this149    // method (i.e. before processing any specific input files). However, we150    // can't decide between fixed and free form based on the file extension151    // earlier than this.152    parserOptions.isFixedForm = getCurrentInput().getIsFixedForm();153  }154 155  // Prescan. In case of failure, report and return.156  ci.getParsing().Prescan(currentInputPath, parserOptions);157 158  return !reportFatalScanningErrors();159}160 161bool FrontendAction::runParse(bool emitMessages) {162  CompilerInstance &ci = this->getInstance();163 164  // Parse. In case of failure, report and return.165  ci.getParsing().Parse(llvm::outs());166 167  if (reportFatalParsingErrors()) {168    return false;169  }170 171  if (emitMessages) {172    // Report any non-fatal diagnostics from getParsing now rather than173    // combining them with messages from semantics.174    const common::LanguageFeatureControl &features{175        ci.getInvocation().getFortranOpts().features};176    // Default maxErrors here because none are fatal.177    ci.getParsing().messages().Emit(llvm::errs(), ci.getAllCookedSources(),178                                    /*echoSourceLine=*/true, &features);179  }180  return true;181}182 183bool FrontendAction::runSemanticChecks() {184  CompilerInstance &ci = this->getInstance();185  std::optional<parser::Program> &parseTree{ci.getParsing().parseTree()};186  assert(parseTree && "Cannot run semantic checks without a parse tree!");187 188  // Transfer any pending non-fatal messages from parsing to semantics189  // so that they are merged and all printed in order.190  auto &semanticsCtx{ci.createNewSemanticsContext()};191  semanticsCtx.messages().Annex(std::move(ci.getParsing().messages()));192  semanticsCtx.set_debugModuleWriter(ci.getInvocation().getDebugModuleDir());193 194  // Prepare semantics195  ci.setSemantics(std::make_unique<Fortran::semantics::Semantics>(semanticsCtx,196                                                                  *parseTree));197  auto &semantics = ci.getSemantics();198  semantics.set_hermeticModuleFileOutput(199      ci.getInvocation().getHermeticModuleFileOutput());200 201  // Run semantic checks202  semantics.Perform();203 204  if (reportFatalSemanticErrors()) {205    return false;206  }207 208  // Report the diagnostics from parsing and the semantic checks209  semantics.EmitMessages(ci.getSemaOutputStream());210 211  return true;212}213 214bool FrontendAction::generateRtTypeTables() {215  getInstance().setRtTyTables(216      std::make_unique<Fortran::semantics::RuntimeDerivedTypeTables>(217          BuildRuntimeDerivedTypeTables(getInstance().getSemanticsContext())));218 219  // The runtime derived type information table builder may find additional220  // semantic errors. Report them.221  if (reportFatalSemanticErrors()) {222    return false;223  }224 225  return true;226}227 228template <unsigned N>229bool FrontendAction::reportFatalErrors(const char (&message)[N]) {230  const common::LanguageFeatureControl &features{231      instance->getInvocation().getFortranOpts().features};232  const size_t maxErrors{instance->getInvocation().getMaxErrors()};233  const bool warningsAreErrors{instance->getInvocation().getWarnAsErr()};234  if (instance->getParsing().messages().AnyFatalError(warningsAreErrors)) {235    const unsigned diagID = instance->getDiagnostics().getCustomDiagID(236        clang::DiagnosticsEngine::Error, message);237    instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();238    instance->getParsing().messages().Emit(239        llvm::errs(), instance->getAllCookedSources(),240        /*echoSourceLines=*/true, &features, maxErrors, warningsAreErrors);241    return true;242  }243  if (instance->getParsing().parseTree().has_value() &&244      !instance->getParsing().consumedWholeFile()) {245    // Parsing failed without error.246    const unsigned diagID = instance->getDiagnostics().getCustomDiagID(247        clang::DiagnosticsEngine::Error, message);248    instance->getDiagnostics().Report(diagID) << getCurrentFileOrBufferName();249    instance->getParsing().messages().Emit(250        llvm::errs(), instance->getAllCookedSources(),251        /*echoSourceLine=*/true, &features, maxErrors, warningsAreErrors);252    instance->getParsing().EmitMessage(253        llvm::errs(), instance->getParsing().finalRestingPlace(),254        "parser FAIL (final position)", "error: ", llvm::raw_ostream::RED);255    return true;256  }257  return false;258}259 260bool FrontendAction::reportFatalSemanticErrors() {261  auto &diags = instance->getDiagnostics();262  auto &sema = instance->getSemantics();263 264  if (instance->getSemantics().AnyFatalError()) {265    unsigned diagID = diags.getCustomDiagID(clang::DiagnosticsEngine::Error,266                                            "Semantic errors in %0");267    diags.Report(diagID) << getCurrentFileOrBufferName();268    sema.EmitMessages(instance->getSemaOutputStream());269 270    return true;271  }272 273  return false;274}275