diff mbox series

[avr] Implement PR116056: attribute signal(n) and interrupt(n)

Message ID 68252709-ae72-46d4-b787-b650051e7f22@gjlay.de
State New
Headers show
Series [avr] Implement PR116056: attribute signal(n) and interrupt(n) | expand

Commit Message

Georg-Johann Lay July 23, 2024, 8:19 p.m. UTC
This patch adds support for arguments to the signal and interrupt
function attributes.  It allows to specify the ISR by means of the
associated IRQ number, in extension to the current attributes that
require to specify the ISR name like "__vector_1" as (assembly) name
for the function.  The new feature is more convenient, e.g. when the
ISR is implemented by a class method or in a namespace.  There is no
requirement that the ISR is externally visible.  The syntax is like:

__attribute__((signal(1, 2, ...), signal(3, 4, ...)))
[static] void isr_function (void)
{
     // Code
}

Ok for trunk?

Johann

--

AVR target 116056 - Support attribute signal(n) and interrupt(n).

This patch adds support for arguments to the signal and interrupt
function attributes.  It allows to specify the ISR by means of the
associated IRQ number, in extension to the current attributes that
require to specify the ISR name like "__vector_1" as (assembly) name
for the function.  The new feature is more convenient, e.g. when the
ISR is implemented by a class method or in a namespace.  There is no
requirement that the ISR is externally visible.  The syntax is like:

__attribute__((signal(1, 2, ...), signal(3, 4, ...)))
[static] void isr_function (void)
{
     // Code
}

	PR target/116056
gcc/
	* config/avr/avr.h (ASM_DECLARE_FUNCTION_NAME): New define.
	* config/avr/avr-protos.h (avr_declare_function_name): New proto.
	* config/avr/avr-c.cc (avr_cpu_cpp_builtins) <__HAVE_SIGNAL_N__>: New
	built-in macro.
	* config/avr/avr.cc (avr_declare_function_name): New function.
	(avr_attribute_table) <signal,interrupt>: Allow any number of args.
	(avr_insert_attributes): Check validity of "signal" and "interrupt"
	arguments.
	(avr_foreach_function_attribute, avr_interrupt_signal_function)
	(avr_isr_number, avr_asm_isr_alias, avr_handle_isr_attribute):
	New static functions.
	(avr_interrupt_function): New from avr_interrupt_function_p.
	Adjust callers.
	(avr_signal_function): New from avr_signal_function_p.
	Adjust callers.
	(avr_set_current_function): Only diagnose non-__vector ISR names
	when "signal" or "interrupt" attribute has no args.
	(struct avr_fun_cookie): New.
	* doc/extend.texi (AVR Function Attributes): Document
	signal(num) and interrupt(num).
	* doc/invoke.texi (AVR Built-in Macros) <__HAVE_SIGNAL_N__>: Document.
gcc/testsuite/
	* gcc.target/avr/torture/signal_n-1.c: New test.
	* gcc.target/avr/torture/signal_n-2.c: New test.
	* gcc.target/avr/torture/signal_n-3.c: New test.
	* gcc.target/avr/torture/signal_n-4.cpp: New test.

Comments

Jeff Law July 25, 2024, 3:03 p.m. UTC | #1
On 7/23/24 2:19 PM, Georg-Johann Lay wrote:
> This patch adds support for arguments to the signal and interrupt
> function attributes.  It allows to specify the ISR by means of the
> associated IRQ number, in extension to the current attributes that
> require to specify the ISR name like "__vector_1" as (assembly) name
> for the function.  The new feature is more convenient, e.g. when the
> ISR is implemented by a class method or in a namespace.  There is no
> requirement that the ISR is externally visible.  The syntax is like:
> 
> __attribute__((signal(1, 2, ...), signal(3, 4, ...)))
> [static] void isr_function (void)
> {
>      // Code
> }
> 
> Ok for trunk?
> 
> Johann
> 
> -- 
> 
> AVR target 116056 - Support attribute signal(n) and interrupt(n).
> 
> This patch adds support for arguments to the signal and interrupt
> function attributes.  It allows to specify the ISR by means of the
> associated IRQ number, in extension to the current attributes that
> require to specify the ISR name like "__vector_1" as (assembly) name
> for the function.  The new feature is more convenient, e.g. when the
> ISR is implemented by a class method or in a namespace.  There is no
> requirement that the ISR is externally visible.  The syntax is like:
> 
> __attribute__((signal(1, 2, ...), signal(3, 4, ...)))
> [static] void isr_function (void)
> {
>      // Code
> }
> 
>      PR target/116056
> gcc/
>      * config/avr/avr.h (ASM_DECLARE_FUNCTION_NAME): New define.
>      * config/avr/avr-protos.h (avr_declare_function_name): New proto.
>      * config/avr/avr-c.cc (avr_cpu_cpp_builtins) <__HAVE_SIGNAL_N__>: New
>      built-in macro.
>      * config/avr/avr.cc (avr_declare_function_name): New function.
>      (avr_attribute_table) <signal,interrupt>: Allow any number of args.
>      (avr_insert_attributes): Check validity of "signal" and "interrupt"
>      arguments.
>      (avr_foreach_function_attribute, avr_interrupt_signal_function)
>      (avr_isr_number, avr_asm_isr_alias, avr_handle_isr_attribute):
>      New static functions.
>      (avr_interrupt_function): New from avr_interrupt_function_p.
>      Adjust callers.
>      (avr_signal_function): New from avr_signal_function_p.
>      Adjust callers.
>      (avr_set_current_function): Only diagnose non-__vector ISR names
>      when "signal" or "interrupt" attribute has no args.
>      (struct avr_fun_cookie): New.
>      * doc/extend.texi (AVR Function Attributes): Document
>      signal(num) and interrupt(num).
>      * doc/invoke.texi (AVR Built-in Macros) <__HAVE_SIGNAL_N__>: Document.
> gcc/testsuite/
>      * gcc.target/avr/torture/signal_n-1.c: New test.
>      * gcc.target/avr/torture/signal_n-2.c: New test.
>      * gcc.target/avr/torture/signal_n-3.c: New test.
>      * gcc.target/avr/torture/signal_n-4.cpp: New test.
Just a couple whitespace nits.
> 

> +  avr_foreach_function_attribute (func, attr_name,
> +    [] (tree, tree attr, void *cookie)
> +    {
> +      int*pcook = (int*) cookie;
Whitespace nit "int *pcook = (int *) cookie" would be better
> +


> +
> +static void
> +avr_asm_isr_alias (tree /*func*/, tree attr, void *pv)
> +{
> +  avr_fun_cookie*cookie = (avr_fun_cookie*) pv;
Similarly.



No need to wait for another review.  Just fix the whitespace issues and 
you're good to go.

jeff
Georg-Johann Lay July 28, 2024, 8:20 p.m. UTC | #2
Applied with the addendum below.

Am 23.07.24 um 22:19 schrieb Georg-Johann Lay:
> This patch adds support for arguments to the signal and interrupt
> function attributes.  It allows to specify the ISR by means of the
> associated IRQ number, in extension to the current attributes that
> require to specify the ISR name like "__vector_1" as (assembly) name
> for the function.  The new feature is more convenient, e.g. when the
> ISR is implemented by a class method or in a namespace.  There is no
> requirement that the ISR is externally visible.  The syntax is like:
> 
> __attribute__((signal(1, 2, ...), signal(3, 4, ...)))
> [static] void isr_function (void)
> {
>      // Code
> }
> 
> Ok for trunk?
> 
> Johann
> 
> -- 
> 
> AVR target 116056 - Support attribute signal(n) and interrupt(n).
> 
> This patch adds support for arguments to the signal and interrupt
> function attributes.  It allows to specify the ISR by means of the
> associated IRQ number, in extension to the current attributes that
> require to specify the ISR name like "__vector_1" as (assembly) name
> for the function.  The new feature is more convenient, e.g. when the
> ISR is implemented by a class method or in a namespace.  There is no
> requirement that the ISR is externally visible.  The syntax is like:
> 
> __attribute__((signal(1, 2, ...), signal(3, 4, ...)))
> [static] void isr_function (void)
> {
>      // Code
> }
> 
>      PR target/116056
> gcc/
>      * config/avr/avr.h (ASM_DECLARE_FUNCTION_NAME): New define.
>      * config/avr/avr-protos.h (avr_declare_function_name): New proto.
>      * config/avr/avr-c.cc (avr_cpu_cpp_builtins) <__HAVE_SIGNAL_N__>: New
>      built-in macro.
>      * config/avr/avr.cc (avr_declare_function_name): New function.
>      (avr_attribute_table) <signal,interrupt>: Allow any number of args.
>      (avr_insert_attributes): Check validity of "signal" and "interrupt"
>      arguments.
>      (avr_foreach_function_attribute, avr_interrupt_signal_function)
>      (avr_isr_number, avr_asm_isr_alias, avr_handle_isr_attribute):
>      New static functions.
>      (avr_interrupt_function): New from avr_interrupt_function_p.
>      Adjust callers.
>      (avr_signal_function): New from avr_signal_function_p.
>      Adjust callers.
>      (avr_set_current_function): Only diagnose non-__vector ISR names
>      when "signal" or "interrupt" attribute has no args.
>      (struct avr_fun_cookie): New.
>      * doc/extend.texi (AVR Function Attributes): Document
>      signal(num) and interrupt(num).
>      * doc/invoke.texi (AVR Built-in Macros) <__HAVE_SIGNAL_N__>: Document.
> gcc/testsuite/
>      * gcc.target/avr/torture/signal_n-1.c: New test.
>      * gcc.target/avr/torture/signal_n-2.c: New test.
>      * gcc.target/avr/torture/signal_n-3.c: New test.
>      * gcc.target/avr/torture/signal_n-4.cpp: New test.
diff mbox series

Patch

diff --git a/gcc/config/avr/avr-c.cc b/gcc/config/avr/avr-c.cc
index 5e7f759ed73..2c5cfb34df6 100644
--- a/gcc/config/avr/avr-c.cc
+++ b/gcc/config/avr/avr-c.cc
@@ -391,6 +391,9 @@  avr_cpu_cpp_builtins (struct cpp_reader *pfile)
   cpp_define (pfile, "__WITH_AVRLIBC__");
 #endif /* WITH_AVRLIBC */
 
+  // We support __attribute__((signal (n1, n2, ...))).
+  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..7f82858659f 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,48 @@  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 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_interrupt_function_p (tree func)
+static int
+avr_interrupt_signal_function (tree func, const char *attr_name)
 {
-  return avr_lookup_function_attribute1 (func, "interrupt");
+  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 nonzero if FUNC is a signal function as specified
-   by the "signal" attribute.  */
 
-static bool
-avr_signal_function_p (tree func)
+/* 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_lookup_function_attribute1 (func, "signal");
+  return avr_interrupt_signal_function (func, "signal");
 }
 
 /* Return nonzero if FUNC is an OS_task function.  */
@@ -1437,8 +1490,8 @@  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_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,23 +1528,25 @@  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 %<this%> 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
@@ -2976,6 +3031,70 @@  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;
+  tree decl;
+};
+
+/* 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 without ASM_DECLARE_FUNCTION_NAME.
+  ASM_OUTPUT_FUNCTION_LABEL (file, name, decl);
+
+  avr_fun_cookie fc = { file, name, decl };
+
+  if (cfun->machine->is_signal)
+    avr_foreach_function_attribute (decl, "signal", avr_asm_isr_alias, &fc);
+
+  if (cfun->machine->is_interrupt)
+    avr_foreach_function_attribute (decl, "interrupt", avr_asm_isr_alias, &fc);
+}
+
+
 /* Implement `TARGET_ASM_FUNCTION_END_PROLOGUE'.  */
 /* Output summary at end of function prologue.  */
 
@@ -4366,8 +4485,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++)
     {
@@ -11583,9 +11702,9 @@  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 },
   { "no_gccisr", 0, 0, true,  false, false, false,
     avr_handle_fndecl_attribute, NULL },
@@ -11815,6 +11934,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 +11993,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..bd22398a4a1 100644
--- a/gcc/config/avr/avr.h
+++ b/gcc/config/avr/avr.h
@@ -424,6 +424,10 @@  typedef struct avr_args
 #define ASM_OUTPUT_ALIGNED_DECL_LOCAL(STREAM, DECL, NAME, SIZE, ALIGN)  \
   avr_asm_output_aligned_decl_common (STREAM, DECL, NAME, SIZE, ALIGN, true)
 
+#undef ASM_DECLARE_FUNCTION_NAME
+#define ASM_DECLARE_FUNCTION_NAME(STREAM, NAME, DECL) \
+	avr_declare_function_name (STREAM, NAME, DECL)
+
 /* Globalizing directive for a label.  */
 #define GLOBAL_ASM_OP ".global\t"
 
@@ -548,12 +552,18 @@  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 'task' function 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 4b77599380b..3936f469d21 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5136,6 +5136,34 @@  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 <avr/io.h>
+
+__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{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 f052128e2a5..729795618eb 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -24564,6 +24564,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..0e4c109fe9e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/signal_n-1.c
@@ -0,0 +1,32 @@ 
+/* { 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");
+}
+
+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-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..379056a110f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/signal_n-3.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+
+__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" } */
+
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;
+}