This patch outputs .gnu_attribute tags for the bit sizes
of long double (12 = Tag_GNU_AVR_BITS_LONG_DOUBLE) resp.
double (8 = Tag_GNU_AVR_BITS_DOUBLE).

The recently introduced gimple mini-pass avr_pass_has
scans the code for the presence of [long] double, and
avr_file_end performs similar scans on the varpool.

There are cases where libgcc shares multilibs when they
don't depend on -m[long-]double=X, so that just emitting
.gnu_attribute *.X according to -m[long-]double=X is too strict.

The patch uses the .gnu_attribute's as introduced in
https://sourceware.org/PR34557 (Binutils 2.48).

The Binutils patch is currently under review:
https://sourceware.org/pipermail/binutils/2026-August/150970.html

This GCC patch is atop of
https://gcc.gnu.org/pipermail/gcc-patches/2026-August/728536.html
which introduces avr_pass_has and does the configure work to
determine HAVE_AS_AVR_GNU_ATTRIBUTE.

The patch passes without new regressions.

Ok for trunk?

Johann

--

        PR target/127032
gcc/
        * config/avr/avr.md (Tag_GNU_AVR_BITS_LONG_DOUBLE)
        (Tag_GNU_AVR_BITS_DOUBLE): New define_constants.
        * config/avr/avr.cc (avr_uses_long_double_p)
        (avr_uses_double_p): New static variables.
        (avr_find_double): New function.
        (avr_file_end) [HAVE_AS_AVR_GNU_ATTRIBUTE]: Run avr_find_double
        on each varpool node.  Output .gnu_attribute according to
        avr_uses_long_double_p and avr_uses_double_p.
        * config/avr/avr-passes.cc (gimple-walk.h): Include.
        (avr_pass_has) <op_callback>: New static method.
        <scan_bb>: Use it in walk_gimple_op.
        <execute>: Set up walk_stmt_info and pass it to scan_bb.
        * config/avr/avr-passes.def (avr_pass_has): Adjust comment.
        * config/avr/avr-protos.h (avr_find_double): New proto.
diff --git a/gcc/config/avr/avr-passes.cc b/gcc/config/avr/avr-passes.cc
index 13bc1b125da..769520f3ec9 100644
--- a/gcc/config/avr/avr-passes.cc
+++ b/gcc/config/avr/avr-passes.cc
@@ -32,6 +32,7 @@
 #include "tree.h"
 #include "gimple.h"
 #include "gimple-iterator.h"
+#include "gimple-walk.h"
 #include "diagnostic-core.h"
 #include "cfghooks.h"
 #include "cfganal.h"
@@ -5560,7 +5561,11 @@ public:
 
 
 //////////////////////////////////////////////////////////////////////////////
-// Determine whether there are vtable calls, and set `avr_uses_vtable_p'.
+// Determine whether the target code uses some features:
+// - avr_uses_vtable_p: Are there vtable calls?
+// - avr_uses_double_p: Is double being used?
+// - avr_uses_long_double_p: Is long double being used?
+// In any case, avr_file_end handles objects in static storage.
 
 static const pass_data avr_pass_data_has =
 {
@@ -5584,7 +5589,24 @@ public:
     this->name = name;
   }
 
-  void scan_bb (basic_block bb)
+  static tree op_callback (tree *t, int *walk_subtrees, void *data)
+  {
+    // Almost all of a function's data is piped through SSA_NAMEs, so that
+    // for the purpose of avr_uses_[long_]double_p it is sufficient to look
+    // at these and compile-time constants.
+    if (SSA_VAR_P (*t)
+	|| TREE_CODE (*t) == REAL_CST
+	|| TREE_CODE (*t) == COMPLEX_CST)
+      {
+	walk_stmt_info *wi = (walk_stmt_info *) data;
+	avr_find_double (TREE_TYPE (*t), wi->pset);
+      }
+
+    *walk_subtrees = 1;
+    return NULL_TREE;
+  }
+
+  void scan_bb (basic_block bb, walk_stmt_info &wi)
   {
     gimple_stmt_iterator gsi;
     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
@@ -5596,6 +5618,8 @@ public:
 	    && (fncall = gimple_call_fn (stmt))
 	    && TREE_CODE (fncall) == OBJ_TYPE_REF)
 	  avr_uses_vtable_p = true;
+
+	walk_gimple_op (stmt, op_callback, &wi);
       }
   }
 
@@ -5608,9 +5632,13 @@ public:
 
   unsigned int execute (function *func) final override
   {
+    hash_set<tree> hset;
+    walk_stmt_info wi;
+    wi.pset = &hset;
+
     basic_block bb;
     FOR_ALL_BB_FN (bb, func)
-      scan_bb (bb);
+      scan_bb (bb, wi);
 
     return 0;
   }
diff --git a/gcc/config/avr/avr-passes.def b/gcc/config/avr/avr-passes.def
index d50285c3f89..95d82f8f0a5 100644
--- a/gcc/config/avr/avr-passes.def
+++ b/gcc/config/avr/avr-passes.def
@@ -17,8 +17,11 @@
    along with GCC; see the file COPYING3.  If not see
    <http://www.gnu.org/licenses/>.  */
 
-/* A gimple pass that sets `avr_uses_vtable_p', i.e. whether the target
-   code is using vtables.  Where the pass is inserted doesn't really matter,
+/* A gimple pass that sets
+   `avr_uses_vtable_p', i.e. whether the target code is using vtables.
+   `avr_uses_double_p', i.e. whether the target code is using double.
+   `avr_uses_long_double_p', i.e. whether the target code is using long double.
+   Where the pass is inserted doesn't really matter,
    though it should be an SSA pass that runs after LTO streaming.  */
 
 INSERT_PASS_AFTER (pass_musttail, 1, avr_pass_has);
diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h
index c91c6efc29b..8a971c0813e 100644
--- a/gcc/config/avr/avr-protos.h
+++ b/gcc/config/avr/avr-protos.h
@@ -38,6 +38,7 @@ 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);
+extern void avr_find_double (tree typ, hash_set<tree> *pset);
 
 #ifdef RTX_CODE /* inside TREE_CODE */
 extern void avr_init_cumulative_args (CUMULATIVE_ARGS*, tree, rtx, tree);
diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index dfc4d4526d8..cad3ff3bfea 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -256,7 +256,16 @@ bool avr_need_clear_bss_p = false;
 bool avr_need_copy_data_p = false;
 bool avr_has_rodata_p = false;
 
+/* Features used by the target code that are affected by the chosen ABI.
+   Output as .gnu_attribute in order to guarantee that only compatible
+   objects files are linked together.
+      Notice that for the latter two cases, there are loop holes like
+   sizeof(double).  Just mapping -mdouble=X to a .gnu_attribute *,X
+   would be too strict since libgcc may reuse a multilib provided it
+   doesn't depend on -m[long-]double.  */
 bool avr_uses_vtable_p = false;
+static bool avr_uses_double_p = false;
+static bool avr_uses_long_double_p = false;
 
 /* Counts how often pass avr-fuse-add has been executed.  It is kept in
    sync with cfun->machine->n_avr_fuse_add_executed and serves as an
@@ -12795,6 +12804,41 @@ avr_file_start (void)
 }
 
 
+/* Scan TYP for the occurence of [long] double, and set `avr_uses_double_p'
+   and `avr_uses_long_double_p' accordingly.  This is used by avr_file_end()
+   and also by the avr_pass_has gimple pass.
+   walk_tree() doesn't quite fit the puropse, so cook our own.  */
+
+void
+avr_find_double (tree typ, hash_set<tree> *pset)
+{
+  if (typ == NULL_TREE
+      || typ == error_mark_node
+      || pset->add (typ))
+    return;
+
+  if (POINTER_TYPE_P (typ))
+    return avr_find_double (TREE_TYPE (typ), pset);
+
+  if (TREE_CODE (typ) == ARRAY_TYPE)
+    return avr_find_double (strip_array_types (typ), pset);
+
+  if (RECORD_OR_UNION_TYPE_P (typ))
+    {
+      for (tree fld = TYPE_FIELDS (typ); fld; fld = DECL_CHAIN (fld))
+	if (TREE_CODE (fld) == FIELD_DECL)
+	  avr_find_double (TREE_TYPE (fld), pset);
+      return;
+    }
+
+  if (VECTOR_FLOAT_TYPE_P (typ) || COMPLEX_FLOAT_TYPE_P (typ))
+    typ = TREE_TYPE (typ);
+
+  avr_uses_double_p |= TYPE_MAIN_VARIANT (typ) == double_type_node;
+  avr_uses_long_double_p |= TYPE_MAIN_VARIANT (typ) == long_double_type_node;
+}
+
+
 /* Implement `TARGET_ASM_FILE_END'.  */
 /* Outputs to the stdio stream FILE some
    appropriate text to go at the end of an assembler file.  */
@@ -12816,9 +12860,9 @@ avr_file_end (void)
     fputs (".global __do_clear_bss\n", asm_out_file);
 
 #ifdef HAVE_AS_AVR_GNU_ATTRIBUTE
-  /* Output .gnu_attribute to tag object files with aspects of the ABI.
-     .gnu_attribute 4: The named address space for C++ virtual tables.  */
+  /* Output .gnu_attribute to tag object files with aspects of the ABI. */
 
+  hash_set<tree> hset;
   varpool_node *vnode;
 
   FOR_EACH_VARIABLE (vnode)
@@ -12826,11 +12870,26 @@ avr_file_end (void)
       const char *id = IDENTIFIER_POINTER (DECL_NAME (vnode->decl));
       avr_uses_vtable_p |= startswith (id, "_ZTV"); // vtable
       avr_uses_vtable_p |= startswith (id, "_ZTT"); // vtable table
+
+      avr_find_double (TREE_TYPE (vnode->decl), &hset);
     }
 
+  // .gnu_attribute 4: The named address space for C++ virtual tables.
   if (avr_uses_vtable_p)
     fprintf (asm_out_file, ".gnu_attribute %d,%d\n",
 	     Tag_GNU_AVR_VTABLE_AS, Val_GNU_AVR_VTABLE_RAM);
+
+  // .gnu_attribute 8: The bitsize of double, or 0 if not used.
+  if (avr_uses_double_p)
+    fprintf (asm_out_file, ".gnu_attribute %d,%d\n",
+	     Tag_GNU_AVR_BITS_DOUBLE,
+	     (int) (CHAR_BIT * int_size_in_bytes (double_type_node)));
+
+  // .gnu_attribute 12: The bitsize of long double, or 0 if not used.
+  if (avr_uses_long_double_p)
+    fprintf (asm_out_file, ".gnu_attribute %d,%d\n",
+	     Tag_GNU_AVR_BITS_LONG_DOUBLE,
+	     (int) (CHAR_BIT * int_size_in_bytes (long_double_type_node)));
 #endif // HAVE_AS_AVR_GNU_ATTRIBUTE
 }
 
diff --git a/gcc/config/avr/avr.md b/gcc/config/avr/avr.md
index 26ae9e501c9..405055bb077 100644
--- a/gcc/config/avr/avr.md
+++ b/gcc/config/avr/avr.md
@@ -113,7 +113,7 @@ (define_constants
    (GASISR_Done     0)
    ])
 
-;; Vtables address space are hard-coded in Binutils include/elf/avr.h.
+;; Repeat values for .gnu_attribute from Binutils include/elf/avr.h.
 (define_constants
  [(Tag_GNU_AVR_VTABLE_AS 4)
   (Val_GNU_AVR_VTABLE_NONE   0)
@@ -125,6 +125,9 @@ (define_constants
   (Val_GNU_AVR_VTABLE_FLASH4 6)
   (Val_GNU_AVR_VTABLE_FLASH5 7)
   (Val_GNU_AVR_VTABLE_FLASHX 8)
+
+  (Tag_GNU_AVR_BITS_DOUBLE       8)
+  (Tag_GNU_AVR_BITS_LONG_DOUBLE 12)
   ])
 
 (include "predicates.md")

Reply via email to