cubecl_wgpu/compiler/wgsl/
compiler.rs1use super::shader::ComputeShader;
2use crate::compiler::wgsl::{
3 self, EnableFeaturesPass, builtin::LowerBuiltinsPass, lower::LowerOpsWgslPass,
4 metadata::declare_info, rewrite_args, shared_memory_size, types,
5};
6use cubecl_server::kernel::BufferIOAttr;
7
8use cubecl_core::{
9 WgpuCompilationOptions,
10 post_processing::{
11 checked_io::{CheckedIo, CheckedIoPass},
12 minifloat::{
13 Fp8Container, LowerMinifloatCast, LowerMinifloatCastPass, LowerMinifloatCompare,
14 LowerMinifloatComparePass,
15 },
16 saturating::LowerSaturatingArithmeticPass,
17 unroll::UnrollPass,
18 },
19};
20use cubecl_environment::backtrace::BackTrace;
21use cubecl_ir::{
22 ContextExt,
23 features::EnumSet,
24 pliron::{
25 builtin::ops::{FuncOp, ModuleOp},
26 operation::verify_operation,
27 opts::{dce::DCEPass, mem2reg::Mem2RegPass},
28 },
29 prelude::{AnalysisManager, NestedOpsPass, Op, OpPass, PMConfig, Pass, Passes},
30 rewrite::SimplifyOpsPass,
31 settings::Dim3,
32};
33use cubecl_opt::passes::{
34 annotate_buffer_visibility::AnnotateGlobalVisibilityPass, inst_combine::InstCombinePass,
35 sccp::SCCPPass, simple_cse::SimpleCSEPass, sroa::SROAPass,
36};
37use cubecl_server::compiler::CompilationError;
38use cubecl_server::kernel;
39
40const MAX_VECTOR_SIZE: usize = 4;
41
42pub struct KernelInfo {
43 pub cube_dim: Dim3,
44}
45
46#[derive(Clone, Default)]
48pub struct WgslCompiler;
49
50impl core::fmt::Debug for WgslCompiler {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.write_str("WgslCompiler")
53 }
54}
55
56impl cubecl_core::Compiler for WgslCompiler {
57 type Representation = ComputeShader;
58 type CompilationOptions = WgpuCompilationOptions;
59
60 fn buffer_io(repr: &Self::Representation) -> Option<Vec<BufferIOAttr>> {
61 Some(repr.io.clone())
62 }
63
64 fn compile(
65 &mut self,
66 shader: kernel::KernelDefinition,
67 compilation_options: &Self::CompilationOptions,
68 ) -> Result<Self::Representation, CompilationError> {
69 self.compile_shader(shader, compilation_options)
70 }
71
72 fn extension(&self) -> &'static str {
73 "wgsl"
74 }
75
76 fn lang_tag(&self) -> &'static str {
77 "wgsl"
78 }
79}
80
81impl WgslCompiler {
82 fn compile_shader(
83 &mut self,
84 value: kernel::KernelDefinition,
85 compilation_options: &WgpuCompilationOptions,
86 ) -> Result<wgsl::ComputeShader, CompilationError> {
87 #[cfg(target_family = "wasm")]
88 crate::compiler::wgsl::ops::wasm_inventory_root();
89
90 let errors = value.body.pop_errors();
91 if !errors.is_empty() {
92 let mut reason = "Can't compile wgsl kernel".to_string();
93 for error in errors {
94 reason += error.as_str();
95 reason += "\n";
96 }
97
98 return Err(CompilationError::Validation {
99 reason,
100 backtrace: BackTrace::capture(),
101 });
102 }
103
104 #[cfg(feature = "pliron-dump")]
105 let ir_printing_dir = kernel_dir_name(&value.settings.kernel_name);
106
107 let module = value.body.state().module;
108 let entry_func = value.body.state().entry_func;
109 let module_op = module.get_operation();
110 let mut ctx = value.body.into_context().expect("Should be unique");
111 ctx.set_aux_ty(value.info);
112 ctx.set_aux_ty(KernelInfo {
113 cube_dim: value.settings.cube_dim,
114 });
115 ctx.set_aux_ty(*compilation_options);
116
117 #[cfg(feature = "pliron-dump")]
118 if let Some(print_dir) = &ir_printing_dir {
119 use pliron::printable::Printable;
120 let str = std::format!("{}", module_op.disp(&ctx));
121 std::fs::write(print_dir.join("initial.plir"), &str).unwrap();
122 }
123
124 verify_operation(module_op, &ctx)?;
125 types::check_fp8_lanes(&ctx, module_op)?;
126
127 let config = PMConfig {
128 #[cfg(feature = "pliron-dump")]
129 ir_printing_dir,
130 print_after_all: cfg!(feature = "pliron-dump"),
131 ..Default::default()
132 };
133
134 let mut analyses = AnalysisManager::default();
135 analyses.set_config(config);
136
137 let mut passes = OpPass::<ModuleOp, Passes>::default();
138 let mut func_passes = OpPass::<FuncOp, Passes>::default();
139
140 func_passes.add_pass(SROAPass);
141 func_passes.add_pass(CheckedIoPass::new(CheckedIo::new(
142 value.settings.execution_mode,
143 value.settings.kernel_name.clone(),
144 )));
145 func_passes.add_pass(UnrollPass::new(MAX_VECTOR_SIZE));
146 func_passes.add_pass(LowerMinifloatCastPass::new(LowerMinifloatCast::new(
148 EnumSet::empty(),
149 Fp8Container::Words,
150 )));
151 func_passes.add_pass(LowerMinifloatComparePass::new(LowerMinifloatCompare::new(
152 Fp8Container::Words,
153 )));
154
155 func_passes.add_pass(LowerOpsWgslPass::default());
156 func_passes.add_pass(LowerSaturatingArithmeticPass::default());
157 func_passes.add_pass(LowerBuiltinsPass);
158
159 func_passes.add_pass(SCCPPass);
160 func_passes.add_pass(InstCombinePass::default());
161 func_passes.add_pass(SimpleCSEPass::without_memory());
162 func_passes.add_pass(SimplifyOpsPass::default());
163 func_passes.add_pass(DCEPass);
164 func_passes.add_pass(SROAPass);
165
166 func_passes.add_pass(Mem2RegPass);
168
169 func_passes.add_pass(SROAPass);
170 func_passes.add_pass(SCCPPass);
171 func_passes.add_pass(SimpleCSEPass::with_memory());
172 func_passes.add_pass(SimplifyOpsPass::default());
173 func_passes.add_pass(DCEPass);
174
175 passes.add_pass(NestedOpsPass::new(func_passes));
176 passes.add_pass(AnnotateGlobalVisibilityPass);
177 passes.add_pass(EnableFeaturesPass);
178
179 passes.run(module_op, &mut ctx, &mut analyses)?;
180
181 let io = cubecl_core::ir::attributes::buffer_io_by_position(&ctx, entry_func)
184 .into_iter()
185 .collect();
186 let buffers = rewrite_args(&mut ctx, entry_func);
187 declare_info(&mut ctx, entry_func, buffers.len());
188 let shared_memory_size = shared_memory_size(&ctx, module_op);
189
190 verify_operation(module.get_operation(), &ctx)?;
191
192 Ok(ComputeShader {
193 buffers,
194 io,
195 shared_memory_size,
196 ctx,
197 })
198 }
199}
200
201#[cfg(feature = "pliron-dump")]
202pub fn kernel_dir_name(name: &str) -> Option<std::path::PathBuf> {
203 if let Ok(dir) = std::env::var("CUBECL_DEBUG_PLIRON") {
204 let path = sanitize_filename::sanitize_with_options(
205 name,
206 sanitize_filename::Options {
207 replacement: "_",
208 ..Default::default()
209 },
210 );
211 let dir = std::path::PathBuf::from(dir).join(&path);
212 std::fs::create_dir_all(&dir).unwrap();
213 Some(dir)
214 } else {
215 None
216 }
217}