Skip to content

Commit c95cabc

Browse files
jakevossen5pestctrl
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 34d4f66 commit c95cabc

File tree

12 files changed

+392
-10
lines changed

12 files changed

+392
-10
lines changed

clang/include/clang/Basic/Attr.td

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

978+
def ARMInterruptSaveFP : InheritableAttr, TargetSpecificAttr<TargetARM> {
979+
let Spellings = [GNU<"interrupt_save_fp">];
980+
let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
981+
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""],
982+
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", "Generic"],
983+
1>];
984+
let HasCustomParsing = 0;
985+
let Documentation = [ARMInterruptSaveFPDocs];
986+
}
987+
988+
def ARMSaveFP : InheritableAttr, TargetSpecificAttr<TargetARM> {
989+
let Spellings = [];
990+
let Subjects = SubjectList<[Function]>;
991+
let Documentation = [InternalOnly];
992+
}
993+
978994
def AVRInterrupt : InheritableAttr, TargetSpecificAttr<TargetAVR> {
979995
let Spellings = [GCC<"interrupt">];
980996
let Subjects = SubjectList<[Function]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,6 +2301,20 @@ The semantics are as follows:
23012301
}];
23022302
}
23032303

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

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,14 @@ def warn_anyx86_excessive_regsave : Warning<
346346
InGroup<DiagGroup<"excessive-regsave">>;
347347
def warn_arm_interrupt_vfp_clobber : Warning<
348348
"interrupt service routine with vfp enabled may clobber the "
349-
"interruptee's vfp state">,
349+
"interruptee's vfp state; "
350+
"consider using the `interrupt_save_fp` attribute to prevent this behavior">,
350351
InGroup<DiagGroup<"arm-interrupt-vfp-clobber">>;
352+
def warn_arm_interrupt_save_fp_without_vfp_unit : Warning<
353+
"`interrupt_save_fp` only applies to targets that have a VFP unit enabled "
354+
"for this compilation; this will be treated as a regular `interrupt` "
355+
"attribute">,
356+
InGroup<DiagGroup<"arm-interrupt-vfp-clobber">>;
351357
def err_arm_interrupt_called : Error<
352358
"interrupt service routine cannot be called directly">;
353359
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
@@ -189,6 +189,12 @@ class ARMTargetCodeGenInfo : public TargetCodeGenInfo {
189189

190190
Fn->addFnAttr("interrupt", Kind);
191191

192+
// Note: the ARMSaveFPAttr can only exist if we also have an interrupt
193+
// attribute
194+
const ARMSaveFPAttr *SaveFPAttr = FD->getAttr<ARMSaveFPAttr>();
195+
if (SaveFPAttr)
196+
Fn->addFnAttr("save-fp");
197+
192198
ARMABIKind ABI = getABIInfo<ARMABIInfo>().getABIKind();
193199
if (ABI == ARMABIKind::APCS)
194200
return;

clang/lib/Sema/SemaARM.cpp

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

1323-
const TargetInfo &TI = getASTContext().getTargetInfo();
1324-
if (TI.hasFeature("vfp"))
1325-
Diag(D->getLocation(), diag::warn_arm_interrupt_vfp_clobber);
1323+
if (!D->hasAttr<ARMSaveFPAttr>()) {
1324+
const TargetInfo &TI = getASTContext().getTargetInfo();
1325+
if (TI.hasFeature("vfp"))
1326+
Diag(D->getLocation(), diag::warn_arm_interrupt_vfp_clobber);
1327+
}
13261328

13271329
D->addAttr(::new (getASTContext())
13281330
ARMInterruptAttr(getASTContext(), AL, Kind));

clang/lib/Sema/SemaDeclAttr.cpp

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

5587+
static void handleARMInterruptSaveFPAttr(Sema &S, Decl *D,
5588+
const ParsedAttr &AL) {
5589+
handleARMInterruptAttr(S, D, AL);
5590+
5591+
bool VFP = S.Context.getTargetInfo().hasFeature("vfp");
5592+
5593+
if (!VFP) {
5594+
S.Diag(D->getLocation(), diag::warn_arm_interrupt_save_fp_without_vfp_unit);
5595+
return;
5596+
}
5597+
5598+
D->addAttr(::new (S.Context) ARMSaveFPAttr(S.Context, AL));
5599+
}
5600+
55875601
static bool hasBTFDeclTagAttr(Decl *D, StringRef Tag) {
55885602
for (const auto *I : D->specific_attrs<BTFDeclTagAttr>()) {
55895603
if (I->getBTFDeclTag() == Tag)
@@ -6477,6 +6491,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
64776491
case ParsedAttr::AT_Interrupt:
64786492
handleInterruptAttr(S, D, AL);
64796493
break;
6494+
case ParsedAttr::AT_ARMInterruptSaveFP:
6495+
handleARMInterruptSaveFPAttr(S, D, AL);
6496+
break;
64806497
case ParsedAttr::AT_X86ForceAlignArgPointer:
64816498
S.X86().handleForceAlignArgPointerAttr(D, AL);
64826499
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: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,44 @@ ARMBaseRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
8080
? CSR_ATPCS_SplitPush_SwiftTail_SaveList
8181
: CSR_AAPCS_SwiftTail_SaveList);
8282
} else if (F.hasFnAttribute("interrupt")) {
83+
bool SaveFP = F.hasFnAttribute("save-fp");
84+
bool HasNEON = MF->getSubtarget<ARMSubtarget>().hasNEON();
85+
8386
if (STI.isMClass()) {
8487
// M-class CPUs have hardware which saves the registers needed to allow a
8588
// function conforming to the AAPCS to function as a handler.
86-
return PushPopSplit == ARMSubtarget::SplitR7
87-
? CSR_ATPCS_SplitPush_SaveList
88-
: CSR_AAPCS_SaveList;
89+
// Additionally, M Class has hardware support for saving VFP registers,
90+
// but the option can be disabled
91+
if (SaveFP) {
92+
if (HasNEON) {
93+
return UseSplitPush ? CSR_AAPCS_SplitPush_FP_NEON_SaveList
94+
: CSR_AAPCS_FP_NEON_SaveList;
95+
} else {
96+
return UseSplitPush ? CSR_AAPCS_SplitPush_FP_SaveList
97+
: CSR_AAPCS_FP_SaveList;
98+
}
99+
} else {
100+
return PushPopSplit == ARMSubtarget::SplitR7
101+
? CSR_ATPCS_SplitPush_SaveList
102+
: CSR_AAPCS_SaveList;
103+
}
89104
} else if (F.getFnAttribute("interrupt").getValueAsString() == "FIQ") {
90105
// Fast interrupt mode gives the handler a private copy of R8-R14, so less
91106
// need to be saved to restore user-mode state.
92-
return CSR_FIQ_SaveList;
107+
if (SaveFP) {
108+
return HasNEON ? CSR_FIQ_FP_NEON_SaveList : CSR_FIQ_FP_SaveList;
109+
} else {
110+
return CSR_FIQ_SaveList;
111+
}
93112
} else {
94113
// Generally only R13-R14 (i.e. SP, LR) are automatically preserved by
95114
// exception handling.
96-
return CSR_GenericInt_SaveList;
115+
if (SaveFP) {
116+
return HasNEON ? CSR_GenericInt_FP_NEON_SaveList
117+
: CSR_GenericInt_FP_SaveList;
118+
} else {
119+
return CSR_GenericInt_SaveList;
120+
}
97121
}
98122
}
99123

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)