summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRenato Golin <renato.golin@linaro.org>2014-05-06 16:51:25 +0000
committerRenato Golin <renato.golin@linaro.org>2014-05-06 16:51:25 +0000
commit22f779d1fd0d7a7e878a3c28bc5d9910ae655df3 (patch)
tree272b3978eb4e467ad4854aa00a03dda055046001
parentb8894488415a81bb6bdaf9851acacc93a4e7cfe2 (diff)
downloadllvm-22f779d1fd0d7a7e878a3c28bc5d9910ae655df3.tar.gz
llvm-22f779d1fd0d7a7e878a3c28bc5d9910ae655df3.tar.bz2
llvm-22f779d1fd0d7a7e878a3c28bc5d9910ae655df3.tar.xz
Implememting named register intrinsics
This patch implements the infrastructure to use named register constructs in programs that need access to specific registers (bare metal, kernels, etc). So far, only the stack pointer is supported as a technology preview, but as it is, the intrinsic can already support all non-allocatable registers from any architecture. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@208104 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--docs/LangRef.rst45
-rw-r--r--include/llvm/CodeGen/ISDOpcodes.h5
-rw-r--r--include/llvm/CodeGen/SelectionDAGISel.h2
-rw-r--r--include/llvm/IR/Intrinsics.td4
-rw-r--r--include/llvm/Target/TargetLowering.h7
-rw-r--r--lib/CodeGen/SelectionDAG/LegalizeDAG.cpp7
-rw-r--r--lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp16
-rw-r--r--lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp2
-rw-r--r--lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp30
-rw-r--r--lib/Target/AArch64/AArch64ISelLowering.cpp12
-rw-r--r--lib/Target/AArch64/AArch64ISelLowering.h2
-rw-r--r--lib/Target/ARM/ARMISelLowering.cpp11
-rw-r--r--lib/Target/ARM/ARMISelLowering.h2
-rw-r--r--lib/Target/ARM64/ARM64ISelLowering.cpp11
-rw-r--r--lib/Target/ARM64/ARM64ISelLowering.h1
-rw-r--r--lib/Target/X86/X86ISelLowering.cpp13
-rw-r--r--lib/Target/X86/X86ISelLowering.h2
-rw-r--r--test/CodeGen/AArch64/named-reg-alloc.ll13
-rw-r--r--test/CodeGen/AArch64/named-reg-notareg.ll12
-rw-r--r--test/CodeGen/AArch64/stackpointer.ll24
-rw-r--r--test/CodeGen/ARM/named-reg-alloc.ll14
-rw-r--r--test/CodeGen/ARM/named-reg-notareg.ll13
-rw-r--r--test/CodeGen/ARM/stackpointer.ll25
-rw-r--r--test/CodeGen/ARM64/named-reg-alloc.ll14
-rw-r--r--test/CodeGen/ARM64/named-reg-notareg.ll13
-rw-r--r--test/CodeGen/ARM64/stackpointer.ll24
-rw-r--r--test/CodeGen/X86/named-reg-alloc.ll14
-rw-r--r--test/CodeGen/X86/named-reg-notareg.ll13
-rw-r--r--test/CodeGen/X86/stackpointer.ll25
29 files changed, 376 insertions, 0 deletions
diff --git a/docs/LangRef.rst b/docs/LangRef.rst
index 3d99a0e79b..a5055f6605 100644
--- a/docs/LangRef.rst
+++ b/docs/LangRef.rst
@@ -6804,6 +6804,51 @@ Note that calling this intrinsic does not prevent function inlining or
other aggressive transformations, so the value returned may not be that
of the obvious source-language caller.
+.. _int_read_register:
+.. _int_write_register:
+
+'``llvm.read_register``' and '``llvm.write_register``' Intrinsics
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+ declare i32 @llvm.read_register.i32(metadata)
+ declare i64 @llvm.read_register.i64(metadata)
+ declare void @llvm.write_register.i32(metadata, i32 @value)
+ declare void @llvm.write_register.i64(metadata, i64 @value)
+ !0 = metadata !{metadata !"sp\00"}
+
+Overview:
+"""""""""
+
+The '``llvm.read_register``' and '``llvm.write_register``' intrinsics
+provides access to the named register. The register must be valid on
+the architecture being compiled to. The type needs to be compatible
+with the register being read.
+
+Semantics:
+""""""""""
+
+The '``llvm.read_register``' intrinsic returns the current value of the
+register, where possible. The '``llvm.write_register``' intrinsic sets
+the current value of the register, where possible.
+
+This is useful to implement named register global variables that need
+to always be mapped to a specific register, as is common practice on
+bare-metal programs including OS kernels.
+
+The compiler doesn't check for register availability or use of the used
+register in surrounding code, including inline assembly. Because of that,
+allocatable registers are not supported.
+
+Warning: So far it only works with the stack pointer on selected
+architectures (ARM, ARM64, x86_64 and AArch64). Significant amount of
+work is needed to support other registers and even more so, allocatable
+registers.
+
.. _int_stacksave:
'``llvm.stacksave``' Intrinsic
diff --git a/include/llvm/CodeGen/ISDOpcodes.h b/include/llvm/CodeGen/ISDOpcodes.h
index 89b0908625..49891b2934 100644
--- a/include/llvm/CodeGen/ISDOpcodes.h
+++ b/include/llvm/CodeGen/ISDOpcodes.h
@@ -72,6 +72,11 @@ namespace ISD {
/// the parent's frame or return address, and so on.
FRAMEADDR, RETURNADDR,
+ /// READ_REGISTER, WRITE_REGISTER - This node represents llvm.register on
+ /// the DAG, which implements the named register global variables extension.
+ READ_REGISTER,
+ WRITE_REGISTER,
+
/// FRAME_TO_ARGS_OFFSET - This node represents offset from frame pointer to
/// first (possible) on-stack argument. This is needed for correct stack
/// adjustment during unwind.
diff --git a/include/llvm/CodeGen/SelectionDAGISel.h b/include/llvm/CodeGen/SelectionDAGISel.h
index 315cddc995..520be402cf 100644
--- a/include/llvm/CodeGen/SelectionDAGISel.h
+++ b/include/llvm/CodeGen/SelectionDAGISel.h
@@ -242,6 +242,8 @@ private:
// Calls to these functions are generated by tblgen.
SDNode *Select_INLINEASM(SDNode *N);
+ SDNode *Select_READ_REGISTER(SDNode *N);
+ SDNode *Select_WRITE_REGISTER(SDNode *N);
SDNode *Select_UNDEF(SDNode *N);
void CannotYetSelect(SDNode *N);
diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td
index 6a48f17393..8ad24b37f7 100644
--- a/include/llvm/IR/Intrinsics.td
+++ b/include/llvm/IR/Intrinsics.td
@@ -250,6 +250,10 @@ def int_gcwrite : Intrinsic<[],
//
def int_returnaddress : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], [IntrNoMem]>;
def int_frameaddress : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], [IntrNoMem]>;
+def int_read_register : Intrinsic<[llvm_anyint_ty], [llvm_metadata_ty],
+ [IntrNoMem], "llvm.read_register">;
+def int_write_register : Intrinsic<[], [llvm_metadata_ty, llvm_anyint_ty],
+ [IntrNoMem], "llvm.write_register">;
// Note: we treat stacksave/stackrestore as writemem because we don't otherwise
// model their dependencies on allocas.
diff --git a/include/llvm/Target/TargetLowering.h b/include/llvm/Target/TargetLowering.h
index 99adf231b5..5ce0643ea7 100644
--- a/include/llvm/Target/TargetLowering.h
+++ b/include/llvm/Target/TargetLowering.h
@@ -2214,6 +2214,13 @@ public:
return "__clear_cache";
}
+ /// Return the register ID of the name passed in. Used by named register
+ /// global variables extension. There is no target-independent behaviour
+ /// so the default action is to bail.
+ virtual unsigned getRegisterByName(const char* RegName) const {
+ report_fatal_error("Named registers not implemented for this target");
+ }
+
/// Return the type that should be used to zero or sign extend a
/// zeroext/signext integer argument or return value. FIXME: Most C calling
/// convention requires the return type to be promoted, but this is not true
diff --git a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
index dac372fcea..a2ea1aa537 100644
--- a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
+++ b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
@@ -1265,6 +1265,13 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) {
if (Action == TargetLowering::Legal)
Action = TargetLowering::Custom;
break;
+ case ISD::READ_REGISTER:
+ case ISD::WRITE_REGISTER:
+ // Named register is legal in the DAG, but blocked by register name
+ // selection if not implemented by target (to chose the correct register)
+ // They'll be converted to Copy(To/From)Reg.
+ Action = TargetLowering::Legal;
+ break;
case ISD::DEBUGTRAP:
Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0));
if (Action == TargetLowering::Expand) {
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 1cc6105e98..87b9797de7 100644
--- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -4627,6 +4627,22 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
setValue(&I, DAG.getNode(ISD::FRAMEADDR, sdl, TLI->getPointerTy(),
getValue(I.getArgOperand(0))));
return nullptr;
+ case Intrinsic::read_register: {
+ Value *Reg = I.getArgOperand(0);
+ SDValue RegName = DAG.getMDNode(cast<MDNode>(Reg));
+ EVT VT = TM.getTargetLowering()->getValueType(I.getType());
+ setValue(&I, DAG.getNode(ISD::READ_REGISTER, sdl, VT, RegName));
+ return nullptr;
+ }
+ 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<MDNode>(Reg));
+ DAG.setRoot(DAG.getNode(ISD::WRITE_REGISTER, sdl, MVT::Other, Chain,
+ RegName, getValue(RegValue)));
+ return nullptr;
+ }
case Intrinsic::setjmp:
return &"_setjmp"[!TLI->usesUnderscoreSetJmp()];
case Intrinsic::longjmp:
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
index aebda04b8f..d6b525500a 100644
--- a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -93,6 +93,8 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
case ISD::GLOBAL_OFFSET_TABLE: return "GLOBAL_OFFSET_TABLE";
case ISD::RETURNADDR: return "RETURNADDR";
case ISD::FRAMEADDR: return "FRAMEADDR";
+ case ISD::READ_REGISTER: return "READ_REGISTER";
+ case ISD::WRITE_REGISTER: return "WRITE_REGISTER";
case ISD::FRAME_TO_ARGS_OFFSET: return "FRAME_TO_ARGS_OFFSET";
case ISD::EH_RETURN: return "EH_RETURN";
case ISD::EH_SJLJ_SETJMP: return "EH_SJLJ_SETJMP";
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index a249028107..1cbc83a3f6 100644
--- a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -1807,6 +1807,34 @@ SDNode *SelectionDAGISel::Select_INLINEASM(SDNode *N) {
return New.getNode();
}
+SDNode
+*SelectionDAGISel::Select_READ_REGISTER(SDNode *Op) {
+ SDLoc dl(Op);
+ MDNodeSDNode *MD = dyn_cast<MDNodeSDNode>(Op->getOperand(0));
+ const MDString *RegStr = dyn_cast<MDString>(MD->getMD()->getOperand(0));
+ unsigned Reg = getTargetLowering()->getRegisterByName(
+ RegStr->getString().data());
+ SDValue New = CurDAG->getCopyFromReg(
+ CurDAG->getEntryNode(), dl, Reg, Op->getValueType(0));
+ New->setNodeId(-1);
+ return New.getNode();
+}
+
+SDNode
+*SelectionDAGISel::Select_WRITE_REGISTER(SDNode *Op) {
+ SDLoc dl(Op);
+ MDNodeSDNode *MD = dyn_cast<MDNodeSDNode>(Op->getOperand(1));
+ const MDString *RegStr = dyn_cast<MDString>(MD->getMD()->getOperand(0));
+ unsigned Reg = getTargetLowering()->getRegisterByName(
+ RegStr->getString().data());
+ SDValue New = CurDAG->getCopyToReg(
+ CurDAG->getEntryNode(), dl, Reg, Op->getOperand(2));
+ New->setNodeId(-1);
+ return New.getNode();
+}
+
+
+
SDNode *SelectionDAGISel::Select_UNDEF(SDNode *N) {
return CurDAG->SelectNodeTo(N, TargetOpcode::IMPLICIT_DEF,N->getValueType(0));
}
@@ -2399,6 +2427,8 @@ SelectCodeCommon(SDNode *NodeToMatch, const unsigned char *MatcherTable,
NodeToMatch->getOperand(0));
return nullptr;
case ISD::INLINEASM: return Select_INLINEASM(NodeToMatch);
+ case ISD::READ_REGISTER: return Select_READ_REGISTER(NodeToMatch);
+ case ISD::WRITE_REGISTER: return Select_WRITE_REGISTER(NodeToMatch);
case ISD::UNDEF: return Select_UNDEF(NodeToMatch);
}
diff --git a/lib/Target/AArch64/AArch64ISelLowering.cpp b/lib/Target/AArch64/AArch64ISelLowering.cpp
index 5ad2036d51..a016e6e485 100644
--- a/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -15,6 +15,7 @@
#include "AArch64.h"
#include "AArch64ISelLowering.h"
#include "AArch64MachineFunctionInfo.h"
+#include "AArch64Subtarget.h"
#include "AArch64TargetMachine.h"
#include "AArch64TargetObjectFile.h"
#include "Utils/AArch64BaseInfo.h"
@@ -2406,6 +2407,17 @@ SDValue AArch64TargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG)
return FrameAddr;
}
+// FIXME? Maybe this could be a TableGen attribute on some registers and
+// this table could be generated automatically from RegInfo.
+unsigned AArch64TargetLowering::getRegisterByName(const char* RegName) const {
+ unsigned Reg = StringSwitch<unsigned>(RegName)
+ .Case("sp", AArch64::XSP)
+ .Default(0);
+ if (Reg)
+ return Reg;
+ report_fatal_error("Invalid register name global variable");
+}
+
SDValue
AArch64TargetLowering::LowerGlobalAddressELFLarge(SDValue Op,
SelectionDAG &DAG) const {
diff --git a/lib/Target/AArch64/AArch64ISelLowering.h b/lib/Target/AArch64/AArch64ISelLowering.h
index f6239a04f8..b3c3b1cecf 100644
--- a/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/lib/Target/AArch64/AArch64ISelLowering.h
@@ -350,6 +350,8 @@ public:
SDValue PerformDAGCombine(SDNode *N,DAGCombinerInfo &DCI) const override;
+ unsigned getRegisterByName(const char* RegName) const;
+
/// isFMAFasterThanFMulAndFAdd - Return true if an FMA operation is faster
/// than a pair of fmul and fadd instructions. fmuladd intrinsics will be
/// expanded to FMAs when this method returns true, otherwise fmuladd is
diff --git a/lib/Target/ARM/ARMISelLowering.cpp b/lib/Target/ARM/ARMISelLowering.cpp
index e1e403c6e7..a8f0d8cc0f 100644
--- a/lib/Target/ARM/ARMISelLowering.cpp
+++ b/lib/Target/ARM/ARMISelLowering.cpp
@@ -3784,6 +3784,17 @@ SDValue ARMTargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const {
return FrameAddr;
}
+// FIXME? Maybe this could be a TableGen attribute on some registers and
+// this table could be generated automatically from RegInfo.
+unsigned ARMTargetLowering::getRegisterByName(const char* RegName) const {
+ unsigned Reg = StringSwitch<unsigned>(RegName)
+ .Case("sp", ARM::SP)
+ .Default(0);
+ if (Reg)
+ return Reg;
+ report_fatal_error("Invalid register name global variable");
+}
+
/// 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
diff --git a/lib/Target/ARM/ARMISelLowering.h b/lib/Target/ARM/ARMISelLowering.h
index e8dd043ecd..7f28a251f5 100644
--- a/lib/Target/ARM/ARMISelLowering.h
+++ b/lib/Target/ARM/ARMISelLowering.h
@@ -461,6 +461,8 @@ namespace llvm {
SDValue LowerFSINCOS(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerDivRem(SDValue Op, SelectionDAG &DAG) const;
+ unsigned getRegisterByName(const char* RegName) const;
+
/// isFMAFasterThanFMulAndFAdd - Return true if an FMA operation is faster
/// than a pair of fmul and fadd instructions. fmuladd intrinsics will be
/// expanded to FMAs when this method returns true, otherwise fmuladd is
diff --git a/lib/Target/ARM64/ARM64ISelLowering.cpp b/lib/Target/ARM64/ARM64ISelLowering.cpp
index 7de9d079ed..19c76c6431 100644
--- a/lib/Target/ARM64/ARM64ISelLowering.cpp
+++ b/lib/Target/ARM64/ARM64ISelLowering.cpp
@@ -3384,6 +3384,17 @@ SDValue ARM64TargetLowering::LowerFRAMEADDR(SDValue Op,
return FrameAddr;
}
+// FIXME? Maybe this could be a TableGen attribute on some registers and
+// this table could be generated automatically from RegInfo.
+unsigned ARM64TargetLowering::getRegisterByName(const char* RegName) const {
+ unsigned Reg = StringSwitch<unsigned>(RegName)
+ .Case("sp", ARM64::SP)
+ .Default(0);
+ if (Reg)
+ return Reg;
+ report_fatal_error("Invalid register name global variable");
+}
+
SDValue ARM64TargetLowering::LowerRETURNADDR(SDValue Op,
SelectionDAG &DAG) const {
MachineFunction &MF = DAG.getMachineFunction();
diff --git a/lib/Target/ARM64/ARM64ISelLowering.h b/lib/Target/ARM64/ARM64ISelLowering.h
index 96a32ea85c..aa27b2d43d 100644
--- a/lib/Target/ARM64/ARM64ISelLowering.h
+++ b/lib/Target/ARM64/ARM64ISelLowering.h
@@ -388,6 +388,7 @@ private:
ConstraintType
getConstraintType(const std::string &Constraint) const override;
+ unsigned getRegisterByName(const char* RegName) const;
/// Examine constraint string and operand type and determine a weight value.
/// The operand object must already have been set up with the operand type.
diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp
index 546a6068ad..2f14d79074 100644
--- a/lib/Target/X86/X86ISelLowering.cpp
+++ b/lib/Target/X86/X86ISelLowering.cpp
@@ -22,6 +22,7 @@
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/VariadicFunction.h"
#include "llvm/CodeGen/IntrinsicLowering.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
@@ -12726,6 +12727,18 @@ SDValue X86TargetLowering::LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const {
return FrameAddr;
}
+// FIXME? Maybe this could be a TableGen attribute on some registers and
+// this table could be generated automatically from RegInfo.
+unsigned X86TargetLowering::getRegisterByName(const char* RegName) const {
+ unsigned Reg = StringSwitch<unsigned>(RegName)
+ .Case("esp", X86::ESP)
+ .Case("rsp", X86::RSP)
+ .Default(0);
+ if (Reg)
+ return Reg;
+ report_fatal_error("Invalid register name global variable");
+}
+
SDValue X86TargetLowering::LowerFRAME_TO_ARGS_OFFSET(SDValue Op,
SelectionDAG &DAG) const {
const X86RegisterInfo *RegInfo =
diff --git a/lib/Target/X86/X86ISelLowering.h b/lib/Target/X86/X86ISelLowering.h
index dd6ff76d6c..3ddf395357 100644
--- a/lib/Target/X86/X86ISelLowering.h
+++ b/lib/Target/X86/X86ISelLowering.h
@@ -786,6 +786,8 @@ namespace llvm {
return nullptr; // nothing to do, move along.
}
+ unsigned getRegisterByName(const char* RegName) const;
+
/// createFastISel - This method returns a target specific FastISel object,
/// or null if the target does not support "fast" ISel.
FastISel *createFastISel(FunctionLoweringInfo &funcInfo,
diff --git a/test/CodeGen/AArch64/named-reg-alloc.ll b/test/CodeGen/AArch64/named-reg-alloc.ll
new file mode 100644
index 0000000000..52ccdf01c7
--- /dev/null
+++ b/test/CodeGen/AArch64/named-reg-alloc.ll
@@ -0,0 +1,13 @@
+; RUN: not llc < %s -mtriple=aarch64-linux-gnueabi 2>&1 | FileCheck %s
+
+define i32 @get_stack() nounwind {
+entry:
+; FIXME: Include an allocatable-specific error message
+; CHECK: Invalid register name global variable
+ %sp = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %sp
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+
+!0 = metadata !{metadata !"x5\00"}
diff --git a/test/CodeGen/AArch64/named-reg-notareg.ll b/test/CodeGen/AArch64/named-reg-notareg.ll
new file mode 100644
index 0000000000..aed8a85b44
--- /dev/null
+++ b/test/CodeGen/AArch64/named-reg-notareg.ll
@@ -0,0 +1,12 @@
+; RUN: not llc < %s -mtriple=aarch64-linux-gnueabi 2>&1 | FileCheck %s
+
+define i32 @get_stack() nounwind {
+entry:
+; CHECK: Invalid register name global variable
+ %sp = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %sp
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+
+!0 = metadata !{metadata !"notareg\00"}
diff --git a/test/CodeGen/AArch64/stackpointer.ll b/test/CodeGen/AArch64/stackpointer.ll
new file mode 100644
index 0000000000..00eabcb297
--- /dev/null
+++ b/test/CodeGen/AArch64/stackpointer.ll
@@ -0,0 +1,24 @@
+; RUN: llc < %s -mtriple=aarch64-linux-gnueabi | FileCheck %s
+
+define i64 @get_stack() nounwind {
+entry:
+; CHECK-LABEL: get_stack:
+; CHECK: mov x0, sp
+ %sp = call i64 @llvm.read_register.i64(metadata !0)
+ ret i64 %sp
+}
+
+define void @set_stack(i64 %val) nounwind {
+entry:
+; CHECK-LABEL: set_stack:
+; CHECK: mov sp, x0
+ call void @llvm.write_register.i64(metadata !0, i64 %val)
+ ret void
+}
+
+declare i64 @llvm.read_register.i64(metadata) nounwind
+declare void @llvm.write_register.i64(metadata, i64) nounwind
+
+; register unsigned long current_stack_pointer asm("sp");
+; CHECK-NOT: .asciz "sp"
+!0 = metadata !{metadata !"sp\00"}
diff --git a/test/CodeGen/ARM/named-reg-alloc.ll b/test/CodeGen/ARM/named-reg-alloc.ll
new file mode 100644
index 0000000000..3c27d2244e
--- /dev/null
+++ b/test/CodeGen/ARM/named-reg-alloc.ll
@@ -0,0 +1,14 @@
+; RUN: not llc < %s -mtriple=arm-apple-darwin 2>&1 | FileCheck %s
+; RUN: not llc < %s -mtriple=arm-linux-gnueabi 2>&1 | FileCheck %s
+
+define i32 @get_stack() nounwind {
+entry:
+; FIXME: Include an allocatable-specific error message
+; CHECK: Invalid register name global variable
+ %sp = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %sp
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+
+!0 = metadata !{metadata !"r5\00"}
diff --git a/test/CodeGen/ARM/named-reg-notareg.ll b/test/CodeGen/ARM/named-reg-notareg.ll
new file mode 100644
index 0000000000..af38b609b4
--- /dev/null
+++ b/test/CodeGen/ARM/named-reg-notareg.ll
@@ -0,0 +1,13 @@
+; RUN: not llc < %s -mtriple=arm-apple-darwin 2>&1 | FileCheck %s
+; RUN: not llc < %s -mtriple=arm-linux-gnueabi 2>&1 | FileCheck %s
+
+define i32 @get_stack() nounwind {
+entry:
+; CHECK: Invalid register name global variable
+ %sp = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %sp
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+
+!0 = metadata !{metadata !"notareg\00"}
diff --git a/test/CodeGen/ARM/stackpointer.ll b/test/CodeGen/ARM/stackpointer.ll
new file mode 100644
index 0000000000..420a9166d7
--- /dev/null
+++ b/test/CodeGen/ARM/stackpointer.ll
@@ -0,0 +1,25 @@
+; RUN: llc < %s -mtriple=arm-apple-darwin | FileCheck %s
+; RUN: llc < %s -mtriple=arm-linux-gnueabi | FileCheck %s
+
+define i32 @get_stack() nounwind {
+entry:
+; CHECK-LABEL: get_stack:
+; CHECK: mov r0, sp
+ %sp = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %sp
+}
+
+define void @set_stack(i32 %val) nounwind {
+entry:
+; CHECK-LABEL: set_stack:
+; CHECK: mov sp, r0
+ call void @llvm.write_register.i32(metadata !0, i32 %val)
+ ret void
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+declare void @llvm.write_register.i32(metadata, i32) nounwind
+
+; register unsigned long current_stack_pointer asm("sp");
+; CHECK-NOT: .asciz "sp"
+!0 = metadata !{metadata !"sp\00"}
diff --git a/test/CodeGen/ARM64/named-reg-alloc.ll b/test/CodeGen/ARM64/named-reg-alloc.ll
new file mode 100644
index 0000000000..d86d2e617e
--- /dev/null
+++ b/test/CodeGen/ARM64/named-reg-alloc.ll
@@ -0,0 +1,14 @@
+; RUN: not llc < %s -mtriple=arm64-apple-darwin 2>&1 | FileCheck %s
+; RUN: not llc < %s -mtriple=arm64-linux-gnueabi 2>&1 | FileCheck %s
+
+define i32 @get_stack() nounwind {
+entry:
+; FIXME: Include an allocatable-specific error message
+; CHECK: Invalid register name global variable
+ %sp = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %sp
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+
+!0 = metadata !{metadata !"x5\00"}
diff --git a/test/CodeGen/ARM64/named-reg-notareg.ll b/test/CodeGen/ARM64/named-reg-notareg.ll
new file mode 100644
index 0000000000..3ca14c408f
--- /dev/null
+++ b/test/CodeGen/ARM64/named-reg-notareg.ll
@@ -0,0 +1,13 @@
+; RUN: not llc < %s -mtriple=arm64-apple-darwin 2>&1 | FileCheck %s
+; RUN: not llc < %s -mtriple=arm64-linux-gnueabi 2>&1 | FileCheck %s
+
+define i32 @get_stack() nounwind {
+entry:
+; CHECK: Invalid register name global variable
+ %sp = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %sp
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+
+!0 = metadata !{metadata !"notareg\00"}
diff --git a/test/CodeGen/ARM64/stackpointer.ll b/test/CodeGen/ARM64/stackpointer.ll
new file mode 100644
index 0000000000..581faf130f
--- /dev/null
+++ b/test/CodeGen/ARM64/stackpointer.ll
@@ -0,0 +1,24 @@
+; RUN: llc < %s -mtriple=arm64-linux-gnu | FileCheck %s
+
+define i64 @get_stack() nounwind {
+entry:
+; CHECK-LABEL: get_stack:
+; CHECK: mov x0, sp
+ %sp = call i64 @llvm.read_register.i64(metadata !0)
+ ret i64 %sp
+}
+
+define void @set_stack(i64 %val) nounwind {
+entry:
+; CHECK-LABEL: set_stack:
+; CHECK: mov sp, x0
+ call void @llvm.write_register.i64(metadata !0, i64 %val)
+ ret void
+}
+
+declare i64 @llvm.read_register.i64(metadata) nounwind
+declare void @llvm.write_register.i64(metadata, i64) nounwind
+
+; register unsigned long current_stack_pointer asm("sp");
+; CHECK-NOT: .asciz "sp"
+!0 = metadata !{metadata !"sp\00"}
diff --git a/test/CodeGen/X86/named-reg-alloc.ll b/test/CodeGen/X86/named-reg-alloc.ll
new file mode 100644
index 0000000000..9463ea377a
--- /dev/null
+++ b/test/CodeGen/X86/named-reg-alloc.ll
@@ -0,0 +1,14 @@
+; RUN: not llc < %s -mtriple=x86_64-apple-darwin 2>&1 | FileCheck %s
+; RUN: not llc < %s -mtriple=x86_64-linux-gnueabi 2>&1 | FileCheck %s
+
+define i32 @get_stack() nounwind {
+entry:
+; FIXME: Include an allocatable-specific error message
+; CHECK: Invalid register name global variable
+ %sp = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %sp
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+
+!0 = metadata !{metadata !"eax\00"}
diff --git a/test/CodeGen/X86/named-reg-notareg.ll b/test/CodeGen/X86/named-reg-notareg.ll
new file mode 100644
index 0000000000..d85ddddbea
--- /dev/null
+++ b/test/CodeGen/X86/named-reg-notareg.ll
@@ -0,0 +1,13 @@
+; RUN: not llc < %s -mtriple=x86_64-apple-darwin 2>&1 | FileCheck %s
+; RUN: not llc < %s -mtriple=x86_64-linux-gnueabi 2>&1 | FileCheck %s
+
+define i32 @get_stack() nounwind {
+entry:
+; CHECK: Invalid register name global variable
+ %sp = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %sp
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+
+!0 = metadata !{metadata !"notareg\00"}
diff --git a/test/CodeGen/X86/stackpointer.ll b/test/CodeGen/X86/stackpointer.ll
new file mode 100644
index 0000000000..26f9999fd5
--- /dev/null
+++ b/test/CodeGen/X86/stackpointer.ll
@@ -0,0 +1,25 @@
+; RUN: llc < %s -mtriple=x86_64-apple-darwin | FileCheck %s
+; RUN: llc < %s -mtriple=x86_64-linux-gnueabi | FileCheck %s
+
+define i64 @get_stack() nounwind {
+entry:
+; CHECK-LABEL: get_stack:
+; CHECK: movq %rsp, %rax
+ %sp = call i64 @llvm.read_register.i64(metadata !0)
+ ret i64 %sp
+}
+
+define void @set_stack(i64 %val) nounwind {
+entry:
+; CHECK-LABEL: set_stack:
+; CHECK: movq %rdi, %rsp
+ call void @llvm.write_register.i64(metadata !0, i64 %val)
+ ret void
+}
+
+declare i64 @llvm.read_register.i64(metadata) nounwind
+declare void @llvm.write_register.i64(metadata, i64) nounwind
+
+; register unsigned long current_stack_pointer asm("rsp");
+; CHECK-NOT: .asciz "rsp"
+!0 = metadata !{metadata !"rsp\00"}