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