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

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
}

Moreover, this patch adds support for the "noblock" function attribute
to let an ISR start with a SEI instruction.  Attribute "signal" together
with "noblock" behaves like "interrupt" but without imposing a specific
function name or visibility like "interrupt" does.

	PR target/116056
gcc/
	* config/avr/avr.h (machine_function) <is_noblock>: New field.
	* 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) <noblock>: New function attribute>.
	<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)
	(avr_noblock_function_p): 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. Set
	cfun->machine->is_noblock.  Warn about "noblock" in non-ISR functions.
	(struct avr_fun_cookie): New.
	(avr_expand_prologue, avr_asm_function_end_prologue): Handle "noblock".
	* config/avr/elf.h (ASM_DECLARE_FUNCTION_NAME): New define.
	* config/avr/avr-protos.h (avr_declare_function_name): New proto.
	* 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.
This commit is contained in:
Georg-Johann Lay 2024-07-24 08:37:43 +02:00
parent 79e029a418
commit 0f1de0c4ce
11 changed files with 413 additions and 32 deletions

View file

@ -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

View file

@ -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);

View file

@ -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 %<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
}
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)

View file

@ -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;

View file

@ -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)

View file

@ -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 <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{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

View file

@ -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.

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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" } */

View file

@ -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;
}