/*
 * Copyright 2010-2012, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef BCC_COMPILER_H
#define BCC_COMPILER_H

namespace llvm {

class raw_ostream;
class PassManager;
class TargetData;
class TargetMachine;

} // end namespace llvm

namespace bcc {

class CompilerConfig;
class OutputFile;
class Script;

//===----------------------------------------------------------------------===//
// Design of Compiler
//===----------------------------------------------------------------------===//
// 1. A compiler instance can be constructed provided an "initial config."
// 2. A compiler can later be re-configured using config().
// 3. Once config() is invoked, it'll re-create TargetMachine instance (i.e.,
//    mTarget) according to the configuration supplied. TargetMachine instance
//    is *shared* across the different calls to compile() before the next call
//    to config().
// 4. Once a compiler instance is created, you can use the compile() service
//    to compile the file over and over again. Each call uses TargetMachine
//    instance to construct the compilation passes.
class Compiler {
public:
  enum ErrorCode {
    kSuccess,

    kInvalidConfigNoTarget,
    kErrCreateTargetMachine,
    kErrSwitchTargetMachine,
    kErrNoTargetMachine,
    kErrTargetDataNoMemory,
    kErrMaterialization,
    kErrInvalidOutputFileState,
    kErrPrepareOutput,
    kPrepareCodeGenPass,

    kErrHookBeforeAddLTOPasses,
    kErrHookAfterAddLTOPasses,
    kErrHookBeforeExecuteLTOPasses,
    kErrHookAfterExecuteLTOPasses,

    kErrHookBeforeAddCodeGenPasses,
    kErrHookAfterAddCodeGenPasses,
    kErrHookBeforeExecuteCodeGenPasses,
    kErrHookAfterExecuteCodeGenPasses,

    kMaxErrorCode,
  };

  static const char *GetErrorString(enum ErrorCode pErrCode);

private:
  llvm::TargetMachine *mTarget;
  // LTO is enabled by default.
  bool mEnableLTO;

  enum ErrorCode runLTO(Script &pScript);
  enum ErrorCode runCodeGen(Script &pScript, llvm::raw_ostream &pResult);

public:
  Compiler();
  Compiler(const CompilerConfig &pConfig);

  enum ErrorCode config(const CompilerConfig &pConfig);

  // Compile a script and output the result to a LLVM stream.
  enum ErrorCode compile(Script &pScript, llvm::raw_ostream &pResult);

  // Compile a script and output the result to a file.
  enum ErrorCode compile(Script &pScript, OutputFile &pResult);

  const llvm::TargetMachine& getTargetMachine() const
  { return *mTarget; }

  void enableLTO(bool pEnable = true)
  { mEnableLTO = pEnable; }

  virtual ~Compiler();

protected:
  //===--------------------------------------------------------------------===//
  // Plugin callbacks for sub-class.
  //===--------------------------------------------------------------------===//
  // Called before adding first pass to code-generation passes.
  virtual bool beforeAddLTOPasses(Script &pScript, llvm::PassManager &pPM)
  { return true; }

  // Called after adding last pass to code-generation passes.
  virtual bool afterAddLTOPasses(Script &pScript, llvm::PassManager &pPM)
  { return true; }

  // Called before executing code-generation passes.
  virtual bool beforeExecuteLTOPasses(Script &pScript,
                                          llvm::PassManager &pPM)
  { return true; }

  // Called after executing code-generation passes.
  virtual bool afterExecuteLTOPasses(Script &pScript)
  { return true; }

  // Called before adding first pass to code-generation passes.
  virtual bool beforeAddCodeGenPasses(Script &pScript, llvm::PassManager &pPM)
  { return true; }

  // Called after adding last pass to code-generation passes.
  virtual bool afterAddCodeGenPasses(Script &pScript, llvm::PassManager &pPM)
  { return true; }

  // Called before executing code-generation passes.
  virtual bool beforeExecuteCodeGenPasses(Script &pScript,
                                          llvm::PassManager &pPM)
  { return true; }

  // Called after executing code-generation passes.
  virtual bool afterExecuteCodeGenPasses(Script &pScript)
  { return true; }
};

} // end namespace bcc

#endif // BCC_COMPILER_H