nak: Add spill/fill statistics

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33773>
This commit is contained in:
Mel Henning 2025-01-15 16:40:10 -05:00 committed by Marge Bot
parent 0480d8294c
commit 2a0302967f
8 changed files with 111 additions and 28 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -85,21 +85,24 @@ trait Spill {
fn fill(&mut self, dst: Dst, src: SSAValue) -> Box<Instr>;
}
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<Instr> {
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<Instr> {
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<Instr> {
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<Instr> {
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<Instr> {
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<Instr> {
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<Instr> {
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<Instr> {
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),

View file

@ -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,