Skip to main content

cubecl_core/frontend/operation/
binary.rs

1use crate::ir::{ExpandValue, Scope};
2use crate::{
3    flex32,
4    frontend::{CubePrimitive, NativeExpand},
5    prelude::*,
6};
7use crate::{frontend::CubeType, tf32};
8use crate::{frontend::operation::base::binary_expand, unexpanded};
9use core::ops::*;
10use cubecl_ir::dialect::{
11    bitwise::*,
12    general::{BoolAndOp, BoolOrOp},
13    math::*,
14    vector::{FDotOp, SDotOp, UDotOp},
15};
16use half::{bf16, f16};
17
18pub mod sub {
19    use cubecl_ir::{ConstantValue, ExpandValue};
20
21    use super::*;
22
23    pub fn expand<C: CubeSub>(
24        scope: &Scope,
25        lhs: NativeExpand<C>,
26        rhs: NativeExpand<C>,
27    ) -> NativeExpand<C> {
28        // Dirty hack to enable slice destructuring with trailing patterns on `Sequence`
29        match (lhs.expand, rhs.expand.as_const()) {
30            (
31                ExpandValue::Constant {
32                    value: ConstantValue::UInt(lhs_val),
33                    ty,
34                },
35                Some(ConstantValue::UInt(rhs_val)),
36            ) => {
37                let value = (lhs_val - rhs_val).into();
38                ExpandValue::constant(value, ty).into()
39            }
40            _ => C::Scalar::__expand_native_sub(scope, lhs.into(), rhs.into()).into(),
41        }
42    }
43}
44
45pub mod clamp {
46    use super::*;
47
48    pub fn expand<C: CubePartialOrd>(
49        scope: &Scope,
50        input: NativeExpand<C>,
51        min: NativeExpand<C>,
52        max: NativeExpand<C>,
53    ) -> NativeExpand<C> {
54        C::Scalar::__expand_native_clamp(scope, input.into(), min.into(), max.into()).into()
55    }
56}
57
58/// Floating-point minimum, propagating NaN from either operand.
59///
60/// Opposite-signed zeros may yield either sign; NaN payloads and signs are unspecified.
61#[allow(unused_variables)]
62pub fn min_nan<C: CubePrimitive<Scalar: Float>>(lhs: C, rhs: C) -> C {
63    unexpanded!()
64}
65
66/// Expansion of [`min_nan()`].
67pub mod min_nan {
68    use super::*;
69    use cubecl_ir::dialect::cmp::FMinNanOp;
70
71    pub fn expand<C: CubePrimitive<Scalar: Float>>(
72        scope: &Scope,
73        lhs: NativeExpand<C>,
74        rhs: NativeExpand<C>,
75    ) -> NativeExpand<C> {
76        let [lhs, rhs] =
77            normalize_same_vectorization(scope, [lhs.read_value(scope), rhs.read_value(scope)]);
78        let op = FMinNanOp::new(scope.ctx_mut(), lhs, rhs);
79        scope.register_with_result(&op).into()
80    }
81}
82
83/// Floating-point maximum, propagating NaN from either operand.
84///
85/// Opposite-signed zeros may yield either sign; NaN payloads and signs are unspecified.
86#[allow(unused_variables)]
87pub fn max_nan<C: CubePrimitive<Scalar: Float>>(lhs: C, rhs: C) -> C {
88    unexpanded!()
89}
90
91/// Expansion of [`max_nan()`].
92pub mod max_nan {
93    use super::*;
94    use cubecl_ir::dialect::cmp::FMaxNanOp;
95
96    pub fn expand<C: CubePrimitive<Scalar: Float>>(
97        scope: &Scope,
98        lhs: NativeExpand<C>,
99        rhs: NativeExpand<C>,
100    ) -> NativeExpand<C> {
101        let [lhs, rhs] =
102            normalize_same_vectorization(scope, [lhs.read_value(scope), rhs.read_value(scope)]);
103        let op = FMaxNanOp::new(scope.ctx_mut(), lhs, rhs);
104        scope.register_with_result(&op).into()
105    }
106}
107
108/// Clamp a float scalar or vector, propagating NaN from the input or either bound.
109///
110/// Applies the upper bound first, then the lower bound; reversed non-NaN bounds
111/// yield `min`. If the result is zero and the operands include zeros of opposite
112/// signs, either sign of zero may be returned. NaN payloads and signs are unspecified.
113#[allow(unused_variables)]
114pub fn clamp_nan<C: CubePrimitive<Scalar: Float>>(input: C, min: C, max: C) -> C {
115    unexpanded!()
116}
117
118/// Expansion of [`clamp_nan()`].
119pub mod clamp_nan {
120    use super::*;
121    use cubecl_ir::dialect::cmp::FClampNanOp;
122
123    pub fn expand<C: CubePrimitive<Scalar: Float>>(
124        scope: &Scope,
125        input: NativeExpand<C>,
126        min: NativeExpand<C>,
127        max: NativeExpand<C>,
128    ) -> NativeExpand<C> {
129        let [input, min, max] = normalize_same_vectorization(
130            scope,
131            [
132                input.read_value(scope),
133                min.read_value(scope),
134                max.read_value(scope),
135            ],
136        );
137        let op = FClampNanOp::new(scope.ctx_mut(), input, min, max);
138        scope.register_with_result(&op).into()
139    }
140}
141
142pub mod clamp_max {
143    use super::*;
144
145    pub fn expand<C: CubePartialOrd>(
146        scope: &Scope,
147        lhs: NativeExpand<C>,
148        rhs: NativeExpand<C>,
149    ) -> NativeExpand<C> {
150        C::Scalar::__expand_native_min(scope, lhs.into(), rhs.into()).into()
151    }
152}
153
154pub mod clamp_min {
155    use super::*;
156
157    pub fn expand<C: CubePartialOrd>(
158        scope: &Scope,
159        lhs: NativeExpand<C>,
160        rhs: NativeExpand<C>,
161    ) -> NativeExpand<C> {
162        C::Scalar::__expand_native_max(scope, lhs.into(), rhs.into()).into()
163    }
164}
165
166/// The minimum of two values, not requiring `Ord`. Provided for clarity in certain cases, though
167/// `clamp_max` may sometimes be more clear.
168pub fn min<T: CubePartialOrd>(lhs: T, rhs: T) -> T {
169    clamp_max(lhs, rhs)
170}
171
172pub mod min {
173    use super::*;
174
175    pub fn expand<C: CubePartialOrd>(
176        scope: &Scope,
177        lhs: NativeExpand<C>,
178        rhs: NativeExpand<C>,
179    ) -> NativeExpand<C> {
180        C::Scalar::__expand_native_min(scope, lhs.into(), rhs.into()).into()
181    }
182}
183
184/// The maximum of two values, not requiring `Ord`. Provided for clarity in certain cases, though
185/// `clamp_min` may sometimes be more clear.
186pub fn max<T: CubePartialOrd>(lhs: T, rhs: T) -> T {
187    clamp_min(lhs, rhs)
188}
189
190pub mod max {
191    use super::*;
192
193    pub fn expand<C: CubePartialOrd>(
194        scope: &Scope,
195        lhs: NativeExpand<C>,
196        rhs: NativeExpand<C>,
197    ) -> NativeExpand<C> {
198        C::Scalar::__expand_native_max(scope, lhs.into(), rhs.into()).into()
199    }
200}
201
202/// For binary functions without special syntax
203macro_rules! define_binary_func {
204    ($trait_name:ident, $method_name:ident) => {
205        paste::paste! {
206            pub trait [<Scalar $trait_name>]: $trait_name + [<$trait_name NativeExpand>] {}
207            impl<T: $trait_name + [<$trait_name NativeExpand>]> [<Scalar $trait_name>] for T {}
208
209            pub trait $trait_name: CubePrimitive<Scalar: [<$trait_name NativeExpand>]>
210                + CubeType<ExpandType: [<$trait_name Expand>]> + Sized {
211                fn $method_name(self, _rhs: Self) -> Self {
212                    unexpanded!()
213                }
214
215                fn [<__expand_ $method_name>](
216                    scope: &Scope,
217                    lhs: NativeExpand<Self>,
218                    rhs: NativeExpand<Self>,
219                ) -> NativeExpand<Self> {
220                    lhs.[<__expand_ $method_name _method>](scope, rhs)
221                }
222            }
223
224            pub trait [<$trait_name Expand>] {
225                fn [<__expand_ $method_name _method>](self, scope: &Scope, rhs: Self) -> Self;
226            }
227
228            pub trait [<$trait_name NativeExpand>] {
229                fn [<__expand_native_ $method_name>](scope: &Scope, lhs: ExpandValue, rhs: ExpandValue) -> ExpandValue;
230            }
231
232            impl<T: $trait_name> [<$trait_name Expand>] for NativeExpand<T> {
233                fn [<__expand_ $method_name _method>](self, scope: &Scope, rhs: Self) -> Self {
234                    T::Scalar::[<__expand_native_ $method_name>](scope, self.into(), rhs.into()).into()
235                }
236            }
237        }
238    }
239}
240
241macro_rules! impl_binary_func {
242    ($($type:ty),*; $trait_name:ident, $method_name:ident, $operator:expr) => {
243        paste::paste! {
244            $(impl $trait_name for $type {})*
245            $(impl [<$trait_name NativeExpand>] for $type {
246                fn [<__expand_native_ $method_name>](scope: &Scope, lhs: ExpandValue, rhs: ExpandValue) -> ExpandValue {
247                    binary_expand(scope, lhs, rhs, $operator::new)
248                }
249            })*
250        }
251    }
252}
253
254macro_rules! define_binary_func_scalar_out {
255    ($trait_name:ident, $method_name:ident) => {
256        paste::paste! {
257            pub trait [<Scalar $trait_name>]: $trait_name + [<$trait_name NativeExpand>] {}
258            impl<T: $trait_name + [<$trait_name NativeExpand>]> [<Scalar $trait_name>] for T {}
259
260            pub trait $trait_name: CubePrimitive<Scalar: [<$trait_name NativeExpand>]>
261                + CubeType<ExpandType: [<$trait_name Expand>]
262                + CubePrimitiveExpand<Scalar = NativeExpand<Self::Scalar>>>
263                + Sized {
264                fn $method_name(self, _rhs: Self) -> Self::Scalar {
265                    unexpanded!()
266                }
267
268                fn [<__expand_ $method_name>](
269                    scope: &Scope,
270                    lhs: NativeExpand<Self>,
271                    rhs: NativeExpand<Self>,
272                ) -> NativeExpand<Self::Scalar> {
273                    lhs.[<__expand_ $method_name _method>](scope, rhs)
274                }
275            }
276
277            pub trait [<$trait_name Expand>]: CubePrimitiveExpand {
278                fn [<__expand_ $method_name _method>](self, scope: &Scope, rhs: Self) -> Self::Scalar;
279            }
280
281            pub trait [<$trait_name NativeExpand>] {
282                fn [<__expand_native_ $method_name _scalar>](scope: &Scope, lhs: ExpandValue, rhs: ExpandValue) -> ExpandValue;
283                fn [<__expand_native_ $method_name>](scope: &Scope, lhs: ExpandValue, rhs: ExpandValue) -> ExpandValue;
284            }
285
286            impl<T: CubePrimitive + $trait_name> [<$trait_name Expand>] for NativeExpand<T> {
287                fn [<__expand_ $method_name _method>](self, scope: &Scope, rhs: Self) -> Self::Scalar {
288                    // A lot of backends can't deal with 1-sized vectors, and we want to validate
289                    // that the input is a vector
290                    if self.__expand_vector_size_method(scope) == 1 {
291                        T::Scalar::[<__expand_native_ $method_name _scalar>](scope, self.into(), rhs.into()).into()
292                    } else {
293                        T::Scalar::[<__expand_native_ $method_name>](scope, self.into(), rhs.into()).into()
294                    }
295                }
296            }
297        }
298    }
299}
300
301macro_rules! impl_binary_func_scalar_out {
302    ($($type:ty),*; $trait_name:ident, $method_name:ident, $operator:expr, $scalar_op:expr) => {
303        paste::paste! {
304            $(impl $trait_name for $type {})*
305            $(impl [<$trait_name NativeExpand>] for $type {
306                fn [<__expand_native_ $method_name _scalar>](scope: &Scope, lhs: ExpandValue, rhs: ExpandValue) -> ExpandValue {
307                    binary_expand(scope, lhs, rhs, $scalar_op::new)
308                }
309                fn [<__expand_native_ $method_name>](scope: &Scope, lhs: ExpandValue, rhs: ExpandValue) -> ExpandValue {
310                    binary_expand(scope, lhs, rhs, $operator::new)
311                }
312            })*
313        }
314    }
315}
316
317macro_rules! impl_binary_func_mixed_types {
318    ($trait_name:ident, $method_name:ident, $rhs_ty: ident, $operator:expr, $($type:ty),*) => {
319        paste::paste! {
320            pub trait $trait_name<Rhs: CubePrimitive + CubeType<ExpandType: Into<ExpandValue>> + Sized>:
321                CubePrimitive + CubeType<ExpandType: [<$trait_name Expand>]<Rhs>> + Sized {
322                fn $method_name(self, _rhs: Rhs) -> Self {
323                    unexpanded!()
324                }
325
326                fn [<__expand_ $method_name>](
327                    scope: &Scope,
328                    lhs: NativeExpand<Self>,
329                    rhs: NativeExpand<Rhs>,
330                ) -> NativeExpand<Self> {
331                    binary_expand(scope, lhs.into(), rhs.into(), $operator::new).into()
332                }
333            }
334
335            pub trait [<$trait_name Expand>]<Rhs: CubeType>{
336                fn [<__expand_ $method_name _method>](self, scope: &Scope, rhs: Rhs::ExpandType) -> Self;
337            }
338
339            $(impl $trait_name<$rhs_ty> for $type {})*
340            impl<Rhs: CubePrimitive, T: CubePrimitive + $trait_name<Rhs>> [<$trait_name Expand>]<Rhs> for NativeExpand<T> {
341                fn [<__expand_ $method_name _method>](self, scope: &Scope, rhs: NativeExpand<Rhs>) -> Self {
342                    binary_expand(scope, self.into(), rhs.into(), $operator::new).into()
343                }
344            }
345        }
346    }
347}
348
349macro_rules! define_core_binop {
350    ($trait: ident, $method: ident) => {
351        paste::paste! {
352            pub trait [<Scalar $trait>]: [<Cube $trait>] + [<$trait NativeExpand>] {}
353            impl<T: [<Cube $trait>] + [<$trait NativeExpand>]> [<Scalar $trait>] for T {}
354
355            pub trait [<Cube $trait>]:
356                $trait<Output = Self> + CubePrimitive<Scalar: [<$trait NativeExpand>]> + IntoRuntime
357                + CubeType<ExpandType: [<$trait Expand>]> + Sized {
358                fn [<__expand_ $method _method>](self, scope: &Scope, rhs: NativeExpand<Self>) -> NativeExpand<Self> {
359                    let this = self.__expand_runtime_method(scope);
360                    this.[<__expand_ $method _method>](scope, rhs)
361                }
362
363                fn [<__expand_ $method>](
364                    scope: &Scope,
365                    lhs: NativeExpand<Self>,
366                    rhs: NativeExpand<Self>,
367                ) -> NativeExpand<Self> {
368                    lhs.[<__expand_ $method _method>](scope, rhs)
369                }
370            }
371
372            pub trait [<$trait Expand>] {
373                fn [<__expand_ $method _method>](self, scope: &Scope, rhs: Self) -> Self;
374            }
375
376            pub trait [<$trait NativeExpand>] {
377                fn [<__expand_native_ $method>](scope: &Scope, this: ExpandValue, rhs: ExpandValue) -> ExpandValue;
378            }
379
380            impl<T: $trait<Output = Self> + CubePrimitive<Scalar: [<$trait NativeExpand>]> + IntoRuntime> [<Cube $trait>] for T {}
381            impl<T: [<Cube $trait>]> [<$trait Expand>] for NativeExpand<T> {
382                fn [<__expand_ $method _method>](self, scope: &Scope, rhs: Self) -> Self {
383                    T::Scalar::[<__expand_native_ $method>](scope, self.into(), rhs.into()).into()
384                }
385            }
386        }
387    };
388}
389
390macro_rules! impl_core_binop {
391    ($($ty: ty),*; $trait: ident, $method: ident, $op: expr) => {
392        paste::paste! {
393            $(impl [<$trait NativeExpand>] for $ty {
394                fn [<__expand_native_ $method>](scope: &Scope, this: ExpandValue, rhs: ExpandValue) -> ExpandValue {
395                    binary_expand(scope, this, rhs, $op::new)
396                }
397            })*
398        }
399    };
400}
401macro_rules! define_core_assign_binop {
402    ($trait: ident, $base_trait: ident, $method: ident, $base_method: ident) => {
403        paste::paste! {
404            pub trait [<Cube $trait>]: $trait + CubePrimitive<Scalar: [<$base_trait NativeExpand>]>
405                + CubeType<ExpandType: [<$trait Expand>]> + Sized {
406                fn [<__expand_ $method>](
407                    scope: &Scope,
408                    lhs: &mut NativeExpand<Self>,
409                    rhs: NativeExpand<Self>,
410                ) {
411                    lhs.[<__expand_ $method _method>](scope, rhs)
412                }
413            }
414
415            pub trait [<$trait Expand>] {
416                fn [<__expand_ $method _method>](&mut self, scope: &Scope, rhs: Self);
417            }
418
419            impl<T: $trait + [<Cube $base_trait>]> [<Cube $trait>] for T {}
420            impl<T: $trait + [<Cube $base_trait>]> [<$trait Expand>] for NativeExpand<T> {
421                fn [<__expand_ $method _method>](&mut self, scope: &Scope, rhs: Self) {
422                    assign_binop_expand(scope, self, rhs, T::Scalar::[<__expand_native_ $base_method>]);
423                }
424            }
425        }
426    };
427}
428
429define_core_binop!(Add, add);
430define_core_binop!(Sub, sub);
431define_core_binop!(Mul, mul);
432define_core_binop!(Div, div);
433define_core_binop!(Rem, rem);
434
435impl_core_binop!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; Add, add, IAddOp);
436impl_core_binop!(f16, bf16, f32, flex32, tf32, f64; Add, add, FAddOp);
437
438impl_core_binop!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; Sub, sub, ISubOp);
439impl_core_binop!(f16, bf16, f32, flex32, tf32, f64; Sub, sub, FSubOp);
440
441impl_core_binop!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; Mul, mul, IMulOp);
442impl_core_binop!(f16, bf16, f32, flex32, tf32, f64; Mul, mul, FMulOp);
443
444impl_core_binop!(i8, i16, i32, i64, isize; Div, div, SDivOp);
445impl_core_binop!(u8, u16, u32, u64, usize; Div, div, UDivOp);
446impl_core_binop!(f16, bf16, f32, flex32, tf32, f64; Div, div, FDivOp);
447
448impl_core_binop!(i8, i16, i32, i64, isize; Rem, rem, SRemOp);
449impl_core_binop!(u8, u16, u32, u64, usize; Rem, rem, URemOp);
450impl_core_binop!(f16, bf16, f32, flex32, tf32, f64; Rem, rem, FRemOp);
451
452define_core_assign_binop!(AddAssign, Add, add_assign, add);
453define_core_assign_binop!(SubAssign, Sub, sub_assign, sub);
454define_core_assign_binop!(MulAssign, Mul, mul_assign, mul);
455define_core_assign_binop!(DivAssign, Div, div_assign, div);
456define_core_assign_binop!(RemAssign, Rem, rem_assign, rem);
457
458define_core_binop!(BitAnd, bitand);
459define_core_binop!(BitOr, bitor);
460define_core_binop!(BitXor, bitxor);
461define_core_binop!(Shl, shl);
462define_core_binop!(Shr, shr);
463
464impl_core_binop!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; BitAnd, bitand, BitwiseAndOp);
465impl_core_binop!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; BitOr, bitor, BitwiseOrOp);
466impl_core_binop!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; BitXor, bitxor, BitwiseXorOp);
467impl_core_binop!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; Shl, shl, ShiftLeftOp);
468impl_core_binop!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize; Shr, shr, ShiftRightOp);
469
470define_core_assign_binop!(BitAndAssign, BitAnd, bitand_assign, bitand);
471define_core_assign_binop!(BitOrAssign, BitOr, bitor_assign, bitor);
472define_core_assign_binop!(BitXorAssign, BitXor, bitxor_assign, bitxor);
473define_core_assign_binop!(ShlAssign, Shl, shl_assign, shl);
474define_core_assign_binop!(ShrAssign, Shr, shr_assign, shr);
475
476impl BitAndNativeExpand for bool {
477    fn __expand_native_bitand(scope: &Scope, this: ExpandValue, rhs: ExpandValue) -> ExpandValue {
478        binary_expand(scope, this, rhs, BoolAndOp::new)
479    }
480}
481impl BitOrNativeExpand for bool {
482    fn __expand_native_bitor(scope: &Scope, this: ExpandValue, rhs: ExpandValue) -> ExpandValue {
483        binary_expand(scope, this, rhs, BoolOrOp::new)
484    }
485}
486
487pub trait CubeAnd:
488    CubePrimitive + Into<ExpandValue> + CubeType<ExpandType: AndExpand> + Sized
489{
490    fn __expand_and_method(self, scope: &Scope, rhs: NativeExpand<Self>) -> NativeExpand<Self> {
491        let this: ExpandValue = self.into();
492        let this: NativeExpand<Self> = this.into();
493        this.__expand_and_method(scope, rhs)
494    }
495    fn __expand_and(
496        scope: &Scope,
497        lhs: NativeExpand<Self>,
498        rhs: NativeExpand<Self>,
499    ) -> NativeExpand<Self> {
500        lhs.__expand_and_method(scope, rhs)
501    }
502}
503pub trait AndExpand {
504    fn __expand_and_method(self, scope: &Scope, rhs: Self) -> Self;
505}
506
507impl CubeAnd for bool {}
508impl<T: CubeAnd + CubePrimitive> AndExpand for NativeExpand<T> {
509    fn __expand_and_method(self, scope: &Scope, rhs: Self) -> Self {
510        binary_expand(scope, self.into(), rhs.into(), BoolAndOp::new).into()
511    }
512}
513
514pub trait CubeOr:
515    CubePrimitive + Into<ExpandValue> + CubeType<ExpandType: OrExpand> + Sized
516{
517    fn __expand_or_method(self, scope: &Scope, rhs: NativeExpand<Self>) -> NativeExpand<Self> {
518        let this: ExpandValue = self.into();
519        let this: NativeExpand<Self> = this.into();
520        this.__expand_or_method(scope, rhs)
521    }
522    fn __expand_or(
523        scope: &Scope,
524        lhs: NativeExpand<Self>,
525        rhs: NativeExpand<Self>,
526    ) -> NativeExpand<Self> {
527        lhs.__expand_or_method(scope, rhs)
528    }
529}
530pub trait OrExpand {
531    fn __expand_or_method(self, scope: &Scope, rhs: Self) -> Self;
532}
533
534impl CubeOr for bool {}
535impl<T: CubeOr + CubePrimitive> OrExpand for NativeExpand<T> {
536    fn __expand_or_method(self, scope: &Scope, rhs: Self) -> Self {
537        binary_expand(scope, self.into(), rhs.into(), BoolOrOp::new).into()
538    }
539}
540
541define_binary_func!(Powf, powf);
542impl_binary_func!(f16, bf16, flex32, tf32, f32, f64; Powf, powf, PowfOp);
543
544define_binary_func!(Hypot, hypot);
545impl_binary_func!(f16, bf16, flex32, tf32, f32, f64; Hypot, hypot, HypotOp);
546
547define_binary_func!(Rhypot, rhypot);
548impl_binary_func!(f16, bf16, flex32, tf32, f32, f64; Rhypot, rhypot, RhypotOp);
549
550define_binary_func!(ArcTan2, atan2);
551impl_binary_func!(f16, bf16, flex32, tf32, f32, f64; ArcTan2, atan2, ArcTan2Op);
552
553define_binary_func!(ModFloor, mod_floor);
554impl_binary_func!(i8, i16, i32, i64, isize; ModFloor, mod_floor, SModFloorOp);
555impl_binary_func!(u8, u16, u32, u64, usize; ModFloor, mod_floor, URemOp);
556impl_binary_func!(f16, bf16, flex32, tf32, f32, f64; ModFloor, mod_floor, FModFloorOp);
557
558define_binary_func!(MulHi, mul_hi);
559impl_binary_func!(i32, i64, isize; MulHi, mul_hi, SMulHiOp);
560impl_binary_func!(u32, u64, usize; MulHi, mul_hi, UMulHiOp);
561
562define_binary_func!(SaturatingAdd, saturating_add);
563impl_binary_func!(i8, i16, i32, i64, isize; SaturatingAdd, saturating_add, SaturatingSAddOp);
564impl_binary_func!(u8, u16, u32, u64, usize; SaturatingAdd, saturating_add, SaturatingUAddOp);
565
566define_binary_func!(SaturatingSub, saturating_sub);
567impl_binary_func!(i8, i16, i32, i64, isize; SaturatingSub, saturating_sub, SaturatingSSubOp);
568impl_binary_func!(u8, u16, u32, u64, usize; SaturatingSub, saturating_sub, SaturatingUSubOp);
569
570define_binary_func_scalar_out!(Dot, dot);
571impl_binary_func_scalar_out!(i8, i16, i32, i64, isize; Dot, dot, SDotOp, IMulOp);
572impl_binary_func_scalar_out!(u8, u16, u32, u64, usize; Dot, dot, UDotOp, IMulOp);
573impl_binary_func_scalar_out!(f16, bf16, flex32, tf32, f32, f64; Dot, dot, FDotOp, FMulOp);
574
575impl_binary_func_mixed_types!(
576    Powi, powi, i32, PowiOp, f16, bf16, flex32, tf32, f32, f64, i8, i16, i32, i64, u8, u16, u32,
577    u64, usize, isize
578);