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
//! Utilities for printing to stdout from GPU threads.
//!
//! CUDA contains a syscall named `vprintf` which provides a way of atomically
//! printing from GPU threads, this module provides safe wrappers over it.
//! Printing is atomic, meaning that simultaneous calls from different
//! threads (which will naturally happen) will not clash with eachother
//! unlike printing from multiple CPU threads.
//!
//! # Important Notes
//!
//! Printing output in CUDA is stored inside of a circular buffer which has a fixed size (1mb by default).
//! If the buffer is filled, old output will be overwritten.
//!
//! This buffer is flushed for:
//! - Kernel launches
//! - Synchronization (stream/device synchronization)
//! - Blocking memory copies
//! - Module load/unload
//! - Context destruction
//!
//! This does NOT include exiting the program, however, because rust uses RAII, unless you leak the
//! context, output will always be flushed.

extern "C" {
    // CUDA syscalls implicitly defined by nvvm you can link to.

    #[doc(hidden)]
    pub fn vprintf(format: *const u8, valist: *const core::ffi::c_void) -> i32;

    #[doc(hidden)]
    pub fn __assertfail(
        message: *const u8,
        file: *const u8,
        line: u32,
        function: *const u8,
        char_size: usize,
    );
}

/// Alternative to [`print!`](std::print) which works on CUDA. See [`print`](self) for more info.
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => {
        let msg = ::alloc::format!($($arg)*);
        let cstring = ::alloc::format!("{}\0", msg);
        unsafe {
            $crate::io::vprintf(cstring.as_ptr(), ::core::ptr::null_mut());
        }
    }
}

/// Alternative to [`println!`](std::println) which works on CUDA. See [`print`](self) for more info.
#[macro_export]
macro_rules! println {
    () => ($crate::print!("\n"));
    ($fmt:expr) => ($crate::print!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => ($crate::print!(concat!($fmt, "\n"), $($arg)*));
}

/// Asserts that two expression are equal and returns an `AssertionFailed` error to the application that launched the kernel
/// if it is not true.
#[macro_export]
macro_rules! assert_eq {
    ($a:expr, $b:expr) => {
        let _a = $a;
        let _b = $b;

        if _a != _b {
            let msg = ::alloc::format!(
                "\nassertion failed: ({} == {})\nleft : {:?}\nright: {:?}",
                stringify!($a),
                stringify!($b),
                _a,
                _b
            );

            unsafe {
                $crate::io::__assertfail(msg.as_ptr(), file!().as_ptr(), line!(), "".as_ptr(), 1)
            };
        }
    };
}

/// Asserts that two expression are not equal and returns an `AssertionFailed` error to the application that launched the kernel
/// if it is not true.
#[macro_export]
macro_rules! assert_ne {
    ($a:expr, $b:expr) => {
        let _a = $a;
        let _b = $b;

        if _a == _b {
            let msg = ::alloc::format!(
                "\nassertion failed: ({} != {})\nleft : {:?}\nright: {:?}",
                stringify!($a),
                stringify!($b),
                _a,
                _b
            );

            unsafe {
                $crate::io::__assertfail(msg.as_ptr(), file!().as_ptr(), line!(), "".as_ptr(), 1)
            };
        }
    };
}