qir_stdlib/
arrays.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::{strings::convert, update_counts};
5use std::{mem::ManuallyDrop, rc::Rc};
6
7#[cfg(not(feature = "fail-support"))]
8#[allow(improper_ctypes)]
9extern "C" {
10    fn __quantum__rt__fail(str: *const std::ffi::CString);
11}
12
13#[cfg(feature = "fail-support")]
14use crate::__quantum__rt__fail;
15
16#[derive(Debug, Clone)]
17pub struct QirArray {
18    pub(crate) elem_size: usize,
19    pub(crate) data: Vec<u8>,
20}
21
22#[unsafe(no_mangle)]
23pub extern "C" fn __quantum__rt__array_create_1d(elem_size: u32, count: u64) -> *const QirArray {
24    let elem_size = elem_size
25        .try_into()
26        .expect("The `elem_size` argument should fit in the `usize` type for this platform.");
27    let count: usize = count
28        .try_into()
29        .expect("The `count` argument should fit in the `usize` type for this platform.");
30    let data = vec![0_u8; elem_size * count];
31    Rc::into_raw(Rc::new(QirArray { elem_size, data }))
32}
33
34#[unsafe(no_mangle)]
35pub unsafe extern "C" fn __quantum__rt__array_copy(
36    arr: *const QirArray,
37    force: bool,
38) -> *const QirArray {
39    unsafe {
40        // Wrap the array in a `ManuallyDrop` to effectively borrow it and ensure the array
41        // won't be dropped, refcount decremented, and cleaned up.
42        let rc = ManuallyDrop::new(Rc::from_raw(arr));
43        if force || Rc::weak_count(&rc) > 0 {
44            let copy = rc.as_ref().clone();
45            Rc::into_raw(Rc::new(copy))
46        } else {
47            let _ = Rc::into_raw(Rc::clone(&rc));
48            arr
49        }
50    }
51}
52
53#[unsafe(no_mangle)]
54pub unsafe extern "C" fn __quantum__rt__array_concatenate(
55    arr1: *const QirArray,
56    arr2: *const QirArray,
57) -> *const QirArray {
58    unsafe {
59        let array1 = &*arr1;
60        let array2 = &*arr2;
61        if array1.elem_size != array2.elem_size {
62            __quantum__rt__fail(convert(&format!(
63                "Cannot concatenate arrays with differing element sizes: {} vs {}",
64                array1.elem_size, array2.elem_size
65            )));
66        }
67
68        let mut new_array = QirArray {
69            elem_size: array1.elem_size,
70            data: Vec::new(),
71        };
72        new_array.data.resize(array1.data.len(), 0_u8);
73        new_array.data.copy_from_slice(array1.data.as_slice());
74
75        let mut copy = vec![0; array2.data.len()];
76        copy.copy_from_slice(array2.data.as_slice());
77
78        new_array.data.append(&mut copy);
79        Rc::into_raw(Rc::new(new_array))
80    }
81}
82
83#[unsafe(no_mangle)]
84pub unsafe extern "C" fn __quantum__rt__array_get_size_1d(arr: *const QirArray) -> u64 {
85    unsafe {
86        let array = &*arr;
87        let len = array.data.len() / array.elem_size;
88        len.try_into()
89            .expect("Length of array should always fit in a 64-bit integer.")
90    }
91}
92
93#[unsafe(no_mangle)]
94pub unsafe extern "C" fn __quantum__rt__array_get_element_ptr_1d(
95    arr: *const QirArray,
96    index: u64,
97) -> *mut i8 {
98    unsafe {
99        let array = &*arr;
100        let index: usize = index
101            .try_into()
102            .expect("Indices into an array should fit into the `usize` ");
103        array.data.as_ptr().add(array.elem_size * index) as *mut i8
104    }
105}
106
107#[unsafe(no_mangle)]
108pub unsafe extern "C" fn __quantum__rt__array_update_reference_count(
109    arr: *const QirArray,
110    update: i32,
111) {
112    unsafe {
113        update_counts(arr, update, false);
114    }
115}
116
117#[unsafe(no_mangle)]
118pub unsafe extern "C" fn __quantum__rt__array_update_alias_count(
119    arr: *const QirArray,
120    update: i32,
121) {
122    unsafe {
123        update_counts(arr, update, true);
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use std::{convert::TryInto, mem::size_of};
130
131    use super::*;
132
133    #[test]
134    fn test_array_creation_simple() {
135        let arr = __quantum__rt__array_create_1d(1, 3);
136        unsafe {
137            assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
138            // Note: array reference counts must be decremented to prevent leaking array from test
139            // and failing address sanitized runs.
140            __quantum__rt__array_update_reference_count(arr, -1);
141        }
142    }
143
144    #[test]
145    fn test_array_item_zero_init() {
146        let arr = __quantum__rt__array_create_1d(1, 3);
147        unsafe {
148            assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
149            // Array contents should always be zero initialized.
150            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0);
151            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0);
152            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0);
153            __quantum__rt__array_update_reference_count(arr, -1);
154        }
155    }
156
157    #[test]
158    fn test_array_item_updates() {
159        let arr = __quantum__rt__array_create_1d(1, 3);
160        unsafe {
161            assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
162            // Confirm that array contents can be updated.
163            let first = __quantum__rt__array_get_element_ptr_1d(arr, 0);
164            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0);
165            *first = 42;
166            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42);
167            let second = __quantum__rt__array_get_element_ptr_1d(arr, 1);
168            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0);
169            *second = 31;
170            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31);
171            let third = __quantum__rt__array_get_element_ptr_1d(arr, 2);
172            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0);
173            *third = 20;
174            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 20);
175            __quantum__rt__array_update_reference_count(arr, -1);
176        }
177    }
178
179    #[test]
180    fn test_array_copy() {
181        let arr = __quantum__rt__array_create_1d(1, 3);
182        unsafe {
183            assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
184            let first = __quantum__rt__array_get_element_ptr_1d(arr, 0);
185            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0);
186            *first = 42;
187            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42);
188            let second = __quantum__rt__array_get_element_ptr_1d(arr, 1);
189            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0);
190            *second = 31;
191            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31);
192            // First array has contents [42, 31, 0], copy to second array and confirm contents.
193            let arr2 = __quantum__rt__array_copy(arr, true);
194            assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3);
195            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42);
196            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31);
197            // Update first array to [42, 31, 20], confirm second array is independent copy that still
198            // has original value.
199            let third = __quantum__rt__array_get_element_ptr_1d(arr, 2);
200            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0);
201            *third = 20;
202            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 20);
203            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 2), 0);
204            // Decrement ref count on first array to trigger deletion, confirm the second array is
205            // independent copy with original values.
206            __quantum__rt__array_update_reference_count(arr, -1);
207            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42);
208            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31);
209            __quantum__rt__array_update_reference_count(arr2, -1);
210        }
211    }
212
213    #[test]
214    fn test_array_concat() {
215        let arr = __quantum__rt__array_create_1d(1, 3);
216        unsafe {
217            assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
218            let first = __quantum__rt__array_get_element_ptr_1d(arr, 0);
219            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0);
220            *first = 42;
221            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42);
222            let second = __quantum__rt__array_get_element_ptr_1d(arr, 1);
223            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0);
224            *second = 31;
225            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31);
226            // First array has contents [42, 31, 0], copy to second array and confirm contents.
227            let arr2 = __quantum__rt__array_copy(arr, true);
228            assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3);
229            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42);
230            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31);
231            // Create third array with concatenated contents [42, 31, 0, 42, 31, 0].
232            let arr3 = __quantum__rt__array_concatenate(arr, arr2);
233            assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6);
234            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42);
235            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31);
236            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0);
237            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42);
238            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31);
239            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0);
240            // Decrement counts on first and secon array to trigger deletion, confirm third array
241            // maintains contents.
242            __quantum__rt__array_update_reference_count(arr, -1);
243            __quantum__rt__array_update_reference_count(arr2, -1);
244            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42);
245            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31);
246            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0);
247            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42);
248            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31);
249            assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0);
250            __quantum__rt__array_update_reference_count(arr3, -1);
251        }
252    }
253
254    #[allow(clippy::cast_ptr_alignment)]
255    #[test]
256    fn test_array_creation_large_size() {
257        // Use a struct that is known to have a size greater than 1.
258        struct Data {
259            first: i64,
260            second: i64,
261            third: i64,
262        }
263        let arr = __quantum__rt__array_create_1d(size_of::<Data>().try_into().unwrap(), 3);
264        unsafe {
265            assert_eq!(__quantum__rt__array_get_size_1d(arr), 3);
266            let first = __quantum__rt__array_get_element_ptr_1d(arr, 0).cast::<Data>();
267            *first = Data {
268                first: 1,
269                second: 2,
270                third: 3,
271            };
272            let second = __quantum__rt__array_get_element_ptr_1d(arr, 1).cast::<Data>();
273            *second = Data {
274                first: 10,
275                second: 20,
276                third: 30,
277            };
278            let third = __quantum__rt__array_get_element_ptr_1d(arr, 2).cast::<Data>();
279            *third = Data {
280                first: 100,
281                second: 200,
282                third: 300,
283            };
284            assert_eq!(
285                (*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::<Data>())).first,
286                1
287            );
288            assert_eq!(
289                (*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::<Data>())).second,
290                2
291            );
292            assert_eq!(
293                (*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::<Data>())).third,
294                3
295            );
296            assert_eq!(
297                (*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::<Data>())).first,
298                10
299            );
300            assert_eq!(
301                (*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::<Data>())).second,
302                20
303            );
304            assert_eq!(
305                (*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::<Data>())).third,
306                30
307            );
308            assert_eq!(
309                (*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::<Data>())).first,
310                100
311            );
312            assert_eq!(
313                (*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::<Data>())).second,
314                200
315            );
316            assert_eq!(
317                (*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::<Data>())).third,
318                300
319            );
320            __quantum__rt__array_update_reference_count(arr, -1);
321        }
322    }
323}