1#[macro_use]
2extern crate derive_new;
3
4extern crate alloc;
5
6mod backend;
7mod compiler;
8mod compute;
9mod device;
10mod graphics;
11mod runtime;
12
13pub use compiler::base::*;
14pub use compute::*;
15pub use device::*;
16pub use graphics::*;
17pub use runtime::*;
18
19#[cfg(feature = "spirv")]
20pub use backend::vulkan;
21
22#[cfg(all(feature = "msl", target_os = "macos"))]
23pub use backend::metal;
24
25#[cfg(all(test, not(feature = "spirv"), not(feature = "msl")))]
26#[allow(unexpected_cfgs)]
27mod tests {
28 pub type TestRuntime = crate::WgpuRuntime;
29 use half::f16;
30
31 cubecl_core::testgen_all!(f32: [f16, f32], i32: [i32, i64], u32: [u32, u64]);
36 cubecl_std::testgen!();
37 cubecl_std::testgen_tensor_identity!([flex32, f32, u32]);
38 cubecl_std::testgen_quantized_view!(f32);
39 cubecl_core::testgen_profiling!();
40
41 mod precompiled {
44 use cubecl_core::prelude::*;
45 use cubecl_ir::{UIntKind, metadata::Info, settings::Dim3};
46 use cubecl_server::kernel::{
47 CubeKernel, KernelDefinition, KernelMetadata, PrecompiledSource,
48 };
49 use cubecl_server::runtime::Runtime;
50 use cubecl_server::server::KernelArguments;
51
52 use super::TestRuntime;
53
54 const SOURCE: &str = r#"
55@group(0) @binding(0) var<storage, read_write> data: array<f32>;
56
57@compute @workgroup_size(1)
58fn double(@builtin(global_invocation_id) id: vec3<u32>) {
59 data[id.x] = data[id.x] * 2.0;
60}
61"#;
62
63 struct Double;
64
65 impl KernelMetadata for Double {
66 fn id(&self) -> KernelId {
67 KernelId::new::<Self>()
68 }
69
70 fn address_type(&self) -> ElemType {
71 ElemType::UInt(UIntKind::U32)
72 }
73 }
74
75 impl CubeKernel for Double {
76 fn define(&self) -> KernelDefinition {
77 let settings = KernelSettings::new(
78 Dim3::new_single(),
79 ExecutionMode::Checked,
80 AddressType::U32,
81 );
82 KernelDefinition {
83 body: Scope::root(settings.clone()),
84 settings,
85 info: Info::default(),
86 }
87 }
88
89 fn source(&self) -> Option<PrecompiledSource> {
90 Some(PrecompiledSource {
91 source: SOURCE.to_string(),
92 entrypoint_name: "double".to_string(),
93 lang: "wgsl",
94 })
95 }
96 }
97
98 #[test]
99 fn a_hand_written_wgsl_kernel_launches() {
100 let client = TestRuntime::client(&Default::default());
101 let input = [1.0f32, 2.0, 3.0, 4.0];
102 let handle = client.create_from_slice(bytemuck::cast_slice(&input));
103
104 client.launch(
105 Box::new(Double),
106 CubeCount::Static(input.len() as u32, 1, 1),
107 KernelArguments::new().with_buffer(handle.clone().binding()),
108 );
109
110 let bytes = client.read_one(handle).expect("the launch ran");
111 let output: &[f32] = bytemuck::cast_slice(&bytes);
112 assert_eq!(output, [2.0, 4.0, 6.0, 8.0]);
113 }
114 }
115
116 mod fp8_lanes {
120 use cubecl_common::e4m3;
121 use cubecl_core::prelude::*;
122 use cubecl_core::{self as cubecl};
123 use cubecl_server::runtime::Runtime;
124 use cubecl_server::server::Handle;
125
126 use super::TestRuntime;
127
128 #[cube(launch_unchecked)]
130 fn cast_fp8<N: Size>(input: &[Vector<f32, N>], out: &mut [Vector<f32, N>]) {
131 if ABSOLUTE_POS < input.len() {
132 let codes = Vector::<e4m3, N>::cast_from(input[ABSOLUTE_POS]);
133 out[ABSOLUTE_POS] = Vector::cast_from(codes);
134 }
135 }
136
137 #[cube(launch_unchecked)]
139 fn copy_fp8<N: Size>(input: &[Vector<e4m3, N>], out: &mut [Vector<e4m3, N>]) {
140 if ABSOLUTE_POS < input.len() {
141 out[ABSOLUTE_POS] = input[ABSOLUTE_POS];
142 }
143 }
144
145 fn assert_rejected(client: &Client, out: Handle) {
146 let err = client
147 .read_one(out)
148 .expect_err("two fp8 lanes have no WGSL representation, the launch must fail")
149 .to_string();
150 assert!(
151 err.contains("fp8 on WGSL is packed 4 lanes to a u32"),
152 "the packing rule has to be in the error the caller sees, got: {err}"
153 );
154 }
155
156 #[test]
157 fn cast_at_two_lanes_is_reported() {
158 let client = TestRuntime::client(&Default::default());
159 let input = client.create_from_slice(&[0u8; 64]);
160 let out = client.empty(64);
161 unsafe {
162 cast_fp8::launch_unchecked(
163 &client,
164 CubeCount::new_single(),
165 CubeDim::new_1d(8),
166 2,
167 BufferArg::from_raw_parts(input, 16),
168 BufferArg::from_raw_parts(out.clone(), 16),
169 )
170 };
171 assert_rejected(&client, out);
172 }
173
174 #[test]
175 fn copy_at_two_lanes_is_reported() {
176 let client = TestRuntime::client(&Default::default());
177 let input = client.create_from_slice(&[0u8; 32]);
178 let out = client.empty(32);
179 unsafe {
180 copy_fp8::launch_unchecked(
181 &client,
182 CubeCount::new_single(),
183 CubeDim::new_1d(8),
184 2,
185 BufferArg::from_raw_parts(input, 32),
186 BufferArg::from_raw_parts(out.clone(), 32),
187 )
188 };
189 assert_rejected(&client, out);
190 }
191 }
192
193 cubecl_core::testgen_complex_validation!();
194}
195
196#[cfg(all(test, feature = "spirv"))]
197#[allow(unexpected_cfgs)]
198mod tests_spirv {
199 pub type TestRuntime = crate::WgpuRuntime;
200 use cubecl_core::flex32;
201 use half::f16;
202
203 cubecl_core::testgen_all!(f32: [f16, flex32, f32], i32: [i8, i16, i32, i64], u32: [u8, u16, u32, u64]);
204 cubecl_std::testgen!();
205 cubecl_std::testgen_tensor_identity!([f16, flex32, f32, u32]);
206 cubecl_std::testgen_quantized_view!(f16);
207 cubecl_core::testgen_profiling!();
208}
209
210#[cfg(all(test, feature = "msl"))]
211#[allow(unexpected_cfgs)]
212mod tests_msl {
213 pub type TestRuntime = crate::WgpuRuntime;
214 use half::f16;
215
216 cubecl_core::testgen_all!(f32: [f16, f32], i32: [i16, i32], u32: [u16, u32]);
217 cubecl_std::testgen!();
218 cubecl_std::testgen_tensor_identity!([f16, flex32, f32, u32]);
219 cubecl_std::testgen_quantized_view!(f16);
220}