//===- FrontendActions.h -----------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_FRONTEND_FRONTENDACTIONS_H
#define FORTRAN_FRONTEND_FRONTENDACTIONS_H

#include "flang/Frontend/CodeGenOptions.h"
#include "flang/Frontend/FrontendAction.h"
#include "flang/Parser/parsing.h"
#include "flang/Semantics/semantics.h"

#include "mlir/IR/BuiltinOps.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Module.h"
#include "llvm/Target/TargetMachine.h"
#include <memory>

namespace Fortran::frontend {

// TODO: This is a copy from f18.cpp. It doesn't really belong here and should
// be moved to a more suitable place in future.
struct MeasurementVisitor {
  template <typename A>
  bool Pre(const A &) {
    return true;
  }
  template <typename A>
  void Post(const A &) {
    ++objects;
    bytes += sizeof(A);
  }
  size_t objects{0}, bytes{0};
};

//===----------------------------------------------------------------------===//
// Custom Consumer Actions
//===----------------------------------------------------------------------===//

class InputOutputTestAction : public FrontendAction {
  void executeAction() override;
};

class InitOnlyAction : public FrontendAction {
  void executeAction() override;
};

//===----------------------------------------------------------------------===//
// Prescan Actions
//===----------------------------------------------------------------------===//
class PrescanAction : public FrontendAction {
  void executeAction() override = 0;
  bool beginSourceFileAction() override;
};

class PrintPreprocessedAction : public PrescanAction {
  void executeAction() override;
};

class DebugDumpProvenanceAction : public PrescanAction {
  void executeAction() override;
};

class DebugDumpParsingLogAction : public PrescanAction {
  void executeAction() override;
};

class DebugMeasureParseTreeAction : public PrescanAction {
  void executeAction() override;
};

//===----------------------------------------------------------------------===//
// PrescanAndParse Actions
//===----------------------------------------------------------------------===//
class PrescanAndParseAction : public FrontendAction {
  void executeAction() override = 0;
  bool beginSourceFileAction() override;
};

class DebugUnparseNoSemaAction : public PrescanAndParseAction {
  void executeAction() override;
};

class DebugDumpParseTreeNoSemaAction : public PrescanAndParseAction {
  void executeAction() override;
};

//===----------------------------------------------------------------------===//
// PrescanAndSema Actions
//
// These actions will parse the input, run the semantic checks and execute
// their actions provided that no parsing or semantic errors were found.
//===----------------------------------------------------------------------===//
class PrescanAndSemaAction : public FrontendAction {

  void executeAction() override = 0;
  bool beginSourceFileAction() override;
};

class DebugUnparseWithSymbolsAction : public PrescanAndSemaAction {
  void executeAction() override;
};

class DebugUnparseAction : public PrescanAndSemaAction {
  void executeAction() override;
};

class DebugDumpSymbolsAction : public PrescanAndSemaAction {
  void executeAction() override;
};

class DebugDumpParseTreeAction : public PrescanAndSemaAction {
  void executeAction() override;
};

class DebugDumpPFTAction : public PrescanAndSemaAction {
  void executeAction() override;
};

class DebugPreFIRTreeAction : public PrescanAndSemaAction {
  void executeAction() override;
};

class GetDefinitionAction : public PrescanAndSemaAction {
  void executeAction() override;
};

class GetSymbolsSourcesAction : public PrescanAndSemaAction {
  void executeAction() override;
};

class ParseSyntaxOnlyAction : public PrescanAndSemaAction {
  void executeAction() override;
};

class PluginParseTreeAction : public PrescanAndSemaAction {
  void executeAction() override = 0;

public:
  Fortran::parser::Parsing &getParsing();
  /// Creates an output file. This is just a wrapper for calling
  /// CreateDefaultOutputFile from CompilerInstance. Use it to make sure that
  /// your plugin respects driver's `-o` flag.
  /// \param extension  The extension to use for the output file (ignored when
  ///                   the user decides to print to stdout via `-o -`)
  /// \return           Null on error, ostream for the output file otherwise
  std::unique_ptr<llvm::raw_pwrite_stream>
  createOutputFile(llvm::StringRef extension);
};

//===----------------------------------------------------------------------===//
// PrescanAndSemaDebug Actions
//
// These actions will parse the input, run the semantic checks and execute
// their actions _regardless of_ whether any semantic errors have been found.
// This can be useful when adding new languge feature and when you wish to
// investigate compiler output (e.g. the parse tree) despite any semantic
// errors.
//
// NOTE: Use with care and for development only!
//===----------------------------------------------------------------------===//
class PrescanAndSemaDebugAction : public FrontendAction {

  void executeAction() override = 0;
  bool beginSourceFileAction() override;
};

class DebugDumpAllAction : public PrescanAndSemaDebugAction {
  void executeAction() override;
};

//===----------------------------------------------------------------------===//
// CodeGen Actions
//===----------------------------------------------------------------------===//
/// Represents the type of "backend" action to perform by the corresponding
/// CodeGenAction. Note that from Flang's perspective, both LLVM and MLIR are
/// "backends" that are used for generating LLVM IR/BC, assembly files or
/// machine code. This enum captures "what" exactly one of these backends is to
/// do. The names are similar to what is used in Clang - this allows us to
/// maintain some level of consistency/similarity between the drivers.
enum class BackendActionTy {
  Backend_EmitAssembly, ///< Emit native assembly files
  Backend_EmitObj,      ///< Emit native object files
  Backend_EmitBC,       ///< Emit LLVM bitcode files
  Backend_EmitLL,       ///< Emit human-readable LLVM assembly
  Backend_EmitMLIR      ///< Emit MLIR files
};

/// Abstract base class for actions that generate code (MLIR, LLVM IR, assembly
/// and machine code). Every action that inherits from this class will at
/// least run the prescanning, parsing, semantic checks and lower the parse
/// tree to an MLIR module.
class CodeGenAction : public FrontendAction {

  void executeAction() override;
  /// Runs prescan, parsing, sema and lowers to MLIR.
  bool beginSourceFileAction() override;
  /// Sets up LLVM's TargetMachine.
  void setUpTargetMachine();
  /// Runs the optimization (aka middle-end) pipeline on the LLVM module
  /// associated with this action.
  void runOptimizationPipeline(llvm::raw_pwrite_stream &os);

protected:
  CodeGenAction(BackendActionTy act) : action{act} {};
  /// @name MLIR
  /// {
  std::unique_ptr<mlir::ModuleOp> mlirModule;
  std::unique_ptr<mlir::MLIRContext> mlirCtx;
  /// }

  /// @name LLVM IR
  std::unique_ptr<llvm::LLVMContext> llvmCtx;
  std::unique_ptr<llvm::Module> llvmModule;

  /// Generates an LLVM IR module from CodeGenAction::mlirModule and saves it
  /// in CodeGenAction::llvmModule.
  void generateLLVMIR();

  BackendActionTy action;

  std::unique_ptr<llvm::TargetMachine> tm;
  /// }
public:
  ~CodeGenAction() override;
};

class EmitMLIRAction : public CodeGenAction {
public:
  EmitMLIRAction() : CodeGenAction(BackendActionTy::Backend_EmitMLIR) {}
};

class EmitLLVMAction : public CodeGenAction {
public:
  EmitLLVMAction() : CodeGenAction(BackendActionTy::Backend_EmitLL) {}
};

class EmitLLVMBitcodeAction : public CodeGenAction {
public:
  EmitLLVMBitcodeAction() : CodeGenAction(BackendActionTy::Backend_EmitBC) {}
};

class EmitObjAction : public CodeGenAction {
public:
  EmitObjAction() : CodeGenAction(BackendActionTy::Backend_EmitObj) {}
};

class EmitAssemblyAction : public CodeGenAction {
public:
  EmitAssemblyAction() : CodeGenAction(BackendActionTy::Backend_EmitAssembly) {}
};

} // namespace Fortran::frontend

#endif // FORTRAN_FRONTEND_FRONTENDACTIONS_H
