From e56764bad10621ac9dcf9d3541533ff2cb0f88b4 Mon Sep 17 00:00:00 2001 From: Weiming Zhao Date: Fri, 16 Nov 2012 21:55:34 +0000 Subject: Remove hard coded registers in ARM ldrexd and strexd instructions This patch replaces the hard coded GPR pair [R0, R1] of Intrinsic:arm_ldrexd and [R2, R3] of Intrinsic:arm_strexd with even/odd GPRPair reg class. Similar to the lowering of atomic_64 operation. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@168207 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/ARM/ARMISelDAGToDAG.cpp | 94 +++++++++++++++------------ lib/Target/ARM/ARMISelLowering.cpp | 58 +++++++++++++---- lib/Target/ARM/ARMInstrInfo.td | 10 +-- lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 46 +++++++++++-- lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp | 38 +++++++++++ lib/Target/ARM/InstPrinter/ARMInstPrinter.h | 1 + 6 files changed, 185 insertions(+), 62 deletions(-) (limited to 'lib/Target') diff --git a/lib/Target/ARM/ARMISelDAGToDAG.cpp b/lib/Target/ARM/ARMISelDAGToDAG.cpp index efd6d2b839..92fd4c8fa0 100644 --- a/lib/Target/ARM/ARMISelDAGToDAG.cpp +++ b/lib/Target/ARM/ARMISelDAGToDAG.cpp @@ -266,6 +266,7 @@ private: std::vector &OutOps); // Form pairs of consecutive S, D, or Q registers. + SDNode *createGPRPairNode(EVT VT, SDValue V0, SDValue V1); SDNode *PairSRegs(EVT VT, SDValue V0, SDValue V1); SDNode *PairDRegs(EVT VT, SDValue V0, SDValue V1); SDNode *PairQRegs(EVT VT, SDValue V0, SDValue V1); @@ -1444,6 +1445,17 @@ SDNode *ARMDAGToDAGISel::SelectT2IndexedLoad(SDNode *N) { return NULL; } +/// \brief Form a GPRPair pseudo register from a pair of GPR regs. +SDNode *ARMDAGToDAGISel::createGPRPairNode(EVT VT, SDValue V0, SDValue V1) { + DebugLoc dl = V0.getNode()->getDebugLoc(); + SDValue RegClass = + CurDAG->getTargetConstant(ARM::GPRPairRegClassID, MVT::i32); + SDValue SubReg0 = CurDAG->getTargetConstant(ARM::gsub_0, MVT::i32); + SDValue SubReg1 = CurDAG->getTargetConstant(ARM::gsub_1, MVT::i32); + const SDValue Ops[] = { RegClass, V0, SubReg0, V1, SubReg1 }; + return CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, dl, VT, Ops, 5); +} + /// PairSRegs - Form a D register from a pair of S registers. /// SDNode *ARMDAGToDAGISel::PairSRegs(EVT VT, SDValue V0, SDValue V1) { @@ -3009,17 +3021,19 @@ SDNode *ARMDAGToDAGISel::Select(SDNode *N) { DebugLoc dl = N->getDebugLoc(); SDValue Chain = N->getOperand(0); - unsigned NewOpc = ARM::LDREXD; - if (Subtarget->isThumb() && Subtarget->hasThumb2()) - NewOpc = ARM::t2LDREXD; + bool isThumb = Subtarget->isThumb() && Subtarget->hasThumb2(); + unsigned NewOpc = isThumb ? ARM::t2LDREXD :ARM::LDREXD; // arm_ldrexd returns a i64 value in {i32, i32} std::vector ResTys; - ResTys.push_back(MVT::i32); - ResTys.push_back(MVT::i32); + if (isThumb) { + ResTys.push_back(MVT::i32); + ResTys.push_back(MVT::i32); + } else + ResTys.push_back(MVT::Untyped); ResTys.push_back(MVT::Other); - // place arguments in the right order + // Place arguments in the right order. SmallVector Ops; Ops.push_back(MemAddr); Ops.push_back(getAL(CurDAG)); @@ -3032,30 +3046,35 @@ SDNode *ARMDAGToDAGISel::Select(SDNode *N) { MemOp[0] = cast(N)->getMemOperand(); cast(Ld)->setMemRefs(MemOp, MemOp + 1); - // Until there's support for specifing explicit register constraints - // like the use of even/odd register pair, hardcode ldrexd to always - // use the pair [R0, R1] to hold the load result. - Chain = CurDAG->getCopyToReg(CurDAG->getEntryNode(), dl, ARM::R0, - SDValue(Ld, 0), SDValue(0,0)); - Chain = CurDAG->getCopyToReg(Chain, dl, ARM::R1, - SDValue(Ld, 1), Chain.getValue(1)); - // Remap uses. - SDValue Glue = Chain.getValue(1); + SDValue Glue = isThumb ? SDValue(Ld, 2) : SDValue(Ld, 1); if (!SDValue(N, 0).use_empty()) { - SDValue Result = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl, - ARM::R0, MVT::i32, Glue); - Glue = Result.getValue(2); + SDValue Result; + if (isThumb) + Result = SDValue(Ld, 0); + else { + SDValue SubRegIdx = CurDAG->getTargetConstant(ARM::gsub_0, MVT::i32); + SDNode *ResNode = CurDAG->getMachineNode(TargetOpcode::EXTRACT_SUBREG, + dl, MVT::i32, MVT::Glue, SDValue(Ld, 0), SubRegIdx, Glue); + Result = SDValue(ResNode,0); + Glue = Result.getValue(1); + } ReplaceUses(SDValue(N, 0), Result); } if (!SDValue(N, 1).use_empty()) { - SDValue Result = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl, - ARM::R1, MVT::i32, Glue); - Glue = Result.getValue(2); + SDValue Result; + if (isThumb) + Result = SDValue(Ld, 1); + else { + SDValue SubRegIdx = CurDAG->getTargetConstant(ARM::gsub_1, MVT::i32); + SDNode *ResNode = CurDAG->getMachineNode(TargetOpcode::EXTRACT_SUBREG, + dl, MVT::i32, MVT::Glue, SDValue(Ld, 0), SubRegIdx, Glue); + Result = SDValue(ResNode,0); + Glue = Result.getValue(1); + } ReplaceUses(SDValue(N, 1), Result); } - - ReplaceUses(SDValue(N, 2), SDValue(Ld, 2)); + ReplaceUses(SDValue(N, 2), Glue); return NULL; } @@ -3066,38 +3085,27 @@ SDNode *ARMDAGToDAGISel::Select(SDNode *N) { SDValue Val1 = N->getOperand(3); SDValue MemAddr = N->getOperand(4); - // Until there's support for specifing explicit register constraints - // like the use of even/odd register pair, hardcode strexd to always - // use the pair [R2, R3] to hold the i64 (i32, i32) value to be stored. - Chain = CurDAG->getCopyToReg(CurDAG->getEntryNode(), dl, ARM::R2, Val0, - SDValue(0, 0)); - Chain = CurDAG->getCopyToReg(Chain, dl, ARM::R3, Val1, Chain.getValue(1)); - - SDValue Glue = Chain.getValue(1); - Val0 = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl, - ARM::R2, MVT::i32, Glue); - Glue = Val0.getValue(1); - Val1 = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl, - ARM::R3, MVT::i32, Glue); - // Store exclusive double return a i32 value which is the return status // of the issued store. std::vector ResTys; ResTys.push_back(MVT::i32); ResTys.push_back(MVT::Other); - // place arguments in the right order + bool isThumb = Subtarget->isThumb() && Subtarget->hasThumb2(); + // Place arguments in the right order. SmallVector Ops; - Ops.push_back(Val0); - Ops.push_back(Val1); + if (isThumb) { + Ops.push_back(Val0); + Ops.push_back(Val1); + } else + // arm_strexd uses GPRPair. + Ops.push_back(SDValue(createGPRPairNode(MVT::Untyped, Val0, Val1), 0)); Ops.push_back(MemAddr); Ops.push_back(getAL(CurDAG)); Ops.push_back(CurDAG->getRegister(0, MVT::i32)); Ops.push_back(Chain); - unsigned NewOpc = ARM::STREXD; - if (Subtarget->isThumb() && Subtarget->hasThumb2()) - NewOpc = ARM::t2STREXD; + unsigned NewOpc = isThumb ? ARM::t2STREXD : ARM::STREXD; SDNode *St = CurDAG->getMachineNode(NewOpc, dl, ResTys, Ops.data(), Ops.size()); diff --git a/lib/Target/ARM/ARMISelLowering.cpp b/lib/Target/ARM/ARMISelLowering.cpp index 3dd08d93bf..e123f1563e 100644 --- a/lib/Target/ARM/ARMISelLowering.cpp +++ b/lib/Target/ARM/ARMISelLowering.cpp @@ -5807,12 +5807,16 @@ ARMTargetLowering::EmitAtomicBinary64(MachineInstr *MI, MachineBasicBlock *BB, // for ldrexd must be different. BB = loopMBB; // Load + unsigned GPRPair0 = MRI.createVirtualRegister(&ARM::GPRPairRegClass); + unsigned GPRPair1 = MRI.createVirtualRegister(&ARM::GPRPairRegClass); + AddDefaultPred(BuildMI(BB, dl, TII->get(ldrOpc)) - .addReg(ARM::R2, RegState::Define) - .addReg(ARM::R3, RegState::Define).addReg(ptr)); + .addReg(GPRPair0, RegState::Define).addReg(ptr)); // Copy r2/r3 into dest. (This copy will normally be coalesced.) - BuildMI(BB, dl, TII->get(TargetOpcode::COPY), destlo).addReg(ARM::R2); - BuildMI(BB, dl, TII->get(TargetOpcode::COPY), desthi).addReg(ARM::R3); + BuildMI(BB, dl, TII->get(TargetOpcode::COPY), destlo) + .addReg(GPRPair0, 0, ARM::gsub_0); + BuildMI(BB, dl, TII->get(TargetOpcode::COPY), desthi) + .addReg(GPRPair0, 0, ARM::gsub_1); if (IsCmpxchg) { // Add early exit @@ -5831,24 +5835,56 @@ ARMTargetLowering::EmitAtomicBinary64(MachineInstr *MI, MachineBasicBlock *BB, // Copy to physregs for strexd unsigned setlo = MI->getOperand(5).getReg(); unsigned sethi = MI->getOperand(6).getReg(); - BuildMI(BB, dl, TII->get(TargetOpcode::COPY), ARM::R0).addReg(setlo); - BuildMI(BB, dl, TII->get(TargetOpcode::COPY), ARM::R1).addReg(sethi); + unsigned undef = MRI.createVirtualRegister(&ARM::GPRPairRegClass); + unsigned r1 = MRI.createVirtualRegister(&ARM::GPRPairRegClass); + BuildMI(BB, dl, TII->get(TargetOpcode::IMPLICIT_DEF), undef); + BuildMI(BB, dl, TII->get(TargetOpcode::INSERT_SUBREG), r1) + .addReg(undef) + .addReg(setlo) + .addImm(ARM::gsub_0); + BuildMI(BB, dl, TII->get(TargetOpcode::INSERT_SUBREG), GPRPair1) + .addReg(r1) + .addReg(sethi) + .addImm(ARM::gsub_1); } else if (Op1) { // Perform binary operation - AddDefaultPred(BuildMI(BB, dl, TII->get(Op1), ARM::R0) + unsigned tmpRegLo = MRI.createVirtualRegister(TRC); + AddDefaultPred(BuildMI(BB, dl, TII->get(Op1), tmpRegLo) .addReg(destlo).addReg(vallo)) .addReg(NeedsCarry ? ARM::CPSR : 0, getDefRegState(NeedsCarry)); - AddDefaultPred(BuildMI(BB, dl, TII->get(Op2), ARM::R1) + unsigned tmpRegHi = MRI.createVirtualRegister(TRC); + AddDefaultPred(BuildMI(BB, dl, TII->get(Op2), tmpRegHi) .addReg(desthi).addReg(valhi)).addReg(0); + + unsigned UndefPair = MRI.createVirtualRegister(&ARM::GPRPairRegClass); + BuildMI(BB, dl, TII->get(TargetOpcode::IMPLICIT_DEF), UndefPair); + unsigned r1 = MRI.createVirtualRegister(&ARM::GPRPairRegClass); + BuildMI(BB, dl, TII->get(TargetOpcode::INSERT_SUBREG), r1) + .addReg(UndefPair) + .addReg(tmpRegLo) + .addImm(ARM::gsub_0); + BuildMI(BB, dl, TII->get(TargetOpcode::INSERT_SUBREG), GPRPair1) + .addReg(r1) + .addReg(tmpRegHi) + .addImm(ARM::gsub_1); } else { // Copy to physregs for strexd - BuildMI(BB, dl, TII->get(TargetOpcode::COPY), ARM::R0).addReg(vallo); - BuildMI(BB, dl, TII->get(TargetOpcode::COPY), ARM::R1).addReg(valhi); + unsigned UndefPair = MRI.createVirtualRegister(&ARM::GPRPairRegClass); + unsigned r1 = MRI.createVirtualRegister(&ARM::GPRPairRegClass); + BuildMI(BB, dl, TII->get(TargetOpcode::IMPLICIT_DEF), UndefPair); + BuildMI(BB, dl, TII->get(TargetOpcode::INSERT_SUBREG), r1) + .addReg(UndefPair) + .addReg(vallo) + .addImm(ARM::gsub_0); + BuildMI(BB, dl, TII->get(TargetOpcode::INSERT_SUBREG), GPRPair1) + .addReg(r1) + .addReg(valhi) + .addImm(ARM::gsub_1); } // Store AddDefaultPred(BuildMI(BB, dl, TII->get(strOpc), storesuccess) - .addReg(ARM::R0).addReg(ARM::R1).addReg(ptr)); + .addReg(GPRPair1).addReg(ptr)); // Cmp+jump AddDefaultPred(BuildMI(BB, dl, TII->get(isThumb2 ? ARM::t2CMPri : ARM::CMPri)) .addReg(storesuccess).addImm(0)); diff --git a/lib/Target/ARM/ARMInstrInfo.td b/lib/Target/ARM/ARMInstrInfo.td index df2e55ed5c..5cf7c3c372 100644 --- a/lib/Target/ARM/ARMInstrInfo.td +++ b/lib/Target/ARM/ARMInstrInfo.td @@ -417,6 +417,8 @@ def reglist : Operand { let DecoderMethod = "DecodeRegListOperand"; } +def GPRPairOp : RegisterOperand; + def DPRRegListAsmOperand : AsmOperandClass { let Name = "DPRRegList"; } def dpr_reglist : Operand { let EncoderMethod = "getRegisterListOpValue"; @@ -4229,8 +4231,8 @@ def LDREXH : AIldrex<0b11, (outs GPR:$Rt), (ins addr_offset_none:$addr), def LDREX : AIldrex<0b00, (outs GPR:$Rt), (ins addr_offset_none:$addr), NoItinerary, "ldrex", "\t$Rt, $addr", []>; let hasExtraDefRegAllocReq = 1 in -def LDREXD: AIldrex<0b01, (outs GPR:$Rt, GPR:$Rt2),(ins addr_offset_none:$addr), - NoItinerary, "ldrexd", "\t$Rt, $Rt2, $addr", []> { +def LDREXD: AIldrex<0b01, (outs GPRPairOp:$Rt),(ins addr_offset_none:$addr), + NoItinerary, "ldrexd", "\t$Rt, $addr", []> { let DecoderMethod = "DecodeDoubleRegLoad"; } } @@ -4244,8 +4246,8 @@ def STREX : AIstrex<0b00, (outs GPR:$Rd), (ins GPR:$Rt, addr_offset_none:$addr), NoItinerary, "strex", "\t$Rd, $Rt, $addr", []>; let hasExtraSrcRegAllocReq = 1 in def STREXD : AIstrex<0b01, (outs GPR:$Rd), - (ins GPR:$Rt, GPR:$Rt2, addr_offset_none:$addr), - NoItinerary, "strexd", "\t$Rd, $Rt, $Rt2, $addr", []> { + (ins GPRPairOp:$Rt, addr_offset_none:$addr), + NoItinerary, "strexd", "\t$Rd, $Rt, $addr", []> { let DecoderMethod = "DecodeDoubleRegStore"; } } diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index c61e3bd99d..7383aa21ce 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -5187,6 +5187,45 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, } } + // Adjust operands of ldrexd/strexd to MCK_GPRPair. + // ldrexd/strexd require even/odd GPR pair. To enforce this constraint, + // a single GPRPair reg operand is used in the .td file to replace the two + // GPRs. However, when parsing from asm, the two GRPs cannot be automatically + // expressed as a GPRPair, so we have to manually merge them. + // FIXME: We would really like to be able to tablegen'erate this. + if (!isThumb() && Operands.size() > 4 && + (Mnemonic == "ldrexd" || Mnemonic == "strexd")) { + bool isLoad = (Mnemonic == "ldrexd"); + unsigned Idx = isLoad ? 2 : 3; + ARMOperand* Op1 = static_cast(Operands[Idx]); + ARMOperand* Op2 = static_cast(Operands[Idx+1]); + + const MCRegisterClass& MRC = MRI->getRegClass(ARM::GPRRegClassID); + // Adjust only if Op1 and Op2 are GPRs. + if (Op1->isReg() && Op2->isReg() && MRC.contains(Op1->getReg()) && + MRC.contains(Op2->getReg())) { + unsigned Reg1 = Op1->getReg(); + unsigned Reg2 = Op2->getReg(); + unsigned Rt = MRI->getEncodingValue(Reg1); + unsigned Rt2 = MRI->getEncodingValue(Reg2); + + // Rt2 must be Rt + 1 and Rt must be even. + if (Rt + 1 != Rt2 || (Rt & 1)) { + Error(Op2->getStartLoc(), isLoad ? + "destination operands must be sequential" : + "source operands must be sequential"); + return true; + } + unsigned NewReg = MRI->getMatchingSuperReg(Reg1, ARM::gsub_0, + &(MRI->getRegClass(ARM::GPRPairRegClassID))); + Operands.erase(Operands.begin() + Idx, Operands.begin() + Idx + 2); + Operands.insert(Operands.begin() + Idx, ARMOperand::CreateReg( + NewReg, Op1->getStartLoc(), Op2->getEndLoc())); + delete Op1; + delete Op2; + } + } + return false; } @@ -5274,8 +5313,7 @@ validateInstruction(MCInst &Inst, switch (Inst.getOpcode()) { case ARM::LDRD: case ARM::LDRD_PRE: - case ARM::LDRD_POST: - case ARM::LDREXD: { + case ARM::LDRD_POST: { // Rt2 must be Rt + 1. unsigned Rt = MRI->getEncodingValue(Inst.getOperand(0).getReg()); unsigned Rt2 = MRI->getEncodingValue(Inst.getOperand(1).getReg()); @@ -5294,8 +5332,7 @@ validateInstruction(MCInst &Inst, return false; } case ARM::STRD_PRE: - case ARM::STRD_POST: - case ARM::STREXD: { + case ARM::STRD_POST: { // Rt2 must be Rt + 1. unsigned Rt = MRI->getEncodingValue(Inst.getOperand(1).getReg()); unsigned Rt2 = MRI->getEncodingValue(Inst.getOperand(2).getReg()); @@ -7483,6 +7520,7 @@ MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, bool MatchingInlineAsm) { MCInst Inst; unsigned MatchResult; + MatchResult = MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); switch (MatchResult) { diff --git a/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp b/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp index dcc41d93f5..7c8f906136 100644 --- a/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp +++ b/lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp @@ -252,6 +252,35 @@ void ARMInstPrinter::printInst(const MCInst *MI, raw_ostream &O, return; } + // Combine 2 GPRs from disassember into a GPRPair to match with instr def. + // ldrexd/strexd require even/odd GPR pair. To enforce this constraint, + // a single GPRPair reg operand is used in the .td file to replace the two + // GPRs. However, when decoding them, the two GRPs cannot be automatically + // expressed as a GPRPair, so we have to manually merge them. + // FIXME: We would really like to be able to tablegen'erate this. + if (Opcode == ARM::LDREXD || Opcode == ARM::STREXD) { + const MCRegisterClass& MRC = MRI.getRegClass(ARM::GPRRegClassID); + bool isStore = Opcode == ARM::STREXD; + unsigned Reg = MI->getOperand(isStore ? 1 : 0).getReg(); + if (MRC.contains(Reg)) { + MCInst NewMI; + MCOperand NewReg; + NewMI.setOpcode(Opcode); + + if (isStore) + NewMI.addOperand(MI->getOperand(0)); + NewReg = MCOperand::CreateReg(MRI.getMatchingSuperReg(Reg, ARM::gsub_0, + &MRI.getRegClass(ARM::GPRPairRegClassID))); + NewMI.addOperand(NewReg); + + // Copy the rest operands into NewMI. + for(unsigned i= isStore ? 3 : 2; i < MI->getNumOperands(); ++i) + NewMI.addOperand(MI->getOperand(i)); + printInstruction(&NewMI, O); + return; + } + } + printInstruction(MI, O); printAnnotation(O, Annot); } @@ -691,6 +720,15 @@ void ARMInstPrinter::printRegisterList(const MCInst *MI, unsigned OpNum, O << "}"; } +void ARMInstPrinter::printGPRPairOperand(const MCInst *MI, unsigned OpNum, + raw_ostream &O) { + unsigned Reg = MI->getOperand(OpNum).getReg(); + printRegName(O, MRI.getSubReg(Reg, ARM::gsub_0)); + O << ", "; + printRegName(O, MRI.getSubReg(Reg, ARM::gsub_1)); +} + + void ARMInstPrinter::printSetendOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O) { const MCOperand &Op = MI->getOperand(OpNum); diff --git a/lib/Target/ARM/InstPrinter/ARMInstPrinter.h b/lib/Target/ARM/InstPrinter/ARMInstPrinter.h index b7bab5fdcd..edff75d886 100644 --- a/lib/Target/ARM/InstPrinter/ARMInstPrinter.h +++ b/lib/Target/ARM/InstPrinter/ARMInstPrinter.h @@ -124,6 +124,7 @@ public: void printNEONModImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); void printImmPlusOneOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); void printRotImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + void printGPRPairOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); void printPCLabel(const MCInst *MI, unsigned OpNum, raw_ostream &O); void printThumbLdrLabelOperand(const MCInst *MI, unsigned OpNum, -- cgit v1.2.3