use generic_array::{GenericArray, typenum};
use super::{spirv_swap_bytes_u32, spirv_swap_bytes_u64};
use crate::backend::spirv::arch::{self as arch, glsl};
use crate::{
isa::InstructionSet,
math::policy::{Policy, PrecisionPolicy},
register::{
BitwiseRegister, CoreRegister, Element, FloatElement, FloatRegister, IndexableRegister, InterleaveRegister,
MaskElement, NativeCapability, NumericRegister, PartialOrdRegister, PermuteRegister, Register, ShuffleRegister,
SignedRegister, Storage, UnsignedIntegerRegister, ZeroUpper,
},
vector::ops::MulAddExt,
};
#[rustfmt::skip]
macro_rules! decl_spirv_float_scalar { ($f:ty => $width:literal) => { paste::paste! {
#[thermite_macros::inline_always]
impl CoreRegister for [<f $width>] {
type NativeIsa = crate::backend::scalar::Scalar;
type Lanes = typenum::U1;
type Storage = [<f $width>];
type Mask = bool;
const IS_EMULATED: bool = false;
const EMPTY: Storage<Self> = 0.0;
const HAS_EQUAL_SIZE_MASK: bool = false;
fn blendv(mask: bool, lhs: Self, rhs: Self) -> Self {
unsafe { arch::op_opselect::<Self, bool>(mask, rhs, lhs) }
}
fn z (mask: bool, value: Self) -> Self { unsafe { arch::op_opselect::<Self, bool>(mask, value, 0.0) } }
fn nz(mask: bool, value: Self) -> Self { unsafe { arch::op_opselect::<Self, bool>(mask, 0.0, value) } }
fn zeroupper_z<Z: ZeroUpper>(value: Self) -> Self {
if const { Z::N >= 1 } { value } else { Self::EMPTY }
}
fn from_mask(mask: bool) -> Self { Self::from_bool(mask) }
}
#[thermite_macros::inline_always]
impl BitwiseRegister for [<f $width>] {
fn bitxor(lhs: Self, rhs: Self) -> Self { <$f>::from_bits(lhs.to_bits() ^ rhs.to_bits()) }
fn bitand(lhs: Self, rhs: Self) -> Self { <$f>::from_bits(lhs.to_bits() & rhs.to_bits()) }
fn bitor (lhs: Self, rhs: Self) -> Self { <$f>::from_bits(lhs.to_bits() | rhs.to_bits()) }
fn not(value: Self) -> Self { <$f>::from_bits(!value.to_bits()) }
}
#[rustfmt::skip]
#[thermite_macros::inline_always]
impl InterleaveRegister for [<f $width>] {
fn interleave (a: Self, b: Self) -> (Self, Self) { (a, b) }
fn deinterleave(a: Self, b: Self) -> (Self, Self) { (a, b) }
}
#[thermite_macros::inline_always]
impl Register for [<f $width>] {
type Element = [<f $width>];
type Signed = [<i $width>];
type Unsigned = [<u $width>];
fn into_mask(value: Self) -> bool { value.to_bool() }
fn msb_to_mask(value: Self) -> bool {
Self::into_mask(Self::bitand(value, Self::NEG_ZERO))
}
fn new(value: GenericArray<Self::Element, Self::Lanes>) -> Self { value[0] }
fn single(value: Self::Element) -> Self { value }
fn splat (value: Self::Element) -> Self { value }
fn broadcast<const I: usize>(value: Self) -> Self { value }
fn reverse(value: Self) -> Self { value }
fn swap_bytes(value: Self) -> Self {
<$f>::from_bits([<spirv_swap_bytes_u $width>](value.to_bits()))
}
const HAS_PERMUTEV: bool = false;
fn permutev(value: Self, _idxs: GenericArray<u32, Self::Lanes>) -> Self { value }
fn swizzle(a: Self, b: Self, idxs: GenericArray<u32, Self::Lanes>) -> Self {
if idxs[0] & 0b1 == 0 { a } else { b }
}
}
#[thermite_macros::inline_always]
impl<I: UnsignedIntegerRegister<Lanes = Self::Lanes>> IndexableRegister<I> for [<f $width>] {}
#[thermite_macros::inline_always]
impl ShuffleRegister for [<f $width>] {
fn shuffle<const IMM8: i32>(lhs: Self, rhs: Self) -> Self {
if IMM8 & 0b01 == 0 { lhs } else { rhs }
}
}
#[thermite_macros::inline_always]
impl PermuteRegister for [<f $width>] {
fn permute<const IMM8: i32>(value: Self) -> Self { value }
}
#[rustfmt::skip]
#[thermite_macros::inline_always]
impl PartialOrdRegister for [<f $width>] {
fn gt(lhs: Self, rhs: Self) -> bool { lhs > rhs }
fn eq(lhs: Self, rhs: Self) -> bool { lhs == rhs }
fn ge(lhs: Self, rhs: Self) -> bool { lhs >= rhs }
fn lt(lhs: Self, rhs: Self) -> bool { lhs < rhs }
fn le(lhs: Self, rhs: Self) -> bool { lhs <= rhs }
fn ne(lhs: Self, rhs: Self) -> bool { lhs != rhs }
}
#[thermite_macros::inline_always]
impl NumericRegister for [<f $width>] {
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
const TWO: Self = 2.0;
const MIN: Self = <$f>::MIN;
const MAX: Self = <$f>::MAX;
fn min_element (value: Self) -> Self::Element { value }
fn max_element (value: Self) -> Self::Element { value }
fn sum_elements(value: Self) -> Self::Element { value }
fn prod_elements(value: Self) -> Self::Element { value }
fn pairwise_sum(lo: Self, hi: Self) -> Self { lo + hi }
fn offset() -> Self { 1.0 }
fn indexed() -> Self { 0.0 }
fn add(lhs: Self, rhs: Self) -> Self { lhs + rhs }
fn sub(lhs: Self, rhs: Self) -> Self { lhs - rhs }
fn mul(lhs: Self, rhs: Self) -> Self { lhs * rhs }
fn div(lhs: Self, rhs: Self) -> Self { lhs / rhs }
fn rem(lhs: Self, rhs: Self) -> Self { lhs % rhs }
fn sort(value: Self) -> Self { value }
fn min(lhs: Self, rhs: Self) -> Self { unsafe { arch::glsl_op2::<Self, Self, Self, {glsl::F_MIN}, false>(lhs, rhs) } }
fn max(lhs: Self, rhs: Self) -> Self { unsafe { arch::glsl_op2::<Self, Self, Self, {glsl::F_MAX}, false>(lhs, rhs) } }
}
#[thermite_macros::inline_always]
impl SignedRegister for [<f $width>] {
const NEG_ONE: Self = -1.0;
const MIN_POSITIVE: Self = <$f>::MIN_POSITIVE;
fn neg(value: Self) -> Self { unsafe { arch::op_opfnegate::<Self>(value) } }
fn abs(value: Self) -> Self {
unsafe { arch::glsl_op1::<Self, Self, { glsl::F_ABS }, false>(value) }
}
fn signum(value: Self) -> Self { value.signum() }
fn copysign(lhs: Self, rhs: Self) -> Self { lhs.copysign(rhs) }
fn neg_c(mask: bool, value: Self) -> Self {
unsafe { arch::op_opselect::<Self, bool>(mask, Self::neg(value), value) }
}
fn is_negative(value: Self) -> bool {
unsafe { cfg_select! {
target_feature = "Kernel" => {
arch::op_opsignbitset::<bool, Self>(value)
}
_ => arch::op_opfordlessthan::<bool, Self>(value, 0.0),
} }
}
}
#[thermite_macros::inline_always]
impl FloatRegister for [<f $width>] {
type Bits = [<u $width>];
type SignedBits = [<i $width>];
type ExtendedPrecision = f64;
const HAS_TRUE_FMA: bool = true;
const HALF: Self = 0.5;
const NEG_ZERO: Self = -0.0;
const INFINITY: Self = <$f>::INFINITY;
const NEG_INFINITY: Self = <$f>::NEG_INFINITY;
const NAN: Self = <$f>::NAN;
const EPSILON: Self = <$f>::EPSILON;
const EXP_MASK: Storage<Self::Bits> = <$f>::INFINITY.to_bits();
const HAS_APPROX_RSQRT: bool = true;
const HAS_APPROX_RCP: bool = false;
const NATIVE_CAP: NativeCapability = NativeCapability(
NativeCapability::LDEXP
| NativeCapability::FREXP
| NativeCapability::SIN
| NativeCapability::COS
| NativeCapability::TAN
| NativeCapability::EXP2
| NativeCapability::LOG2
| NativeCapability::EXP
| NativeCapability::LN
| NativeCapability::POWF,
);
fn mul_add (lhs: Self, rhs: Self, acc: Self) -> Self {
unsafe { arch::glsl_op3::<Self, Self, Self, Self, { glsl::FMA }, false>(lhs, rhs, acc) }
}
fn nmul_add(lhs: Self, rhs: Self, acc: Self) -> Self {
unsafe { arch::glsl_op3::<Self, Self, Self, Self, { glsl::FMA }, false>(Self::neg(lhs), rhs, acc) }
}
fn mul_sub (lhs: Self, rhs: Self, acc: Self) -> Self {
unsafe { arch::glsl_op3::<Self, Self, Self, Self, { glsl::FMA }, false>(lhs, rhs, Self::neg(acc)) }
}
fn nmul_sub(lhs: Self, rhs: Self, acc: Self) -> Self {
unsafe { arch::glsl_op3::<Self, Self, Self, Self, { glsl::FMA }, false>(Self::neg(lhs), rhs, Self::neg(acc)) }
}
fn sqrt (value: Self) -> Self {
unsafe { arch::glsl_op1::<Self, Self, { glsl::SQRT }, false>(value) }
}
fn rsqrt(value: Self) -> Self {
unsafe { arch::glsl_op1::<Self, Self, { glsl::INVERSE_SQRT }, false>(value) }
}
fn floor(value: Self) -> Self {
unsafe { arch::glsl_op1::<Self, Self, { glsl::FLOOR }, false>(value) }
}
fn ceil (value: Self) -> Self {
unsafe { arch::glsl_op1::<Self, Self, { glsl::CEIL }, false>(value) }
}
fn round(value: Self) -> Self {
unsafe { arch::glsl_op1::<Self, Self, { glsl::ROUND }, false>(value) }
}
fn trunc(value: Self) -> Self {
unsafe { arch::glsl_op1::<Self, Self, { glsl::TRUNC }, false>(value) }
}
fn fract(value: Self) -> Self {
unsafe { arch::glsl_op1::<Self, Self, { glsl::FRACT }, false>(value) }
}
fn next_up (value: Self) -> Self { FloatElement::next_up (value) }
fn next_down(value: Self) -> Self { FloatElement::next_down(value) }
fn mix(a: Self, b: Self, t: Self) -> Self {
unsafe { arch::glsl_op3::<Self, Self, Self, Self, { glsl::F_MIX }, false>(a, b, t) }
}
fn is_nan (value: Self) -> bool { unsafe { arch::op_opisnan ::<bool, Self>(value) } }
fn is_infinite(value: Self) -> bool { unsafe { arch::op_opisinf ::<bool, Self>(value) } }
fn is_finite (value: Self) -> bool { unsafe { arch::op_opisfinite ::<bool, Self>(value) } }
fn is_normal (value: Self) -> bool { unsafe { arch::op_opisnormal ::<bool, Self>(value) } }
unsafe fn native_ldexp(value: Self, exp: [<i $width>]) -> Self {
unsafe { arch::glsl_op2::<Self, Self, [<i $width>], { glsl::LDEXP }, false>(value, exp) }
}
unsafe fn native_frexp(value: Self) -> (Self, [<i $width>]) {
unsafe { arch::glsl_frexp::<Self, [<i $width>]>(value) }
}
unsafe fn native_sin<P: Policy>(value: Self) -> Self {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
unsafe { arch::glsl_op1::<Self, Self, { glsl::SIN }, true>(value) }
} else {
unsafe { arch::glsl_op1::<Self, Self, { glsl::SIN }, false>(value) }
}
}
unsafe fn native_cos<P: Policy>(value: Self) -> Self {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
unsafe { arch::glsl_op1::<Self, Self, { glsl::COS }, true>(value) }
} else {
unsafe { arch::glsl_op1::<Self, Self, { glsl::COS }, false>(value) }
}
}
unsafe fn native_sin_cos<P: Policy>(value: Self) -> (Self, Self) {
unsafe { (Self::native_sin::<P>(value), Self::native_cos::<P>(value)) }
}
unsafe fn native_tan<P: Policy>(value: Self) -> Self {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
unsafe { arch::glsl_op1::<Self, Self, { glsl::TAN }, true>(value) }
} else {
unsafe { arch::glsl_op1::<Self, Self, { glsl::TAN }, false>(value) }
}
}
unsafe fn native_exp2<P: Policy>(value: Self) -> Self {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
unsafe { arch::glsl_op1::<Self, Self, { glsl::EXP2 }, true>(value) }
} else {
unsafe { arch::glsl_op1::<Self, Self, { glsl::EXP2 }, false>(value) }
}
}
unsafe fn native_log2<P: Policy>(value: Self) -> Self {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
unsafe { arch::glsl_op1::<Self, Self, { glsl::LOG2 }, true>(value) }
} else {
unsafe { arch::glsl_op1::<Self, Self, { glsl::LOG2 }, false>(value) }
}
}
unsafe fn native_exp<P: Policy>(value: Self) -> Self {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
unsafe { arch::glsl_op1::<Self, Self, { glsl::EXP }, true>(value) }
} else {
unsafe { arch::glsl_op1::<Self, Self, { glsl::EXP }, false>(value) }
}
}
unsafe fn native_ln<P: Policy>(value: Self) -> Self {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
unsafe { arch::glsl_op1::<Self, Self, { glsl::LOG }, true>(value) }
} else {
unsafe { arch::glsl_op1::<Self, Self, { glsl::LOG }, false>(value) }
}
}
unsafe fn native_powf<P: Policy>(base: Self, exp: Self) -> Self {
if const { P::POLICY.precision.lt(PrecisionPolicy::Average) } {
unsafe { arch::glsl_op2::<Self, Self, Self, { glsl::POW }, true>(base, exp) }
} else {
unsafe { arch::glsl_op2::<Self, Self, Self, { glsl::POW }, false>(base, exp) }
}
}
unsafe fn block_autovectorization(_value: &mut Self) {}
}
}}}
decl_spirv_float_scalar!(f32 => 32);
decl_spirv_float_scalar!(f64 => 64);