From 22f779d1fd0d7a7e878a3c28bc5d9910ae655df3 Mon Sep 17 00:00:00 2001 From: Renato Golin Date: Tue, 6 May 2014 16:51:25 +0000 Subject: [PATCH] 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 --- docs/LangRef.rst | 45 +++++++++++++++++++ include/llvm/CodeGen/ISDOpcodes.h | 5 +++ include/llvm/CodeGen/SelectionDAGISel.h | 2 + include/llvm/IR/Intrinsics.td | 4 ++ include/llvm/Target/TargetLowering.h | 7 +++ lib/CodeGen/SelectionDAG/LegalizeDAG.cpp | 7 +++ .../SelectionDAG/SelectionDAGBuilder.cpp | 16 +++++++ .../SelectionDAG/SelectionDAGDumper.cpp | 2 + lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp | 30 +++++++++++++ lib/Target/AArch64/AArch64ISelLowering.cpp | 12 +++++ lib/Target/AArch64/AArch64ISelLowering.h | 2 + lib/Target/ARM/ARMISelLowering.cpp | 11 +++++ lib/Target/ARM/ARMISelLowering.h | 2 + lib/Target/ARM64/ARM64ISelLowering.cpp | 11 +++++ lib/Target/ARM64/ARM64ISelLowering.h | 1 + lib/Target/X86/X86ISelLowering.cpp | 13 ++++++ lib/Target/X86/X86ISelLowering.h | 2 + test/CodeGen/AArch64/named-reg-alloc.ll | 13 ++++++ test/CodeGen/AArch64/named-reg-notareg.ll | 12 +++++ test/CodeGen/AArch64/stackpointer.ll | 24 ++++++++++ test/CodeGen/ARM/named-reg-alloc.ll | 14 ++++++ test/CodeGen/ARM/named-reg-notareg.ll | 13 ++++++ test/CodeGen/ARM/stackpointer.ll | 25 +++++++++++ test/CodeGen/ARM64/named-reg-alloc.ll | 14 ++++++ test/CodeGen/ARM64/named-reg-notareg.ll | 13 ++++++ test/CodeGen/ARM64/stackpointer.ll | 24 ++++++++++ test/CodeGen/X86/named-reg-alloc.ll | 14 ++++++ test/CodeGen/X86/named-reg-notareg.ll | 13 ++++++ test/CodeGen/X86/stackpointer.ll | 25 +++++++++++ 29 files changed, 376 insertions(+) create mode 100644 test/CodeGen/AArch64/named-reg-alloc.ll create mode 100644 test/CodeGen/AArch64/named-reg-notareg.ll create mode 100644 test/CodeGen/AArch64/stackpointer.ll create mode 100644 test/CodeGen/ARM/named-reg-alloc.ll create mode 100644 test/CodeGen/ARM/named-reg-notareg.ll create mode 100644 test/CodeGen/ARM/stackpointer.ll create mode 100644 test/CodeGen/ARM64/named-reg-alloc.ll create mode 100644 test/CodeGen/ARM64/named-reg-notareg.ll create mode 100644 test/CodeGen/ARM64/stackpointer.ll create mode 100644 test/CodeGen/X86/named-reg-alloc.ll create mode 100644 test/CodeGen/X86/named-reg-notareg.ll create mode 100644 test/CodeGen/X86/stackpointer.ll diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 3d99a0e79b1..a5055f6605d 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 89b09086251..49891b2934c 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 315cddc995e..520be402cfc 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 6a48f173935..8ad24b37f74 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 99adf231b5c..5ce0643ea74 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 dac372fceac..a2ea1aa537b 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 1cc6105e983..87b9797de77 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(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(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 aebda04b8f0..d6b525500a8 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 a2490281079..1cbc83a3f65 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(Op->getOperand(0)); + const MDString *RegStr = dyn_cast(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(Op->getOperand(1)); + const MDString *RegStr = dyn_cast(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 5ad2036d512..a016e6e4855 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(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 f6239a04f84..b3c3b1cecfe 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 e1e403c6e74..a8f0d8cc0f5 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(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 e8dd043ecd2..7f28a251f52 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 7de9d079ed2..19c76c64314 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(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 96a32ea85c0..aa27b2d43d2 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 546a6068ad2..2f14d790742 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(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 dd6ff76d6c2..3ddf3953573 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 00000000000..52ccdf01c7a --- /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 00000000000..aed8a85b44d --- /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 00000000000..00eabcb297c --- /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 00000000000..3c27d2244e3 --- /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 00000000000..af38b609b40 --- /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 00000000000..420a9166d79 --- /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 00000000000..d86d2e617ee --- /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 00000000000..3ca14c408f4 --- /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 00000000000..581faf130f1 --- /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 00000000000..9463ea377a9 --- /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 00000000000..d85ddddbea8 --- /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 00000000000..26f9999fd5d --- /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"}