Skip to content

Commit ef21213

Browse files
committed
[clang][ARM] Fix warning for VFP function calls from interrupts.
This warning has two issues: - The interrupt attribute doesn't only change how volatile registers are treated; it also 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 the 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. - 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 both issues. Rather than check that the callee has the interrupt attribute, check that it uses the soft-float feature, which will prevent use of VFP state. The warning is also given its own diagnostic group. Closes #34876.
1 parent 58df646 commit ef21213

File tree

5 files changed

+39
-19
lines changed

5 files changed

+39
-19
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,11 @@ Modified Compiler Flags
409409
evaluating to ``true`` and an empty body such as ``while(1);``)
410410
are considered infinite, even when the ``-ffinite-loop`` flag is set.
411411

412+
- Removed "arm interrupt calling convention" warning that was included in
413+
``-Wextra`` without its own flag.
414+
415+
- Added ``-Warm-interrupt-vfp-clobber``, with its own warning group.
416+
412417
Removed Compiler Flags
413418
-------------------------
414419

@@ -569,6 +574,12 @@ Improvements to Clang's diagnostics
569574
- Clang no longer emits a "declared here" note for a builtin function that has no declaration in source.
570575
Fixes #GH93369.
571576

577+
- For the ARM target, Clang no longer suggests adding ``__attribute__((interrupt))`` to
578+
functions that are called from interrupt handlers to prevent clobbering VFP registers
579+
as part of ``-Wextra`` (#GH34876). Following this suggestion leads to unpredictable
580+
behavior. Instead, a new warning, ``-Warm-interrupt-vfp-clobber`` will detect cases
581+
where calling a function from an interrupt handler may clobber VFP state.
582+
572583
Improvements to Clang's time-trace
573584
----------------------------------
574585

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3121,6 +3121,13 @@ def Target : InheritableAttr {
31213121
}
31223122
}
31233123

3124+
bool hasFeature(StringRef Feature) const {
3125+
StringRef Features = getFeaturesStr();
3126+
SmallVector<StringRef, 1> AttrFeatures;
3127+
Features.split(AttrFeatures, ",");
3128+
return Features.contains(Feature);
3129+
}
3130+
31243131
bool isDefaultVersion() const { return getFeaturesStr() == "default"; }
31253132
}];
31263133
}

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,10 @@ 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+
"calling a function from an interrupt handler could clobber the "
341+
"interruptee's VFP registers if the callee also uses VFP">,
342+
InGroup<DiagGroup<"arm-interrupt-vfp-clobber">>;
342343
def warn_interrupt_attribute_invalid : Warning<
343344
"%select{MIPS|MSP430|RISC-V}0 'interrupt' attribute only applies to "
344345
"functions that have %select{no parameters|a 'void' return type}1">,

clang/lib/Sema/SemaExpr.cpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6760,22 +6760,23 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
67606760
return ExprError();
67616761
}
67626762

6763-
// Interrupt handlers don't save off the VFP regs automatically on ARM,
6764-
// so there's some risk when calling out to non-interrupt handler functions
6765-
// that the callee might not preserve them. This is easy to diagnose here,
6766-
// but can be very challenging to debug.
6767-
// Likewise, X86 interrupt handlers may only call routines with attribute
6768-
// no_caller_saved_registers since there is no efficient way to
6769-
// save and restore the non-GPR state.
67706763
if (auto *Caller = getCurFunctionDecl()) {
6764+
// Interrupt handlers don't save volatile VFP registers automatically on
6765+
// ARM, so calling other functions that use VFP will likely cause the
6766+
// interruptee's VFP state to be clobbered. This is easy to diagnose here,
6767+
// but can be very challenging to debug.
67716768
if (Caller->hasAttr<ARMInterruptAttr>()) {
67726769
bool VFP = Context.getTargetInfo().hasFeature("vfp");
6773-
if (VFP && (!FDecl || !FDecl->hasAttr<ARMInterruptAttr>())) {
6774-
Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_calling_convention);
6770+
if (VFP && (!FDecl || !FDecl->hasAttr<TargetAttr>() ||
6771+
!FDecl->getAttr<TargetAttr>()->hasFeature("soft-float"))) {
6772+
Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_vfp_clobber);
67756773
if (FDecl)
67766774
Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl;
67776775
}
67786776
}
6777+
// X86 interrupt handlers may only call routines with attribute
6778+
// no_caller_saved_registers since there is no efficient way to
6779+
// save and restore the non-GPR state.
67796780
if (Caller->hasAttr<AnyX86InterruptAttr>() ||
67806781
Caller->hasAttr<AnyX86NoCallerSavedRegistersAttr>()) {
67816782
const TargetInfo &TI = Context.getTargetInfo();

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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
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
55
// RUN: %clang_cc1 %s -triple thumbeb-none-eabi -target-feature +neon -target-feature +soft-float -DSOFT -verify -fsyntax-only
66

77
__attribute__((interrupt(IRQ))) void foo(void) {} // expected-error {{'interrupt' attribute requires a string}}
@@ -23,21 +23,21 @@ __attribute__((interrupt(""))) void foo10(void) {}
2323
// expected-note@+2 {{'callee1' declared here}}
2424
#endif
2525
void callee1(void);
26-
__attribute__((interrupt("IRQ"))) void callee2(void);
26+
__attribute__((target("soft-float"))) void callee2(void);
2727
void caller1(void) {
2828
callee1();
2929
callee2();
3030
}
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 {{calling a function from an interrupt handler could clobber the interruptee's VFP registers if the callee also uses VFP}}
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 {{calling a function from an interrupt handler could clobber the interruptee's VFP registers if the callee also uses VFP}}
4141
}
4242
#else
4343
__attribute__((interrupt("IRQ"))) void caller2(void) {

0 commit comments

Comments
 (0)