//===- BlackfinISelDAGToDAG.cpp - A dag to dag inst selector for Blackfin -===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines an instruction selector for the Blackfin target.
//
//===----------------------------------------------------------------------===//

#include "Blackfin.h"
#include "BlackfinTargetMachine.h"
#include "BlackfinRegisterInfo.h"
#include "llvm/Intrinsics.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

//===----------------------------------------------------------------------===//
// Instruction Selector Implementation
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
/// BlackfinDAGToDAGISel - Blackfin specific code to select blackfin machine
/// instructions for SelectionDAG operations.
namespace {
  class BlackfinDAGToDAGISel : public SelectionDAGISel {
    /// Subtarget - Keep a pointer to the Blackfin Subtarget around so that we
    /// can make the right decision when generating code for different targets.
    //const BlackfinSubtarget &Subtarget;
  public:
    BlackfinDAGToDAGISel(BlackfinTargetMachine &TM, CodeGenOpt::Level OptLevel)
      : SelectionDAGISel(TM, OptLevel) {}

    virtual void PostprocessISelDAG();

    virtual const char *getPassName() const {
      return "Blackfin DAG->DAG Pattern Instruction Selection";
    }

    // Include the pieces autogenerated from the target description.
#include "BlackfinGenDAGISel.inc"

  private:
    SDNode *Select(SDNode *N);
    bool SelectADDRspii(SDValue Addr, SDValue &Base, SDValue &Offset);

    // Walk the DAG after instruction selection, fixing register class issues.
    void FixRegisterClasses(SelectionDAG &DAG);

    const BlackfinInstrInfo &getInstrInfo() {
      return *static_cast<const BlackfinTargetMachine&>(TM).getInstrInfo();
    }
    const BlackfinRegisterInfo *getRegisterInfo() {
      return static_cast<const BlackfinTargetMachine&>(TM).getRegisterInfo();
    }
  };
}  // end anonymous namespace

FunctionPass *llvm::createBlackfinISelDag(BlackfinTargetMachine &TM,
                                          CodeGenOpt::Level OptLevel) {
  return new BlackfinDAGToDAGISel(TM, OptLevel);
}

void BlackfinDAGToDAGISel::PostprocessISelDAG() {
  FixRegisterClasses(*CurDAG);
}

SDNode *BlackfinDAGToDAGISel::Select(SDNode *N) {
  if (N->isMachineOpcode())
    return NULL;   // Already selected.

  switch (N->getOpcode()) {
  default: break;
  case ISD::FrameIndex: {
    // Selects to ADDpp FI, 0 which in turn will become ADDimm7 SP, imm or ADDpp
    // SP, Px
    int FI = cast<FrameIndexSDNode>(N)->getIndex();
    SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i32);
    return CurDAG->SelectNodeTo(N, BF::ADDpp, MVT::i32, TFI,
                                CurDAG->getTargetConstant(0, MVT::i32));
  }
  }

  return SelectCode(N);
}

bool BlackfinDAGToDAGISel::SelectADDRspii(SDValue Addr,
                                          SDValue &Base,
                                          SDValue &Offset) {
  FrameIndexSDNode *FIN = 0;
  if ((FIN = dyn_cast<FrameIndexSDNode>(Addr))) {
    Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32);
    Offset = CurDAG->getTargetConstant(0, MVT::i32);
    return true;
  }
  if (Addr.getOpcode() == ISD::ADD) {
    ConstantSDNode *CN = 0;
    if ((FIN = dyn_cast<FrameIndexSDNode>(Addr.getOperand(0))) &&
        (CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1))) &&
        (CN->getSExtValue() % 4 == 0 && CN->getSExtValue() >= 0)) {
      // Constant positive word offset from frame index
      Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32);
      Offset = CurDAG->getTargetConstant(CN->getSExtValue(), MVT::i32);
      return true;
    }
  }
  return false;
}

static inline bool isCC(const TargetRegisterClass *RC) {
  return BF::AnyCCRegClass.hasSubClassEq(RC);
}

static inline bool isDCC(const TargetRegisterClass *RC) {
  return BF::DRegClass.hasSubClassEq(RC) || isCC(RC);
}

static void UpdateNodeOperand(SelectionDAG &DAG,
                              SDNode *N,
                              unsigned Num,
                              SDValue Val) {
  SmallVector<SDValue, 8> ops(N->op_begin(), N->op_end());
  ops[Num] = Val;
  SDNode *New = DAG.UpdateNodeOperands(N, ops.data(), ops.size());
  DAG.ReplaceAllUsesWith(N, New);
}

// After instruction selection, insert COPY_TO_REGCLASS nodes to help in
// choosing the proper register classes.
void BlackfinDAGToDAGISel::FixRegisterClasses(SelectionDAG &DAG) {
  const BlackfinInstrInfo &TII = getInstrInfo();
  const BlackfinRegisterInfo *TRI = getRegisterInfo();
  DAG.AssignTopologicalOrder();
  HandleSDNode Dummy(DAG.getRoot());

  for (SelectionDAG::allnodes_iterator NI = DAG.allnodes_begin();
       NI != DAG.allnodes_end(); ++NI) {
    if (NI->use_empty() || !NI->isMachineOpcode())
      continue;
    const MCInstrDesc &DefMCID = TII.get(NI->getMachineOpcode());
    for (SDNode::use_iterator UI = NI->use_begin(); !UI.atEnd(); ++UI) {
      if (!UI->isMachineOpcode())
        continue;

      if (UI.getUse().getResNo() >= DefMCID.getNumDefs())
        continue;
      const TargetRegisterClass *DefRC =
        TII.getRegClass(DefMCID, UI.getUse().getResNo(), TRI);

      const MCInstrDesc &UseMCID = TII.get(UI->getMachineOpcode());
      if (UseMCID.getNumDefs()+UI.getOperandNo() >= UseMCID.getNumOperands())
        continue;
      const TargetRegisterClass *UseRC =
        TII.getRegClass(UseMCID, UseMCID.getNumDefs()+UI.getOperandNo(), TRI);
      if (!DefRC || !UseRC)
        continue;
      // We cannot copy CC <-> !(CC/D)
      if ((isCC(DefRC) && !isDCC(UseRC)) || (isCC(UseRC) && !isDCC(DefRC))) {
        SDNode *Copy =
          DAG.getMachineNode(TargetOpcode::COPY_TO_REGCLASS,
                             NI->getDebugLoc(),
                             MVT::i32,
                             UI.getUse().get(),
                             DAG.getTargetConstant(BF::DRegClassID, MVT::i32));
        UpdateNodeOperand(DAG, *UI, UI.getOperandNo(), SDValue(Copy, 0));
      }
    }
  }
  DAG.setRoot(Dummy.getValue());
}

