brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.6 KiB · 5920ed8 Raw
382 lines · cpp
1//===--- CompilerInstance.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/CompilerInstance.h"14#include "flang/Frontend/CompilerInvocation.h"15#include "flang/Frontend/TextDiagnosticPrinter.h"16#include "flang/Parser/parsing.h"17#include "flang/Parser/provenance.h"18#include "flang/Semantics/semantics.h"19#include "flang/Support/Fortran-features.h"20#include "flang/Support/Timing.h"21#include "mlir/Support/RawOstreamExtras.h"22#include "clang/Basic/DiagnosticFrontend.h"23#include "llvm/ADT/StringExtras.h"24#include "llvm/MC/TargetRegistry.h"25#include "llvm/Pass.h"26#include "llvm/Support/Errc.h"27#include "llvm/Support/Error.h"28#include "llvm/Support/FileSystem.h"29#include "llvm/Support/Path.h"30#include "llvm/Support/raw_ostream.h"31#include "llvm/TargetParser/TargetParser.h"32#include "llvm/TargetParser/Triple.h"33 34using namespace Fortran::frontend;35 36CompilerInstance::CompilerInstance()37    : invocation(new CompilerInvocation()),38      allSources(new Fortran::parser::AllSources()),39      allCookedSources(new Fortran::parser::AllCookedSources(*allSources)),40      parsing(new Fortran::parser::Parsing(*allCookedSources)) {41  // TODO: This is a good default during development, but ultimately we should42  // give the user the opportunity to specify this.43  allSources->set_encoding(Fortran::parser::Encoding::UTF_8);44}45 46CompilerInstance::~CompilerInstance() {47  assert(outputFiles.empty() && "Still output files in flight?");48}49 50void CompilerInstance::setInvocation(51    std::shared_ptr<CompilerInvocation> value) {52  invocation = std::move(value);53}54 55void CompilerInstance::setSemaOutputStream(raw_ostream &value) {56  ownedSemaOutputStream.release();57  semaOutputStream = &value;58}59 60void CompilerInstance::setSemaOutputStream(std::unique_ptr<raw_ostream> value) {61  ownedSemaOutputStream.swap(value);62  semaOutputStream = ownedSemaOutputStream.get();63}64 65// Helper method to generate the path of the output file. The following logic66// applies:67// 1. If the user specifies the output file via `-o`, then use that (i.e.68//    the outputFilename parameter).69// 2. If the user does not specify the name of the output file, derive it from70//    the input file (i.e. inputFilename + extension)71// 3. If the output file is not specified and the input file is `-`, then set72//    the output file to `-` as well.73static std::string getOutputFilePath(llvm::StringRef outputFilename,74                                     llvm::StringRef inputFilename,75                                     llvm::StringRef extension) {76 77  // Output filename _is_ specified. Just use that.78  if (!outputFilename.empty())79    return std::string(outputFilename);80 81  // Output filename _is not_ specified. Derive it from the input file name.82  std::string outFile = "-";83  if (!extension.empty() && (inputFilename != "-")) {84    llvm::SmallString<128> path(inputFilename);85    llvm::sys::path::replace_extension(path, extension);86    outFile = std::string(path);87  }88 89  return outFile;90}91 92std::unique_ptr<llvm::raw_pwrite_stream>93CompilerInstance::createDefaultOutputFile(bool binary, llvm::StringRef baseName,94                                          llvm::StringRef extension) {95 96  // Get the path of the output file97  std::string outputFilePath =98      getOutputFilePath(getFrontendOpts().outputFile, baseName, extension);99 100  // Create the output file101  llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> os =102      createOutputFileImpl(outputFilePath, binary);103 104  // If successful, add the file to the list of tracked output files and105  // return.106  if (os) {107    outputFiles.emplace_back(OutputFile(outputFilePath));108    return std::move(*os);109  }110 111  // If unsuccessful, issue an error and return Null112  unsigned diagID = getDiagnostics().getCustomDiagID(113      clang::DiagnosticsEngine::Error, "unable to open output file '%0': '%1'");114  getDiagnostics().Report(diagID)115      << outputFilePath << llvm::errorToErrorCode(os.takeError()).message();116  return nullptr;117}118 119llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>>120CompilerInstance::createOutputFileImpl(llvm::StringRef outputFilePath,121                                       bool binary) {122 123  // Creates the file descriptor for the output file124  std::unique_ptr<llvm::raw_fd_ostream> os;125 126  std::error_code error;127  os.reset(new llvm::raw_fd_ostream(128      outputFilePath, error,129      (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF)));130  if (error) {131    return llvm::errorCodeToError(error);132  }133 134  // For seekable streams, just return the stream corresponding to the output135  // file.136  if (!binary || os->supportsSeeking())137    return std::move(os);138 139  // For non-seekable streams, we need to wrap the output stream into something140  // that supports 'pwrite' and takes care of the ownership for us.141  return std::make_unique<llvm::buffer_unique_ostream>(std::move(os));142}143 144void CompilerInstance::clearOutputFiles(bool eraseFiles) {145  for (OutputFile &of : outputFiles)146    if (!of.filename.empty() && eraseFiles)147      llvm::sys::fs::remove(of.filename);148 149  outputFiles.clear();150}151 152bool CompilerInstance::executeAction(FrontendAction &act) {153  CompilerInvocation &invoc = this->getInvocation();154 155  llvm::Triple targetTriple{llvm::Triple(invoc.getTargetOpts().triple)};156 157  // Set some sane defaults for the frontend.158  invoc.setDefaultFortranOpts();159  // Update the fortran options based on user-based input.160  invoc.setFortranOpts();161  // Set the encoding to read all input files in based on user input.162  allSources->set_encoding(invoc.getFortranOpts().encoding);163  if (!setUpTargetMachine())164    return false;165  // Set options controlling lowering to FIR.166  invoc.setLoweringOptions();167 168  if (invoc.getEnableTimers()) {169    llvm::TimePassesIsEnabled = true;170 171    timingStreamMLIR = std::make_unique<Fortran::support::string_ostream>();172    timingStreamLLVM = std::make_unique<Fortran::support::string_ostream>();173    timingStreamCodeGen = std::make_unique<Fortran::support::string_ostream>();174 175    timingMgr.setEnabled(true);176    timingMgr.setDisplayMode(mlir::DefaultTimingManager::DisplayMode::Tree);177    timingMgr.setOutput(178        Fortran::support::createTimingFormatterText(*timingStreamMLIR));179 180    // Creating a new TimingScope will automatically start the timer. Since this181    // is the top-level timer, this is ok because it will end up capturing the182    // time for all the bookkeeping and other tasks that take place between183    // parsing, lowering etc. for which finer-grained timers will be created.184    timingScopeRoot = timingMgr.getRootScope();185  }186 187  // Run the frontend action `act` for every input file.188  for (const FrontendInputFile &fif : getFrontendOpts().inputs) {189    if (act.beginSourceFile(*this, fif)) {190      if (llvm::Error err = act.execute()) {191        consumeError(std::move(err));192      }193      act.endSourceFile();194    }195  }196 197  if (timingMgr.isEnabled()) {198    timingScopeRoot.stop();199 200    // Write the timings to the associated output stream and clear all timers.201    // We need to provide another stream because the TimingManager will attempt202    // to print in its destructor even if it has been cleared. By the time that203    // destructor runs, the output streams will have been destroyed, so give it204    // a null stream.205    timingMgr.print();206    timingMgr.setOutput(207        Fortran::support::createTimingFormatterText(mlir::thread_safe_nulls()));208 209    // This prints the timings in "reverse" order, starting from code210    // generation, followed by LLVM-IR optimizations, then MLIR optimizations211    // and transformations and the frontend. If any of the steps are disabled,212    // for instance because code generation was not performed, the strings213    // will be empty.214    if (!timingStreamCodeGen->str().empty())215      llvm::errs() << timingStreamCodeGen->str() << "\n";216 217    if (!timingStreamLLVM->str().empty())218      llvm::errs() << timingStreamLLVM->str() << "\n";219 220    if (!timingStreamMLIR->str().empty())221      llvm::errs() << timingStreamMLIR->str() << "\n";222  }223 224  return !getDiagnostics().getClient()->getNumErrors();225}226 227void CompilerInstance::createDiagnostics(clang::DiagnosticConsumer *client,228                                         bool shouldOwnClient) {229  diagnostics = createDiagnostics(getDiagnosticOpts(), client, shouldOwnClient);230}231 232clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>233CompilerInstance::createDiagnostics(clang::DiagnosticOptions &opts,234                                    clang::DiagnosticConsumer *client,235                                    bool shouldOwnClient) {236  auto diags = llvm::makeIntrusiveRefCnt<clang::DiagnosticsEngine>(237      clang::DiagnosticIDs::create(), opts);238 239  // Create the diagnostic client for reporting errors or for240  // implementing -verify.241  if (client) {242    diags->setClient(client, shouldOwnClient);243  } else {244    diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));245  }246  return diags;247}248 249// Get feature string which represents combined explicit target features250// for AMD GPU and the target features specified by the user251static std::string252getExplicitAndImplicitAMDGPUTargetFeatures(clang::DiagnosticsEngine &diags,253                                           const TargetOptions &targetOpts,254                                           const llvm::Triple triple) {255  llvm::StringRef cpu = targetOpts.cpu;256  llvm::StringMap<bool> FeaturesMap;257 258  // Add target features specified by the user259  for (auto &userFeature : targetOpts.featuresAsWritten) {260    std::string userKeyString = userFeature.substr(1);261    FeaturesMap[userKeyString] = (userFeature[0] == '+');262  }263 264  auto HasError = llvm::AMDGPU::fillAMDGPUFeatureMap(cpu, triple, FeaturesMap);265  if (HasError.first) {266    unsigned diagID = diags.getCustomDiagID(clang::DiagnosticsEngine::Error,267                                            "Unsupported feature ID: %0");268    diags.Report(diagID) << HasError.second;269    return std::string();270  }271 272  llvm::SmallVector<std::string> featuresVec;273  for (auto &FeatureItem : FeaturesMap) {274    featuresVec.push_back((llvm::Twine(FeatureItem.second ? "+" : "-") +275                           FeatureItem.first().str())276                              .str());277  }278  llvm::sort(featuresVec);279  return llvm::join(featuresVec, ",");280}281 282// Get feature string which represents combined explicit target features283// for NVPTX and the target features specified by the user/284// TODO: Have a more robust target conf like `clang/lib/Basic/Targets/NVPTX.cpp`285static std::string286getExplicitAndImplicitNVPTXTargetFeatures(clang::DiagnosticsEngine &diags,287                                          const TargetOptions &targetOpts,288                                          const llvm::Triple triple) {289  llvm::StringRef cpu = targetOpts.cpu;290  llvm::StringMap<bool> implicitFeaturesMap;291  std::string errorMsg;292  bool ptxVer = false;293 294  // Add target features specified by the user295  for (auto &userFeature : targetOpts.featuresAsWritten) {296    llvm::StringRef userKeyString(llvm::StringRef(userFeature).drop_front(1));297    implicitFeaturesMap[userKeyString.str()] = (userFeature[0] == '+');298    // Check if the user provided a PTX version299    if (userKeyString.starts_with("ptx"))300      ptxVer = true;301  }302 303  // Set the default PTX version to `ptx61` if none was provided.304  // TODO: set the default PTX version based on the chip.305  if (!ptxVer)306    implicitFeaturesMap["ptx61"] = true;307 308  // Set the compute capability.309  implicitFeaturesMap[cpu.str()] = true;310 311  llvm::SmallVector<std::string> featuresVec;312  for (auto &implicitFeatureItem : implicitFeaturesMap) {313    featuresVec.push_back((llvm::Twine(implicitFeatureItem.second ? "+" : "-") +314                           implicitFeatureItem.first().str())315                              .str());316  }317  llvm::sort(featuresVec);318  return llvm::join(featuresVec, ",");319}320 321std::string CompilerInstance::getTargetFeatures() {322  const TargetOptions &targetOpts = getInvocation().getTargetOpts();323  const llvm::Triple triple(targetOpts.triple);324 325  // Clang does not append all target features to the clang -cc1 invocation.326  // Some target features are parsed implicitly by clang::TargetInfo child327  // class. Clang::TargetInfo classes are the basic clang classes and328  // they cannot be reused by Flang.329  // That's why we need to extract implicit target features and add330  // them to the target features specified by the user331  if (triple.isAMDGPU()) {332    return getExplicitAndImplicitAMDGPUTargetFeatures(getDiagnostics(),333                                                      targetOpts, triple);334  } else if (triple.isNVPTX()) {335    return getExplicitAndImplicitNVPTXTargetFeatures(getDiagnostics(),336                                                     targetOpts, triple);337  }338  return llvm::join(targetOpts.featuresAsWritten.begin(),339                    targetOpts.featuresAsWritten.end(), ",");340}341 342bool CompilerInstance::setUpTargetMachine() {343  const TargetOptions &targetOpts = getInvocation().getTargetOpts();344  const std::string &theTriple = targetOpts.triple;345 346  // Create `Target`347  const llvm::Triple triple(theTriple);348  std::string error;349  const llvm::Target *theTarget =350      llvm::TargetRegistry::lookupTarget(triple, error);351  if (!theTarget) {352    getDiagnostics().Report(clang::diag::err_fe_unable_to_create_target)353        << error;354    return false;355  }356  // Create `TargetMachine`357  const auto &CGOpts = getInvocation().getCodeGenOpts();358  std::optional<llvm::CodeGenOptLevel> OptLevelOrNone =359      llvm::CodeGenOpt::getLevel(CGOpts.OptimizationLevel);360  assert(OptLevelOrNone && "Invalid optimization level!");361  llvm::CodeGenOptLevel OptLevel = *OptLevelOrNone;362  std::string featuresStr = getTargetFeatures();363  std::optional<llvm::CodeModel::Model> cm = getCodeModel(CGOpts.CodeModel);364 365  llvm::TargetOptions tOpts = llvm::TargetOptions();366  tOpts.EnableAIXExtendedAltivecABI = targetOpts.EnableAIXExtendedAltivecABI;367 368  targetMachine.reset(theTarget->createTargetMachine(369      triple, /*CPU=*/targetOpts.cpu,370      /*Features=*/featuresStr, /*Options=*/tOpts,371      /*Reloc::Model=*/CGOpts.getRelocationModel(),372      /*CodeModel::Model=*/cm, OptLevel));373  assert(targetMachine && "Failed to create TargetMachine");374  if (cm.has_value()) {375    if ((cm == llvm::CodeModel::Medium || cm == llvm::CodeModel::Large) &&376        triple.getArch() == llvm::Triple::x86_64) {377      targetMachine->setLargeDataThreshold(CGOpts.LargeDataThreshold);378    }379  }380  return true;381}382