Updated version with a better testcase that doesn't fail on platforms using an older linker.

Thanks and thanks to the rustc_codegen_gcc team for reviewing it and ACKing it :)

Best,

Arthur

---

From 31b188a0396c25cfca9f152f3a58d8188ab4274e Mon Sep 17 00:00:00 2001
From: Arthur Cohen <[email protected]>
Date: Wed, 16 Sep 2026 05:02:30 +0200
Subject: [PATCH] backend: Add proper handling for #[used] attribute.

We now support the full implementation of the #[used] attribute, which
can be used to conserve a static variable even if the compiler deems it
unused. This commit also implements #[used(linker)], which instructs the
linker to not garbage collect the static even if it seems unused.

This behavior is equivalent to the `used` and `retain` attributes in C.

gcc/rust/ChangeLog:

        * backend/rust-compile-item.cc (handle_used_attr): New function.
        (CompileItem::visit): Call it.

gcc/testsuite/ChangeLog:

        * rust/compile/used-attr4.rs: New test.
---
 gcc/rust/backend/rust-compile-item.cc    | 61 ++++++++++++++++++------
 gcc/testsuite/rust/compile/used-attr4.rs | 16 +++++++
 2 files changed, 63 insertions(+), 14 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/used-attr4.rs

diff --git a/gcc/rust/backend/rust-compile-item.cc b/gcc/rust/backend/rust-compile-item.cc
index fa1e8eec99f..d9afdcacc3c 100644
--- a/gcc/rust/backend/rust-compile-item.cc
+++ b/gcc/rust/backend/rust-compile-item.cc
@@ -23,10 +23,53 @@
 #include "rust-substitution-mapper.h"
 #include "rust-type-util.h"
 #include "rust-finalized-name-resolution-context.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "target.h"

 namespace Rust {
 namespace Compile {

+static void
+handle_used_attr (HIR::StaticItem &var, tree decl)
+{
+  auto used_compiler = false; /* equivalent to C's used attribute */
+  auto used_linker = false;   /* equivalent to C's retain attribute */
+
+ // If the attribute has no argument, then it is equivalent to `used(compiler)`
+  for (const auto &attr : var.get_outer_attrs ())
+    {
+      if (attr.as_string () == "used")
+       used_compiler = true;
+
+ // We've already checked that the attribute was used properly, so just
+      // fetch the information we need
+      if (attr.has_attr_input ())
+       {
+         auto input
+           = static_cast<const AST::DelimTokenTree &> (attr.get_attr_input ())
+               .parse_to_meta_item ();
+
+         for (const auto &item : input->get_items ())
+           if (item->as_string () == "linker")
+             used_linker = true;
+       }
+    }
+
+  if (used_compiler)
+    {
+      DECL_PRESERVE_P (decl) = 1;
+      DECL_READ_P (decl) = 1;
+    }
+
+  // Check that the linker supports the attribute, and then add it
+  if (SUPPORTS_SHF_GNU_RETAIN && used_linker)
+    {
+      DECL_ATTRIBUTES (decl)
+       = tree_cons (get_identifier ("retain"), NULL, DECL_ATTRIBUTES (decl));
+    }
+}
+
 void
 CompileItem::visit (HIR::StaticItem &var)
 {
@@ -71,24 +114,14 @@ CompileItem::visit (HIR::StaticItem &var)
   bool is_hidden = false;
   bool in_unique_section = true;

- // NOTE: We currently compile global variables and always mark them as "used" - // with TREE_USED(tree). I'm not sure this is correct - they should be marked - // as used only if the `MarkLive` pass reaches them and flags them. However,
-  // the `#[used]` built-in attribute can be used here to *force* a static
-  // variable to be used by the compiler so that it isn't deleted.
-  // We should probably be doing something along the lines of:
-  //
- // - Removing the `TREE_USED (decl) = 1` line in `Backend::global_variable`
-  // - Check if the static item has a `used` attribute
- // - If it does, check whether it's the default `used` version, or the version
-  // with an argument - `used(compiler)` or `used(linker)`
- // - If we have the attribute or the `compiler` version of the attribute, then
-  // we `TREE_USED` the decl.
-
   Bvariable *static_global
= Backend::global_variable (name, asm_name, type, is_external, is_hidden,
                                in_unique_section, var.get_locus ());

+  // Static items are concerned by the #[used] attribute which needs to be
+  // handled here
+  handle_used_attr (var, static_global->get_decl ());
+
tree init = value == error_mark_node ? error_mark_node : DECL_INITIAL (value);
   Backend::global_variable_set_init (static_global, init);

diff --git a/gcc/testsuite/rust/compile/used-attr4.rs b/gcc/testsuite/rust/compile/used-attr4.rs
new file mode 100644
index 00000000000..18078a8ca1f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/used-attr4.rs
@@ -0,0 +1,16 @@
+#![feature(no_core)]
+#![feature(used_with_arg)]
+#![no_core]
+
+#[used]
+static FOO0: i32 = 15;
+// { dg-final { scan-assembler "FOO0" } }
+
+#[used(compiler)]
+static FOO1: i32 = 15;
+// { dg-final { scan-assembler "FOO1" } }
+
+#[used(linker)]
+static FOO2: i32 = 15;
+// { dg-final { scan-assembler "FOO2" } }
+// { dg-final { scan-assembler ".section.*awR" { target gnu_retain } } }
--
2.50.1


Reply via email to