From 2a0302967feb5efb541a8a702d18c933af6e75d1 Mon Sep 17 00:00:00 2001 From: Mel Henning Date: Wed, 15 Jan 2025 16:40:10 -0500 Subject: [PATCH] nak: Add spill/fill statistics Part-of: --- src/nouveau/compiler/nak.h | 12 ++++ src/nouveau/compiler/nak/api.rs | 8 +++ src/nouveau/compiler/nak/assign_regs.rs | 4 +- src/nouveau/compiler/nak/from_nir.rs | 4 ++ src/nouveau/compiler/nak/hw_tests.rs | 4 ++ src/nouveau/compiler/nak/ir.rs | 4 ++ src/nouveau/compiler/nak/spill_values.rs | 73 +++++++++++++++--------- src/nouveau/vulkan/nvk_shader.c | 30 ++++++++++ 8 files changed, 111 insertions(+), 28 deletions(-) diff --git a/src/nouveau/compiler/nak.h b/src/nouveau/compiler/nak.h index 5503c6b4c36..851d1bf2fb5 100644 --- a/src/nouveau/compiler/nak.h +++ b/src/nouveau/compiler/nak.h @@ -146,6 +146,18 @@ struct nak_shader_info { /** Number of cycles used by fixed-latency instructions */ uint32_t num_static_cycles; + /** Number of spills from GPRs to Memory */ + uint32_t num_spills_to_mem; + + /** Number of fills from Memory to GPRs */ + uint32_t num_fills_from_mem; + + /** Number of spills between register files */ + uint32_t num_spills_to_reg; + + /** Number of fills between register files */ + uint32_t num_fills_from_reg; + /** Size of shader local (scratch) memory */ uint32_t slm_size; diff --git a/src/nouveau/compiler/nak/api.rs b/src/nouveau/compiler/nak/api.rs index b191f5d4e27..f81edb995a3 100644 --- a/src/nouveau/compiler/nak/api.rs +++ b/src/nouveau/compiler/nak/api.rs @@ -241,6 +241,10 @@ impl ShaderBin { max_warps_per_sm: info.max_warps_per_sm, num_instrs: info.num_instrs, num_static_cycles: info.num_static_cycles, + num_spills_to_mem: info.num_spills_to_mem, + num_fills_from_mem: info.num_fills_from_mem, + num_spills_to_reg: info.num_spills_to_reg, + num_fills_from_reg: info.num_fills_from_reg, slm_size: info.slm_size, crs_size: sm.crs_size(info.max_crs_depth), __bindgen_anon_1: match &info.stage { @@ -317,6 +321,10 @@ impl ShaderBin { eprintln!("Instruction count: {}", c_info.num_instrs); eprintln!("Static cycle count: {}", c_info.num_static_cycles); eprintln!("Max warps/SM: {}", c_info.max_warps_per_sm); + eprintln!("Spills to mem: {}", c_info.num_spills_to_mem); + eprintln!("Spills to reg: {}", c_info.num_spills_to_reg); + eprintln!("Fills from mem: {}", c_info.num_fills_from_mem); + eprintln!("Fills from reg: {}", c_info.num_fills_from_reg); eprintln!("Num GPRs: {}", c_info.num_gprs); eprintln!("SLM size: {}", c_info.slm_size); diff --git a/src/nouveau/compiler/nak/assign_regs.rs b/src/nouveau/compiler/nak/assign_regs.rs index 149ce0a1778..194689f9b70 100644 --- a/src/nouveau/compiler/nak/assign_regs.rs +++ b/src/nouveau/compiler/nak/assign_regs.rs @@ -1419,7 +1419,7 @@ impl Shader<'_> { for file in spill_files { let num_regs = self.sm.num_regs(file); if max_live[file] > num_regs { - f.spill_values(file, num_regs); + f.spill_values(file, num_regs, &mut self.info); // Re-calculate liveness after we spill live = SimpleLiveness::for_function(f); @@ -1461,7 +1461,7 @@ impl Shader<'_> { total_gprs = max_gprs; gpr_limit = total_gprs - u32::from(tmp_gprs); - f.spill_values(RegFile::GPR, gpr_limit); + f.spill_values(RegFile::GPR, gpr_limit, &mut self.info); // Re-calculate liveness one last time live = SimpleLiveness::for_function(f); diff --git a/src/nouveau/compiler/nak/from_nir.rs b/src/nouveau/compiler/nak/from_nir.rs index 0b7be834001..1abfd089d8f 100644 --- a/src/nouveau/compiler/nak/from_nir.rs +++ b/src/nouveau/compiler/nak/from_nir.rs @@ -25,6 +25,10 @@ fn init_info_from_nir(nak: &nak_compiler, nir: &nir_shader) -> ShaderInfo { num_gprs: 0, num_instrs: 0, num_static_cycles: 0, + num_spills_to_mem: 0, + num_fills_from_mem: 0, + num_spills_to_reg: 0, + num_fills_from_reg: 0, num_control_barriers: 0, slm_size: nir.scratch_size, max_crs_depth: 0, diff --git a/src/nouveau/compiler/nak/hw_tests.rs b/src/nouveau/compiler/nak/hw_tests.rs index 1955ffc6390..34bab288ca7 100644 --- a/src/nouveau/compiler/nak/hw_tests.rs +++ b/src/nouveau/compiler/nak/hw_tests.rs @@ -203,6 +203,10 @@ impl<'a> TestShaderBuilder<'a> { num_control_barriers: 0, num_instrs: 0, num_static_cycles: 0, + num_spills_to_mem: 0, + num_fills_from_mem: 0, + num_spills_to_reg: 0, + num_fills_from_reg: 0, slm_size: 0, max_crs_depth: 0, uses_global_mem: true, diff --git a/src/nouveau/compiler/nak/ir.rs b/src/nouveau/compiler/nak/ir.rs index 5e97a11ad1b..e19dca3c4a2 100644 --- a/src/nouveau/compiler/nak/ir.rs +++ b/src/nouveau/compiler/nak/ir.rs @@ -7458,6 +7458,10 @@ pub struct ShaderInfo { pub num_control_barriers: u8, pub num_instrs: u32, pub num_static_cycles: u32, + pub num_spills_to_mem: u32, + pub num_fills_from_mem: u32, + pub num_spills_to_reg: u32, + pub num_fills_from_reg: u32, pub slm_size: u32, pub max_crs_depth: u32, pub uses_global_mem: bool, diff --git a/src/nouveau/compiler/nak/spill_values.rs b/src/nouveau/compiler/nak/spill_values.rs index 0c8629c714c..9245d42a6ae 100644 --- a/src/nouveau/compiler/nak/spill_values.rs +++ b/src/nouveau/compiler/nak/spill_values.rs @@ -85,21 +85,24 @@ trait Spill { fn fill(&mut self, dst: Dst, src: SSAValue) -> Box; } -struct SpillUniform {} +struct SpillUniform<'a> { + info: &'a mut ShaderInfo, +} -impl SpillUniform { - fn new() -> Self { - Self {} +impl<'a> SpillUniform<'a> { + fn new(info: &'a mut ShaderInfo) -> Self { + Self { info } } } -impl Spill for SpillUniform { +impl Spill for SpillUniform<'_> { fn spill_file(&self, file: RegFile) -> RegFile { debug_assert!(file.is_uniform()); file.to_warp() } fn spill(&mut self, dst: SSAValue, src: Src) -> Box { + self.info.num_spills_to_reg += 1; Instr::new_boxed(OpCopy { dst: dst.into(), src: src, @@ -107,6 +110,7 @@ impl Spill for SpillUniform { } fn fill(&mut self, dst: Dst, src: SSAValue) -> Box { + self.info.num_fills_from_reg += 1; Instr::new_boxed(OpR2UR { dst: dst, src: src.into(), @@ -114,15 +118,17 @@ impl Spill for SpillUniform { } } -struct SpillPred {} +struct SpillPred<'a> { + info: &'a mut ShaderInfo, +} -impl SpillPred { - fn new() -> Self { - Self {} +impl<'a> SpillPred<'a> { + fn new(info: &'a mut ShaderInfo) -> Self { + Self { info } } } -impl Spill for SpillPred { +impl Spill for SpillPred<'_> { fn spill_file(&self, file: RegFile) -> RegFile { match file { RegFile::Pred => RegFile::GPR, @@ -133,6 +139,7 @@ impl Spill for SpillPred { fn spill(&mut self, dst: SSAValue, src: Src) -> Box { assert!(matches!(dst.file(), RegFile::GPR | RegFile::UGPR)); + self.info.num_spills_to_reg += 1; if let Some(b) = src.as_bool() { let u32_src = if b { Src::new_imm_u32(!0) @@ -154,6 +161,7 @@ impl Spill for SpillPred { fn fill(&mut self, dst: Dst, src: SSAValue) -> Box { assert!(matches!(src.file(), RegFile::GPR | RegFile::UGPR)); + self.info.num_fills_from_reg += 1; Instr::new_boxed(OpISetP { dst: dst, set_op: PredSetOp::And, @@ -167,15 +175,17 @@ impl Spill for SpillPred { } } -struct SpillBar {} +struct SpillBar<'a> { + info: &'a mut ShaderInfo, +} -impl SpillBar { - fn new() -> Self { - Self {} +impl<'a> SpillBar<'a> { + fn new(info: &'a mut ShaderInfo) -> Self { + Self { info } } } -impl Spill for SpillBar { +impl Spill for SpillBar<'_> { fn spill_file(&self, file: RegFile) -> RegFile { assert!(file == RegFile::Bar); RegFile::GPR @@ -183,6 +193,7 @@ impl Spill for SpillBar { fn spill(&mut self, dst: SSAValue, src: Src) -> Box { assert!(dst.file() == RegFile::GPR); + self.info.num_spills_to_reg += 1; Instr::new_boxed(OpBMov { dst: dst.into(), src: src, @@ -192,6 +203,7 @@ impl Spill for SpillBar { fn fill(&mut self, dst: Dst, src: SSAValue) -> Box { assert!(src.file() == RegFile::GPR); + self.info.num_fills_from_reg += 1; Instr::new_boxed(OpBMov { dst: dst, src: src.into(), @@ -200,15 +212,17 @@ impl Spill for SpillBar { } } -struct SpillGPR {} +struct SpillGPR<'a> { + info: &'a mut ShaderInfo, +} -impl SpillGPR { - fn new() -> Self { - Self {} +impl<'a> SpillGPR<'a> { + fn new(info: &'a mut ShaderInfo) -> Self { + Self { info } } } -impl Spill for SpillGPR { +impl Spill for SpillGPR<'_> { fn spill_file(&self, file: RegFile) -> RegFile { assert!(file == RegFile::GPR); RegFile::Mem @@ -216,6 +230,7 @@ impl Spill for SpillGPR { fn spill(&mut self, dst: SSAValue, src: Src) -> Box { assert!(dst.file() == RegFile::Mem); + self.info.num_spills_to_mem += 1; Instr::new_boxed(OpCopy { dst: dst.into(), src: src, @@ -224,6 +239,7 @@ impl Spill for SpillGPR { fn fill(&mut self, dst: Dst, src: SSAValue) -> Box { assert!(src.file() == RegFile::Mem); + self.info.num_fills_from_mem += 1; Instr::new_boxed(OpCopy { dst: dst, src: src.into(), @@ -1023,26 +1039,31 @@ impl Function { /// just for the sake of a parallel copy. While this may not be true in /// general, especially not when spilling to memory, the register allocator /// is good at eliding unnecessary copies. - pub fn spill_values(&mut self, file: RegFile, limit: u32) { + pub fn spill_values( + &mut self, + file: RegFile, + limit: u32, + info: &mut ShaderInfo, + ) { match file { RegFile::GPR => { - let spill = SpillGPR::new(); + let spill = SpillGPR::new(info); spill_values(self, file, limit, spill); } RegFile::UGPR => { - let spill = SpillUniform::new(); + let spill = SpillUniform::new(info); spill_values(self, file, limit, spill); } RegFile::Pred => { - let spill = SpillPred::new(); + let spill = SpillPred::new(info); spill_values(self, file, limit, spill); } RegFile::UPred => { - let spill = SpillPred::new(); + let spill = SpillPred::new(info); spill_values(self, file, limit, spill); } RegFile::Bar => { - let spill = SpillBar::new(); + let spill = SpillBar::new(info); spill_values(self, file, limit, spill); } _ => panic!("Don't know how to spill {} registers", file), diff --git a/src/nouveau/vulkan/nvk_shader.c b/src/nouveau/vulkan/nvk_shader.c index 9e2ab32fa4a..94763698fbf 100644 --- a/src/nouveau/vulkan/nvk_shader.c +++ b/src/nouveau/vulkan/nvk_shader.c @@ -1263,6 +1263,36 @@ nvk_shader_get_executable_statistics( stat->value.u64 = shader->info.max_warps_per_sm; } + vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) { + WRITE_STR(stat->name, "Spills to memory"); + WRITE_STR(stat->description, "Number of spills from GPRs to memory"); + stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR; + stat->value.u64 = shader->info.num_spills_to_mem; + } + + vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) { + WRITE_STR(stat->name, "Fills from memory"); + WRITE_STR(stat->description, "Number of fills from memory to GPRs"); + stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR; + stat->value.u64 = shader->info.num_spills_to_mem; + } + + vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) { + WRITE_STR(stat->name, "Spills to reg"); + WRITE_STR(stat->description, + "Number of spills between different register files"); + stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR; + stat->value.u64 = shader->info.num_spills_to_reg; + } + + vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) { + WRITE_STR(stat->name, "Fills from reg"); + WRITE_STR(stat->description, + "Number of fills between different register files"); + stat->format = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR; + stat->value.u64 = shader->info.num_fills_from_reg; + } + vk_outarray_append_typed(VkPipelineExecutableStatisticKHR, &out, stat) { WRITE_STR(stat->name, "Code Size"); WRITE_STR(stat->description,