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);