GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 72.6% 69 / 0 / 95
Functions: 100.0% 5 / 0 / 5
Branches: 50.0% 45 / 0 / 90

src/cpu/elementwise.cpp
Line Branch Exec Source
1 // ─── CPU elementwise ops (CHUNK 1) ─────────────────────────────────────────
2 //
3 // FP32 scalar host implementations. Ports the GPU elementwise kernels in
4 // src/cuda/elementwise.cu — kernel math reproduced verbatim, FP32 path only.
5 //
6 // clamp — in-place per-element clamp to [lo, hi].
7 // mul_inplace — in-place per-element multiply y *= x.
8 // axpby_inplace — y = a*y + b*x, FP32 arithmetic for all storage dtypes.
9 // threshold_u8 — Y = X > t ? 1 : 0 as an INT8 byte mask (FP32 or FP16 X).
10
11 #include <brotensor/tensor.h>
12
13 #include <cstdint>
14 #include <cstring>
15 #include <stdexcept>
16
17 namespace brotensor::detail::cpu {
18
19 6 void clamp(::brotensor::Tensor& y, float lo, float hi) {
20 6 const int n = y.size();
21
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (n == 0) return;
22 6 float* yp = y.host_f32_mut();
23
2/2
✓ Branch 0 taken 1116 times.
✓ Branch 1 taken 6 times.
1122 for (int i = 0; i < n; ++i) {
24 1116 float v = yp[i];
25
2/2
✓ Branch 0 taken 661 times.
✓ Branch 1 taken 455 times.
1116 if (v < lo) v = lo;
26
2/2
✓ Branch 0 taken 741 times.
✓ Branch 1 taken 375 times.
1116 if (v > hi) v = hi;
27 1116 yp[i] = v;
28 1116 }
29 6 }
30
31 147 void mul_inplace(::brotensor::Tensor& y, const ::brotensor::Tensor& x) {
32 // Matches the GPU contract: shape/dtype must match exactly.
33
1/2
✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
147 if (y.dtype != x.dtype || y.rows != x.rows || y.cols != x.cols) {
34 throw std::runtime_error("mul_inplace: shape/dtype mismatch");
35 }
36 147 const int n = y.size();
37
1/2
✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
147 if (n == 0) return;
38 147 float* yp = y.host_f32_mut();
39 147 const float* xp = x.host_f32();
40
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 1209 times.
1356 for (int i = 0; i < n; ++i) yp[i] *= xp[i];
41 147 }
42
43 // y = a*y + b*x in FP32 regardless of storage dtype; only the final store
44 // rounds back. Matches the GPU kernel exactly (same FP32 expression).
45 1 void axpby_inplace(::brotensor::Tensor& y, const ::brotensor::Tensor& x,
46 float a, float b) {
47 using ::brotensor::Dtype;
48
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (y.dtype != x.dtype || y.rows != x.rows || y.cols != x.cols) {
49 throw std::runtime_error("axpby_inplace: shape/dtype mismatch");
50 }
51 1 const int n = y.size();
52
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (n == 0) return;
53
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 if (y.dtype == Dtype::FP32) {
54 1 float* yp = y.host_f32_mut();
55 1 const float* xp = x.host_f32();
56
2/2
✓ Branch 0 taken 263 times.
✓ Branch 1 taken 1 time.
264 for (int i = 0; i < n; ++i) yp[i] = a * yp[i] + b * xp[i];
57
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
1 } else if (y.dtype == Dtype::FP16) {
58 std::uint16_t* yp = y.host_fp16_mut();
59 const std::uint16_t* xp = x.host_fp16();
60 for (int i = 0; i < n; ++i) {
61 const float v = a * ::brotensor::fp16_bits_to_fp32(yp[i]) +
62 b * ::brotensor::fp16_bits_to_fp32(xp[i]);
63 yp[i] = ::brotensor::fp32_to_fp16_bits(v);
64 }
65 } else if (y.dtype == Dtype::BF16) {
66 std::uint16_t* yp = y.host_bf16_mut();
67 const std::uint16_t* xp = x.host_bf16();
68 for (int i = 0; i < n; ++i) {
69 const float v = a * ::brotensor::bf16_bits_to_fp32(yp[i]) +
70 b * ::brotensor::bf16_bits_to_fp32(xp[i]);
71 yp[i] = ::brotensor::fp32_to_bf16_bits(v);
72 }
73 } else {
74 throw std::runtime_error("axpby_inplace: unsupported dtype");
75 }
76 1 }
77
78 // Strict >: a value exactly at t maps to 0.
79 4 void threshold_u8(const ::brotensor::Tensor& X, float t,
80 ::brotensor::Tensor& Y) {
81 using ::brotensor::Dtype;
82
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 if (X.dtype != Dtype::FP32 && X.dtype != Dtype::FP16) {
83 throw std::runtime_error("threshold_u8: X must be FP32 or FP16");
84 }
85
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
4 if (Y.rows != X.rows || Y.cols != X.cols || Y.dtype != Dtype::INT8) {
86 4 Y.resize(X.rows, X.cols, Dtype::INT8);
87 4 }
88 4 const int n = X.size();
89
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (n == 0) return;
90 4 int8_t* yp = static_cast<int8_t*>(Y.host_raw_mut());
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (X.dtype == Dtype::FP32) {
92 4 const float* xp = X.host_f32();
93
2/2
✓ Branch 0 taken 4408 times.
✓ Branch 1 taken 4 times.
4412 for (int i = 0; i < n; ++i) yp[i] = (xp[i] > t) ? 1 : 0;
94 4 } else {
95 const std::uint16_t* xp = X.host_fp16();
96 for (int i = 0; i < n; ++i) {
97 yp[i] = (::brotensor::fp16_bits_to_fp32(xp[i]) > t) ? 1 : 0;
98 }
99 }
100 4 }
101
102 14 void cast(const ::brotensor::Tensor& src, ::brotensor::Tensor& dst,
103 ::brotensor::Dtype out_dtype) {
104 using ::brotensor::Dtype;
105
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
14 if (dst.rows != src.rows || dst.cols != src.cols ||
106 dst.dtype != out_dtype) {
107 14 dst.resize(src.rows, src.cols, out_dtype);
108 14 }
109 14 const int n = src.size();
110
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (n == 0) return;
111
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 if (src.dtype == out_dtype) {
112 2 std::memcpy(dst.host_raw_mut(), src.host_raw(), src.bytes());
113 2 return;
114 }
115
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
12 if (src.dtype == Dtype::FP32 && out_dtype == Dtype::FP16) {
116 4 const float* s = src.host_f32();
117 4 std::uint16_t* d = dst.host_fp16_mut();
118
2/2
✓ Branch 0 taken 2112 times.
✓ Branch 1 taken 4 times.
2116 for (int i = 0; i < n; ++i) d[i] = ::brotensor::fp32_to_fp16_bits(s[i]);
119
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
12 } else if (src.dtype == Dtype::FP16 && out_dtype == Dtype::FP32) {
120 2 const std::uint16_t* s = src.host_fp16();
121 2 float* d = dst.host_f32_mut();
122
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 2 times.
1058 for (int i = 0; i < n; ++i) d[i] = ::brotensor::fp16_bits_to_fp32(s[i]);
123
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
8 } else if (src.dtype == Dtype::FP32 && out_dtype == Dtype::BF16) {
124 4 const float* s = src.host_f32();
125 4 std::uint16_t* d = dst.host_bf16_mut();
126
2/2
✓ Branch 0 taken 2112 times.
✓ Branch 1 taken 4 times.
2116 for (int i = 0; i < n; ++i) d[i] = ::brotensor::fp32_to_bf16_bits(s[i]);
127
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
6 } else if (src.dtype == Dtype::BF16 && out_dtype == Dtype::FP32) {
128 2 const std::uint16_t* s = src.host_bf16();
129 2 float* d = dst.host_f32_mut();
130
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 2 times.
1058 for (int i = 0; i < n; ++i) d[i] = ::brotensor::bf16_bits_to_fp32(s[i]);
131 2 } else {
132 throw std::runtime_error(
133 "brotensor: cast: unsupported dtype pair "
134 "(CPU supports FP32<->FP16 and FP32<->BF16)");
135 }
136 14 }
137
138 } // namespace brotensor::detail::cpu
139