about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFolkert de Vries <folkert@folkertdev.nl>2025-06-07 22:56:22 +0200
committerFolkert de Vries <folkert@folkertdev.nl>2025-09-09 21:54:54 +0200
commitcbacd00f106778830803ad2e2364518f06ff9078 (patch)
tree4e17ddefcf9c27f5d7bacf37f7f5c57b81ce1679
parentc559c4a741836c4ffa8e4f60cb9fe7e92af5298e (diff)
downloadrust-cbacd00f106778830803ad2e2364518f06ff9078.tar.gz
rust-cbacd00f106778830803ad2e2364518f06ff9078.zip
allow `#[rustc_align_static(N)]` on `static`s
We need a different attribute than `rustc_align` because unstable attributes are
tied to their feature (we can't have two unstable features use the same
unstable attribute). Otherwise this uses all of the same infrastructure
as `#[rustc_align]`.
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/repr.rs27
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs3
-rw-r--r--compiler/rustc_codegen_gcc/src/consts.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs1
-rw-r--r--compiler/rustc_const_eval/src/interpret/util.rs11
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs1
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs11
-rw-r--r--compiler/rustc_passes/messages.ftl4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs8
-rw-r--r--compiler/rustc_passes/src/errors.rs9
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--src/tools/miri/tests/pass/static_align.rs14
-rw-r--r--tests/codegen-llvm/align-static.rs31
-rw-r--r--tests/ui/attributes/malformed-static-align.rs17
-rw-r--r--tests/ui/attributes/malformed-static-align.stderr45
-rw-r--r--tests/ui/feature-gates/feature-gate-static_align.rs11
-rw-r--r--tests/ui/feature-gates/feature-gate-static_align.stderr23
-rw-r--r--tests/ui/static/static-align.rs26
21 files changed, 246 insertions, 5 deletions
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index ffdacff7152..d5d51f2e79a 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -218,6 +218,7 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
             sym::rustc_std_internal_symbol,
             // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
             sym::rustc_align,
+            sym::rustc_align_static,
             // obviously compatible with self
             sym::naked,
             // documentation
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index 23aabd15597..0330e2515c7 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -331,3 +331,30 @@ impl<S: Stage> AttributeParser<S> for AlignParser {
         Some(AttributeKind::Align { align, span })
     }
 }
+
+#[derive(Default)]
+pub(crate) struct AlignStaticParser(AlignParser);
+
+impl AlignStaticParser {
+    const PATH: &'static [Symbol] = &[sym::rustc_align_static];
+    const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE;
+
+    fn parse<'c, S: Stage>(
+        &mut self,
+        cx: &'c mut AcceptContext<'_, '_, S>,
+        args: &'c ArgParser<'_>,
+    ) {
+        self.0.parse(cx, args)
+    }
+}
+
+impl<S: Stage> AttributeParser<S> for AlignStaticParser {
+    const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
+    const ALLOWED_TARGETS: AllowedTargets =
+        AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
+
+    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
+        let (align, span) = self.0.0?;
+        Some(AttributeKind::Align { align, span })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 7f5b810f244..1fb0660f534 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -47,7 +47,7 @@ use crate::attributes::proc_macro_attrs::{
     ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
 };
 use crate::attributes::prototype::CustomMirParser;
-use crate::attributes::repr::{AlignParser, ReprParser};
+use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
 use crate::attributes::rustc_internal::{
     RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
     RustcObjectLifetimeDefaultParser,
@@ -149,6 +149,7 @@ attribute_parsers!(
     pub(crate) static ATTRIBUTE_PARSERS = [
         // tidy-alphabetical-start
         AlignParser,
+        AlignStaticParser,
         BodyStabilityParser,
         ConfusablesParser,
         ConstStabilityParser,
diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs
index 619277eba8b..7fe8fc122b3 100644
--- a/compiler/rustc_codegen_gcc/src/consts.rs
+++ b/compiler/rustc_codegen_gcc/src/consts.rs
@@ -81,6 +81,8 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> {
         if global.to_rvalue().get_type() != val_llty {
             global.to_rvalue().set_type(val_llty);
         }
+
+        // NOTE: Alignment from attributes has already been applied to the allocation.
         set_global_alignment(self, global, alloc.align);
 
         global.global_set_initializer_rvalue(value);
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 9ec7b0f80ae..dc9bb743560 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -452,6 +452,8 @@ impl<'ll> CodegenCx<'ll, '_> {
             self.statics_to_rauw.borrow_mut().push((g, new_g));
             new_g
         };
+
+        // NOTE: Alignment from attributes has already been applied to the allocation.
         set_global_alignment(self, g, alloc.align);
         llvm::set_initializer(g, v);
 
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 2c1e5087e1c..67e1f6b2fbd 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -953,6 +953,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
         // # Global allocations
         if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) {
+            // NOTE: `static` alignment from attributes has already been applied to the allocation.
             let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env);
             let mutbl = global_alloc.mutability(*self.tcx, self.typing_env);
             let kind = match global_alloc {
diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs
index 72bee345406..f667823723c 100644
--- a/compiler/rustc_const_eval/src/interpret/util.rs
+++ b/compiler/rustc_const_eval/src/interpret/util.rs
@@ -1,6 +1,6 @@
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::mir;
-use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer};
+use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer};
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt};
 use tracing::debug;
@@ -38,7 +38,14 @@ pub(crate) fn create_static_alloc<'tcx>(
     static_def_id: LocalDefId,
     layout: TyAndLayout<'tcx>,
 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
-    let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit, ())?;
+    // Inherit size and align from the `GlobalAlloc::Static` so we can avoid duplicating
+    // the alignment attribute logic.
+    let (size, align) =
+        GlobalAlloc::Static(static_def_id.into()).size_and_align(*ecx.tcx, ecx.typing_env);
+    assert_eq!(size, layout.size);
+    assert!(align >= layout.align.abi);
+
+    let alloc = Allocation::try_new(size, align, AllocInit::Uninit, ())?;
     let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into());
     assert_eq!(ecx.machine.static_root_ids, None);
     ecx.machine.static_root_ids = Some((alloc_id, static_def_id));
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index e81003b1897..129ab7eccb5 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -621,6 +621,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
     gated!(rustc_align, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(rustc_align)),
+    gated!(rustc_align_static, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, static_align, experimental!(rustc_align_static)),
     ungated!(
         unsafe(Edition2024) export_name, Normal,
         template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute"),
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 4f35bf63a1a..93e5588146e 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -632,6 +632,8 @@ declare_features! (
     (unstable, simd_ffi, "1.0.0", Some(27731)),
     /// Allows specialization of implementations (RFC 1210).
     (incomplete, specialization, "1.7.0", Some(31844)),
+    /// Allows using `#[rustc_align_static(...)]` on static items.
+    (unstable, static_align, "CURRENT_RUSTC_VERSION", Some(146177)),
     /// Allows attributes on expressions and non-item statements.
     (unstable, stmt_expr_attributes, "1.6.0", Some(15701)),
     /// Allows lints part of the strict provenance effort.
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index bed99a4ff2a..9762e0f21da 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -386,7 +386,16 @@ impl<'tcx> GlobalAlloc<'tcx> {
                         .expect("statics should not have generic parameters");
                     let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap();
                     assert!(layout.is_sized());
-                    (layout.size, layout.align.abi)
+
+                    // Take over-alignment from attributes into account.
+                    let align = match tcx.codegen_fn_attrs(def_id).alignment {
+                        Some(align_from_attribute) => {
+                            Ord::max(align_from_attribute, layout.align.abi)
+                        }
+                        None => layout.align.abi,
+                    };
+
+                    (layout.size, align)
                 }
             }
             GlobalAlloc::Memory(alloc) => {
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index afd08319738..ab7885905a6 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -501,6 +501,10 @@ passes_repr_align_should_be_align =
     `#[repr(align(...))]` is not supported on {$item}
     .help = use `#[rustc_align(...)]` instead
 
+passes_repr_align_should_be_align_static =
+    `#[repr(align(...))]` is not supported on {$item}
+    .help = use `#[rustc_align_static(...)]` instead
+
 passes_repr_conflicting =
     conflicting representation hints
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 2cd4830b5d9..10585e7b920 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -1602,12 +1602,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 ReprAttr::ReprAlign(align) => {
                     match target {
                         Target::Struct | Target::Union | Target::Enum => {}
-                        Target::Fn | Target::Method(_) => {
+                        Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => {
                             self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
                                 span: *repr_span,
                                 item: target.plural_name(),
                             });
                         }
+                        Target::Static if self.tcx.features().static_align() => {
+                            self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic {
+                                span: *repr_span,
+                                item: target.plural_name(),
+                            });
+                        }
                         _ => {
                             self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
                                 hint_span: *repr_span,
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 23dcabef1a1..2da4b6f52cf 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1610,6 +1610,15 @@ pub(crate) struct ReprAlignShouldBeAlign {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_repr_align_should_be_align_static)]
+pub(crate) struct ReprAlignShouldBeAlignStatic {
+    #[primary_span]
+    #[help]
+    pub span: Span,
+    pub item: &'static str,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_custom_mir_phase_requires_dialect)]
 pub(crate) struct CustomMirPhaseRequiresDialect {
     #[primary_span]
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index e5108d8b7e9..ac933b20608 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1846,6 +1846,7 @@ symbols! {
         rustc_abi,
         // FIXME(#82232, #143834): temporary name to mitigate `#[align]` nameres ambiguity
         rustc_align,
+        rustc_align_static,
         rustc_allocator,
         rustc_allocator_zeroed,
         rustc_allocator_zeroed_variant,
@@ -2097,6 +2098,7 @@ symbols! {
         staged_api,
         start,
         state,
+        static_align,
         static_in_const,
         static_nobundle,
         static_recursion,
diff --git a/src/tools/miri/tests/pass/static_align.rs b/src/tools/miri/tests/pass/static_align.rs
new file mode 100644
index 00000000000..f292f028568
--- /dev/null
+++ b/src/tools/miri/tests/pass/static_align.rs
@@ -0,0 +1,14 @@
+#![feature(static_align)]
+
+// When a static uses `align(N)`, its address should be a multiple of `N`.
+
+#[rustc_align_static(256)]
+static FOO: u64 = 0;
+
+#[rustc_align_static(512)]
+static BAR: u64 = 0;
+
+fn main() {
+    assert!(core::ptr::from_ref(&FOO).addr().is_multiple_of(256));
+    assert!(core::ptr::from_ref(&BAR).addr().is_multiple_of(512));
+}
diff --git a/tests/codegen-llvm/align-static.rs b/tests/codegen-llvm/align-static.rs
new file mode 100644
index 00000000000..53db998919a
--- /dev/null
+++ b/tests/codegen-llvm/align-static.rs
@@ -0,0 +1,31 @@
+//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0
+
+#![crate_type = "lib"]
+#![feature(static_align)]
+
+// CHECK: @STATIC_ALIGN =
+// CHECK-SAME: align 16
+#[no_mangle]
+#[rustc_align_static(16)]
+pub static STATIC_ALIGN: u64 = 0;
+
+// CHECK: @ALIGN_SPECIFIED_TWICE_1 =
+// CHECK-SAME: align 64
+#[no_mangle]
+#[rustc_align_static(32)]
+#[rustc_align_static(64)]
+pub static ALIGN_SPECIFIED_TWICE_1: u64 = 0;
+
+// CHECK: @ALIGN_SPECIFIED_TWICE_2 =
+// CHECK-SAME: align 128
+#[no_mangle]
+#[rustc_align_static(128)]
+#[rustc_align_static(32)]
+pub static ALIGN_SPECIFIED_TWICE_2: u64 = 0;
+
+// CHECK: @ALIGN_SPECIFIED_TWICE_3 =
+// CHECK-SAME: align 256
+#[no_mangle]
+#[rustc_align_static(32)]
+#[rustc_align_static(256)]
+pub static ALIGN_SPECIFIED_TWICE_3: u64 = 0;
diff --git a/tests/ui/attributes/malformed-static-align.rs b/tests/ui/attributes/malformed-static-align.rs
new file mode 100644
index 00000000000..305d8acf8af
--- /dev/null
+++ b/tests/ui/attributes/malformed-static-align.rs
@@ -0,0 +1,17 @@
+#![feature(static_align)]
+#![crate_type = "lib"]
+
+#[rustc_align_static = 16] //~ ERROR malformed `rustc_align_static` attribute input
+static S1: () = ();
+
+#[rustc_align_static("hello")] //~ ERROR invalid alignment value: not an unsuffixed integer
+static S2: () = ();
+
+#[rustc_align_static(0)] //~ ERROR invalid alignment value: not a power of two
+static S3: () = ();
+
+#[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on static
+static S4: () = ();
+
+#[rustc_align_static(16)] //~ ERROR `#[rustc_align_static]` attribute cannot be used on structs
+struct Struct1;
diff --git a/tests/ui/attributes/malformed-static-align.stderr b/tests/ui/attributes/malformed-static-align.stderr
new file mode 100644
index 00000000000..35f654d3990
--- /dev/null
+++ b/tests/ui/attributes/malformed-static-align.stderr
@@ -0,0 +1,45 @@
+error[E0539]: malformed `rustc_align_static` attribute input
+  --> $DIR/malformed-static-align.rs:4:1
+   |
+LL | #[rustc_align_static = 16]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | expected this to be a list
+   | help: must be of the form: `#[rustc_align_static(<alignment in bytes>)]`
+
+error[E0589]: invalid alignment value: not an unsuffixed integer
+  --> $DIR/malformed-static-align.rs:7:22
+   |
+LL | #[rustc_align_static("hello")]
+   |                      ^^^^^^^
+
+error[E0589]: invalid alignment value: not a power of two
+  --> $DIR/malformed-static-align.rs:10:22
+   |
+LL | #[rustc_align_static(0)]
+   |                      ^
+
+error: `#[rustc_align_static]` attribute cannot be used on structs
+  --> $DIR/malformed-static-align.rs:16:1
+   |
+LL | #[rustc_align_static(16)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: `#[rustc_align_static]` can be applied to statics and foreign statics
+
+error: `#[repr(align(...))]` is not supported on statics
+  --> $DIR/malformed-static-align.rs:13:8
+   |
+LL | #[repr(align(16))]
+   |        ^^^^^^^^^
+   |
+help: use `#[rustc_align_static(...)]` instead
+  --> $DIR/malformed-static-align.rs:13:8
+   |
+LL | #[repr(align(16))]
+   |        ^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0539, E0589.
+For more information about an error, try `rustc --explain E0539`.
diff --git a/tests/ui/feature-gates/feature-gate-static_align.rs b/tests/ui/feature-gates/feature-gate-static_align.rs
new file mode 100644
index 00000000000..4d8f0e18d94
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-static_align.rs
@@ -0,0 +1,11 @@
+#![crate_type = "lib"]
+
+#[rustc_align_static(16)]
+//~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature
+static REQUIRES_ALIGNMENT: u64 = 0;
+
+extern "C" {
+    #[rustc_align_static(16)]
+    //~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature
+    static FOREIGN_STATIC: u32;
+}
diff --git a/tests/ui/feature-gates/feature-gate-static_align.stderr b/tests/ui/feature-gates/feature-gate-static_align.stderr
new file mode 100644
index 00000000000..b45fcdefc9c
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-static_align.stderr
@@ -0,0 +1,23 @@
+error[E0658]: the `#[rustc_align_static]` attribute is an experimental feature
+  --> $DIR/feature-gate-static_align.rs:3:1
+   |
+LL | #[rustc_align_static(16)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #146177 <https://github.com/rust-lang/rust/issues/146177> for more information
+   = help: add `#![feature(static_align)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the `#[rustc_align_static]` attribute is an experimental feature
+  --> $DIR/feature-gate-static_align.rs:8:5
+   |
+LL |     #[rustc_align_static(16)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #146177 <https://github.com/rust-lang/rust/issues/146177> for more information
+   = help: add `#![feature(static_align)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/static/static-align.rs b/tests/ui/static/static-align.rs
new file mode 100644
index 00000000000..93241db09f9
--- /dev/null
+++ b/tests/ui/static/static-align.rs
@@ -0,0 +1,26 @@
+//@ run-pass
+#![feature(static_align)]
+
+#[rustc_align_static(64)]
+static A: u8 = 0;
+
+#[rustc_align_static(64)]
+static B: u8 = 0;
+
+#[rustc_align_static(128)]
+#[no_mangle]
+static EXPORTED: u64 = 0;
+
+unsafe extern "C" {
+    #[rustc_align_static(128)]
+    #[link_name = "EXPORTED"]
+    static C: u64;
+}
+
+fn main() {
+    assert!(core::ptr::from_ref(&A).addr().is_multiple_of(64));
+    assert!(core::ptr::from_ref(&B).addr().is_multiple_of(64));
+
+    assert!(core::ptr::from_ref(&EXPORTED).addr().is_multiple_of(128));
+    unsafe { assert!(core::ptr::from_ref(&C).addr().is_multiple_of(128)) };
+}