1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#![deny(clippy::all, clippy::pedantic)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::missing_panics_doc)]

//! # Rust Implementation for Quantum Intermediate Representation
//! This library implements the classical runtime functions described in the [QIR specification](https://github.com/qir-alliance/qir-spec).

// FUTURE: We should add microbenchmarks to verify behavior of these APIs and have a baseline on how changes affect
// peformance of the APIs.

pub mod arrays;
pub mod bigints;
pub mod callables;
pub mod math;
pub mod output_recording;
pub mod range_support;
pub mod strings;
pub mod tuples;

use std::{
    ffi::CString,
    mem::{self, ManuallyDrop},
    rc::{Rc, Weak},
};

use output_recording::record_output_str;

/// Utility used for managing refcounted items.
unsafe fn update_counts<T>(raw_rc: *const T, update: i32, is_alias: bool) {
    let mut remaining = update;
    while remaining != 0 {
        let rc = ManuallyDrop::new(Rc::from_raw(raw_rc));
        if remaining > 0 {
            if is_alias {
                // Create and leak new downgraded instances to increment the weak count on the contained item.
                mem::forget(Rc::downgrade(&rc));
            } else {
                Rc::increment_strong_count(raw_rc);
            }

            remaining -= 1;
        } else {
            if is_alias {
                // Create and drop downgraded instances to decrement the weak count on contained item.
                let w = Weak::into_raw(Rc::downgrade(&rc));

                // Need to drop two for a net decrement, since above line increments.
                drop(Weak::from_raw(w));
                drop(Weak::from_raw(w));
            } else {
                Rc::decrement_strong_count(raw_rc);
            }

            remaining += 1;
        }
    }
}

#[derive(Debug, Copy, Clone)]
#[repr(i8)]
pub enum Pauli {
    I = 0,
    X = 1,
    Z = 2,
    Y = 3,
}

#[no_mangle]
pub extern "C" fn __quantum__rt__memory_allocate(size: u64) -> *mut u8 {
    (vec![
        0_u8;
        size.try_into()
            .expect("Memory size is too large for `usize` type on this platform.")
    ])
    .leak()
    .as_mut_ptr()
}

#[cfg(feature = "fail-support")]
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__fail(str: *const CString) {
    __quantum__rt__message(str);
    panic!("{}", (*str).to_str().expect("Unable to convert string"));
}

#[cfg(feature = "message-support")]
#[no_mangle]
pub unsafe extern "C" fn __quantum__rt__message(str: *const CString) {
    record_output_str(&format!(
        "INFO\t{}",
        (*str)
            .to_str()
            .expect("Unable to convert input string")
            .escape_default()
    ))
    .expect("Failed to write message output");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_memory_allocate() {
        let size = 10;
        let mem = __quantum__rt__memory_allocate(size as u64);
        unsafe {
            for val in Vec::from_raw_parts(mem, size, size) {
                assert_eq!(val, 0);
            }
        }
    }

    #[test]
    #[should_panic(expected = "FAIL")]
    fn test_fail() {
        let str = CString::new("FAIL").unwrap();
        unsafe {
            __quantum__rt__fail(&str);
        }
    }

    #[test]
    fn test_message() {
        let str = CString::new("Message").unwrap();
        unsafe {
            __quantum__rt__message(&str);
        }
        // message should not consume string, so check that it's value is still correct.
        assert_eq!(str.to_str().unwrap(), "Message");
    }
}