1use super::Item;
2use super::Subgroup;
3use super::shader::ComputeShader;
4use crate::compiler::wgsl::{self, SharedValue};
5
6use cubecl_common::backtrace::BackTrace;
7use cubecl_core::ir::{Processor, UIntKind};
8use cubecl_core::{
9 Info,
10 post_processing::{
11 checked_io::CheckedIoVisitor, optimize_scope, saturating::SaturatingArithmeticProcessor,
12 unroll::UnrollVisitor,
13 },
14};
15use cubecl_core::{
16 Metadata, WgpuCompilationOptions,
17 ir::{self as cube, Scope},
18 prelude::expand_erf,
19};
20use cubecl_core::{post_processing::disaggregate::DisaggregateVisitor, prelude::*};
21use cubecl_ir::AddressSpace;
22use cubecl_runtime::compiler::CompilationError;
23use cubecl_runtime::kernel;
24use hashbrown::HashMap;
25
26pub const MAX_VECTOR_SIZE: usize = 4;
27
28#[derive(Clone, Default)]
30pub struct WgslCompiler {
31 kernel_name: String,
32 info: Info,
33 ext_meta_pos: HashMap<cube::Value, u32>,
34 buffer_vis: Vec<Visibility>,
35 local_invocation_index: bool,
36 local_invocation_id: bool,
37 global_invocation_id: bool,
38 workgroup_id: bool,
39 subgroup_size: bool,
40 subgroup_id: bool,
41 subgroup_invocation_id: bool,
42 id: bool,
43 num_workgroups: bool,
44 workgroup_id_no_axis: bool,
45 workgroup_size_no_axis: bool,
46 num_workgroup_no_axis: bool,
47 shared_values: Vec<SharedValue>,
48 #[allow(dead_code)]
49 compilation_options: WgpuCompilationOptions,
50 strategy: ExecutionMode,
51 subgroup_instructions_used: bool,
52 f16_used: bool,
53}
54
55impl core::fmt::Debug for WgslCompiler {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 f.write_str("WgslCompiler")
58 }
59}
60
61impl cubecl_core::Compiler for WgslCompiler {
62 type Representation = ComputeShader;
63 type CompilationOptions = WgpuCompilationOptions;
64
65 fn compile(
66 &mut self,
67 shader: kernel::KernelDefinition,
68 compilation_options: &Self::CompilationOptions,
69 mode: ExecutionMode,
70 address_type: StorageType,
71 ) -> Result<Self::Representation, CompilationError> {
72 self.compilation_options = *compilation_options;
73 self.compile_shader(shader, mode, address_type)
74 }
75
76 fn elem_size(&self, elem: cube::ElemType) -> usize {
77 elem.size()
78 }
79
80 fn extension(&self) -> &'static str {
81 "wgsl"
82 }
83}
84
85impl WgslCompiler {
86 fn compile_shader(
87 &mut self,
88 value: kernel::KernelDefinition,
89 mode: ExecutionMode,
90 address_type: StorageType,
91 ) -> Result<wgsl::ComputeShader, CompilationError> {
92 let errors = value.body.pop_errors();
93 if !errors.is_empty() {
94 let mut reason = "Can't compile wgsl kernel".to_string();
95 for error in errors {
96 reason += error.as_str();
97 reason += "\n";
98 }
99
100 return Err(CompilationError::Validation {
101 reason,
102 backtrace: BackTrace::capture(),
103 });
104 }
105
106 self.strategy = mode;
107 self.kernel_name = value.options.kernel_name.clone();
108
109 let num_meta = value.buffers.len();
110
111 self.ext_meta_pos = HashMap::new();
112 let mut num_ext = 0;
113
114 for binding in value.buffers.iter() {
115 self.ext_meta_pos.insert(binding.value, num_ext);
116 if binding.has_extended_meta {
117 num_ext += 1;
118 }
119 }
120
121 let metadata = Metadata::new(num_meta as u32, num_ext);
122 self.info = Info::new(&value.scalars, metadata, address_type);
123
124 CheckedIoVisitor::new(self.strategy, self.kernel_name.clone()).apply(&value.body);
125 DisaggregateVisitor::apply(&value.body);
126 UnrollVisitor::new(MAX_VECTOR_SIZE).apply(&value.body);
127
128 self.buffer_vis = optimize_scope(&value.body).into();
129 self.buffer_vis
130 .resize(value.num_global_buffers(), Visibility::Read);
131
132 let address_type = self.compile_storage_type(address_type);
133 let instructions = self.compile_scope(&value.body);
134 let extensions = register_extensions(&instructions);
135 let body = wgsl::Body {
136 instructions,
137 id: self.id,
138 address_type,
139 };
140
141 Ok(wgsl::ComputeShader {
142 address_type,
143 buffers: value
144 .buffers
145 .into_iter()
146 .map(|mut it| {
147 if it.value.ty.vector_size() > MAX_VECTOR_SIZE {
150 it.value.ty = it.value.ty.with_vector_size(MAX_VECTOR_SIZE);
151 }
152 self.compile_binding(it)
153 })
154 .collect(),
155 scalars: value
156 .scalars
157 .into_iter()
158 .map(|binding| (self.compile_storage_type(binding.ty), binding.count))
159 .collect(),
160 shared_values: self.shared_values.clone(),
161 static_meta_len: self.info.metadata.static_len() as usize,
162 info: self.info.clone(),
163 workgroup_size: value.cube_dim,
164 global_invocation_id: self.global_invocation_id || self.id,
165 local_invocation_index: self.local_invocation_index,
166 local_invocation_id: self.local_invocation_id,
167 num_workgroups: self.id
168 || self.num_workgroups
169 || self.num_workgroup_no_axis
170 || self.workgroup_id_no_axis,
171 workgroup_id: self.workgroup_id || self.workgroup_id_no_axis,
172 subgroup_size: self.subgroup_size,
173 subgroup_id: self.subgroup_id,
174 subgroup_invocation_id: self.subgroup_invocation_id,
175 body,
176 extensions,
177 num_workgroups_no_axis: self.num_workgroup_no_axis,
178 workgroup_id_no_axis: self.workgroup_id_no_axis,
179 workgroup_size_no_axis: self.workgroup_size_no_axis,
180 subgroup_instructions_used: self.subgroup_instructions_used,
181 f16_used: self.f16_used,
182 kernel_name: value.options.kernel_name,
183 })
184 }
185
186 fn compile_type(&mut self, item: cube::Type) -> Item {
187 match item {
188 cube::Type::Scalar(ty) => wgsl::Item::Scalar(self.compile_storage_type(ty)),
189 cube::Type::Vector(ty, size) => {
190 let elem = self.compile_storage_type(ty.storage_type());
191 wgsl::Item::Vector(elem, size)
192 }
193 cube::Type::Atomic(ty) => {
194 let inner = self.compile_type(*ty);
195 wgsl::Item::Atomic(inner.intern())
196 }
197 cube::Type::Pointer(ty, class) => {
198 let inner = self.compile_type(*ty);
199 let class = self.compile_pointer_class(class);
200 wgsl::Item::Pointer(inner.intern(), class)
201 }
202 cube::Type::Array(ty, size) => {
203 let inner = self.compile_type(*ty);
204 wgsl::Item::Array(inner.intern(), size)
205 }
206 cube::Type::DynamicArray(ty) => {
207 let inner = self.compile_type(*ty);
208 wgsl::Item::DynamicArray(inner.intern())
209 }
210 cube::Type::Opaque(_) => unimplemented!("Can't compile opaque type"),
211 cube::Type::Semantic(_) => unimplemented!("Can't compile semantic type"),
212 cube::Type::Matrix(_) => unimplemented!("Matrices not yet supported in WGSL"),
213 cube::Type::Aggregate(_) => {
214 unreachable!("Should be disaggregated at this point")
215 }
216 }
217 }
218
219 fn compile_storage_type(&mut self, ty: cube::StorageType) -> wgsl::Elem {
220 match ty {
221 cube::StorageType::Scalar(ty) => self.compile_elem(ty),
222 cube::StorageType::Packed(_, _) => {
223 unimplemented!("Packed types not yet supported in WGSL")
224 }
225 }
226 }
227
228 fn compile_elem(&mut self, value: cube::ElemType) -> wgsl::Elem {
229 match value {
230 cube::ElemType::Float(f) => match f {
231 cube::FloatKind::E2M1
232 | cube::FloatKind::E2M3
233 | cube::FloatKind::E3M2
234 | cube::FloatKind::E4M3
235 | cube::FloatKind::E5M2
236 | cube::FloatKind::UE8M0 => panic!("Minifloat is not a valid WgpuElement"),
237 cube::FloatKind::F16 => {
238 self.f16_used = true;
239 wgsl::Elem::F16
240 }
241 cube::FloatKind::BF16 => panic!("bf16 is not a valid WgpuElement"),
242 cube::FloatKind::TF32 => panic!("tf32 is not a valid WgpuElement"),
243 cube::FloatKind::Flex32 => wgsl::Elem::F32,
244 cube::FloatKind::F32 => wgsl::Elem::F32,
245 cube::FloatKind::F64 => wgsl::Elem::F64,
246 },
247 cube::ElemType::Int(i) => match i {
248 cube::IntKind::I32 => wgsl::Elem::I32,
249 cube::IntKind::I64 => wgsl::Elem::I64,
250 kind => panic!("{kind:?} is not a valid WgpuElement"),
251 },
252 cube::ElemType::UInt(kind) => match kind {
253 cube::UIntKind::U32 => wgsl::Elem::U32,
254 cube::UIntKind::U64 => wgsl::Elem::U64,
255 kind => panic!("{kind:?} is not a valid WgpuElement"),
256 },
257 cube::ElemType::Bool => wgsl::Elem::Bool,
258 }
259 }
260
261 fn compile_pointer_class(&self, class: cube::AddressSpace) -> wgsl::PointerClass {
262 match class {
263 cubecl_ir::AddressSpace::Global(id) => {
264 wgsl::PointerClass::Global(self.buffer_vis[id as usize])
265 }
266 cubecl_ir::AddressSpace::Shared => wgsl::PointerClass::Shared,
267 cubecl_ir::AddressSpace::Local => wgsl::PointerClass::Local,
268 }
269 }
270
271 fn ext_meta_pos(&self, val: &cube::Value) -> u32 {
272 self.ext_meta_pos[val]
273 }
274
275 pub(crate) fn compile_value(&mut self, value: cube::Value) -> wgsl::Value {
276 let item = value.ty;
277 match value.kind {
278 cube::ValueKind::Value { id } => wgsl::Value::Value {
279 id,
280 item: self.compile_type(item),
281 },
282 cube::ValueKind::Constant(value) => {
283 wgsl::Value::Constant(value, self.compile_type(item))
284 }
285 }
286 }
287
288 fn constant_var(&mut self, value: u32) -> wgsl::Value {
289 let val = cube::Value::constant(value.into(), UIntKind::U32);
290 self.compile_value(val)
291 }
292
293 fn compile_scope(&mut self, scope: &cube::Scope) -> Vec<wgsl::Instruction> {
294 let mut instructions = Vec::new();
295
296 let saturating: Box<dyn Processor> = Box::new(SaturatingArithmeticProcessor::new(true));
297 let processing = scope.process([&*saturating]);
298
299 processing
300 .instructions
301 .into_iter()
302 .for_each(|op| self.compile_operation(&mut instructions, op.operation, op.out, scope));
303
304 instructions
305 }
306
307 fn compile_operation(
308 &mut self,
309 instructions: &mut Vec<wgsl::Instruction>,
310 operation: cube::Operation,
311 out: Option<cube::Value>,
312 scope: &cube::Scope,
313 ) {
314 match operation {
315 cube::Operation::Copy(value) => instructions.push(wgsl::Instruction::Assign {
316 input: self.compile_value(value),
317 out: self.compile_value(out.unwrap()),
318 }),
319 cube::Operation::DeclareVariable {
320 value_ty,
321 addr_space: AddressSpace::Local,
322 ..
323 } => instructions.push(wgsl::Instruction::DeclareVariable {
324 val: self.compile_value(out.unwrap()),
325 value_ty: self.compile_type(value_ty),
326 }),
327 cube::Operation::DeclareVariable {
328 value_ty,
329 addr_space: AddressSpace::Shared,
330 alignment,
331 } => {
332 let ty = self.compile_type(value_ty);
333 let value = self.compile_value(out.unwrap());
334 self.shared_values
335 .push(SharedValue::new(ty, value, alignment as u32));
336 }
337 cube::Operation::DeclareVariable { addr_space, .. } => {
338 unimplemented!("Unsupported declare address space {addr_space}")
339 }
340 cube::Operation::Memory(memory) => self.compile_memory(memory, out, instructions),
341 cube::Operation::Arithmetic(op) => {
342 self.compile_arithmetic(op, out, instructions, scope)
343 }
344 cube::Operation::Comparison(op) => self.compile_cmp(op, out, instructions),
345 cube::Operation::Bitwise(op) => self.compile_bitwise(op, out, instructions),
346 cube::Operation::Operator(op) => self.compile_operator(op, out, instructions),
347 cube::Operation::Atomic(op) => instructions.push(self.compile_atomic(op, out)),
348 cube::Operation::Metadata(op) => instructions.push(self.compile_metadata(op, out)),
349 cube::Operation::Branch(val) => self.compile_branch(instructions, val),
350 cube::Operation::Synchronization(val) => {
351 self.compile_synchronization(instructions, val)
352 }
353 cube::Operation::WorkgroupUniformLoad(op) => {
354 instructions.push(wgsl::Instruction::WorkgroupUniformLoad {
355 input: self.compile_value(op),
356 out: self.compile_value(out.unwrap()),
357 });
358 }
359 cube::Operation::Plane(op) => self.compile_subgroup(instructions, op, out),
360 cube::Operation::CoopMma(_) => {
361 panic!("Cooperative matrix-multiply and accumulate isn't supported on wgpu.")
362 }
363 cube::Operation::NonSemantic(cube::NonSemantic::Comment { content }) => {
364 self.compile_comment(instructions, content)
365 }
366 cube::Operation::NonSemantic(_) => {}
367 cube::Operation::Barrier(_) => {
368 panic!("Barrier isn't supported on wgpu.")
369 }
370 cube::Operation::Tma(_) => panic!("TMA isn't supported on wgpu."),
371 cube::Operation::TensorIndexing(_) => panic!("TMA isn't supported on wgpu."),
372 cube::Operation::Marker(_) => {}
373 cube::Operation::ConstructAggregate(..)
374 | cube::Operation::ExtractAggregateField(..) => {
375 unreachable!("Should be disaggregated at this point")
376 }
377 }
378 }
379
380 fn compile_subgroup(
381 &mut self,
382 instructions: &mut Vec<wgsl::Instruction>,
383 subgroup: cube::Plane,
384 out: Option<cube::Value>,
385 ) {
386 self.subgroup_instructions_used = true;
387
388 let out = out.unwrap();
389 let op = match subgroup {
390 cube::Plane::Elect => Subgroup::Elect {
391 out: self.compile_value(out),
392 },
393 cube::Plane::All(op) => Subgroup::All {
394 input: self.compile_value(op.input),
395 out: self.compile_value(out),
396 },
397 cube::Plane::Any(op) => Subgroup::Any {
398 input: self.compile_value(op.input),
399 out: self.compile_value(out),
400 },
401 cube::Plane::Ballot(op) => Subgroup::Ballot {
402 input: self.compile_value(op.input),
403 out: self.compile_value(out),
404 },
405
406 cube::Plane::Broadcast(op) => Subgroup::Broadcast {
407 lhs: self.compile_value(op.lhs),
408 rhs: self.compile_value(op.rhs),
409 out: self.compile_value(out),
410 },
411
412 cube::Plane::Sum(op) => Subgroup::Sum {
413 input: self.compile_value(op.input),
414 out: self.compile_value(out),
415 },
416
417 cube::Plane::ExclusiveSum(op) => Subgroup::ExclusiveSum {
418 input: self.compile_value(op.input),
419 out: self.compile_value(out),
420 },
421 cube::Plane::InclusiveSum(op) => Subgroup::InclusiveSum {
422 input: self.compile_value(op.input),
423 out: self.compile_value(out),
424 },
425 cube::Plane::Prod(op) => Subgroup::Prod {
426 input: self.compile_value(op.input),
427 out: self.compile_value(out),
428 },
429 cube::Plane::ExclusiveProd(op) => Subgroup::ExclusiveProd {
430 input: self.compile_value(op.input),
431 out: self.compile_value(out),
432 },
433 cube::Plane::InclusiveProd(op) => Subgroup::InclusiveProd {
434 input: self.compile_value(op.input),
435 out: self.compile_value(out),
436 },
437 cube::Plane::Min(op) => Subgroup::Min {
438 input: self.compile_value(op.input),
439 out: self.compile_value(out),
440 },
441 cube::Plane::Max(op) => Subgroup::Max {
442 input: self.compile_value(op.input),
443 out: self.compile_value(out),
444 },
445 cube::Plane::Shuffle(op) => Subgroup::Shuffle {
446 lhs: self.compile_value(op.lhs),
447 rhs: self.compile_value(op.rhs),
448 out: self.compile_value(out),
449 },
450 cube::Plane::ShuffleXor(op) => Subgroup::ShuffleXor {
451 lhs: self.compile_value(op.lhs),
452 rhs: self.compile_value(op.rhs),
453 out: self.compile_value(out),
454 },
455 cube::Plane::ShuffleUp(op) => Subgroup::ShuffleUp {
456 lhs: self.compile_value(op.lhs),
457 rhs: self.compile_value(op.rhs),
458 out: self.compile_value(out),
459 },
460 cube::Plane::ShuffleDown(op) => Subgroup::ShuffleDown {
461 lhs: self.compile_value(op.lhs),
462 rhs: self.compile_value(op.rhs),
463 out: self.compile_value(out),
464 },
465 };
466
467 instructions.push(wgsl::Instruction::Subgroup(op));
468 }
469
470 fn compile_branch(&mut self, instructions: &mut Vec<wgsl::Instruction>, branch: cube::Branch) {
471 match branch {
472 cube::Branch::If(op) => instructions.push(wgsl::Instruction::If {
473 cond: self.compile_value(op.cond),
474 instructions: self.compile_scope(&op.scope),
475 }),
476 cube::Branch::IfElse(op) => instructions.push(wgsl::Instruction::IfElse {
477 cond: self.compile_value(op.cond),
478 instructions_if: self.compile_scope(&op.scope_if),
479 instructions_else: self.compile_scope(&op.scope_else),
480 }),
481 cube::Branch::Switch(op) => instructions.push(wgsl::Instruction::Switch {
482 value: self.compile_value(op.value),
483 instructions_default: self.compile_scope(&op.scope_default),
484 cases: op
485 .cases
486 .into_iter()
487 .map(|(val, scope)| (self.compile_value(val), self.compile_scope(&scope)))
488 .collect(),
489 }),
490 cube::Branch::Return => instructions.push(wgsl::Instruction::Return),
491 cube::Branch::Unreachable => instructions.push(wgsl::Instruction::Return),
493 cube::Branch::Break => instructions.push(wgsl::Instruction::Break),
494 cube::Branch::RangeLoop(range_loop) => {
495 instructions.push(wgsl::Instruction::RangeLoop {
496 i: self.compile_value(range_loop.i),
497 start: self.compile_value(range_loop.start),
498 end: self.compile_value(range_loop.end),
499 step: range_loop.step.map(|it| self.compile_value(it)),
500 inclusive: range_loop.inclusive,
501 instructions: self.compile_scope(&range_loop.scope),
502 })
503 }
504 cube::Branch::Loop(op) => instructions.push(wgsl::Instruction::Loop {
505 instructions: self.compile_scope(&op.scope),
506 }),
507 };
508 }
509
510 fn compile_synchronization(
511 &mut self,
512 instructions: &mut Vec<wgsl::Instruction>,
513 synchronization: cube::Synchronization,
514 ) {
515 match synchronization {
516 cube::Synchronization::SyncCube => {
517 instructions.push(wgsl::Instruction::WorkgroupBarrier)
518 }
519 cube::Synchronization::SyncPlane => {
520 panic!("Synchronization within a plane is not supported in WGSL")
521 }
522 cube::Synchronization::SyncStorage => {
523 instructions.push(wgsl::Instruction::StorageBarrier)
524 }
525 cube::Synchronization::SyncAsyncProxyShared => panic!("TMA is not supported in WGSL"),
526 };
527 }
528
529 fn compile_comment(&mut self, instructions: &mut Vec<wgsl::Instruction>, content: String) {
530 instructions.push(wgsl::Instruction::Comment { content })
531 }
532
533 fn compile_metadata(
534 &mut self,
535 metadata: cube::Metadata,
536 out: Option<cube::Value>,
537 ) -> wgsl::Instruction {
538 let out = out.unwrap();
539 match metadata {
540 cube::Metadata::Stride { dim, list } => {
541 let position = self.ext_meta_pos(&list);
542 let offset = self.info.metadata.stride_offset_index(position);
543 wgsl::Instruction::ExtendedMeta {
544 info_offset: self.compile_value(offset.into()),
545 dim: self.compile_value(dim),
546 out: self.compile_value(out),
547 }
548 }
549 cube::Metadata::Shape { dim, list } => {
550 let position = self.ext_meta_pos(&list);
551 let offset = self.info.metadata.shape_offset_index(position);
552 wgsl::Instruction::ExtendedMeta {
553 info_offset: self.compile_value(offset.into()),
554 dim: self.compile_value(dim),
555 out: self.compile_value(out),
556 }
557 }
558 cube::Metadata::BufferLength { list } => match list.address_space() {
559 cube::AddressSpace::Global(id) => {
560 let offset = self.info.metadata.buffer_len_index(id);
561 wgsl::Instruction::Metadata {
562 out: self.compile_value(out),
563 info_offset: self.compile_value(offset.into()),
564 }
565 }
566 _ => wgsl::Instruction::Length {
567 list: self.compile_value(list),
568 out: self.compile_value(out),
569 },
570 },
571 }
572 }
573
574 fn compile_memory(
575 &mut self,
576 value: cube::Memory,
577 out: Option<cube::Value>,
578 instructions: &mut Vec<wgsl::Instruction>,
579 ) {
580 match value {
581 cube::Memory::Index(op) => {
582 instructions.push(wgsl::Instruction::Index {
583 lhs: self.compile_value(op.list),
584 rhs: self.compile_value(op.index),
585 out: self.compile_value(out.unwrap()),
586 });
587 }
588 cube::Memory::Load(value) => instructions.push(wgsl::Instruction::Load {
589 input: self.compile_value(value),
590 out: self.compile_value(out.unwrap()),
591 }),
592 cube::Memory::Store(op) => instructions.push(wgsl::Instruction::Store {
593 input: self.compile_value(op.value),
594 out: self.compile_value(op.ptr),
595 }),
596 cube::Memory::CopyMemory(op) => instructions.push(wgsl::Instruction::CopyBulk {
597 source: self.compile_value(op.source),
598 target: self.compile_value(op.target),
599 len: op.len as u32,
600 }),
601 }
602 }
603
604 fn compile_arithmetic(
605 &mut self,
606 value: cube::Arithmetic,
607 out: Option<cube::Value>,
608 instructions: &mut Vec<wgsl::Instruction>,
609 scope: &Scope,
610 ) {
611 let out = out.unwrap();
612 match value {
613 cube::Arithmetic::Max(op) => instructions.push(wgsl::Instruction::Max {
614 lhs: self.compile_value(op.lhs),
615 rhs: self.compile_value(op.rhs),
616 out: self.compile_value(out),
617 }),
618 cube::Arithmetic::Min(op) => instructions.push(wgsl::Instruction::Min {
619 lhs: self.compile_value(op.lhs),
620 rhs: self.compile_value(op.rhs),
621 out: self.compile_value(out),
622 }),
623 cube::Arithmetic::Add(op) => instructions.push(wgsl::Instruction::Add {
624 lhs: self.compile_value(op.lhs),
625 rhs: self.compile_value(op.rhs),
626 out: self.compile_value(out),
627 }),
628 cube::Arithmetic::SaturatingAdd(_) => {
629 unreachable!("Saturating add should be removed by processor");
630 }
631 cube::Arithmetic::Fma(op) => instructions.push(wgsl::Instruction::Fma {
632 a: self.compile_value(op.a),
633 b: self.compile_value(op.b),
634 c: self.compile_value(op.c),
635 out: self.compile_value(out),
636 }),
637 cube::Arithmetic::ModFloor(op) => instructions.push(wgsl::Instruction::ModFloor {
638 lhs: self.compile_value(op.lhs),
639 rhs: self.compile_value(op.rhs),
640 out: self.compile_value(out),
641 }),
642 cube::Arithmetic::Sub(op) => instructions.push(wgsl::Instruction::Sub {
643 lhs: self.compile_value(op.lhs),
644 rhs: self.compile_value(op.rhs),
645 out: self.compile_value(out),
646 }),
647 cube::Arithmetic::SaturatingSub(_) => {
648 unreachable!("Saturating sub should be removed by processor");
649 }
650 cube::Arithmetic::Mul(op) => instructions.push(wgsl::Instruction::Mul {
651 lhs: self.compile_value(op.lhs),
652 rhs: self.compile_value(op.rhs),
653 out: self.compile_value(out),
654 }),
655 cube::Arithmetic::Div(op) => instructions.push(wgsl::Instruction::Div {
656 lhs: self.compile_value(op.lhs),
657 rhs: self.compile_value(op.rhs),
658 out: self.compile_value(out),
659 }),
660 cube::Arithmetic::Abs(op) => instructions.push(wgsl::Instruction::Abs {
661 input: self.compile_value(op.input),
662 out: self.compile_value(out),
663 }),
664 cube::Arithmetic::Exp(op) => instructions.push(wgsl::Instruction::Exp {
665 input: self.compile_value(op.input),
666 out: self.compile_value(out),
667 }),
668 cube::Arithmetic::Log(op) => instructions.push(wgsl::Instruction::Log {
669 input: self.compile_value(op.input),
670 out: self.compile_value(out),
671 }),
672 cube::Arithmetic::Log1p(op) => instructions.push(wgsl::Instruction::Log1p {
673 input: self.compile_value(op.input),
674 out: self.compile_value(out),
675 }),
676 cube::Arithmetic::Expm1(op) => instructions.push(wgsl::Instruction::Expm1 {
677 input: self.compile_value(op.input),
678 out: self.compile_value(out),
679 }),
680 cube::Arithmetic::Cos(op) => instructions.push(wgsl::Instruction::Cos {
681 input: self.compile_value(op.input),
682 out: self.compile_value(out),
683 }),
684 cube::Arithmetic::Sin(op) => instructions.push(wgsl::Instruction::Sin {
685 input: self.compile_value(op.input),
686 out: self.compile_value(out),
687 }),
688 cube::Arithmetic::Tan(op) => instructions.push(wgsl::Instruction::Tan {
689 input: self.compile_value(op.input),
690 out: self.compile_value(out),
691 }),
692 cube::Arithmetic::Tanh(op) => instructions.push(wgsl::Instruction::Tanh {
693 input: self.compile_value(op.input),
694 out: self.compile_value(out),
695 }),
696 cube::Arithmetic::Sinh(op) => instructions.push(wgsl::Instruction::Sinh {
697 input: self.compile_value(op.input),
698 out: self.compile_value(out),
699 }),
700 cube::Arithmetic::Cosh(op) => instructions.push(wgsl::Instruction::Cosh {
701 input: self.compile_value(op.input),
702 out: self.compile_value(out),
703 }),
704 cube::Arithmetic::ArcCos(op) => instructions.push(wgsl::Instruction::ArcCos {
705 input: self.compile_value(op.input),
706 out: self.compile_value(out),
707 }),
708 cube::Arithmetic::ArcSin(op) => instructions.push(wgsl::Instruction::ArcSin {
709 input: self.compile_value(op.input),
710 out: self.compile_value(out),
711 }),
712 cube::Arithmetic::ArcTan(op) => instructions.push(wgsl::Instruction::ArcTan {
713 input: self.compile_value(op.input),
714 out: self.compile_value(out),
715 }),
716 cube::Arithmetic::ArcSinh(op) => instructions.push(wgsl::Instruction::ArcSinh {
717 input: self.compile_value(op.input),
718 out: self.compile_value(out),
719 }),
720 cube::Arithmetic::ArcCosh(op) => instructions.push(wgsl::Instruction::ArcCosh {
721 input: self.compile_value(op.input),
722 out: self.compile_value(out),
723 }),
724 cube::Arithmetic::ArcTanh(op) => instructions.push(wgsl::Instruction::ArcTanh {
725 input: self.compile_value(op.input),
726 out: self.compile_value(out),
727 }),
728 cube::Arithmetic::Degrees(op) => instructions.push(wgsl::Instruction::Degrees {
729 input: self.compile_value(op.input),
730 out: self.compile_value(out),
731 }),
732 cube::Arithmetic::Radians(op) => instructions.push(wgsl::Instruction::Radians {
733 input: self.compile_value(op.input),
734 out: self.compile_value(out),
735 }),
736 cube::Arithmetic::ArcTan2(op) => instructions.push(wgsl::Instruction::ArcTan2 {
737 lhs: self.compile_value(op.lhs),
738 rhs: self.compile_value(op.rhs),
739 out: self.compile_value(out),
740 }),
741 cube::Arithmetic::Powf(op) | cube::Arithmetic::Powi(op) => {
743 instructions.push(wgsl::Instruction::Powf {
744 lhs: self.compile_value(op.lhs),
745 rhs: self.compile_value(op.rhs),
746 out: self.compile_value(out),
747 })
748 }
749 cube::Arithmetic::Hypot(op) => {
750 let scope = scope.child();
751 expand_hypot(&scope, op.lhs, op.rhs, out);
752 instructions.extend(self.compile_scope(&scope));
753 }
754 cube::Arithmetic::Rhypot(op) => {
755 let scope = scope.child();
756 expand_rhypot(&scope, op.lhs, op.rhs, out);
757 instructions.extend(self.compile_scope(&scope));
758 }
759
760 cube::Arithmetic::Sqrt(op) => instructions.push(wgsl::Instruction::Sqrt {
761 input: self.compile_value(op.input),
762 out: self.compile_value(out),
763 }),
764 cube::Arithmetic::InverseSqrt(op) => {
765 instructions.push(wgsl::Instruction::InverseSqrt {
766 input: self.compile_value(op.input),
767 out: self.compile_value(out),
768 })
769 }
770 cube::Arithmetic::Round(op) => instructions.push(wgsl::Instruction::Round {
771 input: self.compile_value(op.input),
772 out: self.compile_value(out),
773 }),
774 cube::Arithmetic::Floor(op) => instructions.push(wgsl::Instruction::Floor {
775 input: self.compile_value(op.input),
776 out: self.compile_value(out),
777 }),
778 cube::Arithmetic::Ceil(op) => instructions.push(wgsl::Instruction::Ceil {
779 input: self.compile_value(op.input),
780 out: self.compile_value(out),
781 }),
782 cube::Arithmetic::Trunc(op) => instructions.push(wgsl::Instruction::Trunc {
783 input: self.compile_value(op.input),
784 out: self.compile_value(out),
785 }),
786 cube::Arithmetic::Erf(op) => {
787 let scope = scope.child();
788 expand_erf(&scope, op.input, out);
789 instructions.extend(self.compile_scope(&scope));
790 }
791 cube::Arithmetic::MulHi(op) => {
792 let scope = scope.child();
793 match self.compilation_options.supports_u64 {
794 true => expand_himul_64(&scope, op.lhs, op.rhs, out),
795 false => expand_himul_sim(&scope, op.lhs, op.rhs, out),
796 }
797 instructions.extend(self.compile_scope(&scope));
798 }
799 cube::Arithmetic::Recip(op) => instructions.push(wgsl::Instruction::Recip {
800 input: self.compile_value(op.input),
801 out: self.compile_value(out),
802 }),
803 cube::Arithmetic::Clamp(op) => instructions.push(wgsl::Instruction::Clamp {
804 input: self.compile_value(op.input),
805 min_value: self.compile_value(op.min_value),
806 max_value: self.compile_value(op.max_value),
807 out: self.compile_value(out),
808 }),
809 cube::Arithmetic::Rem(op) => instructions.push(wgsl::Instruction::Remainder {
810 lhs: self.compile_value(op.lhs),
811 rhs: self.compile_value(op.rhs),
812 out: self.compile_value(out),
813 }),
814 cube::Arithmetic::Neg(op) => instructions.push(wgsl::Instruction::Negate {
815 input: self.compile_value(op.input),
816 out: self.compile_value(out),
817 }),
818 cube::Arithmetic::Magnitude(op) => instructions.push(wgsl::Instruction::Magnitude {
819 input: self.compile_value(op.input),
820 out: self.compile_value(out),
821 }),
822 cube::Arithmetic::Normalize(op) => instructions.push(wgsl::Instruction::Normalize {
823 input: self.compile_value(op.input),
824 out: self.compile_value(out),
825 }),
826 cube::Arithmetic::Dot(op) => instructions.push(wgsl::Instruction::Dot {
827 lhs: self.compile_value(op.lhs),
828 rhs: self.compile_value(op.rhs),
829 out: self.compile_value(out),
830 }),
831 cube::Arithmetic::VectorSum(op) => instructions.push(wgsl::Instruction::VectorSum {
832 input: self.compile_value(op.input),
833 out: self.compile_value(out),
834 }),
835 }
836 }
837
838 fn compile_cmp(
839 &mut self,
840 value: cube::Comparison,
841 out: Option<cube::Value>,
842 instructions: &mut Vec<wgsl::Instruction>,
843 ) {
844 let out = out.unwrap();
845 match value {
846 cube::Comparison::Equal(op) => instructions.push(wgsl::Instruction::Equal {
847 lhs: self.compile_value(op.lhs),
848 rhs: self.compile_value(op.rhs),
849 out: self.compile_value(out),
850 }),
851 cube::Comparison::Lower(op) => instructions.push(wgsl::Instruction::Lower {
852 lhs: self.compile_value(op.lhs),
853 rhs: self.compile_value(op.rhs),
854 out: self.compile_value(out),
855 }),
856 cube::Comparison::Greater(op) => instructions.push(wgsl::Instruction::Greater {
857 lhs: self.compile_value(op.lhs),
858 rhs: self.compile_value(op.rhs),
859 out: self.compile_value(out),
860 }),
861 cube::Comparison::LowerEqual(op) => instructions.push(wgsl::Instruction::LowerEqual {
862 lhs: self.compile_value(op.lhs),
863 rhs: self.compile_value(op.rhs),
864 out: self.compile_value(out),
865 }),
866 cube::Comparison::GreaterEqual(op) => {
867 instructions.push(wgsl::Instruction::GreaterEqual {
868 lhs: self.compile_value(op.lhs),
869 rhs: self.compile_value(op.rhs),
870 out: self.compile_value(out),
871 })
872 }
873 cube::Comparison::NotEqual(op) => instructions.push(wgsl::Instruction::NotEqual {
874 lhs: self.compile_value(op.lhs),
875 rhs: self.compile_value(op.rhs),
876 out: self.compile_value(out),
877 }),
878 cube::Comparison::IsNan(op) => instructions.push(wgsl::Instruction::IsNan {
879 input: self.compile_value(op.input),
880 out: self.compile_value(out),
881 }),
882 cube::Comparison::IsInf(op) => instructions.push(wgsl::Instruction::IsInf {
883 input: self.compile_value(op.input),
884 out: self.compile_value(out),
885 }),
886 }
887 }
888
889 fn compile_bitwise(
890 &mut self,
891 value: cube::Bitwise,
892 out: Option<cube::Value>,
893 instructions: &mut Vec<wgsl::Instruction>,
894 ) {
895 let out = out.unwrap();
896 match value {
897 cube::Bitwise::BitwiseOr(op) => instructions.push(wgsl::Instruction::BitwiseOr {
898 lhs: self.compile_value(op.lhs),
899 rhs: self.compile_value(op.rhs),
900 out: self.compile_value(out),
901 }),
902 cube::Bitwise::BitwiseAnd(op) => instructions.push(wgsl::Instruction::BitwiseAnd {
903 lhs: self.compile_value(op.lhs),
904 rhs: self.compile_value(op.rhs),
905 out: self.compile_value(out),
906 }),
907 cube::Bitwise::BitwiseXor(op) => instructions.push(wgsl::Instruction::BitwiseXor {
908 lhs: self.compile_value(op.lhs),
909 rhs: self.compile_value(op.rhs),
910 out: self.compile_value(out),
911 }),
912 cube::Bitwise::CountOnes(op) => instructions.push(wgsl::Instruction::CountBits {
913 input: self.compile_value(op.input),
914 out: self.compile_value(out),
915 }),
916 cube::Bitwise::ReverseBits(op) => instructions.push(wgsl::Instruction::ReverseBits {
917 input: self.compile_value(op.input),
918 out: self.compile_value(out),
919 }),
920 cube::Bitwise::ShiftLeft(op) => instructions.push(wgsl::Instruction::ShiftLeft {
921 lhs: self.compile_value(op.lhs),
922 rhs: self.compile_value(op.rhs),
923 out: self.compile_value(out),
924 }),
925 cube::Bitwise::ShiftRight(op) => instructions.push(wgsl::Instruction::ShiftRight {
926 lhs: self.compile_value(op.lhs),
927 rhs: self.compile_value(op.rhs),
928 out: self.compile_value(out),
929 }),
930 cube::Bitwise::BitwiseNot(op) => instructions.push(wgsl::Instruction::BitwiseNot {
931 input: self.compile_value(op.input),
932 out: self.compile_value(out),
933 }),
934 cube::Bitwise::LeadingZeros(op) => instructions.push(wgsl::Instruction::LeadingZeros {
935 input: self.compile_value(op.input),
936 out: self.compile_value(out),
937 }),
938 cube::Bitwise::TrailingZeros(op) => {
939 instructions.push(wgsl::Instruction::TrailingZeros {
940 input: self.compile_value(op.input),
941 out: self.compile_value(out),
942 })
943 }
944 cube::Bitwise::FindFirstSet(op) => instructions.push(wgsl::Instruction::FindFirstSet {
945 input: self.compile_value(op.input),
946 out: self.compile_value(out),
947 }),
948 }
949 }
950
951 fn compile_operator(
952 &mut self,
953 value: cube::Operator,
954 out: Option<cube::Value>,
955 instructions: &mut Vec<wgsl::Instruction>,
956 ) {
957 let out = out.unwrap();
958 match value {
959 cube::Operator::Cast(op) => instructions.push(wgsl::Instruction::Assign {
960 input: self.compile_value(op.input),
961 out: self.compile_value(out),
962 }),
963
964 cube::Operator::And(op) => instructions.push(wgsl::Instruction::And {
965 lhs: self.compile_value(op.lhs),
966 rhs: self.compile_value(op.rhs),
967 out: self.compile_value(out),
968 }),
969 cube::Operator::Or(op) => instructions.push(wgsl::Instruction::Or {
970 lhs: self.compile_value(op.lhs),
971 rhs: self.compile_value(op.rhs),
972 out: self.compile_value(out),
973 }),
974 cube::Operator::Not(op) => instructions.push(wgsl::Instruction::Not {
975 input: self.compile_value(op.input),
976 out: self.compile_value(out),
977 }),
978 cube::Operator::Reinterpret(op) => instructions.push(wgsl::Instruction::Bitcast {
979 input: self.compile_value(op.input),
980 out: self.compile_value(out),
981 }),
982 cube::Operator::InitVector(op) => instructions.push(wgsl::Instruction::VecInit {
983 inputs: op
984 .inputs
985 .into_iter()
986 .map(|val| self.compile_value(val))
987 .collect(),
988 out: self.compile_value(out),
989 }),
990 cube::Operator::ExtractComponent(op) => instructions.push(wgsl::Instruction::Extract {
991 vector: self.compile_value(op.lhs),
992 index: self.compile_value(op.rhs),
993 out: self.compile_value(out),
994 }),
995 cube::Operator::InsertComponent(op) => instructions.push(wgsl::Instruction::Insert {
996 vector: self.compile_value(op.vector),
997 index: self.compile_value(op.index),
998 value: self.compile_value(op.value),
999 out: self.compile_value(out),
1000 }),
1001 cube::Operator::Select(op) => instructions.push(wgsl::Instruction::Select {
1002 cond: self.compile_value(op.cond),
1003 then: self.compile_value(op.then),
1004 or_else: self.compile_value(op.or_else),
1005 out: self.compile_value(out),
1006 }),
1007 cube::Operator::ReadBuiltin(builtin) => {
1008 let out = self.compile_value(out);
1009 let constant = {
1010 let out = out.clone();
1011 |value| {
1012 instructions.push(wgsl::Instruction::Assign { input: value, out });
1013 }
1014 };
1015 let builtin = match builtin {
1016 cube::Builtin::AbsolutePos => {
1017 self.id = true;
1018 wgsl::Builtin::Id
1019 }
1020 cube::Builtin::UnitPos => {
1021 self.local_invocation_index = true;
1022 wgsl::Builtin::LocalInvocationIndex
1023 }
1024 cube::Builtin::UnitPosX => {
1025 self.local_invocation_id = true;
1026 wgsl::Builtin::LocalInvocationIdX
1027 }
1028 cube::Builtin::UnitPosY => {
1029 self.local_invocation_id = true;
1030 wgsl::Builtin::LocalInvocationIdY
1031 }
1032 cube::Builtin::UnitPosZ => {
1033 self.local_invocation_id = true;
1034 wgsl::Builtin::LocalInvocationIdZ
1035 }
1036 cube::Builtin::CubePosX => {
1037 self.workgroup_id = true;
1038 wgsl::Builtin::WorkgroupIdX
1039 }
1040 cube::Builtin::CubePosY => {
1041 self.workgroup_id = true;
1042 wgsl::Builtin::WorkgroupIdY
1043 }
1044 cube::Builtin::CubePosZ => {
1045 self.workgroup_id = true;
1046 wgsl::Builtin::WorkgroupIdZ
1047 }
1048 cube::Builtin::CubePosCluster
1049 | cube::Builtin::CubePosClusterX
1050 | cube::Builtin::CubePosClusterY
1051 | cube::Builtin::CubePosClusterZ => {
1052 constant(self.constant_var(1));
1053 return;
1054 }
1055 cube::Builtin::AbsolutePosX => {
1056 self.global_invocation_id = true;
1057 wgsl::Builtin::GlobalInvocationIdX
1058 }
1059 cube::Builtin::AbsolutePosY => {
1060 self.global_invocation_id = true;
1061 wgsl::Builtin::GlobalInvocationIdY
1062 }
1063 cube::Builtin::AbsolutePosZ => {
1064 self.global_invocation_id = true;
1065 wgsl::Builtin::GlobalInvocationIdZ
1066 }
1067 cube::Builtin::CubeDimX => wgsl::Builtin::WorkgroupSizeX,
1068 cube::Builtin::CubeDimY => wgsl::Builtin::WorkgroupSizeY,
1069 cube::Builtin::CubeDimZ => wgsl::Builtin::WorkgroupSizeZ,
1070 cube::Builtin::CubeClusterDim
1071 | cube::Builtin::CubeClusterDimX
1072 | cube::Builtin::CubeClusterDimY
1073 | cube::Builtin::CubeClusterDimZ => {
1074 constant(self.constant_var(1));
1075 return;
1076 }
1077 cube::Builtin::CubeCountX => {
1078 self.num_workgroups = true;
1079 wgsl::Builtin::NumWorkgroupsX
1080 }
1081 cube::Builtin::CubeCountY => {
1082 self.num_workgroups = true;
1083 wgsl::Builtin::NumWorkgroupsY
1084 }
1085 cube::Builtin::CubeCountZ => {
1086 self.num_workgroups = true;
1087 wgsl::Builtin::NumWorkgroupsZ
1088 }
1089 cube::Builtin::CubePos => {
1090 self.workgroup_id_no_axis = true;
1091 wgsl::Builtin::WorkgroupId
1092 }
1093 cube::Builtin::CubeDim => {
1094 self.workgroup_size_no_axis = true;
1095 wgsl::Builtin::WorkgroupSize
1096 }
1097 cube::Builtin::CubeCount => {
1098 self.num_workgroup_no_axis = true;
1099 wgsl::Builtin::NumWorkgroups
1100 }
1101 cube::Builtin::PlaneDim => {
1102 self.subgroup_size = true;
1103 wgsl::Builtin::SubgroupSize
1104 }
1105 cube::Builtin::PlanePos => {
1106 self.subgroup_id = true;
1107 wgsl::Builtin::SubgroupId
1108 }
1109 cube::Builtin::UnitPosPlane => {
1110 self.subgroup_invocation_id = true;
1111 wgsl::Builtin::SubgroupInvocationId
1112 }
1113 };
1114 instructions.push(wgsl::Instruction::ReadBuiltin { builtin, out });
1115 }
1116 cube::Operator::ReadScalar(id) => instructions.push(wgsl::Instruction::ReadScalar {
1117 id,
1118 out: self.compile_value(out),
1119 }),
1120 }
1121 }
1122
1123 fn compile_atomic(
1124 &mut self,
1125 atomic: cube::AtomicOp,
1126 out: Option<cube::Value>,
1127 ) -> wgsl::Instruction {
1128 match atomic {
1129 cube::AtomicOp::Add(op) => wgsl::Instruction::AtomicAdd {
1130 ptr: self.compile_value(op.ptr),
1131 value: self.compile_value(op.value),
1132 out: self.compile_value(out.unwrap()),
1133 },
1134 cube::AtomicOp::Sub(op) => wgsl::Instruction::AtomicSub {
1135 ptr: self.compile_value(op.ptr),
1136 value: self.compile_value(op.value),
1137 out: self.compile_value(out.unwrap()),
1138 },
1139 cube::AtomicOp::Max(op) => wgsl::Instruction::AtomicMax {
1140 ptr: self.compile_value(op.ptr),
1141 value: self.compile_value(op.value),
1142 out: self.compile_value(out.unwrap()),
1143 },
1144 cube::AtomicOp::Min(op) => wgsl::Instruction::AtomicMin {
1145 ptr: self.compile_value(op.ptr),
1146 value: self.compile_value(op.value),
1147 out: self.compile_value(out.unwrap()),
1148 },
1149 cube::AtomicOp::And(op) => wgsl::Instruction::AtomicAnd {
1150 ptr: self.compile_value(op.ptr),
1151 value: self.compile_value(op.value),
1152 out: self.compile_value(out.unwrap()),
1153 },
1154 cube::AtomicOp::Or(op) => wgsl::Instruction::AtomicOr {
1155 ptr: self.compile_value(op.ptr),
1156 value: self.compile_value(op.value),
1157 out: self.compile_value(out.unwrap()),
1158 },
1159 cube::AtomicOp::Xor(op) => wgsl::Instruction::AtomicXor {
1160 ptr: self.compile_value(op.ptr),
1161 value: self.compile_value(op.value),
1162 out: self.compile_value(out.unwrap()),
1163 },
1164 cube::AtomicOp::Load(ptr) => wgsl::Instruction::AtomicLoad {
1165 input: self.compile_value(ptr),
1166 out: self.compile_value(out.unwrap()),
1167 },
1168 cube::AtomicOp::Store(op) => wgsl::Instruction::AtomicStore {
1169 input: self.compile_value(op.value),
1170 out: self.compile_value(op.ptr),
1171 },
1172 cube::AtomicOp::Swap(op) => wgsl::Instruction::AtomicSwap {
1173 lhs: self.compile_value(op.ptr),
1174 rhs: self.compile_value(op.value),
1175 out: self.compile_value(out.unwrap()),
1176 },
1177 cube::AtomicOp::CompareAndSwap(op) => wgsl::Instruction::AtomicCompareExchangeWeak {
1178 ptr: self.compile_value(op.ptr),
1179 cmp: self.compile_value(op.cmp),
1180 value: self.compile_value(op.val),
1181 out: self.compile_value(out.unwrap()),
1182 },
1183 }
1184 }
1185
1186 fn compile_binding(&mut self, arg: kernel::KernelArg) -> wgsl::KernelArg {
1187 wgsl::KernelArg {
1188 id: arg.id,
1189 visibility: self.buffer_vis[arg.id as usize],
1190 value: self.compile_value(arg.value),
1191 }
1192 }
1193}
1194
1195fn register_extensions(instructions: &[wgsl::Instruction]) -> Vec<wgsl::Extension> {
1196 let mut extensions = Vec::new();
1197
1198 let mut register_extension = |extension: wgsl::Extension| {
1199 if !extensions.contains(&extension) {
1200 extensions.push(extension);
1201 }
1202 };
1203
1204 for instruction in instructions {
1206 match instruction {
1207 wgsl::Instruction::Powf { lhs: _, rhs, out } => {
1208 register_extension(wgsl::Extension::PowfPrimitive(out.elem()));
1209 register_extension(wgsl::powf_extension(rhs, out));
1210 }
1211 #[cfg(target_os = "macos")]
1212 wgsl::Instruction::Tanh { input, out: _ } => {
1213 register_extension(wgsl::Extension::SafeTanhPrimitive(input.elem()));
1214 register_extension(wgsl::Extension::SafeTanh(input.item()));
1215 }
1216 wgsl::Instruction::IsNan { input, out } => {
1217 register_extension(wgsl::Extension::IsNanPrimitive(input.elem()));
1218 register_extension(wgsl::Extension::IsNan(input.item(), out.item()));
1219 }
1220 wgsl::Instruction::IsInf { input, out } => {
1221 register_extension(wgsl::Extension::IsInfPrimitive(input.elem()));
1222 register_extension(wgsl::Extension::IsInf(input.item(), out.item()));
1223 }
1224 wgsl::Instruction::If { instructions, .. } => {
1225 for extension in register_extensions(instructions) {
1226 register_extension(extension);
1227 }
1228 }
1229 wgsl::Instruction::IfElse {
1230 instructions_if,
1231 instructions_else,
1232 ..
1233 } => {
1234 for extension in register_extensions(instructions_if) {
1235 register_extension(extension);
1236 }
1237 for extension in register_extensions(instructions_else) {
1238 register_extension(extension);
1239 }
1240 }
1241 wgsl::Instruction::Loop { instructions } => {
1242 for extension in register_extensions(instructions) {
1243 register_extension(extension);
1244 }
1245 }
1246 wgsl::Instruction::RangeLoop { instructions, .. } => {
1247 for extension in register_extensions(instructions) {
1248 register_extension(extension);
1249 }
1250 }
1251 _ => {}
1252 }
1253 }
1254
1255 extensions
1256}