use std::marker::PhantomData;
use furiosa_mapping::*;
use furiosa_opt_lower::{ReduceLabelInput, config_reduce_label};
use furiosa_opt_macro::primitive;
use super::VeTensorShape;
use super::VectorFinalTensor;
use crate::backend::Backend;
use crate::context::*;
use crate::engine::vector::alu::RngdAlu;
use crate::engine::vector::branch::{
BinaryBranchedOperand, ExecutionId, RfPort, TagGuard, TagMode, TernaryBranchedOperand, VeOperandLayout,
apply_branch_config,
};
use crate::engine::vector::layer::{FpToFxp, FxpToFp, Reinterpret};
use crate::engine::vector::op::semantics::HasConversionOp;
use crate::engine::vector::op::semantics::{HasBinaryOp, HasTernaryOp, HasUnaryOp};
use crate::engine::vector::op::{
BinaryArgMode, ClipBinaryOpF32, ClipBinaryOpI32, FpBinaryOp, FpDivBinaryOp, FpTernaryOp, FxpBinaryOp, HasAlu,
InterSliceReduceOpF32, InterSliceReduceOpI32, IntraSliceReduceOpF32, IntraSliceReduceOpI32, LogicBinaryOpF32,
LogicBinaryOpI32, TernaryArgMode,
};
use crate::runtime::CurrentBackend;
use crate::tensor::*;
use crate::engine::vector::operand::{IntoBranchedOperand, IntoGuardedUnaryOp, IntoTernaryOperand, StashTransition};
use crate::engine::vector::scalar::VeScalar;
use crate::engine::vector::stage::markers as stage;
use crate::engine::vector::stage::markers::CanTransitionTo;
use crate::engine::vector::stage::markers::VeOrder;
use crate::engine::vector::stage::markers::Way::{self, Way4, Way8};
use crate::engine::vector::stage::state::VeState;
use crate::engine::vector::stash_slot::{Fresh, Occupied, StashState};
use crate::engine::vector::tensor::verify::{
verify_vector_intra_slice_unzip, verify_vector_narrow_split, verify_vector_narrow_trim, verify_vector_widen_concat,
verify_vector_widen_pad,
};
use super::vector_tensor_pair::VectorTensorPair;
#[derive(Debug)]
pub struct VectorInitTensor<'l, const T: Tu, D: VeScalar, Chip: M, Cluster: M, Slice: M, Time: M, Packet: M> {
pub(crate) ctx: &'l mut TuContext<{ T }>,
pub(crate) inner: Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
}
impl<'l, const T: Tu, D: VeScalar, Chip: M, Cluster: M, Slice: M, Time: M, Packet: M>
VectorInitTensor<'l, T, D, Chip, Cluster, Slice, Time, Packet>
{
pub fn new(
ctx: &'l mut TuContext<{ T }>,
inner: Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
) -> Self {
Self { ctx, inner }
}
}
#[derive(Debug)]
pub struct VeTensorData<
S: stage::Stage,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
const VE_ORDER: VeOrder,
FS: stage::VeTensorContext = stage::Standalone,
const W: Way = { Way8 },
> {
pub(crate) inner: Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
pub(crate) tag: Tensor<u8, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
pub(crate) ve_state: VeState<Stash>,
pub(crate) _stage: PhantomData<S>,
pub(crate) _filter_state: PhantomData<FS>,
}
#[derive(Debug)]
pub struct VectorTensor<
'l,
const T: Tu,
S: stage::Stage,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
const VE_ORDER: VeOrder,
FS: stage::VeTensorContext = stage::Standalone,
const W: Way = { Way8 },
> {
pub(crate) ctx: &'l mut TuContext<{ T }>,
pub(crate) data: VeTensorData<S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>,
}
impl<
S: stage::Stage,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const W: Way,
const VE_ORDER: VeOrder,
> VeTensorData<S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>
{
pub fn ve_state_mut(&mut self) -> &mut VeState<Stash> {
&mut self.ve_state
}
pub fn ve_state(&self) -> &VeState<Stash> {
&self.ve_state
}
pub fn inner(&self) -> &Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>> {
&self.inner
}
pub fn tag(&self) -> &Tensor<u8, VeTensorShape<Chip, Cluster, Slice, Time, Packet>> {
&self.tag
}
pub fn into_parts(
self,
) -> (
Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
Tensor<u8, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
VeState<Stash>,
) {
(self.inner, self.tag, self.ve_state)
}
pub(crate) fn apply_binary<NextStage: stage::Stage, NextFS: stage::VeTensorContext>(
mut self,
alu: RngdAlu,
op_fn: impl Fn(D, D) -> D + Sync,
operand: &BinaryBranchedOperand<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
stash_data: Option<Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>>,
) -> VeTensorData<NextStage, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, NextFS, W> {
self.ve_state.use_alu(alu);
let result = apply_binary_op(&self.inner, &self.tag, op_fn, operand, stash_data.as_ref());
VeTensorData {
inner: result,
tag: self.tag,
ve_state: self.ve_state,
_stage: PhantomData,
_filter_state: PhantomData,
}
}
}
impl<
S: stage::Stage,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const W: Way,
const VE_ORDER: VeOrder,
> VeTensorData<S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>
{
pub(crate) fn apply_stash_transition<Op: StashTransition<Stash, D, W>>(
self,
) -> VeTensorData<S, D, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER, FS, W> {
VeTensorData {
inner: self.inner,
tag: self.tag,
ve_state: Op::transition(self.ve_state),
_stage: PhantomData,
_filter_state: PhantomData,
}
}
}
impl<
'l,
const T: Tu,
S: stage::Stage,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const W: Way,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>
{
pub fn into_parts(
self,
) -> (
&'l mut TuContext<{ T }>,
Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
Tensor<u8, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
VeState<Stash>,
) {
let (inner, tag, ve_state) = self.data.into_parts();
(self.ctx, inner, tag, ve_state)
}
pub fn into_ctx_and_data(
self,
) -> (
&'l mut TuContext<{ T }>,
VeTensorData<S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>,
) {
(self.ctx, self.data)
}
pub fn ve_state_mut(&mut self) -> &mut VeState<Stash> {
self.data.ve_state_mut()
}
pub fn ve_state(&self) -> &VeState<Stash> {
self.data.ve_state()
}
pub fn inner(&self) -> &Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>> {
self.data.inner()
}
pub fn tag(&self) -> &Tensor<u8, VeTensorShape<Chip, Cluster, Slice, Time, Packet>> {
self.data.tag()
}
pub fn data(&self) -> &VeTensorData<S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W> {
&self.data
}
pub fn data_mut(&mut self) -> &mut VeTensorData<S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W> {
&mut self.data
}
pub fn from_parts(
ctx: &'l mut TuContext<{ T }>,
inner: Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
tag: Tensor<u8, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
ve_state: VeState<Stash>,
) -> Self {
Self {
ctx,
data: VeTensorData {
inner,
tag,
ve_state,
_stage: PhantomData,
_filter_state: PhantomData,
},
}
}
pub fn from_ctx_and_data(
ctx: &'l mut TuContext<{ T }>,
data: VeTensorData<S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>,
) -> Self {
Self { ctx, data }
}
pub(crate) fn do_binary<NextStage: stage::Stage, NextFS: stage::VeTensorContext, Op>(
self,
op: impl HasAlu + HasBinaryOp<D>,
mode: Option<BinaryArgMode>,
operand: Op,
) -> VectorTensor<'l, T, NextStage, D, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER, NextFS, W>
where
Op: IntoBranchedOperand<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>> + StashTransition<Stash, D, W>,
{
let stash_data = Op::stashed(self.ve_state());
let operand = operand.into_branched_operand();
let data = self
.data
.apply_binary(op.alu(), op.binary_op_fn(mode), &operand, stash_data)
.apply_stash_transition::<Op>();
VectorTensor { ctx: self.ctx, data }
}
}
impl<
'l,
const T: Tu,
S: stage::Stashable,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
const W: Way,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Fresh, VE_ORDER, stage::Standalone, W>
{
#[primitive(VectorTensor::vector_stash)]
pub fn vector_stash(
self,
) -> VectorTensor<
'l,
T,
S,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Occupied<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>, W>,
VE_ORDER,
stage::Standalone,
W,
> {
let new_ve_state = self.data.ve_state.write_stash(&self.data.inner);
VectorTensor {
ctx: self.ctx,
data: VeTensorData {
inner: self.data.inner,
tag: self.data.tag,
ve_state: new_ve_state,
_stage: PhantomData,
_filter_state: PhantomData,
},
}
}
}
impl<
'l,
const T: Tu,
S: stage::Stage,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::Commitable,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
{
#[primitive(VectorTensor::vector_final)]
pub fn vector_final(self) -> VectorFinalTensor<'l, T, D, Chip, Cluster, Slice, Time, Packet> {
VectorFinalTensor::new(self.ctx, self.data.inner)
}
}
impl<
'l,
const T: Tu,
S: stage::IntraSliceStage + stage::CanTransitionTo<stage::InterSliceReduce>,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::Commitable,
> VectorTensor<'l, T, S, i32, Chip, Cluster, Slice, Time, Packet, Stash, { VeOrder::IntraFirst }, FS, { Way8 }>
{
#[primitive(VectorTensor::vector_inter_slice_reduce)]
pub fn vector_inter_slice_reduce<OutSlice: M, OutTime: M>(
self,
op: InterSliceReduceOpI32,
) -> VectorInterSliceReduceTensor<'l, T, i32, Chip, Cluster, OutSlice, OutTime, Packet, { VeOrder::IntraFirst }>
{
let reduced = self.data.inner.reduce(op.reduce_fn(), op.identity(), true);
create_inter_slice_reduce_tensor(self.ctx, reduced)
}
}
impl<
'l,
const T: Tu,
S: stage::IntraSliceStage + stage::CanTransitionTo<stage::InterSliceReduce>,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::Commitable,
> VectorTensor<'l, T, S, f32, Chip, Cluster, Slice, Time, Packet, Stash, { VeOrder::IntraFirst }, FS, { Way8 }>
{
#[primitive(VectorTensor::vector_inter_slice_reduce)]
pub fn vector_inter_slice_reduce<OutSlice: M, OutTime: M>(
self,
op: InterSliceReduceOpF32,
) -> VectorInterSliceReduceTensor<'l, T, f32, Chip, Cluster, OutSlice, OutTime, Packet, { VeOrder::IntraFirst }>
{
let reduced = self.data.inner.reduce(op.reduce_fn(), op.identity(), true);
create_inter_slice_reduce_tensor(self.ctx, reduced)
}
}
pub(crate) fn create_inter_slice_reduce_tensor<
'l,
const T: Tu,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
const VE_ORDER: VeOrder,
>(
ctx: &'l mut TuContext<{ T }>,
inner: Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
) -> VectorInterSliceReduceTensor<'l, T, D, Chip, Cluster, Slice, Time, Packet, VE_ORDER> {
VectorTensor {
ctx,
data: VeTensorData {
inner,
tag: Tensor::zeroed(),
ve_state: VeState::new(),
_stage: PhantomData,
_filter_state: PhantomData,
},
}
}
impl<'l, const T: Tu, D: VeScalar, Chip: M, Cluster: M, Slice: M, Time: M, Packet: M>
VectorInitTensor<'l, T, D, Chip, Cluster, Slice, Time, Packet>
{
#[primitive(VectorInitTensor::vector_intra_slice_tag)]
pub fn vector_intra_slice_tag(
self,
branch: TagMode<D>,
) -> VectorBranchTensor<'l, T, D, Chip, Cluster, Slice, Time, Packet, Fresh, { VeOrder::IntraFirst }> {
VectorBranchTensor::new(self.ctx, self.inner, branch)
}
#[primitive(VectorInitTensor::vector_intra_slice_unzip)]
pub fn vector_intra_slice_unzip<I: AxisName, TileTime: M, SplitTime: M>(
self,
) -> VectorTensorPair<'l, T, D, stage::Tag, Chip, Cluster, Slice, SplitTime, Packet> {
verify_vector_intra_slice_unzip::<I, Time, Packet>();
VectorTensorPair::new::<I, Time, TileTime>(self.ctx, self.inner)
}
}
impl<'l, const T: Tu, Chip: M, Cluster: M, Slice: M, Time: M, Packet: M>
VectorInitTensor<'l, T, i32, Chip, Cluster, Slice, Time, Packet>
{
#[primitive(VectorInitTensor::vector_inter_slice_reduce)]
pub fn vector_inter_slice_reduce<OutSlice: M, OutTime: M>(
self,
op: InterSliceReduceOpI32,
) -> VectorInterSliceReduceTensor<'l, T, i32, Chip, Cluster, OutSlice, OutTime, Packet, { VeOrder::InterFirst }>
{
let reduced = self.inner.reduce(op.reduce_fn(), op.identity(), true);
create_inter_slice_reduce_tensor(self.ctx, reduced)
}
}
impl<'l, const T: Tu, Chip: M, Cluster: M, Slice: M, Time: M, Packet: M>
VectorInitTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet>
{
#[primitive(VectorInitTensor::vector_inter_slice_reduce)]
pub fn vector_inter_slice_reduce<OutSlice: M, OutTime: M>(
self,
op: InterSliceReduceOpF32,
) -> VectorInterSliceReduceTensor<'l, T, f32, Chip, Cluster, OutSlice, OutTime, Packet, { VeOrder::InterFirst }>
{
let reduced = self.inner.reduce(op.reduce_fn(), op.identity(), true);
create_inter_slice_reduce_tensor(self.ctx, reduced)
}
}
pub type VectorInterSliceReduceTensor<'l, const T: Tu, D, Chip, Cluster, Slice, Time, Packet, const VE_ORDER: VeOrder> =
VectorTensor<
'l,
T,
stage::InterSliceReduce,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Fresh,
VE_ORDER,
stage::Standalone,
{ Way8 },
>;
impl<
'l,
const T: Tu,
S: stage::InterSliceStage + stage::CanTransitionTo<stage::Tag>,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::Commitable,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Stash, { VeOrder::InterFirst }, FS, { Way8 }>
{
#[primitive(VectorTensor::vector_intra_slice_tag)]
pub fn vector_intra_slice_tag(
self,
branch: TagMode<D>,
) -> VectorBranchTensor<'l, T, D, Chip, Cluster, Slice, Time, Packet, Fresh, { VeOrder::InterFirst }> {
VectorBranchTensor::new(self.ctx, self.data.inner, branch)
}
}
pub type VectorBranchTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way8 },
> = VectorTensor<'l, T, stage::Tag, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
impl<'l, const T: Tu, D: VeScalar, Chip: M, Cluster: M, Slice: M, Time: M, Packet: M, const VE_ORDER: VeOrder>
VectorBranchTensor<'l, T, D, Chip, Cluster, Slice, Time, Packet, Fresh, VE_ORDER>
{
pub fn new(
ctx: &'l mut TuContext<{ T }>,
inner: Tensor<D, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
branch_config: TagMode<D>,
) -> Self {
assert_eq!(
Packet::SIZE,
8,
"VectorTensor requires Packet of 8 elements (one flit) in Way8 mode, got {}",
Packet::SIZE,
);
let tag = apply_branch_config(&inner, &branch_config);
Self::from_parts(ctx, inner, tag, VeState::new())
}
}
pub type VectorLogicTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way8 },
> = VectorTensor<'l, T, stage::Logic, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorFxpTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way8 },
> = VectorTensor<'l, T, stage::Fxp, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorFxpToFpTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way8 },
> = VectorTensor<'l, T, stage::FxpToFp, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorNarrowTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way4 },
> = VectorTensor<'l, T, stage::Narrow, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorFpTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way4 },
> = VectorTensor<'l, T, stage::Fp, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorIntraSliceReduceTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way4 },
> = VectorTensor<'l, T, stage::IntraSliceReduce, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorFpDivTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way4 },
> = VectorTensor<'l, T, stage::FpDiv, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorWidenTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way8 },
> = VectorTensor<'l, T, stage::Widen, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorFpToFxpTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way8 },
> = VectorTensor<'l, T, stage::FpToFxp, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorClipTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way8 },
> = VectorTensor<'l, T, stage::Clip, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
pub type VectorFilterTensor<
'l,
const T: Tu,
D,
Chip,
Cluster,
Slice,
Time,
Packet,
Stash,
const VE_ORDER: VeOrder,
FS = stage::Standalone,
const W: Way = { Way8 },
> = VectorTensor<'l, T, stage::Filter, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>;
fn exec_id(raw: u8) -> ExecutionId {
ExecutionId::try_new(raw)
.unwrap_or_else(|| panic!("an execution id is four bits, but the tag tensor holds {raw:#04x}"))
}
fn apply_slots<D: VeScalar, Mapping: M, L: VeOperandLayout<D, Mapping>>(
data: &Tensor<D, Mapping>,
tag: &Tensor<u8, Mapping>,
operand: &L,
stash_data: Option<&Tensor<D, Mapping>>,
combine: impl Fn(D, D, L::Operand1) -> D + Sync,
) -> Tensor<D, Mapping> {
let regs = operand
.regs()
.map(|slot| slot.map(|(guard, operand0, operand1)| (*guard, operand0, operand1)));
let port = operand.port().map(|(guard, port, operand1)| {
let rhs = match port {
RfPort::External(register) => register,
RfPort::Stash => stash_data.expect(
"a stash operand reached the engine without a stashed tensor: use the `Stash` operand, whose \
`StashTransition` supplies it, not a hand-built slot",
),
};
(*guard, rhs, operand1)
});
let port_rhs = port.map_or(data, |(_, rhs, _)| rhs);
let port_slot = port.map(|(guard, _, operand1)| (guard, operand1));
let inner = CurrentBackend::zip3_with(
&data.inner,
&port_rhs.inner,
&tag.inner,
move |cell, port_cell, raw_id| {
let id = exec_id(raw_id);
for (guard, operand0, operand1) in regs.iter().flatten() {
if guard.admits(id) {
return combine(cell, *operand0, *operand1);
}
}
match port_slot {
Some((guard, operand1)) if guard.admits(id) => combine(cell, port_cell, operand1),
_ => cell,
}
},
);
Tensor::from_inner(inner)
}
pub(super) fn apply_binary_op<D: VeScalar, Mapping: M>(
data: &Tensor<D, Mapping>,
tag: &Tensor<u8, Mapping>,
op: impl Fn(D, D) -> D + Sync,
operand: &BinaryBranchedOperand<D, Mapping>,
stash_data: Option<&Tensor<D, Mapping>>,
) -> Tensor<D, Mapping> {
apply_slots(data, tag, operand, stash_data, |mainstream, rhs, ()| {
op(mainstream, rhs)
})
}
pub(super) fn apply_unary_op_where<D: VeScalar, Mapping: M>(
data: &Tensor<D, Mapping>,
tag: &Tensor<u8, Mapping>,
guard: TagGuard,
op: impl Fn(D) -> D + Sync,
) -> Tensor<D, Mapping> {
let inner = CurrentBackend::zip_with(&data.inner, &tag.inner, move |cell, raw_id| {
if guard.admits(exec_id(raw_id)) { op(cell) } else { cell }
});
Tensor::from_inner(inner)
}
pub(super) fn apply_ternary_op<Mapping: M>(
data: &Tensor<f32, Mapping>,
tag: &Tensor<u8, Mapping>,
op: impl Fn(f32, f32, f32) -> f32 + Sync,
operand: &TernaryBranchedOperand<f32, Mapping>,
stash_data: Option<&Tensor<f32, Mapping>>,
) -> Tensor<f32, Mapping> {
apply_slots(data, tag, operand, stash_data, op)
}
impl<
'l,
const T: Tu,
S,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, i32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::Logic>,
{
#[primitive(VectorTensor::vector_logic)]
pub fn vector_logic<Op>(
self,
op: LogicBinaryOpI32,
operand: Op,
) -> VectorLogicTensor<'l, T, i32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<i32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, i32, { Way8 }>,
{
self.do_binary::<stage::Logic, stage::Standalone, Op>(op, None, operand)
}
#[primitive(VectorTensor::vector_logic_with_mode)]
pub fn vector_logic_with_mode<Op>(
self,
op: LogicBinaryOpI32,
mode: BinaryArgMode,
operand: Op,
) -> VectorLogicTensor<'l, T, i32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<i32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, i32, { Way8 }>,
{
self.do_binary::<stage::Logic, stage::Standalone, Op>(op, Some(mode), operand)
}
}
impl<
'l,
const T: Tu,
S,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, f32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::Logic>,
{
#[primitive(VectorTensor::vector_logic)]
pub fn vector_logic<Op>(
self,
op: LogicBinaryOpF32,
operand: Op,
) -> VectorLogicTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way8 }>,
{
self.do_binary::<stage::Logic, stage::Standalone, Op>(op, None, operand)
}
#[primitive(VectorTensor::vector_logic_with_mode)]
pub fn vector_logic_with_mode<Op>(
self,
op: LogicBinaryOpF32,
mode: BinaryArgMode,
operand: Op,
) -> VectorLogicTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way8 }>,
{
self.do_binary::<stage::Logic, stage::Standalone, Op>(op, Some(mode), operand)
}
}
impl<
'l,
const T: Tu,
S,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, i32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::Fxp>,
{
#[primitive(VectorTensor::vector_fxp)]
pub fn vector_fxp<Op>(
self,
op: FxpBinaryOp,
operand: Op,
) -> VectorFxpTensor<'l, T, i32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<i32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, i32, { Way8 }>,
{
self.do_binary::<stage::Fxp, stage::Standalone, Op>(op, None, operand)
}
#[primitive(VectorTensor::vector_fxp_with_mode)]
pub fn vector_fxp_with_mode<Op>(
self,
op: FxpBinaryOp,
mode: BinaryArgMode,
operand: Op,
) -> VectorFxpTensor<'l, T, i32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<i32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, i32, { Way8 }>,
{
self.do_binary::<stage::Fxp, stage::Standalone, Op>(op, Some(mode), operand)
}
}
impl<
'l,
const T: Tu,
S,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, i32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::FxpToFp>,
{
#[primitive(VectorTensor::vector_fxp_to_fp)]
pub fn vector_fxp_to_fp(
self,
int_width: u32,
) -> VectorFxpToFpTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER> {
let op = FxpToFp::new(int_width);
let op_fn = op.op_fn();
let result = self.inner().map(&op_fn);
let (ctx, _inner, tag, ve_state) = self.into_parts();
VectorFxpToFpTensor::from_parts(ctx, result, tag, ve_state)
}
}
impl<
'l,
const T: Tu,
S,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::Narrow>,
{
#[primitive(VectorTensor::vector_narrow_split)]
pub fn vector_narrow_split<Time2: M, Packet2: M>(
self,
) -> VectorNarrowTensor<'l, T, D, Chip, Cluster, Slice, Time2, Packet2, Stash, VE_ORDER, FS, { Way4 }> {
verify_vector_narrow_split::<Time, Packet, Time2, Packet2>();
let (ctx, inner, tag, ve_state) = self.into_parts();
let split_inner = inner.transpose::<VeTensorShape<Chip, Cluster, Slice, Time2, Packet2>>(true);
let split_eid = tag.transpose::<VeTensorShape<Chip, Cluster, Slice, Time2, Packet2>>(true);
VectorNarrowTensor::from_parts(ctx, split_inner, split_eid, ve_state)
}
}
impl<
'l,
const T: Tu,
S,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::Narrow>,
{
#[primitive(VectorTensor::vector_narrow_trim)]
pub fn vector_narrow_trim<Packet2: M>(
self,
) -> VectorNarrowTensor<'l, T, D, Chip, Cluster, Slice, Time, Packet2, Stash, VE_ORDER, FS, { Way4 }> {
verify_vector_narrow_trim::<Packet, Packet2>();
let (ctx, inner, tag, ve_state) = self.into_parts();
let stripped = inner.transpose::<VeTensorShape<Chip, Cluster, Slice, Time, Packet2>>(true);
let stripped_eid = tag.transpose::<VeTensorShape<Chip, Cluster, Slice, Time, Packet2>>(true);
VectorNarrowTensor::from_parts(ctx, stripped, stripped_eid, ve_state)
}
}
impl<
'l,
const T: Tu,
S,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, f32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way4 }>
where
S: stage::Stage + CanTransitionTo<stage::Fp>,
{
#[primitive(VectorTensor::vector_fp_unary)]
pub fn vector_fp_unary<Op: IntoGuardedUnaryOp>(
mut self,
op: Op,
) -> VectorFpTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER> {
let (guard, op) = op.into_guarded_unary_op();
self.ve_state_mut().use_alu(op.alu());
let result = apply_unary_op_where(self.inner(), self.tag(), guard, op.unary_op_fn());
let (ctx, _inner, tag, ve_state) = self.into_parts();
VectorFpTensor::from_parts(ctx, result, tag, ve_state)
}
#[primitive(VectorTensor::vector_fp_binary)]
pub fn vector_fp_binary<Op>(
self,
op: FpBinaryOp,
operand: Op,
) -> VectorFpTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way4 }>,
{
self.do_binary::<stage::Fp, stage::Standalone, Op>(op, None, operand)
}
#[primitive(VectorTensor::vector_fp_binary_with_mode)]
pub fn vector_fp_binary_with_mode<Op>(
self,
op: FpBinaryOp,
mode: BinaryArgMode,
operand: Op,
) -> VectorFpTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way4 }>,
{
self.do_binary::<stage::Fp, stage::Standalone, Op>(op, Some(mode), operand)
}
#[primitive(VectorTensor::vector_fp_ternary)]
pub fn vector_fp_ternary<Op>(
self,
op: FpTernaryOp,
operands: Op,
) -> VectorFpTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoTernaryOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way4 }>,
{
self.vector_fp_ternary_with_mode(op, TernaryArgMode::Mode012, operands)
}
#[primitive(VectorTensor::vector_fp_ternary_with_mode)]
pub fn vector_fp_ternary_with_mode<Op>(
mut self,
op: FpTernaryOp,
mode: TernaryArgMode,
operands: Op,
) -> VectorFpTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoTernaryOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way4 }>,
{
let stash_data = Op::stashed(self.ve_state());
let operands = operands.into_ternary_operand();
self.ve_state_mut().use_alu(op.alu());
let op_fn = op.ternary_op_fn(Some(mode));
let result = apply_ternary_op(self.inner(), self.tag(), op_fn, &operands, stash_data.as_ref());
let (ctx, _inner, tag, ve_state) = self.into_parts();
let data = VeTensorData {
inner: result,
tag,
ve_state,
_stage: PhantomData,
_filter_state: PhantomData,
}
.apply_stash_transition::<Op>();
VectorFpTensor::from_ctx_and_data(ctx, data)
}
}
fn verify_reduce_label(time: Mapping, packet: Mapping, out_time: Mapping, out_packet: Mapping, reduce_label: &Ident) {
config_reduce_label(ReduceLabelInput {
in_time: time,
in_packet: packet,
out_time,
out_packet,
reduce_label: *reduce_label,
})
.unwrap_or_else(|message| panic!("{message}"));
}
fn reduce_tag<Chip: M, Cluster: M, Slice: M, Time: M, Packet: M, OutTime: M, OutPacket: M>(
tag: Tensor<u8, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>,
) -> Tensor<u8, VeTensorShape<Chip, Cluster, Slice, OutTime, OutPacket>> {
tag.reduce::<VeTensorShape<Chip, Cluster, Slice, OutTime, OutPacket>>(|_, y| y, 0, false)
}
impl<'l, const T: Tu, S, Chip: M, Cluster: M, Slice: M, Time: M, Packet: M, Stash: StashState, const VE_ORDER: VeOrder>
VectorTensor<'l, T, S, i32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, stage::Standalone, { Way4 }>
where
S: stage::Stage + CanTransitionTo<stage::IntraSliceReduce>,
{
#[primitive(VectorTensor::vector_intra_slice_reduce)]
pub fn vector_intra_slice_reduce<Reduce: AxisName, OutTime: M, OutPacket: M>(
mut self,
op: IntraSliceReduceOpI32,
) -> VectorIntraSliceReduceTensor<
'l,
T,
i32,
Chip,
Cluster,
Slice,
OutTime,
OutPacket,
Stash,
VE_ORDER,
stage::Standalone,
{ Way4 },
>
{
self.ve_state_mut().use_alu(op.alu());
let (ctx, inner, tag, ve_state) = self.into_parts();
verify_reduce_label(
Time::to_value(),
Packet::to_value(),
OutTime::to_value(),
OutPacket::to_value(),
&Reduce::NAME,
);
let reduced_inner = inner.reduce::<VeTensorShape<Chip, Cluster, Slice, OutTime, OutPacket>>(
op.reduce_fn(),
op.identity(),
false,
);
let reduced_eid = reduce_tag::<Chip, Cluster, Slice, Time, Packet, OutTime, OutPacket>(tag);
VectorIntraSliceReduceTensor::from_parts(ctx, reduced_inner, reduced_eid, ve_state)
}
}
impl<'l, const T: Tu, S, Chip: M, Cluster: M, Slice: M, Time: M, Packet: M, Stash: StashState, const VE_ORDER: VeOrder>
VectorTensor<'l, T, S, f32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, stage::Standalone, { Way4 }>
where
S: stage::Stage + CanTransitionTo<stage::IntraSliceReduce>,
{
#[primitive(VectorTensor::vector_intra_slice_reduce)]
pub fn vector_intra_slice_reduce<Reduce: AxisName, OutTime: M, OutPacket: M>(
mut self,
op: IntraSliceReduceOpF32,
) -> VectorIntraSliceReduceTensor<
'l,
T,
f32,
Chip,
Cluster,
Slice,
OutTime,
OutPacket,
Stash,
VE_ORDER,
stage::Standalone,
{ Way4 },
>
{
self.ve_state_mut().use_alu(op.alu());
let (ctx, inner, tag, ve_state) = self.into_parts();
verify_reduce_label(
Time::to_value(),
Packet::to_value(),
OutTime::to_value(),
OutPacket::to_value(),
&Reduce::NAME,
);
let reduced_inner = inner.reduce::<VeTensorShape<Chip, Cluster, Slice, OutTime, OutPacket>>(
op.reduce_fn(),
op.identity(),
false,
);
let reduced_eid = reduce_tag::<Chip, Cluster, Slice, Time, Packet, OutTime, OutPacket>(tag);
VectorIntraSliceReduceTensor::from_parts(ctx, reduced_inner, reduced_eid, ve_state)
}
}
impl<
'l,
const T: Tu,
S,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, f32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way4 }>
where
S: stage::Stage + CanTransitionTo<stage::FpDiv>,
{
#[primitive(VectorTensor::vector_fp_div)]
pub fn vector_fp_div<Op>(
self,
operand: Op,
) -> VectorFpDivTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER, FS, { Way4 }>
where
Op: IntoBranchedOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way4 }>,
{
self.do_binary::<stage::FpDiv, FS, Op>(FpDivBinaryOp::DivF, None, operand)
}
#[primitive(VectorTensor::vector_fp_div_with_mode)]
pub fn vector_fp_div_with_mode<Op>(
self,
mode: BinaryArgMode,
operand: Op,
) -> VectorFpDivTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER, FS, { Way4 }>
where
Op: IntoBranchedOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way4 }>,
{
self.do_binary::<stage::FpDiv, FS, Op>(FpDivBinaryOp::DivF, Some(mode), operand)
}
}
impl<
'l,
const T: Tu,
S,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way4 }>
where
S: stage::Stage + CanTransitionTo<stage::Widen>,
{
#[primitive(VectorTensor::vector_widen_concat)]
pub fn vector_widen_concat<Time2: M, Packet2: M>(
self,
) -> VectorWidenTensor<'l, T, D, Chip, Cluster, Slice, Time2, Packet2, Stash, VE_ORDER, FS, { Way8 }> {
verify_vector_widen_concat::<Time, Packet, Time2, Packet2>();
let (ctx, inner, tag, ve_state) = self.into_parts();
let concat_inner = inner.transpose::<VeTensorShape<Chip, Cluster, Slice, Time2, Packet2>>(true);
let concat_eid = tag.transpose::<VeTensorShape<Chip, Cluster, Slice, Time2, Packet2>>(true);
VectorWidenTensor::from_parts(ctx, concat_inner, concat_eid, ve_state)
}
}
impl<
'l,
const T: Tu,
S,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way4 }>
where
S: stage::Stage + CanTransitionTo<stage::Widen>,
{
#[primitive(VectorTensor::vector_widen_pad)]
pub fn vector_widen_pad<Packet2: M>(
self,
) -> VectorWidenTensor<'l, T, D, Chip, Cluster, Slice, Time, Packet2, Stash, VE_ORDER, FS, { Way8 }> {
verify_vector_widen_pad::<Packet, Packet2>();
let (ctx, inner, tag, ve_state) = self.into_parts();
let padded = inner.transpose::<VeTensorShape<Chip, Cluster, Slice, Time, Packet2>>(true);
let padded_eid = tag.transpose::<VeTensorShape<Chip, Cluster, Slice, Time, Packet2>>(true);
VectorWidenTensor::from_parts(ctx, padded, padded_eid, ve_state)
}
}
impl<
'l,
const T: Tu,
S,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, f32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::FpToFxp>,
{
#[primitive(VectorTensor::vector_fp_to_fxp)]
pub fn vector_fp_to_fxp(
self,
int_width: u32,
) -> VectorFpToFxpTensor<'l, T, i32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER> {
let op = FpToFxp::new(int_width);
let op_fn = op.op_fn();
let result = self.inner().map(&op_fn);
let (ctx, _inner, tag, ve_state) = self.into_parts();
VectorFpToFxpTensor::from_parts(ctx, result, tag, ve_state)
}
}
impl<
'l,
const T: Tu,
S,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, i32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::Clip>,
{
#[primitive(VectorTensor::vector_clip)]
pub fn vector_clip<Op>(
self,
op: ClipBinaryOpI32,
operand: Op,
) -> VectorClipTensor<'l, T, i32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<i32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, i32, { Way8 }>,
{
self.do_binary::<stage::Clip, stage::Standalone, Op>(op, None, operand)
}
#[primitive(VectorTensor::vector_clip_with_mode)]
pub fn vector_clip_with_mode<Op>(
self,
op: ClipBinaryOpI32,
mode: BinaryArgMode,
operand: Op,
) -> VectorClipTensor<'l, T, i32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<i32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, i32, { Way8 }>,
{
self.do_binary::<stage::Clip, stage::Standalone, Op>(op, Some(mode), operand)
}
}
impl<
'l,
const T: Tu,
S,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, f32, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::Clip>,
{
#[primitive(VectorTensor::vector_clip)]
pub fn vector_clip<Op>(
self,
op: ClipBinaryOpF32,
operand: Op,
) -> VectorClipTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way8 }>,
{
self.do_binary::<stage::Clip, stage::Standalone, Op>(op, None, operand)
}
#[primitive(VectorTensor::vector_clip_with_mode)]
pub fn vector_clip_with_mode<Op>(
self,
op: ClipBinaryOpF32,
mode: BinaryArgMode,
operand: Op,
) -> VectorClipTensor<'l, T, f32, Chip, Cluster, Slice, Time, Packet, Op::Next, VE_ORDER>
where
Op: IntoBranchedOperand<f32, VeTensorShape<Chip, Cluster, Slice, Time, Packet>>
+ StashTransition<Stash, f32, { Way8 }>,
{
self.do_binary::<stage::Clip, stage::Standalone, Op>(op, Some(mode), operand)
}
}
impl<
'l,
const T: Tu,
S,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, stage::Standalone, { Way8 }>
where
S: stage::Stage + CanTransitionTo<stage::Filter>,
{
#[primitive(VectorTensor::vector_filter)]
pub fn vector_filter<Time2: M>(
self,
_config: TagGuard,
) -> VectorFilterTensor<'l, T, D, Chip, Cluster, Slice, Time2, Packet, Stash, VE_ORDER, stage::Standalone, { Way8 }>
{
todo!("Implement vector_filter operation")
}
}
impl<
'l,
const T: Tu,
S: stage::Stage,
D: VeScalar,
Chip: M,
Cluster: M,
Slice: M,
Time: M,
Packet: M,
Stash: StashState,
FS: stage::VeTensorContext,
const W: Way,
const VE_ORDER: VeOrder,
> VectorTensor<'l, T, S, D, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W>
{
#[primitive(VectorTensor::vector_reinterpret)]
pub fn vector_reinterpret<D2: VeScalar>(
self,
) -> VectorTensor<'l, T, S, D2, Chip, Cluster, Slice, Time, Packet, Stash, VE_ORDER, FS, W> {
let op_fn = HasConversionOp::<D, D2>::conversion_op_fn(&Reinterpret);
let result = self.inner().map(&op_fn);
let (ctx, _inner, tag, ve_state) = self.into_parts();
VectorTensor::from_parts(ctx, result, tag, ve_state)
}
}
#[cfg(test)]
mod tests {
use furiosa_opt_common_ir::{BitReq, BranchedOperand, TagGuard};
use super::*;
#[test]
fn an_overlapping_later_slot_does_not_recombine_a_claimed_cell() {
axes![A = 4];
let negative = TagGuard::matches([BitReq::Ignore, BitReq::Ignore, BitReq::One, BitReq::Ignore]);
let data = Tensor::<i32, m![A]>::from_vec([0, 0, 0, 0]);
let tag = Tensor::<u8, m![A]>::from_vec([0b0100, 0b0000, 0b0100, 0b0000]);
let register = Tensor::<i32, m![A]>::from_vec([50, 50, 50, 50]);
let operand: BinaryBranchedOperand<i32, m![A]> = BranchedOperand::try_from_slots(
[Some((negative, 10)), None, None],
Some((TagGuard::all(), RfPort::External(register))),
)
.unwrap();
let out = apply_binary_op(&data, &tag, |a, b| a.max(b), &operand, None);
assert_eq!(out.into_vec(), vec![10, 50, 10, 50]);
}
#[test]
fn a_guarded_unary_leaves_unclaimed_cells_alone() {
axes![A = 4];
let negative = TagGuard::matches([BitReq::Ignore, BitReq::Ignore, BitReq::One, BitReq::Ignore]);
let data = Tensor::<i32, m![A]>::from_vec([1, 2, 3, 4]);
let tag = Tensor::<u8, m![A]>::from_vec([0b0100, 0b0000, 0b0100, 0b0000]);
let guarded = apply_unary_op_where(&data, &tag, negative, |v| v * 10);
assert_eq!(guarded.into_vec(), vec![10, 2, 30, 4]);
let everywhere = apply_unary_op_where(&data, &tag, TagGuard::all(), |v| v * 10);
assert_eq!(everywhere.into_vec(), vec![10, 20, 30, 40]);
}
#[test]
fn a_cell_no_slot_claims_passes_through() {
axes![A = 2];
let only_bit0 = TagGuard::matches([BitReq::One, BitReq::Ignore, BitReq::Ignore, BitReq::Ignore]);
let data = Tensor::<i32, m![A]>::from_vec([7, 7]);
let tag = Tensor::<u8, m![A]>::from_vec([0b0001, 0b0000]);
let operand: BinaryBranchedOperand<i32, m![A]> =
BranchedOperand::try_from_slots([Some((only_bit0, 1)), None, None], None).unwrap();
let out = apply_binary_op(&data, &tag, |a, b| a + b, &operand, None);
assert_eq!(out.into_vec(), vec![8, 7]);
}
#[test]
fn an_empty_layout_leaves_the_stream_alone() {
axes![A = 2];
let data = Tensor::<i32, m![A]>::from_vec([3, 4]);
let tag = Tensor::<u8, m![A]>::from_vec([0b1111, 0b0000]);
let operand = BinaryBranchedOperand::<i32, m![A]>::default();
let out = apply_binary_op(&data, &tag, |a, b| a + b, &operand, None);
assert_eq!(out.into_vec(), vec![3, 4]);
}
#[test]
fn each_ternary_slot_feeds_its_own_operand1() {
axes![A = 4];
let group0 = TagGuard::matches([BitReq::Ignore, BitReq::Ignore, BitReq::Ignore, BitReq::Zero]);
let group1 = TagGuard::matches([BitReq::Ignore, BitReq::Ignore, BitReq::Ignore, BitReq::One]);
let data = Tensor::<f32, m![A]>::from_vec([1.0, 1.0, 1.0, 1.0]);
let tag = Tensor::<u8, m![A]>::from_vec([0b0000, 0b1000, 0b0000, 0b1000]);
let operand: TernaryBranchedOperand<f32, m![A]> =
BranchedOperand::try_from_slots([Some((group0, (2.0, 10.0))), Some((group1, (3.0, 20.0))), None], None)
.unwrap();
let out = apply_ternary_op(&data, &tag, |a, b, c| a * b + c, &operand, None);
assert_eq!(out.into_vec(), vec![12.0, 23.0, 12.0, 23.0]);
}
#[test]
fn the_port_slot_reads_the_stash_tensor() {
axes![A = 2];
let bit0 = TagGuard::matches([BitReq::One, BitReq::Ignore, BitReq::Ignore, BitReq::Ignore]);
let data = Tensor::<i32, m![A]>::from_vec([1, 1]);
let tag = Tensor::<u8, m![A]>::from_vec([0b0001, 0b0000]);
let stash = Tensor::<i32, m![A]>::from_vec([40, 40]);
let operand: BinaryBranchedOperand<i32, m![A]> =
BranchedOperand::try_from_slots([None, None, None], Some((bit0, RfPort::Stash))).unwrap();
let out = apply_binary_op(&data, &tag, |a, b| a + b, &operand, Some(&stash));
assert_eq!(out.into_vec(), vec![41, 1], "the unguarded cell is untouched");
}
}