Functions with interrupt or no_caller_saved_registers attribute should have different function pointer types from those without the attribute since they require different prologue and epilogue. 2 functions with different interrupt or no_caller_saved_registers attribute, which are otherwise equivalent, aren't identical.
In case of 2 identical functions with interrupt attribute, we issue an error after the ICF merge to detect duplicated interrupt handlers to reduce code size which is very important in embedded environment. The alternatives are 1. Disable the ICF merge, which leads to duplicated codes. 2. Allow the ICF merge and convert the ICF tail call to a jump, which requires codegen changes in prologue and epilogue of interrupt handlers as well as an extra jump. OK for trunk if there is no regression on i686 and x86-64? H.J. --- gcc/ PR target/78098 * config/i386/i386.c (ix86_comp_type_attributes): Return 0 if interrupt or no_caller_saved_registers attribute is different. (ix86_expand_call): Issue a note when interrupt handler is merged. (ix86_attribute_table): Change the affects_type_identity field to true for interrupt and no_caller_saved_registers attributes. gcc/testsuite/ PR target/78098 * gcc.target/i386/interrupt-1.c: Updated. * gcc.target/i386/interrupt-6.c: Likewise. * gcc.target/i386/interrupt-7.c: Likewise. * gcc.target/i386/interrupt-16.c: Likewise. * gcc.target/i386/interrupt-17.c: Likewise. * gcc.target/i386/interrupt-iamcu.c: Likewise. * gcc.target/i386/interrupt-sibcall-1.c: Likewise. * gcc.target/i386/interrupt-sibcall-2.c: Likewise. * gcc.target/i386/pr78098-1.c: New test. * gcc.target/i386/pr78098-2.c: Likewise. * gcc.target/i386/pr78098-3.c: Likewise. * gcc.target/i386/pr78098-4.c: Likewise. * gcc.target/i386/pr78098-5.c: Likewise. * gcc.target/i386/pr78098-6.c: Likewise. * gcc.target/i386/pr78098-7.c: Likewise. * gcc.target/i386/pr78098-8.c: Likewise. * gcc.target/i386/pr78098-9.c: Likewise. --- gcc/config/i386/i386.c | 30 +++++++++++++++++++--- gcc/testsuite/gcc.target/i386/interrupt-1.c | 1 + gcc/testsuite/gcc.target/i386/interrupt-16.c | 1 + gcc/testsuite/gcc.target/i386/interrupt-17.c | 1 + gcc/testsuite/gcc.target/i386/interrupt-6.c | 3 ++- gcc/testsuite/gcc.target/i386/interrupt-iamcu.c | 1 + .../gcc.target/i386/interrupt-sibcall-1.c | 1 + .../gcc.target/i386/interrupt-sibcall-2.c | 1 + gcc/testsuite/gcc.target/i386/pr78098-1.c | 18 +++++++++++++ gcc/testsuite/gcc.target/i386/pr78098-2.c | 19 ++++++++++++++ gcc/testsuite/gcc.target/i386/pr78098-3.c | 19 ++++++++++++++ gcc/testsuite/gcc.target/i386/pr78098-4.c | 19 ++++++++++++++ gcc/testsuite/gcc.target/i386/pr78098-5.c | 19 ++++++++++++++ gcc/testsuite/gcc.target/i386/pr78098-6.c | 13 ++++++++++ gcc/testsuite/gcc.target/i386/pr78098-7.c | 12 +++++++++ gcc/testsuite/gcc.target/i386/pr78098-8.c | 13 ++++++++++ gcc/testsuite/gcc.target/i386/pr78098-9.c | 12 +++++++++ 17 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/pr78098-1.c create mode 100644 gcc/testsuite/gcc.target/i386/pr78098-2.c create mode 100644 gcc/testsuite/gcc.target/i386/pr78098-3.c create mode 100644 gcc/testsuite/gcc.target/i386/pr78098-4.c create mode 100644 gcc/testsuite/gcc.target/i386/pr78098-5.c create mode 100644 gcc/testsuite/gcc.target/i386/pr78098-6.c create mode 100644 gcc/testsuite/gcc.target/i386/pr78098-7.c create mode 100644 gcc/testsuite/gcc.target/i386/pr78098-8.c create mode 100644 gcc/testsuite/gcc.target/i386/pr78098-9.c diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index f70eb43..2dbabf5 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -7729,6 +7729,18 @@ ix86_comp_type_attributes (const_tree type1, const_tree type2) && TREE_CODE (type1) != METHOD_TYPE) return 1; + if ((lookup_attribute ("interrupt", + TYPE_ATTRIBUTES (type1)) != NULL) + != (lookup_attribute ("interrupt", + TYPE_ATTRIBUTES (type2)) != NULL)) + return 0; + + if ((lookup_attribute ("no_caller_saved_registers", + TYPE_ATTRIBUTES (type1)) != NULL) + != (lookup_attribute ("no_caller_saved_registers", + TYPE_ATTRIBUTES (type2)) != NULL)) + return 0; + ccvt1 = ix86_get_callcvt (type1); ccvt2 = ix86_get_callcvt (type2); if (ccvt1 != ccvt2) @@ -28019,7 +28031,19 @@ ix86_expand_call (rtx retval, rtx fnaddr, rtx callarg1, if (fndecl && (lookup_attribute ("interrupt", TYPE_ATTRIBUTES (TREE_TYPE (fndecl))))) - error ("interrupt service routine can't be called directly"); + { + if (lookup_attribute ("interrupt", + TYPE_ATTRIBUTES (TREE_TYPE (cfun->decl))) + && cgraph_node::get (cfun->decl)->icf_merged) + { + error ("interrupt service routine is merged"); + inform (input_location, + "interrupt service routine %q+D is equivalent to %q+D", + cfun->decl, fndecl); + } + else + error ("interrupt service routine can't be called directly"); + } } else fndecl = NULL_TREE; @@ -44977,9 +45001,9 @@ static const struct attribute_spec ix86_attribute_table[] = { "callee_pop_aggregate_return", 1, 1, false, true, true, ix86_handle_callee_pop_aggregate_return, true }, { "interrupt", 0, 0, false, true, true, - ix86_handle_interrupt_attribute, false }, + ix86_handle_interrupt_attribute, true }, { "no_caller_saved_registers", 0, 0, false, true, true, - ix86_handle_no_caller_saved_registers_attribute, false }, + ix86_handle_no_caller_saved_registers_attribute, true }, /* End element. */ { NULL, 0, 0, false, false, false, NULL, false } diff --git a/gcc/testsuite/gcc.target/i386/interrupt-1.c b/gcc/testsuite/gcc.target/i386/interrupt-1.c index 3dfe3d8..c82ed5c 100644 --- a/gcc/testsuite/gcc.target/i386/interrupt-1.c +++ b/gcc/testsuite/gcc.target/i386/interrupt-1.c @@ -4,6 +4,7 @@ extern void foo (void *) __attribute__ ((interrupt)); extern int bar (int); +__attribute__ ((interrupt)) void foo (void *frame) { int a,b,c,d,e,f,i; diff --git a/gcc/testsuite/gcc.target/i386/interrupt-16.c b/gcc/testsuite/gcc.target/i386/interrupt-16.c index bc929c6..98c3d8e 100644 --- a/gcc/testsuite/gcc.target/i386/interrupt-16.c +++ b/gcc/testsuite/gcc.target/i386/interrupt-16.c @@ -4,6 +4,7 @@ extern int foo (int) __attribute__ ((no_caller_saved_registers)); extern int bar (int) __attribute__ ((no_caller_saved_registers)); +__attribute__ ((no_caller_saved_registers)) int foo (int i) { diff --git a/gcc/testsuite/gcc.target/i386/interrupt-17.c b/gcc/testsuite/gcc.target/i386/interrupt-17.c index 5d5b59e..a2b7556 100644 --- a/gcc/testsuite/gcc.target/i386/interrupt-17.c +++ b/gcc/testsuite/gcc.target/i386/interrupt-17.c @@ -4,6 +4,7 @@ extern int foo (int) __attribute__ ((no_caller_saved_registers)); extern int bar (int) __attribute__ ((no_caller_saved_registers)); + __attribute__ ((no_caller_saved_registers)) int foo (int i) { diff --git a/gcc/testsuite/gcc.target/i386/interrupt-6.c b/gcc/testsuite/gcc.target/i386/interrupt-6.c index 2a0266a..998a834 100644 --- a/gcc/testsuite/gcc.target/i386/interrupt-6.c +++ b/gcc/testsuite/gcc.target/i386/interrupt-6.c @@ -33,8 +33,9 @@ fn4 (uword_t error_code, void *frame) extern int fn5 (void *) __attribute__ ((interrupt)); /* { dg-error "interrupt service routine can't have non-void return value" } */ +__attribute__ ((interrupt)) int fn5 (void *frame) -{ +{ /* { dg-error "interrupt service routine can't have non-void return value" } */ return 0; } diff --git a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c index c2256ef..b8aaa0f 100644 --- a/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c +++ b/gcc/testsuite/gcc.target/i386/interrupt-iamcu.c @@ -4,6 +4,7 @@ extern void foo (void *) __attribute__ ((interrupt)); extern int bar (int); +__attribute__ ((interrupt)) void foo (void *frame) { int a,b,c,d,e,f,i; diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c index e2cda61..564d426 100644 --- a/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c +++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-1.c @@ -4,6 +4,7 @@ extern void foo (void *) __attribute__ ((interrupt)); extern void bar (void); +__attribute__ ((interrupt)) void foo (void *frame) { bar (); diff --git a/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c index f59253a..d8f4b5d 100644 --- a/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c +++ b/gcc/testsuite/gcc.target/i386/interrupt-sibcall-2.c @@ -5,6 +5,7 @@ extern void foo (void *) __attribute__ ((interrupt)); extern void bar (void) __attribute__ ((no_caller_saved_registers)); +__attribute__ ((interrupt)) void foo (void *frame) { bar (); diff --git a/gcc/testsuite/gcc.target/i386/pr78098-1.c b/gcc/testsuite/gcc.target/i386/pr78098-1.c new file mode 100644 index 0000000..2096913 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr78098-1.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mgeneral-regs-only" } */ + +void bar (void *p); + +__attribute__((interrupt)) +void foo1 (void *p) /* { dg-error "interrupt service routine" } */ +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +__attribute__((interrupt)) +void foo2 (void *p) /* { dg-message "note: interrupt service routine" } */ +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} diff --git a/gcc/testsuite/gcc.target/i386/pr78098-2.c b/gcc/testsuite/gcc.target/i386/pr78098-2.c new file mode 100644 index 0000000..faadb80 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr78098-2.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mgeneral-regs-only" } */ + +void bar (void *p); + +void foo1 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +__attribute__((interrupt)) +void foo2 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +/* { dg-final { scan-assembler-times "call\t_?bar" 2 } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr78098-3.c b/gcc/testsuite/gcc.target/i386/pr78098-3.c new file mode 100644 index 0000000..8158ca0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr78098-3.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mgeneral-regs-only" } */ + +void bar (void *p); + +__attribute__((interrupt)) +void foo1 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +void foo2 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +/* { dg-final { scan-assembler-times "call\t_?bar" 2 } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr78098-4.c b/gcc/testsuite/gcc.target/i386/pr78098-4.c new file mode 100644 index 0000000..35016f5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr78098-4.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mgeneral-regs-only" } */ + +void bar (void *p); + +void foo1 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +__attribute__((no_caller_saved_registers)) +void foo2 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +/* { dg-final { scan-assembler-times "call\t_?bar" 2 } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr78098-5.c b/gcc/testsuite/gcc.target/i386/pr78098-5.c new file mode 100644 index 0000000..d4ce241 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr78098-5.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mgeneral-regs-only" } */ + +void bar (void *p); + +__attribute__((no_caller_saved_registers)) +void foo1 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +void foo2 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +/* { dg-final { scan-assembler-times "call\t_?bar" 2 } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr78098-6.c b/gcc/testsuite/gcc.target/i386/pr78098-6.c new file mode 100644 index 0000000..45724f6 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr78098-6.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mgeneral-regs-only" } */ + +void bar (void *p); + +__attribute__((interrupt)) +void foo1 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +void (*foo2) (void *) = foo1; /* { dg-message "incompatible pointer type" } */ diff --git a/gcc/testsuite/gcc.target/i386/pr78098-7.c b/gcc/testsuite/gcc.target/i386/pr78098-7.c new file mode 100644 index 0000000..7343826 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr78098-7.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mgeneral-regs-only" } */ + +void bar (void *p); + +void foo1 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +void (*foo2) (void *) __attribute__((interrupt)) = foo1; /* { dg-message "incompatible pointer type" } */ diff --git a/gcc/testsuite/gcc.target/i386/pr78098-8.c b/gcc/testsuite/gcc.target/i386/pr78098-8.c new file mode 100644 index 0000000..6c2bdd7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr78098-8.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mgeneral-regs-only" } */ + +void bar (void *p); + +__attribute__((no_caller_saved_registers)) +void foo1 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +void (*foo2) (void *) = foo1; /* { dg-message "incompatible pointer type" } */ diff --git a/gcc/testsuite/gcc.target/i386/pr78098-9.c b/gcc/testsuite/gcc.target/i386/pr78098-9.c new file mode 100644 index 0000000..eec5629 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr78098-9.c @@ -0,0 +1,12 @@ +/* { dg-do compile } */ +/* { dg-options "-Os -mgeneral-regs-only" } */ + +void bar (void *p); + +void foo1 (void *p) +{ + bar (p); + *((int *)0xFEE00080L) = 0; +} + +void (*foo2) (void *) __attribute__((no_caller_saved_registers)) = foo1; /* { dg-message "incompatible pointer type" } */ -- 2.7.4