pub(in crate::passes) mod conversions;
use super::*;
mod images;
use images::*;
mod dispatch_texture;
pub(in crate::passes) use dispatch_texture::*;
mod integer_simd;
pub(in crate::passes) use integer_simd::*;
mod float_imageblock;
pub(in crate::passes) use float_imageblock::*;
mod rawbyte_unary;
pub(in crate::passes) use rawbyte_unary::*;
mod matrix_shuffle;
pub(in crate::passes) use matrix_shuffle::*;
mod reduce_bitops;
pub(in crate::passes) use reduce_bitops::*;
mod bfloat_glsl;
pub(in crate::passes) use bfloat_glsl::*;
mod agx_emask;
pub(in crate::passes) use agx_emask::*;
fn clamp_edges(ctx: &mut Ctx, rty: Word, zero: Word, one: Word) -> (Word, Word) {
let defs = type_defs(&ctx.module);
let is_vec = ctx
.new_globals
.iter()
.chain(ctx.module.types_global_values.iter())
.find(|g| g.result_id == Some(rty))
.map(|g| g.class.opcode == Op::TypeVector)
.or_else(|| defs.get(&rty).map(|g| g.class.opcode == Op::TypeVector))
.unwrap_or(false);
if !is_vec {
return (zero, one);
}
let n = ctx
.new_globals
.iter()
.chain(ctx.module.types_global_values.iter())
.find(|g| g.result_id == Some(rty))
.and_then(|g| g.operands.get(1).cloned())
.or_else(|| defs.get(&rty).and_then(|g| g.operands.get(1).cloned()))
.and_then(|o| match o {
Operand::LiteralBit32(n) => Some(n),
_ => None,
})
.unwrap_or(4);
let lo = splat(ctx, rty, zero, n);
let hi = splat(ctx, rty, one, n);
(lo, hi)
}
fn vector_len(ctx: &Ctx, ty: Word) -> u32 {
let find = |id: Word| {
ctx.new_globals
.iter()
.chain(ctx.module.types_global_values.iter())
.find(|g| g.result_id == Some(id))
.cloned()
};
if let Some(def) = find(ty) {
if def.class.opcode == Op::TypeVector {
if let Some(Operand::LiteralBit32(n)) = def.operands.get(1) {
return *n;
}
}
}
1
}
fn splat_or_scalar(ctx: &mut Ctx, rty: Word, v: f32, n: u32) -> Word {
let elem = element_type(ctx, rty);
let s = if is_half_scalar(ctx, elem) {
ctx.const_half(v)
} else {
ctx.const_float(v)
};
if n <= 1 {
s
} else {
splat(ctx, rty, s, n)
}
}
fn splat(ctx: &mut Ctx, vty: Word, scalar: Word, n: u32) -> Word {
let id = ctx.module.fresh_id();
let mut ops = vec![];
for _ in 0..n {
ops.push(Operand::IdRef(scalar));
}
ctx.new_globals.push(Instruction::new(
Op::ConstantComposite,
Some(vty),
Some(id),
ops,
));
id
}
#[cfg(test)]
mod tests {
use super::*;
use spirv::GroupOperation;
#[test]
fn zero_count_steps_cover_all_supported_widths() {
assert_eq!(
trailing_zero_steps(32),
vec![(0xffff, 16), (0xff, 8), (0xf, 4), (0x3, 2), (0x1, 1)]
);
assert_eq!(
trailing_zero_steps(16),
vec![(0xff, 8), (0xf, 4), (0x3, 2), (0x1, 1)]
);
assert_eq!(trailing_zero_steps(8), vec![(0xf, 4), (0x3, 2), (0x1, 1)]);
assert_eq!(
leading_zero_steps(32),
vec![
(0xffff_0000, 16),
(0xff00_0000, 8),
(0xf000_0000, 4),
(0xc000_0000, 2),
(0x8000_0000, 1),
]
);
assert_eq!(
leading_zero_steps(16),
vec![(0xff00, 8), (0xf000, 4), (0xc000, 2), (0x8000, 1)]
);
assert_eq!(
leading_zero_steps(64)[0],
(0xffff_ffff_0000_0000u64 as i64, 32)
);
}
#[test]
fn glsl_extinst_does_not_match_explicit_memory_order() {
assert!(glsl_extinst("air.atomic_fetch_max_explicit_texture_2d.i16.u.v4i32").is_none());
assert!(matches!(
glsl_extinst("air.fast_exp.v4f32"),
Some(GLSLstd450::Exp)
));
assert!(matches!(
glsl_extinst("air.fast_powr.v4f32"),
Some(GLSLstd450::Pow)
));
}
fn const_uint_value(ctx: &Ctx, id: Word) -> Option<u32> {
ctx.new_globals
.iter()
.chain(ctx.module.types_global_values.iter())
.find(|g| g.result_id == Some(id) && g.class.opcode == Op::Constant)
.and_then(|g| match g.operands.first() {
Some(Operand::LiteralBit32(v)) => Some(*v),
_ => None,
})
}
#[test]
fn group_reduce_operands_clusters_reduce_when_enabled() {
let mut ctx = Ctx::new(Module::new());
let scope = ctx.const_uint(Scope::Subgroup as u32);
let value = ctx.const_uint(7);
let ops = group_reduce_operands(&mut ctx, scope, GroupOperation::Reduce, value, true);
assert_eq!(
ops.len(),
4,
"clustered reduce carries a cluster-size operand"
);
assert_eq!(ops[0], Operand::IdScope(scope));
assert_eq!(
ops[1],
Operand::GroupOperation(GroupOperation::ClusteredReduce)
);
assert_eq!(ops[2], Operand::IdRef(value));
let Operand::IdRef(cluster) = ops[3] else {
panic!("cluster-size operand is an IdRef");
};
assert_eq!(
const_uint_value(&ctx, cluster),
Some(32),
"cluster size is 32 lanes"
);
}
#[test]
fn group_reduce_operands_plain_reduce_when_disabled() {
let mut ctx = Ctx::new(Module::new());
let scope = ctx.const_uint(Scope::Subgroup as u32);
let value = ctx.const_uint(7);
let ops = group_reduce_operands(&mut ctx, scope, GroupOperation::Reduce, value, false);
assert_eq!(
ops,
vec![
Operand::IdScope(scope),
Operand::GroupOperation(GroupOperation::Reduce),
Operand::IdRef(value),
]
);
}
#[test]
fn group_reduce_operands_never_clusters_scan() {
let mut ctx = Ctx::new(Module::new());
let scope = ctx.const_uint(Scope::Subgroup as u32);
let value = ctx.const_uint(7);
let ops =
group_reduce_operands(&mut ctx, scope, GroupOperation::InclusiveScan, value, true);
assert_eq!(ops.len(), 3, "a scan is not turned into a clustered reduce");
assert_eq!(
ops[1],
Operand::GroupOperation(GroupOperation::InclusiveScan)
);
}
}