hodu_cpu_kernels/kernels/
ops_binary.rs

1//! Binary operations for tensor element-wise computations
2//!
3//! This module provides element-wise binary operations including:
4//! - Arithmetic: add, sub, mul, div, pow, maximum, minimum
5//! - Logical: logical_and, logical_or, logical_xor
6//! - Comparison: eq, ne, lt, le, gt, ge
7//!
8//! All operations support multiple data types including floating point
9//! (f8e4m3, f8e5m2, bf16, f16, f32, f64), integer (u8-u64, i8-i64), and bool.
10
11use crate::{error::Result, kernels::macros::ops};
12use core::ffi::c_void;
13
14// Define all binary operations using the macro
15ops!(
16    add,
17    sub,
18    mul,
19    div,
20    pow,
21    maximum,
22    minimum,
23    logical_and,
24    logical_or,
25    logical_xor,
26    eq,
27    ne,
28    lt,
29    le,
30    gt,
31    ge
32);
33
34/// Execute a binary operation on two tensors
35///
36/// Performs element-wise binary operations on tensors with arbitrary shapes and strides.
37/// The function automatically handles contiguous and strided memory layouts for optimal
38/// performance. Broadcasting is not handled here - shapes must be compatible.
39///
40/// # Arguments
41/// * `kernel_name` - The binary operation to perform (e.g., add::F32, mul::I32)
42/// * `lhs` - Pointer to left-hand side tensor data
43/// * `rhs` - Pointer to right-hand side tensor data
44/// * `output` - Pointer to output tensor buffer
45/// * `metadata` - Tensor metadata array (see layout below)
46///
47/// # Metadata layout
48/// - metadata[0]: num_els (total number of elements to process)
49/// - metadata[1]: num_dims (number of dimensions)
50/// - metadata[2..2+num_dims]: lhs_shape (shape of left tensor)
51/// - metadata[2+num_dims..2+2*num_dims]: rhs_shape (shape of right tensor)
52/// - metadata[2+2*num_dims..2+3*num_dims]: lhs_strides (stride of left tensor)
53/// - metadata[2+3*num_dims..2+4*num_dims]: rhs_strides (stride of right tensor)
54/// - metadata[2+4*num_dims]: lhs_offset (starting offset in left tensor)
55/// - metadata[2+4*num_dims+1]: rhs_offset (starting offset in right tensor)
56///
57/// # Safety
58/// This function uses unsafe FFI calls to C kernels. Caller must ensure:
59/// - Pointers are valid and properly aligned
60/// - Metadata accurately describes tensor layout
61/// - Output buffer has sufficient capacity
62///
63/// # Returns
64/// Returns `Ok(())` on success. Currently does not propagate C kernel errors.
65pub fn call_ops_binary(
66    kernel_name: crate::kernels::macros::Kernel,
67    lhs: *const c_void,
68    rhs: *const c_void,
69    output: *mut c_void,
70    metadata: &[usize],
71) -> Result<()> {
72    unsafe {
73        dispatch_binary(kernel_name.0, lhs, rhs, output, metadata.as_ptr());
74    }
75
76    Ok(())
77}
78
79/// Macro to generate extern C declarations and dispatch logic for binary operations
80///
81/// This macro generates:
82/// 1. Extern "C" function declarations for all operation/type combinations
83/// 2. A dispatch function that routes kernel name strings to the appropriate C function
84///
85/// For each operation (e.g., add, mul), it creates declarations for all supported types
86/// (bool, f8e4m3, f8e5m2, bf16, f16, f32, f64, u8, u16, u32, u64, i8, i16, i32, i64).
87///
88/// # Safety
89/// The generated code involves unsafe FFI calls. Type safety is ensured by the
90/// kernel naming convention (operation_type format, e.g., "add_f32").
91macro_rules! declare_and_dispatch_binary {
92    ($($op:ident),* $(,)?) => {
93        paste::paste! {
94            // Extern C declarations for all operations and types
95            extern "C" {
96                $(
97                    fn [<$op _bool>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
98                    fn [<$op _f8e4m3>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
99                    fn [<$op _f8e5m2>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
100                    fn [<$op _bf16>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
101                    fn [<$op _f16>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
102                    fn [<$op _f32>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
103                    fn [<$op _f64>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
104                    fn [<$op _u8>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
105                    fn [<$op _u16>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
106                    fn [<$op _u32>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
107                    fn [<$op _u64>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
108                    fn [<$op _i8>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
109                    fn [<$op _i16>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
110                    fn [<$op _i32>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
111                    fn [<$op _i64>](lhs: *const c_void, rhs: *const c_void, output: *mut c_void, metadata: *const usize);
112                )*
113            }
114
115            // Dispatch function
116            unsafe fn dispatch_binary(
117                name: &str,
118                lhs: *const c_void,
119                rhs: *const c_void,
120                output: *mut c_void,
121                metadata: *const usize,
122            ) {
123                match name {
124                    $(
125                        concat!(stringify!($op), "_bool") => [<$op _bool>](lhs, rhs, output, metadata),
126                        concat!(stringify!($op), "_f8e4m3") => [<$op _f8e4m3>](lhs, rhs, output, metadata),
127                        concat!(stringify!($op), "_f8e5m2") => [<$op _f8e5m2>](lhs, rhs, output, metadata),
128                        concat!(stringify!($op), "_bf16") => [<$op _bf16>](lhs, rhs, output, metadata),
129                        concat!(stringify!($op), "_f16") => [<$op _f16>](lhs, rhs, output, metadata),
130                        concat!(stringify!($op), "_f32") => [<$op _f32>](lhs, rhs, output, metadata),
131                        concat!(stringify!($op), "_f64") => [<$op _f64>](lhs, rhs, output, metadata),
132                        concat!(stringify!($op), "_u8") => [<$op _u8>](lhs, rhs, output, metadata),
133                        concat!(stringify!($op), "_u16") => [<$op _u16>](lhs, rhs, output, metadata),
134                        concat!(stringify!($op), "_u32") => [<$op _u32>](lhs, rhs, output, metadata),
135                        concat!(stringify!($op), "_u64") => [<$op _u64>](lhs, rhs, output, metadata),
136                        concat!(stringify!($op), "_i8") => [<$op _i8>](lhs, rhs, output, metadata),
137                        concat!(stringify!($op), "_i16") => [<$op _i16>](lhs, rhs, output, metadata),
138                        concat!(stringify!($op), "_i32") => [<$op _i32>](lhs, rhs, output, metadata),
139                        concat!(stringify!($op), "_i64") => [<$op _i64>](lhs, rhs, output, metadata),
140                    )*
141                    _ => panic!("Unsupported binary kernel: {}", name),
142                }
143            }
144        }
145    };
146}
147
148// Declare all binary operations
149declare_and_dispatch_binary!(
150    add,
151    sub,
152    mul,
153    div,
154    pow,
155    maximum,
156    minimum,
157    logical_and,
158    logical_or,
159    logical_xor,
160    eq,
161    ne,
162    lt,
163    le,
164    gt,
165    ge
166);