kraid: Add Src/Dst data types

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41841>
This commit is contained in:
Faith Ekstrand 2026-05-06 17:34:30 -04:00 committed by Marge Bot
parent eaead919d5
commit 3de388f652
2 changed files with 352 additions and 0 deletions

View file

@ -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<u32> for SrcRef {
fn from(u: u32) -> SrcRef {
SrcRef::Imm32(u)
}
}
impl From<FAURef> for SrcRef {
fn from(fau: FAURef) -> SrcRef {
SrcRef::FAU(fau)
}
}
impl<T: Into<SSARef>> From<T> for SrcRef {
fn from(ssa: T) -> SrcRef {
SrcRef::SSA(ssa.into())
}
}
impl From<RegRef> 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<T: Into<SrcRef>> From<T> 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<T: Into<SSARef>> From<T> for Dst {
fn from(ssa: T) -> Dst {
Dst::SSA(ssa.into())
}
}
impl From<RegRef> for Dst {
fn from(reg: RegRef) -> Dst {
Dst::Reg(reg)
}
}

View file

@ -3,6 +3,7 @@
mod compile;
mod data_type;
mod ir;
mod model;
mod ssa_value;
mod swizzle;