Skip to content

Commit b9efa10

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 dd79632 commit b9efa10

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
@@ -914,6 +914,22 @@ def ARMInterrupt : InheritableAttr, TargetSpecificAttr<TargetARM> {
914914
let Documentation = [ARMInterruptDocs];
915915
}
916916

917+
def ARMInterruptSaveFP : InheritableAttr, TargetSpecificAttr<TargetARM> {
918+
let Spellings = [GNU<"interrupt_save_fp">];
919+
let Args = [EnumArgument<"Interrupt", "InterruptType", /*is_string=*/true,
920+
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""],
921+
["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", "Generic"],
922+
1>];
923+
let HasCustomParsing = 0;
924+
let Documentation = [ARMInterruptSaveFPDocs];
925+
}
926+
927+
def ARMSaveFP : InheritableAttr, TargetSpecificAttr<TargetARM> {
928+
let Spellings = [];
929+
let Subjects = SubjectList<[Function]>;
930+
let Documentation = [InternalOnly];
931+
}
932+
917933
def AVRInterrupt : InheritableAttr, TargetSpecificAttr<TargetAVR> {
918934
let Spellings = [GCC<"interrupt">];
919935
let Subjects = SubjectList<[Function]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,6 +2239,20 @@ The semantics are as follows:
22392239
}];
22402240
}
22412241

2242+
def ARMInterruptSaveFPDocs : Documentation {
2243+
let Category = DocCatFunction;
2244+
let Heading = "interrupt_save_fp (ARM)";
2245+
let Content = [{
2246+
Clang supports the GNU style ``__attribute__((interrupt_save_fp("TYPE")))``
2247+
on ARM targets. This attribute behaves the same way as the ARM interrupt
2248+
attribute, except the general purpose floating point registers are also saved.
2249+
If the FPEXC or FPSCR are needed, that state must be saved manually. Note, even
2250+
on M-class CPUs, where the floating point context can be automatically saved
2251+
depending on the FPCCR, the general purpose floating point registers will be
2252+
saved.
2253+
}];
2254+
}
2255+
22422256
def BPFPreserveAccessIndexDocs : Documentation {
22432257
let Category = DocCatFunction;
22442258
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,14 @@ def warn_anyx86_excessive_regsave : Warning<
335335
" or be compiled with '-mgeneral-regs-only'">,
336336
InGroup<DiagGroup<"excessive-regsave">>;
337337
def warn_arm_interrupt_calling_convention : Warning<
338-
"call to function without interrupt attribute could clobber interruptee's VFP registers">,
338+
"call to function without interrupt attribute could clobber interruptee's "
339+
"VFP registers; consider using the `interrupt_save_fp` attribute to prevent "
340+
"this behavior">,
341+
InGroup<Extra>;
342+
def warn_arm_interrupt_save_fp_without_vfp_unit : Warning<
343+
"`interrupt_save_fp` only applies to targets that have a VFP unit enabled "
344+
"for this compilation; this will be treated as a regular `interrupt` "
345+
"attribute">,
339346
InGroup<Extra>;
340347
def warn_interrupt_attribute_invalid : Warning<
341348
"%select{MIPS|MSP430|RISC-V}0 'interrupt' attribute only applies to "

clang/lib/CodeGen/Targets/ARM.cpp

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

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

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

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7524,6 +7524,20 @@ static void handleARMInterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
75247524
D->addAttr(::new (S.Context) ARMInterruptAttr(S.Context, AL, Kind));
75257525
}
75267526

7527+
static void handleARMInterruptSaveFPAttr(Sema &S, Decl *D,
7528+
const ParsedAttr &AL) {
7529+
handleARMInterruptAttr(S, D, AL);
7530+
7531+
bool VFP = S.Context.getTargetInfo().hasFeature("vfp");
7532+
7533+
if (!VFP) {
7534+
S.Diag(D->getLocation(), diag::warn_arm_interrupt_save_fp_without_vfp_unit);
7535+
return;
7536+
}
7537+
7538+
D->addAttr(::new (S.Context) ARMSaveFPAttr(S.Context, AL));
7539+
}
7540+
75277541
static void handleMSP430InterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
75287542
// MSP430 'interrupt' attribute is applied to
75297543
// a function with no parameters and void return type.
@@ -9134,9 +9148,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
91349148
if (AL.isCXX11Attribute() && !Options.IncludeCXX11Attributes)
91359149
return;
91369150

9137-
// Unknown attributes are automatically warned on. Target-specific attributes
9138-
// which do not apply to the current target architecture are treated as
9139-
// though they were unknown attributes.
9151+
// Unknown attributes are automatically warned on. Target-specific
9152+
// attributes which do not apply to the current target architecture are
9153+
// treated as though they were unknown attributes.
91409154
if (AL.getKind() == ParsedAttr::UnknownAttribute ||
91419155
!AL.existsInTarget(S.Context.getTargetInfo())) {
91429156
S.Diag(AL.getLoc(),
@@ -9241,6 +9255,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
92419255
case ParsedAttr::AT_Interrupt:
92429256
handleInterruptAttr(S, D, AL);
92439257
break;
9258+
case ParsedAttr::AT_ARMInterruptSaveFP:
9259+
handleARMInterruptSaveFPAttr(S, D, AL);
9260+
break;
92449261
case ParsedAttr::AT_X86ForceAlignArgPointer:
92459262
handleX86ForceAlignArgPointerAttr(S, D, AL);
92469263
break;

clang/lib/Sema/SemaExpr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6931,7 +6931,8 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
69316931
// no_caller_saved_registers since there is no efficient way to
69326932
// save and restore the non-GPR state.
69336933
if (auto *Caller = getCurFunctionDecl()) {
6934-
if (Caller->hasAttr<ARMInterruptAttr>()) {
6934+
if (Caller->hasAttr<ARMInterruptAttr>() &&
6935+
!Caller->hasAttr<ARMSaveFPAttr>()) {
69356936
bool VFP = Context.getTargetInfo().hasFeature("vfp");
69366937
if (VFP && (!FDecl || !FDecl->hasAttr<ARMInterruptAttr>())) {
69376938
Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_calling_convention);
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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ void caller1(void) {
3131

3232
#ifndef SOFT
3333
__attribute__((interrupt("IRQ"))) void caller2(void) {
34-
callee1(); // expected-warning {{call to function without interrupt attribute could clobber interruptee's VFP registers}}
34+
callee1(); // expected-warning {{call to function without interrupt attribute could clobber interruptee's VFP registers; consider using the `interrupt_save_fp` attribute to prevent this behavior}}
3535
callee2();
3636
}
3737

3838
void (*callee3)(void);
3939
__attribute__((interrupt("IRQ"))) void caller3(void) {
40-
callee3(); // expected-warning {{call to function without interrupt attribute could clobber interruptee's VFP registers}}
40+
callee3(); // expected-warning {{call to function without interrupt attribute could clobber interruptee's VFP registers; consider using the `interrupt_save_fp` attribute to prevent this behavior}}
4141
}
4242
#else
4343
__attribute__((interrupt("IRQ"))) void caller2(void) {
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)