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,115 @@ 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 +2247,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,405 @@ return SelectCode(N); } +// 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."); + } +} + +// Maps a Banked Register string to its mask value. The mask value returned is +// for use in the MRSbanked / MSRbanked instruction nodes as the Banked Register +// mask operand, which expresses which register is to be used, e.g. r8, and in +// which mode it is to be used, e.g. usr. Returns -1 to signify that the string +// was invalid. +static inline int getBankedRegisterMask(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 getMClassRegisterSYSmValueMask(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); +} + +// 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. Returns a +// value representing which flags were present, -1 if invalid. +static inline int getMClassFlagsMask(StringRef Flags) { + if (Flags.empty()) + return 0x3; + + return StringSwitch(Flags) + .Case("g", 0x1) + .Case("nzcvq", 0x2) + .Case("nzcvqg", 0x3) + .Default(-1); +} + +static int getMClassRegisterMask(StringRef Reg, StringRef Flags, bool IsRead, + const ARMSubtarget *Subtarget) { + // Ensure that the register (without flags) was a valid M Class special + // register. + int SYSmvalue = getMClassRegisterSYSmValueMask(Reg); + if (SYSmvalue == -1) + return -1; + + // basepri, basepri_max and faultmask are only valid for V7m. + if (!Subtarget->hasV7Ops() && SYSmvalue >= 0x11 && SYSmvalue <= 0x13) + return -1; + + // If it was a read then we won't be expecting flags and so at this point + // we can return the mask. + if (IsRead) { + assert (Flags.empty() && "Unexpected flags for reading M class register."); + return SYSmvalue; + } + + // We know we are now handling a write so need to get the mask for the flags. + int Mask = getMClassFlagsMask(Flags); + + // Only apsr, iapsr, eapsr, xpsr can have flags. The other register values + // shouldn't have flags present. + if ((SYSmvalue < 0x4 && Mask == -1) || (SYSmvalue > 0x4 && !Flags.empty())) + return -1; + + // The _g and _nzcvqg versions are only valid if the DSP extension is + // available. + if (!Subtarget->hasThumb2DSP() && (Mask & 0x2)) + return -1; + + // The register was valid so need to put the mask in the correct place + // (the flags need to be in bits 11-10) and combine with the SYSmvalue to + // construct the operand for the instruction node. + if (SYSmvalue < 0x4) + return SYSmvalue | Mask << 10; + + return SYSmvalue; +} + +static int getARClassRegisterMask(StringRef Reg, StringRef Flags) { + // The mask operand contains the special register (R Bit) in bit 4, whether + // the register is spsr (R bit is 1) or one of cpsr/apsr (R bit is 0), and + // bits 3-0 contains the fields to be accessed in the special register, set by + // the flags provided with the register. + int Mask = 0; + if (Reg == "apsr") { + // The flags permitted for apsr are the same flags that are allowed in + // M class registers. We get the flag value and then shift the flags into + // the correct place to combine with the mask. + Mask = getMClassFlagsMask(Flags); + if (Mask == -1) + return -1; + return Mask << 2; + } + + if (Reg != "cpsr" && Reg != "spsr") { + return -1; + } + + // This is the same as if the flags were "fc" + if (Flags.empty() || Flags == "all") + return Mask | 0x9; + + // Inspect the supplied flags string and set the bits in the mask for + // the relevant and valid flags allowed for cpsr and spsr. + for (char Flag : Flags) { + int 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 -1; + Mask |= FlagVal; + } + + // If the register is spsr then we need to set the R bit. + if (Reg == "spsr") + Mask |= 0x10; + + return Mask; +} + +// 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 = getBankedRegisterMask(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); + } + + // The VFP registers are read by creating SelectionDAG nodes with opcodes + // corresponding to the register that is being read from. So we switch on the + // string to find which opcode we need to use. + 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 an opcode was found then we can lower the read to a VFP instruction. + if (Subtarget->hasVFP3() && Opcode) { + Ops = { getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32) }; + return CurDAG->getMachineNode(Opcode, DL, MVT::i32, Ops); + } + + // If the target is M Class then need to validate that the register string + // is an acceptable value, so check that a mask can be constructed from the + // string. + if (Subtarget->isMClass()) { + int SYSmValue = getMClassRegisterMask(SpecialReg, "", true, Subtarget); + if (SYSmValue == -1) + return nullptr; + + SDValue 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); + } + + // Here we know the target is not M Class so we need to check if it is one + // of the remaining possible values which are apsr, cpsr or spsr. + 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 (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 = getBankedRegisterMask(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); + } + + // The VFP registers are written to by creating SelectionDAG nodes with + // opcodes corresponding to the register that is being written. So we switch + // on the string to find which opcode we need to use. + 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] : ""; + + // If the target was M Class then need to validate the special register value + // and retrieve the mask for use in the instruction node. + if (Subtarget->isMClass()) { + // basepri_max gets split so need to correct Reg and Flags. + if (SpecialReg == "basepri_max") { + Reg = SpecialReg; + Flags = ""; + } + int SYSmValue = getMClassRegisterMask(Reg, Flags, false, Subtarget); + if (SYSmValue == -1) + return nullptr; + + SDValue 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); + } + + // At this point must be considering register values for an A or R class core, + // so check if a valid mask can be constructed from the register and flags. + int Mask = getARClassRegisterMask(Reg, Flags); + if (Mask != -1) { + 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 type argument. + assert(WriteValue.getValueType() == MVT::i64 + && "LowerWRITE_REGISTER called for non-i64 type argument."); + + 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 type destination. + assert(N->getValueType(0) == MVT::i64 + && "ExpandREAD_REGISTER called for non-i64 type result."); + + 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,78 @@ +; 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 "cpsr". + +define i32 @read_cpsr() nounwind { + ; ACORE-LABEL: read_cpsr: + ; ACORE: mrs r0, apsr + %reg = call i32 @llvm.read_register.i32(metadata !1) + ret i32 %reg +} + +define i32 @read_aclass_registers() nounwind { +entry: + ; ACORE-LABEL: read_aclass_registers: + ; ACORE: mrs r0, apsr + ; ACORE: mrs r1, spsr + + %0 = call i32 @llvm.read_register.i32(metadata !0) + %1 = call i32 @llvm.read_register.i32(metadata !1) + %add1 = add i32 %1, %0 + %2 = call i32 @llvm.read_register.i32(metadata !2) + %add2 = add i32 %add1, %2 + ret i32 %add2 +} + +define void @write_aclass_registers(i32 %x) nounwind { +entry: + ; ACORE-LABEL: write_aclass_registers: + ; ACORE: msr APSR_nzcvq, r0 + ; ACORE: msr APSR_g, r0 + ; ACORE: msr APSR_nzcvqg, r0 + ; ACORE: msr CPSR_c, r0 + ; ACORE: msr CPSR_x, r0 + ; ACORE: msr APSR_g, r0 + ; ACORE: msr APSR_nzcvq, r0 + ; ACORE: msr CPSR_fsxc, r0 + ; ACORE: msr SPSR_c, r0 + ; ACORE: msr SPSR_x, r0 + ; ACORE: msr SPSR_s, r0 + ; ACORE: msr SPSR_f, r0 + ; ACORE: msr SPSR_fsxc, r0 + + call void @llvm.write_register.i32(metadata !3, i32 %x) + call void @llvm.write_register.i32(metadata !4, i32 %x) + call void @llvm.write_register.i32(metadata !5, i32 %x) + call void @llvm.write_register.i32(metadata !6, i32 %x) + call void @llvm.write_register.i32(metadata !7, i32 %x) + call void @llvm.write_register.i32(metadata !8, i32 %x) + call void @llvm.write_register.i32(metadata !9, i32 %x) + call void @llvm.write_register.i32(metadata !10, i32 %x) + call void @llvm.write_register.i32(metadata !11, i32 %x) + call void @llvm.write_register.i32(metadata !12, i32 %x) + call void @llvm.write_register.i32(metadata !13, i32 %x) + call void @llvm.write_register.i32(metadata !14, i32 %x) + call void @llvm.write_register.i32(metadata !15, i32 %x) + ret void +} + +declare i32 @llvm.read_register.i32(metadata) nounwind +declare void @llvm.write_register.i32(metadata, i32) nounwind + +!0 = !{!"apsr"} +!1 = !{!"cpsr"} +!2 = !{!"spsr"} +!3 = !{!"apsr_nzcvq"} +!4 = !{!"apsr_g"} +!5 = !{!"apsr_nzcvqg"} +!6 = !{!"cpsr_c"} +!7 = !{!"cpsr_x"} +!8 = !{!"cpsr_s"} +!9 = !{!"cpsr_f"} +!10 = !{!"cpsr_cxsf"} +!11 = !{!"spsr_c"} +!12 = !{!"spsr_x"} +!13 = !{!"spsr_s"} +!14 = !{!"spsr_f"} +!15 = !{!"spsr_cxsf"} Index: test/CodeGen/ARM/special-reg-mcore.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/special-reg-mcore.ll @@ -0,0 +1,143 @@ +; 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 "control". +; M3CORE: LLVM ERROR: Invalid register name "control". + +define i32 @read_mclass_registers() nounwind { +entry: + ; MCORE-LABEL: read_mclass_registers: + ; MCORE: mrs r0, apsr + ; MCORE: mrs r1, iapsr + ; MCORE: mrs r1, eapsr + ; MCORE: mrs r1, xpsr + ; MCORE: mrs r1, ipsr + ; MCORE: mrs r1, epsr + ; MCORE: mrs r1, iepsr + ; MCORE: mrs r1, msp + ; MCORE: mrs r1, psp + ; MCORE: mrs r1, primask + ; MCORE: mrs r1, basepri + ; MCORE: mrs r1, basepri_max + ; MCORE: mrs r1, faultmask + ; MCORE: mrs r1, control + + %0 = call i32 @llvm.read_register.i32(metadata !0) + %1 = call i32 @llvm.read_register.i32(metadata !4) + %add1 = add i32 %1, %0 + %2 = call i32 @llvm.read_register.i32(metadata !8) + %add2 = add i32 %add1, %2 + %3 = call i32 @llvm.read_register.i32(metadata !12) + %add3 = add i32 %add2, %3 + %4 = call i32 @llvm.read_register.i32(metadata !16) + %add4 = add i32 %add3, %4 + %5 = call i32 @llvm.read_register.i32(metadata !17) + %add5 = add i32 %add4, %5 + %6 = call i32 @llvm.read_register.i32(metadata !18) + %add6 = add i32 %add5, %6 + %7 = call i32 @llvm.read_register.i32(metadata !19) + %add7 = add i32 %add6, %7 + %8 = call i32 @llvm.read_register.i32(metadata !20) + %add8 = add i32 %add7, %8 + %9 = call i32 @llvm.read_register.i32(metadata !21) + %add9 = add i32 %add8, %9 + %10 = call i32 @llvm.read_register.i32(metadata !22) + %add10 = add i32 %add9, %10 + %11 = call i32 @llvm.read_register.i32(metadata !23) + %add11 = add i32 %add10, %11 + %12 = call i32 @llvm.read_register.i32(metadata !24) + %add12 = add i32 %add11, %12 + %13 = call i32 @llvm.read_register.i32(metadata !25) + %add13 = add i32 %add12, %13 + ret i32 %add13 +} + +define void @write_mclass_registers(i32 %x) nounwind { +entry: + ; MCORE-LABEL: write_mclass_registers: + ; MCORE: msr apsr_nzcvqg, r0 + ; MCORE: msr apsr_nzcvq, r0 + ; MCORE: msr apsr_g, r0 + ; MCORE: msr apsr_nzcvqg, r0 + ; MCORE: msr iapsr_nzcvqg, r0 + ; MCORE: msr iapsr_nzcvq, r0 + ; MCORE: msr iapsr_g, r0 + ; MCORE: msr iapsr_nzcvqg, r0 + ; MCORE: msr eapsr_nzcvqg, r0 + ; MCORE: msr eapsr_nzcvq, r0 + ; MCORE: msr eapsr_g, r0 + ; MCORE: msr eapsr_nzcvqg, r0 + ; MCORE: msr xpsr_nzcvqg, r0 + ; MCORE: msr xpsr_nzcvq, r0 + ; MCORE: msr xpsr_g, r0 + ; MCORE: msr xpsr_nzcvqg, r0 + ; MCORE: msr ipsr, r0 + ; MCORE: msr epsr, r0 + ; MCORE: msr iepsr, r0 + ; MCORE: msr msp, r0 + ; MCORE: msr psp, r0 + ; MCORE: msr primask, r0 + ; MCORE: msr basepri, r0 + ; MCORE: msr basepri_max, r0 + ; MCORE: msr faultmask, r0 + ; MCORE: msr control, r0 + + call void @llvm.write_register.i32(metadata !0, i32 %x) + call void @llvm.write_register.i32(metadata !1, i32 %x) + call void @llvm.write_register.i32(metadata !2, i32 %x) + call void @llvm.write_register.i32(metadata !3, i32 %x) + call void @llvm.write_register.i32(metadata !4, i32 %x) + call void @llvm.write_register.i32(metadata !5, i32 %x) + call void @llvm.write_register.i32(metadata !6, i32 %x) + call void @llvm.write_register.i32(metadata !7, i32 %x) + call void @llvm.write_register.i32(metadata !8, i32 %x) + call void @llvm.write_register.i32(metadata !9, i32 %x) + call void @llvm.write_register.i32(metadata !10, i32 %x) + call void @llvm.write_register.i32(metadata !11, i32 %x) + call void @llvm.write_register.i32(metadata !12, i32 %x) + call void @llvm.write_register.i32(metadata !13, i32 %x) + call void @llvm.write_register.i32(metadata !14, i32 %x) + call void @llvm.write_register.i32(metadata !15, i32 %x) + call void @llvm.write_register.i32(metadata !16, i32 %x) + call void @llvm.write_register.i32(metadata !17, i32 %x) + call void @llvm.write_register.i32(metadata !18, i32 %x) + call void @llvm.write_register.i32(metadata !19, i32 %x) + call void @llvm.write_register.i32(metadata !20, i32 %x) + call void @llvm.write_register.i32(metadata !21, i32 %x) + call void @llvm.write_register.i32(metadata !22, i32 %x) + call void @llvm.write_register.i32(metadata !23, i32 %x) + call void @llvm.write_register.i32(metadata !24, i32 %x) + call void @llvm.write_register.i32(metadata !25, i32 %x) + ret void +} + +declare i32 @llvm.read_register.i32(metadata) nounwind +declare void @llvm.write_register.i32(metadata, i32) nounwind + +!0 = !{!"apsr"} +!1 = !{!"apsr_nzcvq"} +!2 = !{!"apsr_g"} +!3 = !{!"apsr_nzcvqg"} +!4 = !{!"iapsr"} +!5 = !{!"iapsr_nzcvq"} +!6 = !{!"iapsr_g"} +!7 = !{!"iapsr_nzcvqg"} +!8 = !{!"eapsr"} +!9 = !{!"eapsr_nzcvq"} +!10 = !{!"eapsr_g"} +!11 = !{!"eapsr_nzcvqg"} +!12 = !{!"xpsr"} +!13 = !{!"xpsr_nzcvq"} +!14 = !{!"xpsr_g"} +!15 = !{!"xpsr_nzcvqg"} +!16 = !{!"ipsr"} +!17 = !{!"epsr"} +!18 = !{!"iepsr"} +!19 = !{!"msp"} +!20 = !{!"psp"} +!21 = !{!"primask"} +!22 = !{!"basepri"} +!23 = !{!"basepri_max"} +!24 = !{!"faultmask"} +!25 = !{!"control"} 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"}