/*
 * Copyright (C) 2011 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 ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_
#define ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_

#include <llvm/ADT/ArrayRef.h>
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>

#include "invoke_type.h"
#include "compiled_method.h"
#include "dex/compiler_enums.h"
#include "dex/compiler_ir.h"
#include "dex/backend.h"
#include "llvm/intrinsic_helper.h"
#include "llvm/llvm_compilation_unit.h"
#include "safe_map.h"

namespace llvm {
  class Module;
  class LLVMContext;
}

namespace art {

namespace llvm {
  class IntrinsicHelper;
  class IRBuilder;
}

class LLVMInfo {
  public:
    LLVMInfo();
    ~LLVMInfo();

    ::llvm::LLVMContext* GetLLVMContext() {
      return llvm_context_.get();
    }

    ::llvm::Module* GetLLVMModule() {
      return llvm_module_;
    }

    art::llvm::IntrinsicHelper* GetIntrinsicHelper() {
      return intrinsic_helper_.get();
    }

    art::llvm::IRBuilder* GetIRBuilder() {
      return ir_builder_.get();
    }

  private:
    std::unique_ptr< ::llvm::LLVMContext> llvm_context_;
    ::llvm::Module* llvm_module_;  // Managed by context_.
    std::unique_ptr<art::llvm::IntrinsicHelper> intrinsic_helper_;
    std::unique_ptr<art::llvm::IRBuilder> ir_builder_;
};

struct BasicBlock;
struct CallInfo;
struct CompilationUnit;
struct MIR;
struct RegLocation;
struct RegisterInfo;
class MIRGraph;

// Target-specific initialization.
Backend* PortableCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
                               ArenaAllocator* const arena,
                               llvm::LlvmCompilationUnit* const llvm_compilation_unit);

class MirConverter : public Backend {
  public:
    // TODO: flesh out and integrate into new world order.
    MirConverter(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena,
                 llvm::LlvmCompilationUnit* llvm_compilation_unit)
      : Backend(arena),
        cu_(cu),
        mir_graph_(mir_graph),
        llvm_compilation_unit_(llvm_compilation_unit),
        llvm_info_(llvm_compilation_unit->GetQuickContext()),
        symbol_(llvm_compilation_unit->GetDexCompilationUnit()->GetSymbol()),
        context_(NULL),
        module_(NULL),
        func_(NULL),
        intrinsic_helper_(NULL),
        irb_(NULL),
        placeholder_bb_(NULL),
        entry_bb_(NULL),
        entry_target_bb_(NULL),
        llvm_values_(arena, mir_graph->GetNumSSARegs()),
        temp_name_(0),
        current_dalvik_offset_(0) {
      if (kIsDebugBuild) {
        cu->enable_debug |= (1 << kDebugVerifyBitcode);
      }
    }

    void Materialize() {
      MethodMIR2Bitcode();
    }

    CompiledMethod* GetCompiledMethod() {
      return NULL;
    }

  private:
    ::llvm::BasicBlock* GetLLVMBlock(int id);
    ::llvm::Value* GetLLVMValue(int s_reg);
    void SetVregOnValue(::llvm::Value* val, int s_reg);
    void DefineValueOnly(::llvm::Value* val, int s_reg);
    void DefineValue(::llvm::Value* val, int s_reg);
    ::llvm::Type* LlvmTypeFromLocRec(RegLocation loc);
    void InitIR();
    ::llvm::BasicBlock* FindCaseTarget(uint32_t vaddr);
    void ConvertPackedSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
                             RegLocation rl_src);
    void ConvertSparseSwitch(BasicBlock* bb, MIR* mir, int32_t table_offset,
                             RegLocation rl_src);
    void ConvertSget(int32_t field_index,
                     art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
    void ConvertSput(int32_t field_index,
                     art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src);
    void ConvertFillArrayData(int32_t offset, RegLocation rl_array);
    ::llvm::Value* EmitConst(::llvm::ArrayRef< ::llvm::Value*> src,
                             RegLocation loc);
    void EmitPopShadowFrame();
    ::llvm::Value* EmitCopy(::llvm::ArrayRef< ::llvm::Value*> src,
                            RegLocation loc);
    void ConvertMoveException(RegLocation rl_dest);
    void ConvertThrow(RegLocation rl_src);
    void ConvertMonitorEnterExit(int opt_flags,
                                 art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_src);
    void ConvertArrayLength(int opt_flags, RegLocation rl_dest,
                            RegLocation rl_src);
    void EmitSuspendCheck();
    ::llvm::Value* ConvertCompare(ConditionCode cc,
                                  ::llvm::Value* src1, ::llvm::Value* src2);
    void ConvertCompareAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc,
                                 RegLocation rl_src1, RegLocation rl_src2);
    void ConvertCompareZeroAndBranch(BasicBlock* bb, MIR* mir, ConditionCode cc,
                                     RegLocation rl_src1);
    ::llvm::Value* GenDivModOp(bool is_div, bool is_long, ::llvm::Value* src1,
                               ::llvm::Value* src2);
    ::llvm::Value* GenArithOp(OpKind op, bool is_long, ::llvm::Value* src1,
                              ::llvm::Value* src2);
    void ConvertFPArithOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
                          RegLocation rl_src2);
    void ConvertShift(art::llvm::IntrinsicHelper::IntrinsicId id,
                      RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
    void ConvertShiftLit(art::llvm::IntrinsicHelper::IntrinsicId id,
                         RegLocation rl_dest, RegLocation rl_src, int shift_amount);
    void ConvertArithOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
                        RegLocation rl_src2);
    void ConvertArithOpLit(OpKind op, RegLocation rl_dest, RegLocation rl_src1,
                           int32_t imm);
    void ConvertInvoke(BasicBlock* bb, MIR* mir, InvokeType invoke_type,
                       bool is_range, bool is_filled_new_array);
    void ConvertConstObject(uint32_t idx,
                            art::llvm::IntrinsicHelper::IntrinsicId id, RegLocation rl_dest);
    void ConvertCheckCast(uint32_t type_idx, RegLocation rl_src);
    void ConvertNewInstance(uint32_t type_idx, RegLocation rl_dest);
    void ConvertNewArray(uint32_t type_idx, RegLocation rl_dest,
                         RegLocation rl_src);
    void ConvertAget(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
                     RegLocation rl_dest, RegLocation rl_array, RegLocation rl_index);
    void ConvertAput(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
                     RegLocation rl_src, RegLocation rl_array, RegLocation rl_index);
    void ConvertIget(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
                     RegLocation rl_dest, RegLocation rl_obj, int field_index);
    void ConvertIput(int opt_flags, art::llvm::IntrinsicHelper::IntrinsicId id,
                     RegLocation rl_src, RegLocation rl_obj, int field_index);
    void ConvertInstanceOf(uint32_t type_idx, RegLocation rl_dest,
                           RegLocation rl_src);
    void ConvertIntToLong(RegLocation rl_dest, RegLocation rl_src);
    void ConvertLongToInt(RegLocation rl_dest, RegLocation rl_src);
    void ConvertFloatToDouble(RegLocation rl_dest, RegLocation rl_src);
    void ConvertDoubleToFloat(RegLocation rl_dest, RegLocation rl_src);
    void ConvertWideComparison(art::llvm::IntrinsicHelper::IntrinsicId id,
                               RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
    void ConvertIntNarrowing(RegLocation rl_dest, RegLocation rl_src,
                             art::llvm::IntrinsicHelper::IntrinsicId id);
    void ConvertNeg(RegLocation rl_dest, RegLocation rl_src);
    void ConvertIntToFP(::llvm::Type* ty, RegLocation rl_dest, RegLocation rl_src);
    void ConvertFPToInt(art::llvm::IntrinsicHelper::IntrinsicId id,
                        RegLocation rl_dest, RegLocation rl_src);
    void ConvertNegFP(RegLocation rl_dest, RegLocation rl_src);
    void ConvertNot(RegLocation rl_dest, RegLocation rl_src);
    void EmitConstructorBarrier();
    bool ConvertMIRNode(MIR* mir, BasicBlock* bb, ::llvm::BasicBlock* llvm_bb);
    void SetDexOffset(int32_t offset);
    void SetMethodInfo();
    void HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb);
    void ConvertExtendedMIR(BasicBlock* bb, MIR* mir, ::llvm::BasicBlock* llvm_bb);
    bool BlockBitcodeConversion(BasicBlock* bb);
    ::llvm::FunctionType* GetFunctionType();
    bool CreateFunction();
    bool CreateLLVMBasicBlock(BasicBlock* bb);
    void MethodMIR2Bitcode();

    CompilationUnit* cu_;
    MIRGraph* mir_graph_;
    llvm::LlvmCompilationUnit* const llvm_compilation_unit_;
    LLVMInfo* llvm_info_;
    std::string symbol_;
    ::llvm::LLVMContext* context_;
    ::llvm::Module* module_;
    ::llvm::Function* func_;
    art::llvm::IntrinsicHelper* intrinsic_helper_;
    art::llvm::IRBuilder* irb_;
    ::llvm::BasicBlock* placeholder_bb_;
    ::llvm::BasicBlock* entry_bb_;
    ::llvm::BasicBlock* entry_target_bb_;
    std::string bitcode_filename_;
    GrowableArray< ::llvm::Value*> llvm_values_;
    int32_t temp_name_;
    SafeMap<int32_t, ::llvm::BasicBlock*> id_to_block_map_;  // block id -> llvm bb.
    int current_dalvik_offset_;
};  // Class MirConverter

}  // namespace art

#endif  // ART_COMPILER_DEX_PORTABLE_MIR_TO_GBC_H_
