1 // Copyright © 2022 Collabora, Ltd.
2 // SPDX-License-Identifier: MIT
3
4 use crate::from_nir::*;
5 use crate::ir::{ShaderInfo, ShaderIoInfo, ShaderModel, ShaderStageInfo};
6 use crate::sm50::ShaderModel50;
7 use crate::sm70::ShaderModel70;
8 use crate::sph;
9
10 use compiler::bindings::*;
11 use nak_bindings::*;
12
13 use std::cmp::max;
14 use std::env;
15 use std::ffi::{CStr, CString};
16 use std::fmt::Write;
17 use std::os::raw::c_void;
18 use std::sync::OnceLock;
19
20 #[repr(u8)]
21 enum DebugFlags {
22 Print,
23 Serial,
24 Spill,
25 Annotate,
26 NoUgpr,
27 }
28
29 pub struct Debug {
30 flags: u32,
31 }
32
33 impl Debug {
new() -> Debug34 fn new() -> Debug {
35 let debug_var = "NAK_DEBUG";
36 let debug_str = match env::var(debug_var) {
37 Ok(s) => s,
38 Err(_) => {
39 return Debug { flags: 0 };
40 }
41 };
42
43 let mut flags = 0;
44 for flag in debug_str.split(',') {
45 match flag.trim() {
46 "print" => flags |= 1 << DebugFlags::Print as u8,
47 "serial" => flags |= 1 << DebugFlags::Serial as u8,
48 "spill" => flags |= 1 << DebugFlags::Spill as u8,
49 "annotate" => flags |= 1 << DebugFlags::Annotate as u8,
50 "nougpr" => flags |= 1 << DebugFlags::NoUgpr as u8,
51 unk => eprintln!("Unknown NAK_DEBUG flag \"{}\"", unk),
52 }
53 }
54 Debug { flags: flags }
55 }
56 }
57
58 pub trait GetDebugFlags {
debug_flags(&self) -> u3259 fn debug_flags(&self) -> u32;
60
print(&self) -> bool61 fn print(&self) -> bool {
62 self.debug_flags() & (1 << DebugFlags::Print as u8) != 0
63 }
64
serial(&self) -> bool65 fn serial(&self) -> bool {
66 self.debug_flags() & (1 << DebugFlags::Serial as u8) != 0
67 }
68
spill(&self) -> bool69 fn spill(&self) -> bool {
70 self.debug_flags() & (1 << DebugFlags::Spill as u8) != 0
71 }
72
annotate(&self) -> bool73 fn annotate(&self) -> bool {
74 self.debug_flags() & (1 << DebugFlags::Annotate as u8) != 0
75 }
76
no_ugpr(&self) -> bool77 fn no_ugpr(&self) -> bool {
78 self.debug_flags() & (1 << DebugFlags::NoUgpr as u8) != 0
79 }
80 }
81
82 pub static DEBUG: OnceLock<Debug> = OnceLock::new();
83
84 impl GetDebugFlags for OnceLock<Debug> {
debug_flags(&self) -> u3285 fn debug_flags(&self) -> u32 {
86 self.get_or_init(Debug::new).flags
87 }
88 }
89
90 #[no_mangle]
nak_should_print_nir() -> bool91 pub extern "C" fn nak_should_print_nir() -> bool {
92 DEBUG.print()
93 }
94
nir_options(dev: &nv_device_info) -> nir_shader_compiler_options95 fn nir_options(dev: &nv_device_info) -> nir_shader_compiler_options {
96 let mut op: nir_shader_compiler_options = unsafe { std::mem::zeroed() };
97
98 op.lower_fdiv = true;
99 op.fuse_ffma16 = true;
100 op.fuse_ffma32 = true;
101 op.fuse_ffma64 = true;
102 op.lower_flrp16 = true;
103 op.lower_flrp32 = true;
104 op.lower_flrp64 = true;
105 op.lower_fsqrt = dev.sm < 52;
106 op.lower_bitfield_extract = dev.sm >= 70;
107 op.lower_bitfield_insert = true;
108 op.lower_pack_half_2x16 = true;
109 op.lower_pack_unorm_2x16 = true;
110 op.lower_pack_snorm_2x16 = true;
111 op.lower_pack_unorm_4x8 = true;
112 op.lower_pack_snorm_4x8 = true;
113 op.lower_unpack_half_2x16 = true;
114 op.lower_unpack_unorm_2x16 = true;
115 op.lower_unpack_snorm_2x16 = true;
116 op.lower_unpack_unorm_4x8 = true;
117 op.lower_unpack_snorm_4x8 = true;
118 op.lower_insert_byte = true;
119 op.lower_insert_word = true;
120 op.lower_cs_local_index_to_id = true;
121 op.lower_device_index_to_zero = true;
122 op.lower_isign = true;
123 op.lower_uadd_sat = dev.sm < 70;
124 op.lower_usub_sat = dev.sm < 70;
125 op.lower_iadd_sat = true; // TODO
126 op.lower_doubles_options = nir_lower_drcp
127 | nir_lower_dsqrt
128 | nir_lower_drsq
129 | nir_lower_dtrunc
130 | nir_lower_dfloor
131 | nir_lower_dceil
132 | nir_lower_dfract
133 | nir_lower_dround_even
134 | nir_lower_dsat;
135 if dev.sm >= 70 {
136 op.lower_doubles_options |= nir_lower_dminmax;
137 }
138 op.lower_int64_options = !(nir_lower_icmp64
139 | nir_lower_iadd64
140 | nir_lower_ineg64
141 | nir_lower_shift64
142 | nir_lower_imul_2x32_64
143 | nir_lower_conv64);
144 op.lower_ldexp = true;
145 op.lower_fmod = true;
146 op.lower_ffract = true;
147 op.lower_fpow = true;
148 op.lower_scmp = true;
149 op.lower_uadd_carry = true;
150 op.lower_usub_borrow = true;
151 op.has_iadd3 = dev.sm >= 70;
152 op.has_imad32 = dev.sm >= 70;
153 op.has_sdot_4x8 = dev.sm >= 70;
154 op.has_udot_4x8 = dev.sm >= 70;
155 op.has_sudot_4x8 = dev.sm >= 70;
156 // We set .ftz on f32 by default so we can support fmulz whenever the client
157 // doesn't explicitly request denorms.
158 op.has_fmulz_no_denorms = true;
159 op.has_find_msb_rev = true;
160 op.has_pack_half_2x16_rtz = true;
161 op.has_bfm = dev.sm >= 70;
162 op.discard_is_demote = true;
163
164 op.max_unroll_iterations = 32;
165 op.scalarize_ddx = true;
166
167 op
168 }
169
170 #[no_mangle]
nak_compiler_create( dev: *const nv_device_info, ) -> *mut nak_compiler171 pub extern "C" fn nak_compiler_create(
172 dev: *const nv_device_info,
173 ) -> *mut nak_compiler {
174 assert!(!dev.is_null());
175 let dev = unsafe { &*dev };
176
177 let nak = Box::new(nak_compiler {
178 sm: dev.sm,
179 warps_per_sm: dev.max_warps_per_mp,
180 nir_options: nir_options(dev),
181 });
182
183 Box::into_raw(nak)
184 }
185
186 #[no_mangle]
nak_compiler_destroy(nak: *mut nak_compiler)187 pub extern "C" fn nak_compiler_destroy(nak: *mut nak_compiler) {
188 unsafe { drop(Box::from_raw(nak)) };
189 }
190
191 #[no_mangle]
nak_debug_flags(_nak: *const nak_compiler) -> u64192 pub extern "C" fn nak_debug_flags(_nak: *const nak_compiler) -> u64 {
193 DEBUG.debug_flags().into()
194 }
195
196 #[no_mangle]
nak_nir_options( nak: *const nak_compiler, ) -> *const nir_shader_compiler_options197 pub extern "C" fn nak_nir_options(
198 nak: *const nak_compiler,
199 ) -> *const nir_shader_compiler_options {
200 assert!(!nak.is_null());
201 let nak = unsafe { &*nak };
202 &nak.nir_options
203 }
204
205 #[repr(C)]
206 pub struct ShaderBin {
207 pub bin: nak_shader_bin,
208 code: Vec<u32>,
209 asm: CString,
210 }
211
212 impl ShaderBin {
new( sm: &dyn ShaderModel, info: &ShaderInfo, fs_key: Option<&nak_fs_key>, code: Vec<u32>, asm: &str, ) -> ShaderBin213 pub fn new(
214 sm: &dyn ShaderModel,
215 info: &ShaderInfo,
216 fs_key: Option<&nak_fs_key>,
217 code: Vec<u32>,
218 asm: &str,
219 ) -> ShaderBin {
220 let asm = CString::new(asm)
221 .expect("NAK assembly has unexpected null characters");
222
223 let c_info = nak_shader_info {
224 stage: match info.stage {
225 ShaderStageInfo::Compute(_) => MESA_SHADER_COMPUTE,
226 ShaderStageInfo::Vertex => MESA_SHADER_VERTEX,
227 ShaderStageInfo::Fragment(_) => MESA_SHADER_FRAGMENT,
228 ShaderStageInfo::Geometry(_) => MESA_SHADER_GEOMETRY,
229 ShaderStageInfo::TessellationInit(_) => MESA_SHADER_TESS_CTRL,
230 ShaderStageInfo::Tessellation(_) => MESA_SHADER_TESS_EVAL,
231 },
232 sm: sm.sm(),
233 num_gprs: {
234 max(4, info.num_gprs as u32 + sm.hw_reserved_gprs())
235 .try_into()
236 .unwrap()
237 },
238 num_control_barriers: info.num_control_barriers,
239 _pad0: Default::default(),
240 num_instrs: info.num_instrs,
241 slm_size: info.slm_size,
242 crs_size: sm.crs_size(info.max_crs_depth),
243 __bindgen_anon_1: match &info.stage {
244 ShaderStageInfo::Compute(cs_info) => {
245 nak_shader_info__bindgen_ty_1 {
246 cs: nak_shader_info__bindgen_ty_1__bindgen_ty_1 {
247 local_size: [
248 cs_info.local_size[0],
249 cs_info.local_size[1],
250 cs_info.local_size[2],
251 ],
252 smem_size: cs_info.smem_size,
253 _pad: Default::default(),
254 },
255 }
256 }
257 ShaderStageInfo::Fragment(fs_info) => {
258 let fs_io_info = match &info.io {
259 ShaderIoInfo::Fragment(io) => io,
260 _ => unreachable!(),
261 };
262 nak_shader_info__bindgen_ty_1 {
263 fs: nak_shader_info__bindgen_ty_1__bindgen_ty_2 {
264 writes_depth: fs_io_info.writes_depth,
265 reads_sample_mask: fs_io_info.reads_sample_mask,
266 post_depth_coverage: fs_info.post_depth_coverage,
267 uses_sample_shading: fs_info.uses_sample_shading,
268 early_fragment_tests: fs_info.early_fragment_tests,
269 _pad: Default::default(),
270 },
271 }
272 }
273 ShaderStageInfo::Tessellation(ts_info) => {
274 nak_shader_info__bindgen_ty_1 {
275 ts: nak_shader_info__bindgen_ty_1__bindgen_ty_3 {
276 domain: ts_info.domain as u8,
277 spacing: ts_info.spacing as u8,
278 prims: ts_info.primitives as u8,
279 _pad: Default::default(),
280 },
281 }
282 }
283 _ => nak_shader_info__bindgen_ty_1 {
284 _pad: Default::default(),
285 },
286 },
287 vtg: match &info.io {
288 ShaderIoInfo::Vtg(io) => nak_shader_info__bindgen_ty_2 {
289 writes_layer: io.attr_written(NAK_ATTR_RT_ARRAY_INDEX),
290 writes_point_size: io.attr_written(NAK_ATTR_POINT_SIZE),
291 writes_vprs_table_index: io
292 .attr_written(NAK_ATTR_VPRS_TABLE_INDEX),
293 clip_enable: io.clip_enable.try_into().unwrap(),
294 cull_enable: io.cull_enable.try_into().unwrap(),
295 xfb: if let Some(xfb) = &io.xfb {
296 **xfb
297 } else {
298 unsafe { std::mem::zeroed() }
299 },
300 _pad: Default::default(),
301 },
302 _ => unsafe { std::mem::zeroed() },
303 },
304 hdr: sph::encode_header(sm, &info, fs_key),
305 };
306
307 if DEBUG.print() {
308 let stage_name = unsafe {
309 let c_name = _mesa_shader_stage_to_string(c_info.stage as u32);
310 CStr::from_ptr(c_name).to_str().expect("Invalid UTF-8")
311 };
312
313 eprintln!("Stage: {}", stage_name);
314 eprintln!("Instruction count: {}", c_info.num_instrs);
315 eprintln!("Num GPRs: {}", c_info.num_gprs);
316 eprintln!("SLM size: {}", c_info.slm_size);
317
318 if c_info.stage != MESA_SHADER_COMPUTE {
319 eprint_hex("Header", &c_info.hdr);
320 }
321
322 eprint_hex("Encoded shader", &code);
323 }
324
325 let bin = nak_shader_bin {
326 info: c_info,
327 code_size: (code.len() * 4).try_into().unwrap(),
328 code: code.as_ptr() as *const c_void,
329 asm_str: if asm.is_empty() {
330 std::ptr::null()
331 } else {
332 asm.as_ptr()
333 },
334 };
335 ShaderBin {
336 bin: bin,
337 code: code,
338 asm: asm,
339 }
340 }
341 }
342
343 impl std::ops::Deref for ShaderBin {
344 type Target = nak_shader_bin;
345
deref(&self) -> &nak_shader_bin346 fn deref(&self) -> &nak_shader_bin {
347 &self.bin
348 }
349 }
350
351 #[no_mangle]
nak_shader_bin_destroy(bin: *mut nak_shader_bin)352 pub extern "C" fn nak_shader_bin_destroy(bin: *mut nak_shader_bin) {
353 unsafe {
354 _ = Box::from_raw(bin as *mut ShaderBin);
355 };
356 }
357
eprint_hex(label: &str, data: &[u32])358 fn eprint_hex(label: &str, data: &[u32]) {
359 eprint!("{}:", label);
360 for i in 0..data.len() {
361 if (i % 8) == 0 {
362 eprintln!("");
363 eprint!(" ");
364 }
365 eprint!(" {:08x}", data[i]);
366 }
367 eprintln!("");
368 }
369
370 macro_rules! pass {
371 ($s: expr, $pass: ident) => {
372 $s.$pass();
373 if DEBUG.print() {
374 eprintln!("NAK IR after {}:\n{}", stringify!($pass), $s);
375 }
376 };
377 }
378
379 #[no_mangle]
nak_compile_shader( nir: *mut nir_shader, dump_asm: bool, nak: *const nak_compiler, robust2_modes: nir_variable_mode, fs_key: *const nak_fs_key, ) -> *mut nak_shader_bin380 pub extern "C" fn nak_compile_shader(
381 nir: *mut nir_shader,
382 dump_asm: bool,
383 nak: *const nak_compiler,
384 robust2_modes: nir_variable_mode,
385 fs_key: *const nak_fs_key,
386 ) -> *mut nak_shader_bin {
387 unsafe { nak_postprocess_nir(nir, nak, robust2_modes, fs_key) };
388 let nak = unsafe { &*nak };
389 let nir = unsafe { &*nir };
390 let fs_key = if fs_key.is_null() {
391 None
392 } else {
393 Some(unsafe { &*fs_key })
394 };
395
396 let sm: Box<dyn ShaderModel> = if nak.sm >= 70 {
397 Box::new(ShaderModel70::new(nak.sm))
398 } else if nak.sm >= 50 {
399 Box::new(ShaderModel50::new(nak.sm))
400 } else {
401 panic!("Unsupported shader model");
402 };
403
404 let mut s = nak_shader_from_nir(nak, nir, sm.as_ref());
405
406 if DEBUG.print() {
407 eprintln!("NAK IR:\n{}", &s);
408 }
409
410 pass!(s, opt_bar_prop);
411 pass!(s, opt_uniform_instrs);
412 pass!(s, opt_copy_prop);
413 pass!(s, opt_prmt);
414 pass!(s, opt_lop);
415 pass!(s, opt_copy_prop);
416 pass!(s, opt_dce);
417 pass!(s, opt_out);
418 pass!(s, legalize);
419 pass!(s, assign_regs);
420 pass!(s, lower_par_copies);
421 pass!(s, lower_copy_swap);
422 if nak.sm >= 70 {
423 pass!(s, opt_jump_thread);
424 } else {
425 pass!(s, opt_crs);
426 }
427
428 s.remove_annotations();
429
430 pass!(s, calc_instr_deps);
431
432 s.gather_info();
433
434 let mut asm = String::new();
435 if dump_asm {
436 write!(asm, "{}", s).expect("Failed to dump assembly");
437 }
438
439 let code = sm.encode_shader(&s);
440 let bin =
441 Box::new(ShaderBin::new(sm.as_ref(), &s.info, fs_key, code, &asm));
442 Box::into_raw(bin) as *mut nak_shader_bin
443 }
444