/*
 * Copyright (C) 2013 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_SEA_IR_CODE_GEN_CODE_GEN_H_
#define ART_COMPILER_SEA_IR_CODE_GEN_CODE_GEN_H_

#include "instruction_set.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Analysis/Verifier.h"
#include "sea_ir/ir/visitor.h"

namespace sea_ir {
// Abstracts away the containers we use to map SEA IR objects to LLVM IR objects.
class CodeGenData {
 public:
  explicit CodeGenData(): context_(&llvm::getGlobalContext()), module_("sea_ir", *context_),
      builder_(*context_), function_(), blocks_(), values_() { }
  // Returns the llvm::BasicBlock* corresponding to the sea_ir::Region with id @region_id.
  llvm::BasicBlock* GetBlock(int region_id) {
    std::map<int, llvm::BasicBlock*>::iterator block_it = blocks_.find(region_id);
    DCHECK(block_it != blocks_.end());
    return block_it->second;
  }
  // Returns the llvm::BasicBlock* corresponding top the sea_ir::Region @region.
  llvm::BasicBlock* GetBlock(Region* region) {
    return GetBlock(region->Id());
  }
  // Records @block as corresponding to the sea_ir::Region with id @region_id.
  void AddBlock(int region_id, llvm::BasicBlock* block) {
    blocks_.insert(std::pair<int, llvm::BasicBlock*>(region_id, block));
  }
  // Records @block as corresponding to the sea_ir::Region with @region.
  void AddBlock(Region* region, llvm::BasicBlock* block) {
    AddBlock(region->Id(), block);
  }

  llvm::Value* GetValue(int instruction_id) {
    std::map<int, llvm::Value*>::iterator value_it = values_.find(instruction_id);
    DCHECK(value_it != values_.end());
    return value_it->second;
  }
  // Returns the llvm::Value* corresponding to the output of @instruction.
  llvm::Value* GetValue(InstructionNode* instruction) {
    return GetValue(instruction->Id());
  }
  // Records @value as corresponding to the sea_ir::InstructionNode with id @instruction_id.
  void AddValue(int instruction_id, llvm::Value* value) {
    values_.insert(std::pair<int, llvm::Value*>(instruction_id, value));
  }
  // Records @value as corresponding to the sea_ir::InstructionNode  @instruction.
  void AddValue(InstructionNode* instruction, llvm::Value* value) {
      AddValue(instruction->Id(), value);
  }
  // Generates and returns in @elf the executable code corresponding to the llvm module
  //
  std::string GetElf(art::InstructionSet instruction_set);

  llvm::LLVMContext* const context_;
  llvm::Module module_;
  llvm::IRBuilder<> builder_;
  llvm::Function* function_;

 private:
  std::map<int, llvm::BasicBlock*> blocks_;
  std::map<int, llvm::Value*> values_;
};

class CodeGenPassVisitor: public IRVisitor {
 public:
  explicit CodeGenPassVisitor(CodeGenData* cgd): llvm_data_(cgd) { }
  CodeGenPassVisitor(): llvm_data_(new CodeGenData()) { }
  // Initialize any data structure needed before the start of visiting.
  virtual void Initialize(SeaGraph* graph);
  CodeGenData* GetData() {
    return llvm_data_;
  }
  void Write(std::string file) {
      llvm_data_->module_.dump();
      llvm::verifyFunction(*llvm_data_->function_);
    }

 protected:
  CodeGenData* const llvm_data_;
};

class CodeGenPrepassVisitor: public CodeGenPassVisitor {
 public:
  explicit CodeGenPrepassVisitor(const std::string& function_name):
    function_name_(function_name) { }
  void Visit(SeaGraph* graph);
  void Visit(SignatureNode* region);
  void Visit(Region* region);
  void Visit(InstructionNode* instruction) { }

  void Visit(UnnamedConstInstructionNode* instruction) { }
  void Visit(ConstInstructionNode* instruction) { }
  void Visit(ReturnInstructionNode* instruction) { }
  void Visit(IfNeInstructionNode* instruction) { }
  // void Visit(AddIntLitInstructionNode* instruction) { }
  void Visit(MoveResultInstructionNode* instruction) { }
  void Visit(InvokeStaticInstructionNode* instruction) { }
  void Visit(AddIntInstructionNode* instruction) { }
  void Visit(GotoInstructionNode* instruction) { }
  void Visit(IfEqzInstructionNode* instruction) { }
  void Visit(PhiInstructionNode* region);

 private:
  std::string function_name_;
};

class CodeGenPostpassVisitor: public CodeGenPassVisitor {
 public:
  explicit CodeGenPostpassVisitor(CodeGenData* code_gen_data): CodeGenPassVisitor(code_gen_data) { }
  void Visit(SeaGraph* graph);
  void Visit(SignatureNode* region);
  void Visit(Region* region);
  void Visit(InstructionNode* region) { }
  void Visit(UnnamedConstInstructionNode* instruction) { }
  void Visit(ConstInstructionNode* instruction) { }
  void Visit(ReturnInstructionNode* instruction) { }
  void Visit(IfNeInstructionNode* instruction) { }
  // void Visit(AddIntLitInstructionNode* instruction) { }
  void Visit(MoveResultInstructionNode* instruction) { }
  void Visit(InvokeStaticInstructionNode* instruction) { }
  void Visit(AddIntInstructionNode* instruction) { }
  void Visit(GotoInstructionNode* instruction) { }
  void Visit(IfEqzInstructionNode* instruction) { }
  void Visit(PhiInstructionNode* region);
};

class CodeGenVisitor: public CodeGenPassVisitor {
 public:
  explicit CodeGenVisitor(CodeGenData* code_gen_data,
      const art::DexFile& dex_file): CodeGenPassVisitor(code_gen_data), dex_file_(dex_file) { }
  void Visit(SeaGraph* graph);
  void Visit(SignatureNode* region);
  void Visit(Region* region);
  void Visit(InstructionNode* region);
  void Visit(UnnamedConstInstructionNode* instruction);
  void Visit(ConstInstructionNode* instruction);
  void Visit(ReturnInstructionNode* instruction);
  void Visit(IfNeInstructionNode* instruction);
  void Visit(MoveResultInstructionNode* instruction);
  void Visit(InvokeStaticInstructionNode* instruction);
  void Visit(AddIntInstructionNode* instruction);
  void Visit(GotoInstructionNode* instruction);
  void Visit(IfEqzInstructionNode* instruction);
  void Visit(PhiInstructionNode* region) { }

 private:
  std::string function_name_;
  const art::DexFile& dex_file_;
};
}  // namespace sea_ir
#endif  // ART_COMPILER_SEA_IR_CODE_GEN_CODE_GEN_H_