Skip to content

Commit 6186aad

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 cf6a520 commit 6186aad

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
@@ -994,6 +994,22 @@ def ARMInterrupt : InheritableAttr, TargetSpecificAttr<TargetARM> {
994994
let Documentation = [ARMInterruptDocs];
995995
}
996996

997+
def ARMInterruptSaveFP : InheritableAttr, TargetSpecificAttr<TargetARM> {
998+
let Spellings = [GNU<"interrupt_save_fp">];
999+
let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
1000+
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""],
1001+
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", "Generic"],
1002+
1>];
1003+
let HasCustomParsing = 0;
1004+
let Documentation = [ARMInterruptSaveFPDocs];
1005+
}
1006+
1007+
def ARMSaveFP : InheritableAttr, TargetSpecificAttr<TargetARM> {
1008+
let Spellings = [];
1009+
let Subjects = SubjectList<[Function]>;
1010+
let Documentation = [InternalOnly];
1011+
}
1012+
9971013
def AVRInterrupt : InheritableAttr, TargetSpecificAttr<TargetAVR> {
9981014
let Spellings = [GCC<"interrupt">];
9991015
let Subjects = SubjectList<[Function]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2662,6 +2662,20 @@ The semantics are as follows:
26622662
}];
26632663
}
26642664

2665+
def ARMInterruptSaveFPDocs : Documentation {
2666+
let Category = DocCatFunction;
2667+
let Heading = "interrupt_save_fp (ARM)";
2668+
let Content = [{
2669+
Clang supports the GNU style ``__attribute__((interrupt_save_fp("TYPE")))``
2670+
on ARM targets. This attribute behaves the same way as the ARM interrupt
2671+
attribute, except the general purpose floating point registers are also saved.
2672+
If the FPEXC or FPSCR are needed, that state must be saved manually. Note, even
2673+
on M-class CPUs, where the floating point context can be automatically saved
2674+
depending on the FPCCR, the general purpose floating point registers will be
2675+
saved.
2676+
}];
2677+
}
2678+
26652679
def BPFPreserveAccessIndexDocs : Documentation {
26662680
let Category = DocCatFunction;
26672681
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_signal_attribute_invalid : Warning<

clang/lib/CodeGen/Targets/ARM.cpp

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

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

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

clang/lib/Sema/SemaARM.cpp

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

1310-
const TargetInfo &TI = getASTContext().getTargetInfo();
1311-
if (TI.hasFeature("vfp"))
1312-
Diag(D->getLocation(), diag::warn_arm_interrupt_vfp_clobber);
1310+
if (!D->hasAttr<ARMSaveFPAttr>()) {
1311+
const TargetInfo &TI = getASTContext().getTargetInfo();
1312+
if (TI.hasFeature("vfp"))
1313+
Diag(D->getLocation(), diag::warn_arm_interrupt_vfp_clobber);
1314+
}
13131315

13141316
D->addAttr(::new (getASTContext())
13151317
ARMInterruptAttr(getASTContext(), AL, Kind));

clang/lib/Sema/SemaDeclAttr.cpp

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

5996+
static void handleARMInterruptSaveFPAttr(Sema &S, Decl *D,
5997+
const ParsedAttr &AL) {
5998+
handleARMInterruptAttr(S, D, AL);
5999+
6000+
bool VFP = S.Context.getTargetInfo().hasFeature("vfp");
6001+
6002+
if (!VFP) {
6003+
S.Diag(D->getLocation(), diag::warn_arm_interrupt_save_fp_without_vfp_unit);
6004+
return;
6005+
}
6006+
6007+
D->addAttr(::new (S.Context) ARMSaveFPAttr(S.Context, AL));
6008+
}
6009+
59966010
static bool hasBTFDeclTagAttr(Decl *D, StringRef Tag) {
59976011
for (const auto *I : D->specific_attrs<BTFDeclTagAttr>()) {
59986012
if (I->getBTFDeclTag() == Tag)
@@ -6897,6 +6911,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
68976911
case ParsedAttr::AT_Interrupt:
68986912
handleInterruptAttr(S, D, AL);
68996913
break;
6914+
case ParsedAttr::AT_ARMInterruptSaveFP:
6915+
handleARMInterruptSaveFPAttr(S, D, AL);
6916+
break;
69006917
case ParsedAttr::AT_X86ForceAlignArgPointer:
69016918
S.X86().handleForceAlignArgPointerAttr(D, AL);
69026919
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
@@ -268,9 +268,18 @@ def CC_ARM_Win32_CFGuard_Check : CallingConv<[
268268
def CSR_NoRegs : CalleeSavedRegs<(add)>;
269269
def CSR_FPRegs : CalleeSavedRegs<(add (sequence "D%u", 0, 31))>;
270270

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

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

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

380+
def CSR_GenericInt_FP : CalleeSavedRegs<(add CSR_GenericInt,
381+
CSR_FP_Interrupt_Regs)>;
382+
383+
def CSR_GenericInt_FP_NEON : CalleeSavedRegs<(add CSR_GenericInt_FP,
384+
CSR_FP_NEON_Interrupt_Regs)>;
385+
386+
364387
// The fast interrupt handlers have more private state and get their own copies
365388
// of R8-R12, in addition to SP and LR. As before, mark LR for saving too.
366389

@@ -369,4 +392,9 @@ def CSR_GenericInt : CalleeSavedRegs<(add LR, (sequence "R%u", 12, 0))>;
369392
// registers.
370393
def CSR_FIQ : CalleeSavedRegs<(add LR, R11, (sequence "R%u", 7, 0))>;
371394

395+
def CSR_FIQ_FP : CalleeSavedRegs<(add CSR_FIQ, CSR_FP_Interrupt_Regs)>;
396+
397+
def CSR_FIQ_FP_NEON : CalleeSavedRegs<(add CSR_FIQ_FP,
398+
CSR_FP_NEON_Interrupt_Regs)>;
399+
372400

0 commit comments

Comments
 (0)