mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-02-20 14:29:27 +00:00
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
This commit is contained in:
parent
b889448841
commit
22f779d1fd
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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";
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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 =
|
||||
|
@ -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,
|
||||
|
13
test/CodeGen/AArch64/named-reg-alloc.ll
Normal file
13
test/CodeGen/AArch64/named-reg-alloc.ll
Normal file
@ -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"}
|
12
test/CodeGen/AArch64/named-reg-notareg.ll
Normal file
12
test/CodeGen/AArch64/named-reg-notareg.ll
Normal file
@ -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"}
|
24
test/CodeGen/AArch64/stackpointer.ll
Normal file
24
test/CodeGen/AArch64/stackpointer.ll
Normal file
@ -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"}
|
14
test/CodeGen/ARM/named-reg-alloc.ll
Normal file
14
test/CodeGen/ARM/named-reg-alloc.ll
Normal file
@ -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"}
|
13
test/CodeGen/ARM/named-reg-notareg.ll
Normal file
13
test/CodeGen/ARM/named-reg-notareg.ll
Normal file
@ -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"}
|
25
test/CodeGen/ARM/stackpointer.ll
Normal file
25
test/CodeGen/ARM/stackpointer.ll
Normal file
@ -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"}
|
14
test/CodeGen/ARM64/named-reg-alloc.ll
Normal file
14
test/CodeGen/ARM64/named-reg-alloc.ll
Normal file
@ -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"}
|
13
test/CodeGen/ARM64/named-reg-notareg.ll
Normal file
13
test/CodeGen/ARM64/named-reg-notareg.ll
Normal file
@ -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"}
|
24
test/CodeGen/ARM64/stackpointer.ll
Normal file
24
test/CodeGen/ARM64/stackpointer.ll
Normal file
@ -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"}
|
14
test/CodeGen/X86/named-reg-alloc.ll
Normal file
14
test/CodeGen/X86/named-reg-alloc.ll
Normal file
@ -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"}
|
13
test/CodeGen/X86/named-reg-notareg.ll
Normal file
13
test/CodeGen/X86/named-reg-notareg.ll
Normal file
@ -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"}
|
25
test/CodeGen/X86/stackpointer.ll
Normal file
25
test/CodeGen/X86/stackpointer.ll
Normal file
@ -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"}
|
Loading…
x
Reference in New Issue
Block a user