Skip to content

Commit 588a6d7

Browse files
authored
[clang][ARM] Fix warning for using VFP from interrupts. (llvm#91870)
[clang][ARM] Fix warning for using VFP from interrupts. This warning has three issues: - The interrupt attribute causes the function to return using an exception return instruction. This warning allows calls from one function with the interrupt attribute to another, and the diagnostic text suggests that not having the attribute on the callee is a problem. Actually making such a call will lead to a double exception return, which is unpredictable according to the ARM architecture manual section B9.1.1, "Restrictions on exception return instructions". Even on machines where an exception return from user/system mode is tolerated, if the callee's interrupt type is anything other than a supervisor call or secure monitor call, it will also return to a different address than a normal function would. For example, returning from an "IRQ" handler will return to lr - 4, which will generally result in calling the same function again. - The interrupt attribute currently does not cause caller-saved VFP registers to be saved and restored if they are used, so putting __attribute__((interrupt)) on a called function doesn't prevent it from clobbering VFP state. - It is part of the -Wextra diagnostic group and can't be individually disabled when using -Wextra, which also means the diagnostic text of this specific warning appears in the documentation of -Wextra. This change addresses all three issues by instead generating a warning for any interrupt handler where the vfp feature is enabled. The warning is also given its own diagnostic group. Closes llvm#34876. [clang][ARM] Emit an error when an interrupt handler is called. Closes llvm#95359.
1 parent 8210087 commit 588a6d7

File tree

5 files changed

+44
-57
lines changed

5 files changed

+44
-57
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,9 @@ New Compiler Flags
457457
that relies on it. Users should carefully consider this possibiilty when using
458458
the flag.
459459

460+
- For the ARM target, added ``-Warm-interrupt-vfp-clobber`` that will emit a
461+
diagnostic when an interrupt handler is declared and VFP is enabled.
462+
460463
Deprecated Compiler Flags
461464
-------------------------
462465

@@ -499,6 +502,13 @@ Modified Compiler Flags
499502
now include dianostics about C++26 features that are not present in older
500503
versions.
501504

505+
- Removed the "arm interrupt calling convention" warning that was included in
506+
``-Wextra`` without its own flag. This warning suggested adding
507+
``__attribute__((interrupt))`` to functions that are called from interrupt
508+
handlers to prevent clobbering VFP registers. Following this suggestion leads
509+
to unpredictable behavior by causing multiple exception returns from one
510+
exception. Fixes #GH34876.
511+
502512
Removed Compiler Flags
503513
-------------------------
504514

@@ -693,6 +703,8 @@ Improvements to Clang's diagnostics
693703
- Clang no longer emits a "no previous prototype" warning for Win32 entry points under ``-Wmissing-prototypes``.
694704
Fixes #GH94366.
695705

706+
- For the ARM target, calling an interrupt handler from another function is now an error. #GH95359.
707+
696708
Improvements to Clang's time-trace
697709
----------------------------------
698710

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,12 @@ def warn_anyx86_excessive_regsave : Warning<
336336
" with attribute 'no_caller_saved_registers'"
337337
" or be compiled with '-mgeneral-regs-only'">,
338338
InGroup<DiagGroup<"excessive-regsave">>;
339-
def warn_arm_interrupt_calling_convention : Warning<
340-
"call to function without interrupt attribute could clobber interruptee's VFP registers">,
341-
InGroup<Extra>;
339+
def warn_arm_interrupt_vfp_clobber : Warning<
340+
"interrupt service routine with vfp enabled may clobber the "
341+
"interruptee's vfp state">,
342+
InGroup<DiagGroup<"arm-interrupt-vfp-clobber">>;
343+
def err_arm_interrupt_called : Error<
344+
"interrupt service routine cannot be called directly">;
342345
def warn_interrupt_attribute_invalid : Warning<
343346
"%select{MIPS|MSP430|RISC-V}0 'interrupt' attribute only applies to "
344347
"functions that have %select{no parameters|a 'void' return type}1">,

clang/lib/Sema/SemaARM.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,10 @@ void SemaARM::handleInterruptAttr(Decl *D, const ParsedAttr &AL) {
13291329
return;
13301330
}
13311331

1332+
const TargetInfo &TI = getASTContext().getTargetInfo();
1333+
if (TI.hasFeature("vfp"))
1334+
Diag(D->getLocation(), diag::warn_arm_interrupt_vfp_clobber);
1335+
13321336
D->addAttr(::new (getASTContext())
13331337
ARMInterruptAttr(getASTContext(), AL, Kind));
13341338
}

clang/lib/Sema/SemaExpr.cpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6625,27 +6625,21 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
66256625
unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0);
66266626

66276627
// Functions with 'interrupt' attribute cannot be called directly.
6628-
if (FDecl && FDecl->hasAttr<AnyX86InterruptAttr>()) {
6629-
Diag(Fn->getExprLoc(), diag::err_anyx86_interrupt_called);
6630-
return ExprError();
6628+
if (FDecl) {
6629+
if (FDecl->hasAttr<AnyX86InterruptAttr>()) {
6630+
Diag(Fn->getExprLoc(), diag::err_anyx86_interrupt_called);
6631+
return ExprError();
6632+
}
6633+
if (FDecl->hasAttr<ARMInterruptAttr>()) {
6634+
Diag(Fn->getExprLoc(), diag::err_arm_interrupt_called);
6635+
return ExprError();
6636+
}
66316637
}
66326638

6633-
// Interrupt handlers don't save off the VFP regs automatically on ARM,
6634-
// so there's some risk when calling out to non-interrupt handler functions
6635-
// that the callee might not preserve them. This is easy to diagnose here,
6636-
// but can be very challenging to debug.
6637-
// Likewise, X86 interrupt handlers may only call routines with attribute
6639+
// X86 interrupt handlers may only call routines with attribute
66386640
// no_caller_saved_registers since there is no efficient way to
66396641
// save and restore the non-GPR state.
66406642
if (auto *Caller = getCurFunctionDecl()) {
6641-
if (Caller->hasAttr<ARMInterruptAttr>()) {
6642-
bool VFP = Context.getTargetInfo().hasFeature("vfp");
6643-
if (VFP && (!FDecl || !FDecl->hasAttr<ARMInterruptAttr>())) {
6644-
Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_calling_convention);
6645-
if (FDecl)
6646-
Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl;
6647-
}
6648-
}
66496643
if (Caller->hasAttr<AnyX86InterruptAttr>() ||
66506644
Caller->hasAttr<AnyX86NoCallerSavedRegistersAttr>()) {
66516645
const TargetInfo &TI = Context.getTargetInfo();

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

Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,26 @@
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
1+
// RUN: %clang_cc1 %s -triple arm-none-eabi -verify -fsyntax-only
2+
// RUN: %clang_cc1 %s -triple arm-none-eabi -target-feature +vfp2 -verify -fsyntax-only
63

7-
__attribute__((interrupt(IRQ))) void foo(void) {} // expected-error {{'interrupt' attribute requires a string}}
8-
__attribute__((interrupt("irq"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: irq}}
94

5+
#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}}
7+
#else // !defined(__ARM_FP)
8+
__attribute__((interrupt("irq"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: irq}}
9+
__attribute__((interrupt(IRQ))) void foo(void) {} // expected-error {{'interrupt' attribute requires a string}}
1010
__attribute__((interrupt("IRQ", 1))) void foo2(void) {} // expected-error {{'interrupt' attribute takes no more than 1 argument}}
11-
1211
__attribute__((interrupt("IRQ"))) void foo3(void) {}
1312
__attribute__((interrupt("FIQ"))) void foo4(void) {}
1413
__attribute__((interrupt("SWI"))) void foo5(void) {}
1514
__attribute__((interrupt("ABORT"))) void foo6(void) {}
1615
__attribute__((interrupt("UNDEF"))) void foo7(void) {}
17-
1816
__attribute__((interrupt)) void foo8(void) {}
1917
__attribute__((interrupt())) void foo9(void) {}
2018
__attribute__((interrupt(""))) void foo10(void) {}
2119

22-
#ifndef SOFT
23-
// expected-note@+2 {{'callee1' declared here}}
24-
#endif
25-
void callee1(void);
26-
__attribute__((interrupt("IRQ"))) void callee2(void);
27-
void caller1(void) {
28-
callee1();
29-
callee2();
30-
}
31-
32-
#ifndef SOFT
33-
__attribute__((interrupt("IRQ"))) void caller2(void) {
34-
callee1(); // expected-warning {{call to function without interrupt attribute could clobber interruptee's VFP registers}}
35-
callee2();
36-
}
37-
38-
void (*callee3)(void);
39-
__attribute__((interrupt("IRQ"))) void caller3(void) {
40-
callee3(); // expected-warning {{call to function without interrupt attribute could clobber interruptee's VFP registers}}
41-
}
42-
#else
43-
__attribute__((interrupt("IRQ"))) void caller2(void) {
44-
callee1();
45-
callee2();
46-
}
20+
__attribute__((interrupt("IRQ"))) void callee(void) {}
4721

48-
void (*callee3)(void);
49-
__attribute__((interrupt("IRQ"))) void caller3(void) {
50-
callee3();
22+
void caller(void)
23+
{
24+
callee(); // expected-error {{interrupt service routine cannot be called directly}}
5125
}
52-
#endif
26+
#endif // __ARM_FP

0 commit comments

Comments
 (0)