/*******************************************************\
* Copyright (C) 2006, ApS s.r.o Brno, AllRightsReserved *
\*******************************************************/

/* Creates target specific DAG from general one.
 * We should try to leave this file as it is and describe
 * as much as we can in td in InstrInfo.td */

#define DEBUG_TYPE "codasip-isel"

#include "llvm/Intrinsics.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Target/TargetIntrinsicInfo.h"

#include "Codasip.h"
#include "CodasipISelLowering.h"
#include "CodasipTargetMachine.h"

#include <cstdio>
#include <iostream>
#include <cassert>

using namespace llvm;

namespace {
  
  class CodasipGenDAGToDAGISel: public SelectionDAGISel {

    protected:
      CodasipTargetMachine &TM;

    public:
      explicit CodasipGenDAGToDAGISel(CodasipTargetMachine &tm):
      SelectionDAGISel(tm), TM(tm) {}

      virtual bool SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, std::vector<SDValue> &OutOps);

      virtual const char *getPassName() const
      {return "Codasip DAG2DAG Pattern Instruction Selection";}
      
      void PreprocessISelDAG();
      void PostprocessISelDAG() {}

    protected:
      #include "CodasipGenDAGISel.inc"

      SDNode* Select(SDNode *Node);
  };

}


// invoked before the selection
void CodasipGenDAGToDAGISel::PreprocessISelDAG()
{
  // go through the nodes and find all frame indexes
  for (SelectionDAG::allnodes_iterator I = CurDAG->allnodes_begin();  I != CurDAG->allnodes_end(); ++I)
  {
    SDNode *N = I;
    if (N->getOpcode()==ISD::FrameIndex)
    {
      // === turn (fi) into (add tfi,0)
      // prepare vt list
      SDVTList vts = N->getVTList();
      // make a copy (target version)
      FrameIndexSDNode *fi = static_cast<FrameIndexSDNode*>(N);
      SDValue tfi = CurDAG->getTargetFrameIndex(fi->getIndex(), fi->getValueType(0));
      // prepare zero
      SDValue zr = CurDAG->getConstant(0, fi->getValueType(0));
      // morph the original into (add tfi,0)
      SDValue ops[2] = {tfi,zr};
      N = CurDAG->MorphNodeTo(N, ISD::ADD, vts, ops,2);
      // === now handle the case, when there already was an add
      //     original: (add fi,imm)   preprocessed: (add (add tfi,0),imm)   desired: (add tfi,0) & (add tfi,imm)
      // go through all uses of the add node (the original fi)
      for (SDNode::use_iterator i=N->use_begin(); !i.atEnd(); ++i)
      {
        // is it an add?
        if (i->getOpcode()==ISD::ADD)
        {
          // and is adding an imm/const?
          if (i->getOperand(1).getOpcode()==ISD::Constant) {
            // redirect the first operand of this add to the newly created tfi
            CurDAG->UpdateNodeOperands(*i,tfi,i->getOperand(1));
          }
        }
      }
    }
  }
  //CurDAG->viewGraph();
  // tidy up
  CurDAG->RemoveDeadNodes();
}


// we should try to leave this on automatic processes of llvm
SDNode* CodasipGenDAGToDAGISel::Select(SDNode *Node)
{
  // no manual selection here...
  //DebugLoc dl = Node->getDebugLoc();
  SDNode *ResNode;
  ResNode = SelectCode(Node);
  return ResNode;
}

// not implemented yet
bool CodasipGenDAGToDAGISel::
SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode, std::vector<SDValue> &OutOps)
{
  /*assert(false && "SelectInlineAsmMemoryOperand");
  std::cout << ConstraintCode << '\n';*/
  return true;
}

// user's reimplementation
#include "CodasipCustSelDAG.inc"

/// Creates this pass.
FunctionPass *llvm::createCodasipISelDag(CodasipTargetMachine &TM) {
  return new CodasipDAGToDAGISel(TM);
}

