#include "llvm/Support/Debug.h"
using namespace llvm;
+static const unsigned arm_dsubreg_0 = 5;
+static const unsigned arm_dsubreg_1 = 6;
+
//===--------------------------------------------------------------------===//
/// ARMDAGToDAGISel - ARM specific code to select ARM machine
/// instructions for SelectionDAG operations.
///
namespace {
class ARMDAGToDAGISel : public SelectionDAGISel {
- ARMTargetMachine &TM;
+ ARMBaseTargetMachine &TM;
/// Subtarget - Keep a pointer to the ARMSubtarget around so that we can
/// make the right decision when generating code for different targets.
const ARMSubtarget *Subtarget;
public:
- explicit ARMDAGToDAGISel(ARMTargetMachine &tm)
+ explicit ARMDAGToDAGISel(ARMBaseTargetMachine &tm)
: SelectionDAGISel(tm), TM(tm),
Subtarget(&TM.getSubtarget<ARMSubtarget>()) {
}
virtual const char *getPassName() const {
return "ARM Instruction Selection";
- }
-
+ }
+
+ /// getI32Imm - Return a target constant with the specified value, of type i32.
+ inline SDValue getI32Imm(unsigned Imm) {
+ return CurDAG->getTargetConstant(Imm, MVT::i32);
+ }
+
SDNode *Select(SDValue Op);
virtual void InstructionSelect();
+ bool SelectShifterOperandReg(SDValue Op, SDValue N, SDValue &A,
+ SDValue &B, SDValue &C);
bool SelectAddrMode2(SDValue Op, SDValue N, SDValue &Base,
SDValue &Offset, SDValue &Opc);
bool SelectAddrMode2Offset(SDValue Op, SDValue N,
bool SelectThumbAddrModeSP(SDValue Op, SDValue N, SDValue &Base,
SDValue &OffImm);
- bool SelectShifterOperandReg(SDValue Op, SDValue N, SDValue &A,
- SDValue &B, SDValue &C);
+ bool SelectT2ShifterOperandReg(SDValue Op, SDValue N,
+ SDValue &BaseReg, SDValue &Opc);
+ bool SelectT2AddrModeImm12(SDValue Op, SDValue N, SDValue &Base,
+ SDValue &OffImm);
+ bool SelectT2AddrModeImm8(SDValue Op, SDValue N, SDValue &Base,
+ SDValue &OffImm);
+ bool SelectT2AddrModeSoReg(SDValue Op, SDValue N, SDValue &Base,
+ SDValue &OffReg, SDValue &ShImm);
+
// Include the pieces autogenerated from the target description.
#include "ARMGenDAGISel.inc"
+
+private:
+ /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
+ /// inline asm expressions.
+ virtual bool SelectInlineAsmMemoryOperand(const SDValue &Op,
+ char ConstraintCode,
+ std::vector<SDValue> &OutOps);
};
}
CurDAG->RemoveDeadNodes();
}
+bool ARMDAGToDAGISel::SelectShifterOperandReg(SDValue Op,
+ SDValue N,
+ SDValue &BaseReg,
+ SDValue &ShReg,
+ SDValue &Opc) {
+ ARM_AM::ShiftOpc ShOpcVal = ARM_AM::getShiftOpcForNode(N);
+
+ // Don't match base register only case. That is matched to a separate
+ // lower complexity pattern with explicit register operand.
+ if (ShOpcVal == ARM_AM::no_shift) return false;
+
+ BaseReg = N.getOperand(0);
+ unsigned ShImmVal = 0;
+ if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
+ ShReg = CurDAG->getRegister(0, MVT::i32);
+ ShImmVal = RHS->getZExtValue() & 31;
+ } else {
+ ShReg = N.getOperand(1);
+ }
+ Opc = CurDAG->getTargetConstant(ARM_AM::getSORegOpc(ShOpcVal, ShImmVal),
+ MVT::i32);
+ return true;
+}
+
bool ARMDAGToDAGISel::SelectAddrMode2(SDValue Op, SDValue N,
SDValue &Base, SDValue &Offset,
SDValue &Opc) {
return false;
}
-bool ARMDAGToDAGISel::SelectShifterOperandReg(SDValue Op,
- SDValue N,
- SDValue &BaseReg,
- SDValue &ShReg,
- SDValue &Opc) {
+bool ARMDAGToDAGISel::SelectT2ShifterOperandReg(SDValue Op, SDValue N,
+ SDValue &BaseReg,
+ SDValue &Opc) {
ARM_AM::ShiftOpc ShOpcVal = ARM_AM::getShiftOpcForNode(N);
// Don't match base register only case. That is matched to a separate
// lower complexity pattern with explicit register operand.
if (ShOpcVal == ARM_AM::no_shift) return false;
-
+
BaseReg = N.getOperand(0);
unsigned ShImmVal = 0;
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
- ShReg = CurDAG->getRegister(0, MVT::i32);
ShImmVal = RHS->getZExtValue() & 31;
- } else {
- ShReg = N.getOperand(1);
+ Opc = getI32Imm(ARM_AM::getSORegOpc(ShOpcVal, ShImmVal));
+ return true;
}
- Opc = CurDAG->getTargetConstant(ARM_AM::getSORegOpc(ShOpcVal, ShImmVal),
- MVT::i32);
+
+ return false;
+}
+
+bool ARMDAGToDAGISel::SelectT2AddrModeImm12(SDValue Op, SDValue N,
+ SDValue &Base, SDValue &OffImm) {
+ // Match simple R + imm12 operands.
+ if (N.getOpcode() != ISD::ADD)
+ return false;
+
+ if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
+ int RHSC = (int)RHS->getZExtValue();
+ if (RHSC >= 0 && RHSC < 0x1000) { // 12 bits.
+ Base = N.getOperand(0);
+ OffImm = CurDAG->getTargetConstant(RHSC, MVT::i32);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ARMDAGToDAGISel::SelectT2AddrModeImm8(SDValue Op, SDValue N,
+ SDValue &Base, SDValue &OffImm) {
+ if (N.getOpcode() == ISD::ADD) {
+ if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
+ int RHSC = (int)RHS->getZExtValue();
+ if (RHSC < 0 && RHSC > -0x100) { // 8 bits.
+ Base = N.getOperand(0);
+ OffImm = CurDAG->getTargetConstant(RHSC, MVT::i32);
+ return true;
+ }
+ }
+ } else if (N.getOpcode() == ISD::SUB) {
+ if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
+ int RHSC = (int)RHS->getZExtValue();
+ if (RHSC >= 0 && RHSC < 0x100) { // 8 bits.
+ Base = N.getOperand(0);
+ OffImm = CurDAG->getTargetConstant(-RHSC, MVT::i32);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ARMDAGToDAGISel::SelectT2AddrModeSoReg(SDValue Op, SDValue N,
+ SDValue &Base,
+ SDValue &OffReg, SDValue &ShImm) {
+ // Base only.
+ if (N.getOpcode() != ISD::ADD && N.getOpcode() != ISD::SUB) {
+ Base = N;
+ if (N.getOpcode() == ISD::FrameIndex) {
+ int FI = cast<FrameIndexSDNode>(N)->getIndex();
+ Base = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
+ } else if (N.getOpcode() == ARMISD::Wrapper) {
+ Base = N.getOperand(0);
+ if (Base.getOpcode() == ISD::TargetConstantPool)
+ return false; // We want to select t2LDRpci instead.
+ }
+ OffReg = CurDAG->getRegister(0, MVT::i32);
+ ShImm = CurDAG->getTargetConstant(0, MVT::i32);
+ return true;
+ }
+
+ // Look for (R + R) or (R + (R << [1,2,3])).
+ unsigned ShAmt = 0;
+ Base = N.getOperand(0);
+ OffReg = N.getOperand(1);
+
+ // Swap if it is ((R << c) + R).
+ ARM_AM::ShiftOpc ShOpcVal = ARM_AM::getShiftOpcForNode(OffReg);
+ if (ShOpcVal != ARM_AM::lsl) {
+ ShOpcVal = ARM_AM::getShiftOpcForNode(Base);
+ if (ShOpcVal == ARM_AM::lsl)
+ std::swap(Base, OffReg);
+ }
+
+ if (ShOpcVal == ARM_AM::lsl) {
+ // Check to see if the RHS of the shift is a constant, if not, we can't fold
+ // it.
+ if (ConstantSDNode *Sh = dyn_cast<ConstantSDNode>(OffReg.getOperand(1))) {
+ ShAmt = Sh->getZExtValue();
+ if (ShAmt >= 4) {
+ ShAmt = 0;
+ ShOpcVal = ARM_AM::no_shift;
+ } else
+ OffReg = OffReg.getOperand(0);
+ } else {
+ ShOpcVal = ARM_AM::no_shift;
+ }
+ } else if (SelectT2AddrModeImm12(Op, N, Base, ShImm) ||
+ SelectT2AddrModeImm8 (Op, N, Base, ShImm))
+ // Don't match if it's possible to match to one of the r +/- imm cases.
+ return false;
+
+ ShImm = CurDAG->getTargetConstant(ShAmt, MVT::i32);
+
return true;
}
+//===--------------------------------------------------------------------===//
+
/// getAL - Returns a ARMCC::AL immediate node.
static inline SDValue getAL(SelectionDAG *CurDAG) {
return CurDAG->getTargetConstant((uint64_t)ARMCC::AL, MVT::i32);
case ISD::Constant: {
unsigned Val = cast<ConstantSDNode>(N)->getZExtValue();
bool UseCP = true;
- if (Subtarget->isThumb())
- UseCP = (Val > 255 && // MOV
- ~Val > 255 && // MOV + MVN
- !ARM_AM::isThumbImmShiftedVal(Val)); // MOV + LSL
- else
+ if (Subtarget->isThumb()) {
+ if (Subtarget->hasThumb2())
+ // Thumb2 has the MOVT instruction, so all immediates can
+ // be done with MOV + MOVT, at worst.
+ UseCP = 0;
+ else
+ UseCP = (Val > 255 && // MOV
+ ~Val > 255 && // MOV + MVN
+ !ARM_AM::isThumbImmShiftedVal(Val)); // MOV + LSL
+ } else
UseCP = (ARM_AM::getSOImmVal(Val) == -1 && // MOV
ARM_AM::getSOImmVal(~Val) == -1 && // MVN
!ARM_AM::isSOImmTwoPartVal(Val)); // two instrs.
// Selects to ADDri FI, 0 which in turn will become ADDri SP, imm.
int FI = cast<FrameIndexSDNode>(N)->getIndex();
SDValue TFI = CurDAG->getTargetFrameIndex(FI, TLI.getPointerTy());
- if (Subtarget->isThumb())
+ if (Subtarget->isThumb()) {
return CurDAG->SelectNodeTo(N, ARM::tADDrSPi, MVT::i32, TFI,
CurDAG->getTargetConstant(0, MVT::i32));
- else {
+ } else {
SDValue Ops[] = { TFI, CurDAG->getTargetConstant(0, MVT::i32),
getAL(CurDAG), CurDAG->getRegister(0, MVT::i32),
CurDAG->getRegister(0, MVT::i32) };
}
}
case ISD::ADD: {
+ if (!Subtarget->isThumb())
+ break;
// Select add sp, c to tADDhirr.
SDValue N0 = Op.getOperand(0);
SDValue N1 = Op.getOperand(1);
std::swap(LHSR, RHSR);
}
if (RHSR && RHSR->getReg() == ARM::SP) {
- return CurDAG->SelectNodeTo(N, ARM::tADDhirr, Op.getValueType(), N0, N1);
+ SDValue Val = SDValue(CurDAG->getTargetNode(ARM::tMOVlor2hir, dl,
+ Op.getValueType(), N0, N0), 0);
+ return CurDAG->SelectNodeTo(N, ARM::tADDhirr, Op.getValueType(), Val, N1);
}
break;
}
// Emits: (tBcc:void (bb:Other):$dst, (imm:i32):$cc)
// Pattern complexity = 6 cost = 1 size = 0
- unsigned Opc = Subtarget->isThumb() ? ARM::tBcc : ARM::Bcc;
+ // Pattern: (ARMbrcond:void (bb:Other):$dst, (imm:i32):$cc)
+ // Emits: (t2Bcc:void (bb:Other):$dst, (imm:i32):$cc)
+ // Pattern complexity = 6 cost = 1 size = 0
+
+ unsigned Opc = Subtarget->isThumb() ?
+ ((Subtarget->hasThumb2()) ? ARM::t2Bcc : ARM::tBcc) : ARM::Bcc;
SDValue Chain = Op.getOperand(0);
SDValue N1 = Op.getOperand(1);
SDValue N2 = Op.getOperand(2);
SDValue N1 = Op.getOperand(1);
SDValue N2 = Op.getOperand(2);
FrameIndexSDNode *FINode = dyn_cast<FrameIndexSDNode>(N1);
- if (!FINode)
- break;
+ // FIXME: handle VLAs.
+ if (!FINode) {
+ ReplaceUses(Op.getValue(0), Chain);
+ return NULL;
+ }
if (N2.getOpcode() == ARMISD::PIC_ADD && isa<LoadSDNode>(N2.getOperand(0)))
N2 = N2.getOperand(0);
LoadSDNode *Ld = dyn_cast<LoadSDNode>(N2);
- if (!Ld)
- break;
+ if (!Ld) {
+ ReplaceUses(Op.getValue(0), Chain);
+ return NULL;
+ }
SDValue BasePtr = Ld->getBasePtr();
assert(BasePtr.getOpcode() == ARMISD::Wrapper &&
isa<ConstantPoolSDNode>(BasePtr.getOperand(0)) &&
GV = ACPV->getGV();
} else
GV = dyn_cast<GlobalValue>(CP->getConstVal());
- if (GV) {
- SDValue Tmp1 = CurDAG->getTargetFrameIndex(FINode->getIndex(),
- TLI.getPointerTy());
- SDValue Tmp2 = CurDAG->getTargetGlobalAddress(GV, TLI.getPointerTy());
- SDValue Ops[] = { Tmp1, Tmp2, Chain };
- return CurDAG->getTargetNode(TargetInstrInfo::DECLARE, dl,
- MVT::Other, Ops, 3);
+ if (!GV) {
+ ReplaceUses(Op.getValue(0), Chain);
+ return NULL;
}
+
+ SDValue Tmp1 = CurDAG->getTargetFrameIndex(FINode->getIndex(),
+ TLI.getPointerTy());
+ SDValue Tmp2 = CurDAG->getTargetGlobalAddress(GV, TLI.getPointerTy());
+ SDValue Ops[] = { Tmp1, Tmp2, Chain };
+ return CurDAG->getTargetNode(TargetInstrInfo::DECLARE, dl,
+ MVT::Other, Ops, 3);
+ }
+
+ case ISD::CONCAT_VECTORS: {
+ MVT VT = Op.getValueType();
+ assert(VT.is128BitVector() && Op.getNumOperands() == 2 &&
+ "unexpected CONCAT_VECTORS");
+ SDValue N0 = Op.getOperand(0);
+ SDValue N1 = Op.getOperand(1);
+ SDNode *Result =
+ CurDAG->getTargetNode(TargetInstrInfo::IMPLICIT_DEF, dl, VT);
+ if (N0.getOpcode() != ISD::UNDEF)
+ Result = CurDAG->getTargetNode(TargetInstrInfo::INSERT_SUBREG, dl, VT,
+ SDValue(Result, 0), N0,
+ CurDAG->getTargetConstant(arm_dsubreg_0,
+ MVT::i32));
+ if (N1.getOpcode() != ISD::UNDEF)
+ Result = CurDAG->getTargetNode(TargetInstrInfo::INSERT_SUBREG, dl, VT,
+ SDValue(Result, 0), N1,
+ CurDAG->getTargetConstant(arm_dsubreg_1,
+ MVT::i32));
+ return Result;
+ }
+
+ case ISD::VECTOR_SHUFFLE: {
+ MVT VT = Op.getValueType();
+
+ // Match 128-bit splat to VDUPLANEQ. (This could be done with a Pat in
+ // ARMInstrNEON.td but it is awkward because the shuffle mask needs to be
+ // transformed first into a lane number and then to both a subregister
+ // index and an adjusted lane number.) If the source operand is a
+ // SCALAR_TO_VECTOR, leave it so it will be matched later as a VDUP.
+ ShuffleVectorSDNode *SVOp = cast<ShuffleVectorSDNode>(N);
+ if (VT.is128BitVector() && SVOp->isSplat() &&
+ Op.getOperand(0).getOpcode() != ISD::SCALAR_TO_VECTOR &&
+ Op.getOperand(1).getOpcode() == ISD::UNDEF) {
+ unsigned LaneVal = SVOp->getSplatIndex();
+
+ MVT HalfVT;
+ unsigned Opc = 0;
+ switch (VT.getVectorElementType().getSimpleVT()) {
+ default: assert(false && "unhandled VDUP splat type");
+ case MVT::i8: Opc = ARM::VDUPLN8q; HalfVT = MVT::v8i8; break;
+ case MVT::i16: Opc = ARM::VDUPLN16q; HalfVT = MVT::v4i16; break;
+ case MVT::i32: Opc = ARM::VDUPLN32q; HalfVT = MVT::v2i32; break;
+ case MVT::f32: Opc = ARM::VDUPLNfq; HalfVT = MVT::v2f32; break;
+ }
+
+ // The source operand needs to be changed to a subreg of the original
+ // 128-bit operand, and the lane number needs to be adjusted accordingly.
+ unsigned NumElts = VT.getVectorNumElements() / 2;
+ unsigned SRVal = (LaneVal < NumElts ? arm_dsubreg_0 : arm_dsubreg_1);
+ SDValue SR = CurDAG->getTargetConstant(SRVal, MVT::i32);
+ SDValue NewLane = CurDAG->getTargetConstant(LaneVal % NumElts, MVT::i32);
+ SDNode *SubReg = CurDAG->getTargetNode(TargetInstrInfo::EXTRACT_SUBREG,
+ dl, HalfVT, N->getOperand(0), SR);
+ return CurDAG->SelectNodeTo(N, Opc, VT, SDValue(SubReg, 0), NewLane);
+ }
+
break;
}
}
return SelectCode(Op);
}
+bool ARMDAGToDAGISel::
+SelectInlineAsmMemoryOperand(const SDValue &Op, char ConstraintCode,
+ std::vector<SDValue> &OutOps) {
+ assert(ConstraintCode == 'm' && "unexpected asm memory constraint");
+
+ SDValue Base, Offset, Opc;
+ if (!SelectAddrMode2(Op, Op, Base, Offset, Opc))
+ return true;
+
+ OutOps.push_back(Base);
+ OutOps.push_back(Offset);
+ OutOps.push_back(Opc);
+ return false;
+}
+
/// createARMISelDag - This pass converts a legalized DAG into a
/// ARM-specific DAG, ready for instruction scheduling.
///
-FunctionPass *llvm::createARMISelDag(ARMTargetMachine &TM) {
+FunctionPass *llvm::createARMISelDag(ARMBaseTargetMachine &TM) {
return new ARMDAGToDAGISel(TM);
}