//===-- BasicBlockBuilder.h - Java bytecode BasicBlock builder --*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains a BasicBlock builder that creates a LLVM
// BasicBlocks from Java bytecode. It also keeps track of the java
// bytecode indices that bound each basic block.
//
//===----------------------------------------------------------------------===//

#include <llvm/Java/BytecodeParser.h>
#include <llvm/Java/ClassFile.h>
#include <llvm/ADT/STLExtras.h>
#include <llvm/ADT/StringExtras.h>
#include <llvm/Instructions.h>
#include <map>

namespace llvm { namespace Java {

  class BasicBlockBuilder : public BytecodeParser<BasicBlockBuilder> {
    Function* function_;
    typedef std::map<unsigned, BasicBlock*> BC2BBMap;
    BC2BBMap bc2bbMap_;
    typedef std::map<BasicBlock*, std::pair<unsigned,unsigned> > BB2BCMap;
    BB2BCMap bb2bcMap_;

    BasicBlock* getOrCreateBasicBlockAt(unsigned bcI) {
      BC2BBMap::iterator it = bc2bbMap_.lower_bound(bcI);
      if (it == bc2bbMap_.end() || it->first != bcI) {
        bool inserted;
        BasicBlock* newBB = new BasicBlock("bc" + utostr(bcI), function_);
        tie(it, inserted) = bc2bbMap_.insert(std::make_pair(bcI, newBB));
        assert(inserted && "LLVM basic block multiply defined!");
      }

      return it->second;
    }

  public:
    BasicBlockBuilder(Function* f, CodeAttribute* c)
      : function_(f) {

      // Create the entry block.
      BasicBlock* entry = new BasicBlock("entry", function_);

      // Create the block for bytecode 0 (bb0).
      BasicBlock* bb = getOrCreateBasicBlockAt(0);

      // Create basic blocks for exception handlers.
      const CodeAttribute::Exceptions& exceptions = c->getExceptions();
      for (unsigned i = 0, e = exceptions.size(); i != e; ++i)
        getOrCreateBasicBlockAt(exceptions[i]->getHandlerPc());

      // Parse the bytecode and create basic blocks for all targets of
      // control flow instructions.
      parse(c->getCode(), 0, c->getCodeSize());

      // Build the bytecode->basic block map.
      for (BC2BBMap::const_iterator i = bc2bbMap_.begin(), e = bc2bbMap_.end();
           i != e; ++i) {
        unsigned end = next(i) != e ? next(i)->first : c->getCodeSize();
        bb2bcMap_.insert(
          std::make_pair(i->second, std::make_pair(i->first, end)));
      }

      assert(function_->getEntryBlock().getName() == "entry");
      assert(bb2bcMap_.find(&function_->getEntryBlock()) == bb2bcMap_.end());
    }

    BasicBlock* getBasicBlock(unsigned bcI) const {
      BC2BBMap::const_iterator i = bc2bbMap_.find(bcI);
      assert(i != bc2bbMap_.end() &&
             "No block is mapped to this bytecode index!");
      return i->second;
    }

    std::pair<unsigned,unsigned> getBytecodeIndices(BasicBlock* bb) const {
      BB2BCMap::const_iterator i = bb2bcMap_.find(bb);
      assert(i != bb2bcMap_.end() &&
             "BasicBlock was not created by this builder!");
      return i->second;
    }

    void do_ifeq(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_ifne(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_iflt(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_ifge(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_ifgt(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_ifle(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_if_icmpeq(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_if_icmpne(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_if_icmplt(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_if_icmpgt(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_if_icmpge(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_if_icmple(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_if_acmpeq(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_if_acmpne(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_jsr(unsigned t, unsigned r) {
      getOrCreateBasicBlockAt(r);
      getOrCreateBasicBlockAt(t);
    }

    void do_switch_common(unsigned defTarget, const SwitchCases& sw) {
      for (unsigned i = 0, e = sw.size(); i != e; ++i) {
        unsigned target = sw[i].second;
        getOrCreateBasicBlockAt(target);
      }
      getOrCreateBasicBlockAt(defTarget);
    }

    void do_tableswitch(unsigned defTarget, const SwitchCases& sw) {
      do_switch_common(defTarget, sw);
    }

    void do_lookupswitch(unsigned defTarget, const SwitchCases& sw) {
      do_switch_common(defTarget, sw);
    }

    void do_goto(unsigned target) {
      getOrCreateBasicBlockAt(target);
    }

    void do_ifnull(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }

    void do_ifnonnull(unsigned t, unsigned f) {
      getOrCreateBasicBlockAt(f);
      getOrCreateBasicBlockAt(t);
    }
  };

} } // namespace llvm::Java
