1#![feature(asm_const)]
2
3mod zeroable;
59
60use std::{any::TypeId, mem, ptr};
61
62pub use zeroable::Zeroable;
63
64const fn cmp_max(a: usize, b: usize) -> usize {
65 if a > b {
66 a
67 } else {
68 b
69 }
70}
71
72pub unsafe trait Namespace: 'static + Send + Sync + Copy + Clone {
79 #[inline(never)]
84 #[must_use]
85 fn generic_static<T: 'static + Zeroable>() -> &'static T {
86 #[allow(unused_assignments)]
87 let mut addr: *const () = ptr::null();
88
89 let type_id = TypeId::of::<(Self, T)> as *const ();
92
93 #[cfg(all(
94 target_arch = "aarch64",
95 any(target_os = "macos", target_os = "ios", target_os = "tvos")
96 ))]
97 unsafe {
98 std::arch::asm!(
99 "/* {type_id} */",
100 "adrp {x}, 1f@PAGE",
101 "add {x}, {x}, 1f@PAGEOFF",
102 ".pushsection __DATA,__data",
103 ".p2align {align}, 0",
104 "1: .zero {size}",
105 ".popsection",
106 size = const { cmp_max(mem::size_of::<T>(), 1) },
107 align = const { mem::align_of::<T>().ilog2() },
108 type_id = in(reg) type_id,
109 x = out(reg) addr,
110 options(nostack)
111 );
112 }
113
114 #[cfg(all(
115 target_arch = "aarch64",
116 any(target_os = "none", target_os = "linux", target_os = "freebsd")
117 ))]
118 unsafe {
119 std::arch::asm!(
120 "/* {type_id} */",
121 "adrp {x}, 1f",
122 "add {x}, {x}, :lo12:1f",
123 ".pushsection .bss.generic_statics,\"aw\",@nobits",
124 ".p2align {align}, 0",
125 "1: .zero {size}",
126 ".popsection",
127 size = const { cmp_max(mem::size_of::<T>(), 1) },
128 align = const { mem::align_of::<T>().ilog2() },
129 type_id = in(reg) type_id,
130 x = out(reg) addr,
131 options(nostack)
132 );
133 }
134
135 #[cfg(all(
136 target_arch = "x86_64",
137 any(target_os = "macos", target_os = "ios", target_os = "tvos")
138 ))]
139 unsafe {
140 std::arch::asm!(
141 "/* {type_id} */",
142 "lea {x}, [rip + 1f]",
143 ".pushsection __DATA,__data",
144 ".p2align {align}, 0",
145 "1: .zero {size}",
146 ".popsection",
147 size = const { cmp_max(mem::size_of::<T>(), 1) },
148 align = const { mem::align_of::<T>().ilog2() },
149 type_id = in(reg) type_id,
150 x = out(reg) addr,
151 options(nostack)
152 );
153 }
154
155 #[cfg(all(
156 target_arch = "x86_64",
157 any(target_os = "none", target_os = "linux", target_os = "freebsd")
158 ))]
159 unsafe {
160 std::arch::asm!(
161 "/* {type_id} */",
162 "lea {x}, [rip + 1f]",
163 ".pushsection .bss.generic_statics,\"aw\",@nobits",
164 ".p2align {align}, 0",
165 "1: .zero {size}",
166 ".popsection",
167 size = const { cmp_max(mem::size_of::<T>(), 1) },
168 align = const { mem::align_of::<T>().ilog2() },
169 type_id = in(reg) type_id,
170 x = out(reg) addr,
171 options(nostack)
172 );
173 }
174
175 #[cfg(all(target_arch = "x86_64", target_os = "windows"))]
176 unsafe {
177 std::arch::asm!(
178 "/* {type_id} */",
179 "lea {x}, [rip + 1f]",
180 ".pushsection .bss.generic_statics,\"bw\"",
181 ".p2align {align}, 0",
182 "1: .zero {size}",
183 ".popsection",
184 size = const { cmp_max(mem::size_of::<T>(), 1) },
185 align = const { mem::align_of::<T>().ilog2() },
186 type_id = in(reg) type_id,
187 x = out(reg) addr,
188 options(nostack)
189 );
190 }
191
192 #[cfg(not(any(
193 target_os = "none",
194 target_os = "linux",
195 target_os = "freebsd",
196 target_os = "macos",
197 target_os = "ios",
198 target_os = "tvos",
199 target_os = "windows",
200 )))]
201 std::compile_error!("static-generics is not supported on this platform");
202
203 assert!(!addr.is_null(), "unsupported platform");
204
205 unsafe { &*addr.cast::<T>() }
206 }
207}
208
209#[macro_export]
210macro_rules! define_namespace {
211 ($vis:vis $name:ident) => {
212 #[derive(Debug, Copy, Clone)]
213 $vis struct $name;
214
215 unsafe impl $crate::Namespace for $name {}
216 };
217}
218
219#[cfg(test)]
220mod tests {
221 use std::{
222 assert_ne,
223 marker::PhantomData,
224 sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering},
225 };
226
227 use super::Namespace;
228
229 define_namespace!(pub Test);
230
231 #[test]
232 fn stable_addr() {
233 let a = Test::generic_static::<*const ()>() as *const _;
234 let b = Test::generic_static::<*const ()>() as *const _;
235 assert_eq!(a, b);
236
237 let d = Test::generic_static::<(AtomicUsize, AtomicUsize, AtomicUsize)>() as *const _;
238 let e = Test::generic_static::<(AtomicUsize, AtomicUsize, AtomicUsize)>() as *const _;
239 assert_eq!(d, e);
240
241 assert_ne!(a as *const (), d as *const _ as *const ());
242 }
243
244 #[test]
245 fn unique_address() {
246 let a = Test::generic_static::<AtomicUsize>() as *const _ as *const ();
247 let b = Test::generic_static::<AtomicIsize>() as *const _ as *const ();
248 let c = Test::generic_static::<usize>() as *const _ as *const ();
249 let d = Test::generic_static::<AtomicPtr<()>>() as *const _ as *const ();
250
251 assert_ne!(a, b);
252 assert_ne!(a, c);
253 assert_ne!(a, d);
254 assert_ne!(b, c);
255 assert_ne!(b, d);
256 assert_ne!(c, d);
257 }
258
259 #[test]
260 fn unique_address_dyn() {
261 trait Foo<A: 'static> {}
262
263 let a = Test::generic_static::<PhantomData<dyn Foo<usize>>>() as *const _ as *const ();
264 let b = Test::generic_static::<PhantomData<dyn Foo<isize>>>() as *const _ as *const ();
265 let c = Test::generic_static::<PhantomData<dyn Foo<()>>>() as *const _ as *const ();
266
267 assert_ne!(a, b);
268 assert_ne!(a, c);
269 assert_ne!(b, c);
270 }
271
272 #[test]
273 fn mutation() {
274 let a = Test::generic_static::<AtomicUsize>();
275 assert_eq!(a.load(Ordering::Relaxed), 0);
276 a.store(42, Ordering::Relaxed);
277
278 let b = Test::generic_static::<AtomicUsize>();
279 assert_eq!(b.load(Ordering::Relaxed), 42);
280
281 let a2 = Test::generic_static::<AtomicIsize>();
282 assert_eq!(a2.load(Ordering::Relaxed), 0);
283 }
284}