Skip to main content

core/stdarch/crates/core_arch/src/aarch64/
mte.rs

1//! AArch64 Memory tagging intrinsics
2//!
3//! [ACLE documentation](https://arm-software.github.io/acle/main/acle.html#markdown-toc-mte-intrinsics)
4
5unsafe extern "unadjusted" {
6    #[link_name = "llvm.aarch64.irg"]
7    fn irg_(ptr: *const (), exclude: i64) -> *const ();
8    #[link_name = "llvm.aarch64.gmi"]
9    fn gmi_(ptr: *const (), exclude: i64) -> i64;
10    #[link_name = "llvm.aarch64.ldg"]
11    fn ldg_(ptr: *const (), tag_ptr: *const ()) -> *const ();
12    #[link_name = "llvm.aarch64.stg"]
13    fn stg_(tagged_ptr: *const (), addr_to_tag: *const ());
14    #[link_name = "llvm.aarch64.addg"]
15    fn addg_(ptr: *const (), value: i64) -> *const ();
16    #[link_name = "llvm.aarch64.subp"]
17    fn subp_(ptr_a: *const (), ptr_b: *const ()) -> i64;
18}
19
20/// Return a pointer containing a randomly generated logical address tag.
21///
22/// `src`: A pointer containing an address.
23/// `mask`: A mask where each of the lower 16 bits specifies logical
24///         tags which must be excluded from consideration. Zero excludes no
25///         tags.
26///
27/// The returned pointer contains a copy of the `src` address, but with a
28/// randomly generated logical tag, excluding any specified by `mask`.
29///
30/// SAFETY: The pointer provided by this intrinsic will be invalid until the memory
31/// has been appropriately tagged with `__arm_mte_set_tag`. If using that intrinsic
32/// on the provided pointer is itself invalid, then it will be permanently invalid
33/// and Undefined Behavior to dereference it.
34#[inline]
35#[target_feature(enable = "mte")]
36#[unstable(feature = "stdarch_aarch64_mte", issue = "129010")]
37pub unsafe fn __arm_mte_create_random_tag<T>(src: *const T, mask: u64) -> *const T {
38    irg_(src as *const (), mask as i64) as *const T
39}
40
41/// Return a pointer with the logical address tag offset by a value.
42///
43/// `src`: A pointer containing an address and a logical tag.
44/// `OFFSET`: A compile-time constant value in the range [0, 15].
45///
46/// Adds offset to the logical address tag in `src`, wrapping if the result is
47/// outside of the valid 16 tags.
48///
49/// SAFETY: See `__arm_mte_create_random_tag`.
50#[inline]
51#[target_feature(enable = "mte")]
52#[unstable(feature = "stdarch_aarch64_mte", issue = "129010")]
53pub unsafe fn __arm_mte_increment_tag<const OFFSET: i64, T>(src: *const T) -> *const T {
54    addg_(src as *const (), OFFSET) as *const T
55}
56
57/// Add a logical tag to the set of excluded logical tags.
58///
59/// `src`: A pointer containing an address and a logical tag.
60/// `excluded`: A mask where the lower 16 bits each specify currently-excluded
61///             logical tags.
62///
63/// Adds the logical tag stored in `src` to the set in `excluded`, and returns
64/// the result.
65#[inline]
66#[target_feature(enable = "mte")]
67#[unstable(feature = "stdarch_aarch64_mte", issue = "129010")]
68pub unsafe fn __arm_mte_exclude_tag<T>(src: *const T, excluded: u64) -> u64 {
69    gmi_(src as *const (), excluded as i64) as u64
70}
71
72/// Store an allocation tag for the 16-byte granule of memory.
73///
74/// `tag_address`: A pointer containing an address and a logical tag, which
75///                must be 16-byte aligned.
76///
77/// SAFETY: `tag_address` must be 16-byte aligned. The tag will apply to the
78/// entire 16-byte memory granule.
79#[inline]
80#[target_feature(enable = "mte")]
81#[unstable(feature = "stdarch_aarch64_mte", issue = "129010")]
82pub unsafe fn __arm_mte_set_tag<T>(tag_address: *const T) {
83    stg_(tag_address as *const (), tag_address as *const ());
84}
85
86/// Load an allocation tag from memory, returning a new pointer with the
87/// corresponding logical tag.
88///
89/// `address`: A pointer containing an address from which allocation tag memory
90///            is read. This does not need to be 16-byte aligned.
91#[inline]
92#[target_feature(enable = "mte")]
93#[unstable(feature = "stdarch_aarch64_mte", issue = "129010")]
94pub unsafe fn __arm_mte_get_tag<T>(address: *const T) -> *const T {
95    ldg_(address as *const (), address as *const ()) as *const T
96}
97
98/// Calculate the difference between the address parts of two pointers, ignoring
99/// the tags, and sign-extending the result.
100#[inline]
101#[target_feature(enable = "mte")]
102#[unstable(feature = "stdarch_aarch64_mte", issue = "129010")]
103pub unsafe fn __arm_mte_ptrdiff<T, U>(a: *const T, b: *const U) -> i64 {
104    subp_(a as *const (), b as *const ())
105}
106
107#[cfg(test)]
108mod test {
109    use super::*;
110    use stdarch_test::assert_instr;
111
112    // Instruction tests are separate because the functions use generics.
113    //
114    // FIXME: As of 2026 MSVC  `dumpbin` doesn't support MTE.
115
116    #[cfg_attr(not(target_env = "msvc"), assert_instr(irg))]
117    #[allow(dead_code)]
118    #[target_feature(enable = "mte")]
119    unsafe fn test_arm_mte_create_random_tag(src: *const (), mask: u64) -> *const () {
120        __arm_mte_create_random_tag(src, mask)
121    }
122
123    #[cfg_attr(not(target_env = "msvc"), assert_instr(addg))]
124    #[allow(dead_code)]
125    #[target_feature(enable = "mte")]
126    unsafe fn test_arm_mte_increment_tag(src: *const ()) -> *const () {
127        __arm_mte_increment_tag::<1, _>(src)
128    }
129
130    #[cfg_attr(not(target_env = "msvc"), assert_instr(gmi))]
131    #[allow(dead_code)]
132    #[target_feature(enable = "mte")]
133    unsafe fn test_arm_mte_exclude_tag(src: *const (), excluded: u64) -> u64 {
134        __arm_mte_exclude_tag(src, excluded)
135    }
136
137    #[cfg_attr(not(target_env = "msvc"), assert_instr(stg))]
138    #[allow(dead_code)]
139    #[target_feature(enable = "mte")]
140    unsafe fn test_arm_mte_set_tag(src: *const ()) {
141        __arm_mte_set_tag(src)
142    }
143
144    #[cfg_attr(not(target_env = "msvc"), assert_instr(ldg))]
145    #[allow(dead_code)]
146    #[target_feature(enable = "mte")]
147    unsafe fn test_arm_mte_get_tag(src: *const ()) -> *const () {
148        __arm_mte_get_tag(src)
149    }
150
151    #[cfg_attr(not(target_env = "msvc"), assert_instr(subp))]
152    #[allow(dead_code)]
153    #[target_feature(enable = "mte")]
154    unsafe fn test_arm_mte_ptrdiff(a: *const (), b: *const ()) -> i64 {
155        __arm_mte_ptrdiff(a, b)
156    }
157}