diff --git a/gcc/testsuite/gcc.target/avr/attribute-io.h b/gcc/testsuite/gcc.target/avr/attribute-io.h new file mode 100644 index 00000000000..39abd4ef59f --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/attribute-io.h @@ -0,0 +1,74 @@ +/* { dg-do run } */ +/* { dg-options "-Os -save-temps" } */ + +__attribute__((address(1234))) +int g_1234; + +__attribute__((weak, address(4321))) +int w_4321; + +__attribute__((address(5678))) +static int l_5678; + +__attribute__((io_low(__AVR_SFR_OFFSET__ + 3))) +volatile unsigned char g_low; + +__attribute__((weak, io_low(__AVR_SFR_OFFSET__ + 2))) +volatile unsigned char w_low; + +__attribute__((io_low(__AVR_SFR_OFFSET__ + 1))) +static volatile unsigned char l_low; + +__attribute__((io(__AVR_SFR_OFFSET__ + 35))) +volatile unsigned char g_io; + +__attribute__((weak, io(__AVR_SFR_OFFSET__ + 34))) +volatile unsigned char w_io; + +__attribute__((io(__AVR_SFR_OFFSET__ + 33))) +static volatile unsigned char l_io; + +#define CMP(SYM, VAL) \ + do { \ + unsigned x; \ + __asm ("" : "=d" (x) : "0" (& SYM)); \ + if (x != VAL) \ + __builtin_abort(); \ + } while(0) + + +int main (void) +{ + CMP (g_1234, 1234); + CMP (w_4321, 4321); + CMP (l_5678, 5678); + + CMP (g_low, __AVR_SFR_OFFSET__ + 3); + CMP (w_low, __AVR_SFR_OFFSET__ + 2); + CMP (l_low, __AVR_SFR_OFFSET__ + 1); + + CMP (g_io, __AVR_SFR_OFFSET__ + 35); + CMP (w_io, __AVR_SFR_OFFSET__ + 34); + CMP (l_io, __AVR_SFR_OFFSET__ + 33); + + l_low = l_io; + g_low = g_io; + w_low = w_io; + l_low |= 1; + g_low |= 2; + w_low |= 4; + + return 0; +} + +/* { dg-final { scan-assembler "g_1234 = 1234" } } */ +/* { dg-final { scan-assembler "w_4321 = 4321" } } */ +/* { dg-final { scan-assembler "l_5678 = 5678" } } */ + +/* { dg-final { scan-assembler "\\.globl g_1234" } } */ +/* { dg-final { scan-assembler "\\.globl g_low" } } */ +/* { dg-final { scan-assembler "\\.globl g_io" } } */ + +/* { dg-final { scan-assembler "\\.weak w_4321" } } */ +/* { dg-final { scan-assembler "\\.weak w_low" } } */ +/* { dg-final { scan-assembler "\\.weak w_io" } } */ diff --git a/gcc/testsuite/gcc.target/avr/pr112952-0.c b/gcc/testsuite/gcc.target/avr/pr112952-0.c new file mode 100644 index 00000000000..1870bf3f702 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/pr112952-0.c @@ -0,0 +1,16 @@ +/* { dg-do run } */ +/* { dg-options "-Os -save-temps -fno-data-sections -fno-common" } */ + +#include "attribute-io.h" + +/* { dg-final { scan-assembler "g_1234 = 1234" } } */ +/* { dg-final { scan-assembler "w_4321 = 4321" } } */ +/* { dg-final { scan-assembler "l_5678 = 5678" } } */ + +/* { dg-final { scan-assembler "\\.globl g_1234" } } */ +/* { dg-final { scan-assembler "\\.globl g_low" } } */ +/* { dg-final { scan-assembler "\\.globl g_io" } } */ + +/* { dg-final { scan-assembler "\\.weak w_4321" } } */ +/* { dg-final { scan-assembler "\\.weak w_low" } } */ +/* { dg-final { scan-assembler "\\.weak w_io" } } */ diff --git a/gcc/testsuite/gcc.target/avr/pr112952-1.c b/gcc/testsuite/gcc.target/avr/pr112952-1.c new file mode 100644 index 00000000000..6e7d273c979 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/pr112952-1.c @@ -0,0 +1,16 @@ +/* { dg-do run } */ +/* { dg-options "-Os -save-temps -fno-data-sections -fcommon" } */ + +#include "attribute-io.h" + +/* { dg-final { scan-assembler "g_1234 = 1234" } } */ +/* { dg-final { scan-assembler "w_4321 = 4321" } } */ +/* { dg-final { scan-assembler "l_5678 = 5678" } } */ + +/* { dg-final { scan-assembler "\\.globl g_1234" } } */ +/* { dg-final { scan-assembler "\\.globl g_low" } } */ +/* { dg-final { scan-assembler "\\.globl g_io" } } */ + +/* { dg-final { scan-assembler "\\.weak w_4321" } } */ +/* { dg-final { scan-assembler "\\.weak w_low" } } */ +/* { dg-final { scan-assembler "\\.weak w_io" } } */ diff --git a/gcc/testsuite/gcc.target/avr/pr112952-2.c b/gcc/testsuite/gcc.target/avr/pr112952-2.c new file mode 100644 index 00000000000..0ef059804b8 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/pr112952-2.c @@ -0,0 +1,16 @@ +/* { dg-do run } */ +/* { dg-options "-Os -save-temps -fdata-sections -fno-common" } */ + +#include "attribute-io.h" + +/* { dg-final { scan-assembler "g_1234 = 1234" } } */ +/* { dg-final { scan-assembler "w_4321 = 4321" } } */ +/* { dg-final { scan-assembler "l_5678 = 5678" } } */ + +/* { dg-final { scan-assembler "\\.globl g_1234" } } */ +/* { dg-final { scan-assembler "\\.globl g_low" } } */ +/* { dg-final { scan-assembler "\\.globl g_io" } } */ + +/* { dg-final { scan-assembler "\\.weak w_4321" } } */ +/* { dg-final { scan-assembler "\\.weak w_low" } } */ +/* { dg-final { scan-assembler "\\.weak w_io" } } */ diff --git a/gcc/testsuite/gcc.target/avr/pr112952-3.c b/gcc/testsuite/gcc.target/avr/pr112952-3.c new file mode 100644 index 00000000000..51da96761f7 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/pr112952-3.c @@ -0,0 +1,16 @@ +/* { dg-do run } */ +/* { dg-options "-Os -save-temps -fdata-sections -fcommon" } */ + +#include "attribute-io.h" + +/* { dg-final { scan-assembler "g_1234 = 1234" } } */ +/* { dg-final { scan-assembler "w_4321 = 4321" } } */ +/* { dg-final { scan-assembler "l_5678 = 5678" } } */ + +/* { dg-final { scan-assembler "\\.globl g_1234" } } */ +/* { dg-final { scan-assembler "\\.globl g_low" } } */ +/* { dg-final { scan-assembler "\\.globl g_io" } } */ + +/* { dg-final { scan-assembler "\\.weak w_4321" } } */ +/* { dg-final { scan-assembler "\\.weak w_low" } } */ +/* { dg-final { scan-assembler "\\.weak w_io" } } */ diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc index 224cb6c665f..7eb5d08653e 100644 --- a/gcc/config/avr/avr.cc +++ b/gcc/config/avr/avr.cc @@ -10362,6 +10362,10 @@ avr_handle_addr_attribute (tree *node, tree name, tree args, int flags ATTRIBUTE_UNUSED, bool *no_add) { bool io_p = startswith (IDENTIFIER_POINTER (name), "io"); + HOST_WIDE_INT io_start = avr_arch->sfr_offset; + HOST_WIDE_INT io_end = strcmp (IDENTIFIER_POINTER (name), "io_low") == 0 + ? io_start + 0x1f + : io_start + 0x3f; location_t loc = DECL_SOURCE_LOCATION (*node); if (!VAR_P (*node)) @@ -10385,12 +10389,10 @@ avr_handle_addr_attribute (tree *node, tree name, tree args, } else if (io_p && (!tree_fits_shwi_p (arg) - || !(strcmp (IDENTIFIER_POINTER (name), "io_low") == 0 - ? low_io_address_operand : io_address_operand) - (GEN_INT (TREE_INT_CST_LOW (arg)), QImode))) + || ! IN_RANGE (TREE_INT_CST_LOW (arg), io_start, io_end))) { - warning_at (loc, OPT_Wattributes, "%qE attribute address " - "out of range", name); + warning_at (loc, OPT_Wattributes, "%qE attribute address out of " + "range 0x%x...0x%x", name, (int) io_start, (int) io_end); *no_add = true; } else @@ -10433,7 +10435,6 @@ avr_eval_addr_attrib (rtx x) attr = lookup_attribute ("io", DECL_ATTRIBUTES (decl)); if (!attr || !TREE_VALUE (attr)) attr = lookup_attribute ("io_low", DECL_ATTRIBUTES (decl)); - gcc_assert (attr); } if (!attr || !TREE_VALUE (attr)) attr = lookup_attribute ("address", DECL_ATTRIBUTES (decl)); @@ -10689,6 +10690,16 @@ avr_pgm_check_var_decl (tree node) static void avr_insert_attributes (tree node, tree *attributes) { + if (VAR_P (node) + && ! TREE_STATIC (node)) + { + const char *names[] = { "io", "io_low", "address", NULL }; + for (const char **p = names; *p; ++p) + if (lookup_attribute (*p, *attributes)) + error ("variable %q+D with attribute %qs must be located in " + "static storage", node, *p); + } + avr_pgm_check_var_decl (node); if (TARGET_MAIN_IS_OS_TASK @@ -10792,37 +10803,11 @@ avr_rodata_in_flash_p () /* Track need of __do_clear_bss. */ void -avr_asm_output_aligned_decl_common (FILE * stream, - tree decl, - const char *name, - unsigned HOST_WIDE_INT size, - unsigned int align, bool local_p) +avr_asm_output_aligned_decl_common (FILE *stream, tree /* decl */, + const char *name, + unsigned HOST_WIDE_INT size, + unsigned int align, bool local_p) { - rtx mem = decl == NULL_TREE ? NULL_RTX : DECL_RTL (decl); - rtx symbol; - - if (mem != NULL_RTX && MEM_P (mem) - && SYMBOL_REF_P ((symbol = XEXP (mem, 0))) - && (SYMBOL_REF_FLAGS (symbol) & (SYMBOL_FLAG_IO | SYMBOL_FLAG_ADDRESS))) - { - if (!local_p) - { - fprintf (stream, "\t.globl\t"); - assemble_name (stream, name); - fprintf (stream, "\n"); - } - if (SYMBOL_REF_FLAGS (symbol) & SYMBOL_FLAG_ADDRESS) - { - assemble_name (stream, name); - fprintf (stream, " = %ld\n", - (long) INTVAL (avr_eval_addr_attrib (symbol))); - } - else if (local_p) - error_at (DECL_SOURCE_LOCATION (decl), - "static IO declaration for %q+D needs an address", decl); - return; - } - /* __gnu_lto_slim is just a marker for the linker injected by toplev.cc. There is no need to trigger __do_clear_bss code for them. */ @@ -10835,6 +10820,9 @@ avr_asm_output_aligned_decl_common (FILE * stream, ASM_OUTPUT_ALIGNED_COMMON (stream, name, size, align); } + +/* Implement `ASM_OUTPUT_ALIGNED_BSS'. */ + void avr_asm_asm_output_aligned_bss (FILE *file, tree decl, const char *name, unsigned HOST_WIDE_INT size, int align, @@ -10842,20 +10830,10 @@ avr_asm_asm_output_aligned_bss (FILE *file, tree decl, const char *name, (FILE *, tree, const char *, unsigned HOST_WIDE_INT, int)) { - rtx mem = decl == NULL_TREE ? NULL_RTX : DECL_RTL (decl); - rtx symbol; + if (!startswith (name, "__gnu_lto")) + avr_need_clear_bss_p = true; - if (mem != NULL_RTX && MEM_P (mem) - && SYMBOL_REF_P ((symbol = XEXP (mem, 0))) - && (SYMBOL_REF_FLAGS (symbol) & (SYMBOL_FLAG_IO | SYMBOL_FLAG_ADDRESS))) - { - if (!(SYMBOL_REF_FLAGS (symbol) & SYMBOL_FLAG_ADDRESS)) - error_at (DECL_SOURCE_LOCATION (decl), - "IO definition for %q+D needs an address", decl); - avr_asm_output_aligned_decl_common (file, decl, name, size, align, false); - } - else - default_func (file, decl, name, size, align); + default_func (file, decl, name, size, align); } @@ -10894,6 +10872,58 @@ avr_output_progmem_section_asm_op (const char *data) } +/* A noswitch section callback to output symbol definitions for + attributes "io", "io_low" and "address". */ + +static bool +avr_output_addr_attrib (tree decl, const char *name, + unsigned HOST_WIDE_INT /* size */, + unsigned HOST_WIDE_INT /* align */) +{ + gcc_assert (DECL_RTL_SET_P (decl)); + + FILE *stream = asm_out_file; + bool local_p = ! DECL_WEAK (decl) && ! TREE_PUBLIC (decl); + rtx symbol, mem = DECL_RTL (decl); + + if (mem != NULL_RTX && MEM_P (mem) + && SYMBOL_REF_P ((symbol = XEXP (mem, 0))) + && (SYMBOL_REF_FLAGS (symbol) & (SYMBOL_FLAG_IO | SYMBOL_FLAG_ADDRESS))) + { + if (! local_p) + { + fprintf (stream, "\t%s\t", DECL_WEAK (decl) ? ".weak" : ".globl"); + assemble_name (stream, name); + fprintf (stream, "\n"); + } + + if (SYMBOL_REF_FLAGS (symbol) & SYMBOL_FLAG_ADDRESS) + { + assemble_name (stream, name); + fprintf (stream, " = %ld\n", + (long) INTVAL (avr_eval_addr_attrib (symbol))); + } + else if (local_p) + { + const char *names[] = { "io", "io_low", "address", NULL }; + for (const char **p = names; *p; ++p) + if (lookup_attribute (*p, DECL_ATTRIBUTES (decl))) + { + error ("static attribute %qs declaration for %q+D needs an " + "address", *p, decl); + break; + } + } + + return true; + } + + gcc_unreachable(); + + return false; +} + + /* Implement `TARGET_ASM_INIT_SECTIONS'. */ static void @@ -10907,6 +10937,7 @@ avr_asm_init_sections (void) readonly_data_section->unnamed.callback = avr_output_data_section_asm_op; data_section->unnamed.callback = avr_output_data_section_asm_op; bss_section->unnamed.callback = avr_output_bss_section_asm_op; + tls_comm_section->noswitch.callback = avr_output_addr_attrib; } @@ -11085,15 +11116,17 @@ avr_encode_section_info (tree decl, rtx rtl, int new_decl_p) tree io_low_attr = lookup_attribute ("io_low", attr); tree io_attr = lookup_attribute ("io", attr); + tree address_attr = lookup_attribute ("address", attr); if (io_low_attr && TREE_VALUE (io_low_attr) && TREE_VALUE (TREE_VALUE (io_low_attr))) - addr_attr = io_attr; + addr_attr = io_low_attr; else if (io_attr && TREE_VALUE (io_attr) && TREE_VALUE (TREE_VALUE (io_attr))) addr_attr = io_attr; else - addr_attr = lookup_attribute ("address", attr); + addr_attr = address_attr; + if (io_low_attr || (io_attr && addr_attr && low_io_address_operand @@ -11108,6 +11141,36 @@ avr_encode_section_info (tree decl, rtx rtl, int new_decl_p) don't use the exact value for constant propagation. */ if (addr_attr && !DECL_EXTERNAL (decl)) SYMBOL_REF_FLAGS (sym) |= SYMBOL_FLAG_ADDRESS; + + if (io_attr || io_low_attr || address_attr) + { + if (DECL_INITIAL (decl)) + { + /* Initializers are not yet parsed in TARGET_INSERT_ATTRIBUTES, + hence deny initializers now. The values of symbols with an + address attribute are determined by the attribute, not by + some initializer. */ + + error ("variable %q+D with attribute %qs must not have an " + "initializer", decl, + io_low_attr ? "io_low" : io_attr ? "io" : "address"); + } + else + { + /* PR112952: The only way to output a variable declaration in a + custom manner is by means of a noswitch section callback. + There are only three noswitch sections: comm_section, + lcomm_section and tls_comm_section. And there is no way to + wire a custom noswitch section to a decl. As lcomm_section + is bypassed with -fdata-sections -fno-common, there is no + other way than making use of tls_comm_section. As we are + using that section anyway, also use it in the public case. */ + + DECL_COMMON (decl) = 1; + set_decl_section_name (decl, (const char*) nullptr); + set_decl_tls_model (decl, (tls_model) 2); + } + } } if (AVR_TINY