diff --git a/lib/Target/Mips/MipsISelLowering.cpp b/lib/Target/Mips/MipsISelLowering.cpp index 28ab854c38b..d2a5541bc94 100644 --- a/lib/Target/Mips/MipsISelLowering.cpp +++ b/lib/Target/Mips/MipsISelLowering.cpp @@ -35,6 +35,24 @@ #include "llvm/Support/ErrorHandling.h" using namespace llvm; +namespace { + // If I is a shifted mask, set the size (Size) and the first bit of the + // mask (Pos), and return true. + bool IsShiftedMask(uint64_t I, unsigned SizeInBits, uint64_t &Pos, + uint64_t &Size) { + assert(SizeInBits == 32 || SizeInBits == 64); + bool Is32Bits = (SizeInBits == 32); + + if ((Is32Bits == 32 && !isShiftedMask_32(I)) || + (!Is32Bits && !isShiftedMask_64(I))) + return false; + + Size = Is32Bits ? CountPopulation_32(I) : CountPopulation_64(I); + Pos = Is32Bits ? CountTrailingZeros_32(I) : CountTrailingZeros_64(I); + return true; + } +} + const char *MipsTargetLowering::getTargetNodeName(unsigned Opcode) const { switch (Opcode) { case MipsISD::JmpLink: return "MipsISD::JmpLink"; @@ -62,6 +80,8 @@ const char *MipsTargetLowering::getTargetNodeName(unsigned Opcode) const { case MipsISD::WrapperPIC: return "MipsISD::WrapperPIC"; case MipsISD::DynAlloc: return "MipsISD::DynAlloc"; case MipsISD::Sync: return "MipsISD::Sync"; + case MipsISD::Ext: return "MipsISD::Ext"; + case MipsISD::Ins: return "MipsISD::Ins"; default: return NULL; } } @@ -111,6 +131,8 @@ MipsTargetLowering(MipsTargetMachine &TM) setOperationAction(ISD::BRCOND, MVT::Other, Custom); setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Custom); setOperationAction(ISD::VASTART, MVT::Other, Custom); + setOperationAction(ISD::AND, MVT::i32, Custom); + setOperationAction(ISD::OR, MVT::i32, Custom); setOperationAction(ISD::SDIV, MVT::i32, Expand); setOperationAction(ISD::SREM, MVT::i32, Expand); @@ -539,6 +561,8 @@ LowerOperation(SDValue Op, SelectionDAG &DAG) const case ISD::FRAMEADDR: return LowerFRAMEADDR(Op, DAG); case ISD::MEMBARRIER: return LowerMEMBARRIER(Op, DAG); case ISD::ATOMIC_FENCE: return LowerATOMIC_FENCE(Op, DAG); + case ISD::AND: return LowerAND(Op, DAG); + case ISD::OR: return LowerOR(Op, DAG); } return SDValue(); } @@ -1556,6 +1580,98 @@ SDValue MipsTargetLowering::LowerATOMIC_FENCE(SDValue Op, DAG.getConstant(SType, MVT::i32)); } +SDValue MipsTargetLowering::LowerAND(SDValue Op, SelectionDAG& DAG) const { + // Pattern match EXT. + // $dst = and ((sra or srl) $src , pos), (2**size - 1) + // => ext $dst, $src, size, pos + if (!Subtarget->isMips32r2()) + return Op; + + SDValue ShiftRight = Op.getOperand(0), Mask = Op.getOperand(1); + + // Op's first operand must be a shift right. + if (ShiftRight.getOpcode() != ISD::SRA && ShiftRight.getOpcode() != ISD::SRL) + return Op; + + // The second operand of the shift must be an immediate. + uint64_t Pos; + ConstantSDNode *CN; + if (!(CN = dyn_cast(ShiftRight.getOperand(1)))) + return Op; + + Pos = CN->getZExtValue(); + + uint64_t SMPos, SMSize; + // Op's second operand must be a shifted mask. + if (!(CN = dyn_cast(Mask)) || + !IsShiftedMask(CN->getZExtValue(), 32, SMPos, SMSize)) + return Op; + + // Return if the shifted mask does not start at bit 0 or the sum of its size + // and Pos exceeds the word's size. + if (SMPos != 0 || Pos + SMSize > 32) + return Op; + + return DAG.getNode(MipsISD::Ext, Op.getDebugLoc(), MVT::i32, + ShiftRight.getOperand(0), + DAG.getConstant(SMSize, MVT::i32), + DAG.getConstant(Pos, MVT::i32)); +} + +SDValue MipsTargetLowering::LowerOR(SDValue Op, SelectionDAG& DAG) const { + // Pattern match INS. + // $dst = or (and $src1 , mask0), (and (shl $src, pos), mask1), + // where mask1 = (2**size - 1) << pos, mask0 = ~mask1 + // => ins $dst, $src, size, pos + if (!Subtarget->isMips32r2()) + return Op; + + SDValue And0 = Op.getOperand(0), And1 = Op.getOperand(1); + uint64_t SMPos0, SMSize0, SMPos1, SMSize1; + ConstantSDNode *CN; + + // See if Op's first operand matches (and $src1 , mask0). + if (And0.getOpcode() != ISD::AND) + return Op; + + if (!(CN = dyn_cast(And0.getOperand(1))) || + !IsShiftedMask(~CN->getZExtValue(), 32, SMPos0, SMSize0)) + return Op; + + // See if Op's second operand matches (and (shl $src, pos), mask1). + if (And1.getOpcode() != ISD::AND) + return Op; + + if (!(CN = dyn_cast(And1.getOperand(1))) || + !IsShiftedMask(CN->getZExtValue(), CN->getValueSizeInBits(0), SMPos1, + SMSize1)) + return Op; + + // The shift masks must have the same position and size. + if (SMPos0 != SMPos1 || SMSize0 != SMSize1) + return Op; + + SDValue Shl = And1.getOperand(0); + if (Shl.getOpcode() != ISD::SHL) + return Op; + + if (!(CN = dyn_cast(Shl.getOperand(1)))) + return Op; + + unsigned Shamt = CN->getZExtValue(); + + // Return if the shift amount and the first bit position of mask are not the + // same. + if (Shamt != SMPos0) + return Op; + + return DAG.getNode(MipsISD::Ins, Op.getDebugLoc(), MVT::i32, + Shl.getOperand(0), + DAG.getConstant(SMSize0, MVT::i32), + DAG.getConstant(SMPos0, MVT::i32), + And0.getOperand(0)); +} + //===----------------------------------------------------------------------===// // Calling Convention Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Target/Mips/MipsISelLowering.h b/lib/Target/Mips/MipsISelLowering.h index 2d8661987a3..7e4c1f400bd 100644 --- a/lib/Target/Mips/MipsISelLowering.h +++ b/lib/Target/Mips/MipsISelLowering.h @@ -83,7 +83,10 @@ namespace llvm { DynAlloc, - Sync + Sync, + + Ext, + Ins }; } @@ -134,6 +137,8 @@ namespace llvm { SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; SDValue LowerMEMBARRIER(SDValue Op, SelectionDAG& DAG) const; SDValue LowerATOMIC_FENCE(SDValue Op, SelectionDAG& DAG) const; + SDValue LowerAND(SDValue Op, SelectionDAG& DAG) const; + SDValue LowerOR(SDValue Op, SelectionDAG& DAG) const; virtual SDValue LowerFormalArguments(SDValue Chain, diff --git a/lib/Target/Mips/MipsInstrFormats.td b/lib/Target/Mips/MipsInstrFormats.td index 9f55fb38b95..fd6422048d9 100644 --- a/lib/Target/Mips/MipsInstrFormats.td +++ b/lib/Target/Mips/MipsInstrFormats.td @@ -102,6 +102,28 @@ class FJ op, dag outs, dag ins, string asmstr, list pattern, let Inst{25-0} = addr; } +// Ext and Ins +class ExtIns _funct, string instr_asm, dag Outs, dag Ins, + list pattern, InstrItinClass itin>: + MipsInst +{ + bits<5> rt; + bits<5> rs; + bits<5> sz; + bits<5> pos; + bits<6> funct; + + let opcode = 0x1f; + let funct = _funct; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = sz; + let Inst{10-6} = pos; + let Inst{5-0} = funct; +} + //===----------------------------------------------------------------------===// // // FLOATING POINT INSTRUCTION FORMATS diff --git a/lib/Target/Mips/MipsInstrInfo.td b/lib/Target/Mips/MipsInstrInfo.td index 0680c36b6f9..a30761dbbab 100644 --- a/lib/Target/Mips/MipsInstrInfo.td +++ b/lib/Target/Mips/MipsInstrInfo.td @@ -43,6 +43,12 @@ def SDT_MipsDynAlloc : SDTypeProfile<1, 1, [SDTCisVT<0, i32>, SDTCisVT<1, iPTR>]>; def SDT_Sync : SDTypeProfile<0, 1, [SDTCisVT<0, i32>]>; +def SDT_Ext : SDTypeProfile<1, 3, [SDTCisVT<0, i32>, SDTCisSameAs<0, 1>, + SDTCisInt<2>, SDTCisSameAs<2, 3>]>; +def SDT_Ins : SDTypeProfile<1, 4, [SDTCisVT<0, i32>, SDTCisSameAs<0, 1>, + SDTCisInt<2>, SDTCisSameAs<2, 3>, + SDTCisSameAs<0, 4>]>; + // Call def MipsJmpLink : SDNode<"MipsISD::JmpLink",SDT_MipsJmpLink, [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, @@ -109,6 +115,9 @@ def MipsDynAlloc : SDNode<"MipsISD::DynAlloc", SDT_MipsDynAlloc, def MipsSync : SDNode<"MipsISD::Sync", SDT_Sync, [SDNPHasChain]>; +def MipsExt : SDNode<"MipsISD::Ext", SDT_Ext>; +def MipsIns : SDNode<"MipsISD::Ins", SDT_Ins>; + //===----------------------------------------------------------------------===// // Mips Instruction Predicate Definitions. //===----------------------------------------------------------------------===// @@ -661,6 +670,23 @@ def MUL : ArithR<0x1c, 0x02, "mul", mul, IIImul, 1>, Requires<[IsMips32]>; def RDHWR : ReadHardware; +let Predicates = [IsMips32r2] in { + def Ext : ExtIns<0b000000, "ext", (outs CPURegs:$dst), + (ins CPURegs:$src, uimm16:$size, uimm16:$pos), + [(set CPURegs:$dst, + (MipsExt CPURegs:$src, immZExt5:$size, immZExt5:$pos))], + NoItinerary>; + let Constraints = "$src1 = $dst" in + def Ins : ExtIns<0b000100, "ins", + (outs CPURegs:$dst), + (ins CPURegs:$src, uimm16:$size, uimm16:$pos, + CPURegs:$src1), + [(set CPURegs:$dst, + (MipsIns CPURegs:$src, immZExt5:$size, immZExt5:$pos, + CPURegs:$src1))], + NoItinerary>; +} + //===----------------------------------------------------------------------===// // Arbitrary patterns that map to one or more instructions //===----------------------------------------------------------------------===// diff --git a/test/CodeGen/Mips/extins.ll b/test/CodeGen/Mips/extins.ll new file mode 100644 index 00000000000..69f53e503f6 --- /dev/null +++ b/test/CodeGen/Mips/extins.ll @@ -0,0 +1,21 @@ +; RUN: llc -march=mips -mcpu=4ke < %s | FileCheck %s + +define i32 @ext0_5_9(i32 %s, i32 %pos, i32 %sz) nounwind readnone { +entry: +; CHECK: ext ${{[0-9]+}}, $4, 5, 9 + %shr = lshr i32 %s, 5 + %and = and i32 %shr, 511 + ret i32 %and +} + +define void @ins2_5_9(i32 %s, i32* nocapture %d) nounwind { +entry: +; CHECK: ins ${{[0-9]+}}, $4, 5, 9 + %and = shl i32 %s, 5 + %shl = and i32 %and, 16352 + %tmp3 = load i32* %d, align 4 + %and5 = and i32 %tmp3, -16353 + %or = or i32 %and5, %shl + store i32 %or, i32* %d, align 4 + ret void +}