llvm.org GIT mirror llvm / 22f779d
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 Renato Golin 6 years ago
29 changed file(s) with 376 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
68036803 other aggressive transformations, so the value returned may not be that
68046804 of the obvious source-language caller.
68056805
6806 .. _int_read_register:
6807 .. _int_write_register:
6808
6809 '``llvm.read_register``' and '``llvm.write_register``' Intrinsics
6810 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6811
6812 Syntax:
6813 """""""
6814
6815 ::
6816
6817 declare i32 @llvm.read_register.i32(metadata)
6818 declare i64 @llvm.read_register.i64(metadata)
6819 declare void @llvm.write_register.i32(metadata, i32 @value)
6820 declare void @llvm.write_register.i64(metadata, i64 @value)
6821 !0 = metadata !{metadata !"sp\00"}
6822
6823 Overview:
6824 """""""""
6825
6826 The '``llvm.read_register``' and '``llvm.write_register``' intrinsics
6827 provides access to the named register. The register must be valid on
6828 the architecture being compiled to. The type needs to be compatible
6829 with the register being read.
6830
6831 Semantics:
6832 """"""""""
6833
6834 The '``llvm.read_register``' intrinsic returns the current value of the
6835 register, where possible. The '``llvm.write_register``' intrinsic sets
6836 the current value of the register, where possible.
6837
6838 This is useful to implement named register global variables that need
6839 to always be mapped to a specific register, as is common practice on
6840 bare-metal programs including OS kernels.
6841
6842 The compiler doesn't check for register availability or use of the used
6843 register in surrounding code, including inline assembly. Because of that,
6844 allocatable registers are not supported.
6845
6846 Warning: So far it only works with the stack pointer on selected
6847 architectures (ARM, ARM64, x86_64 and AArch64). Significant amount of
6848 work is needed to support other registers and even more so, allocatable
6849 registers.
6850
68066851 .. _int_stacksave:
68076852
68086853 '``llvm.stacksave``' Intrinsic
7070 /// to the current function's frame or return address, an index of one to
7171 /// the parent's frame or return address, and so on.
7272 FRAMEADDR, RETURNADDR,
73
74 /// READ_REGISTER, WRITE_REGISTER - This node represents llvm.register on
75 /// the DAG, which implements the named register global variables extension.
76 READ_REGISTER,
77 WRITE_REGISTER,
7378
7479 /// FRAME_TO_ARGS_OFFSET - This node represents offset from frame pointer to
7580 /// first (possible) on-stack argument. This is needed for correct stack
241241
242242 // Calls to these functions are generated by tblgen.
243243 SDNode *Select_INLINEASM(SDNode *N);
244 SDNode *Select_READ_REGISTER(SDNode *N);
245 SDNode *Select_WRITE_REGISTER(SDNode *N);
244246 SDNode *Select_UNDEF(SDNode *N);
245247 void CannotYetSelect(SDNode *N);
246248
249249 //
250250 def int_returnaddress : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], [IntrNoMem]>;
251251 def int_frameaddress : Intrinsic<[llvm_ptr_ty], [llvm_i32_ty], [IntrNoMem]>;
252 def int_read_register : Intrinsic<[llvm_anyint_ty], [llvm_metadata_ty],
253 [IntrNoMem], "llvm.read_register">;
254 def int_write_register : Intrinsic<[], [llvm_metadata_ty, llvm_anyint_ty],
255 [IntrNoMem], "llvm.write_register">;
252256
253257 // Note: we treat stacksave/stackrestore as writemem because we don't otherwise
254258 // model their dependencies on allocas.
22132213 return "__clear_cache";
22142214 }
22152215
2216 /// Return the register ID of the name passed in. Used by named register
2217 /// global variables extension. There is no target-independent behaviour
2218 /// so the default action is to bail.
2219 virtual unsigned getRegisterByName(const char* RegName) const {
2220 report_fatal_error("Named registers not implemented for this target");
2221 }
2222
22162223 /// Return the type that should be used to zero or sign extend a
22172224 /// zeroext/signext integer argument or return value. FIXME: Most C calling
22182225 /// convention requires the return type to be promoted, but this is not true
12631263 Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0));
12641264 if (Action == TargetLowering::Legal)
12651265 Action = TargetLowering::Custom;
1266 break;
1267 case ISD::READ_REGISTER:
1268 case ISD::WRITE_REGISTER:
1269 // Named register is legal in the DAG, but blocked by register name
1270 // selection if not implemented by target (to chose the correct register)
1271 // They'll be converted to Copy(To/From)Reg.
1272 Action = TargetLowering::Legal;
12661273 break;
12671274 case ISD::DEBUGTRAP:
12681275 Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0));
46264626 setValue(&I, DAG.getNode(ISD::FRAMEADDR, sdl, TLI->getPointerTy(),
46274627 getValue(I.getArgOperand(0))));
46284628 return nullptr;
4629 case Intrinsic::read_register: {
4630 Value *Reg = I.getArgOperand(0);
4631 SDValue RegName = DAG.getMDNode(cast(Reg));
4632 EVT VT = TM.getTargetLowering()->getValueType(I.getType());
4633 setValue(&I, DAG.getNode(ISD::READ_REGISTER, sdl, VT, RegName));
4634 return nullptr;
4635 }
4636 case Intrinsic::write_register: {
4637 Value *Reg = I.getArgOperand(0);
4638 Value *RegValue = I.getArgOperand(1);
4639 SDValue Chain = getValue(RegValue).getOperand(0);
4640 SDValue RegName = DAG.getMDNode(cast(Reg));
4641 DAG.setRoot(DAG.getNode(ISD::WRITE_REGISTER, sdl, MVT::Other, Chain,
4642 RegName, getValue(RegValue)));
4643 return nullptr;
4644 }
46294645 case Intrinsic::setjmp:
46304646 return &"_setjmp"[!TLI->usesUnderscoreSetJmp()];
46314647 case Intrinsic::longjmp:
9292 case ISD::GLOBAL_OFFSET_TABLE: return "GLOBAL_OFFSET_TABLE";
9393 case ISD::RETURNADDR: return "RETURNADDR";
9494 case ISD::FRAMEADDR: return "FRAMEADDR";
95 case ISD::READ_REGISTER: return "READ_REGISTER";
96 case ISD::WRITE_REGISTER: return "WRITE_REGISTER";
9597 case ISD::FRAME_TO_ARGS_OFFSET: return "FRAME_TO_ARGS_OFFSET";
9698 case ISD::EH_RETURN: return "EH_RETURN";
9799 case ISD::EH_SJLJ_SETJMP: return "EH_SJLJ_SETJMP";
18051805 New->setNodeId(-1);
18061806 return New.getNode();
18071807 }
1808
1809 SDNode
1810 *SelectionDAGISel::Select_READ_REGISTER(SDNode *Op) {
1811 SDLoc dl(Op);
1812 MDNodeSDNode *MD = dyn_cast(Op->getOperand(0));
1813 const MDString *RegStr = dyn_cast(MD->getMD()->getOperand(0));
1814 unsigned Reg = getTargetLowering()->getRegisterByName(
1815 RegStr->getString().data());
1816 SDValue New = CurDAG->getCopyFromReg(
1817 CurDAG->getEntryNode(), dl, Reg, Op->getValueType(0));
1818 New->setNodeId(-1);
1819 return New.getNode();
1820 }
1821
1822 SDNode
1823 *SelectionDAGISel::Select_WRITE_REGISTER(SDNode *Op) {
1824 SDLoc dl(Op);
1825 MDNodeSDNode *MD = dyn_cast(Op->getOperand(1));
1826 const MDString *RegStr = dyn_cast(MD->getMD()->getOperand(0));
1827 unsigned Reg = getTargetLowering()->getRegisterByName(
1828 RegStr->getString().data());
1829 SDValue New = CurDAG->getCopyToReg(
1830 CurDAG->getEntryNode(), dl, Reg, Op->getOperand(2));
1831 New->setNodeId(-1);
1832 return New.getNode();
1833 }
1834
1835
18081836
18091837 SDNode *SelectionDAGISel::Select_UNDEF(SDNode *N) {
18101838 return CurDAG->SelectNodeTo(N, TargetOpcode::IMPLICIT_DEF,N->getValueType(0));
23982426 NodeToMatch->getOperand(0));
23992427 return nullptr;
24002428 case ISD::INLINEASM: return Select_INLINEASM(NodeToMatch);
2429 case ISD::READ_REGISTER: return Select_READ_REGISTER(NodeToMatch);
2430 case ISD::WRITE_REGISTER: return Select_WRITE_REGISTER(NodeToMatch);
24012431 case ISD::UNDEF: return Select_UNDEF(NodeToMatch);
24022432 }
24032433
1414 #include "AArch64.h"
1515 #include "AArch64ISelLowering.h"
1616 #include "AArch64MachineFunctionInfo.h"
17 #include "AArch64Subtarget.h"
1718 #include "AArch64TargetMachine.h"
1819 #include "AArch64TargetObjectFile.h"
1920 #include "Utils/AArch64BaseInfo.h"
24052406 return FrameAddr;
24062407 }
24072408
2409 // FIXME? Maybe this could be a TableGen attribute on some registers and
2410 // this table could be generated automatically from RegInfo.
2411 unsigned AArch64TargetLowering::getRegisterByName(const char* RegName) const {
2412 unsigned Reg = StringSwitch(RegName)
2413 .Case("sp", AArch64::XSP)
2414 .Default(0);
2415 if (Reg)
2416 return Reg;
2417 report_fatal_error("Invalid register name global variable");
2418 }
2419
24082420 SDValue
24092421 AArch64TargetLowering::LowerGlobalAddressELFLarge(SDValue Op,
24102422 SelectionDAG &DAG) const {
349349
350350 SDValue PerformDAGCombine(SDNode *N,DAGCombinerInfo &DCI) const override;
351351
352 unsigned getRegisterByName(const char* RegName) const;
353
352354 /// isFMAFasterThanFMulAndFAdd - Return true if an FMA operation is faster
353355 /// than a pair of fmul and fadd instructions. fmuladd intrinsics will be
354356 /// expanded to FMAs when this method returns true, otherwise fmuladd is
37813781 MachinePointerInfo(),
37823782 false, false, false, 0);
37833783 return FrameAddr;
3784 }
3785
3786 // FIXME? Maybe this could be a TableGen attribute on some registers and
3787 // this table could be generated automatically from RegInfo.
3788 unsigned ARMTargetLowering::getRegisterByName(const char* RegName) const {
3789 unsigned Reg = StringSwitch(RegName)
3790 .Case("sp", ARM::SP)
3791 .Default(0);
3792 if (Reg)
3793 return Reg;
3794 report_fatal_error("Invalid register name global variable");
37843795 }
37853796
37863797 /// ExpandBITCAST - If the target supports VFP, this function is called to
460460 SDValue LowerFSINCOS(SDValue Op, SelectionDAG &DAG) const;
461461 SDValue LowerDivRem(SDValue Op, SelectionDAG &DAG) const;
462462
463 unsigned getRegisterByName(const char* RegName) const;
464
463465 /// isFMAFasterThanFMulAndFAdd - Return true if an FMA operation is faster
464466 /// than a pair of fmul and fadd instructions. fmuladd intrinsics will be
465467 /// expanded to FMAs when this method returns true, otherwise fmuladd is
33833383 return FrameAddr;
33843384 }
33853385
3386 // FIXME? Maybe this could be a TableGen attribute on some registers and
3387 // this table could be generated automatically from RegInfo.
3388 unsigned ARM64TargetLowering::getRegisterByName(const char* RegName) const {
3389 unsigned Reg = StringSwitch(RegName)
3390 .Case("sp", ARM64::SP)
3391 .Default(0);
3392 if (Reg)
3393 return Reg;
3394 report_fatal_error("Invalid register name global variable");
3395 }
3396
33863397 SDValue ARM64TargetLowering::LowerRETURNADDR(SDValue Op,
33873398 SelectionDAG &DAG) const {
33883399 MachineFunction &MF = DAG.getMachineFunction();
387387
388388 ConstraintType
389389 getConstraintType(const std::string &Constraint) const override;
390 unsigned getRegisterByName(const char* RegName) const;
390391
391392 /// Examine constraint string and operand type and determine a weight value.
392393 /// The operand object must already have been set up with the operand type.
2121 #include "llvm/ADT/SmallSet.h"
2222 #include "llvm/ADT/Statistic.h"
2323 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/ADT/StringSwitch.h"
2425 #include "llvm/ADT/VariadicFunction.h"
2526 #include "llvm/CodeGen/IntrinsicLowering.h"
2627 #include "llvm/CodeGen/MachineFrameInfo.h"
1272312724 MachinePointerInfo(),
1272412725 false, false, false, 0);
1272512726 return FrameAddr;
12727 }
12728
12729 // FIXME? Maybe this could be a TableGen attribute on some registers and
12730 // this table could be generated automatically from RegInfo.
12731 unsigned X86TargetLowering::getRegisterByName(const char* RegName) const {
12732 unsigned Reg = StringSwitch(RegName)
12733 .Case("esp", X86::ESP)
12734 .Case("rsp", X86::RSP)
12735 .Default(0);
12736 if (Reg)
12737 return Reg;
12738 report_fatal_error("Invalid register name global variable");
1272612739 }
1272712740
1272812741 SDValue X86TargetLowering::LowerFRAME_TO_ARGS_OFFSET(SDValue Op,
784784 const char * getClearCacheBuiltinName() const override {
785785 return nullptr; // nothing to do, move along.
786786 }
787
788 unsigned getRegisterByName(const char* RegName) const;
787789
788790 /// createFastISel - This method returns a target specific FastISel object,
789791 /// or null if the target does not support "fast" ISel.
0 ; RUN: not llc < %s -mtriple=aarch64-linux-gnueabi 2>&1 | FileCheck %s
1
2 define i32 @get_stack() nounwind {
3 entry:
4 ; FIXME: Include an allocatable-specific error message
5 ; CHECK: Invalid register name global variable
6 %sp = call i32 @llvm.read_register.i32(metadata !0)
7 ret i32 %sp
8 }
9
10 declare i32 @llvm.read_register.i32(metadata) nounwind
11
12 !0 = metadata !{metadata !"x5\00"}
0 ; RUN: not llc < %s -mtriple=aarch64-linux-gnueabi 2>&1 | FileCheck %s
1
2 define i32 @get_stack() nounwind {
3 entry:
4 ; CHECK: Invalid register name global variable
5 %sp = call i32 @llvm.read_register.i32(metadata !0)
6 ret i32 %sp
7 }
8
9 declare i32 @llvm.read_register.i32(metadata) nounwind
10
11 !0 = metadata !{metadata !"notareg\00"}
0 ; RUN: llc < %s -mtriple=aarch64-linux-gnueabi | FileCheck %s
1
2 define i64 @get_stack() nounwind {
3 entry:
4 ; CHECK-LABEL: get_stack:
5 ; CHECK: mov x0, sp
6 %sp = call i64 @llvm.read_register.i64(metadata !0)
7 ret i64 %sp
8 }
9
10 define void @set_stack(i64 %val) nounwind {
11 entry:
12 ; CHECK-LABEL: set_stack:
13 ; CHECK: mov sp, x0
14 call void @llvm.write_register.i64(metadata !0, i64 %val)
15 ret void
16 }
17
18 declare i64 @llvm.read_register.i64(metadata) nounwind
19 declare void @llvm.write_register.i64(metadata, i64) nounwind
20
21 ; register unsigned long current_stack_pointer asm("sp");
22 ; CHECK-NOT: .asciz "sp"
23 !0 = metadata !{metadata !"sp\00"}
0 ; RUN: not llc < %s -mtriple=arm-apple-darwin 2>&1 | FileCheck %s
1 ; RUN: not llc < %s -mtriple=arm-linux-gnueabi 2>&1 | FileCheck %s
2
3 define i32 @get_stack() nounwind {
4 entry:
5 ; FIXME: Include an allocatable-specific error message
6 ; CHECK: Invalid register name global variable
7 %sp = call i32 @llvm.read_register.i32(metadata !0)
8 ret i32 %sp
9 }
10
11 declare i32 @llvm.read_register.i32(metadata) nounwind
12
13 !0 = metadata !{metadata !"r5\00"}
0 ; RUN: not llc < %s -mtriple=arm-apple-darwin 2>&1 | FileCheck %s
1 ; RUN: not llc < %s -mtriple=arm-linux-gnueabi 2>&1 | FileCheck %s
2
3 define i32 @get_stack() nounwind {
4 entry:
5 ; CHECK: Invalid register name global variable
6 %sp = call i32 @llvm.read_register.i32(metadata !0)
7 ret i32 %sp
8 }
9
10 declare i32 @llvm.read_register.i32(metadata) nounwind
11
12 !0 = metadata !{metadata !"notareg\00"}
0 ; RUN: llc < %s -mtriple=arm-apple-darwin | FileCheck %s
1 ; RUN: llc < %s -mtriple=arm-linux-gnueabi | FileCheck %s
2
3 define i32 @get_stack() nounwind {
4 entry:
5 ; CHECK-LABEL: get_stack:
6 ; CHECK: mov r0, sp
7 %sp = call i32 @llvm.read_register.i32(metadata !0)
8 ret i32 %sp
9 }
10
11 define void @set_stack(i32 %val) nounwind {
12 entry:
13 ; CHECK-LABEL: set_stack:
14 ; CHECK: mov sp, r0
15 call void @llvm.write_register.i32(metadata !0, i32 %val)
16 ret void
17 }
18
19 declare i32 @llvm.read_register.i32(metadata) nounwind
20 declare void @llvm.write_register.i32(metadata, i32) nounwind
21
22 ; register unsigned long current_stack_pointer asm("sp");
23 ; CHECK-NOT: .asciz "sp"
24 !0 = metadata !{metadata !"sp\00"}
0 ; RUN: not llc < %s -mtriple=arm64-apple-darwin 2>&1 | FileCheck %s
1 ; RUN: not llc < %s -mtriple=arm64-linux-gnueabi 2>&1 | FileCheck %s
2
3 define i32 @get_stack() nounwind {
4 entry:
5 ; FIXME: Include an allocatable-specific error message
6 ; CHECK: Invalid register name global variable
7 %sp = call i32 @llvm.read_register.i32(metadata !0)
8 ret i32 %sp
9 }
10
11 declare i32 @llvm.read_register.i32(metadata) nounwind
12
13 !0 = metadata !{metadata !"x5\00"}
0 ; RUN: not llc < %s -mtriple=arm64-apple-darwin 2>&1 | FileCheck %s
1 ; RUN: not llc < %s -mtriple=arm64-linux-gnueabi 2>&1 | FileCheck %s
2
3 define i32 @get_stack() nounwind {
4 entry:
5 ; CHECK: Invalid register name global variable
6 %sp = call i32 @llvm.read_register.i32(metadata !0)
7 ret i32 %sp
8 }
9
10 declare i32 @llvm.read_register.i32(metadata) nounwind
11
12 !0 = metadata !{metadata !"notareg\00"}
0 ; RUN: llc < %s -mtriple=arm64-linux-gnu | FileCheck %s
1
2 define i64 @get_stack() nounwind {
3 entry:
4 ; CHECK-LABEL: get_stack:
5 ; CHECK: mov x0, sp
6 %sp = call i64 @llvm.read_register.i64(metadata !0)
7 ret i64 %sp
8 }
9
10 define void @set_stack(i64 %val) nounwind {
11 entry:
12 ; CHECK-LABEL: set_stack:
13 ; CHECK: mov sp, x0
14 call void @llvm.write_register.i64(metadata !0, i64 %val)
15 ret void
16 }
17
18 declare i64 @llvm.read_register.i64(metadata) nounwind
19 declare void @llvm.write_register.i64(metadata, i64) nounwind
20
21 ; register unsigned long current_stack_pointer asm("sp");
22 ; CHECK-NOT: .asciz "sp"
23 !0 = metadata !{metadata !"sp\00"}
0 ; RUN: not llc < %s -mtriple=x86_64-apple-darwin 2>&1 | FileCheck %s
1 ; RUN: not llc < %s -mtriple=x86_64-linux-gnueabi 2>&1 | FileCheck %s
2
3 define i32 @get_stack() nounwind {
4 entry:
5 ; FIXME: Include an allocatable-specific error message
6 ; CHECK: Invalid register name global variable
7 %sp = call i32 @llvm.read_register.i32(metadata !0)
8 ret i32 %sp
9 }
10
11 declare i32 @llvm.read_register.i32(metadata) nounwind
12
13 !0 = metadata !{metadata !"eax\00"}
0 ; RUN: not llc < %s -mtriple=x86_64-apple-darwin 2>&1 | FileCheck %s
1 ; RUN: not llc < %s -mtriple=x86_64-linux-gnueabi 2>&1 | FileCheck %s
2
3 define i32 @get_stack() nounwind {
4 entry:
5 ; CHECK: Invalid register name global variable
6 %sp = call i32 @llvm.read_register.i32(metadata !0)
7 ret i32 %sp
8 }
9
10 declare i32 @llvm.read_register.i32(metadata) nounwind
11
12 !0 = metadata !{metadata !"notareg\00"}
0 ; RUN: llc < %s -mtriple=x86_64-apple-darwin | FileCheck %s
1 ; RUN: llc < %s -mtriple=x86_64-linux-gnueabi | FileCheck %s
2
3 define i64 @get_stack() nounwind {
4 entry:
5 ; CHECK-LABEL: get_stack:
6 ; CHECK: movq %rsp, %rax
7 %sp = call i64 @llvm.read_register.i64(metadata !0)
8 ret i64 %sp
9 }
10
11 define void @set_stack(i64 %val) nounwind {
12 entry:
13 ; CHECK-LABEL: set_stack:
14 ; CHECK: movq %rdi, %rsp
15 call void @llvm.write_register.i64(metadata !0, i64 %val)
16 ret void
17 }
18
19 declare i64 @llvm.read_register.i64(metadata) nounwind
20 declare void @llvm.write_register.i64(metadata, i64) nounwind
21
22 ; register unsigned long current_stack_pointer asm("rsp");
23 ; CHECK-NOT: .asciz "rsp"
24 !0 = metadata !{metadata !"rsp\00"}