diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cd3a4c2b1be1a..6ca25339d2ab1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -450,6 +450,9 @@ New Compiler Flags that relies on it. Users should carefully consider this possibiilty when using the flag. +- For the ARM target, added ``-Warm-interrupt-vfp-clobber`` that will emit a + diagnostic when an interrupt handler is declared and VFP is enabled. + Deprecated Compiler Flags ------------------------- @@ -492,6 +495,13 @@ Modified Compiler Flags now include dianostics about C++26 features that are not present in older versions. +- Removed the "arm interrupt calling convention" warning that was included in + ``-Wextra`` without its own flag. This warning suggested adding + ``__attribute__((interrupt))`` to functions that are called from interrupt + handlers to prevent clobbering VFP registers. Following this suggestion leads + to unpredictable behavior by causing multiple exception returns from one + exception. Fixes #GH34876. + Removed Compiler Flags ------------------------- @@ -686,6 +696,8 @@ Improvements to Clang's diagnostics - Clang no longer emits a "no previous prototype" warning for Win32 entry points under ``-Wmissing-prototypes``. Fixes #GH94366. +- For the ARM target, calling an interrupt handler from another function is now an error. #GH95359. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index fa02622f12271..940f9ac226ca8 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -336,9 +336,12 @@ def warn_anyx86_excessive_regsave : Warning< " with attribute 'no_caller_saved_registers'" " or be compiled with '-mgeneral-regs-only'">, InGroup>; -def warn_arm_interrupt_calling_convention : Warning< - "call to function without interrupt attribute could clobber interruptee's VFP registers">, - InGroup; +def warn_arm_interrupt_vfp_clobber : Warning< + "interrupt service routine with vfp enabled may clobber the " + "interruptee's vfp state">, + InGroup>; +def err_arm_interrupt_called : Error< + "interrupt service routine cannot be called directly">; 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/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp index 370db341e997e..d8dd4fe16e3af 100644 --- a/clang/lib/Sema/SemaARM.cpp +++ b/clang/lib/Sema/SemaARM.cpp @@ -1329,6 +1329,10 @@ void SemaARM::handleInterruptAttr(Decl *D, const ParsedAttr &AL) { return; } + const TargetInfo &TI = getASTContext().getTargetInfo(); + if (TI.hasFeature("vfp")) + Diag(D->getLocation(), diag::warn_arm_interrupt_vfp_clobber); + D->addAttr(::new (getASTContext()) ARMInterruptAttr(getASTContext(), AL, Kind)); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 45991e66b3e43..63496b327da4d 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6625,27 +6625,21 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0); // Functions with 'interrupt' attribute cannot be called directly. - if (FDecl && FDecl->hasAttr()) { - Diag(Fn->getExprLoc(), diag::err_anyx86_interrupt_called); - return ExprError(); + if (FDecl) { + if (FDecl->hasAttr()) { + Diag(Fn->getExprLoc(), diag::err_anyx86_interrupt_called); + return ExprError(); + } + if (FDecl->hasAttr()) { + Diag(Fn->getExprLoc(), diag::err_arm_interrupt_called); + 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 + // 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()) { - if (Caller->hasAttr()) { - bool VFP = Context.getTargetInfo().hasFeature("vfp"); - if (VFP && (!FDecl || !FDecl->hasAttr())) { - Diag(Fn->getExprLoc(), diag::warn_arm_interrupt_calling_convention); - if (FDecl) - Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl; - } - } if (Caller->hasAttr() || Caller->hasAttr()) { 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..f2698eedacea1 100644 --- a/clang/test/Sema/arm-interrupt-attr.c +++ b/clang/test/Sema/arm-interrupt-attr.c @@ -1,52 +1,26 @@ -// RUN: %clang_cc1 %s -triple arm-apple-darwin -target-feature +vfp2 -verify -fsyntax-only -// RUN: %clang_cc1 %s -triple thumb-apple-darwin -target-feature +vfp3 -verify -fsyntax-only -// RUN: %clang_cc1 %s -triple armeb-none-eabi -target-feature +vfp4 -verify -fsyntax-only -// RUN: %clang_cc1 %s -triple thumbeb-none-eabi -target-feature +neon -verify -fsyntax-only -// RUN: %clang_cc1 %s -triple thumbeb-none-eabi -target-feature +neon -target-feature +soft-float -DSOFT -verify -fsyntax-only +// RUN: %clang_cc1 %s -triple arm-none-eabi -verify -fsyntax-only +// RUN: %clang_cc1 %s -triple arm-none-eabi -target-feature +vfp2 -verify -fsyntax-only -__attribute__((interrupt(IRQ))) void foo(void) {} // expected-error {{'interrupt' attribute requires a string}} -__attribute__((interrupt("irq"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: irq}} +#ifdef __ARM_FP +__attribute__((interrupt("IRQ"))) void float_irq(void); // expected-warning {{interrupt service routine with vfp enabled may clobber the interruptee's vfp state}} +#else // !defined(__ARM_FP) +__attribute__((interrupt("irq"))) void foo1(void) {} // expected-warning {{'interrupt' attribute argument not supported: irq}} +__attribute__((interrupt(IRQ))) void foo(void) {} // expected-error {{'interrupt' attribute requires a string}} __attribute__((interrupt("IRQ", 1))) void foo2(void) {} // expected-error {{'interrupt' attribute takes no more than 1 argument}} - __attribute__((interrupt("IRQ"))) void foo3(void) {} __attribute__((interrupt("FIQ"))) void foo4(void) {} __attribute__((interrupt("SWI"))) void foo5(void) {} __attribute__((interrupt("ABORT"))) void foo6(void) {} __attribute__((interrupt("UNDEF"))) void foo7(void) {} - __attribute__((interrupt)) void foo8(void) {} __attribute__((interrupt())) void foo9(void) {} __attribute__((interrupt(""))) void foo10(void) {} -#ifndef SOFT -// expected-note@+2 {{'callee1' declared here}} -#endif -void callee1(void); -__attribute__((interrupt("IRQ"))) void callee2(void); -void caller1(void) { - callee1(); - callee2(); -} - -#ifndef SOFT -__attribute__((interrupt("IRQ"))) void caller2(void) { - callee1(); // expected-warning {{call to function without interrupt attribute could clobber 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}} -} -#else -__attribute__((interrupt("IRQ"))) void caller2(void) { - callee1(); - callee2(); -} +__attribute__((interrupt("IRQ"))) void callee(void) {} -void (*callee3)(void); -__attribute__((interrupt("IRQ"))) void caller3(void) { - callee3(); +void caller(void) +{ + callee(); // expected-error {{interrupt service routine cannot be called directly}} } -#endif +#endif // __ARM_FP