1use alloc::{boxed::Box, vec::Vec};
2use core::marker::PhantomData;
3
4use crate::dsl::Runtime;
5use crate::dsl::prelude::{ArrayArg, TensorArg, TensorMapArg, TensorMapKind};
6use crate::dsl::{InfoBuilder, KernelSettings, ScalarArgType};
7#[cfg(any(feature = "frontend-std", feature = "std"))]
8use core::cell::RefCell;
9use ruda_core::ir::{AddressType, Scope, StorageType, Type};
10use ruda::runtime::server::{Binding, RudaCount, TensorMapBinding};
11use ruda::runtime::{
12 client::ComputeClient,
13 compiler::RudaTask,
14 kernel::{RudaKernel, KernelTask},
15 server::KernelArguments,
16};
17
18#[cfg(any(feature = "frontend-std", feature = "std"))]
19std::thread_local! {
20 static INFO: RefCell<InfoBuilder> = RefCell::new(InfoBuilder::default());
21 static SCOPE: RefCell<Scope> = RefCell::new(Scope::root(false));
23}
24
25pub struct PreparedKernel<R: Runtime> {
31 task: Box<dyn RudaTask<R::Compiler>>,
32 count: RudaCount,
33 arguments: KernelArguments,
34 client: ComputeClient<R>,
35 scalar_words: usize,
36}
37
38impl<R: Runtime> PreparedKernel<R> {
39 pub fn scalar_words(&self) -> usize { self.scalar_words }
42
43 pub fn into_parts(self) -> (
46 Box<dyn RudaTask<R::Compiler>>, RudaCount, KernelArguments, ComputeClient<R>,
47 ) {
48 (self.task, self.count, self.arguments, self.client)
49 }
50}
51
52pub struct KernelLauncher<R: Runtime> {
54 buffers: Vec<Binding>,
55 tensor_maps: Vec<TensorMapBinding>,
56 address_type: AddressType,
57 pub settings: KernelSettings,
58 #[cfg(not(any(feature = "frontend-std", feature = "std")))]
59 info: InfoBuilder,
60 #[cfg(not(any(feature = "frontend-std", feature = "std")))]
61 pub scope: Scope,
62 _runtime: PhantomData<R>,
63}
64
65impl<R: Runtime> KernelLauncher<R> {
66 #[cfg(any(feature = "frontend-std", feature = "std"))]
67 pub fn with_scope<T>(&mut self, fun: impl FnMut(&mut Scope) -> T) -> T {
68 SCOPE.with_borrow_mut(fun)
69 }
70
71 #[cfg(not(any(feature = "frontend-std", feature = "std")))]
72 pub fn with_scope<T>(&mut self, mut fun: impl FnMut(&mut Scope) -> T) -> T {
73 fun(&mut self.scope)
74 }
75
76 #[cfg(any(feature = "frontend-std", feature = "std"))]
77 fn with_info<T>(&mut self, fun: impl FnMut(&mut InfoBuilder) -> T) -> T {
78 INFO.with_borrow_mut(fun)
79 }
80
81 #[cfg(not(any(feature = "frontend-std", feature = "std")))]
82 fn with_info<T>(&mut self, mut fun: impl FnMut(&mut InfoBuilder) -> T) -> T {
83 fun(&mut self.info)
84 }
85
86 pub fn register_scalar<C: ScalarArgType>(&mut self, scalar: C) {
88 self.with_info(|info| info.scalars.push(scalar));
89 }
90
91 pub fn register_scalar_raw(&mut self, bytes: &[u8], dtype: StorageType) {
93 self.with_info(|info| info.scalars.push_raw(bytes, dtype));
94 }
95
96 pub fn prepare<K: RudaKernel>(
100 mut self, count: RudaCount, kernel: K, client: &ComputeClient<R>,
101 ) -> PreparedKernel<R> {
102 let scalar_words = self.with_info(|info| info.scalars.len_aligned());
103 PreparedKernel {
104 scalar_words,
105 arguments: self.into_bindings(),
106 task: Box::new(KernelTask::<R::Compiler, K>::new(kernel)),
107 count,
108 client: client.fixed_execution_queue(),
109 }
110 }
111
112 #[track_caller]
114 pub fn launch<K: RudaKernel>(
115 self,
116 ruda_count: RudaCount,
117 kernel: K,
118 client: &ComputeClient<R>,
119 ) {
120 let bindings = self.into_bindings();
121 let kernel = Box::new(KernelTask::<R::Compiler, K>::new(kernel));
122
123 client.launch(kernel, ruda_count, bindings)
124 }
125
126 #[track_caller]
135 pub unsafe fn launch_unchecked<K: RudaKernel>(
136 self,
137 ruda_count: RudaCount,
138 kernel: K,
139 client: &ComputeClient<R>,
140 ) {
141 unsafe {
142 let bindings = self.into_bindings();
143 let kernel = Box::new(KernelTask::<R::Compiler, K>::new(kernel));
144
145 client.launch_unchecked(kernel, ruda_count, bindings)
146 }
147 }
148
149 fn into_bindings(mut self) -> KernelArguments {
159 let mut bindings = KernelArguments::new();
160 let address_type = self.address_type;
161 let info = self.with_info(|info| info.finish(address_type));
162
163 bindings.buffers = self.buffers;
164 bindings.tensor_maps = self.tensor_maps;
165 bindings.info = info;
166
167 bindings
168 }
169}
170
171impl<R: Runtime> KernelLauncher<R> {
173 pub fn register_tensor(&mut self, tensor: TensorArg<R>, ty: Type) {
175 if let Some(tensor) = self.process_tensor(tensor, ty) {
176 self.buffers.push(tensor);
177 }
178 }
179
180 fn process_tensor(&mut self, tensor: TensorArg<R>, ty: Type) -> Option<Binding> {
181 let tensor = match tensor {
182 TensorArg::Handle { handle, .. } => handle,
183 TensorArg::Alias { .. } => return None,
184 };
185
186 let elem_size = ty.size();
187 let vectorization = ty.vector_size();
188
189 let buffer_len = tensor.handle.size_in_used() / elem_size as u64;
190 let len = tensor.shape.iter().product::<usize>() / vectorization;
191 let address_type = self.address_type;
192 self.with_info(|info| {
193 info.metadata.register_tensor(
194 tensor.strides.len() as u64,
195 buffer_len,
196 len as u64,
197 tensor.shape.clone(),
198 tensor.strides.clone(),
199 address_type,
200 )
201 });
202 Some(tensor.handle)
203 }
204
205 pub fn register_array(&mut self, array: ArrayArg<R>, ty: Type) {
207 if let Some(tensor) = self.process_array(array, ty) {
208 self.buffers.push(tensor);
209 }
210 }
211
212 fn process_array(&mut self, array: ArrayArg<R>, ty: Type) -> Option<Binding> {
213 let array = match array {
214 ArrayArg::Handle { handle, .. } => handle,
215 ArrayArg::Alias { .. } => return None,
216 };
217
218 let elem_size = ty.size();
219 let vectorization = ty.vector_size();
220
221 let buffer_len = array.handle.size_in_used() / elem_size as u64;
222 let address_type = self.address_type;
223 self.with_info(|info| {
224 info.metadata.register_array(
225 buffer_len,
226 array.length[0] as u64 / vectorization as u64,
227 address_type,
228 )
229 });
230 Some(array.handle)
231 }
232
233 pub fn register_tensor_map<K: TensorMapKind>(&mut self, map: TensorMapArg<R, K>, ty: Type) {
235 let binding = self
236 .process_tensor(map.tensor, ty)
237 .expect("Can't use alias for TensorMap");
238
239 let map = map.metadata.clone();
240 self.tensor_maps.push(TensorMapBinding { binding, map });
241 }
242}
243
244impl<R: Runtime> KernelLauncher<R> {
245 pub fn new(settings: KernelSettings) -> Self {
246 Self {
247 address_type: settings.address_type,
248 settings,
249 buffers: Vec::new(),
250 tensor_maps: Vec::new(),
251 _runtime: PhantomData,
252 #[cfg(not(any(feature = "frontend-std", feature = "std")))]
253 info: InfoBuilder::default(),
254 #[cfg(not(any(feature = "frontend-std", feature = "std")))]
255 scope: Scope::root(false),
256 }
257 }
258}