Skip to content

Commit b7117c5

Browse files
jakevossen5Benson Chu
authored andcommitted
[ARM] Save floating point registers with save_fp function attribute
[ARM] and interrupt_save_fp attribute interupt_save_fp update name; fix bugs [ARM] fix typos and register class name used better push / pop instructions change epilog emitting order WIP with FPSCR save just d regs cleaned up docs and ARMRegisterInfo td change m3 to m4 fix reg tests Minor format changes on top of Jake Vossen's support for interrupt_save_fp function attribute which preserves VFP D registers at the moment. FPSCR and FPEXC registers to follow.
1 parent 78abeca commit b7117c5

File tree

12 files changed

+390
-8
lines changed

12 files changed

+390
-8
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,22 @@ def ARMInterrupt : InheritableAttr, TargetSpecificAttr<TargetARM> {
970970
let Documentation = [ARMInterruptDocs];
971971
}
972972

973+
def ARMInterruptSaveFP : InheritableAttr, TargetSpecificAttr<TargetARM> {
974+
let Spellings = [GNU<"interrupt_save_fp">];
975+
let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
976+
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""],
977+
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", "Generic"],
978+
1>];
979+
let HasCustomParsing = 0;
980+
let Documentation = [ARMInterruptSaveFPDocs];
981+
}
982+
983+
def ARMSaveFP : InheritableAttr, TargetSpecificAttr<TargetARM> {
984+
let Spellings = [];
985+
let Subjects = SubjectList<[Function]>;
986+
let Documentation = [InternalOnly];
987+
}
988+
973989
def AVRInterrupt : InheritableAttr, TargetSpecificAttr<TargetAVR> {
974990
let Spellings = [GCC<"interrupt">];
975991
let Subjects = SubjectList<[Function]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2295,6 +2295,20 @@ The semantics are as follows:
22952295
}];
22962296
}
22972297

2298+
def ARMInterruptSaveFPDocs : Documentation {
2299+
let Category = DocCatFunction;
2300+
let Heading = "interrupt_save_fp (ARM)";
2301+
let Content = [{
2302+
Clang supports the GNU style ``__attribute__((interrupt_save_fp("TYPE")))``
2303+
on ARM targets. This attribute behaves the same way as the ARM interrupt
2304+
attribute, except the general purpose floating point registers are also saved.
2305+
If the FPEXC or FPSCR are needed, that state must be saved manually. Note, even
2306+
on M-class CPUs, where the floating point context can be automatically saved
2307+
depending on the FPCCR, the general purpose floating point registers will be
2308+
saved.
2309+
}];
2310+
}
2311+
22982312
def BPFPreserveAccessIndexDocs : Documentation {
22992313
let Category = DocCatFunction;
23002314
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,14 @@ def warn_anyx86_excessive_regsave : Warning<
338338
InGroup<DiagGroup<"excessive-regsave">>;
339339
def warn_arm_interrupt_vfp_clobber : Warning<
340340
"interrupt service routine with vfp enabled may clobber the "
341-
"interruptee's vfp state">,
341+
"interruptee's vfp state; "
342+
"consider using the `interrupt_save_fp` attribute to prevent this behavior">,
342343
InGroup<DiagGroup<"arm-interrupt-vfp-clobber">>;
344+
def warn_arm_interrupt_save_fp_without_vfp_unit : Warning<
345+
"`interrupt_save_fp` only applies to targets that have a VFP unit enabled "
346+
"for this compilation; this will be treated as a regular `interrupt` "
347+
"attribute">,
348+
InGroup<DiagGroup<"arm-interrupt-vfp-clobber">>;
343349
def err_arm_interrupt_called : Error<
344350
"interrupt service routine cannot be called directly">;
345351
def warn_interrupt_attribute_invalid : Warning<

clang/lib/CodeGen/Targets/ARM.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ class ARMTargetCodeGenInfo : public TargetCodeGenInfo {
186186

187187
Fn->addFnAttr("interrupt", Kind);
188188

189+
// Note: the ARMSaveFPAttr can only exist if we also have an interrupt
190+
// attribute
191+
const ARMSaveFPAttr *SaveFPAttr = FD->getAttr<ARMSaveFPAttr>();
192+
if (SaveFPAttr)
193+
Fn->addFnAttr("save-fp");
194+
189195
ARMABIKind ABI = getABIInfo<ARMABIInfo>().getABIKind();
190196
if (ABI == ARMABIKind::APCS)
191197
return;

clang/lib/Sema/SemaARM.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,9 +1331,11 @@ void SemaARM::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
13311331
return;
13321332
}
13331333

1334-
const TargetInfo &TI = getASTContext().getTargetInfo();
1335-
if (TI.hasFeature("vfp"))
1336-
Diag(D->getLocation(), diag::warn_arm_interrupt_vfp_clobber);
1334+
if (!D->hasAttr<ARMSaveFPAttr>()) {
1335+
const TargetInfo &TI = getASTContext().getTargetInfo();
1336+
if (TI.hasFeature("vfp"))
1337+
Diag(D->getLocation(), diag::warn_arm_interrupt_vfp_clobber);
1338+
}
13371339

13381340
D->addAttr(::new (getASTContext())
13391341
ARMInterruptAttr(getASTContext(), AL, Kind));

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5498,6 +5498,20 @@ static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
54985498
AbiTagAttr(S.Context, AL, Tags.data(), Tags.size()));
54995499
}
55005500

5501+
static void handleARMInterruptSaveFPAttr(Sema &S, Decl *D,
5502+
const ParsedAttr &AL) {
5503+
handleARMInterruptAttr(S, D, AL);
5504+
5505+
bool VFP = S.Context.getTargetInfo().hasFeature("vfp");
5506+
5507+
if (!VFP) {
5508+
S.Diag(D->getLocation(), diag::warn_arm_interrupt_save_fp_without_vfp_unit);
5509+
return;
5510+
}
5511+
5512+
D->addAttr(::new (S.Context) ARMSaveFPAttr(S.Context, AL));
5513+
}
5514+
55015515
static bool hasBTFDeclTagAttr(Decl *D, StringRef Tag) {
55025516
for (const auto *I : D->specific_attrs<BTFDeclTagAttr>()) {
55035517
if (I->getBTFDeclTag() == Tag)
@@ -6391,6 +6405,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
63916405
case ParsedAttr::AT_Interrupt:
63926406
handleInterruptAttr(S, D, AL);
63936407
break;
6408+
case ParsedAttr::AT_ARMInterruptSaveFP:
6409+
handleARMInterruptSaveFPAttr(S, D, AL);
6410+
break;
63946411
case ParsedAttr::AT_X86ForceAlignArgPointer:
63956412
S.X86().handleForceAlignArgPointerAttr(D, AL);
63966413
break;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %clang_cc1 -triple thumb-apple-darwin -target-abi aapcs -target-feature +vfp4 -target-cpu cortex-m3 -emit-llvm -o - %s | FileCheck %s
2+
// RUN: %clang_cc1 -triple arm-apple-darwin -target-abi apcs-gnu -target-feature +vfp4 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-APCS
3+
4+
__attribute__((interrupt_save_fp)) void test_generic_interrupt() {
5+
// CHECK: define{{.*}} arm_aapcscc void @test_generic_interrupt() [[GENERIC_ATTR:#[0-9]+]]
6+
7+
// CHECK-APCS: define{{.*}} void @test_generic_interrupt() [[GENERIC_ATTR:#[0-9]+]]
8+
}
9+
10+
__attribute__((interrupt_save_fp("IRQ"))) void test_irq_interrupt() {
11+
// CHECK: define{{.*}} arm_aapcscc void @test_irq_interrupt() [[IRQ_ATTR:#[0-9]+]]
12+
}
13+
14+
__attribute__((interrupt_save_fp("FIQ"))) void test_fiq_interrupt() {
15+
// CHECK: define{{.*}} arm_aapcscc void @test_fiq_interrupt() [[FIQ_ATTR:#[0-9]+]]
16+
}
17+
18+
__attribute__((interrupt_save_fp("SWI"))) void test_swi_interrupt() {
19+
// CHECK: define{{.*}} arm_aapcscc void @test_swi_interrupt() [[SWI_ATTR:#[0-9]+]]
20+
}
21+
22+
__attribute__((interrupt_save_fp("ABORT"))) void test_abort_interrupt() {
23+
// CHECK: define{{.*}} arm_aapcscc void @test_abort_interrupt() [[ABORT_ATTR:#[0-9]+]]
24+
}
25+
26+
27+
__attribute__((interrupt_save_fp("UNDEF"))) void test_undef_interrupt() {
28+
// CHECK: define{{.*}} arm_aapcscc void @test_undef_interrupt() [[UNDEF_ATTR:#[0-9]+]]
29+
}
30+
31+
32+
// CHECK: attributes [[GENERIC_ATTR]] = { {{.*}} {{"interrupt"[^=]}}{{.*}} "save-fp"
33+
// CHECK: attributes [[IRQ_ATTR]] = { {{.*}} "interrupt"="IRQ" {{.*}} "save-fp"
34+
// CHECK: attributes [[FIQ_ATTR]] = { {{.*}} "interrupt"="FIQ" {{.*}} "save-fp"
35+
// CHECK: attributes [[SWI_ATTR]] = { {{.*}} "interrupt"="SWI" {{.*}} "save-fp"
36+
// CHECK: attributes [[ABORT_ATTR]] = { {{.*}} "interrupt"="ABORT" {{.*}} "save-fp"
37+
// CHECK: attributes [[UNDEF_ATTR]] = { {{.*}} "interrupt"="UNDEF" {{.*}} "save-fp"
38+
39+
// CHECK-APCS: attributes [[GENERIC_ATTR]] = { {{.*}} "interrupt" {{.*}} "save-fp"

clang/test/Sema/arm-interrupt-attr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
#ifdef __ARM_FP
6-
__attribute__((interrupt("IRQ"))) void float_irq(void); // expected-warning {{interrupt service routine with vfp enabled may clobber the interruptee's vfp state}}
6+
__attribute__((interrupt("IRQ"))) void float_irq(void); // expected-warning {{interrupt service routine with vfp enabled may clobber the interruptee's vfp state; consider using the `interrupt_save_fp` attribute to prevent this behavior}}
77
#else // !defined(__ARM_FP)
88
__attribute__((interrupt("irq"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: irq}}
99
__attribute__((interrupt(IRQ))) void foo(void) {} // expected-error {{'interrupt' attribute requires a string}}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %clang_cc1 %s -triple arm-apple-darwin -target-feature +vfp2 -verify -fsyntax-only
2+
// RUN: %clang_cc1 %s -triple thumb-apple-darwin -target-feature +vfp3 -verify -fsyntax-only
3+
// RUN: %clang_cc1 %s -triple armeb-none-eabi -target-feature +vfp4 -verify -fsyntax-only
4+
// RUN: %clang_cc1 %s -triple thumbeb-none-eabi -target-feature +neon -verify -fsyntax-only
5+
// RUN: %clang_cc1 %s -triple thumbeb-none-eabi -target-feature +neon -target-feature +soft-float -DSOFT -verify -fsyntax-only
6+
7+
#ifndef SOFT
8+
__attribute__((interrupt_save_fp(IRQ))) void foo() {} // expected-error {{'interrupt_save_fp' attribute requires a string}}
9+
__attribute__((interrupt_save_fp("irq"))) void foo1() {} // expected-warning {{'interrupt_save_fp' attribute argument not supported: irq}}
10+
11+
__attribute__((interrupt_save_fp("IRQ", 1))) void foo2() {} // expected-error {{'interrupt_save_fp' attribute takes no more than 1 argument}}
12+
__attribute__((interrupt_save_fp("IRQ"))) void foo3() {}
13+
__attribute__((interrupt_save_fp("FIQ"))) void foo4() {}
14+
__attribute__((interrupt_save_fp("SWI"))) void foo5() {}
15+
__attribute__((interrupt_save_fp("ABORT"))) void foo6() {}
16+
__attribute__((interrupt_save_fp("UNDEF"))) void foo7() {}
17+
__attribute__((interrupt_save_fp)) void foo8() {}
18+
__attribute__((interrupt_save_fp())) void foo9() {}
19+
__attribute__((interrupt_save_fp(""))) void foo10() {}
20+
void callee1();
21+
__attribute__((interrupt_save_fp("IRQ"))) void callee2();
22+
void caller1() {
23+
callee1();
24+
callee2();
25+
}
26+
__attribute__((interrupt_save_fp("IRQ"))) void caller2() {
27+
callee1();
28+
callee2();
29+
}
30+
31+
void (*callee3)();
32+
__attribute__((interrupt_save_fp("IRQ"))) void caller3() {
33+
callee3();
34+
}
35+
#else
36+
__attribute__((interrupt_save_fp("IRQ"))) void foo3() {} // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
37+
__attribute__((interrupt_save_fp("FIQ"))) void foo4() {} // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
38+
__attribute__((interrupt_save_fp("SWI"))) void foo5() {} // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
39+
__attribute__((interrupt_save_fp("ABORT"))) void foo6() {} // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
40+
__attribute__((interrupt_save_fp("UNDEF"))) void foo7() {} // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
41+
__attribute__((interrupt_save_fp)) void foo8() {} // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
42+
__attribute__((interrupt_save_fp())) void foo9() {} // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
43+
__attribute__((interrupt_save_fp(""))) void foo10() {} // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
44+
void callee1();
45+
__attribute__((interrupt_save_fp("IRQ"))) void callee2(); // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
46+
void caller1() {
47+
callee1();
48+
callee2();
49+
}
50+
__attribute__((interrupt_save_fp("IRQ"))) void caller2() { // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
51+
callee1();
52+
callee2();
53+
}
54+
55+
void (*callee3)();
56+
__attribute__((interrupt_save_fp("IRQ"))) void caller3() { // expected-warning {{`interrupt_save_fp` only applies to targets that have a VFP unit enabled for this compilation; this will be treated as a regular `interrupt` attribute}}
57+
callee3();
58+
}
59+
#endif

llvm/lib/Target/ARM/ARMBaseRegisterInfo.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,42 @@ ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
7979
: (UseSplitPush ? CSR_ATPCS_SplitPush_SwiftTail_SaveList
8080
: CSR_AAPCS_SwiftTail_SaveList);
8181
} else if (F.hasFnAttribute("interrupt")) {
82+
bool SaveFP = F.hasFnAttribute("save-fp");
83+
bool HasNEON = MF->getSubtarget<ARMSubtarget>().hasNEON();
84+
8285
if (STI.isMClass()) {
8386
// M-class CPUs have hardware which saves the registers needed to allow a
8487
// function conforming to the AAPCS to function as a handler.
85-
return UseSplitPush ? CSR_ATPCS_SplitPush_SaveList : CSR_AAPCS_SaveList;
88+
// Additionally, M Class has hardware support for saving VFP registers,
89+
// but the option can be disabled
90+
if (SaveFP) {
91+
if (HasNEON) {
92+
return UseSplitPush ? CSR_AAPCS_SplitPush_FP_NEON_SaveList
93+
: CSR_AAPCS_FP_NEON_SaveList;
94+
} else {
95+
return UseSplitPush ? CSR_AAPCS_SplitPush_FP_SaveList
96+
: CSR_AAPCS_FP_SaveList;
97+
}
98+
} else {
99+
return UseSplitPush ? CSR_AAPCS_SplitPush_SaveList : CSR_AAPCS_SaveList;
100+
}
86101
} else if (F.getFnAttribute("interrupt").getValueAsString() == "FIQ") {
87102
// Fast interrupt mode gives the handler a private copy of R8-R14, so less
88103
// need to be saved to restore user-mode state.
89-
return CSR_FIQ_SaveList;
104+
if (SaveFP) {
105+
return HasNEON ? CSR_FIQ_FP_NEON_SaveList : CSR_FIQ_FP_SaveList;
106+
} else {
107+
return CSR_FIQ_SaveList;
108+
}
90109
} else {
91110
// Generally only R13-R14 (i.e. SP, LR) are automatically preserved by
92111
// exception handling.
93-
return CSR_GenericInt_SaveList;
112+
if (SaveFP) {
113+
return HasNEON ? CSR_GenericInt_FP_NEON_SaveList
114+
: CSR_GenericInt_FP_SaveList;
115+
} else {
116+
return CSR_GenericInt_SaveList;
117+
}
94118
}
95119
}
96120

llvm/lib/Target/ARM/ARMCallingConv.td

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,18 @@ def CC_ARM_Win32_CFGuard_Check : CallingConv<[
267267
def CSR_NoRegs : CalleeSavedRegs<(add)>;
268268
def CSR_FPRegs : CalleeSavedRegs<(add (sequence "D%u", 0, 31))>;
269269

270+
def CSR_FP_Interrupt_Regs : CalleeSavedRegs<(add (sequence "D%u", 7, 0))>;
271+
def CSR_FP_NEON_Interrupt_Regs : CalleeSavedRegs<(add CSR_FP_Interrupt_Regs,
272+
(sequence "D%u", 31, 16))>;
273+
270274
def CSR_AAPCS : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7, R6, R5, R4,
271275
(sequence "D%u", 15, 8))>;
272276

277+
def CSR_AAPCS_FP : CalleeSavedRegs<(add CSR_AAPCS, CSR_FP_Interrupt_Regs)>;
278+
279+
def CSR_AAPCS_FP_NEON : CalleeSavedRegs<(add CSR_AAPCS_FP,
280+
CSR_FP_NEON_Interrupt_Regs)>;
281+
273282
// The Windows Control Flow Guard Check function preserves the same registers as
274283
// AAPCS, and also preserves all floating point registers.
275284
def CSR_Win_AAPCS_CFGuard_Check : CalleeSavedRegs<(add LR, R11, R10, R9, R8, R7,
@@ -293,6 +302,13 @@ def CSR_Win_SplitFP : CalleeSavedRegs<(add R10, R9, R8, R7, R6, R5, R4,
293302
(sequence "D%u", 15, 8),
294303
LR, R11)>;
295304

305+
def CSR_AAPCS_SplitPush_FP : CalleeSavedRegs<(add CSR_AAPCS_SplitPush,
306+
CSR_FP_Interrupt_Regs)>;
307+
308+
def CSR_AAPCS_SplitPush_FP_NEON : CalleeSavedRegs<(add CSR_AAPCS_SplitPush_FP,
309+
CSR_FP_NEON_Interrupt_Regs)>;
310+
311+
296312
// R8 is used to pass swifterror, remove it from CSR.
297313
def CSR_ATPCS_SplitPush_SwiftError : CalleeSavedRegs<(sub CSR_ATPCS_SplitPush,
298314
R8)>;
@@ -357,6 +373,13 @@ def CSR_iOS_CXX_TLS_ViaCopy : CalleeSavedRegs<(sub CSR_iOS_CXX_TLS,
357373
// generally does rather than tracking its liveness as a normal register.
358374
def CSR_GenericInt : CalleeSavedRegs<(add LR, (sequence "R%u", 12, 0))>;
359375

376+
def CSR_GenericInt_FP : CalleeSavedRegs<(add CSR_GenericInt,
377+
CSR_FP_Interrupt_Regs)>;
378+
379+
def CSR_GenericInt_FP_NEON : CalleeSavedRegs<(add CSR_GenericInt_FP,
380+
CSR_FP_NEON_Interrupt_Regs)>;
381+
382+
360383
// The fast interrupt handlers have more private state and get their own copies
361384
// of R8-R12, in addition to SP and LR. As before, mark LR for saving too.
362385

@@ -365,4 +388,9 @@ def CSR_GenericInt : CalleeSavedRegs<(add LR, (sequence "R%u", 12, 0))>;
365388
// registers.
366389
def CSR_FIQ : CalleeSavedRegs<(add LR, R11, (sequence "R%u", 7, 0))>;
367390

391+
def CSR_FIQ_FP : CalleeSavedRegs<(add CSR_FIQ, CSR_FP_Interrupt_Regs)>;
392+
393+
def CSR_FIQ_FP_NEON : CalleeSavedRegs<(add CSR_FIQ_FP,
394+
CSR_FP_NEON_Interrupt_Regs)>;
395+
368396

0 commit comments

Comments
 (0)