mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-02-07 14:33:15 +00:00
Re-commit of r238201 with fix for building with shared libraries.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@238739 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
af0e519127
commit
7d97fc4164
@ -156,6 +156,9 @@ public:
|
|||||||
|
|
||||||
SDNode *SelectLIBM(SDNode *N);
|
SDNode *SelectLIBM(SDNode *N);
|
||||||
|
|
||||||
|
SDNode *SelectReadRegister(SDNode *N);
|
||||||
|
SDNode *SelectWriteRegister(SDNode *N);
|
||||||
|
|
||||||
// Include the pieces autogenerated from the target description.
|
// Include the pieces autogenerated from the target description.
|
||||||
#include "AArch64GenDAGISel.inc"
|
#include "AArch64GenDAGISel.inc"
|
||||||
|
|
||||||
@ -2114,6 +2117,120 @@ AArch64DAGToDAGISel::SelectCVTFixedPosOperand(SDValue N, SDValue &FixedPos,
|
|||||||
return true;
|
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<StringRef, 5> Fields;
|
||||||
|
RegString.split(Fields, ":");
|
||||||
|
|
||||||
|
if (Fields.size() == 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
assert(Fields.size() == 5
|
||||||
|
&& "Invalid number of fields in read register string");
|
||||||
|
|
||||||
|
SmallVector<int, 5> 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<MDNodeSDNode>(N->getOperand(1));
|
||||||
|
const MDString *RegString = dyn_cast<MDString>(MD->getMD()->getOperand(0));
|
||||||
|
SDLoc DL(N);
|
||||||
|
|
||||||
|
int Reg = getIntOperandFromRegisterString(RegString->getString());
|
||||||
|
if (Reg != -1)
|
||||||
|
return CurDAG->getMachineNode(AArch64::MRS, DL, N->getSimpleValueType(0),
|
||||||
|
MVT::Other,
|
||||||
|
CurDAG->getTargetConstant(Reg, DL, MVT::i32),
|
||||||
|
N->getOperand(0));
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
MVT::Other,
|
||||||
|
CurDAG->getTargetConstant(Reg, DL, MVT::i32),
|
||||||
|
N->getOperand(0));
|
||||||
|
|
||||||
|
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<MDNodeSDNode>(N->getOperand(1));
|
||||||
|
const MDString *RegString = dyn_cast<MDString>(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<ConstantSDNode>(N->getOperand(2))
|
||||||
|
&& "Expected a constant integer expression.");
|
||||||
|
uint64_t Immed = cast<ConstantSDNode>(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) {
|
SDNode *AArch64DAGToDAGISel::Select(SDNode *Node) {
|
||||||
// Dump information about the Node being selected
|
// Dump information about the Node being selected
|
||||||
DEBUG(errs() << "Selecting: ");
|
DEBUG(errs() << "Selecting: ");
|
||||||
@ -2135,6 +2252,16 @@ SDNode *AArch64DAGToDAGISel::Select(SDNode *Node) {
|
|||||||
default:
|
default:
|
||||||
break;
|
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:
|
case ISD::ADD:
|
||||||
if (SDNode *I = SelectMLAV64LaneV128(Node))
|
if (SDNode *I = SelectMLAV64LaneV128(Node))
|
||||||
return I;
|
return I;
|
||||||
|
@ -4065,7 +4065,8 @@ unsigned AArch64TargetLowering::getRegisterByName(const char* RegName,
|
|||||||
.Default(0);
|
.Default(0);
|
||||||
if (Reg)
|
if (Reg)
|
||||||
return 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,
|
SDValue AArch64TargetLowering::LowerRETURNADDR(SDValue Op,
|
||||||
|
@ -31,5 +31,5 @@ has_jit = 1
|
|||||||
type = Library
|
type = Library
|
||||||
name = AArch64CodeGen
|
name = AArch64CodeGen
|
||||||
parent = AArch64
|
parent = AArch64
|
||||||
required_libraries = AArch64AsmPrinter AArch64Desc AArch64Info Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target
|
required_libraries = AArch64AsmPrinter AArch64Desc AArch64Info AArch64Utils Analysis AsmPrinter CodeGen Core MC Scalar SelectionDAG Support Target
|
||||||
add_to_library_groups = AArch64
|
add_to_library_groups = AArch64
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "ARMBaseInstrInfo.h"
|
#include "ARMBaseInstrInfo.h"
|
||||||
#include "ARMTargetMachine.h"
|
#include "ARMTargetMachine.h"
|
||||||
#include "MCTargetDesc/ARMAddressingModes.h"
|
#include "MCTargetDesc/ARMAddressingModes.h"
|
||||||
|
#include "llvm/ADT/StringSwitch.h"
|
||||||
#include "llvm/CodeGen/MachineFrameInfo.h"
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
||||||
#include "llvm/CodeGen/MachineFunction.h"
|
#include "llvm/CodeGen/MachineFunction.h"
|
||||||
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
||||||
@ -251,6 +252,9 @@ private:
|
|||||||
// Select special operations if node forms integer ABS pattern
|
// Select special operations if node forms integer ABS pattern
|
||||||
SDNode *SelectABSOp(SDNode *N);
|
SDNode *SelectABSOp(SDNode *N);
|
||||||
|
|
||||||
|
SDNode *SelectReadRegister(SDNode *N);
|
||||||
|
SDNode *SelectWriteRegister(SDNode *N);
|
||||||
|
|
||||||
SDNode *SelectInlineAsm(SDNode *N);
|
SDNode *SelectInlineAsm(SDNode *N);
|
||||||
|
|
||||||
SDNode *SelectConcatVector(SDNode *N);
|
SDNode *SelectConcatVector(SDNode *N);
|
||||||
@ -2457,6 +2461,18 @@ SDNode *ARMDAGToDAGISel::Select(SDNode *N) {
|
|||||||
|
|
||||||
switch (N->getOpcode()) {
|
switch (N->getOpcode()) {
|
||||||
default: break;
|
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: {
|
case ISD::INLINEASM: {
|
||||||
SDNode *ResNode = SelectInlineAsm(N);
|
SDNode *ResNode = SelectInlineAsm(N);
|
||||||
if (ResNode)
|
if (ResNode)
|
||||||
@ -3336,6 +3352,418 @@ SDNode *ARMDAGToDAGISel::Select(SDNode *N) {
|
|||||||
return SelectCode(N);
|
return SelectCode(N);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inspect a register string of the form
|
||||||
|
// cp<coprocessor>:<opc1>:c<CRn>:c<CRm>:<opc2> (32bit) or
|
||||||
|
// cp<coprocessor>:<opc1>:c<CRm> (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<SDValue>& Ops) {
|
||||||
|
SmallVector<StringRef, 5> 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<int>(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<int>(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<int>(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<MDNodeSDNode>(N->getOperand(1));
|
||||||
|
const MDString *RegString = dyn_cast<MDString>(MD->getMD()->getOperand(0));
|
||||||
|
bool IsThumb2 = Subtarget->isThumb2();
|
||||||
|
SDLoc DL(N);
|
||||||
|
|
||||||
|
std::vector<SDValue> 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<EVT, 3> ResTypes;
|
||||||
|
if (Ops.size() == 5){
|
||||||
|
Opcode = IsThumb2 ? ARM::t2MRC : ARM::MRC;
|
||||||
|
ResTypes.append({ MVT::i32, MVT::Other });
|
||||||
|
} else {
|
||||||
|
assert(Ops.size() == 3 &&
|
||||||
|
"Invalid number of fields in special register string.");
|
||||||
|
Opcode = IsThumb2 ? ARM::t2MRRC : ARM::MRRC;
|
||||||
|
ResTypes.append({ MVT::i32, MVT::i32, MVT::Other });
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 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),
|
||||||
|
N->getOperand(0) };
|
||||||
|
return CurDAG->getMachineNode(IsThumb2 ? ARM::t2MRSbanked : ARM::MRSbanked,
|
||||||
|
DL, MVT::i32, MVT::Other, 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<unsigned>(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 (Opcode) {
|
||||||
|
if (!Subtarget->hasVFP2())
|
||||||
|
return nullptr;
|
||||||
|
if (Opcode == ARM::VMRS_MVFR2 && !Subtarget->hasFPARMv8())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
Ops = { getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32),
|
||||||
|
N->getOperand(0) };
|
||||||
|
return CurDAG->getMachineNode(Opcode, DL, MVT::i32, MVT::Other, 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),
|
||||||
|
N->getOperand(0) };
|
||||||
|
return CurDAG->getMachineNode(ARM::t2MRS_M, DL, MVT::i32, MVT::Other, 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),
|
||||||
|
N->getOperand(0) };
|
||||||
|
return CurDAG->getMachineNode(IsThumb2 ? ARM::t2MRS_AR : ARM::MRS, DL,
|
||||||
|
MVT::i32, MVT::Other, Ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SpecialReg == "spsr") {
|
||||||
|
Ops = { getAL(CurDAG, DL), CurDAG->getRegister(0, MVT::i32),
|
||||||
|
N->getOperand(0) };
|
||||||
|
return CurDAG->getMachineNode(IsThumb2 ? ARM::t2MRSsys_AR : ARM::MRSsys,
|
||||||
|
DL, MVT::i32, MVT::Other, 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<MDNodeSDNode>(N->getOperand(1));
|
||||||
|
const MDString *RegString = dyn_cast<MDString>(MD->getMD()->getOperand(0));
|
||||||
|
bool IsThumb2 = Subtarget->isThumb2();
|
||||||
|
SDLoc DL(N);
|
||||||
|
|
||||||
|
std::vector<SDValue> 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<unsigned>(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 (Opcode) {
|
||||||
|
if (!Subtarget->hasVFP2())
|
||||||
|
return nullptr;
|
||||||
|
Ops = { N->getOperand(2), getAL(CurDAG, DL),
|
||||||
|
CurDAG->getRegister(0, MVT::i32), N->getOperand(0) };
|
||||||
|
return CurDAG->getMachineNode(Opcode, DL, MVT::Other, Ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
SmallVector<StringRef, 5> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We then check to see if a valid mask can be constructed for one of the
|
||||||
|
// register string values permitted for the A and R class cores. These values
|
||||||
|
// are apsr, spsr and cpsr; these are also valid on older cores.
|
||||||
|
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){
|
SDNode *ARMDAGToDAGISel::SelectInlineAsm(SDNode *N){
|
||||||
std::vector<SDValue> AsmNodeOperands;
|
std::vector<SDValue> AsmNodeOperands;
|
||||||
unsigned Flag, Kind;
|
unsigned Flag, Kind;
|
||||||
|
@ -426,6 +426,9 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
|
|||||||
setOperationAction(ISD::ConstantFP, MVT::f32, Custom);
|
setOperationAction(ISD::ConstantFP, MVT::f32, Custom);
|
||||||
setOperationAction(ISD::ConstantFP, MVT::f64, Custom);
|
setOperationAction(ISD::ConstantFP, MVT::f64, Custom);
|
||||||
|
|
||||||
|
setOperationAction(ISD::READ_REGISTER, MVT::i64, Custom);
|
||||||
|
setOperationAction(ISD::WRITE_REGISTER, MVT::i64, Custom);
|
||||||
|
|
||||||
if (Subtarget->hasNEON()) {
|
if (Subtarget->hasNEON()) {
|
||||||
addDRTypeForNEON(MVT::v2f32);
|
addDRTypeForNEON(MVT::v2f32);
|
||||||
addDRTypeForNEON(MVT::v8i8);
|
addDRTypeForNEON(MVT::v8i8);
|
||||||
@ -2379,6 +2382,24 @@ bool ARMTargetLowering::mayBeEmittedAsTailCall(CallInst *CI) const {
|
|||||||
return !Subtarget->isThumb1Only();
|
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
|
// ConstantPool, JumpTable, GlobalAddress, and ExternalSymbol are lowered as
|
||||||
// their target counterpart wrapped in the ARMISD::Wrapper node. Suppose N is
|
// 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
|
// one of the above mentioned nodes. It has to be wrapped because otherwise
|
||||||
@ -4086,7 +4107,28 @@ unsigned ARMTargetLowering::getRegisterByName(const char* RegName,
|
|||||||
.Default(0);
|
.Default(0);
|
||||||
if (Reg)
|
if (Reg)
|
||||||
return 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 void ExpandREAD_REGISTER(SDNode *N, SmallVectorImpl<SDValue> &Results,
|
||||||
|
SelectionDAG &DAG) {
|
||||||
|
SDLoc DL(N);
|
||||||
|
|
||||||
|
// 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, MVT::Other),
|
||||||
|
N->getOperand(0),
|
||||||
|
N->getOperand(1));
|
||||||
|
|
||||||
|
Results.push_back(DAG.getNode(ISD::BUILD_PAIR, DL, MVT::i64, Read.getValue(0),
|
||||||
|
Read.getValue(1)));
|
||||||
|
Results.push_back(Read.getOperand(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ExpandBITCAST - If the target supports VFP, this function is called to
|
/// ExpandBITCAST - If the target supports VFP, this function is called to
|
||||||
@ -6356,6 +6398,7 @@ static void ReplaceREADCYCLECOUNTER(SDNode *N,
|
|||||||
SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
|
SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
|
||||||
switch (Op.getOpcode()) {
|
switch (Op.getOpcode()) {
|
||||||
default: llvm_unreachable("Don't know how to custom lower this!");
|
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::ConstantPool: return LowerConstantPool(Op, DAG);
|
||||||
case ISD::BlockAddress: return LowerBlockAddress(Op, DAG);
|
case ISD::BlockAddress: return LowerBlockAddress(Op, DAG);
|
||||||
case ISD::GlobalAddress:
|
case ISD::GlobalAddress:
|
||||||
@ -6440,6 +6483,9 @@ void ARMTargetLowering::ReplaceNodeResults(SDNode *N,
|
|||||||
switch (N->getOpcode()) {
|
switch (N->getOpcode()) {
|
||||||
default:
|
default:
|
||||||
llvm_unreachable("Don't know how to custom expand this!");
|
llvm_unreachable("Don't know how to custom expand this!");
|
||||||
|
case ISD::READ_REGISTER:
|
||||||
|
ExpandREAD_REGISTER(N, Results, DAG);
|
||||||
|
break;
|
||||||
case ISD::BITCAST:
|
case ISD::BITCAST:
|
||||||
Res = ExpandBITCAST(N, DAG);
|
Res = ExpandBITCAST(N, DAG);
|
||||||
break;
|
break;
|
||||||
|
@ -5080,10 +5080,11 @@ def : ARMV5TPat<(int_arm_mrc2 imm:$cop, imm:$opc1, imm:$CRn,
|
|||||||
imm:$CRm, imm:$opc2),
|
imm:$CRm, imm:$opc2),
|
||||||
(MRC2 imm:$cop, imm:$opc1, imm:$CRn, imm:$CRm, imm:$opc2)>;
|
(MRC2 imm:$cop, imm:$opc1, imm:$CRn, imm:$CRm, imm:$opc2)>;
|
||||||
|
|
||||||
class MovRRCopro<string opc, bit direction, list<dag> pattern = []>
|
class MovRRCopro<string opc, bit direction, dag oops, dag iops, list<dag>
|
||||||
: ABI<0b1100, (outs), (ins p_imm:$cop, imm0_15:$opc1,
|
pattern = []>
|
||||||
GPRnopc:$Rt, GPRnopc:$Rt2, c_imm:$CRm),
|
: ABI<0b1100, oops, iops, NoItinerary, opc, "\t$cop, $opc1, $Rt, $Rt2, $CRm",
|
||||||
NoItinerary, opc, "\t$cop, $opc1, $Rt, $Rt2, $CRm", pattern> {
|
pattern> {
|
||||||
|
|
||||||
let Inst{23-21} = 0b010;
|
let Inst{23-21} = 0b010;
|
||||||
let Inst{20} = direction;
|
let Inst{20} = direction;
|
||||||
|
|
||||||
@ -5101,9 +5102,13 @@ class MovRRCopro<string opc, bit direction, list<dag> pattern = []>
|
|||||||
}
|
}
|
||||||
|
|
||||||
def MCRR : MovRRCopro<"mcrr", 0 /* from ARM core register to coprocessor */,
|
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,
|
[(int_arm_mcrr imm:$cop, imm:$opc1, GPRnopc:$Rt,
|
||||||
GPRnopc:$Rt2, imm:$CRm)]>;
|
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<string opc, bit direction, list<dag> pattern = []>
|
class MovRRCopro2<string opc, bit direction, list<dag> pattern = []>
|
||||||
: ABXI<0b1100, (outs), (ins p_imm:$cop, imm0_15:$opc1,
|
: ABXI<0b1100, (outs), (ins p_imm:$cop, imm0_15:$opc1,
|
||||||
|
@ -4141,11 +4141,9 @@ class t2MovRCopro<bits<4> Op, string opc, bit direction, dag oops, dag iops,
|
|||||||
let Inst{19-16} = CRn;
|
let Inst{19-16} = CRn;
|
||||||
}
|
}
|
||||||
|
|
||||||
class t2MovRRCopro<bits<4> Op, string opc, bit direction,
|
class t2MovRRCopro<bits<4> Op, string opc, bit direction, dag oops, dag iops,
|
||||||
list<dag> pattern = []>
|
list<dag> pattern = []>
|
||||||
: T2Cop<Op, (outs),
|
: T2Cop<Op, oops, iops, opc, "\t$cop, $opc1, $Rt, $Rt2, $CRm", pattern> {
|
||||||
(ins p_imm:$cop, imm0_15:$opc1, GPR:$Rt, GPR:$Rt2, c_imm:$CRm),
|
|
||||||
opc, "\t$cop, $opc1, $Rt, $Rt2, $CRm", pattern> {
|
|
||||||
let Inst{27-24} = 0b1100;
|
let Inst{27-24} = 0b1100;
|
||||||
let Inst{23-21} = 0b010;
|
let Inst{23-21} = 0b010;
|
||||||
let Inst{20} = direction;
|
let Inst{20} = direction;
|
||||||
@ -4210,19 +4208,25 @@ def : T2v6Pat<(int_arm_mrc2 imm:$cop, imm:$opc1, imm:$CRn, imm:$CRm, imm:$opc2),
|
|||||||
|
|
||||||
|
|
||||||
/* from ARM core register to coprocessor */
|
/* from ARM core register to coprocessor */
|
||||||
def t2MCRR : t2MovRRCopro<0b1110, "mcrr", 0,
|
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,
|
[(int_arm_mcrr imm:$cop, imm:$opc1, GPR:$Rt, GPR:$Rt2,
|
||||||
imm:$CRm)]>;
|
imm:$CRm)]>;
|
||||||
def t2MCRR2 : t2MovRRCopro<0b1111, "mcrr2", 0,
|
def t2MCRR2 : t2MovRRCopro<0b1111, "mcrr2", 0, (outs),
|
||||||
[(int_arm_mcrr2 imm:$cop, imm:$opc1, GPR:$Rt,
|
(ins p_imm:$cop, imm0_15:$opc1, GPR:$Rt, GPR:$Rt2,
|
||||||
GPR:$Rt2, imm:$CRm)]> {
|
c_imm:$CRm),
|
||||||
|
[(int_arm_mcrr2 imm:$cop, imm:$opc1, GPR:$Rt,
|
||||||
|
GPR:$Rt2, imm:$CRm)]> {
|
||||||
let Predicates = [IsThumb2, PreV8];
|
let Predicates = [IsThumb2, PreV8];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* from coprocessor to ARM core register */
|
/* 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];
|
let Predicates = [IsThumb2, PreV8];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
define i32 @get_stack() nounwind {
|
define i32 @get_stack() nounwind {
|
||||||
entry:
|
entry:
|
||||||
; FIXME: Include an allocatable-specific error message
|
; 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)
|
%sp = call i32 @llvm.read_register.i32(metadata !0)
|
||||||
ret i32 %sp
|
ret i32 %sp
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
define i32 @get_stack() nounwind {
|
define i32 @get_stack() nounwind {
|
||||||
entry:
|
entry:
|
||||||
; CHECK: Invalid register name global variable
|
; CHECK: Invalid register name "notareg".
|
||||||
%sp = call i32 @llvm.read_register.i32(metadata !0)
|
%sp = call i32 @llvm.read_register.i32(metadata !0)
|
||||||
ret i32 %sp
|
ret i32 %sp
|
||||||
}
|
}
|
||||||
|
48
test/CodeGen/AArch64/special-reg.ll
Normal file
48
test/CodeGen/AArch64/special-reg.ll
Normal file
@ -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"}
|
3
test/CodeGen/AArch64/special-reg.s.s
Normal file
3
test/CodeGen/AArch64/special-reg.s.s
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.section __TEXT,__text,regular,pure_instructions
|
||||||
|
|
||||||
|
.subsections_via_symbols
|
@ -4,7 +4,7 @@
|
|||||||
define i32 @get_stack() nounwind {
|
define i32 @get_stack() nounwind {
|
||||||
entry:
|
entry:
|
||||||
; FIXME: Include an allocatable-specific error message
|
; 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)
|
%sp = call i32 @llvm.read_register.i32(metadata !0)
|
||||||
ret i32 %sp
|
ret i32 %sp
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
define i32 @get_stack() nounwind {
|
define i32 @get_stack() nounwind {
|
||||||
entry:
|
entry:
|
||||||
; CHECK: Invalid register name global variable
|
; CHECK: Invalid register name "notareg".
|
||||||
%sp = call i32 @llvm.read_register.i32(metadata !0)
|
%sp = call i32 @llvm.read_register.i32(metadata !0)
|
||||||
ret i32 %sp
|
ret i32 %sp
|
||||||
}
|
}
|
||||||
|
78
test/CodeGen/ARM/special-reg-acore.ll
Normal file
78
test/CodeGen/ARM/special-reg-acore.ll
Normal file
@ -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"}
|
143
test/CodeGen/ARM/special-reg-mcore.ll
Normal file
143
test/CodeGen/ARM/special-reg-mcore.ll
Normal file
@ -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"}
|
78
test/CodeGen/ARM/special-reg.ll
Normal file
78
test/CodeGen/ARM/special-reg.ll
Normal file
@ -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"}
|
Loading…
x
Reference in New Issue
Block a user