forked from OSchip/llvm-project
[RISCV] Add support for _interrupt attribute
- Save/restore only registers that are used. This includes Callee saved registers and Caller saved registers (arguments and temporaries) for integer and FP registers. - If there is a call in the interrupt handler, save/restore all Caller saved registers (arguments and temporaries) and all FP registers. - Emit special return instructions depending on "interrupt" attribute type. Based on initial patch by Zhaoshi Zheng. Reviewers: asb Reviewed By: asb Subscribers: rkruppe, the_o, MartinMosbeck, brucehoult, rbar, johnrusso, simoncook, sabuasal, niosHD, kito-cheng, shiva0217, zzheng, edward-jones, mgrang, rogfer01, llvm-commits Differential Revision: https://reviews.llvm.org/D48411 llvm-svn: 338047
This commit is contained in:
parent
09810c9269
commit
2e4106b73d
|
@ -18,3 +18,40 @@ def CSR : CalleeSavedRegs<(add X1, X3, X4, X8, X9, (sequence "X%u", 18, 27))>;
|
||||||
|
|
||||||
// Needed for implementation of RISCVRegisterInfo::getNoPreservedMask()
|
// Needed for implementation of RISCVRegisterInfo::getNoPreservedMask()
|
||||||
def CSR_NoRegs : CalleeSavedRegs<(add)>;
|
def CSR_NoRegs : CalleeSavedRegs<(add)>;
|
||||||
|
|
||||||
|
// Interrupt handler needs to save/restore all registers that are used,
|
||||||
|
// both Caller and Callee saved registers.
|
||||||
|
def CSR_Interrupt : CalleeSavedRegs<(add X1,
|
||||||
|
(sequence "X%u", 3, 9),
|
||||||
|
(sequence "X%u", 10, 11),
|
||||||
|
(sequence "X%u", 12, 17),
|
||||||
|
(sequence "X%u", 18, 27),
|
||||||
|
(sequence "X%u", 28, 31))>;
|
||||||
|
|
||||||
|
// Same as CSR_Interrupt, but including all 32-bit FP registers.
|
||||||
|
def CSR_XLEN_F32_Interrupt: CalleeSavedRegs<(add X1,
|
||||||
|
(sequence "X%u", 3, 9),
|
||||||
|
(sequence "X%u", 10, 11),
|
||||||
|
(sequence "X%u", 12, 17),
|
||||||
|
(sequence "X%u", 18, 27),
|
||||||
|
(sequence "X%u", 28, 31),
|
||||||
|
(sequence "F%u_32", 0, 7),
|
||||||
|
(sequence "F%u_32", 10, 11),
|
||||||
|
(sequence "F%u_32", 12, 17),
|
||||||
|
(sequence "F%u_32", 28, 31),
|
||||||
|
(sequence "F%u_32", 8, 9),
|
||||||
|
(sequence "F%u_32", 18, 27))>;
|
||||||
|
|
||||||
|
// Same as CSR_Interrupt, but including all 64-bit FP registers.
|
||||||
|
def CSR_XLEN_F64_Interrupt: CalleeSavedRegs<(add X1,
|
||||||
|
(sequence "X%u", 3, 9),
|
||||||
|
(sequence "X%u", 10, 11),
|
||||||
|
(sequence "X%u", 12, 17),
|
||||||
|
(sequence "X%u", 18, 27),
|
||||||
|
(sequence "X%u", 28, 31),
|
||||||
|
(sequence "F%u_64", 0, 7),
|
||||||
|
(sequence "F%u_64", 10, 11),
|
||||||
|
(sequence "F%u_64", 12, 17),
|
||||||
|
(sequence "F%u_64", 28, 31),
|
||||||
|
(sequence "F%u_64", 8, 9),
|
||||||
|
(sequence "F%u_64", 18, 27))>;
|
||||||
|
|
|
@ -212,6 +212,36 @@ void RISCVFrameLowering::determineCalleeSaves(MachineFunction &MF,
|
||||||
SavedRegs.set(RISCV::X1);
|
SavedRegs.set(RISCV::X1);
|
||||||
SavedRegs.set(RISCV::X8);
|
SavedRegs.set(RISCV::X8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If interrupt is enabled and there are calls in the handler,
|
||||||
|
// unconditionally save all Caller-saved registers and
|
||||||
|
// all FP registers, regardless whether they are used.
|
||||||
|
MachineFrameInfo &MFI = MF.getFrameInfo();
|
||||||
|
|
||||||
|
if (MF.getFunction().hasFnAttribute("interrupt") && MFI.hasCalls()) {
|
||||||
|
|
||||||
|
static const MCPhysReg CSRegs[] = { RISCV::X1, /* ra */
|
||||||
|
RISCV::X5, RISCV::X6, RISCV::X7, /* t0-t2 */
|
||||||
|
RISCV::X10, RISCV::X11, /* a0-a1, a2-a7 */
|
||||||
|
RISCV::X12, RISCV::X13, RISCV::X14, RISCV::X15, RISCV::X16, RISCV::X17,
|
||||||
|
RISCV::X28, RISCV::X29, RISCV::X30, RISCV::X31, 0 /* t3-t6 */
|
||||||
|
};
|
||||||
|
|
||||||
|
for (unsigned i = 0; CSRegs[i]; ++i)
|
||||||
|
SavedRegs.set(CSRegs[i]);
|
||||||
|
|
||||||
|
if (MF.getSubtarget<RISCVSubtarget>().hasStdExtD() ||
|
||||||
|
MF.getSubtarget<RISCVSubtarget>().hasStdExtF()) {
|
||||||
|
|
||||||
|
// If interrupt is enabled, this list contains all FP registers.
|
||||||
|
const MCPhysReg * Regs = MF.getRegInfo().getCalleeSavedRegs();
|
||||||
|
|
||||||
|
for (unsigned i = 0; Regs[i]; ++i)
|
||||||
|
if (RISCV::FPR32RegClass.contains(Regs[i]) ||
|
||||||
|
RISCV::FPR64RegClass.contains(Regs[i]))
|
||||||
|
SavedRegs.set(Regs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RISCVFrameLowering::processFunctionBeforeFrameFinalized(
|
void RISCVFrameLowering::processFunctionBeforeFrameFinalized(
|
||||||
|
|
|
@ -967,6 +967,21 @@ SDValue RISCVTargetLowering::LowerFormalArguments(
|
||||||
}
|
}
|
||||||
|
|
||||||
MachineFunction &MF = DAG.getMachineFunction();
|
MachineFunction &MF = DAG.getMachineFunction();
|
||||||
|
|
||||||
|
const Function &Func = MF.getFunction();
|
||||||
|
if (Func.hasFnAttribute("interrupt")) {
|
||||||
|
if (!Func.arg_empty())
|
||||||
|
report_fatal_error(
|
||||||
|
"Functions with the interrupt attribute cannot have arguments!");
|
||||||
|
|
||||||
|
StringRef Kind =
|
||||||
|
MF.getFunction().getFnAttribute("interrupt").getValueAsString();
|
||||||
|
|
||||||
|
if (!(Kind == "user" || Kind == "supervisor" || Kind == "machine"))
|
||||||
|
report_fatal_error(
|
||||||
|
"Function interrupt attribute argument not supported!");
|
||||||
|
}
|
||||||
|
|
||||||
EVT PtrVT = getPointerTy(DAG.getDataLayout());
|
EVT PtrVT = getPointerTy(DAG.getDataLayout());
|
||||||
MVT XLenVT = Subtarget.getXLenVT();
|
MVT XLenVT = Subtarget.getXLenVT();
|
||||||
unsigned XLenInBytes = Subtarget.getXLen() / 8;
|
unsigned XLenInBytes = Subtarget.getXLen() / 8;
|
||||||
|
@ -1515,6 +1530,28 @@ RISCVTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
|
||||||
RetOps.push_back(Glue);
|
RetOps.push_back(Glue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interrupt service routines use different return instructions.
|
||||||
|
const Function &Func = DAG.getMachineFunction().getFunction();
|
||||||
|
if (Func.hasFnAttribute("interrupt")) {
|
||||||
|
if (!Func.getReturnType()->isVoidTy())
|
||||||
|
report_fatal_error(
|
||||||
|
"Functions with the interrupt attribute must have void return type!");
|
||||||
|
|
||||||
|
MachineFunction &MF = DAG.getMachineFunction();
|
||||||
|
StringRef Kind =
|
||||||
|
MF.getFunction().getFnAttribute("interrupt").getValueAsString();
|
||||||
|
|
||||||
|
unsigned RetOpc;
|
||||||
|
if (Kind == "user")
|
||||||
|
RetOpc = RISCVISD::URET_FLAG;
|
||||||
|
else if (Kind == "supervisor")
|
||||||
|
RetOpc = RISCVISD::SRET_FLAG;
|
||||||
|
else
|
||||||
|
RetOpc = RISCVISD::MRET_FLAG;
|
||||||
|
|
||||||
|
return DAG.getNode(RetOpc, DL, MVT::Other, RetOps);
|
||||||
|
}
|
||||||
|
|
||||||
return DAG.getNode(RISCVISD::RET_FLAG, DL, MVT::Other, RetOps);
|
return DAG.getNode(RISCVISD::RET_FLAG, DL, MVT::Other, RetOps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1524,6 +1561,12 @@ const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
||||||
break;
|
break;
|
||||||
case RISCVISD::RET_FLAG:
|
case RISCVISD::RET_FLAG:
|
||||||
return "RISCVISD::RET_FLAG";
|
return "RISCVISD::RET_FLAG";
|
||||||
|
case RISCVISD::URET_FLAG:
|
||||||
|
return "RISCVISD::URET_FLAG";
|
||||||
|
case RISCVISD::SRET_FLAG:
|
||||||
|
return "RISCVISD::SRET_FLAG";
|
||||||
|
case RISCVISD::MRET_FLAG:
|
||||||
|
return "RISCVISD::MRET_FLAG";
|
||||||
case RISCVISD::CALL:
|
case RISCVISD::CALL:
|
||||||
return "RISCVISD::CALL";
|
return "RISCVISD::CALL";
|
||||||
case RISCVISD::SELECT_CC:
|
case RISCVISD::SELECT_CC:
|
||||||
|
|
|
@ -25,6 +25,9 @@ namespace RISCVISD {
|
||||||
enum NodeType : unsigned {
|
enum NodeType : unsigned {
|
||||||
FIRST_NUMBER = ISD::BUILTIN_OP_END,
|
FIRST_NUMBER = ISD::BUILTIN_OP_END,
|
||||||
RET_FLAG,
|
RET_FLAG,
|
||||||
|
URET_FLAG,
|
||||||
|
SRET_FLAG,
|
||||||
|
MRET_FLAG,
|
||||||
CALL,
|
CALL,
|
||||||
SELECT_CC,
|
SELECT_CC,
|
||||||
BuildPairF64,
|
BuildPairF64,
|
||||||
|
|
|
@ -118,7 +118,8 @@ void RISCVInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
|
||||||
unsigned Opcode;
|
unsigned Opcode;
|
||||||
|
|
||||||
if (RISCV::GPRRegClass.hasSubClassEq(RC))
|
if (RISCV::GPRRegClass.hasSubClassEq(RC))
|
||||||
Opcode = RISCV::SW;
|
Opcode = TRI->getRegSizeInBits(RISCV::GPRRegClass) == 32 ?
|
||||||
|
RISCV::SW : RISCV::SD;
|
||||||
else if (RISCV::FPR32RegClass.hasSubClassEq(RC))
|
else if (RISCV::FPR32RegClass.hasSubClassEq(RC))
|
||||||
Opcode = RISCV::FSW;
|
Opcode = RISCV::FSW;
|
||||||
else if (RISCV::FPR64RegClass.hasSubClassEq(RC))
|
else if (RISCV::FPR64RegClass.hasSubClassEq(RC))
|
||||||
|
@ -144,7 +145,8 @@ void RISCVInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
|
||||||
unsigned Opcode;
|
unsigned Opcode;
|
||||||
|
|
||||||
if (RISCV::GPRRegClass.hasSubClassEq(RC))
|
if (RISCV::GPRRegClass.hasSubClassEq(RC))
|
||||||
Opcode = RISCV::LW;
|
Opcode = TRI->getRegSizeInBits(RISCV::GPRRegClass) == 32 ?
|
||||||
|
RISCV::LW : RISCV::LD;
|
||||||
else if (RISCV::FPR32RegClass.hasSubClassEq(RC))
|
else if (RISCV::FPR32RegClass.hasSubClassEq(RC))
|
||||||
Opcode = RISCV::FLW;
|
Opcode = RISCV::FLW;
|
||||||
else if (RISCV::FPR64RegClass.hasSubClassEq(RC))
|
else if (RISCV::FPR64RegClass.hasSubClassEq(RC))
|
||||||
|
|
|
@ -36,6 +36,12 @@ def CallSeqEnd : SDNode<"ISD::CALLSEQ_END", SDT_RISCVCallSeqEnd,
|
||||||
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>;
|
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>;
|
||||||
def RetFlag : SDNode<"RISCVISD::RET_FLAG", SDTNone,
|
def RetFlag : SDNode<"RISCVISD::RET_FLAG", SDTNone,
|
||||||
[SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
|
[SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
|
||||||
|
def URetFlag : SDNode<"RISCVISD::URET_FLAG", SDTNone,
|
||||||
|
[SDNPHasChain, SDNPOptInGlue]>;
|
||||||
|
def SRetFlag : SDNode<"RISCVISD::SRET_FLAG", SDTNone,
|
||||||
|
[SDNPHasChain, SDNPOptInGlue]>;
|
||||||
|
def MRetFlag : SDNode<"RISCVISD::MRET_FLAG", SDTNone,
|
||||||
|
[SDNPHasChain, SDNPOptInGlue]>;
|
||||||
def SelectCC : SDNode<"RISCVISD::SELECT_CC", SDT_RISCVSelectCC,
|
def SelectCC : SDNode<"RISCVISD::SELECT_CC", SDT_RISCVSelectCC,
|
||||||
[SDNPInGlue]>;
|
[SDNPInGlue]>;
|
||||||
def Tail : SDNode<"RISCVISD::TAIL", SDT_RISCVCall,
|
def Tail : SDNode<"RISCVISD::TAIL", SDT_RISCVCall,
|
||||||
|
@ -684,6 +690,10 @@ def PseudoCALL : Pseudo<(outs), (ins bare_symbol:$func),
|
||||||
|
|
||||||
def : Pat<(Call texternalsym:$func), (PseudoCALL texternalsym:$func)>;
|
def : Pat<(Call texternalsym:$func), (PseudoCALL texternalsym:$func)>;
|
||||||
|
|
||||||
|
def : Pat<(URetFlag), (URET X0, X0)>;
|
||||||
|
def : Pat<(SRetFlag), (SRET X0, X0)>;
|
||||||
|
def : Pat<(MRetFlag), (MRET X0, X0)>;
|
||||||
|
|
||||||
let isCall = 1, Defs = [X1] in
|
let isCall = 1, Defs = [X1] in
|
||||||
def PseudoCALLIndirect : Pseudo<(outs), (ins GPR:$rs1), [(Call GPR:$rs1)]>,
|
def PseudoCALLIndirect : Pseudo<(outs), (ins GPR:$rs1), [(Call GPR:$rs1)]>,
|
||||||
PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>;
|
PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>;
|
||||||
|
|
|
@ -33,6 +33,13 @@ RISCVRegisterInfo::RISCVRegisterInfo(unsigned HwMode)
|
||||||
|
|
||||||
const MCPhysReg *
|
const MCPhysReg *
|
||||||
RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
|
RISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
|
||||||
|
if (MF->getFunction().hasFnAttribute("interrupt")) {
|
||||||
|
if (MF->getSubtarget<RISCVSubtarget>().hasStdExtD())
|
||||||
|
return CSR_XLEN_F64_Interrupt_SaveList;
|
||||||
|
if (MF->getSubtarget<RISCVSubtarget>().hasStdExtF())
|
||||||
|
return CSR_XLEN_F32_Interrupt_SaveList;
|
||||||
|
return CSR_Interrupt_SaveList;
|
||||||
|
}
|
||||||
return CSR_SaveList;
|
return CSR_SaveList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +115,14 @@ unsigned RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t *
|
const uint32_t *
|
||||||
RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & /*MF*/,
|
RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & MF,
|
||||||
CallingConv::ID /*CC*/) const {
|
CallingConv::ID /*CC*/) const {
|
||||||
|
if (MF.getFunction().hasFnAttribute("interrupt")) {
|
||||||
|
if (MF.getSubtarget<RISCVSubtarget>().hasStdExtD())
|
||||||
|
return CSR_XLEN_F64_Interrupt_RegMask;
|
||||||
|
if (MF.getSubtarget<RISCVSubtarget>().hasStdExtF())
|
||||||
|
return CSR_XLEN_F32_Interrupt_RegMask;
|
||||||
|
return CSR_Interrupt_RegMask;
|
||||||
|
}
|
||||||
return CSR_RegMask;
|
return CSR_RegMask;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
; RUN: not llc -mtriple riscv32-unknown-elf -o - %s \
|
||||||
|
; RUN: 2>&1 | FileCheck %s
|
||||||
|
; RUN: not llc -mtriple riscv64-unknown-elf -o - %s \
|
||||||
|
; RUN: 2>&1 | FileCheck %s
|
||||||
|
|
||||||
|
; CHECK: LLVM ERROR: Functions with the interrupt attribute cannot have arguments!
|
||||||
|
define i32 @isr_user(i8 %n) #0 {
|
||||||
|
ret i32 0
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { "interrupt"="user" }
|
|
@ -0,0 +1,11 @@
|
||||||
|
; RUN: not llc -mtriple riscv32-unknown-elf -o - %s \
|
||||||
|
; RUN: 2>&1 | FileCheck %s
|
||||||
|
; RUN: not llc -mtriple riscv64-unknown-elf -o - %s \
|
||||||
|
; RUN: 2>&1 | FileCheck %s
|
||||||
|
|
||||||
|
; CHECK: LLVM ERROR: Function interrupt attribute argument not supported!
|
||||||
|
define void @isr_user() #0 {
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { "interrupt"="foo" }
|
|
@ -0,0 +1,217 @@
|
||||||
|
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
||||||
|
; RUN: llc -mtriple riscv32-unknown-elf -o - %s \
|
||||||
|
; RUN: 2>&1 | FileCheck %s -check-prefix CHECK-RV32
|
||||||
|
; RUN: llc -mtriple riscv32-unknown-elf -mattr=+f -o - %s \
|
||||||
|
; RUN: 2>&1 | FileCheck %s -check-prefix CHECK-RV32-F
|
||||||
|
; RUN: llc -mtriple riscv32-unknown-elf -mattr=+f,+d -o - %s \
|
||||||
|
; RUN: 2>&1 | FileCheck %s -check-prefix CHECK-RV32-FD
|
||||||
|
;
|
||||||
|
; TODO: Add RV64 tests when we can lower global addresses.
|
||||||
|
|
||||||
|
; Checking all registers that are used are being saved.
|
||||||
|
; This includes Caller (arguments and temps) and
|
||||||
|
; Callee saved registers.
|
||||||
|
;
|
||||||
|
; extern int a, b, c;
|
||||||
|
; __attribute__((interrupt)) void foo_no_call(void) {
|
||||||
|
; c = a + b;
|
||||||
|
; }
|
||||||
|
;
|
||||||
|
|
||||||
|
@a = external global i32
|
||||||
|
@b = external global i32
|
||||||
|
@c = external global i32
|
||||||
|
|
||||||
|
define void @foo_i32() #0 {
|
||||||
|
; CHECK-RV32-LABEL: foo_i32:
|
||||||
|
; CHECK-RV32: # %bb.0:
|
||||||
|
; CHECK-RV32-NEXT: addi sp, sp, -16
|
||||||
|
; CHECK-RV32-NEXT: sw a0, 12(sp)
|
||||||
|
; CHECK-RV32-NEXT: sw a1, 8(sp)
|
||||||
|
; CHECK-RV32-NEXT: lui a0, %hi(a)
|
||||||
|
; CHECK-RV32-NEXT: lw a0, %lo(a)(a0)
|
||||||
|
; CHECK-RV32-NEXT: lui a1, %hi(b)
|
||||||
|
; CHECK-RV32-NEXT: lw a1, %lo(b)(a1)
|
||||||
|
; CHECK-RV32-NEXT: add a0, a1, a0
|
||||||
|
; CHECK-RV32-NEXT: lui a1, %hi(c)
|
||||||
|
; CHECK-RV32-NEXT: sw a0, %lo(c)(a1)
|
||||||
|
; CHECK-RV32-NEXT: lw a1, 8(sp)
|
||||||
|
; CHECK-RV32-NEXT: lw a0, 12(sp)
|
||||||
|
; CHECK-RV32-NEXT: addi sp, sp, 16
|
||||||
|
; CHECK-RV32-NEXT: mret
|
||||||
|
;
|
||||||
|
%1 = load i32, i32* @a
|
||||||
|
%2 = load i32, i32* @b
|
||||||
|
%add = add nsw i32 %2, %1
|
||||||
|
store i32 %add, i32* @c
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
; Additionally check frame pointer and return address are properly saved.
|
||||||
|
;
|
||||||
|
|
||||||
|
define void @foo_fp_i32() #1 {
|
||||||
|
; CHECK-RV32-LABEL: foo_fp_i32:
|
||||||
|
; CHECK-RV32: # %bb.0:
|
||||||
|
; CHECK-RV32-NEXT: addi sp, sp, -16
|
||||||
|
; CHECK-RV32-NEXT: sw ra, 12(sp)
|
||||||
|
; CHECK-RV32-NEXT: sw s0, 8(sp)
|
||||||
|
; CHECK-RV32-NEXT: sw a0, 4(sp)
|
||||||
|
; CHECK-RV32-NEXT: sw a1, 0(sp)
|
||||||
|
; CHECK-RV32-NEXT: addi s0, sp, 16
|
||||||
|
; CHECK-RV32-NEXT: lui a0, %hi(a)
|
||||||
|
; CHECK-RV32-NEXT: lw a0, %lo(a)(a0)
|
||||||
|
; CHECK-RV32-NEXT: lui a1, %hi(b)
|
||||||
|
; CHECK-RV32-NEXT: lw a1, %lo(b)(a1)
|
||||||
|
; CHECK-RV32-NEXT: add a0, a1, a0
|
||||||
|
; CHECK-RV32-NEXT: lui a1, %hi(c)
|
||||||
|
; CHECK-RV32-NEXT: sw a0, %lo(c)(a1)
|
||||||
|
; CHECK-RV32-NEXT: lw a1, 0(sp)
|
||||||
|
; CHECK-RV32-NEXT: lw a0, 4(sp)
|
||||||
|
; CHECK-RV32-NEXT: lw s0, 8(sp)
|
||||||
|
; CHECK-RV32-NEXT: lw ra, 12(sp)
|
||||||
|
; CHECK-RV32-NEXT: addi sp, sp, 16
|
||||||
|
; CHECK-RV32-NEXT: mret
|
||||||
|
;
|
||||||
|
%1 = load i32, i32* @a
|
||||||
|
%2 = load i32, i32* @b
|
||||||
|
%add = add nsw i32 %2, %1
|
||||||
|
store i32 %add, i32* @c
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
@e = external global float
|
||||||
|
@f = external global float
|
||||||
|
@d = external global float
|
||||||
|
|
||||||
|
define void @foo_float() #0 {
|
||||||
|
; CHECK-RV32-F-LABEL: foo_float:
|
||||||
|
; CHECK-RV32-F: # %bb.0:
|
||||||
|
; CHECK-RV32-F-NEXT: addi sp, sp, -16
|
||||||
|
; CHECK-RV32-F-NEXT: sw a0, 12(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: fsw ft0, 8(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: fsw ft1, 4(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: lui a0, %hi(f)
|
||||||
|
; CHECK-RV32-F-NEXT: flw ft0, %lo(f)(a0)
|
||||||
|
; CHECK-RV32-F-NEXT: lui a0, %hi(e)
|
||||||
|
; CHECK-RV32-F-NEXT: flw ft1, %lo(e)(a0)
|
||||||
|
; CHECK-RV32-F-NEXT: fadd.s ft0, ft1, ft0
|
||||||
|
; CHECK-RV32-F-NEXT: lui a0, %hi(d)
|
||||||
|
; CHECK-RV32-F-NEXT: fsw ft0, %lo(d)(a0)
|
||||||
|
; CHECK-RV32-F-NEXT: flw ft1, 4(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: flw ft0, 8(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: lw a0, 12(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: addi sp, sp, 16
|
||||||
|
; CHECK-RV32-F-NEXT: mret
|
||||||
|
;
|
||||||
|
%1 = load float, float* @e
|
||||||
|
%2 = load float, float* @f
|
||||||
|
%add = fadd float %1, %2
|
||||||
|
store float %add, float* @d
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
; Additionally check frame pointer and return address are properly saved.
|
||||||
|
;
|
||||||
|
define void @foo_fp_float() #1 {
|
||||||
|
; CHECK-RV32-F-LABEL: foo_fp_float:
|
||||||
|
; CHECK-RV32-F: # %bb.0:
|
||||||
|
; CHECK-RV32-F-NEXT: addi sp, sp, -32
|
||||||
|
; CHECK-RV32-F-NEXT: sw ra, 28(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: sw s0, 24(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: sw a0, 20(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: fsw ft0, 16(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: fsw ft1, 12(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: addi s0, sp, 32
|
||||||
|
; CHECK-RV32-F-NEXT: lui a0, %hi(f)
|
||||||
|
; CHECK-RV32-F-NEXT: flw ft0, %lo(f)(a0)
|
||||||
|
; CHECK-RV32-F-NEXT: lui a0, %hi(e)
|
||||||
|
; CHECK-RV32-F-NEXT: flw ft1, %lo(e)(a0)
|
||||||
|
; CHECK-RV32-F-NEXT: fadd.s ft0, ft1, ft0
|
||||||
|
; CHECK-RV32-F-NEXT: lui a0, %hi(d)
|
||||||
|
; CHECK-RV32-F-NEXT: fsw ft0, %lo(d)(a0)
|
||||||
|
; CHECK-RV32-F-NEXT: flw ft1, 12(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: flw ft0, 16(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: lw a0, 20(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: lw s0, 24(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: lw ra, 28(sp)
|
||||||
|
; CHECK-RV32-F-NEXT: addi sp, sp, 32
|
||||||
|
; CHECK-RV32-F-NEXT: mret
|
||||||
|
;
|
||||||
|
%1 = load float, float* @e
|
||||||
|
%2 = load float, float* @f
|
||||||
|
%add = fadd float %1, %2
|
||||||
|
store float %add, float* @d
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
@h = external global double
|
||||||
|
@i = external global double
|
||||||
|
@g = external global double
|
||||||
|
|
||||||
|
define void @foo_double() #0 {
|
||||||
|
; CHECK-RV32-FD-LABEL: foo_double:
|
||||||
|
; CHECK-RV32-FD: # %bb.0:
|
||||||
|
; CHECK-RV32-FD-NEXT: addi sp, sp, -32
|
||||||
|
; CHECK-RV32-FD-NEXT: sw a0, 28(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: fsd ft0, 16(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: fsd ft1, 8(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: lui a0, %hi(i)
|
||||||
|
; CHECK-RV32-FD-NEXT: fld ft0, %lo(i)(a0)
|
||||||
|
; CHECK-RV32-FD-NEXT: lui a0, %hi(h)
|
||||||
|
; CHECK-RV32-FD-NEXT: fld ft1, %lo(h)(a0)
|
||||||
|
; CHECK-RV32-FD-NEXT: fadd.d ft0, ft1, ft0
|
||||||
|
; CHECK-RV32-FD-NEXT: lui a0, %hi(g)
|
||||||
|
; CHECK-RV32-FD-NEXT: fsd ft0, %lo(g)(a0)
|
||||||
|
; CHECK-RV32-FD-NEXT: fld ft1, 8(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: fld ft0, 16(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: lw a0, 28(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: addi sp, sp, 32
|
||||||
|
; CHECK-RV32-FD-NEXT: mret
|
||||||
|
;
|
||||||
|
%1 = load double, double* @h
|
||||||
|
%2 = load double, double* @i
|
||||||
|
%add = fadd double %1, %2
|
||||||
|
store double %add, double* @g
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
; Additionally check frame pointer and return address are properly saved.
|
||||||
|
;
|
||||||
|
define void @foo_fp_double() #1 {
|
||||||
|
; CHECK-RV32-FD-LABEL: foo_fp_double:
|
||||||
|
; CHECK-RV32-FD: # %bb.0:
|
||||||
|
; CHECK-RV32-FD-NEXT: addi sp, sp, -32
|
||||||
|
; CHECK-RV32-FD-NEXT: sw ra, 28(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: sw s0, 24(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: sw a0, 20(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: fsd ft0, 8(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: fsd ft1, 0(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: addi s0, sp, 32
|
||||||
|
; CHECK-RV32-FD-NEXT: lui a0, %hi(i)
|
||||||
|
; CHECK-RV32-FD-NEXT: fld ft0, %lo(i)(a0)
|
||||||
|
; CHECK-RV32-FD-NEXT: lui a0, %hi(h)
|
||||||
|
; CHECK-RV32-FD-NEXT: fld ft1, %lo(h)(a0)
|
||||||
|
; CHECK-RV32-FD-NEXT: fadd.d ft0, ft1, ft0
|
||||||
|
; CHECK-RV32-FD-NEXT: lui a0, %hi(g)
|
||||||
|
; CHECK-RV32-FD-NEXT: fsd ft0, %lo(g)(a0)
|
||||||
|
; CHECK-RV32-FD-NEXT: fld ft1, 0(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: fld ft0, 8(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: lw a0, 20(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: lw s0, 24(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: lw ra, 28(sp)
|
||||||
|
; CHECK-RV32-FD-NEXT: addi sp, sp, 32
|
||||||
|
; CHECK-RV32-FD-NEXT: mret
|
||||||
|
;
|
||||||
|
%1 = load double, double* @h
|
||||||
|
%2 = load double, double* @i
|
||||||
|
%add = fadd double %1, %2
|
||||||
|
store double %add, double* @g
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes #0 = { "interrupt"="machine" }
|
||||||
|
attributes #1 = { "interrupt"="machine" "no-frame-pointer-elim"="true" }
|
|
@ -0,0 +1,12 @@
|
||||||
|
; RUN: not llc -mtriple riscv32-unknown-elf -o - %s \
|
||||||
|
; RUN: 2>&1 | FileCheck %s
|
||||||
|
; RUN: not llc -mtriple riscv64-unknown-elf -o - %s \
|
||||||
|
; RUN: 2>&1 | FileCheck %s
|
||||||
|
|
||||||
|
; CHECK: LLVM ERROR: Functions with the interrupt attribute must have void return type!
|
||||||
|
define i32 @isr1_user() #0 {
|
||||||
|
ret i32 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
attributes #0 = { "interrupt"="user" }
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue