Skip to content

Commit 846d22f

Browse files
committed
[ARM][clang] 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 1de6011 commit 846d22f

File tree

5 files changed

+40
-19
lines changed

5 files changed

+40
-19
lines changed

clang/docs/ReleaseNotes.rst

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

387+
- Removed "arm interrupt calling convention" warning that was included in
388+
``-Wextra`` but did not have its own flag. Added
389+
``-Warm-interrupt-vfp-clobber`` that enables the modified warning.
390+
387391
Removed Compiler Flags
388392
-------------------------
389393

@@ -544,6 +548,14 @@ Improvements to Clang's diagnostics
544548
- Clang no longer emits a "declared here" note for a builtin function that has no declaration in source.
545549
Fixes #GH93369.
546550

551+
- On ARM, Clang no longer suggests adding ``__attribute__((interrupt))`` to
552+
normal functions that are called from interrupt handlers to prevent
553+
clobbering VFP registers as part of ``-Wextra`` (#GH34876). Following this
554+
suggestion leads to unpredictable behavior. Instead,
555+
``-Warm-interrupt-vfp-clobber`` can now be used to detect calling functions
556+
that don't have VFP disabled with ``__attribute__((target("soft-float")))``
557+
from an interrupt handler.
558+
547559
Improvements to Clang's time-trace
548560
----------------------------------
549561

clang/include/clang/Basic/Attr.td

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

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

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 VFP-enabled function from an interrupt could clobber the "
341+
"interruptee's VFP registers">,
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
@@ -6754,22 +6754,23 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
67546754
return ExprError();
67556755
}
67566756

6757-
// Interrupt handlers don't save off the VFP regs automatically on ARM,
6758-
// so there's some risk when calling out to non-interrupt handler functions
6759-
// that the callee might not preserve them. This is easy to diagnose here,
6760-
// but can be very challenging to debug.
6761-
// Likewise, X86 interrupt handlers may only call routines with attribute
6762-
// no_caller_saved_registers since there is no efficient way to
6763-
// save and restore the non-GPR state.
67646757
if (auto *Caller = getCurFunctionDecl()) {
6758+
// Interrupt handlers don't save volatile VFP registers automatically on
6759+
// ARM, so calling other functions that use VFP will likely cause the
6760+
// interruptee's VFP state to be clobbered. This is easy to diagnose here,
6761+
// but can be very challenging to debug.
67656762
if (Caller->hasAttr<ARMInterruptAttr>()) {
67666763
bool VFP = Context.getTargetInfo().hasFeature("vfp");
6767-
if (VFP && (!FDecl || !FDecl->hasAttr<ARMInterruptAttr>())) {
6768-
Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_calling_convention);
6764+
if (VFP && (!FDecl || !FDecl->hasAttr<TargetAttr>() ||
6765+
!FDecl->getAttr<TargetAttr>()->hasFeature("soft-float"))) {
6766+
Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_vfp_clobber);
67696767
if (FDecl)
67706768
Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl;
67716769
}
67726770
}
6771+
// X86 interrupt handlers may only call routines with attribute
6772+
// no_caller_saved_registers since there is no efficient way to
6773+
// save and restore the non-GPR state.
67736774
if (Caller->hasAttr<AnyX86InterruptAttr>() ||
67746775
Caller->hasAttr<AnyX86NoCallerSavedRegistersAttr>()) {
67756776
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 VFP-enabled function from an interrupt could clobber the interruptee's VFP registers}}
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 VFP-enabled function from an interrupt could clobber the interruptee's VFP registers}}
4141
}
4242
#else
4343
__attribute__((interrupt("IRQ"))) void caller2(void) {

0 commit comments

Comments
 (0)