From 3de388f6522fd51d4c51144a586716231bf6c141 Mon Sep 17 00:00:00 2001 From: Faith Ekstrand Date: Wed, 6 May 2026 17:34:30 -0400 Subject: [PATCH] kraid: Add Src/Dst data types Part-of: --- src/panfrost/compiler/kraid/ir.rs | 351 +++++++++++++++++++++++++++++ src/panfrost/compiler/kraid/lib.rs | 1 + 2 files changed, 352 insertions(+) create mode 100644 src/panfrost/compiler/kraid/ir.rs diff --git a/src/panfrost/compiler/kraid/ir.rs b/src/panfrost/compiler/kraid/ir.rs new file mode 100644 index 00000000000..f1f7abe8a72 --- /dev/null +++ b/src/panfrost/compiler/kraid/ir.rs @@ -0,0 +1,351 @@ +// Copyright © 2026 Collabora, Ltd. +// SPDX-License-Identifier: MIT + +pub use crate::data_type::DataType; +pub use crate::ssa_value::{SSARef, SSAValue}; +pub use crate::swizzle::Swizzle; + +use std::fmt; + +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub enum FAUPage { + /// The user FAU table. This assumes a single, flat table, unlike the + /// hardware which splits the user FAU into 4 pages. In Kraid, the page + /// split for the user FAU is handled by the back-end as a per-generation + /// constraint, not represented in the IR. + User, + + /// FAU special page 0 + Special0, + + /// FAU special page 3 + Special1, + + /// FAU special page 3 + Special3, + + /// The small constant table + SmallConst, +} + +#[derive(Clone, Copy)] +pub struct FAURef { + pub page: FAUPage, + + /// The FAU index, in units of 32-bit words. The hardware uses 64-bit + /// words and has a word select bit in the source encoding. In this + /// representation, the bottom bit is the word select and the upper 15 bits + /// are the 64-bit FAU index. For 64-bit access, the bottom bit must be + /// zero. + pub idx: u16, + + /// Load 64 bytes + pub load64: bool, +} + +impl fmt::Display for FAURef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.page == FAUPage::SmallConst { + debug_assert!(!self.load64); + return write!(f, "k{}", self.idx); + } + + let idx = self.idx >> 1; + let w = self.idx % 1; + + match self.page { + FAUPage::User => write!(f, "u{idx}")?, + FAUPage::Special0 => write!(f, "s0:{idx}")?, + FAUPage::Special1 => write!(f, "s1:{idx}")?, + FAUPage::Special3 => write!(f, "s3:{idx}")?, + FAUPage::SmallConst => panic!("Already handled"), + } + + if self.load64 { + debug_assert_eq!(w, 0); + } else { + write!(f, ".w{w}")?; + } + Ok(()) + } +} + +/// This struct describes the range of registers read or written by a RegRef. +/// The range provided here acts as a mask on the destination but is purely +/// informational for sources. In all cases, the instruction operates relative +/// to the register itself. If a source needs to read from a value in the top +/// half of a register, it is swizzled accordingly. For 16-bit destinations, +/// the instruction itself continues to operate 32 bits wide and the register +/// write is simply masked. +#[derive(Clone, Copy)] +pub enum RegRange { + Half0, + Half1, + Regs(u8), +} + +#[derive(Clone, Copy)] +pub struct RegRef { + pub idx: u8, + pub range: RegRange, +} + +impl fmt::Display for RegRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.range { + RegRange::Half0 => write!(f, "r{}.h0", self.idx), + RegRange::Half1 => write!(f, "r{}.h1", self.idx), + RegRange::Regs(n) => { + write!(f, "r{}", self.idx)?; + if *n > 1 { + write!(f, "..{}", self.idx + n)?; + } + Ok(()) + } + } + } +} + +impl RegRef { + fn bytes(&self) -> u8 { + match self.range { + RegRange::Half0 | RegRange::Half1 => 2, + RegRange::Regs(n) => n * 4, + } + } +} + +#[derive(Clone)] +pub enum SrcRef { + /// A 32-bit immediate + Imm32(u32), + FAU(FAURef), + SSA(SSARef), + Reg(RegRef), +} + +impl fmt::Display for SrcRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SrcRef::Imm32(u) => write!(f, "{u:#x}"), + SrcRef::FAU(fau) => fau.fmt(f), + SrcRef::SSA(ssa) => ssa.fmt(f), + SrcRef::Reg(reg) => reg.fmt(f), + } + } +} + +impl SrcRef { + /// Returns the number of bytes read + pub fn bytes_read(&self) -> u8 { + match self { + SrcRef::Imm32(_) => 4, + SrcRef::FAU(fau) => { + if fau.load64 { + 8 + } else { + 4 + } + } + SrcRef::SSA(vec) => vec.bytes(), + SrcRef::Reg(reg) => reg.bytes(), + } + } + + pub fn is_small_const(&self) -> bool { + matches!( + self, + SrcRef::FAU(FAURef { + page: FAUPage::SmallConst, + .. + }) + ) + } +} + +impl From for SrcRef { + fn from(u: u32) -> SrcRef { + SrcRef::Imm32(u) + } +} + +impl From for SrcRef { + fn from(fau: FAURef) -> SrcRef { + SrcRef::FAU(fau) + } +} + +impl> From for SrcRef { + fn from(ssa: T) -> SrcRef { + SrcRef::SSA(ssa.into()) + } +} + +impl From for SrcRef { + fn from(reg: RegRef) -> SrcRef { + SrcRef::Reg(reg) + } +} + +#[repr(u8)] +#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)] +pub enum SrcMod { + #[default] + None = 0, + FAbs = 1, + FNeg = 2, + FNegAbs = 3, + BNot = 4, +} + +impl fmt::Display for SrcMod { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SrcMod::None => Ok(()), + SrcMod::FAbs => write!(f, ".fabs"), + SrcMod::FNeg => write!(f, ".fneg"), + SrcMod::FNegAbs => write!(f, ".fabs.fneg"), + SrcMod::BNot => write!(f, ".bnot"), + } + } +} + +impl SrcMod { + pub fn bnot(self) -> SrcMod { + use SrcMod::*; + match self { + None => BNot, + FAbs | FNeg | FNegAbs => { + panic!("Cannot compose float and bitwise modifiers"); + } + BNot => None, + } + } + + pub fn fabs(self) -> SrcMod { + use SrcMod::*; + match self { + None | FAbs | FNeg | FNegAbs => FAbs, + BNot => panic!("Cannot compose float and bitwise modifiers"), + } + } + + pub fn fneg(self) -> SrcMod { + use SrcMod::*; + match self { + None => FNeg, + FAbs => FNegAbs, + FNeg => None, + FNegAbs => FAbs, + BNot => panic!("Cannot compose float and bitwise modifiers"), + } + } + + pub fn modify(self, other: SrcMod) -> SrcMod { + use SrcMod::*; + match other { + None => self, + FAbs => self.fabs(), + FNeg => self.fneg(), + FNegAbs => self.fabs().fneg(), + BNot => self.bnot(), + } + } +} + +#[derive(Clone)] +pub struct Src { + pub src_ref: SrcRef, + pub swizzle: Swizzle, + pub src_mod: SrcMod, + pub last_use: bool, +} + +impl fmt::Display for Src { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let lu = if self.last_use { "^" } else { "" }; + write!(f, "{}{lu}{}{}", self.src_ref, self.swizzle, self.src_mod) + } +} + +impl Src { + pub fn swizzle(mut self, swizzle: Swizzle) -> Src { + self.swizzle = self.swizzle.swizzle(swizzle).unwrap(); + self + } + + pub fn byte(self, byte: u8) -> Src { + self.swizzle(Swizzle::replicate_byte(byte)) + } + + pub fn half(self, half: u8) -> Src { + self.swizzle(Swizzle::replicate_half(half)) + } + + pub fn modify(mut self, src_mod: SrcMod) -> Src { + self.src_mod = self.src_mod.modify(src_mod); + self + } + + pub fn bnot(self) -> Src { + self.modify(SrcMod::BNot) + } + + pub fn fabs(self) -> Src { + self.modify(SrcMod::FAbs) + } + + pub fn fneg(self) -> Src { + self.modify(SrcMod::FNeg) + } +} + +impl> From for Src { + fn from(src_ref: T) -> Src { + Src { + src_ref: src_ref.into(), + swizzle: Default::default(), + src_mod: Default::default(), + last_use: false, + } + } +} + +#[derive(Clone)] +pub enum Dst { + None, + SSA(SSARef), + Reg(RegRef), +} + +impl fmt::Display for Dst { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Dst::None => write!(f, "null"), + Dst::SSA(ssa) => ssa.fmt(f), + Dst::Reg(reg) => reg.fmt(f), + } + } +} + +impl Dst { + pub fn bytes_written(&self) -> u8 { + match self { + Dst::None => 0, + Dst::SSA(vec) => vec.bytes(), + Dst::Reg(reg) => reg.bytes(), + } + } +} + +impl> From for Dst { + fn from(ssa: T) -> Dst { + Dst::SSA(ssa.into()) + } +} + +impl From for Dst { + fn from(reg: RegRef) -> Dst { + Dst::Reg(reg) + } +} diff --git a/src/panfrost/compiler/kraid/lib.rs b/src/panfrost/compiler/kraid/lib.rs index 1a0f009e60a..19dfacee2b2 100644 --- a/src/panfrost/compiler/kraid/lib.rs +++ b/src/panfrost/compiler/kraid/lib.rs @@ -3,6 +3,7 @@ mod compile; mod data_type; +mod ir; mod model; mod ssa_value; mod swizzle;