// Select - Convert the specified operand from a target-independent to a
// target-specific node if it hasn't already been changed.
SDNode *IA64DAGToDAGISel::Select(SDOperand Op) {
  SDNode *N = Op.Val;
  if (N->getOpcode() >= ISD::BUILTIN_OP_END &&
      N->getOpcode() < IA64ISD::FIRST_NUMBER)
    return NULL;   // Already selected.

  switch (N->getOpcode()) {
  default: break;

  case IA64ISD::BRCALL: { // XXX: this is also a hack!
    SDOperand Chain = N->getOperand(0);
    SDOperand InFlag;  // Null incoming flag value.

    AddToISelQueue(Chain);
    if(N->getNumOperands()==3) { // we have an incoming chain, callee and flag
      InFlag = N->getOperand(2);
      AddToISelQueue(InFlag);
    }

    unsigned CallOpcode;
    SDOperand CallOperand;
    
    // if we can call directly, do so
    if (GlobalAddressSDNode *GASD =
      dyn_cast<GlobalAddressSDNode>(N->getOperand(1))) {
      CallOpcode = IA64::BRCALL_IPREL_GA;
      CallOperand = CurDAG->getTargetGlobalAddress(GASD->getGlobal(), MVT::i64);
    } else if (isa<ExternalSymbolSDNode>(N->getOperand(1))) {
      // FIXME: we currently NEED this case for correctness, to avoid
      // "non-pic code with imm reloc.n against dynamic symbol" errors
    CallOpcode = IA64::BRCALL_IPREL_ES;
    CallOperand = N->getOperand(1);
  } else {
    // otherwise we need to load the function descriptor,
    // load the branch target (function)'s entry point and GP,
    // branch (call) then restore the GP
    SDOperand FnDescriptor = N->getOperand(1);
    AddToISelQueue(FnDescriptor);
   
    // load the branch target's entry point [mem] and 
    // GP value [mem+8]
    SDOperand targetEntryPoint=
      SDOperand(CurDAG->getTargetNode(IA64::LD8, MVT::i64, FnDescriptor), 0);
    Chain = targetEntryPoint.getValue(1);
    SDOperand targetGPAddr=
      SDOperand(CurDAG->getTargetNode(IA64::ADDS, MVT::i64, 
                                      FnDescriptor,
                                      CurDAG->getConstant(8, MVT::i64)), 0);
    Chain = targetGPAddr.getValue(1);
    SDOperand targetGP =
      SDOperand(CurDAG->getTargetNode(IA64::LD8, MVT::i64, targetGPAddr), 0);
    Chain = targetGP.getValue(1);

    Chain = CurDAG->getCopyToReg(Chain, IA64::r1, targetGP, InFlag);
    InFlag = Chain.getValue(1);
    Chain = CurDAG->getCopyToReg(Chain, IA64::B6, targetEntryPoint, InFlag); // FLAG these?
    InFlag = Chain.getValue(1);
    
    CallOperand = CurDAG->getRegister(IA64::B6, MVT::i64);
    CallOpcode = IA64::BRCALL_INDIRECT;
  }
 
   // Finally, once everything is setup, emit the call itself
   if(InFlag.Val)
     Chain = SDOperand(CurDAG->getTargetNode(CallOpcode, MVT::Other, MVT::Flag,
                                             CallOperand, InFlag), 0);
   else // there might be no arguments
     Chain = SDOperand(CurDAG->getTargetNode(CallOpcode, MVT::Other, MVT::Flag,
                                             CallOperand, Chain), 0);
   InFlag = Chain.getValue(1);

   std::vector<SDOperand> CallResults;

   CallResults.push_back(Chain);
   CallResults.push_back(InFlag);

   for (unsigned i = 0, e = CallResults.size(); i != e; ++i)
     ReplaceUses(Op.getValue(i), CallResults[i]);
   return NULL;
  }
  
  case IA64ISD::GETFD: {
    SDOperand Input = N->getOperand(0);
    AddToISelQueue(Input);
    return CurDAG->getTargetNode(IA64::GETFD, MVT::i64, Input);
  } 
  
  case ISD::FDIV:
  case ISD::SDIV:
  case ISD::UDIV:
  case ISD::SREM:
  case ISD::UREM:
    return SelectDIV(Op);
 
  case ISD::TargetConstantFP: {
    SDOperand Chain = CurDAG->getEntryNode(); // this is a constant, so..

    SDOperand V;
    if (cast<ConstantFPSDNode>(N)->isExactlyValue(+0.0)) {
      V = CurDAG->getCopyFromReg(Chain, IA64::F0, MVT::f64);
    } else if (cast<ConstantFPSDNode>(N)->isExactlyValue(+1.0)) {
      V = CurDAG->getCopyFromReg(Chain, IA64::F1, MVT::f64);
    } else
      assert(0 && "Unexpected FP constant!");
    
    ReplaceUses(SDOperand(N, 0), V);
    return 0;
  }

  case ISD::FrameIndex: { // TODO: reduce creepyness
    int FI = cast<FrameIndexSDNode>(N)->getIndex();
    if (N->hasOneUse())
      return CurDAG->SelectNodeTo(N, IA64::MOV, MVT::i64,
                                  CurDAG->getTargetFrameIndex(FI, MVT::i64));
    else
      return CurDAG->getTargetNode(IA64::MOV, MVT::i64,
                                   CurDAG->getTargetFrameIndex(FI, MVT::i64));
  }

  case ISD::ConstantPool: { // TODO: nuke the constant pool
    // (ia64 doesn't need one)
    ConstantPoolSDNode *CP = cast<ConstantPoolSDNode>(N);
    Constant *C = CP->getConstVal();
    SDOperand CPI = CurDAG->getTargetConstantPool(C, MVT::i64,
                                                  CP->getAlignment());
    return CurDAG->getTargetNode(IA64::ADDL_GA, MVT::i64, // ?
                                 CurDAG->getRegister(IA64::r1, MVT::i64), CPI);
  }

  case ISD::GlobalAddress: {
    GlobalValue *GV = cast<GlobalAddressSDNode>(N)->getGlobal();
    SDOperand GA = CurDAG->getTargetGlobalAddress(GV, MVT::i64);
    SDOperand Tmp =
      SDOperand(CurDAG->getTargetNode(IA64::ADDL_GA, MVT::i64, 
                                      CurDAG->getRegister(IA64::r1,
                                                          MVT::i64), GA), 0);
    return CurDAG->getTargetNode(IA64::LD8, MVT::i64, Tmp);
  }
  
/* XXX
   case ISD::ExternalSymbol: {
     SDOperand EA = CurDAG->getTargetExternalSymbol(
       cast<ExternalSymbolSDNode>(N)->getSymbol(),
       MVT::i64);
     SDOperand Tmp = CurDAG->getTargetNode(IA64::ADDL_EA, MVT::i64, 
                                           CurDAG->getRegister(IA64::r1,
                                                               MVT::i64),
                                           EA);
     return CurDAG->getTargetNode(IA64::LD8, MVT::i64, Tmp);
   }
*/

  case ISD::LOAD: { // FIXME: load -1, not 1, for bools?
    LoadSDNode *LD = cast<LoadSDNode>(N);
    SDOperand Chain = LD->getChain();
    SDOperand Address = LD->getBasePtr();
    AddToISelQueue(Chain);
    AddToISelQueue(Address);

    MVT::ValueType TypeBeingLoaded = LD->getLoadedVT();
    unsigned Opc;
    switch (TypeBeingLoaded) {
    default:
#ifndef NDEBUG
      N->dump();
#endif
      assert(0 && "Cannot load this type!");
    case MVT::i1: { // this is a bool
      Opc = IA64::LD1; // first we load a byte, then compare for != 0
      if(N->getValueType(0) == MVT::i1) { // XXX: early exit!
        return CurDAG->SelectNodeTo(N, IA64::CMPNE, MVT::i1, MVT::Other, 
                    SDOperand(CurDAG->getTargetNode(Opc, MVT::i64, Address), 0),
                                    CurDAG->getRegister(IA64::r0, MVT::i64), 
                                    Chain);
      }
      /* otherwise, we want to load a bool into something bigger: LD1
         will do that for us, so we just fall through */
    }
    case MVT::i8:  Opc = IA64::LD1; break;
    case MVT::i16: Opc = IA64::LD2; break;
    case MVT::i32: Opc = IA64::LD4; break;
    case MVT::i64: Opc = IA64::LD8; break;
    
    case MVT::f32: Opc = IA64::LDF4; break;
    case MVT::f64: Opc = IA64::LDF8; break;
    }

    // TODO: comment this
    return CurDAG->SelectNodeTo(N, Opc, N->getValueType(0), MVT::Other,
                                Address, Chain);
  }
  
  case ISD::STORE: {
    StoreSDNode *ST = cast<StoreSDNode>(N);
    SDOperand Address = ST->getBasePtr();
    SDOperand Chain = ST->getChain();
    AddToISelQueue(Address);
    AddToISelQueue(Chain);
   
    unsigned Opc;
    if (ISD::isNON_TRUNCStore(N)) {
      switch (N->getOperand(1).getValueType()) {
      default: assert(0 && "unknown type in store");
      case MVT::i1: { // this is a bool
        Opc = IA64::ST1; // we store either 0 or 1 as a byte 
        // first load zero!
        SDOperand Initial = CurDAG->getCopyFromReg(Chain, IA64::r0, MVT::i64);
        Chain = Initial.getValue(1);
        // then load 1 into the same reg iff the predicate to store is 1
        SDOperand Tmp = ST->getValue();
        AddToISelQueue(Tmp);
        Tmp =
          SDOperand(CurDAG->getTargetNode(IA64::TPCADDS, MVT::i64, Initial,
                                          CurDAG->getTargetConstant(1, MVT::i64),
                                          Tmp), 0);
        return CurDAG->SelectNodeTo(N, Opc, MVT::Other, Address, Tmp, Chain);
      }
      case MVT::i64: Opc = IA64::ST8;  break;
      case MVT::f64: Opc = IA64::STF8; break;
      }
    } else { // Truncating store
      switch(ST->getStoredVT()) {
      default: assert(0 && "unknown type in truncstore");
      case MVT::i8:  Opc = IA64::ST1;  break;
      case MVT::i16: Opc = IA64::ST2;  break;
      case MVT::i32: Opc = IA64::ST4;  break;
      case MVT::f32: Opc = IA64::STF4; break;
      }
    }
    
    SDOperand N1 = N->getOperand(1);
    SDOperand N2 = N->getOperand(2);
    AddToISelQueue(N1);
    AddToISelQueue(N2);
    return CurDAG->SelectNodeTo(N, Opc, MVT::Other, N2, N1, Chain);
  }

  case ISD::BRCOND: {
    SDOperand Chain = N->getOperand(0);
    SDOperand CC = N->getOperand(1);
    AddToISelQueue(Chain);
    AddToISelQueue(CC);
    MachineBasicBlock *Dest =
      cast<BasicBlockSDNode>(N->getOperand(2))->getBasicBlock();
    //FIXME - we do NOT need long branches all the time
    return CurDAG->SelectNodeTo(N, IA64::BRLCOND_NOTCALL, MVT::Other, CC, 
                                CurDAG->getBasicBlock(Dest), Chain);
  }

  case ISD::CALLSEQ_START:
  case ISD::CALLSEQ_END: {
    int64_t Amt = cast<ConstantSDNode>(N->getOperand(1))->getValue();
    unsigned Opc = N->getOpcode() == ISD::CALLSEQ_START ?
      IA64::ADJUSTCALLSTACKDOWN : IA64::ADJUSTCALLSTACKUP;
    SDOperand N0 = N->getOperand(0);
    AddToISelQueue(N0);
    return CurDAG->SelectNodeTo(N, Opc, MVT::Other, getI64Imm(Amt), N0);
  }

  case ISD::BR:
    // FIXME: we don't need long branches all the time!
    SDOperand N0 = N->getOperand(0);
    AddToISelQueue(N0);
    return CurDAG->SelectNodeTo(N, IA64::BRL_NOTCALL, MVT::Other, 
                                N->getOperand(1), N0);
  }
  
  return SelectCode(Op);
}