Skip to content

Commit b9aae83

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 ca478bc commit b9aae83

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
@@ -379,6 +379,10 @@ Modified Compiler Flags
379379
evaluating to ``true`` and an empty body such as ``while(1);``)
380380
are considered infinite, even when the ``-ffinite-loop`` flag is set.
381381

382+
- Removed "arm interrupt calling convention" warning that was included in
383+
``-Wextra`` but did not have its own flag. Added
384+
``-Warm-interrupt-vfp-clobber`` that enables the modified warning.
385+
382386
Removed Compiler Flags
383387
-------------------------
384388

@@ -518,6 +522,13 @@ Improvements to Clang's diagnostics
518522
- Clang emits a ``-Wparentheses`` warning for expressions with consecutive comparisons like ``x < y < z``.
519523
Fixes #GH20456.
520524

525+
- On ARM, Clang no longer suggests adding ``__attribute__((interrupt))`` to
526+
normal functions that are called from interrupt handlers to prevent
527+
clobbering VFP registers as part of ``-Wextra``. Following this suggestion
528+
leads to unpredictable behavior. Instead, ``-Warm-interrupt-vfp-clobber`` can
529+
now be used to detect calling functions that don't have VFP disabled with
530+
``__attribute__((target("soft-float")))`` from an interrupt handler.
531+
521532
Improvements to Clang's time-trace
522533
----------------------------------
523534

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3118,6 +3118,13 @@ def Target : InheritableAttr {
31183118
}
31193119
}
31203120

3121+
bool hasFeature(StringRef Feature) const {
3122+
StringRef Features = getFeaturesStr();
3123+
SmallVector<StringRef, 1> AttrFeatures;
3124+
Features.split(AttrFeatures, ",");
3125+
return Features.contains(Feature);
3126+
}
3127+
31213128
bool isDefaultVersion() const { return getFeaturesStr() == "default"; }
31223129
}];
31233130
}

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
@@ -6743,22 +6743,23 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
67436743
return ExprError();
67446744
}
67456745

6746-
// Interrupt handlers don't save off the VFP regs automatically on ARM,
6747-
// so there's some risk when calling out to non-interrupt handler functions
6748-
// that the callee might not preserve them. This is easy to diagnose here,
6749-
// but can be very challenging to debug.
6750-
// Likewise, X86 interrupt handlers may only call routines with attribute
6751-
// no_caller_saved_registers since there is no efficient way to
6752-
// save and restore the non-GPR state.
67536746
if (auto *Caller = getCurFunctionDecl()) {
6747+
// Interrupt handlers don't save volatile VFP registers automatically on
6748+
// ARM, so calling other functions that use VFP will likely cause the
6749+
// interruptee's VFP state to be clobbered. This is easy to diagnose here,
6750+
// but can be very challenging to debug.
67546751
if (Caller->hasAttr<ARMInterruptAttr>()) {
67556752
bool VFP = Context.getTargetInfo().hasFeature("vfp");
6756-
if (VFP && (!FDecl || !FDecl->hasAttr<ARMInterruptAttr>())) {
6757-
Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_calling_convention);
6753+
if (VFP && (!FDecl || !FDecl->hasAttr<TargetAttr>() ||
6754+
!FDecl->getAttr<TargetAttr>()->hasFeature("soft-float"))) {
6755+
Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_vfp_clobber);
67586756
if (FDecl)
67596757
Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl;
67606758
}
67616759
}
6760+
// X86 interrupt handlers may only call routines with attribute
6761+
// no_caller_saved_registers since there is no efficient way to
6762+
// save and restore the non-GPR state.
67626763
if (Caller->hasAttr<AnyX86InterruptAttr>() ||
67636764
Caller->hasAttr<AnyX86NoCallerSavedRegistersAttr>()) {
67646765
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)