diff mbox series

[avr,applied] PR target/112952 Fix attribute "io" et al. handling.

Message ID 83b90302-bd7b-4064-8c8e-7495ebd3931e@gjlay.de
State New
Headers show
Series [avr,applied] PR target/112952 Fix attribute "io" et al. handling. | expand

Commit Message

Georg-Johann Lay Jan. 4, 2024, 4:28 p.m. UTC
This fixes the avr-specific attributes io, io_low and address,
that are all basically the same except that io and io_low imply
assertions on allowed addressing modes.

It also improves some diagnostics.

Johann

--

gcc/
	PR target/112952
	* config/avr/avr.cc (avr_handle_addr_attribute): Also print valid
	range when diagnosing attribute "io" and "io_low" are out of range.
	(avr_eval_addr_attrib): Don't ICE on empty address at that place.
	(avr_insert_attributes): Reject if attribute "address", "io" or "io_low"
	in contexts other than static storage.
	(avr_asm_output_aligned_decl_common): Move output of decls with
	attribute "address", "io", and "io_low" to...
	(avr_output_addr_attrib): ...this new function.
	(avr_asm_asm_output_aligned_bss): Remove output for decls with
	attribute "address", "io", and "io_low".
	(avr_encode_section_info): Rectify handling of decls with attribute
	"address", "io", and "io_low".

gcc/testsuite/
	PR target/112952
	* gcc.target/avr/attribute-io.h: New file.
	* gcc.target/avr/pr112952-0.c: New test.
	* gcc.target/avr/pr112952-1.c: New test.
	* gcc.target/avr/pr112952-2.c: New test.
	* gcc.target/avr/pr112952-3.c: New test.

Comments

Jan-Benedict Glaw Jan. 12, 2024, 3:37 a.m. UTC | #1
On Thu, 2024-01-04 17:28:02 +0100, Georg-Johann Lay <avr@gjlay.de> wrote:
> This fixes the avr-specific attributes io, io_low and address,
> that are all basically the same except that io and io_low imply
> assertions on allowed addressing modes.

> --- a/gcc/config/avr/avr.cc
> +++ b/gcc/config/avr/avr.cc
[...]
> @@ -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

Building with a recent GCC, this results in a new warning (here forced
to an error with --enable-werror-alway--enable-werror-always):

/var/lib/laminar/run/gcc-avr-elf/64/local-toolchain-install/bin/g++  -fno-PIE -c   -g -O2   -DIN_GCC -DCROSS_DIRECTORY_STRUCTURE   -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wmissing-format-attribute -Wconditionally-supported -Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings -Werror -fno-common  -DHAVE_CONFIG_H -fno-PIE -I. -I. -I../../gcc/gcc -I../../gcc/gcc/. -I../../gcc/gcc/../include  -I../../gcc/gcc/../libcpp/include -I../../gcc/gcc/../libcody  -I../../gcc/gcc/../libdecnumber -I../../gcc/gcc/../libdecnumber/dpd -I../libdecnumber -I../../gcc/gcc/../libbacktrace   -o avr.o -MT avr.o -MMD -MP -MF ./.deps/avr.TPo ../../gcc/gcc/config/avr/avr.cc
../../gcc/gcc/config/avr/avr.cc: In function 'tree_node* avr_handle_addr_attribute(tree_node**, tree, tree, int, bool*)':
../../gcc/gcc/config/avr/avr.cc:10391:45: error: unquoted sequence of 3 consecutive punctuation characters '...' in format [-Werror=format-diag]
10391 |           warning_at (loc, OPT_Wattributes, "%qE attribute address out of "
      |                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10392 |                       "range 0x%x...0x%x", name, (int) io_start, (int) io_end);
      |                       ~~~~~~~~~~~~~~~~~~~    
cc1plus: all warnings being treated as errors
make[1]: *** [Makefile:2554: avr.o] Error 1
make[1]: Leaving directory '/var/lib/laminar/run/gcc-avr-elf/64/toolchain-build/gcc'
make: *** [Makefile:4676: all-gcc] Error 2


I think this should be "%<...%>".

MfG, JBG

--
Georg-Johann Lay Jan. 12, 2024, 12:32 p.m. UTC | #2
Am 12.01.24 um 04:37 schrieb Jan-Benedict Glaw:
> On Thu, 2024-01-04 17:28:02 +0100, Georg-Johann Lay <avr@gjlay.de> wrote:
>> This fixes the avr-specific attributes io, io_low and address,
>> that are all basically the same except that io and io_low imply
>> assertions on allowed addressing modes.
> 
>> --- a/gcc/config/avr/avr.cc
>> +++ b/gcc/config/avr/avr.cc
> [...]
>> @@ -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
> 
> Building with a recent GCC, this results in a new warning (here forced
> to an error with --enable-werror-alway--enable-werror-always):
> 
> /var/lib/laminar/run/gcc-avr-elf/64/local-toolchain-install/bin/g++  -fno-PIE -c   -g -O2   -DIN_GCC -DCROSS_DIRECTORY_STRUCTURE   -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wmissing-format-attribute -Wconditionally-supported -Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings -Werror -fno-common  -DHAVE_CONFIG_H -fno-PIE -I. -I. -I../../gcc/gcc -I../../gcc/gcc/. -I../../gcc/gcc/../include  -I../../gcc/gcc/../libcpp/include -I../../gcc/gcc/../libcody  -I../../gcc/gcc/../libdecnumber -I../../gcc/gcc/../libdecnumber/dpd -I../libdecnumber -I../../gcc/gcc/../libbacktrace   -o avr.o -MT avr.o -MMD -MP -MF ./.deps/avr.TPo ../../gcc/gcc/config/avr/avr.cc
> ../../gcc/gcc/config/avr/avr.cc: In function 'tree_node* avr_handle_addr_attribute(tree_node**, tree, tree, int, bool*)':
> ../../gcc/gcc/config/avr/avr.cc:10391:45: error: unquoted sequence of 3 consecutive punctuation characters '...' in format [-Werror=format-diag]
> 10391 |           warning_at (loc, OPT_Wattributes, "%qE attribute address out of "
>        |                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 10392 |                       "range 0x%x...0x%x", name, (int) io_start, (int) io_end);
>        |                       ~~~~~~~~~~~~~~~~~~~
> cc1plus: all warnings being treated as errors
> make[1]: *** [Makefile:2554: avr.o] Error 1
> make[1]: Leaving directory '/var/lib/laminar/run/gcc-avr-elf/64/toolchain-build/gcc'
> make: *** [Makefile:4676: all-gcc] Error 2
> 
> 
> I think this should be "%<...%>".
> 
> MfG, JBG

Hi,

thanks for sorting this out. I would install the patch below.

I must admit that I don't understand that warning and what is
illegal about having ellipses in a format string at that place.

That warning isn't even documented, at least as of 2024-02-12

https://gcc.gnu.org/onlinedocs/gcc/Option-Index.html

There should be no quotes around the ellipses, they are intended
as real ellipses.

Plus, I saw in other modules that it warns about format strings
like printf (";%i", 10); Why is ";" not allowed there?

Johann

--


diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index 0cdd035fa1a..4bc3cf929de 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -10388,8 +10388,8 @@ avr_handle_addr_attribute (tree *node, tree 
name, tree args,
                && (!tree_fits_shwi_p (arg)
                    || ! IN_RANGE (TREE_INT_CST_LOW (arg), io_start, 
io_end)))
         {
-         warning_at (loc, OPT_Wattributes, "%qE attribute address out of "
-                     "range 0x%x...0x%x", name, (int) io_start, (int) 
io_end);
+         warning_at (loc, OPT_Wattributes, "%qE attribute address out 
of range"
+                     " 0x%x%s0x%x", name, (int) io_start, "...", (int) 
io_end);
           *no_add = true;
         }
        else
diff mbox series

Patch

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