mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-05 11:48:06 +02:00
nak: Move SSAValue and friends to a new ssa_value.rs file
This is getting complicated and depends on some inter-linked details for safety such as values being in-range. It's safer if the rest of the IR is forced to use public interfaces. Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/34794>
This commit is contained in:
parent
9d1c38ddf1
commit
56bdf9043b
4 changed files with 353 additions and 351 deletions
|
|
@ -10,16 +10,15 @@ use nak_bindings::*;
|
|||
pub use crate::builder::{Builder, InstrBuilder, SSABuilder, SSAInstrBuilder};
|
||||
use crate::legalize::LegalizeBuilder;
|
||||
use crate::sph::{OutputTopology, PixelImap};
|
||||
pub use crate::ssa_value::*;
|
||||
use compiler::as_slice::*;
|
||||
use compiler::cfg::CFG;
|
||||
use compiler::smallvec::SmallVec;
|
||||
use nak_ir_proc::*;
|
||||
use std::array;
|
||||
use std::cmp::{max, min};
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
use std::iter::Zip;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::{BitAnd, BitOr, Deref, DerefMut, Index, IndexMut, Not, Range};
|
||||
use std::slice;
|
||||
|
||||
|
|
@ -150,7 +149,7 @@ impl RegFile {
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_prefix(&self) -> &'static str {
|
||||
pub fn fmt_prefix(&self) -> &'static str {
|
||||
match self {
|
||||
RegFile::GPR => "r",
|
||||
RegFile::UGPR => "ur",
|
||||
|
|
@ -353,336 +352,6 @@ impl<T> IndexMut<RegFile> for PerRegFile<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// An SSA value
|
||||
///
|
||||
/// Each SSA in NAK represents a single 32-bit or 1-bit (if a predicate) value
|
||||
/// which must either be spilled to memory or allocated space in the specified
|
||||
/// register file. Whenever more data is required such as a 64-bit memory
|
||||
/// address, double-precision float, or a vec4 texture result, multiple SSA
|
||||
/// values are used.
|
||||
///
|
||||
/// Each SSA value logically contains two things: an index and a register file.
|
||||
/// It is required that each index refers to a unique SSA value, regardless of
|
||||
/// register file. This way the index can be used to index tightly-packed data
|
||||
/// structures such as bitsets without having to determine separate ranges for
|
||||
/// each register file.
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub struct SSAValue {
|
||||
packed: NonZeroU32,
|
||||
}
|
||||
|
||||
impl SSAValue {
|
||||
/// Returns an SSA value with the given register file and index
|
||||
pub fn new(file: RegFile, idx: u32) -> SSAValue {
|
||||
assert!(
|
||||
idx > 0
|
||||
&& idx < (1 << 29) - u32::try_from(SSARef::LARGE_SIZE).unwrap()
|
||||
);
|
||||
let mut packed = idx;
|
||||
assert!(u8::from(file) < 8);
|
||||
packed |= u32::from(u8::from(file)) << 29;
|
||||
SSAValue {
|
||||
packed: packed.try_into().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the index of this SSA value
|
||||
pub fn idx(&self) -> u32 {
|
||||
self.packed.get() & 0x1fffffff
|
||||
}
|
||||
|
||||
fn fmt_plain(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}{}", self.file().fmt_prefix(), self.idx())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasRegFile for SSAValue {
|
||||
/// Returns the register file of this SSA value
|
||||
fn file(&self) -> RegFile {
|
||||
RegFile::try_from(self.packed.get() >> 29).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SSAValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "%")?;
|
||||
self.fmt_plain(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
struct SSAValueArray<const SIZE: usize> {
|
||||
v: [SSAValue; SIZE],
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> SSAValueArray<SIZE> {
|
||||
/// Returns a new SSA reference
|
||||
#[inline]
|
||||
fn new(comps: &[SSAValue]) -> Self {
|
||||
assert!(comps.len() > 0 && comps.len() <= SIZE);
|
||||
let mut r = Self {
|
||||
v: [SSAValue {
|
||||
packed: NonZeroU32::MAX,
|
||||
}; SIZE],
|
||||
};
|
||||
for i in 0..comps.len() {
|
||||
r.v[i] = comps[i];
|
||||
}
|
||||
if comps.len() < SIZE {
|
||||
r.v[SIZE - 1].packed =
|
||||
(comps.len() as u32).wrapping_neg().try_into().unwrap();
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
fn comps(&self) -> u8 {
|
||||
let size: u8 = SIZE.try_into().unwrap();
|
||||
if self.v[SIZE - 1].packed.get() >= u32::MAX - (u32::from(size) - 1) {
|
||||
self.v[SIZE - 1].packed.get().wrapping_neg() as u8
|
||||
} else {
|
||||
size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Deref for SSAValueArray<SIZE> {
|
||||
type Target = [SSAValue];
|
||||
|
||||
fn deref(&self) -> &[SSAValue] {
|
||||
let comps = usize::from(self.comps());
|
||||
&self.v[..comps]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> DerefMut for SSAValueArray<SIZE> {
|
||||
fn deref_mut(&mut self) -> &mut [SSAValue] {
|
||||
let comps = usize::from(self.comps());
|
||||
&mut self.v[..comps]
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to one or more SSA values
|
||||
///
|
||||
/// Because each SSA value represents a single 1 or 32-bit scalar, we need a way
|
||||
/// to reference multiple SSA values for instructions which read or write
|
||||
/// multiple registers in the same source. When the register allocator runs,
|
||||
/// all the SSA values in a given SSA ref will be placed in consecutive
|
||||
/// registers, with the base register aligned to the number of values, aligned
|
||||
/// to the next power of two.
|
||||
///
|
||||
/// An SSA reference can reference between 1 and 16 SSA values. It dereferences
|
||||
/// to a slice for easy access to individual SSA values. The structure is
|
||||
/// designed so that is always 16B, regardless of how many SSA values are
|
||||
/// referenced so it's easy and fairly cheap to clone and embed in other
|
||||
/// structures.
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
enum SSARefInner {
|
||||
Small(SSAValueArray<{ SSARef::SMALL_SIZE }>),
|
||||
Large(Box<SSAValueArray<{ SSARef::LARGE_SIZE }>>),
|
||||
}
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
pub struct SSARef {
|
||||
v: SSARefInner,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const _: () = {
|
||||
debug_assert!(std::mem::size_of::<SSARef>() == 16);
|
||||
};
|
||||
|
||||
impl SSARef {
|
||||
const SMALL_SIZE: usize = 4;
|
||||
const LARGE_SIZE: usize = 16;
|
||||
|
||||
/// Returns a new SSA reference
|
||||
#[inline]
|
||||
pub fn new(comps: &[SSAValue]) -> SSARef {
|
||||
SSARef {
|
||||
v: if comps.len() > Self::SMALL_SIZE {
|
||||
Self::cold();
|
||||
SSARefInner::Large(Box::new(SSAValueArray::new(comps)))
|
||||
} else {
|
||||
SSARefInner::Small(SSAValueArray::new(comps))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_iter(mut it: impl ExactSizeIterator<Item = SSAValue>) -> Self {
|
||||
let len = it.len();
|
||||
assert!(len > 0 && len <= Self::LARGE_SIZE);
|
||||
let v: [SSAValue; Self::LARGE_SIZE] = array::from_fn(|_| {
|
||||
it.next().unwrap_or(SSAValue {
|
||||
packed: NonZeroU32::MAX,
|
||||
})
|
||||
});
|
||||
Self::new(&v[..len])
|
||||
}
|
||||
|
||||
/// Returns the number of components in this SSA reference
|
||||
pub fn comps(&self) -> u8 {
|
||||
match &self.v {
|
||||
SSARefInner::Small(x) => x.comps(),
|
||||
SSARefInner::Large(x) => {
|
||||
Self::cold();
|
||||
x.comps()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file(&self) -> Option<RegFile> {
|
||||
let comps = usize::from(self.comps());
|
||||
let file = self[0].file();
|
||||
for i in 1..comps {
|
||||
if self[i].file() != file {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(file)
|
||||
}
|
||||
|
||||
pub fn is_uniform(&self) -> bool {
|
||||
for ssa in &self[..] {
|
||||
if !ssa.is_uniform() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn is_gpr(&self) -> bool {
|
||||
for ssa in &self[..] {
|
||||
if !ssa.is_gpr() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn is_predicate(&self) -> bool {
|
||||
if self[0].is_predicate() {
|
||||
true
|
||||
} else {
|
||||
for ssa in &self[..] {
|
||||
debug_assert!(!ssa.is_predicate());
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline]
|
||||
fn cold() {}
|
||||
}
|
||||
|
||||
impl Deref for SSARef {
|
||||
type Target = [SSAValue];
|
||||
|
||||
fn deref(&self) -> &[SSAValue] {
|
||||
match &self.v {
|
||||
SSARefInner::Small(x) => x.deref(),
|
||||
SSARefInner::Large(x) => {
|
||||
Self::cold();
|
||||
x.deref()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for SSARef {
|
||||
fn deref_mut(&mut self) -> &mut [SSAValue] {
|
||||
match &mut self.v {
|
||||
SSARefInner::Small(x) => x.deref_mut(),
|
||||
SSARefInner::Large(x) => {
|
||||
Self::cold();
|
||||
x.deref_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[SSAValue]> for SSARef {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(comps: &[SSAValue]) -> Result<Self, Self::Error> {
|
||||
if comps.len() == 0 {
|
||||
Err("Empty vector")
|
||||
} else if comps.len() > Self::LARGE_SIZE {
|
||||
Err("Too many vector components")
|
||||
} else {
|
||||
Ok(SSARef::new(comps))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<SSAValue>> for SSARef {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(comps: Vec<SSAValue>) -> Result<Self, Self::Error> {
|
||||
SSARef::try_from(&comps[..])
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_ssa_ref_from_arr {
|
||||
($n: expr) => {
|
||||
impl From<[SSAValue; $n]> for SSARef {
|
||||
fn from(comps: [SSAValue; $n]) -> Self {
|
||||
SSARef::new(&comps[..])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl_ssa_ref_from_arr!(1);
|
||||
impl_ssa_ref_from_arr!(2);
|
||||
impl_ssa_ref_from_arr!(3);
|
||||
impl_ssa_ref_from_arr!(4);
|
||||
|
||||
impl From<SSAValue> for SSARef {
|
||||
fn from(val: SSAValue) -> Self {
|
||||
[val].into()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SSARef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.comps() == 1 {
|
||||
write!(f, "{}", self[0])
|
||||
} else {
|
||||
write!(f, "{{")?;
|
||||
for (i, v) in self.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
write!(f, "{}", v)?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SSAValueAllocator {
|
||||
count: u32,
|
||||
}
|
||||
|
||||
impl SSAValueAllocator {
|
||||
pub fn new() -> SSAValueAllocator {
|
||||
SSAValueAllocator { count: 0 }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn max_idx(&self) -> u32 {
|
||||
self.count
|
||||
}
|
||||
|
||||
pub fn alloc(&mut self, file: RegFile) -> SSAValue {
|
||||
self.count += 1;
|
||||
SSAValue::new(file, self.count)
|
||||
}
|
||||
|
||||
pub fn alloc_vec(&mut self, file: RegFile, comps: u8) -> SSARef {
|
||||
SSARef::from_iter((0..comps).map(|_| self.alloc(file)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub struct RegRef {
|
||||
packed: u32,
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright © 2025 Valve Corporation
|
||||
// SPDX-License-Identifier: MIT
|
||||
use crate::ir::*;
|
||||
|
||||
#[test]
|
||||
fn test_ssa_ref_round_trip() {
|
||||
for len in 1..16 {
|
||||
let vec: Vec<_> = (0..len)
|
||||
.map(|i| SSAValue::new(RegFile::GPR, 1337 ^ i ^ len))
|
||||
.collect();
|
||||
|
||||
let ssa_ref = SSARef::new(&vec);
|
||||
assert!(&ssa_ref[..] == &vec[..]);
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ mod sm75_instr_latencies;
|
|||
mod sm80_instr_latencies;
|
||||
mod sph;
|
||||
mod spill_values;
|
||||
mod ssa_value;
|
||||
mod to_cssa;
|
||||
mod union_find;
|
||||
|
||||
|
|
@ -44,8 +45,5 @@ mod hw_tests;
|
|||
#[cfg(test)]
|
||||
mod hw_runner;
|
||||
|
||||
#[cfg(test)]
|
||||
mod ir_tests;
|
||||
|
||||
#[cfg(test)]
|
||||
mod nvdisasm_tests;
|
||||
|
|
|
|||
350
src/nouveau/compiler/nak/ssa_value.rs
Normal file
350
src/nouveau/compiler/nak/ssa_value.rs
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
// Copyright © 2025 Collabora, Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::ir::{HasRegFile, RegFile};
|
||||
use std::array;
|
||||
use std::fmt;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// An SSA value
|
||||
///
|
||||
/// Each SSA in NAK represents a single 32-bit or 1-bit (if a predicate) value
|
||||
/// which must either be spilled to memory or allocated space in the specified
|
||||
/// register file. Whenever more data is required such as a 64-bit memory
|
||||
/// address, double-precision float, or a vec4 texture result, multiple SSA
|
||||
/// values are used.
|
||||
///
|
||||
/// Each SSA value logically contains two things: an index and a register file.
|
||||
/// It is required that each index refers to a unique SSA value, regardless of
|
||||
/// register file. This way the index can be used to index tightly-packed data
|
||||
/// structures such as bitsets without having to determine separate ranges for
|
||||
/// each register file.
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub struct SSAValue {
|
||||
packed: NonZeroU32,
|
||||
}
|
||||
|
||||
impl SSAValue {
|
||||
/// Returns an SSA value with the given register file and index
|
||||
fn new(file: RegFile, idx: u32) -> SSAValue {
|
||||
assert!(
|
||||
idx > 0
|
||||
&& idx < (1 << 29) - u32::try_from(SSARef::LARGE_SIZE).unwrap()
|
||||
);
|
||||
let mut packed = idx;
|
||||
assert!(u8::from(file) < 8);
|
||||
packed |= u32::from(u8::from(file)) << 29;
|
||||
SSAValue {
|
||||
packed: packed.try_into().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the index of this SSA value
|
||||
pub fn idx(&self) -> u32 {
|
||||
self.packed.get() & 0x1fffffff
|
||||
}
|
||||
|
||||
pub fn fmt_plain(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}{}", self.file().fmt_prefix(), self.idx())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasRegFile for SSAValue {
|
||||
/// Returns the register file of this SSA value
|
||||
fn file(&self) -> RegFile {
|
||||
RegFile::try_from(self.packed.get() >> 29).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SSAValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "%")?;
|
||||
self.fmt_plain(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
struct SSAValueArray<const SIZE: usize> {
|
||||
v: [SSAValue; SIZE],
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> SSAValueArray<SIZE> {
|
||||
/// Returns a new SSA reference
|
||||
#[inline]
|
||||
fn new(comps: &[SSAValue]) -> Self {
|
||||
assert!(comps.len() > 0 && comps.len() <= SIZE);
|
||||
let mut r = Self {
|
||||
v: [SSAValue {
|
||||
packed: NonZeroU32::MAX,
|
||||
}; SIZE],
|
||||
};
|
||||
for i in 0..comps.len() {
|
||||
r.v[i] = comps[i];
|
||||
}
|
||||
if comps.len() < SIZE {
|
||||
r.v[SIZE - 1].packed =
|
||||
(comps.len() as u32).wrapping_neg().try_into().unwrap();
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
fn comps(&self) -> u8 {
|
||||
let size: u8 = SIZE.try_into().unwrap();
|
||||
if self.v[SIZE - 1].packed.get() >= u32::MAX - (u32::from(size) - 1) {
|
||||
self.v[SIZE - 1].packed.get().wrapping_neg() as u8
|
||||
} else {
|
||||
size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> Deref for SSAValueArray<SIZE> {
|
||||
type Target = [SSAValue];
|
||||
|
||||
fn deref(&self) -> &[SSAValue] {
|
||||
let comps = usize::from(self.comps());
|
||||
&self.v[..comps]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> DerefMut for SSAValueArray<SIZE> {
|
||||
fn deref_mut(&mut self) -> &mut [SSAValue] {
|
||||
let comps = usize::from(self.comps());
|
||||
&mut self.v[..comps]
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to one or more SSA values
|
||||
///
|
||||
/// Because each SSA value represents a single 1 or 32-bit scalar, we need a way
|
||||
/// to reference multiple SSA values for instructions which read or write
|
||||
/// multiple registers in the same source. When the register allocator runs,
|
||||
/// all the SSA values in a given SSA ref will be placed in consecutive
|
||||
/// registers, with the base register aligned to the number of values, aligned
|
||||
/// to the next power of two.
|
||||
///
|
||||
/// An SSA reference can reference between 1 and 16 SSA values. It dereferences
|
||||
/// to a slice for easy access to individual SSA values. The structure is
|
||||
/// designed so that is always 16B, regardless of how many SSA values are
|
||||
/// referenced so it's easy and fairly cheap to clone and embed in other
|
||||
/// structures.
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
enum SSARefInner {
|
||||
Small(SSAValueArray<{ SSARef::SMALL_SIZE }>),
|
||||
Large(Box<SSAValueArray<{ SSARef::LARGE_SIZE }>>),
|
||||
}
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
pub struct SSARef {
|
||||
v: SSARefInner,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const _: () = {
|
||||
debug_assert!(std::mem::size_of::<SSARef>() == 16);
|
||||
};
|
||||
|
||||
impl SSARef {
|
||||
const SMALL_SIZE: usize = 4;
|
||||
const LARGE_SIZE: usize = 16;
|
||||
|
||||
/// Returns a new SSA reference
|
||||
#[inline]
|
||||
pub fn new(comps: &[SSAValue]) -> SSARef {
|
||||
SSARef {
|
||||
v: if comps.len() > Self::SMALL_SIZE {
|
||||
Self::cold();
|
||||
SSARefInner::Large(Box::new(SSAValueArray::new(comps)))
|
||||
} else {
|
||||
SSARefInner::Small(SSAValueArray::new(comps))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn from_iter(mut it: impl ExactSizeIterator<Item = SSAValue>) -> Self {
|
||||
let len = it.len();
|
||||
assert!(len > 0 && len <= Self::LARGE_SIZE);
|
||||
let v: [SSAValue; Self::LARGE_SIZE] = array::from_fn(|_| {
|
||||
it.next().unwrap_or(SSAValue {
|
||||
packed: NonZeroU32::MAX,
|
||||
})
|
||||
});
|
||||
Self::new(&v[..len])
|
||||
}
|
||||
|
||||
/// Returns the number of components in this SSA reference
|
||||
pub fn comps(&self) -> u8 {
|
||||
match &self.v {
|
||||
SSARefInner::Small(x) => x.comps(),
|
||||
SSARefInner::Large(x) => {
|
||||
Self::cold();
|
||||
x.comps()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file(&self) -> Option<RegFile> {
|
||||
let comps = usize::from(self.comps());
|
||||
let file = self[0].file();
|
||||
for i in 1..comps {
|
||||
if self[i].file() != file {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(file)
|
||||
}
|
||||
|
||||
pub fn is_uniform(&self) -> bool {
|
||||
for ssa in &self[..] {
|
||||
if !ssa.is_uniform() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn is_gpr(&self) -> bool {
|
||||
for ssa in &self[..] {
|
||||
if !ssa.is_gpr() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn is_predicate(&self) -> bool {
|
||||
if self[0].is_predicate() {
|
||||
true
|
||||
} else {
|
||||
for ssa in &self[..] {
|
||||
debug_assert!(!ssa.is_predicate());
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline]
|
||||
fn cold() {}
|
||||
}
|
||||
|
||||
impl Deref for SSARef {
|
||||
type Target = [SSAValue];
|
||||
|
||||
fn deref(&self) -> &[SSAValue] {
|
||||
match &self.v {
|
||||
SSARefInner::Small(x) => x.deref(),
|
||||
SSARefInner::Large(x) => {
|
||||
Self::cold();
|
||||
x.deref()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for SSARef {
|
||||
fn deref_mut(&mut self) -> &mut [SSAValue] {
|
||||
match &mut self.v {
|
||||
SSARefInner::Small(x) => x.deref_mut(),
|
||||
SSARefInner::Large(x) => {
|
||||
Self::cold();
|
||||
x.deref_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[SSAValue]> for SSARef {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(comps: &[SSAValue]) -> Result<Self, Self::Error> {
|
||||
if comps.len() == 0 {
|
||||
Err("Empty vector")
|
||||
} else if comps.len() > Self::LARGE_SIZE {
|
||||
Err("Too many vector components")
|
||||
} else {
|
||||
Ok(SSARef::new(comps))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<SSAValue>> for SSARef {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(comps: Vec<SSAValue>) -> Result<Self, Self::Error> {
|
||||
SSARef::try_from(&comps[..])
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_ssa_ref_from_arr {
|
||||
($n: expr) => {
|
||||
impl From<[SSAValue; $n]> for SSARef {
|
||||
fn from(comps: [SSAValue; $n]) -> Self {
|
||||
SSARef::new(&comps[..])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl_ssa_ref_from_arr!(1);
|
||||
impl_ssa_ref_from_arr!(2);
|
||||
impl_ssa_ref_from_arr!(3);
|
||||
impl_ssa_ref_from_arr!(4);
|
||||
|
||||
impl From<SSAValue> for SSARef {
|
||||
fn from(val: SSAValue) -> Self {
|
||||
[val].into()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SSARef {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.comps() == 1 {
|
||||
write!(f, "{}", self[0])
|
||||
} else {
|
||||
write!(f, "{{")?;
|
||||
for (i, v) in self.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
write!(f, "{}", v)?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ssa_ref_round_trip() {
|
||||
for len in 1..16 {
|
||||
let vec: Vec<_> = (0..len)
|
||||
.map(|i| SSAValue::new(RegFile::GPR, 1337 ^ i ^ len))
|
||||
.collect();
|
||||
|
||||
let ssa_ref = SSARef::new(&vec);
|
||||
assert!(&ssa_ref[..] == &vec[..]);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SSAValueAllocator {
|
||||
count: u32,
|
||||
}
|
||||
|
||||
impl SSAValueAllocator {
|
||||
pub fn new() -> SSAValueAllocator {
|
||||
SSAValueAllocator { count: 0 }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn max_idx(&self) -> u32 {
|
||||
self.count
|
||||
}
|
||||
|
||||
pub fn alloc(&mut self, file: RegFile) -> SSAValue {
|
||||
self.count += 1;
|
||||
SSAValue::new(file, self.count)
|
||||
}
|
||||
|
||||
pub fn alloc_vec(&mut self, file: RegFile, comps: u8) -> SSARef {
|
||||
SSARef::from_iter((0..comps).map(|_| self.alloc(file)))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue