diff --git a/gcc/config/avr/avr-c.cc b/gcc/config/avr/avr-c.cc index 5e7f759ed73..ca484f26132 100644 --- a/gcc/config/avr/avr-c.cc +++ b/gcc/config/avr/avr-c.cc @@ -391,6 +391,10 @@ avr_cpu_cpp_builtins (struct cpp_reader *pfile) cpp_define (pfile, "__WITH_AVRLIBC__"); #endif /* WITH_AVRLIBC */ + // We support __attribute__((signal/interrupt (n1, n2, ...)[, noblock])) + cpp_define (pfile, "__HAVE_SIGNAL_N__"); + + // From configure --with-libf7={|libgcc|math|math-symbols|yes|no} #ifdef WITH_LIBF7_LIBGCC diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h index 5fdb1305757..7b666f17718 100644 --- a/gcc/config/avr/avr-protos.h +++ b/gcc/config/avr/avr-protos.h @@ -35,6 +35,7 @@ extern void avr_init_expanders (void); #ifdef TREE_CODE extern void avr_asm_output_aligned_decl_common (FILE*, tree, const char*, unsigned HOST_WIDE_INT, unsigned int, bool); extern void avr_asm_asm_output_aligned_bss (FILE *, tree, const char *, unsigned HOST_WIDE_INT, int, void (*) (FILE *, tree, const char *, unsigned HOST_WIDE_INT, int)); +extern void avr_declare_function_name (FILE *, const char *, tree); extern void asm_output_external (FILE *file, tree decl, char *name); extern int avr_progmem_p (tree decl, tree attributes); extern bool avr_addr_space_supported_p (addr_space_t, location_t loc = UNKNOWN_LOCATION); diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index e941730452e..dffb7e056be 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -1356,6 +1356,33 @@ avr_lookup_function_attribute1 (const_tree func, const char *name) return NULL_TREE != lookup_attribute (name, TYPE_ATTRIBUTES (func)); } + +/* Call WORKER on all NAME attributes of function FUNC. */ + +static void +avr_foreach_function_attribute (tree func, const char *name, + void (*worker) (tree, tree, void *), + void *cookie) +{ + tree attrs = NULL_TREE; + + if (TREE_CODE (func) == FUNCTION_DECL) + attrs = DECL_ATTRIBUTES (func); + else if (FUNC_OR_METHOD_TYPE_P (func)) + attrs = TYPE_ATTRIBUTES (TREE_TYPE (func)); + + while (attrs) + { + attrs = lookup_attribute (name, attrs); + if (attrs) + { + worker (func, attrs, cookie); + attrs = TREE_CHAIN (attrs); + } + } +} + + /* Return nonzero if FUNC is a naked function. */ static bool @@ -1364,22 +1391,56 @@ avr_naked_function_p (tree func) return avr_lookup_function_attribute1 (func, "naked"); } -/* Return nonzero if FUNC is an interrupt function as specified - by the "interrupt" attribute. */ +/* Return nonzero if FUNC is a noblock function. */ static bool -avr_interrupt_function_p (tree func) +avr_noblock_function_p (tree func) { - return avr_lookup_function_attribute1 (func, "interrupt"); + return avr_lookup_function_attribute1 (func, "noblock"); } -/* Return nonzero if FUNC is a signal function as specified - by the "signal" attribute. */ +/* Return 1 if FUNC is a function that has a "ATTR_NAME" attribute + (and perhaps also "ATTR_NAME(num)" attributes. Return -1 if FUNC has + "ATTR_NAME(num)" attribute(s) but no "ATTR_NAME" attribute. + When no form of ATTR_NAME is present, return 0. */ -static bool -avr_signal_function_p (tree func) +static int +avr_interrupt_signal_function (tree func, const char *attr_name) { - return avr_lookup_function_attribute1 (func, "signal"); + int res = 0; + + avr_foreach_function_attribute (func, attr_name, + [] (tree, tree attr, void *cookie) + { + int *pcook = (int *) cookie; + + *pcook = TREE_VALUE (attr) + ? *pcook ? *pcook : -1 + : 1; + }, &res); + + return res; +} + + +/* Return 1 if FUNC is an interrupt function that has an "interrupt" attribute + (and perhaps also "interrupt(num)" attributes. Return -1 if FUNC has + "interrupt(num)" attribute(s) but no "interrupt" attribute. */ + +static int +avr_interrupt_function (tree func) +{ + return avr_interrupt_signal_function (func, "interrupt"); +} + +/* Return 1 if FUNC is a signal function that has a "signal" attribute + (and perhaps also "signal(num)" attributes. Return -1 if FUNC has + "signal(num)" attribute(s) but no "signal" attribute. */ + +static int +avr_signal_function (tree func) +{ + return avr_interrupt_signal_function (func, "signal"); } /* Return nonzero if FUNC is an OS_task function. */ @@ -1437,8 +1498,9 @@ avr_set_current_function (tree decl) location_t loc = DECL_SOURCE_LOCATION (decl); cfun->machine->is_naked = avr_naked_function_p (decl); - cfun->machine->is_signal = avr_signal_function_p (decl); - cfun->machine->is_interrupt = avr_interrupt_function_p (decl); + cfun->machine->is_signal = avr_signal_function (decl); + cfun->machine->is_interrupt = avr_interrupt_function (decl); + cfun->machine->is_noblock = avr_noblock_function_p (decl); cfun->machine->is_OS_task = avr_OS_task_function_p (decl); cfun->machine->is_OS_main = avr_OS_main_function_p (decl); cfun->machine->is_no_gccisr = avr_no_gccisr_function_p (decl); @@ -1475,27 +1537,34 @@ avr_set_current_function (tree decl) /* Interrupt handlers must be void __vector (void) functions. */ if (args && TREE_CODE (TREE_VALUE (args)) != VOID_TYPE) - error_at (loc, "%qs function cannot have arguments", isr); + { + error_at (loc, "%qs function cannot have arguments", isr); + if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE) + inform (loc, "method %qs has an inplicit % argument", name); + } if (TREE_CODE (ret) != VOID_TYPE) error_at (loc, "%qs function cannot return a value", isr); #if defined WITH_AVRLIBC - /* Silently ignore 'signal' if 'interrupt' is present. AVR-LibC startet - using this when it switched from SIGNAL and INTERRUPT to ISR. */ - - if (cfun->machine->is_interrupt) - cfun->machine->is_signal = 0; - /* If the function has the 'signal' or 'interrupt' attribute, ensure that the name of the function is "__vector_NN" so as to catch - when the user misspells the vector name. */ + when the user misspells the vector name. This check is only + required when the "interrupt" resp. "signal" attribute does not + have an IRQ-number argument. */ - if (!startswith (name, "__vector")) + if (!startswith (name, "__vector") + && (cfun->machine->is_interrupt == 1 + || cfun->machine->is_signal == 1)) warning_at (loc, OPT_Wmisspelled_isr, "%qs appears to be a misspelled " "%qs handler, missing %<__vector%> prefix", name, isr); #endif // AVR-LibC naming conventions } + else if (cfun->machine->is_noblock) + { + warning (OPT_Wattributes, "%qs attribute ignored on non-ISR function", + "noblock"); + } #if defined WITH_AVRLIBC // Common problem is using "ISR" without first including avr/interrupt.h. @@ -2793,11 +2862,14 @@ avr_prologue_setup_frame (HOST_WIDE_INT size, HARD_REG_SET set) Always move through unspec, see PR50063. For meaning of irq_state see movhi_sp_r insn. */ - if (cfun->machine->is_interrupt) + if (cfun->machine->is_interrupt + || (cfun->machine->is_signal + && cfun->machine->is_noblock)) irq_state = 1; if (TARGET_NO_INTERRUPTS - || cfun->machine->is_signal + || (cfun->machine->is_signal + && ! cfun->machine->is_noblock) || cfun->machine->is_OS_main) irq_state = 0; @@ -2892,7 +2964,8 @@ avr_expand_prologue (void) { int treg = AVR_TMP_REGNO; /* Enable interrupts. */ - if (cfun->machine->is_interrupt) + if (cfun->machine->is_interrupt + || cfun->machine->is_noblock) emit_insn (gen_enable_interrupt ()); if (cfun->machine->gasisr.maybe) @@ -2976,6 +3049,68 @@ avr_expand_prologue (void) } +/* Turn TVAL into an integer that represents an ISR number. When no such + conversion is possible, then return 0. Unfortunately, we don't know + how many IRQs the device actually has. */ + +static int +avr_isr_number (tree tval) +{ + return (TREE_CODE (tval) == INTEGER_CST + && tree_fits_shwi_p (tval) + && tree_to_shwi (tval) > 0) + ? (int) tree_to_shwi (tval) + : 0; +} + + +struct avr_fun_cookie +{ + FILE *file; + const char *name; +}; + +/* A helper for `avr_declare_function_name' below. When the function has + attributes like signal(N) or interrupt(N), then define __vector_N as + a global alias for the function name. */ + +static void +avr_asm_isr_alias (tree /*func*/, tree attr, void *pv) +{ + avr_fun_cookie *cookie = (avr_fun_cookie *) pv; + + for (tree v = TREE_VALUE (attr); v; v = TREE_CHAIN (v)) + { + int ival = avr_isr_number (TREE_VALUE (v)); + + if (ival) + { + fprintf (cookie->file, ".global __vector_%d\n", ival); + fprintf (cookie->file, "__vector_%d = ", ival); + assemble_name (cookie->file, cookie->name); + fprintf (cookie->file, "\n"); + } + } +} + + +/* Worker for `ASM_DECLARE_FUNCTION_NAME'. */ + +void +avr_declare_function_name (FILE *file, const char *name, tree decl) +{ + // Default action from elfos.h. + ASM_OUTPUT_TYPE_DIRECTIVE (file, name, "function"); + ASM_DECLARE_RESULT (file, DECL_RESULT (decl)); + ASM_OUTPUT_FUNCTION_LABEL (file, name, decl); + + avr_fun_cookie fc = { file, name }; + + avr_foreach_function_attribute (decl, "signal", avr_asm_isr_alias, &fc); + avr_foreach_function_attribute (decl, "interrupt", avr_asm_isr_alias, &fc); +} + + /* Implement `TARGET_ASM_FUNCTION_END_PROLOGUE'. */ /* Output summary at end of function prologue. */ @@ -2988,7 +3123,9 @@ avr_asm_function_end_prologue (FILE *file) } else { - if (cfun->machine->is_interrupt) + if (cfun->machine->is_interrupt + || (cfun->machine->is_signal + && cfun->machine->is_noblock)) { fputs ("/* prologue: Interrupt */\n", file); } @@ -4366,8 +4503,8 @@ avr_xload_libgcc_p (machine_mode mode) static rtx avr_find_unused_d_reg (rtx_insn *insn, rtx exclude) { - bool isr_p = (avr_interrupt_function_p (current_function_decl) - || avr_signal_function_p (current_function_decl)); + bool isr_p = (avr_interrupt_function (current_function_decl) + || avr_signal_function (current_function_decl)); for (int regno = REG_16; regno < REG_32; regno++) { @@ -11385,6 +11522,7 @@ avr_class_likely_spilled_p (reg_class_t c) After function prologue interrupts remain disabled. interrupt - Make a function to be hardware interrupt. Before function prologue interrupts are enabled by means of SEI. + noblock - The function is an ISR that starts with a SEI instruction. naked - Don't generate function prologue/epilogue and RET instruction. */ @@ -11583,9 +11721,11 @@ TARGET_GNU_ATTRIBUTES (avr_attribute_table, affects_type_identity, handler, exclude } */ { "progmem", 0, 0, false, false, false, false, avr_handle_progmem_attribute, NULL }, - { "signal", 0, 0, true, false, false, false, + { "signal", 0, -1, true, false, false, false, avr_handle_fndecl_attribute, NULL }, - { "interrupt", 0, 0, true, false, false, false, + { "interrupt", 0, -1, true, false, false, false, + avr_handle_fndecl_attribute, NULL }, + { "noblock", 0, 0, true, false, false, false, avr_handle_fndecl_attribute, NULL }, { "no_gccisr", 0, 0, true, false, false, false, avr_handle_fndecl_attribute, NULL }, @@ -11815,6 +11955,33 @@ avr_pgm_check_var_decl (tree node) } +/* Helper for `avr_insert_attributes'. Print an error when there are invalid + attributes named NAME, where NAME is in { "signal", "interrupt" }. */ + +static void +avr_handle_isr_attribute (tree, tree *attrs, const char *name) +{ + bool seen = false; + + for (tree list = lookup_attribute (name, *attrs); list; + list = lookup_attribute (name, TREE_CHAIN (list))) + { + seen = true; + for (tree v = TREE_VALUE (list); v; v = TREE_CHAIN (v)) + { + if (! avr_isr_number (TREE_VALUE (v))) + error ("attribute %qs expects a constant positive integer" + " argument", name); + } + } + + if (seen + && ! lookup_attribute ("used", *attrs)) + { + *attrs = tree_cons (get_identifier ("used"), NULL, *attrs); + } +} + /* Implement `TARGET_INSERT_ATTRIBUTES'. */ static void @@ -11847,6 +12014,9 @@ avr_insert_attributes (tree node, tree *attributes) NULL, *attributes); } + avr_handle_isr_attribute (node, attributes, "signal"); + avr_handle_isr_attribute (node, attributes, "interrupt"); + /* Add the section attribute if the variable is in progmem. */ if (VAR_P (node) diff --git a/gcc/config/avr/avr.h b/gcc/config/avr/avr.h index 4977e15eeed..56b7f39b834 100644 --- a/gcc/config/avr/avr.h +++ b/gcc/config/avr/avr.h @@ -548,14 +548,24 @@ struct GTY(()) machine_function /* 'true' - if current function is a naked function. */ int is_naked; - /* 'true' - if current function is an interrupt function - as specified by the "interrupt" attribute. */ + /* 0 when no "interrupt" attribute is present. + 1 when an "interrupt" attribute without arguments is present (and + perhaps also "interrupt" attributes with argument(s)). + -1 when "interrupt" attribute(s) with arguments are present but none + without argument. */ int is_interrupt; - /* 'true' - if current function is a signal function - as specified by the "signal" attribute. */ + /* 0 when no "signal" attribute is present. + 1 when a "signal" attribute without arguments is present (and + perhaps also "signal" attributes with argument(s)). + -1 when "signal" attribute(s) with arguments are present but none + without argument. */ int is_signal; + /* 'true' - if current function is a non-blocking interrupt service + routine as specified by the "isr_noblock" attribute. */ + int is_noblock; + /* 'true' - if current function is a 'task' function as specified by the "OS_task" attribute. */ int is_OS_task; diff --git a/gcc/config/avr/elf.h b/gcc/config/avr/elf.h index e334deeb79c..0112aa3d432 100644 --- a/gcc/config/avr/elf.h +++ b/gcc/config/avr/elf.h @@ -31,3 +31,7 @@ /* Be conservative in crtstuff.c. */ #undef INIT_SECTION_ASM_OP #undef FINI_SECTION_ASM_OP + +#undef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \ + avr_declare_function_name (STREAM, NAME, DECL) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 66c99ef7a66..927aa24ab63 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -5136,6 +5136,44 @@ ISR (ADC_vect, ISR_NOBLOCK) // Uses the "interrupt" attribute. When both @code{signal} and @code{interrupt} are specified for the same function, then @code{signal} is silently ignored. +@cindex @code{signal(@var{num})} function attribute, AVR +@cindex @code{interrupt(@var{num})} function attribute, AVR +@item signal(@var{num}) +@itemx interrupt(@var{num}) + +Similar to the @code{signal} resp. @code{interrupt} attribute without +argument, but the IRQ number is supplied as an argument @var{num} to +the attribute, rather than providing the ISR name itself as the function name: + +@example +__attribute__((signal(1))) +void my_handler (void) +@{ + // Code for __vector_1 +@} + +#include + +__attribute__((__signal__(PCINT0_vect_num, PCINT1_vect_num))) +static void my_pcint0_1_handler (void) +@{ + // Code for PCINT0 and PCINT1 (__vector_3 and __vector_4 + // on ATmega328). +@} +@end example + +Notice that the handler function needs not to be externally visible. + +@cindex @code{noblock} function attribute, AVR +@item noblock + +This attribute can be used together with the @code{signal} attribute +to indicate that an interrupt service routine should start with a @code{SEI} +instruction to globally re-enable interrupts. Using attributes @code{signal} +and @code{noblock} together has the same effect like using the @code{interrupt} +attribute. Using the @code{noblock} attribute without @code{signal} has no +effect. + @cindex @code{naked} function attribute, AVR @item naked This attribute allows the compiler to construct the diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 9fb0925ed29..e495b1271fa 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -24569,6 +24569,13 @@ and defined to@tie{}0, otherwise. The compiler is configured to be used together with AVR-Libc. See the @option{--with-avrlibc} configure option. +@item __HAVE_SIGNAL_N__ +The compiler supports the @code{signal(@var{num})} and +@code{interrupt(@var{num})} +@ref{AVR Function Attributes,,function attributes} +with an argument @var{num} that specifies the number of the +interrupt service routine. + @item __HAVE_DOUBLE_MULTILIB__ Defined if @option{-mdouble=} acts as a multilib option. diff --git a/gcc/testsuite/gcc.target/avr/torture/signal_n-1.c b/gcc/testsuite/gcc.target/avr/torture/signal_n-1.c new file mode 100644 index 00000000000..d5f422d2d99 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/signal_n-1.c @@ -0,0 +1,49 @@ +/* { dg-do run } */ + +volatile int i; + +__attribute__((signal(1,2))) +static void fun12 (void) +{ + __asm goto ("brie %x0" ::: "memory" : l_abort); + i += 1234; + return; + + l_abort: + __asm ("%~jmp abort" ::: "memory"); +} + +__attribute__((signal(3),noblock)) +static void fun3 (void) +{ + __asm goto ("brid %x0" ::: "memory" : l_abort); + i += 333; + return; + + l_abort: + __asm ("%~jmp abort" ::: "memory"); +} + +extern void isr1 (void) __asm("__vector_1"); +extern void isr2 (void) __asm("__vector_2"); +extern void isr3 (void) __asm("__vector_3"); + +int main (void) +{ + __asm ("cli" ::: "memory"); + isr1(); + if (i != 1234) + __builtin_abort (); + + __asm ("cli" ::: "memory"); + isr2(); + if (i != 2468) + __builtin_abort (); + + __asm ("cli" ::: "memory"); + isr3(); + if (i != 2468 + 333) + __builtin_abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.target/avr/torture/signal_n-2.c b/gcc/testsuite/gcc.target/avr/torture/signal_n-2.c new file mode 100644 index 00000000000..f6cc19d9842 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/signal_n-2.c @@ -0,0 +1,32 @@ +/* { dg-do run } */ + +volatile int i; + +__attribute__((interrupt(1),interrupt(2))) +static void fun12 (void) +{ + __asm goto ("brid %x0" ::: "memory" : l_abort); + i += 1234; + return; + + l_abort: + __asm ("%~jmp abort" ::: "memory"); +} + +extern void isr1 (void) __asm("__vector_1"); +extern void isr2 (void) __asm("__vector_2"); + +int main (void) +{ + __asm ("cli" ::: "memory"); + isr1(); + if (i != 1234) + __builtin_abort (); + + __asm ("cli" ::: "memory"); + isr2(); + if (i != 2468) + __builtin_abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.target/avr/torture/signal_n-3.c b/gcc/testsuite/gcc.target/avr/torture/signal_n-3.c new file mode 100644 index 00000000000..55f7d14543d --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/signal_n-3.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-Wattributes" } */ + +__attribute__((signal(0))) static void fun1 (void); /* { dg-error "expects a constant positive integer" } */ + +__attribute__((signal("1"))) static void fun2 (void); /* { dg-error "expects a constant positive integer" } */ + +__attribute__((interrupt(-1))) void fun3 (void); /* { dg-error "expects a constant positive integer" } */ + +__attribute__((interrupt("2"))) void fun4 (void); /* { dg-error "expects a constant positive integer" } */ + +__attribute__((noblock)) void fun5 (void) { } /* { dg-warning "attribute ignored on non-ISR" } */ + diff --git a/gcc/testsuite/gcc.target/avr/torture/signal_n-4.cpp b/gcc/testsuite/gcc.target/avr/torture/signal_n-4.cpp new file mode 100644 index 00000000000..749fe3cce78 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/torture/signal_n-4.cpp @@ -0,0 +1,53 @@ +/* { dg-do run } */ + +class IRQS +{ +public: + void test (int x) const + { + if (i != x) + __builtin_abort (); + } + +private: + static volatile int i; + + __attribute__((signal(1,2))) + static void fun12 () + { + i += 1234; + } +}; + +extern void isr1 () __asm("__vector_1"); +extern void isr2 () __asm("__vector_2"); +extern void isr3 () __asm("__vector_3"); + +IRQS irqs; + +volatile int IRQS::i; + +namespace +{ + int j; + __attribute__((signal(3))) + void handle3 () + { + j = 444; + } +} + +int main (void) +{ + isr1(); + irqs.test (1234); + + isr2(); + irqs.test (2468); + + isr3 (); + if (j != 444) + __builtin_abort(); + + return 0; +}