-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[clang][ARM] Fix warning for using VFP from interrupts. #91870
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-backend-aarch64 @llvm/pr-subscribers-clang Author: Chris Copeland (chrisnc) ChangesThis warning has two issues:
This change addresses both issues. Rather than check that the callee has Full diff: https://github.com/llvm/llvm-project/pull/91870.diff 5 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7c5dcc59c7016..3f9f81bc2bfac 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -352,6 +352,10 @@ Modified Compiler Flags
evaluating to ``true`` and an empty body such as ``while(1);``)
are considered infinite, even when the ``-ffinite-loop`` flag is set.
+- Removed "arm interrupt calling convention" warning that was included in
+ ``-Wextra`` but did not have its own flag. Added
+ ``-Warm-interrupt-vfp-clobber`` that enables the modified warning.
+
Removed Compiler Flags
-------------------------
@@ -484,6 +488,13 @@ Improvements to Clang's diagnostics
}
};
+- On ARM, Clang no longer suggests adding ``__attribute__((interrupt))`` to normal
+ functions that are called from interrupt handlers to prevent clobbering VFP
+ registers as part of ``-Wextra``. Following this suggestion leads to
+ unpredictable behavior. Instead, ``-Warm-interrupt-vfp-clobber`` can now be
+ used to detect calling functions that don't have VFP disabled with
+ ``__attribute((target("soft-float")))`` from an interrupt handler.
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 52552ba488560..04e3b8f949992 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3090,6 +3090,13 @@ def Target : InheritableAttr {
}
}
+ bool hasFeature(StringRef Feature) const {
+ StringRef Features = getFeaturesStr();
+ SmallVector<StringRef, 1> AttrFeatures;
+ Features.split(AttrFeatures, ",");
+ return Features.contains(Feature);
+ }
+
bool isDefaultVersion() const { return getFeaturesStr() == "default"; }
}];
}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d6863f90edb6e..1c5f5ffb03dc5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -336,9 +336,10 @@ def warn_anyx86_excessive_regsave : Warning<
" with attribute 'no_caller_saved_registers'"
" or be compiled with '-mgeneral-regs-only'">,
InGroup<DiagGroup<"excessive-regsave">>;
-def warn_arm_interrupt_calling_convention : Warning<
- "call to function without interrupt attribute could clobber interruptee's VFP registers">,
- InGroup<Extra>;
+def warn_arm_interrupt_vfp_clobber : Warning<
+ "calling a VFP-enabled function from an interrupt could clobber the "
+ "interruptee's VFP registers">,
+ InGroup<DiagGroup<"arm-interrupt-vfp-clobber">>;
def warn_interrupt_attribute_invalid : Warning<
"%select{MIPS|MSP430|RISC-V}0 'interrupt' attribute only applies to "
"functions that have %select{no parameters|a 'void' return type}1">,
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c688cb21f2364..c514820bd899c 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6942,22 +6942,23 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
return ExprError();
}
- // Interrupt handlers don't save off the VFP regs automatically on ARM,
- // so there's some risk when calling out to non-interrupt handler functions
- // that the callee might not preserve them. This is easy to diagnose here,
- // but can be very challenging to debug.
- // Likewise, X86 interrupt handlers may only call routines with attribute
- // no_caller_saved_registers since there is no efficient way to
- // save and restore the non-GPR state.
if (auto *Caller = getCurFunctionDecl()) {
+ // Interrupt handlers don't save volatile VFP registers automatically on
+ // ARM, so calling other functions that use VFP will likely cause the
+ // interruptee's VFP state to be clobbered. This is easy to diagnose here,
+ // but can be very challenging to debug.
if (Caller->hasAttr<ARMInterruptAttr>()) {
bool VFP = Context.getTargetInfo().hasFeature("vfp");
- if (VFP && (!FDecl || !FDecl->hasAttr<ARMInterruptAttr>())) {
- Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_calling_convention);
+ if (VFP && (!FDecl || !FDecl->hasAttr<TargetAttr>() ||
+ !FDecl->getAttr<TargetAttr>()->hasFeature("soft-float"))) {
+ Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_vfp_clobber);
if (FDecl)
Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl;
}
}
+ // X86 interrupt handlers may only call routines with attribute
+ // no_caller_saved_registers since there is no efficient way to
+ // save and restore the non-GPR state.
if (Caller->hasAttr<AnyX86InterruptAttr>() ||
Caller->hasAttr<AnyX86NoCallerSavedRegistersAttr>()) {
const TargetInfo &TI = Context.getTargetInfo();
diff --git a/clang/test/Sema/arm-interrupt-attr.c b/clang/test/Sema/arm-interrupt-attr.c
index 3537fba8521ad..112e5b5e8ad1e 100644
--- a/clang/test/Sema/arm-interrupt-attr.c
+++ b/clang/test/Sema/arm-interrupt-attr.c
@@ -23,7 +23,7 @@ __attribute__((interrupt(""))) void foo10(void) {}
// expected-note@+2 {{'callee1' declared here}}
#endif
void callee1(void);
-__attribute__((interrupt("IRQ"))) void callee2(void);
+__attribute__((target("soft-float"))) void callee2(void);
void caller1(void) {
callee1();
callee2();
@@ -31,13 +31,13 @@ void caller1(void) {
#ifndef SOFT
__attribute__((interrupt("IRQ"))) void caller2(void) {
- callee1(); // expected-warning {{call to function without interrupt attribute could clobber interruptee's VFP registers}}
+ callee1(); // expected-warning {{calling a VFP-enabled function from an interrupt could clobber the interruptee's VFP registers}}
callee2();
}
void (*callee3)(void);
__attribute__((interrupt("IRQ"))) void caller3(void) {
- callee3(); // expected-warning {{call to function without interrupt attribute could clobber interruptee's VFP registers}}
+ callee3(); // expected-warning {{calling a VFP-enabled function from an interrupt could clobber the interruptee's VFP registers}}
}
#else
__attribute__((interrupt("IRQ"))) void caller2(void) {
|
Here is an example of the existing warning in action. https://godbolt.org/z/9e84EfeYP |
ef08eb6
to
b72b63c
Compare
I'd like to also address #47815, but I wasn't able to find the right path to answer "is this M-profile" from |
ping @ostannard @smithp35 |
b72b63c
to
b9aae83
Compare
Rebased and fixed conflict in release notes. |
b9aae83
to
846d22f
Compare
ping (rebased and fixed another release notes conflict) |
Ping |
Ping @DavidSpickett @jthackray |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't fully approve this, I leave that to other members of @llvm/pr-subscribers-arm .
2 overall comments:
- Calling a function marked interrupt from a function marked interrupt is still UB isn't it? Should there be another warning to cover that scenario as well?
- I see that gcc has a warning in
-Wattributes
for this, you should raise a bug there equivalent to the one llvm has, perhaps there even is one already.
Any ARM folks know if there are existing M profile only warnings to learn from? |
Thank you for the review!
Correct. I can create a separate issue to describe such a warning. Calling an interrupt handler from a normal function is also UB for similar reasons; when it returns it will attempt an exception return for an exception that hasn't occurred, so either it's immediately undefined because the caller was already in system/user, or the caller was in supervisor mode from reset, and an exception return will update CPSR to whatever garbage was in SPSR_svc, and also it will return to the calling instruction, rather than one past it.
It does, but It seems that LLVM does not have this target feature for 32-bit Arm, so I opted for |
846d22f
to
a67b670
Compare
I've rebased, updated the warning text and release notes, and created #95359 for the future improvement to warn about calling interrupt handlers. (Edit: also updated the tests to expect the new warning text.) |
16a00f4
to
ef21213
Compare
While doing some more research and comparing |
ef21213
to
9b357d2
Compare
Updated to just warn if the attribute is used while vfp is enabled, and added an error for calling an interrupt handler, similar to x86. |
Somewhat related to this -- we (TI) are presently in the process of upstreaming our mod to save/restore VFP registers with the interrupt_save_fp attribute. See #89654. I think the patch needs to be updated in light of upstream changes to the Arm frame lowering code. |
9b357d2
to
282ab33
Compare
Rebased to fix release notes conflict. @DavidSpickett @llvm/pr-subscribers-arm |
282ab33
to
2b94291
Compare
Rebased again, clarified the release notes changes and added a test case for the new error. |
2b94291
to
75e7fad
Compare
Fixed more release notes conflicts. |
75e7fad
to
2789054
Compare
Fixed another release notes conflict. |
2789054
to
66fef3b
Compare
Everything LGTM but I'd like someone who works on Arm's downstream compiler to approve. @ostannard perhaps? (thanks for your patience @chrisnc) |
handlers to prevent clobbering VFP registers. Following this suggestion leads | ||
to unpredictable behavior by causing multiple exception returns from one | ||
exception. Fixes #GH34876. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to other reviewers: This is in the modified flags section because -wextra itself is being modified. A part of it was removed but that part was never a separate flag so this doesn't need to go in the "removed compiler flags" section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
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.
66fef3b
to
7054e63
Compare
Force-pushed to fix the single-line |
Thanks! |
[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.
@chrisnc Catching up with this: as far as I can tell this is true for A/R profiles, but M profile doesn't have this restriction, because the exception return sequence isn't special. And the plain return we codegen only behaves as an exception return when the LR bits are setup in the special way. So it can return just fine if LR is a regular return address. I read that as saying there's no restriction to calling I see you mentioned the related #47815 :
I think the somewhat-clean way to do that would be to add a bit in |
@ahmedbougacha yes, I think it's correct that calling an |
[clang][ARM] Fix warning for using VFP from interrupts.
This warning has three issues:
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.
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.
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 #34876.
[clang][ARM] Emit an error when an interrupt handler is called.
Closes #95359.