Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -4026,10 +4026,9 @@ case Intrinsic::write_register: { Value *Reg = I.getArgOperand(0); Value *RegValue = I.getArgOperand(1); - SDValue Chain = getValue(RegValue).getOperand(0); SDValue RegName = DAG.getMDNode(cast(cast(Reg)->getMetadata())); - DAG.setRoot(DAG.getNode(ISD::WRITE_REGISTER, sdl, MVT::Other, Chain, + DAG.setRoot(DAG.getNode(ISD::WRITE_REGISTER, sdl, MVT::Other, getRoot(), RegName, getValue(RegValue))); return nullptr; } Index: lib/Target/AArch64/AArch64ISelDAGToDAG.cpp =================================================================== --- lib/Target/AArch64/AArch64ISelDAGToDAG.cpp +++ lib/Target/AArch64/AArch64ISelDAGToDAG.cpp @@ -156,6 +156,9 @@ SDNode *SelectLIBM(SDNode *N); + SDNode *SelectReadRegister(SDNode *N); + SDNode *SelectWriteRegister(SDNode *N); + // Include the pieces autogenerated from the target description. #include "AArch64GenDAGISel.inc" @@ -2114,6 +2117,116 @@ return true; } +// Inspects a register string of the form o0:op1:CRn:CRm:op2 gets the fields +// of the string and obtains the integer values from them and combines these +// into a single value to be used in the MRS/MSR instruction. +static int getIntOperandFromRegisterString(StringRef RegString) { + SmallVector Fields; + RegString.split(Fields, ":"); + + if (Fields.size() == 1) + return -1; + + assert(Fields.size() == 5 + && "Invalid number of fields in read register string"); + + SmallVector Ops; + bool AllIntFields = true; + + for (StringRef Field : Fields) { + unsigned IntField; + AllIntFields &= !Field.getAsInteger(10, IntField); + Ops.push_back(IntField); + } + + assert(AllIntFields && + "Unexpected non-integer value in special register string."); + + // Need to combine the integer fields of the string into a single value + // based on the bit encoding of MRS/MSR instruction. + return (Ops[0] << 14) | (Ops[1] << 11) | (Ops[2] << 7) | + (Ops[3] << 3) | (Ops[4]); +} + +// Lower the read_register intrinsic to an MRS instruction node if the special +// register string argument is either of the form detailed in the ALCE (the +// form described in getIntOperandsFromRegsterString) or is a named register +// known by the MRS SysReg mapper. +SDNode *AArch64DAGToDAGISel::SelectReadRegister(SDNode *N) { + const MDNodeSDNode *MD = dyn_cast(N->getOperand(0)); + const MDString *RegString = dyn_cast(MD->getMD()->getOperand(0)); + SDLoc DL(N); + + int Reg = getIntOperandFromRegisterString(RegString->getString()); + if (Reg != -1) + return CurDAG->getMachineNode(AArch64::MRS, DL, N->getSimpleValueType(0), + CurDAG->getTargetConstant(Reg, DL, MVT::i32)); + + // Use the sysreg mapper to map the remaining possible strings to the + // value for the register to be used for the instruction operand. + AArch64SysReg::MRSMapper mapper; + bool IsValidSpecialReg; + Reg = mapper.fromString(RegString->getString(), + Subtarget->getFeatureBits(), + IsValidSpecialReg); + if (IsValidSpecialReg) + return CurDAG->getMachineNode(AArch64::MRS, DL, N->getSimpleValueType(0), + CurDAG->getTargetConstant(Reg, DL, MVT::i32)); + return nullptr; +} + +// Lower the write_register intrinsic to an MSR instruction node if the special +// register string argument is either of the form detailed in the ALCE (the +// form described in getIntOperandsFromRegsterString) or is a named register +// known by the MSR SysReg mapper. +SDNode *AArch64DAGToDAGISel::SelectWriteRegister(SDNode *N) { + const MDNodeSDNode *MD = dyn_cast(N->getOperand(1)); + const MDString *RegString = dyn_cast(MD->getMD()->getOperand(0)); + SDLoc DL(N); + + int Reg = getIntOperandFromRegisterString(RegString->getString()); + if (Reg != -1) + return CurDAG->getMachineNode(AArch64::MSR, DL, MVT::Other, + CurDAG->getTargetConstant(Reg, DL, MVT::i32), + N->getOperand(2), N->getOperand(0)); + + // Check if the register was one of those allowed as the pstatefield value in + // the MSR (immediate) instruction. To accept the values allowed in the + // pstatefield for the MSR (immediate) instruction, we also require that an + // immediate value has been provided as an argument, we know that this is + // the case as it has been ensured by semantic checking. + AArch64PState::PStateMapper PMapper; + bool IsValidSpecialReg; + Reg = PMapper.fromString(RegString->getString(), + Subtarget->getFeatureBits(), + IsValidSpecialReg); + if (IsValidSpecialReg) { + assert (isa(N->getOperand(2)) + && "Expected a constant integer expression."); + uint64_t Immed = cast(N->getOperand(2))->getZExtValue(); + return CurDAG->getMachineNode(AArch64::MSRpstate, DL, MVT::Other, + CurDAG->getTargetConstant(Reg, DL, MVT::i32), + CurDAG->getTargetConstant(Immed, DL, MVT::i16), + N->getOperand(0)); + } + + // Use the sysreg mapper to attempt to map the remaining possible strings + // to the value for the register to be used for the MSR (register) + // instruction operand. + AArch64SysReg::MSRMapper Mapper; + Reg = Mapper.fromString(RegString->getString(), + Subtarget->getFeatureBits(), + IsValidSpecialReg); + + if (IsValidSpecialReg) + return CurDAG->getMachineNode(AArch64::MSR, DL, MVT::Other, + CurDAG->getTargetConstant(Reg, DL, MVT::i32), + N->getOperand(2), N->getOperand(0)); + + + return nullptr; +} + SDNode *AArch64DAGToDAGISel::Select(SDNode *Node) { // Dump information about the Node being selected DEBUG(errs() << "Selecting: "); @@ -2135,6 +2248,16 @@ default: break; + case ISD::READ_REGISTER: + if (SDNode *Res = SelectReadRegister(Node)) + return Res; + break; + + case ISD::WRITE_REGISTER: + if (SDNode *Res = SelectWriteRegister(Node)) + return Res; + break; + case ISD::ADD: if (SDNode *I = SelectMLAV64LaneV128(Node)) return I; Index: lib/Target/AArch64/AArch64ISelLowering.cpp =================================================================== --- lib/Target/AArch64/AArch64ISelLowering.cpp +++ lib/Target/AArch64/AArch64ISelLowering.cpp @@ -4094,7 +4094,8 @@ .Default(0); if (Reg) return Reg; - report_fatal_error("Invalid register name global variable"); + report_fatal_error(Twine("Invalid register name \"" + + StringRef(RegName) + "\".")); } SDValue AArch64TargetLowering::LowerRETURNADDR(SDValue Op, Index: lib/Target/ARM/ARMISelDAGToDAG.cpp =================================================================== --- lib/Target/ARM/ARMISelDAGToDAG.cpp +++ lib/Target/ARM/ARMISelDAGToDAG.cpp @@ -15,6 +15,7 @@ #include "ARMBaseInstrInfo.h" #include "ARMTargetMachine.h" #include "MCTargetDesc/ARMAddressingModes.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstrBuilder.h" @@ -251,6 +252,9 @@ // Select special operations if node forms integer ABS pattern SDNode *SelectABSOp(SDNode *N); + SDNode *SelectReadRegister(SDNode *N); + SDNode *SelectWriteRegister(SDNode *N); + SDNode *SelectInlineAsm(SDNode *N); SDNode *SelectConcatVector(SDNode *N); @@ -2457,6 +2461,18 @@ switch (N->getOpcode()) { default: break; + case ISD::WRITE_REGISTER: { + SDNode *ResNode = SelectWriteRegister(N); + if (ResNode) + return ResNode; + break; + } + case ISD::READ_REGISTER: { + SDNode *ResNode = SelectReadRegister(N); + if (ResNode) + return ResNode; + break; + } case ISD::INLINEASM: { SDNode *ResNode = SelectInlineAsm(N); if (ResNode) @@ -3336,6 +3352,358 @@ return SelectCode(N); } +// Maps a Banked Register string to its value. The value returned is +// for use in the MRSbanked / MSRbanked instruction nodes as the Banked Register +// operand. Returns -1 to signify that the string was invalid. +static inline int getBankedRegister(StringRef RegString) { + return StringSwitch(RegString.lower()) + .Case("r8_usr", 0x00) + .Case("r9_usr", 0x01) + .Case("r10_usr", 0x02) + .Case("r11_usr", 0x03) + .Case("r12_usr", 0x04) + .Case("sp_usr", 0x05) + .Case("lr_usr", 0x06) + .Case("r8_fiq", 0x08) + .Case("r9_fiq", 0x09) + .Case("r10_fiq", 0x0a) + .Case("r11_fiq", 0x0b) + .Case("r12_fiq", 0x0c) + .Case("sp_fiq", 0x0d) + .Case("lr_fiq", 0x0e) + .Case("lr_irq", 0x10) + .Case("sp_irq", 0x11) + .Case("lr_svc", 0x12) + .Case("sp_svc", 0x13) + .Case("lr_abt", 0x14) + .Case("sp_abt", 0x15) + .Case("lr_und", 0x16) + .Case("sp_und", 0x17) + .Case("lr_mon", 0x1c) + .Case("sp_mon", 0x1d) + .Case("elr_hyp", 0x1e) + .Case("sp_hyp", 0x1f) + .Case("spsr_fiq", 0x2e) + .Case("spsr_irq", 0x30) + .Case("spsr_svc", 0x32) + .Case("spsr_abt", 0x34) + .Case("spsr_und", 0x36) + .Case("spsr_mon", 0x3c) + .Case("spsr_hyp", 0x3e) + .Default(-1); +} + +// Maps a MClass special register string to its value for use in the +// t2MRS_M / t2MSR_M instruction nodes as the SYSm value operand. +// Returns -1 to signify that the string was invalid. +static inline int getMClassSpecialRegisterSYSmValue(StringRef RegString) { + return StringSwitch(RegString.lower()) + .Case("apsr", 0x0) + .Case("iapsr", 0x1) + .Case("eapsr", 0x2) + .Case("xpsr", 0x3) + .Case("ipsr", 0x5) + .Case("epsr", 0x6) + .Case("iepsr", 0x7) + .Case("msp", 0x8) + .Case("psp", 0x9) + .Case("primask", 0x10) + .Case("basepri", 0x11) + .Case("basepri_max", 0x12) + .Case("faultmask", 0x13) + .Case("control", 0x14) + .Default(-1); +} + +// Inspect a register string of the form +// cp::c:c: (32bit) or +// cp::c (64bit) inspect the fields of the string +// and obtain the integer operands from them, adding these operands to the +// provided vector. +static void getIntOperandsFromRegisterString(StringRef RegString, + SelectionDAG *CurDAG, + SDLoc DL, + std::vector& Ops) { + SmallVector Fields; + RegString.split(Fields, ":"); + + if (Fields.size() > 1) { + bool AllIntFields = true; + + for (StringRef Field : Fields) { + // Need to trim out leading 'cp' characters and get the integer field. + unsigned IntField; + AllIntFields &= !Field.trim("CPcp").getAsInteger(10, IntField); + Ops.push_back(CurDAG->getTargetConstant(IntField, DL, MVT::i32)); + } + + assert(AllIntFields && + "Unexpected non-integer value in special register string."); + } +} + +// Lower the read_register intrinsic to ARM specific DAG nodes +// using the supplied metadata string to select the instruction node to use +// and the registers/masks to construct as operands for the node. +SDNode *ARMDAGToDAGISel::SelectReadRegister(SDNode *N){ + const MDNodeSDNode *MD = dyn_cast(N->getOperand(0)); + const MDString *RegString = dyn_cast(MD->getMD()->getOperand(0)); + bool IsThumb2 = Subtarget->isThumb2(); + SDLoc DL(N); + + std::vector Ops; + getIntOperandsFromRegisterString(RegString->getString(), CurDAG, DL, Ops); + + if (!Ops.empty()) { + // If the special register string was constructed of fields (as defined + // in the ACLE) then need to lower to MRC node (32 bit) or + // MRRC node(64 bit), we can make the distinction based on the number of + // operands we have. + unsigned Opcode; + SmallVector ResTypes; + if (Ops.size() == 5){ + Opcode = IsThumb2 ? ARM::t2MRC : ARM::MRC; + ResTypes = { MVT::i32 }; + } else { + assert(Ops.size() == 3 && + "Invalid number of fields in special register string."); + Opcode = IsThumb2 ? ARM::t2MRRC : ARM::MRRC; + ResTypes = { MVT::i32, MVT::i32 }; + } + + Ops.push_back(getAL(CurDAG, DL)); + Ops.push_back(CurDAG->getRegister(0, MVT::i32)); + return CurDAG->getMachineNode(Opcode, DL, ResTypes, Ops); + } + + std::string SpecialReg = RegString->getString().lower(); + + int BankedReg = getBankedRegister(SpecialReg); + if (BankedReg != -1) { + Ops = { CurDAG->getTargetConstant(BankedReg, DL, MVT::i32), + getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32) }; + return CurDAG->getMachineNode(IsThumb2 ? ARM::t2MRSbanked : ARM::MRSbanked, + DL, MVT::i32, Ops); + } + + unsigned Opcode = StringSwitch(SpecialReg) + .Case("fpscr", ARM::VMRS) + .Case("fpexc", ARM::VMRS_FPEXC) + .Case("fpsid", ARM::VMRS_FPSID) + .Case("mvfr0", ARM::VMRS_MVFR0) + .Case("mvfr1", ARM::VMRS_MVFR1) + .Case("mvfr2", ARM::VMRS_MVFR2) + .Case("fpinst", ARM::VMRS_FPINST) + .Case("fpinst2", ARM::VMRS_FPINST2) + .Default(0); + + if (Subtarget->hasVFP3() && Opcode) { + Ops = { getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32) }; + return CurDAG->getMachineNode(Opcode, DL, MVT::i32, Ops); + } + + if (SpecialReg == "apsr" || SpecialReg == "cpsr") { + Ops = { getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32) }; + return CurDAG->getMachineNode(IsThumb2 ? ARM::t2MRS_AR : ARM::MRS, DL, + MVT::i32, Ops); + } + + if (Subtarget->isMClass()) { + int SYSmvalue = getMClassSpecialRegisterSYSmValue(SpecialReg); + if (SYSmvalue == -1) + return nullptr; + + Ops = { CurDAG->getTargetConstant(SYSmvalue, DL, MVT::i32), + getAL(CurDAG, DL), + CurDAG->getRegister(0, MVT::i32) }; + return CurDAG->getMachineNode(ARM::t2MRS_M, DL, MVT::i32, Ops); + } + + if (SpecialReg == "spsr") { + Ops = { getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32) }; + return CurDAG->getMachineNode(IsThumb2 ? ARM::t2MRSsys_AR : ARM::MRSsys, + DL, MVT::i32, Ops); + } + + return nullptr; +} + +// Lower the write_register intrinsic to ARM specific DAG nodes +// using the supplied metadata string to select the instruction node to use +// and the registers/masks to use in the nodes +SDNode *ARMDAGToDAGISel::SelectWriteRegister(SDNode *N){ + const MDNodeSDNode *MD = dyn_cast(N->getOperand(1)); + const MDString *RegString = dyn_cast(MD->getMD()->getOperand(0)); + bool IsThumb2 = Subtarget->isThumb2(); + SDLoc DL(N); + + std::vector Ops; + getIntOperandsFromRegisterString(RegString->getString(), CurDAG, DL, Ops); + + if (!Ops.empty()) { + // If the special register string was constructed of fields (as defined + // in the ACLE) then need to lower to MCR node (32 bit) or + // MCRR node(64 bit), we can make the distinction based on the number of + // operands we have. + unsigned Opcode; + if (Ops.size() == 5) { + Opcode = IsThumb2 ? ARM::t2MCR : ARM::MCR; + Ops.insert(Ops.begin()+2, N->getOperand(2)); + } else { + assert(Ops.size() == 3 && + "Invalid number of fields in special register string."); + Opcode = IsThumb2 ? ARM::t2MCRR : ARM::MCRR; + SDValue WriteValue[] = { N->getOperand(2), N->getOperand(3) }; + Ops.insert(Ops.begin()+2, WriteValue, WriteValue+2); + } + + Ops.push_back(getAL(CurDAG, DL)); + Ops.push_back(CurDAG->getRegister(0, MVT::i32)); + Ops.push_back(N->getOperand(0)); + + return CurDAG->getMachineNode(Opcode, DL, MVT::Other, Ops); + } + + std::string SpecialReg = RegString->getString().lower(); + + int BankedReg = getBankedRegister(SpecialReg); + if (BankedReg != -1) { + Ops = { CurDAG->getTargetConstant(BankedReg, DL, MVT::i32), N->getOperand(2), + getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32), + N->getOperand(0) }; + return CurDAG->getMachineNode(IsThumb2 ? ARM::t2MSRbanked + : ARM::MSRbanked, + DL, MVT::Other, Ops); + } + + unsigned Opcode = StringSwitch(SpecialReg) + .Case("fpscr", ARM::VMSR) + .Case("fpexc", ARM::VMSR_FPEXC) + .Case("fpsid", ARM::VMSR_FPSID) + .Case("fpinst", ARM::VMSR_FPINST) + .Case("fpinst2", ARM::VMSR_FPINST2) + .Default(0); + + if (Subtarget->hasVFP3() && Opcode) { + Ops = { N->getOperand(2), getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32), + N->getOperand(0) }; + return CurDAG->getMachineNode(Opcode, DL, MVT::Other, Ops); + } + + SmallVector Fields; + StringRef(SpecialReg).split(Fields, "_", 1, false); + std::string Reg = Fields[0].str(); + StringRef Flags = Fields.size() == 2 ? Fields[1] : ""; + + unsigned Mask = 0; + if (Reg == "apsr" || Subtarget->isMClass()) { + // The flags here are common to those allowed for apsr in the A class + // cores and those allowed for the special registers in the M class cores + // so we set the mask in either case and then check if the target is M + // class, if not then the set mask is used later. + if (Flags.empty()) { + Mask = 0x8; + } else { + Mask = StringSwitch(Flags) + .Case("nzcvq", 0x8) + .Case("g", 0x4) + .Case("nzcvqg", 0xc) + .Default(0); + } + + if (Subtarget->isMClass()) { + // basepri_max gets split so need to correct it before getting the value + // for the banked register. + if (SpecialReg == "basepri_max") { + Reg = SpecialReg; + Flags = ""; + } + + int SYSmvalue = getMClassSpecialRegisterSYSmValue(Reg); + + // Ensure that the register (without flags) was a valid MClass special + // register. + if (SYSmvalue == -1) + return nullptr; + + // Only apsr, iapsr, eapsr, xpsr can have flags. + if ((SYSmvalue < 0x4 && !Mask) || (SYSmvalue > 0x4 && !Flags.empty())) + return nullptr; + + // The _g and _nzcvqg versions are only valid if the DSP extension is + // available. + if (!Subtarget->hasThumb2DSP() && (Mask & 0x4)) + return nullptr; + + // basepri, basepri_max and faultmask only valid for V7m. + if (!Subtarget->hasV7Ops() && SYSmvalue >= 0x11 && SYSmvalue <= 0x13) + return nullptr; + + // The register was valid so need to put the mask in the correct place + // and combine with the SYSmvalue to construct the operand for the + // instruction node. + SYSmvalue |= (SYSmvalue < 0x4) ? Mask << 8 : 0; + Ops = { CurDAG->getTargetConstant(SYSmvalue, DL, MVT::i32), + N->getOperand(2), getAL(CurDAG, DL), + CurDAG->getRegister(0, MVT::i32), N->getOperand(0) }; + return CurDAG->getMachineNode(ARM::t2MSR_M, DL, MVT::Other, Ops); + } + + } + + if (Reg == "cpsr" || Reg == "spsr") { + if (Flags.empty() || Flags == "all") { + // This is the same as if the flags were "fc" + Mask = 0x9; + } else { + // Inspect the supplied register string and set the bits in the mask for + // the relevant and valid flags allowed for cpsr and spsr. + for (char Flag : Flags) { + unsigned FlagVal; + switch (Flag) { + case 'c': + FlagVal = 0x1; + break; + case 'x': + FlagVal = 0x2; + break; + case 's': + FlagVal = 0x4; + break; + case 'f': + FlagVal = 0x8; + break; + default: + FlagVal = 0; + } + + // This avoids allowing strings where the same flag bit appears twice. + if (!FlagVal || (Mask & FlagVal)) + return nullptr; + Mask |= FlagVal; + } + } + + // If the register was spsr then we need to set the R bit in the mask. + if (Reg == "spsr") + Mask |= 0x10; + + } + + // If the string was a valid register then the mask is set and we can + // lower the write_register to an MSR instruction node using the mask + // as an operand to construct the node. + if (Mask) { + Ops = { CurDAG->getTargetConstant(Mask, DL, MVT::i32), N->getOperand(2), + getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32), + N->getOperand(0) }; + return CurDAG->getMachineNode(IsThumb2 ? ARM::t2MSR_AR : ARM::MSR, + DL, MVT::Other, Ops); + } + + return nullptr; +} + SDNode *ARMDAGToDAGISel::SelectInlineAsm(SDNode *N){ std::vector AsmNodeOperands; unsigned Flag, Kind; Index: lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- lib/Target/ARM/ARMISelLowering.cpp +++ lib/Target/ARM/ARMISelLowering.cpp @@ -427,6 +427,9 @@ setOperationAction(ISD::ConstantFP, MVT::f32, Custom); setOperationAction(ISD::ConstantFP, MVT::f64, Custom); + setOperationAction(ISD::READ_REGISTER, MVT::i64, Custom); + setOperationAction(ISD::WRITE_REGISTER, MVT::i64, Custom); + if (Subtarget->hasNEON()) { addDRTypeForNEON(MVT::v2f32); addDRTypeForNEON(MVT::v8i8); @@ -2378,6 +2381,24 @@ return !Subtarget->isThumb1Only(); } +// Trying to write a 64 bit value so need to split into two 32 bit values first, +// and pass the lower and high parts through. +static SDValue LowerWRITE_REGISTER(SDValue Op, SelectionDAG &DAG) { + SDLoc DL(Op); + SDValue WriteValue = Op->getOperand(2); + + // This function is only supposed to be called for i64 argument type. + assert(WriteValue.getValueType() == MVT::i64 + && "LowerWRITE_REGISTER called for non-i64 argument type."); + + SDValue Lo = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, WriteValue, + DAG.getConstant(0, DL, MVT::i32)); + SDValue Hi = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, WriteValue, + DAG.getConstant(1, DL, MVT::i32)); + SDValue Ops[] = { Op->getOperand(0), Op->getOperand(1), Lo, Hi }; + return DAG.getNode(ISD::WRITE_REGISTER, DL, MVT::Other, Ops); +} + // ConstantPool, JumpTable, GlobalAddress, and ExternalSymbol are lowered as // their target counterpart wrapped in the ARMISD::Wrapper node. Suppose N is // one of the above mentioned nodes. It has to be wrapped because otherwise @@ -4090,9 +4111,27 @@ .Default(0); if (Reg) return Reg; - report_fatal_error("Invalid register name global variable"); + report_fatal_error(Twine("Invalid register name \"" + + StringRef(RegName) + "\".")); } +// Result is 64 bit value so split into two 32 bit values and return as a +// pair of values. +static SDValue ExpandREAD_REGISTER(SDNode *N, SelectionDAG &DAG) { + SDLoc DL(N); + SDValue Op = N->getOperand(0); + + // This function is only supposed to be called for i64 destination type. + assert(N->getValueType(0) == MVT::i64 + && "ExpandREAD_REGISTER called for non-i64 result type."); + + SDValue Read = DAG.getNode(ISD::READ_REGISTER, DL, + DAG.getVTList(MVT::i32, MVT::i32), Op); + return DAG.getNode(ISD::BUILD_PAIR, DL, MVT::i64, Read.getValue(0), + Read.getValue(1)); +} + + /// ExpandBITCAST - If the target supports VFP, this function is called to /// expand a bit convert where either the source or destination type is i64 to /// use a VMOVDRR or VMOVRRD node. This should not be done when the non-i64 @@ -6419,6 +6458,7 @@ SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { switch (Op.getOpcode()) { default: llvm_unreachable("Don't know how to custom lower this!"); + case ISD::WRITE_REGISTER: return LowerWRITE_REGISTER(Op, DAG); case ISD::ConstantPool: return LowerConstantPool(Op, DAG); case ISD::BlockAddress: return LowerBlockAddress(Op, DAG); case ISD::GlobalAddress: @@ -6508,6 +6548,9 @@ switch (N->getOpcode()) { default: llvm_unreachable("Don't know how to custom expand this!"); + case ISD::READ_REGISTER: + Res = ExpandREAD_REGISTER(N, DAG); + break; case ISD::BITCAST: Res = ExpandBITCAST(N, DAG); break; @@ -8000,7 +8043,7 @@ // Get widened type and narrowed type. MVT widenType; unsigned numElem = VT.getVectorNumElements(); - + EVT inputLaneType = Vec.getValueType().getVectorElementType(); switch (inputLaneType.getSimpleVT().SimpleTy) { case MVT::i8: widenType = MVT::getVectorVT(MVT::i16, numElem); break; Index: lib/Target/ARM/ARMInstrInfo.td =================================================================== --- lib/Target/ARM/ARMInstrInfo.td +++ lib/Target/ARM/ARMInstrInfo.td @@ -5075,10 +5075,8 @@ imm:$CRm, imm:$opc2), (MRC2 imm:$cop, imm:$opc1, imm:$CRn, imm:$CRm, imm:$opc2)>; -class MovRRCopro pattern = []> - : ABI<0b1100, (outs), (ins p_imm:$cop, imm0_15:$opc1, - GPRnopc:$Rt, GPRnopc:$Rt2, c_imm:$CRm), - NoItinerary, opc, "\t$cop, $opc1, $Rt, $Rt2, $CRm", pattern> { +class MovRRCopro pattern = []> + : ABI<0b1100, oops, iops, NoItinerary, opc, "\t$cop, $opc1, $Rt, $Rt2, $CRm", pattern> { let Inst{23-21} = 0b010; let Inst{20} = direction; @@ -5096,9 +5094,13 @@ } def MCRR : MovRRCopro<"mcrr", 0 /* from ARM core register to coprocessor */, + (outs), (ins p_imm:$cop, imm0_15:$opc1, GPRnopc:$Rt, GPRnopc:$Rt2, + c_imm:$CRm), [(int_arm_mcrr imm:$cop, imm:$opc1, GPRnopc:$Rt, GPRnopc:$Rt2, imm:$CRm)]>; -def MRRC : MovRRCopro<"mrrc", 1 /* from coprocessor to ARM core register */>; +def MRRC : MovRRCopro<"mrrc", 1 /* from coprocessor to ARM core register */, + (outs GPRnopc:$Rt, GPRnopc:$Rt2), + (ins p_imm:$cop, imm0_15:$opc1, c_imm:$CRm), []>; class MovRRCopro2 pattern = []> : ABXI<0b1100, (outs), (ins p_imm:$cop, imm0_15:$opc1, Index: lib/Target/ARM/ARMInstrThumb2.td =================================================================== --- lib/Target/ARM/ARMInstrThumb2.td +++ lib/Target/ARM/ARMInstrThumb2.td @@ -4165,11 +4165,9 @@ let Inst{19-16} = CRn; } -class t2MovRRCopro Op, string opc, bit direction, +class t2MovRRCopro Op, string opc, bit direction, dag oops, dag iops, list pattern = []> - : T2Cop { + : T2Cop { let Inst{27-24} = 0b1100; let Inst{23-21} = 0b010; let Inst{20} = direction; @@ -4234,19 +4232,23 @@ /* from ARM core register to coprocessor */ -def t2MCRR : t2MovRRCopro<0b1110, "mcrr", 0, - [(int_arm_mcrr imm:$cop, imm:$opc1, GPR:$Rt, GPR:$Rt2, +def t2MCRR : t2MovRRCopro<0b1110, "mcrr", 0, (outs), + (ins p_imm:$cop, imm0_15:$opc1, GPR:$Rt, GPR:$Rt2, c_imm:$CRm), + [(int_arm_mcrr imm:$cop, imm:$opc1, GPR:$Rt, GPR:$Rt2, imm:$CRm)]>; -def t2MCRR2 : t2MovRRCopro<0b1111, "mcrr2", 0, - [(int_arm_mcrr2 imm:$cop, imm:$opc1, GPR:$Rt, +def t2MCRR2 : t2MovRRCopro<0b1111, "mcrr2", 0, (outs), + (ins p_imm:$cop, imm0_15:$opc1, GPR:$Rt, GPR:$Rt2, c_imm:$CRm), + [(int_arm_mcrr2 imm:$cop, imm:$opc1, GPR:$Rt, GPR:$Rt2, imm:$CRm)]> { let Predicates = [IsThumb2, PreV8]; } /* from coprocessor to ARM core register */ -def t2MRRC : t2MovRRCopro<0b1110, "mrrc", 1>; +def t2MRRC : t2MovRRCopro<0b1110, "mrrc", 1, (outs GPR:$Rt, GPR:$Rt2), + (ins p_imm:$cop, imm0_15:$opc1, c_imm:$CRm)>; -def t2MRRC2 : t2MovRRCopro<0b1111, "mrrc2", 1> { +def t2MRRC2 : t2MovRRCopro<0b1111, "mrrc2", 1, (outs GPR:$Rt, GPR:$Rt2), + (ins p_imm:$cop, imm0_15:$opc1, c_imm:$CRm)> { let Predicates = [IsThumb2, PreV8]; } Index: test/CodeGen/AArch64/arm64-named-reg-alloc.ll =================================================================== --- test/CodeGen/AArch64/arm64-named-reg-alloc.ll +++ test/CodeGen/AArch64/arm64-named-reg-alloc.ll @@ -4,7 +4,7 @@ define i32 @get_stack() nounwind { entry: ; FIXME: Include an allocatable-specific error message -; CHECK: Invalid register name global variable +; CHECK: Invalid register name "x5". %sp = call i32 @llvm.read_register.i32(metadata !0) ret i32 %sp } Index: test/CodeGen/AArch64/arm64-named-reg-notareg.ll =================================================================== --- test/CodeGen/AArch64/arm64-named-reg-notareg.ll +++ test/CodeGen/AArch64/arm64-named-reg-notareg.ll @@ -3,7 +3,7 @@ define i32 @get_stack() nounwind { entry: -; CHECK: Invalid register name global variable +; CHECK: Invalid register name "notareg". %sp = call i32 @llvm.read_register.i32(metadata !0) ret i32 %sp } Index: test/CodeGen/AArch64/special-reg.ll =================================================================== --- /dev/null +++ test/CodeGen/AArch64/special-reg.ll @@ -0,0 +1,48 @@ +; RUN: llc < %s -mtriple=aarch64-none-eabi -mcpu=cortex-a57 2>&1 | FileCheck %s + +define i64 @read_encoded_register() nounwind { +entry: +; CHECK-LABEL: read_encoded_register: +; CHECK: mrs x0, S1_2_C3_C4_5 + %reg = call i64 @llvm.read_register.i64(metadata !0) + ret i64 %reg +} + +define i64 @read_daif() nounwind { +entry: +; CHECK-LABEL: read_daif: +; CHECK: mrs x0, DAIF + %reg = call i64 @llvm.read_register.i64(metadata !1) + ret i64 %reg +} + +define void @write_encoded_register(i64 %x) nounwind { +entry: +; CHECK-LABEL: write_encoded_register: +; CHECK: msr S1_2_C3_C4_5, x0 + call void @llvm.write_register.i64(metadata !0, i64 %x) + ret void +} + +define void @write_daif(i64 %x) nounwind { +entry: +; CHECK-LABEL: write_daif: +; CHECK: msr DAIF, x0 + call void @llvm.write_register.i64(metadata !1, i64 %x) + ret void +} + +define void @write_daifset() nounwind { +entry: +; CHECK-LABEL: write_daifset: +; CHECK: msr DAIFSET, #2 + call void @llvm.write_register.i64(metadata !2, i64 2) + ret void +} + +declare i64 @llvm.read_register.i64(metadata) nounwind +declare void @llvm.write_register.i64(metadata, i64) nounwind + +!0 = !{!"1:2:3:4:5"} +!1 = !{!"daif"} +!2 = !{!"daifset"} Index: test/CodeGen/ARM/named-reg-alloc.ll =================================================================== --- test/CodeGen/ARM/named-reg-alloc.ll +++ test/CodeGen/ARM/named-reg-alloc.ll @@ -4,7 +4,7 @@ define i32 @get_stack() nounwind { entry: ; FIXME: Include an allocatable-specific error message -; CHECK: Invalid register name global variable +; CHECK: Invalid register name "r5". %sp = call i32 @llvm.read_register.i32(metadata !0) ret i32 %sp } Index: test/CodeGen/ARM/named-reg-notareg.ll =================================================================== --- test/CodeGen/ARM/named-reg-notareg.ll +++ test/CodeGen/ARM/named-reg-notareg.ll @@ -3,7 +3,7 @@ define i32 @get_stack() nounwind { entry: -; CHECK: Invalid register name global variable +; CHECK: Invalid register name "notareg". %sp = call i32 @llvm.read_register.i32(metadata !0) ret i32 %sp } Index: test/CodeGen/ARM/special-reg-acore.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/special-reg-acore.ll @@ -0,0 +1,27 @@ +; RUN: llc < %s -mtriple=arm-none-eabi -mcpu=cortex-a8 2>&1 | FileCheck %s --check-prefix=ACORE +; RUN: not llc < %s -mtriple=thumb-none-eabi -mcpu=cortex-m4 2>&1 | FileCheck %s --check-prefix=MCORE + +; MCORE: LLVM ERROR: Invalid register name "spsr". +define i32 @read_spsr() nounwind { +entry: +; ACORE-LABEL: read_spsr: +; ACORE: mrs r0, spsr + %reg = call i32 @llvm.read_register.i32(metadata !0) + ret i32 %reg +} + +define void @write_spsr(i32 %x) nounwind { +entry: +; ACORE-LABEL: write_spsr: +; ACORE: msr SPSR_fxc, r0 + call void @llvm.write_register.i32(metadata !1, i32 %x) + ret void +} + +declare i32 @llvm.read_register.i32(metadata) nounwind +declare i64 @llvm.read_register.i64(metadata) nounwind +declare void @llvm.write_register.i32(metadata, i32) nounwind +declare void @llvm.write_register.i64(metadata, i64) nounwind + +!0 = !{!"spsr"} +!1 = !{!"spsr_xcf"} Index: test/CodeGen/ARM/special-reg-mcore.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/special-reg-mcore.ll @@ -0,0 +1,29 @@ +; RUN: llc < %s -mtriple=thumb-none-eabi -mcpu=cortex-m4 2>&1 | FileCheck %s --check-prefix=MCORE +; RUN: not llc < %s -mtriple=thumb-none-eabi -mcpu=cortex-m3 2>&1 | FileCheck %s --check-prefix=M3CORE +; RUN: not llc < %s -mtriple=arm-none-eabi -mcpu=cortex-a8 2>&1 | FileCheck %s --check-prefix=ACORE + +; ACORE: LLVM ERROR: Invalid register name "xpsr". +; M3CORE: LLVM ERROR: Invalid register name "xpsr_nzcvqg". +define i32 @read_xpsr() nounwind { +entry: +; MCORE-LABEL: read_xpsr: +; MCORE: mrs r0, xpsr + %reg = call i32 @llvm.read_register.i32(metadata !0) + ret i32 %reg +} + +define void @write_xpsr(i32 %x) nounwind { +entry: +; MCORE-LABEL: write_xpsr: +; MCORE: msr xpsr_nzcvqg, r0 + call void @llvm.write_register.i32(metadata !1, i32 %x) + ret void +} + +declare i32 @llvm.read_register.i32(metadata) nounwind +declare i64 @llvm.read_register.i64(metadata) nounwind +declare void @llvm.write_register.i32(metadata, i32) nounwind +declare void @llvm.write_register.i64(metadata, i64) nounwind + +!0 = !{!"xpsr"} +!1 = !{!"xpsr_nzcvqg"} Index: test/CodeGen/ARM/special-reg.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/special-reg.ll @@ -0,0 +1,78 @@ +; RUN: llc < %s -mtriple=arm-none-eabi -mcpu=cortex-a8 2>&1 | FileCheck %s --check-prefix=ARM --check-prefix=ACORE +; RUN: llc < %s -mtriple=thumb-none-eabi -mcpu=cortex-m4 2>&1 | FileCheck %s --check-prefix=ARM --check-prefix=MCORE + +define i32 @read_i32_encoded_register() nounwind { +entry: +; ARM-LABEL: read_i32_encoded_register: +; ARM: mrc p1, #2, r0, c3, c4, #5 + %reg = call i32 @llvm.read_register.i32(metadata !0) + ret i32 %reg +} + +define i64 @read_i64_encoded_register() nounwind { +entry: +; ARM-LABEL: read_i64_encoded_register: +; ARM: mrrc p1, #2, r0, r1, c3 + %reg = call i64 @llvm.read_register.i64(metadata !1) + ret i64 %reg +} + +define i32 @read_apsr() nounwind { +entry: +; ARM-LABEL: read_apsr: +; ARM: mrs r0, apsr + %reg = call i32 @llvm.read_register.i32(metadata !2) + ret i32 %reg +} + +define i32 @read_fpscr() nounwind { +entry: +; ARM-LABEL: read_fpscr: +; ARM: vmrs r0, fpscr + %reg = call i32 @llvm.read_register.i32(metadata !3) + ret i32 %reg +} + +define void @write_i32_encoded_register(i32 %x) nounwind { +entry: +; ARM-LABEL: write_i32_encoded_register: +; ARM: mcr p1, #2, r0, c3, c4, #5 + call void @llvm.write_register.i32(metadata !0, i32 %x) + ret void +} + +define void @write_i64_encoded_register(i64 %x) nounwind { +entry: +; ARM-LABEL: write_i64_encoded_register: +; ARM: mcrr p1, #2, r0, r1, c3 + call void @llvm.write_register.i64(metadata !1, i64 %x) + ret void +} + +define void @write_apsr(i32 %x) nounwind { +entry: +; ARM-LABEL: write_apsr: +; ACORE: msr APSR_nzcvq, r0 +; MCORE: msr apsr_nzcvq, r0 + call void @llvm.write_register.i32(metadata !4, i32 %x) + ret void +} + +define void @write_fpscr(i32 %x) nounwind { +entry: +; ARM-LABEL: write_fpscr: +; ARM: vmsr fpscr, r0 + call void @llvm.write_register.i32(metadata !3, i32 %x) + ret void +} + +declare i32 @llvm.read_register.i32(metadata) nounwind +declare i64 @llvm.read_register.i64(metadata) nounwind +declare void @llvm.write_register.i32(metadata, i32) nounwind +declare void @llvm.write_register.i64(metadata, i64) nounwind + +!0 = !{!"cp1:2:c3:c4:5"} +!1 = !{!"cp1:2:c3"} +!2 = !{!"apsr"} +!3 = !{!"fpscr"} +!4 = !{!"apsr_nzcvq"}