llvm.org GIT mirror llvm / bba9390
ARM: support interrupt attribute This function-attribute modifies the callee-saved register list and function epilogue (specifically the return instruction) so that a routine is suitable for use as an interrupt-handler of the specified type without disrupting user-mode applications. rdar://problem/14207019 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@191766 91177308-0d34-0410-b5e6-96231b3b80d8 Tim Northover 6 years ago
9 changed file(s) with 278 addition(s) and 21 deletion(s). Raw diff Collapse all Expand all
5050
5151 const uint16_t*
5252 ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
53 bool ghcCall = false;
54
55 if (MF) {
56 const Function *F = MF->getFunction();
57 ghcCall = (F ? F->getCallingConv() == CallingConv::GHC : false);
58 }
59
60 if (ghcCall)
53 const uint16_t *RegList = (STI.isTargetIOS() && !STI.isAAPCS_ABI())
54 ? CSR_iOS_SaveList
55 : CSR_AAPCS_SaveList;
56
57 if (!MF) return RegList;
58
59 const Function *F = MF->getFunction();
60 if (F->getCallingConv() == CallingConv::GHC) {
6161 // GHC set of callee saved regs is empty as all those regs are
6262 // used for passing STG regs around
6363 return CSR_NoRegs_SaveList;
64 else
65 return (STI.isTargetIOS() && !STI.isAAPCS_ABI())
66 ? CSR_iOS_SaveList : CSR_AAPCS_SaveList;
64 } else if (F->hasFnAttribute("interrupt")) {
65 if (STI.isMClass()) {
66 // M-class CPUs have hardware which saves the registers needed to allow a
67 // function conforming to the AAPCS to function as a handler.
68 return CSR_AAPCS_SaveList;
69 } else if (F->getFnAttribute("interrupt").getValueAsString() == "FIQ") {
70 // Fast interrupt mode gives the handler a private copy of R8-R14, so less
71 // need to be saved to restore user-mode state.
72 return CSR_FIQ_SaveList;
73 } else {
74 // Generally only R13-R14 (i.e. SP, LR) are automatically preserved by
75 // exception handling.
76 return CSR_GenericInt_SaveList;
77 }
78 }
79
80 return RegList;
6781 }
6882
6983 const uint32_t*
206206 def CSR_iOS : CalleeSavedRegs<(add LR, R7, R6, R5, R4, (sub CSR_AAPCS, R9))>;
207207
208208 def CSR_iOS_ThisReturn : CalleeSavedRegs<(add LR, R7, R6, R5, R4,
209 (sub CSR_AAPCS_ThisReturn, R9))>;
209 (sub CSR_AAPCS_ThisReturn, R9))>;
210
211 // The "interrupt" attribute is used to generate code that is acceptable in
212 // exception-handlers of various kinds. It makes us use a different return
213 // instruction (handled elsewhere) and affects which registers we must return to
214 // our "caller" in the same state as we receive them.
215
216 // For most interrupts, all registers except SP and LR are shared with
217 // user-space. We mark LR to be saved anyway, since this is what the ARM backend
218 // generally does rather than tracking its liveness as a normal register.
219 def CSR_GenericInt : CalleeSavedRegs<(add LR, (sequence "R%u", 12, 0))>;
220
221 // The fast interrupt handlers have more private state and get their own copies
222 // of R8-R12, in addition to SP and LR. As before, mark LR for saving too.
223
224 // FIXME: we mark R11 as callee-saved since it's often the frame-pointer, and
225 // current frame lowering expects to encounter it while processing callee-saved
226 // registers.
227 def CSR_FIQ : CalleeSavedRegs<(add LR, R11, (sequence "R%u", 7, 0))>;
228
229
961961 ExpandMOV32BitImm(MBB, MBBI);
962962 return true;
963963
964 case ARM::SUBS_PC_LR: {
965 MachineInstrBuilder MIB =
966 BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(ARM::SUBri), ARM::PC)
967 .addReg(ARM::LR)
968 .addOperand(MI.getOperand(0))
969 .addOperand(MI.getOperand(1))
970 .addOperand(MI.getOperand(2))
971 .addReg(ARM::CPSR, RegState::Undef);
972 TransferImpOps(MI, MIB, MIB);
973 MI.eraseFromParent();
974 return true;
975 }
964976 case ARM::VLDMQIA: {
965977 unsigned NewOpc = ARM::VLDMDIA;
966978 MachineInstrBuilder MIB =
174174 unsigned Reg = CSI[i].getReg();
175175 int FI = CSI[i].getFrameIdx();
176176 switch (Reg) {
177 case ARM::R0:
178 case ARM::R1:
179 case ARM::R2:
180 case ARM::R3:
177181 case ARM::R4:
178182 case ARM::R5:
179183 case ARM::R6:
188192 case ARM::R9:
189193 case ARM::R10:
190194 case ARM::R11:
195 case ARM::R12:
191196 if (Reg == FramePtr)
192197 FramePtrSpillFI = FI;
193198 if (STI.isTargetIOS()) {
372377 emitSPUpdate(isARM, MBB, MBBI, dl, TII, NumBytes);
373378 } else {
374379 // Unwind MBBI to point to first LDR / VLDRD.
375 const uint16_t *CSRegs = RegInfo->getCalleeSavedRegs();
380 const uint16_t *CSRegs = RegInfo->getCalleeSavedRegs(&MF);
376381 if (MBBI != MBB.begin()) {
377382 do
378383 --MBBI;
657662 unsigned RetOpcode = MI->getOpcode();
658663 bool isTailCall = (RetOpcode == ARM::TCRETURNdi ||
659664 RetOpcode == ARM::TCRETURNri);
665 bool isInterrupt =
666 RetOpcode == ARM::SUBS_PC_LR || RetOpcode == ARM::t2SUBS_PC_LR;
660667
661668 SmallVector Regs;
662669 unsigned i = CSI.size();
671678 if (Reg >= ARM::D8 && Reg < ARM::D8 + NumAlignedDPRCS2Regs)
672679 continue;
673680
674 if (Reg == ARM::LR && !isTailCall && !isVarArg && STI.hasV5TOps()) {
681 if (Reg == ARM::LR && !isTailCall && !isVarArg && !isInterrupt &&
682 STI.hasV5TOps()) {
675683 Reg = ARM::PC;
676684 LdmOpc = AFI->isThumbFunction() ? ARM::t2LDMIA_RET : ARM::LDMIA_RET;
677685 // Fold the return instruction into the LDM.
11981206
11991207 // Don't spill FP if the frame can be eliminated. This is determined
12001208 // by scanning the callee-save registers to see if any is used.
1201 const uint16_t *CSRegs = RegInfo->getCalleeSavedRegs();
1209 const uint16_t *CSRegs = RegInfo->getCalleeSavedRegs(&MF);
12021210 for (unsigned i = 0; CSRegs[i]; ++i) {
12031211 unsigned Reg = CSRegs[i];
12041212 bool Spilled = false;
12251233 case ARM::LR:
12261234 LRSpilled = true;
12271235 // Fallthrough
1236 case ARM::R0: case ARM::R1:
1237 case ARM::R2: case ARM::R3:
12281238 case ARM::R4: case ARM::R5:
12291239 case ARM::R6: case ARM::R7:
12301240 CS1Spilled = true;
12391249 }
12401250
12411251 switch (Reg) {
1252 case ARM::R0: case ARM::R1:
1253 case ARM::R2: case ARM::R3:
12421254 case ARM::R4: case ARM::R5:
12431255 case ARM::R6: case ARM::R7:
12441256 case ARM::LR:
12941306 if (!LRSpilled && CS1Spilled) {
12951307 MRI.setPhysRegUsed(ARM::LR);
12961308 NumGPRSpills++;
1297 UnspilledCS1GPRs.erase(std::find(UnspilledCS1GPRs.begin(),
1298 UnspilledCS1GPRs.end(), (unsigned)ARM::LR));
1309 SmallVectorImpl::iterator LRPos;
1310 LRPos = std::find(UnspilledCS1GPRs.begin(), UnspilledCS1GPRs.end(),
1311 (unsigned)ARM::LR);
1312 if (LRPos != UnspilledCS1GPRs.end())
1313 UnspilledCS1GPRs.erase(LRPos);
1314
12991315 ForceLRSpill = false;
13001316 ExtraCSSpill = true;
13011317 }
10141014 case ARMISD::BR_JT: return "ARMISD::BR_JT";
10151015 case ARMISD::BR2_JT: return "ARMISD::BR2_JT";
10161016 case ARMISD::RET_FLAG: return "ARMISD::RET_FLAG";
1017 case ARMISD::INTRET_FLAG: return "ARMISD::INTRET_FLAG";
10171018 case ARMISD::PIC_ADD: return "ARMISD::PIC_ADD";
10181019 case ARMISD::CMP: return "ARMISD::CMP";
10191020 case ARMISD::CMN: return "ARMISD::CMN";
19651966 if (isVarArg && !Outs.empty())
19661967 return false;
19671968
1969 // Exception-handling functions need a special set of instructions to indicate
1970 // a return to the hardware. Tail-calling another function would probably
1971 // break this.
1972 if (CallerF->hasFnAttribute("interrupt"))
1973 return false;
1974
19681975 // Also avoid sibcall optimization if either caller or callee uses struct
19691976 // return semantics.
19701977 if (isCalleeStructRet || isCallerStructRet)
20932100 isVarArg));
20942101 }
20952102
2103 static SDValue LowerInterruptReturn(SmallVectorImpl &RetOps,
2104 SDLoc DL, SelectionDAG &DAG) {
2105 const MachineFunction &MF = DAG.getMachineFunction();
2106 const Function *F = MF.getFunction();
2107
2108 StringRef IntKind = F->getFnAttribute("interrupt").getValueAsString();
2109
2110 // See ARM ARM v7 B1.8.3. On exception entry LR is set to a possibly offset
2111 // version of the "preferred return address". These offsets affect the return
2112 // instruction if this is a return from PL1 without hypervisor extensions.
2113 // IRQ/FIQ: +4 "subs pc, lr, #4"
2114 // SWI: 0 "subs pc, lr, #0"
2115 // ABORT: +4 "subs pc, lr, #4"
2116 // UNDEF: +4/+2 "subs pc, lr, #0"
2117 // UNDEF varies depending on where the exception came from ARM or Thumb
2118 // mode. Alongside GCC, we throw our hands up in disgust and pretend it's 0.
2119
2120 int64_t LROffset;
2121 if (IntKind == "" || IntKind == "IRQ" || IntKind == "FIQ" ||
2122 IntKind == "ABORT")
2123 LROffset = 4;
2124 else if (IntKind == "SWI" || IntKind == "UNDEF")
2125 LROffset = 0;
2126 else
2127 report_fatal_error("Unsupported interrupt attribute. If present, value "
2128 "must be one of: IRQ, FIQ, SWI, ABORT or UNDEF");
2129
2130 RetOps.insert(RetOps.begin() + 1, DAG.getConstant(LROffset, MVT::i32, false));
2131
2132 return DAG.getNode(ARMISD::INTRET_FLAG, DL, MVT::Other,
2133 RetOps.data(), RetOps.size());
2134 }
2135
20962136 SDValue
20972137 ARMTargetLowering::LowerReturn(SDValue Chain,
20982138 CallingConv::ID CallConv, bool isVarArg,
21772217 RetOps[0] = Chain;
21782218 if (Flag.getNode())
21792219 RetOps.push_back(Flag);
2220
2221 // CPUs which aren't M-class use a special sequence to return from
2222 // exceptions (roughly, any instruction setting pc and cpsr simultaneously,
2223 // though we use "subs pc, lr, #N").
2224 //
2225 // M-class CPUs actually use a normal return sequence with a special
2226 // (hardware-provided) value in LR, so the normal code path works.
2227 if (DAG.getMachineFunction().getFunction()->hasFnAttribute("interrupt") &&
2228 !Subtarget->isMClass()) {
2229 if (Subtarget->isThumb1Only())
2230 report_fatal_error("interrupt attribute is not supported in Thumb1");
2231 return LowerInterruptReturn(RetOps, dl, DAG);
2232 }
21802233
21812234 return DAG.getNode(ARMISD::RET_FLAG, dl, MVT::Other,
21822235 RetOps.data(), RetOps.size());
22342287 bool HasRet = false;
22352288 for (SDNode::use_iterator UI = Copy->use_begin(), UE = Copy->use_end();
22362289 UI != UE; ++UI) {
2237 if (UI->getOpcode() != ARMISD::RET_FLAG)
2290 if (UI->getOpcode() != ARMISD::RET_FLAG &&
2291 UI->getOpcode() != ARMISD::INTRET_FLAG)
22382292 return false;
22392293 HasRet = true;
22402294 }
5151 BR_JT, // Jumptable branch.
5252 BR2_JT, // Jumptable branch (2 level - jumptable entry is a jump).
5353 RET_FLAG, // Return with a flag operand.
54 INTRET_FLAG, // Interrupt return with an LR-offset and a flag operand.
5455
5556 PIC_ADD, // Add with a PC operand and a PIC label.
5657
120120
121121 def ARMretflag : SDNode<"ARMISD::RET_FLAG", SDTNone,
122122 [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
123
123 def ARMintretflag : SDNode<"ARMISD::INTRET_FLAG", SDT_ARMcall,
124 [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
124125 def ARMcmov : SDNode<"ARMISD::CMOV", SDT_ARMCMov,
125126 [SDNPInGlue]>;
126127
19241925 Requires<[IsARM, NoV4T]>, Sched<[WriteBr]> {
19251926 let Inst{27-0} = 0b0001101000001111000000001110;
19261927 }
1928
1929 // Exception return: N.b. doesn't set CPSR as far as we're concerned (it sets
1930 // the user-space one).
1931 def SUBS_PC_LR : ARMPseudoInst<(outs), (ins i32imm:$offset, pred:$p),
1932 4, IIC_Br,
1933 [(ARMintretflag imm:$offset)]>;
19271934 }
19281935
19291936 // Indirect branches
37573757 [/* For disassembly only; pattern left blank */]>;
37583758
37593759 // B9.3.19 SUBS PC, LR, #imm (Thumb2) system instruction.
3760 let Defs = [PC], Uses = [LR] in
3760 // Exception return instruction is "subs pc, lr, #imm".
3761 let isReturn = 1, isBarrier = 1, isTerminator = 1, Defs = [PC] in
37613762 def t2SUBS_PC_LR : T2I <(outs), (ins imm0_255:$imm), NoItinerary,
3762 "subs", "\tpc, lr, $imm", []>, Requires<[IsThumb2]> {
3763 "subs", "\tpc, lr, $imm",
3764 [(ARMintretflag imm0_255:$imm)]>,
3765 Requires<[IsThumb2]> {
37633766 let Inst{31-8} = 0b111100111101111010001111;
37643767
37653768 bits<8> imm;
0 ; RUN: llc -mtriple=arm-none-none-eabi -mcpu=cortex-a15 -o - %s | FileCheck --check-prefix=CHECK-A %s
1 ; RUN: llc -mtriple=thumb-none-none-eabi -mcpu=cortex-a15 -o - %s | FileCheck --check-prefix=CHECK-A-THUMB %s
2 ; RUN: llc -mtriple=thumb-apple-darwin -mcpu=cortex-m3 -o - %s | FileCheck --check-prefix=CHECK-M %s
3
4 declare arm_aapcscc void @bar()
5
6 @bigvar = global [16 x i32] zeroinitializer
7
8 define arm_aapcscc void @irq_fn() alignstack(8) "interrupt"="IRQ" {
9 ; Must save all registers except banked sp and lr (we save lr anyway because
10 ; we actually need it at the end to execute the return ourselves).
11
12 ; Also need special function return setting pc and CPSR simultaneously.
13 ; CHECK-A-LABEL: irq_fn:
14 ; CHECK-A: push {r0, r1, r2, r3, r11, lr}
15 ; CHECK-A: add r11, sp, #16
16 ; CHECK-A: sub sp, sp, #{{[0-9]+}}
17 ; CHECK-A: bic sp, sp, #7
18 ; CHECK-A: bl bar
19 ; CHECK-A: sub sp, r11, #16
20 ; CHECK-A: pop {r0, r1, r2, r3, r11, lr}
21 ; CHECK-A: subs pc, lr, #4
22
23 ; CHECK-A-THUMB-LABEL: irq_fn:
24 ; CHECK-A-THUMB: push {r0, r1, r2, r3, r4, r7, lr}
25 ; CHECK-A-THUMB: mov r4, sp
26 ; CHECK-A-THUMB: add r7, sp, #20
27 ; CHECK-A-THUMB: bic r4, r4, #7
28 ; CHECK-A-THUMB: bl bar
29 ; CHECK-A-THUMB: sub.w r4, r7, #20
30 ; CHECK-A-THUMB: mov sp, r4
31 ; CHECK-A-THUMB: pop.w {r0, r1, r2, r3, r4, r7, lr}
32 ; CHECK-A-THUMB: subs pc, lr, #4
33
34 ; Normal AAPCS function (r0-r3 pushed onto stack by hardware, lr set to
35 ; appropriate sentinel so no special return needed).
36 ; CHECK-M: push {r4, r7, lr}
37 ; CHECK-M: add r7, sp, #4
38 ; CHECK-M: sub sp, #4
39 ; CHECK-M: mov r4, sp
40 ; CHECK-M: mov sp, r4
41 ; CHECK-M: blx _bar
42 ; CHECK-M: subs r4, r7, #4
43 ; CHECK-M: mov sp, r4
44 ; CHECK-M: pop {r4, r7, pc}
45
46 call arm_aapcscc void @bar()
47 ret void
48 }
49
50 define arm_aapcscc void @fiq_fn() alignstack(8) "interrupt"="FIQ" {
51 ; CHECK-A-LABEL: fiq_fn:
52 ; CHECK-A: push {r0, r1, r2, r3, r4, r5, r6, r7, r11, lr}
53 ; 32 to get past r0, r1, ..., r7
54 ; CHECK-A: add r11, sp, #32
55 ; CHECK-A: sub sp, sp, #{{[0-9]+}}
56 ; CHECK-A: bic sp, sp, #7
57 ; [...]
58 ; 32 must match above
59 ; CHECK-A: sub sp, r11, #32
60 ; CHECK-A: pop {r0, r1, r2, r3, r4, r5, r6, r7, r11, lr}
61 ; CHECK-A: subs pc, lr, #4
62
63 %val = load volatile [16 x i32]* @bigvar
64 store volatile [16 x i32] %val, [16 x i32]* @bigvar
65 ret void
66 }
67
68 define arm_aapcscc void @swi_fn() alignstack(8) "interrupt"="SWI" {
69 ; CHECK-A-LABEL: swi_fn:
70 ; CHECK-A: push {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr}
71 ; CHECK-A: add r11, sp, #44
72 ; CHECK-A: sub sp, sp, #{{[0-9]+}}
73 ; CHECK-A: bic sp, sp, #7
74 ; [...]
75 ; CHECK-A: sub sp, r11, #44
76 ; CHECK-A: pop {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr}
77 ; CHECK-A: subs pc, lr, #0
78
79 %val = load volatile [16 x i32]* @bigvar
80 store volatile [16 x i32] %val, [16 x i32]* @bigvar
81 ret void
82 }
83
84 define arm_aapcscc void @undef_fn() alignstack(8) "interrupt"="UNDEF" {
85 ; CHECK-A-LABEL: undef_fn:
86 ; CHECK-A: push {r0, r1, r2, r3, r11, lr}
87 ; CHECK-A: add r11, sp, #16
88 ; CHECK-A: sub sp, sp, #{{[0-9]+}}
89 ; CHECK-A: bic sp, sp, #7
90 ; [...]
91 ; CHECK-A: sub sp, r11, #16
92 ; CHECK-A: pop {r0, r1, r2, r3, r11, lr}
93 ; CHECK-A: subs pc, lr, #0
94
95 call void @bar()
96 ret void
97 }
98
99 define arm_aapcscc void @abort_fn() alignstack(8) "interrupt"="ABORT" {
100 ; CHECK-A-LABEL: abort_fn:
101 ; CHECK-A: push {r0, r1, r2, r3, r11, lr}
102 ; CHECK-A: add r11, sp, #16
103 ; CHECK-A: sub sp, sp, #{{[0-9]+}}
104 ; CHECK-A: bic sp, sp, #7
105 ; [...]
106 ; CHECK-A: sub sp, r11, #16
107 ; CHECK-A: pop {r0, r1, r2, r3, r11, lr}
108 ; CHECK-A: subs pc, lr, #4
109
110 call void @bar()
111 ret void
112 }
113
114 @var = global double 0.0
115
116 ; We don't save VFP regs, since it would be a massive overhead in the general
117 ; case.
118 define arm_aapcscc void @floating_fn() alignstack(8) "interrupt"="IRQ" {
119 ; CHECK-A-LABEL: floating_fn:
120 ; CHECK-A-NOT: vpush
121 ; CHECK-A-NOT: vstr
122 ; CHECK-A-NOT: vstm
123 ; CHECK-A: vadd.f64 {{d[0-9]+}}, {{d[0-9]+}}, {{d[0-9]+}}
124 %lhs = load volatile double* @var
125 %rhs = load volatile double* @var
126 %sum = fadd double %lhs, %rhs
127 store double %sum, double* @var
128 ret void
129 }