c2rust_asm_casts/
lib.rs

1#![no_std]
2use core::marker::PhantomData;
3
4/// Pseudo-structure that provides the inner type definition
5/// and cast functions for every pair of types used
6/// in C2Rust's implementation of tied inline assembly operands.
7/// For two tied operands of types `In` and `Out`, this
8/// implementation provides the smallest type that can
9/// hold both operands, along with the casts to convert
10/// each operand to this type.
11pub struct AsmCast<Out, In>(PhantomData<(Out, In)>);
12
13/// This trait implements the cast functions for the type pair
14pub trait AsmCastTrait<Out, In> {
15    type Type;
16
17    fn cast_in(_: &mut Out, x: In) -> Self::Type;
18    fn cast_out(out: &mut Out, _: In, x: Self::Type);
19}
20
21macro_rules! impl_triple {
22	{<$($param:ident),*> ($out:ty, $in:ty) => $inner:ty} => {
23		impl<$($param),*> AsmCastTrait<$out, $in> for AsmCast<$out, $in> {
24			type Type = $inner;
25
26			fn cast_in(_: &mut $out, x: $in) -> Self::Type {
27				x as Self::Type
28			}
29
30			fn cast_out(out: &mut $out, _: $in, x: Self::Type) {
31				*out = x as $out;
32			}
33		}
34	}
35}
36
37macro_rules! impl_triple2 {
38	{<$($param:ident),*> ($out:ty, $in:ty) => $inner:ty} => {
39        impl_triple!{<$($param),*> ($out, $in) => $inner}
40        impl_triple!{<$($param),*> ($in, $out) => $inner}
41	}
42}
43
44macro_rules! impl_pair_higher {
45	{$inner:ty: [$(<$($param:ident),*> $ty1:ty),*]} => {
46        impl_triple!{<> ($inner, $inner) => $inner}
47		$(impl_triple2!{<$($param),*> ($inner, $ty1) => $inner})*
48	}
49}
50
51macro_rules! impl_triple_list {
52	{$inner:ty: [$($ty1:ty: ($(<$($param:ident),*> $ty2:ty),*)),*]} => {
53		$($(impl_triple2!{<$($param),*> ($ty1, $ty2) => $inner})*)*
54	}
55}
56
57// Any pair of pointers
58impl_triple! {<T, U> (*const T, *const U) => usize}
59impl_triple! {<T, U> (*const T, *mut U) => usize}
60impl_triple! {<T, U> (*mut T, *const U) => usize}
61impl_triple! {<T, U> (*mut T, *mut U) => usize}
62
63impl_pair_higher! {u8:  [<>i8]}
64impl_pair_higher! {u16: [<>u8, <>i8, <>i16]}
65impl_pair_higher! {u32: [<>u8, <>u16, <>i8, <>i16, <>i32]}
66impl_pair_higher! {u64: [<>u8, <>u16, <>u32, <>i8, <>i16, <>i32, <>i64, <>usize, <>isize]}
67impl_pair_higher! {i8:  []}
68impl_pair_higher! {i16: [<>u8, <>i8]}
69impl_pair_higher! {i32: [<>u8, <>u16, <>i8, <>i16]}
70impl_pair_higher! {i64: [<>u8, <>u16, <>u32, <>i8, <>i16, <>i32, <>usize, <>isize]}
71impl_pair_higher! {usize: [<>u8, <>u16, <>i8, <>i16, <>isize]}
72impl_pair_higher! {isize: [<>u8, <>u16, <>i8, <>i16]}
73
74// Types that are always smaller than a pointer
75impl_triple_list! {usize: [u8: (<T>*const T, <T>*mut T),
76                          u16: (<T>*const T, <T>*mut T),
77                          i8: (<T>*const T, <T>*mut T),
78                          i16: (<T>*const T, <T>*mut T),
79                          usize: (<T>*const T, <T>*mut T),
80                          isize: (<T>*const T, <T>*mut T)]
81}
82
83// Types that are always larger than a pointer
84impl_triple_list! {u64: [u64: (<T>*const T, <T>*mut T)]}
85impl_triple_list! {i64: [i64: (<T>*const T, <T>*mut T)]}
86
87// Target-specific types
88#[cfg(target_pointer_width = "64")]
89impl_triple_list! {u64: [usize: (<>u32, <>i32),
90                        isize: (<>u32, <>i32),
91                        u32: (<T>*const T, <T>*mut T),
92                        i32: (<T>*const T, <T>*mut T)]
93}
94
95#[cfg(target_pointer_width = "32")]
96impl_triple_list! {u32: [usize: (<>u32, <>i32),
97                        isize: (<>u32, <>i32),
98                        u32: (<T>*const T, <T>*mut T),
99                        i32: (<T>*const T, <T>*mut T)]
100}
101
102#[cfg(target_pointer_width = "16")]
103impl_triple_list! {u32: [usize: (<>u32, <>i32),
104                        isize: (<>u32, <>i32),
105                        u32: (<T>*const T, <T>*mut T),
106                        i32: (<T>*const T, <T>*mut T)]
107}
108
109// FIXME: for now, we assume that we have a 16-, 32- or 64-bit architecture
110// we'll need to handle other sizes if we ever encounter any
111
112#[cfg(test)]
113mod tests {
114    /// `#[allow(clippy::zero_ptr)]` is used so `0 as $ty2`
115    /// can be used for both integer and pointer types.
116    /// Otherwise for pointer types, `rustc` suggests using `core::ptr::null::<T>()`.
117    /// That should be needed for such a simple test;
118    /// it would make the test code a lot more verbose and duplicated.
119    #[allow(clippy::zero_ptr)]
120    #[test]
121    fn test_coverage() {
122        macro_rules! test_combo {
123            ($ty1:ty, [$($ty2:ty),*]) => {
124                $({
125                    use super::{AsmCast, AsmCastTrait};
126
127                    let x = 42usize as $ty1;
128                    let mut y: $ty2 = 0 as $ty2;
129                    let z = AsmCast::cast_in(&mut y, x) + 1;
130                    AsmCast::cast_out(&mut y, x, z);
131                    assert_eq!(y as u64, 43);
132                })*
133            }
134        }
135
136        // Test all combinations of types to make sure we cover them
137        test_combo!(u8, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
138        test_combo!(u16, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
139        test_combo!(u32, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
140        test_combo!(u64, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
141        test_combo!(usize, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
142        test_combo!(i8, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
143        test_combo!(i16, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
144        test_combo!(i32, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
145        test_combo!(i64, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
146        test_combo!(isize, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
147        test_combo!(*const u8, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
148        test_combo!(*mut u8, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
149        test_combo!(*const u16, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
150        test_combo!(*mut u16, [u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, *const u8, *mut u8]);
151    }
152}