Skip to content

Commit fa1dc06

Browse files
antangelophoebewang
authored andcommitted
[clang][X86] Update excessive register save diagnostic to more closely follow the interrupt attribute spec
The original diagnostic does not cover all cases according to my reading of the spec. For the interrupt attribute, the spec indicates that if the compiler does not support saving SSE, MMX, or x87 then the function should be compiled with '-mgeneral-regs-only' (GCC requires this). Alternatively, calling functions with the `no_caller_saved_registers` attribute will not clobber state and can be done without disabling these features. The warning as implemented in upstream only detects the latter case but does not consider that disabling the above features also solves the issue of these register saves being undesirable due to inefficiency. For the no_caller_saved_registers attribute, the interrupt spec also indicates that in the absence of saving SSE, MMX and x87 state, these functions should be compiled with '-mgeneral-regs-only' (also required by GCC). It does not make any statements about calling other functions with the attribute, but by extension the result is the same as with the interrupt attribute (in clang, at least). This patch handles the remaining cases by adjusting the diagnostic to: 1. Not be shown if the function is compiled without the SSE, MMX or x87 features enabled (i.e. with '-mgeneral-regs-only') 2. Also be shown for functions with the 'no_caller_saved_registers' attribute 3. In addition to advising that the function should only call functions with the `no_caller_saved_registers` attribute, the text also suggests compiling with `-mgeneral-regs-only` as an alternative. The interrupt spec is available at https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=5ed3cc7b66af4758f7849ed6f65f4365be8223be and was taken from the issue that resulted in this diagnostic being added (llvm#26787) Reviewed By: pengfei Differential Revision: https://reviews.llvm.org/D159068
1 parent b8df24e commit fa1dc06

File tree

6 files changed

+62
-26
lines changed

6 files changed

+62
-26
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,18 @@ Modified Compiler Flags
122122
-----------------------
123123

124124
* ``-Woverriding-t-option`` is renamed to ``-Woverriding-option``.
125+
* ``-Winterrupt-service-routine`` is renamed to ``-Wexcessive-regsave`` as a generalization
125126

126127
Removed Compiler Flags
127128
-------------------------
128129

129130
Attribute Changes in Clang
130131
--------------------------
132+
- On X86, a warning is now emitted if a function with ``__attribute__((no_caller_saved_registers))``
133+
calls a function without ``__attribute__((no_caller_saved_registers))``, and is not compiled with
134+
``-mgeneral-regs-only``
135+
- On X86, a function with ``__attribute__((interrupt))`` can now call a function without
136+
``__attribute__((no_caller_saved_registers))`` provided that it is compiled with ``-mgeneral-regs-only``
131137

132138
- When a non-variadic function is decorated with the ``format`` attribute,
133139
Clang now checks that the format string would match the function's parameters'

clang/include/clang/Basic/AttrDocs.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4815,6 +4815,10 @@ The user can call functions specified with the 'no_caller_saved_registers'
48154815
attribute from an interrupt handler without saving and restoring all
48164816
call-clobbered registers.
48174817

4818+
Functions specified with the 'no_caller_saved_registers' attribute should only
4819+
call other functions with the 'no_caller_saved_registers' attribute, or should be
4820+
compiled with the '-mgeneral-regs-only' flag to avoid saving unused non-GPR registers.
4821+
48184822
Note that 'no_caller_saved_registers' attribute is not a calling convention.
48194823
In fact, it only overrides the decision of which registers should be saved by
48204824
the caller, but not how the parameters are passed from the caller to the callee.

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,12 @@ def err_anyx86_interrupt_attribute : Error<
310310
"a pointer as the first parameter|a %2 type as the second parameter}1">;
311311
def err_anyx86_interrupt_called : Error<
312312
"interrupt service routine cannot be called directly">;
313-
def warn_anyx86_interrupt_regsave : Warning<
314-
"interrupt service routine should only call a function"
315-
" with attribute 'no_caller_saved_registers'">,
316-
InGroup<DiagGroup<"interrupt-service-routine">>;
313+
def warn_anyx86_excessive_regsave : Warning<
314+
"%select{interrupt service routine|function with attribute"
315+
" 'no_caller_saved_registers'}0 should only call a function"
316+
" with attribute 'no_caller_saved_registers'"
317+
" or be compiled with '-mgeneral-regs-only'">,
318+
InGroup<DiagGroup<"excessive-regsave">>;
317319
def warn_arm_interrupt_calling_convention : Warning<
318320
"call to function without interrupt attribute could clobber interruptee's VFP registers">,
319321
InGroup<Extra>;

clang/lib/Sema/SemaExpr.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7413,11 +7413,18 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
74137413
Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl;
74147414
}
74157415
}
7416-
if (Caller->hasAttr<AnyX86InterruptAttr>() &&
7417-
((!FDecl || !FDecl->hasAttr<AnyX86NoCallerSavedRegistersAttr>()))) {
7418-
Diag(Fn->getExprLoc(), diag::warn_anyx86_interrupt_regsave);
7419-
if (FDecl)
7420-
Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl;
7416+
if (Caller->hasAttr<AnyX86InterruptAttr>() ||
7417+
Caller->hasAttr<AnyX86NoCallerSavedRegistersAttr>()) {
7418+
const TargetInfo &TI = Context.getTargetInfo();
7419+
bool HasNonGPRRegisters =
7420+
TI.hasFeature("sse") || TI.hasFeature("x87") || TI.hasFeature("mmx");
7421+
if (HasNonGPRRegisters &&
7422+
(!FDecl || !FDecl->hasAttr<AnyX86NoCallerSavedRegistersAttr>())) {
7423+
Diag(Fn->getExprLoc(), diag::warn_anyx86_excessive_regsave)
7424+
<< (Caller->hasAttr<AnyX86InterruptAttr>() ? 0 : 1);
7425+
if (FDecl)
7426+
Diag(FDecl->getLocation(), diag::note_callee_decl) << FDecl;
7427+
}
74217428
}
74227429
}
74237430

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

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,6 @@ __attribute__((interrupt)) void foo6(float *a, int b) {}
4040
__attribute__((interrupt)) void foo7(int *a, unsigned b) {}
4141
__attribute__((interrupt)) void foo8(int *a) {}
4242

43-
#ifdef _LP64
44-
typedef unsigned long Arg2Type;
45-
#elif defined(__x86_64__)
46-
typedef unsigned long long Arg2Type;
47-
#else
48-
typedef unsigned int Arg2Type;
49-
#endif
50-
#ifndef NOCALLERSAVE
51-
__attribute__((no_caller_saved_registers))
52-
#else
53-
// expected-note@+3 {{'foo9' declared here}}
54-
// expected-warning@+4 {{interrupt service routine should only call a function with attribute 'no_caller_saved_registers'}}
55-
#endif
56-
void foo9(int *a, Arg2Type b) {}
57-
__attribute__((interrupt)) void fooA(int *a, Arg2Type b) {
58-
foo9(a, b);
59-
}
6043
void g(void (*fp)(int *));
6144
int main(int argc, char **argv) {
6245
void *ptr = (void *)&foo7;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify %s
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fsyntax-only -verify %s -DNO_CALLER_SAVED_REGISTERS=1
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-feature -x87 -target-feature -mmx -target-feature -sse -fsyntax-only -verify %s -DGPRONLY=1
4+
5+
#if defined(NO_CALLER_SAVED_REGS) || defined(GPRONLY)
6+
// expected-no-diagnostics
7+
#else
8+
#define EXPECT_WARNING
9+
#endif
10+
11+
#ifdef NO_CALLER_SAVED_REGS
12+
__attribute__((no_caller_saved_registers))
13+
#endif
14+
#ifdef EXPECT_WARNING
15+
// expected-note@+3 {{'foo' declared here}}
16+
// expected-note@+2 {{'foo' declared here}}
17+
#endif
18+
extern void foo(void *);
19+
20+
__attribute__((no_caller_saved_registers))
21+
void no_caller_saved_registers(void *arg) {
22+
#ifdef EXPECT_WARNING
23+
// expected-warning@+2 {{function with attribute 'no_caller_saved_registers' should only call a function with attribute 'no_caller_saved_registers' or be compiled with '-mgeneral-regs-only'}}
24+
#endif
25+
foo(arg);
26+
}
27+
28+
__attribute__((interrupt))
29+
void interrupt(void *arg) {
30+
#ifdef EXPECT_WARNING
31+
// expected-warning@+2 {{interrupt service routine should only call a function with attribute 'no_caller_saved_registers' or be compiled with '-mgeneral-regs-only'}}
32+
#endif
33+
foo(arg);
34+
}

0 commit comments

Comments
 (0)