src/cpu/cpu_jit.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "cpu_jit.h" | ||
| 2 | #include <brotensor/detail/cpu/thread_pool.h> | ||
| 3 | #include <algorithm> | ||
| 4 | #include <cmath> | ||
| 5 | #include <cstdint> | ||
| 6 | |||
| 7 | namespace brotensor::detail::cpu::jit { | ||
| 8 | namespace ref { | ||
| 9 | |||
| 10 | ✗ | inline void rms_norm(const float* X, const float* gamma, float eps, float* Y, int B, int D) { | |
| 11 | ✗ | if (B <= 0 || D <= 0) return; | |
| 12 | ✗ | const float inv_D = 1.0f / static_cast<float>(D); | |
| 13 | ✗ | auto row_fn = [&](int b) { | |
| 14 | ✗ | const float* xr = X + static_cast<size_t>(b) * D; | |
| 15 | ✗ | float* yr = Y + static_cast<size_t>(b) * D; | |
| 16 | ✗ | float sum = 0.0f; | |
| 17 | ✗ | for (int i = 0; i < D; ++i) sum += xr[i] * xr[i]; | |
| 18 | ✗ | float rrms = 1.0f / std::sqrt(sum * inv_D + eps); | |
| 19 | ✗ | for (int i = 0; i < D; ++i) yr[i] = xr[i] * gamma[i] * rrms; | |
| 20 | ✗ | }; | |
| 21 | ✗ | if (B > 1 && static_cast<int64_t>(B) * D >= 262144) { | |
| 22 | ✗ | detail::cpu::parallel_for(static_cast<size_t>(B), [&](size_t bi) { | |
| 23 | ✗ | row_fn(static_cast<int>(bi)); | |
| 24 | ✗ | }); | |
| 25 | ✗ | } else { | |
| 26 | ✗ | for (int b = 0; b < B; ++b) row_fn(b); | |
| 27 | } | ||
| 28 | ✗ | } | |
| 29 | |||
| 30 | 247 | inline void swiglu(const float* X, float* Y, int B, int D) { | |
| 31 |
2/4✓ Branch 0 taken 247 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 247 times.
|
247 | if (B <= 0 || D <= 0) return; |
| 32 | 3364 | auto row_fn = [&](int b) { | |
| 33 | 3117 | const float* gate = X + static_cast<size_t>(b) * 2 * D; | |
| 34 | 3117 | const float* up = gate + D; | |
| 35 | 3117 | float* out = Y + static_cast<size_t>(b) * D; | |
| 36 |
2/2✓ Branch 0 taken 3525511 times.
✓ Branch 1 taken 3117 times.
|
3528628 | for (int i = 0; i < D; ++i) { |
| 37 | 3525511 | float g = gate[i]; | |
| 38 | 3525511 | float silu = g / (1.0f + std::exp(-g)); | |
| 39 | 3525511 | out[i] = silu * up[i]; | |
| 40 | 3525511 | } | |
| 41 | 3117 | }; | |
| 42 |
4/4✓ Branch 0 taken 186 times.
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 124 times.
✓ Branch 3 taken 62 times.
|
247 | if (B > 1 && static_cast<int64_t>(B) * D >= 16384) { |
| 43 |
1/2✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
|
2377 | detail::cpu::parallel_for(static_cast<size_t>(B), [&](size_t bi) { |
| 44 | 2315 | row_fn(static_cast<int>(bi)); | |
| 45 | 2315 | }); | |
| 46 | 62 | } else { | |
| 47 |
2/2✓ Branch 0 taken 807 times.
✓ Branch 1 taken 185 times.
|
992 | for (int b = 0; b < B; ++b) row_fn(b); |
| 48 | } | ||
| 49 | 247 | } | |
| 50 | |||
| 51 | 246 | inline void modulate(const float* X, const float* scale, const float* shift, float* Y, int L, int D) { | |
| 52 |
2/4✓ Branch 0 taken 246 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 246 times.
|
246 | if (L <= 0 || D <= 0) return; |
| 53 | 2975 | auto row_fn = [&](int l) { | |
| 54 | 2729 | const float* xr = X + static_cast<size_t>(l) * D; | |
| 55 | 2729 | float* yr = Y + static_cast<size_t>(l) * D; | |
| 56 |
2/2✓ Branch 0 taken 8453035 times.
✓ Branch 1 taken 2729 times.
|
8455764 | for (int d = 0; d < D; ++d) { |
| 57 | 8453035 | yr[d] = xr[d] * (1.0f + scale[d]) + shift[d]; | |
| 58 | 8453035 | } | |
| 59 | 2729 | }; | |
| 60 |
3/4✓ Branch 0 taken 185 times.
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 185 times.
✗ Branch 3 not taken.
|
246 | if (L > 1 && static_cast<int64_t>(L) * D >= 262144) { |
| 61 | ✗ | detail::cpu::parallel_for(static_cast<size_t>(L), [&](size_t li) { | |
| 62 | ✗ | row_fn(static_cast<int>(li)); | |
| 63 | ✗ | }); | |
| 64 | ✗ | } else { | |
| 65 |
2/2✓ Branch 0 taken 2729 times.
✓ Branch 1 taken 246 times.
|
2975 | for (int l = 0; l < L; ++l) row_fn(l); |
| 66 | } | ||
| 67 | 246 | } | |
| 68 | |||
| 69 | 245 | inline void broadcast_mul(const float* X, const float* v, float* Y, int L, int D) { | |
| 70 |
2/4✓ Branch 0 taken 245 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 245 times.
|
245 | if (L <= 0 || D <= 0) return; |
| 71 | 2973 | auto row_fn = [&](int l) { | |
| 72 | 2728 | const float* xr = X + static_cast<size_t>(l) * D; | |
| 73 | 2728 | float* yr = Y + static_cast<size_t>(l) * D; | |
| 74 |
2/2✓ Branch 0 taken 8453034 times.
✓ Branch 1 taken 2728 times.
|
8455762 | for (int d = 0; d < D; ++d) { |
| 75 | 8453034 | yr[d] = xr[d] * v[d]; | |
| 76 | 8453034 | } | |
| 77 | 2728 | }; | |
| 78 |
3/4✓ Branch 0 taken 185 times.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 185 times.
✗ Branch 3 not taken.
|
245 | if (L > 1 && static_cast<int64_t>(L) * D >= 262144) { |
| 79 | ✗ | detail::cpu::parallel_for(static_cast<size_t>(L), [&](size_t li) { | |
| 80 | ✗ | row_fn(static_cast<int>(li)); | |
| 81 | ✗ | }); | |
| 82 | ✗ | } else { | |
| 83 |
2/2✓ Branch 0 taken 2728 times.
✓ Branch 1 taken 245 times.
|
2973 | for (int l = 0; l < L; ++l) row_fn(l); |
| 84 | } | ||
| 85 | 245 | } | |
| 86 | |||
| 87 | ✗ | inline void layernorm_forward_inference_batched(const float* X, const float* gamma, const float* beta, | |
| 88 | float eps, float* Y, int R, int D) { | ||
| 89 | ✗ | if (R <= 0 || D <= 0) return; | |
| 90 | ✗ | const float inv_D = 1.0f / static_cast<float>(D); | |
| 91 | ✗ | auto row_fn = [&](int r) { | |
| 92 | ✗ | const float* xr = X + static_cast<size_t>(r) * D; | |
| 93 | ✗ | float* yr = Y + static_cast<size_t>(r) * D; | |
| 94 | ✗ | float sum = 0.0f; | |
| 95 | ✗ | for (int i = 0; i < D; ++i) sum += xr[i]; | |
| 96 | ✗ | float mean = sum * inv_D; | |
| 97 | ✗ | float sumsq = 0.0f; | |
| 98 | ✗ | for (int i = 0; i < D; ++i) { | |
| 99 | ✗ | float diff = xr[i] - mean; | |
| 100 | ✗ | sumsq += diff * diff; | |
| 101 | ✗ | } | |
| 102 | ✗ | float var = sumsq * inv_D; | |
| 103 | ✗ | float rstd = 1.0f / std::sqrt(var + eps); | |
| 104 | ✗ | for (int i = 0; i < D; ++i) { | |
| 105 | ✗ | float g = gamma ? gamma[i] : 1.0f; | |
| 106 | ✗ | float b = beta ? beta[i] : 0.0f; | |
| 107 | ✗ | yr[i] = g * ((xr[i] - mean) * rstd) + b; | |
| 108 | ✗ | } | |
| 109 | ✗ | }; | |
| 110 | ✗ | if (R > 1 && static_cast<int64_t>(R) * D >= 262144) { | |
| 111 | ✗ | detail::cpu::parallel_for(static_cast<size_t>(R), [&](size_t ri) { | |
| 112 | ✗ | row_fn(static_cast<int>(ri)); | |
| 113 | ✗ | }); | |
| 114 | ✗ | } else { | |
| 115 | ✗ | for (int r = 0; r < R; ++r) row_fn(r); | |
| 116 | } | ||
| 117 | ✗ | } | |
| 118 | |||
| 119 | } // namespace ref | ||
| 120 | } // namespace brotensor::detail::cpu::jit | ||
| 121 | |||
| 122 | #if BROTENSOR_HAS_BRASS_JIT | ||
| 123 | |||
| 124 | #include <brass/codegen/kernel_jit.hpp> | ||
| 125 | #include <brass/codegen/ml_fusion.hpp> | ||
| 126 | #include <brass/mir/builder.hpp> | ||
| 127 | #include <brass/mir/module.hpp> | ||
| 128 | #include <brass/mir/function.hpp> | ||
| 129 | #include <cmath> | ||
| 130 | #include <mutex> | ||
| 131 | #include <vector> | ||
| 132 | #include <memory> | ||
| 133 | #include <algorithm> | ||
| 134 | #include <iostream> | ||
| 135 | |||
| 136 | namespace brotensor::detail::cpu::jit { | ||
| 137 | |||
| 138 | namespace { | ||
| 139 | |||
| 140 | using namespace brass; | ||
| 141 | using namespace brass::codegen; | ||
| 142 | |||
| 143 | struct JitKernels { | ||
| 144 | bool available = false; | ||
| 145 | |||
| 146 | using AdaLNModulateFn = void (*)(const float* x, const float* scale, const float* shift, float* out, uint64_t n); | ||
| 147 | AdaLNModulateFn adaln_fn = nullptr; | ||
| 148 | |||
| 149 | using BroadcastMulFn = void (*)(const float* x, const float* v, float* out, uint64_t n); | ||
| 150 | BroadcastMulFn broadcast_mul_fn = nullptr; | ||
| 151 | |||
| 152 | using RmsNormRowFn = void (*)(const float* x, const float* gamma, float* y, uint64_t d, float eps, float inv_d); | ||
| 153 | RmsNormRowFn rms_norm_row_fn = nullptr; | ||
| 154 | |||
| 155 | using LayerNormRowFn = void (*)(const float* x, const float* gamma, const float* beta, float* y, uint64_t d, float eps, float inv_d); | ||
| 156 | LayerNormRowFn layernorm_row_fn = nullptr; | ||
| 157 | |||
| 158 | std::vector<std::shared_ptr<JitExecutionEngine>> engines; | ||
| 159 | }; | ||
| 160 | |||
| 161 | // Horizontal sum of an 8-wide float vector using destination scratch buffer | ||
| 162 | 24 | static Value* reduce_vsum8(KernelBuilder& kb, Value* scratch, Value* vsum) { | |
| 163 | 24 | kb.vstore_f32x8(scratch, vsum); | |
| 164 | 24 | Value* s0 = kb.load_f32(scratch, 0); Value* s1 = kb.load_f32(scratch, 4); | |
| 165 | 24 | Value* s2 = kb.load_f32(scratch, 8); Value* s3 = kb.load_f32(scratch, 12); | |
| 166 | 24 | Value* s4 = kb.load_f32(scratch, 16); Value* s5 = kb.load_f32(scratch, 20); | |
| 167 | 24 | Value* s6 = kb.load_f32(scratch, 24); Value* s7 = kb.load_f32(scratch, 28); | |
| 168 | 24 | Value* sum0123 = kb.add(kb.add(s0, s1), kb.add(s2, s3)); | |
| 169 | 24 | Value* sum4567 = kb.add(kb.add(s4, s5), kb.add(s6, s7)); | |
| 170 | 24 | return kb.add(sum0123, sum4567); | |
| 171 | } | ||
| 172 | |||
| 173 | // ── 1. Vectorized Broadcast Mul (AVX2 8-wide + scalar remainder) ──────────── | ||
| 174 | 8 | static KernelFunction build_broadcast_mul(KernelJit& jit) { | |
| 175 | 8 | Module mod("mod_broadcast_mul"); | |
| 176 |
6/12✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
|
8 | Function* fn = mod.create_function("broadcast_mul", Type::void_type(), { |
| 177 | 32 | Type::ptr(), Type::ptr(), Type::ptr(), Type::i64() | |
| 178 | }); | ||
| 179 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | KernelBuilder kb(mod, fn); |
| 180 | |||
| 181 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* entry = kb.builder().append_block("entry"); |
| 182 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* x = kb.builder().add_block_param(entry, Type::ptr()); |
| 183 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* v = kb.builder().add_block_param(entry, Type::ptr()); |
| 184 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* out = kb.builder().add_block_param(entry, Type::ptr()); |
| 185 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* n = kb.builder().add_block_param(entry, Type::i64()); |
| 186 | |||
| 187 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* v_head = kb.builder().create_block("v_head"); |
| 188 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* v_body = kb.builder().create_block("v_body"); |
| 189 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* s_head = kb.builder().create_block("s_head"); |
| 190 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* s_body = kb.builder().create_block("s_body"); |
| 191 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* exit = kb.builder().create_block("exit"); |
| 192 | |||
| 193 | 8 | kb.position_at_end(entry); | |
| 194 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* zero_i64 = kb.builder().build_iconst_i64(0); |
| 195 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* one_i64 = kb.builder().build_iconst_i64(1); |
| 196 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* two_i64 = kb.builder().build_iconst_i64(2); |
| 197 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* eight_i64 = kb.builder().build_iconst_i64(8); |
| 198 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* mask_eight = kb.builder().build_iconst_i64(~int64_t(7)); |
| 199 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vec_n = kb.builder().build_and(n, mask_eight); |
| 200 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(v_head, {zero_i64}); |
| 201 | |||
| 202 | // Vector loop: 8 elements per iteration | ||
| 203 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(v_head); |
| 204 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv_v = kb.builder().add_block_param(v_head, Type::i64()); |
| 205 | 8 | kb.position_at_end(v_head); | |
| 206 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* v_cond = kb.builder().build_slt(iv_v, vec_n); |
| 207 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(v_cond, v_body, {}, s_head, {iv_v}); |
| 208 | |||
| 209 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(v_body); |
| 210 | 8 | kb.position_at_end(v_body); | |
| 211 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* byte_off = kb.builder().build_shl(iv_v, two_i64); |
| 212 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* px = kb.builder().build_add(x, byte_off); |
| 213 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* pv = kb.builder().build_add(v, byte_off); |
| 214 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* py = kb.builder().build_add(out, byte_off); |
| 215 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vx = kb.vload_f32x8(px); |
| 216 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vv = kb.vload_f32x8(pv); |
| 217 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vy = kb.vmul(vx, vv); |
| 218 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.vstore_f32x8(py, vy); |
| 219 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv_v = kb.builder().build_add(iv_v, eight_i64); |
| 220 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(v_head, {next_iv_v}); |
| 221 | |||
| 222 | // Scalar remainder loop | ||
| 223 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(s_head); |
| 224 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv_s = kb.builder().add_block_param(s_head, Type::i64()); |
| 225 | 8 | kb.position_at_end(s_head); | |
| 226 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* s_cond = kb.builder().build_slt(iv_s, n); |
| 227 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(s_cond, s_body, exit); |
| 228 | |||
| 229 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(s_body); |
| 230 | 8 | kb.position_at_end(s_body); | |
| 231 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* xi = kb.load_f32_indexed(x, iv_s, 4, 0); |
| 232 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vi = kb.load_f32_indexed(v, iv_s, 4, 0); |
| 233 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* yi = kb.mul(xi, vi); |
| 234 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.store_f32_indexed(out, iv_s, yi, 4, 0); |
| 235 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv_s = kb.builder().build_add(iv_s, one_i64); |
| 236 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(s_head, {next_iv_s}); |
| 237 | |||
| 238 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(exit); |
| 239 | 8 | kb.position_at_end(exit); | |
| 240 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_ret_void(); |
| 241 | |||
| 242 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | return jit.compile(mod, "broadcast_mul"); |
| 243 | 8 | } | |
| 244 | |||
| 245 | // ── 2. Vectorized AdaLN Modulate (AVX2 8-wide + FMA + scalar remainder) ───── | ||
| 246 | 8 | static KernelFunction build_adaln_modulate(KernelJit& jit) { | |
| 247 | 8 | Module mod("mod_adaln_modulate"); | |
| 248 |
7/14✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8 times.
✗ Branch 13 not taken.
|
8 | Function* fn = mod.create_function("fused_adaln_modulate", Type::void_type(), { |
| 249 | 40 | Type::ptr(), Type::ptr(), Type::ptr(), Type::ptr(), Type::i64() | |
| 250 | }); | ||
| 251 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | KernelBuilder kb(mod, fn); |
| 252 | |||
| 253 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* entry = kb.builder().append_block("entry"); |
| 254 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* x = kb.builder().add_block_param(entry, Type::ptr()); |
| 255 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* scale = kb.builder().add_block_param(entry, Type::ptr()); |
| 256 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* shift = kb.builder().add_block_param(entry, Type::ptr()); |
| 257 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* out = kb.builder().add_block_param(entry, Type::ptr()); |
| 258 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* n = kb.builder().add_block_param(entry, Type::i64()); |
| 259 | |||
| 260 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* v_head = kb.builder().create_block("v_head"); |
| 261 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* v_body = kb.builder().create_block("v_body"); |
| 262 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* s_head = kb.builder().create_block("s_head"); |
| 263 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* s_body = kb.builder().create_block("s_body"); |
| 264 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* exit = kb.builder().create_block("exit"); |
| 265 | |||
| 266 | 8 | kb.position_at_end(entry); | |
| 267 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* zero_i64 = kb.builder().build_iconst_i64(0); |
| 268 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* one_i64 = kb.builder().build_iconst_i64(1); |
| 269 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* two_i64 = kb.builder().build_iconst_i64(2); |
| 270 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* eight_i64 = kb.builder().build_iconst_i64(8); |
| 271 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* one_f32 = kb.builder().build_fconst_f32(1.0f); |
| 272 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* v_one = kb.vbroadcast(Type::f32x8(), one_f32); |
| 273 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* mask_eight = kb.builder().build_iconst_i64(~int64_t(7)); |
| 274 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vec_n = kb.builder().build_and(n, mask_eight); |
| 275 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(v_head, {zero_i64}); |
| 276 | |||
| 277 | // Vector loop: modulated = x * (1.0f + scale) + shift via AVX2 FMA | ||
| 278 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(v_head); |
| 279 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv_v = kb.builder().add_block_param(v_head, Type::i64()); |
| 280 | 8 | kb.position_at_end(v_head); | |
| 281 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* v_cond = kb.builder().build_slt(iv_v, vec_n); |
| 282 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(v_cond, v_body, {}, s_head, {iv_v}); |
| 283 | |||
| 284 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(v_body); |
| 285 | 8 | kb.position_at_end(v_body); | |
| 286 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* byte_off = kb.builder().build_shl(iv_v, two_i64); |
| 287 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* px = kb.builder().build_add(x, byte_off); |
| 288 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* pscale = kb.builder().build_add(scale, byte_off); |
| 289 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* pshift = kb.builder().build_add(shift, byte_off); |
| 290 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* py = kb.builder().build_add(out, byte_off); |
| 291 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vx = kb.vload_f32x8(px); |
| 292 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vscale = kb.vload_f32x8(pscale); |
| 293 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vshift = kb.vload_f32x8(pshift); |
| 294 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* v_1_plus_scale = kb.vadd(v_one, vscale); |
| 295 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vy = kb.vfma(vx, v_1_plus_scale, vshift); |
| 296 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.vstore_f32x8(py, vy); |
| 297 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv_v = kb.builder().build_add(iv_v, eight_i64); |
| 298 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(v_head, {next_iv_v}); |
| 299 | |||
| 300 | // Scalar remainder loop | ||
| 301 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(s_head); |
| 302 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv_s = kb.builder().add_block_param(s_head, Type::i64()); |
| 303 | 8 | kb.position_at_end(s_head); | |
| 304 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* s_cond = kb.builder().build_slt(iv_s, n); |
| 305 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(s_cond, s_body, exit); |
| 306 | |||
| 307 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(s_body); |
| 308 | 8 | kb.position_at_end(s_body); | |
| 309 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* xi = kb.load_f32_indexed(x, iv_s, 4, 0); |
| 310 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* scale_i = kb.load_f32_indexed(scale, iv_s, 4, 0); |
| 311 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* shift_i = kb.load_f32_indexed(shift, iv_s, 4, 0); |
| 312 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* one_plus_scale = kb.add(one_f32, scale_i); |
| 313 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* yi = kb.builder().build_fma_f32(xi, one_plus_scale, shift_i); |
| 314 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.store_f32_indexed(out, iv_s, yi, 4, 0); |
| 315 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv_s = kb.builder().build_add(iv_s, one_i64); |
| 316 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(s_head, {next_iv_s}); |
| 317 | |||
| 318 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(exit); |
| 319 | 8 | kb.position_at_end(exit); | |
| 320 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_ret_void(); |
| 321 | |||
| 322 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | return jit.compile(mod, "fused_adaln_modulate"); |
| 323 | 8 | } | |
| 324 | |||
| 325 | // ── 3. Vectorized RMSNorm Row (AVX2 FMA reduction + AVX2 affine scale) ───── | ||
| 326 | 8 | static KernelFunction build_rms_norm_row(KernelJit& jit) { | |
| 327 | 8 | Module mod("mod_rms_norm_row"); | |
| 328 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | mod.add_external_symbol("rsqrtf"); |
| 329 |
8/16✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 8 times.
✗ Branch 15 not taken.
|
8 | Function* fn = mod.create_function("rms_norm_row", Type::void_type(), { |
| 330 | 48 | Type::ptr(), Type::ptr(), Type::ptr(), Type::i64(), Type::f32(), Type::f32() | |
| 331 | }); | ||
| 332 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | KernelBuilder kb(mod, fn); |
| 333 | |||
| 334 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* entry = kb.builder().append_block("entry"); |
| 335 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* x = kb.builder().add_block_param(entry, Type::ptr()); |
| 336 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* gamma = kb.builder().add_block_param(entry, Type::ptr()); |
| 337 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* y = kb.builder().add_block_param(entry, Type::ptr()); |
| 338 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* d = kb.builder().add_block_param(entry, Type::i64()); |
| 339 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* eps = kb.builder().add_block_param(entry, Type::f32()); |
| 340 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* inv_d = kb.builder().add_block_param(entry, Type::f32()); |
| 341 | |||
| 342 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_vhead = kb.builder().create_block("l1_vhead"); |
| 343 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_vbody = kb.builder().create_block("l1_vbody"); |
| 344 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_vexit = kb.builder().create_block("l1_vexit"); |
| 345 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_shead = kb.builder().create_block("l1_shead"); |
| 346 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_sbody = kb.builder().create_block("l1_sbody"); |
| 347 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_exit = kb.builder().create_block("l1_exit"); |
| 348 | |||
| 349 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_vhead = kb.builder().create_block("l2_vhead"); |
| 350 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_vbody = kb.builder().create_block("l2_vbody"); |
| 351 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_shead = kb.builder().create_block("l2_shead"); |
| 352 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_sbody = kb.builder().create_block("l2_sbody"); |
| 353 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_exit = kb.builder().create_block("l2_exit"); |
| 354 | |||
| 355 | 8 | kb.position_at_end(entry); | |
| 356 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* zero_i64 = kb.builder().build_iconst_i64(0); |
| 357 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* one_i64 = kb.builder().build_iconst_i64(1); |
| 358 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* two_i64 = kb.builder().build_iconst_i64(2); |
| 359 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* eight_i64 = kb.builder().build_iconst_i64(8); |
| 360 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* zero_f32 = kb.builder().build_fconst_f32(0.0f); |
| 361 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vzero = kb.vzero(Type::f32x8()); |
| 362 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* mask_eight = kb.builder().build_iconst_i64(~int64_t(7)); |
| 363 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vec_d = kb.builder().build_and(d, mask_eight); |
| 364 | |||
| 365 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* has_vec = kb.builder().build_sge(d, eight_i64); |
| 366 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(has_vec, l1_vhead, {zero_i64, vzero}, l1_shead, {zero_i64, zero_f32}); |
| 367 | |||
| 368 | // Pass 1: Vector loop accumulating sum_sq via vfma | ||
| 369 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_vhead); |
| 370 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv1_v = kb.builder().add_block_param(l1_vhead, Type::i64()); |
| 371 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vsum = kb.builder().add_block_param(l1_vhead, Type::f32x8()); |
| 372 | 8 | kb.position_at_end(l1_vhead); | |
| 373 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond1_v = kb.builder().build_slt(iv1_v, vec_d); |
| 374 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond1_v, l1_vbody, {}, l1_vexit, {iv1_v, vsum}); |
| 375 | |||
| 376 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_vbody); |
| 377 | 8 | kb.position_at_end(l1_vbody); | |
| 378 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* byte_off1 = kb.builder().build_shl(iv1_v, two_i64); |
| 379 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* px1 = kb.builder().build_add(x, byte_off1); |
| 380 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vx1 = kb.vload_f32x8(px1); |
| 381 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_vsum = kb.vfma(vx1, vx1, vsum); |
| 382 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv1_v = kb.builder().build_add(iv1_v, eight_i64); |
| 383 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l1_vhead, {next_iv1_v, next_vsum}); |
| 384 | |||
| 385 | // Pass 1: Vector exit -> horizontal vector reduction via y scratch buffer | ||
| 386 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_vexit); |
| 387 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* exit_iv1 = kb.builder().add_block_param(l1_vexit, Type::i64()); |
| 388 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* exit_vsum = kb.builder().add_block_param(l1_vexit, Type::f32x8()); |
| 389 | 8 | kb.position_at_end(l1_vexit); | |
| 390 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* init_scalar_sum = reduce_vsum8(kb, y, exit_vsum); |
| 391 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l1_shead, {exit_iv1, init_scalar_sum}); |
| 392 | |||
| 393 | // Pass 1: Scalar remainder loop | ||
| 394 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_shead); |
| 395 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv1_s = kb.builder().add_block_param(l1_shead, Type::i64()); |
| 396 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* sum_sq = kb.builder().add_block_param(l1_shead, Type::f32()); |
| 397 | 8 | kb.position_at_end(l1_shead); | |
| 398 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond1_s = kb.builder().build_slt(iv1_s, d); |
| 399 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond1_s, l1_sbody, {}, l1_exit, {sum_sq}); |
| 400 | |||
| 401 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_sbody); |
| 402 | 8 | kb.position_at_end(l1_sbody); | |
| 403 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* xi = kb.load_f32_indexed(x, iv1_s, 4, 0); |
| 404 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* xi_sq = kb.mul(xi, xi); |
| 405 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_sum = kb.add(sum_sq, xi_sq); |
| 406 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv1_s = kb.builder().build_add(iv1_s, one_i64); |
| 407 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l1_shead, {next_iv1_s, next_sum}); |
| 408 | |||
| 409 | // Pass 1 exit -> compute rrms = rsqrtf(mean_sq + eps) | ||
| 410 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_exit); |
| 411 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* final_sum = kb.builder().add_block_param(l1_exit, Type::f32()); |
| 412 | 8 | kb.position_at_end(l1_exit); | |
| 413 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* mean_sq = kb.mul(final_sum, inv_d); |
| 414 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* denom = kb.add(mean_sq, eps); |
| 415 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | Value* rrms = kb.builder().build_call("rsqrtf", Type::f32(), {denom}); |
| 416 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vrrms = kb.vbroadcast(Type::f32x8(), rrms); |
| 417 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l2_vhead, {zero_i64, rrms, vrrms}); |
| 418 | |||
| 419 | // Pass 2: Vector loop y[i] = x[i] * gamma[i] * rrms | ||
| 420 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_vhead); |
| 421 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv2_v = kb.builder().add_block_param(l2_vhead, Type::i64()); |
| 422 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* rrms_val = kb.builder().add_block_param(l2_vhead, Type::f32()); |
| 423 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vrrms_val = kb.builder().add_block_param(l2_vhead, Type::f32x8()); |
| 424 | 8 | kb.position_at_end(l2_vhead); | |
| 425 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond2_v = kb.builder().build_slt(iv2_v, vec_d); |
| 426 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond2_v, l2_vbody, {}, l2_shead, {iv2_v, rrms_val}); |
| 427 | |||
| 428 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_vbody); |
| 429 | 8 | kb.position_at_end(l2_vbody); | |
| 430 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* byte_off2 = kb.builder().build_shl(iv2_v, two_i64); |
| 431 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* px2 = kb.builder().build_add(x, byte_off2); |
| 432 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* pg2 = kb.builder().build_add(gamma, byte_off2); |
| 433 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* py2 = kb.builder().build_add(y, byte_off2); |
| 434 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vx2 = kb.vload_f32x8(px2); |
| 435 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vg2 = kb.vload_f32x8(pg2); |
| 436 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vxg = kb.vmul(vx2, vg2); |
| 437 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vy = kb.vmul(vxg, vrrms_val); |
| 438 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.vstore_f32x8(py2, vy); |
| 439 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv2_v = kb.builder().build_add(iv2_v, eight_i64); |
| 440 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l2_vhead, {next_iv2_v, rrms_val, vrrms_val}); |
| 441 | |||
| 442 | // Pass 2: Scalar remainder loop | ||
| 443 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_shead); |
| 444 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv2_s = kb.builder().add_block_param(l2_shead, Type::i64()); |
| 445 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* rrms_scalar = kb.builder().add_block_param(l2_shead, Type::f32()); |
| 446 | 8 | kb.position_at_end(l2_shead); | |
| 447 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond2_s = kb.builder().build_slt(iv2_s, d); |
| 448 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond2_s, l2_sbody, l2_exit); |
| 449 | |||
| 450 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_sbody); |
| 451 | 8 | kb.position_at_end(l2_sbody); | |
| 452 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* x2 = kb.load_f32_indexed(x, iv2_s, 4, 0); |
| 453 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* g2 = kb.load_f32_indexed(gamma, iv2_s, 4, 0); |
| 454 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* xg2 = kb.mul(x2, g2); |
| 455 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* out_val = kb.mul(xg2, rrms_scalar); |
| 456 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.store_f32_indexed(y, iv2_s, out_val, 4, 0); |
| 457 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv2_s = kb.builder().build_add(iv2_s, one_i64); |
| 458 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l2_shead, {next_iv2_s, rrms_scalar}); |
| 459 | |||
| 460 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_exit); |
| 461 | 8 | kb.position_at_end(l2_exit); | |
| 462 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_ret_void(); |
| 463 | |||
| 464 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | return jit.compile(mod, "rms_norm_row"); |
| 465 | 8 | } | |
| 466 | |||
| 467 | // ── 4. Vectorized LayerNorm Row (AVX2 mean + var + affine) ────────────────── | ||
| 468 | 8 | static KernelFunction build_layernorm_row(KernelJit& jit) { | |
| 469 | 8 | Module mod("mod_layernorm_row"); | |
| 470 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | mod.add_external_symbol("rsqrtf"); |
| 471 |
9/18✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 8 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 8 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 8 times.
✗ Branch 17 not taken.
|
8 | Function* fn = mod.create_function("layernorm_row", Type::void_type(), { |
| 472 | 56 | Type::ptr(), Type::ptr(), Type::ptr(), Type::ptr(), Type::i64(), Type::f32(), Type::f32() | |
| 473 | }); | ||
| 474 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | KernelBuilder kb(mod, fn); |
| 475 | |||
| 476 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* entry = kb.builder().append_block("entry"); |
| 477 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* x = kb.builder().add_block_param(entry, Type::ptr()); |
| 478 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* gamma = kb.builder().add_block_param(entry, Type::ptr()); |
| 479 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* beta = kb.builder().add_block_param(entry, Type::ptr()); |
| 480 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* y = kb.builder().add_block_param(entry, Type::ptr()); |
| 481 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* d = kb.builder().add_block_param(entry, Type::i64()); |
| 482 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* eps = kb.builder().add_block_param(entry, Type::f32()); |
| 483 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* inv_d = kb.builder().add_block_param(entry, Type::f32()); |
| 484 | |||
| 485 | // Pass 1 blocks (mean) | ||
| 486 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_vhead = kb.builder().create_block("ln_l1_vhead"); |
| 487 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_vbody = kb.builder().create_block("ln_l1_vbody"); |
| 488 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_vexit = kb.builder().create_block("ln_l1_vexit"); |
| 489 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_shead = kb.builder().create_block("ln_l1_shead"); |
| 490 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_sbody = kb.builder().create_block("ln_l1_sbody"); |
| 491 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l1_exit = kb.builder().create_block("ln_l1_exit"); |
| 492 | |||
| 493 | // Pass 2 blocks (variance) | ||
| 494 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_vhead = kb.builder().create_block("ln_l2_vhead"); |
| 495 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_vbody = kb.builder().create_block("ln_l2_vbody"); |
| 496 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_vexit = kb.builder().create_block("ln_l2_vexit"); |
| 497 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_shead = kb.builder().create_block("ln_l2_shead"); |
| 498 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_sbody = kb.builder().create_block("ln_l2_sbody"); |
| 499 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l2_exit = kb.builder().create_block("ln_l2_exit"); |
| 500 | |||
| 501 | // Pass 3 blocks (affine) | ||
| 502 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l3_vhead = kb.builder().create_block("ln_l3_vhead"); |
| 503 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l3_vbody = kb.builder().create_block("ln_l3_vbody"); |
| 504 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l3_shead = kb.builder().create_block("ln_l3_shead"); |
| 505 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l3_sbody = kb.builder().create_block("ln_l3_sbody"); |
| 506 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | BasicBlock* l3_exit = kb.builder().create_block("ln_l3_exit"); |
| 507 | |||
| 508 | 8 | kb.position_at_end(entry); | |
| 509 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* zero_i64 = kb.builder().build_iconst_i64(0); |
| 510 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* one_i64 = kb.builder().build_iconst_i64(1); |
| 511 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* two_i64 = kb.builder().build_iconst_i64(2); |
| 512 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* eight_i64 = kb.builder().build_iconst_i64(8); |
| 513 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* zero_f32 = kb.builder().build_fconst_f32(0.0f); |
| 514 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vzero = kb.vzero(Type::f32x8()); |
| 515 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* mask_eight = kb.builder().build_iconst_i64(~int64_t(7)); |
| 516 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vec_d = kb.builder().build_and(d, mask_eight); |
| 517 | |||
| 518 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* has_vec = kb.builder().build_sge(d, eight_i64); |
| 519 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(has_vec, l1_vhead, {zero_i64, vzero}, l1_shead, {zero_i64, zero_f32}); |
| 520 | |||
| 521 | // Pass 1: Vector sum | ||
| 522 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_vhead); |
| 523 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv1_v = kb.builder().add_block_param(l1_vhead, Type::i64()); |
| 524 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vsum1 = kb.builder().add_block_param(l1_vhead, Type::f32x8()); |
| 525 | 8 | kb.position_at_end(l1_vhead); | |
| 526 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond1_v = kb.builder().build_slt(iv1_v, vec_d); |
| 527 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond1_v, l1_vbody, {}, l1_vexit, {iv1_v, vsum1}); |
| 528 | |||
| 529 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_vbody); |
| 530 | 8 | kb.position_at_end(l1_vbody); | |
| 531 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* byte_off1 = kb.builder().build_shl(iv1_v, two_i64); |
| 532 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* px1 = kb.builder().build_add(x, byte_off1); |
| 533 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vx1 = kb.vload_f32x8(px1); |
| 534 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_vsum1 = kb.vadd(vsum1, vx1); |
| 535 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv1_v = kb.builder().build_add(iv1_v, eight_i64); |
| 536 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l1_vhead, {next_iv1_v, next_vsum1}); |
| 537 | |||
| 538 | // Pass 1: Vector exit -> horizontal sum | ||
| 539 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_vexit); |
| 540 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* exit_iv1 = kb.builder().add_block_param(l1_vexit, Type::i64()); |
| 541 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* exit_vsum1 = kb.builder().add_block_param(l1_vexit, Type::f32x8()); |
| 542 | 8 | kb.position_at_end(l1_vexit); | |
| 543 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* init_sum1 = reduce_vsum8(kb, y, exit_vsum1); |
| 544 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l1_shead, {exit_iv1, init_sum1}); |
| 545 | |||
| 546 | // Pass 1: Scalar remainder | ||
| 547 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_shead); |
| 548 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv1_s = kb.builder().add_block_param(l1_shead, Type::i64()); |
| 549 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* sum1 = kb.builder().add_block_param(l1_shead, Type::f32()); |
| 550 | 8 | kb.position_at_end(l1_shead); | |
| 551 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond1_s = kb.builder().build_slt(iv1_s, d); |
| 552 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond1_s, l1_sbody, {}, l1_exit, {sum1}); |
| 553 | |||
| 554 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_sbody); |
| 555 | 8 | kb.position_at_end(l1_sbody); | |
| 556 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* xi1 = kb.load_f32_indexed(x, iv1_s, 4, 0); |
| 557 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_sum1 = kb.add(sum1, xi1); |
| 558 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv1_s = kb.builder().build_add(iv1_s, one_i64); |
| 559 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l1_shead, {next_iv1_s, next_sum1}); |
| 560 | |||
| 561 | // Pass 1: Mean computed -> enter Pass 2 | ||
| 562 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l1_exit); |
| 563 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* final_sum1 = kb.builder().add_block_param(l1_exit, Type::f32()); |
| 564 | 8 | kb.position_at_end(l1_exit); | |
| 565 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* mean = kb.mul(final_sum1, inv_d); |
| 566 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vmean = kb.vbroadcast(Type::f32x8(), mean); |
| 567 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(has_vec, l2_vhead, {zero_i64, vzero, mean, vmean}, l2_shead, {zero_i64, zero_f32, mean}); |
| 568 | |||
| 569 | // Pass 2: Vector sum of squared deviations | ||
| 570 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_vhead); |
| 571 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv2_v = kb.builder().add_block_param(l2_vhead, Type::i64()); |
| 572 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vsum2 = kb.builder().add_block_param(l2_vhead, Type::f32x8()); |
| 573 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* m2_scalar = kb.builder().add_block_param(l2_vhead, Type::f32()); |
| 574 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vm2 = kb.builder().add_block_param(l2_vhead, Type::f32x8()); |
| 575 | 8 | kb.position_at_end(l2_vhead); | |
| 576 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond2_v = kb.builder().build_slt(iv2_v, vec_d); |
| 577 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond2_v, l2_vbody, {}, l2_vexit, {iv2_v, vsum2, m2_scalar}); |
| 578 | |||
| 579 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_vbody); |
| 580 | 8 | kb.position_at_end(l2_vbody); | |
| 581 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* byte_off2 = kb.builder().build_shl(iv2_v, two_i64); |
| 582 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* px2 = kb.builder().build_add(x, byte_off2); |
| 583 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vx2 = kb.vload_f32x8(px2); |
| 584 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vdev = kb.vsub(vx2, vm2); |
| 585 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_vsum2 = kb.vfma(vdev, vdev, vsum2); |
| 586 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv2_v = kb.builder().build_add(iv2_v, eight_i64); |
| 587 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l2_vhead, {next_iv2_v, next_vsum2, m2_scalar, vm2}); |
| 588 | |||
| 589 | // Pass 2: Vector exit -> horizontal sum | ||
| 590 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_vexit); |
| 591 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* exit_iv2 = kb.builder().add_block_param(l2_vexit, Type::i64()); |
| 592 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* exit_vsum2 = kb.builder().add_block_param(l2_vexit, Type::f32x8()); |
| 593 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* m2_out = kb.builder().add_block_param(l2_vexit, Type::f32()); |
| 594 | 8 | kb.position_at_end(l2_vexit); | |
| 595 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* init_sumsq = reduce_vsum8(kb, y, exit_vsum2); |
| 596 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l2_shead, {exit_iv2, init_sumsq, m2_out}); |
| 597 | |||
| 598 | // Pass 2: Scalar remainder | ||
| 599 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_shead); |
| 600 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv2_s = kb.builder().add_block_param(l2_shead, Type::i64()); |
| 601 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* sumsq = kb.builder().add_block_param(l2_shead, Type::f32()); |
| 602 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* m2_s = kb.builder().add_block_param(l2_shead, Type::f32()); |
| 603 | 8 | kb.position_at_end(l2_shead); | |
| 604 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond2_s = kb.builder().build_slt(iv2_s, d); |
| 605 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond2_s, l2_sbody, {}, l2_exit, {sumsq, m2_s}); |
| 606 | |||
| 607 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_sbody); |
| 608 | 8 | kb.position_at_end(l2_sbody); | |
| 609 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* xi2 = kb.load_f32_indexed(x, iv2_s, 4, 0); |
| 610 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* diff2 = kb.sub(xi2, m2_s); |
| 611 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* diff_sq2 = kb.mul(diff2, diff2); |
| 612 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_sumsq = kb.add(sumsq, diff_sq2); |
| 613 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv2_s = kb.builder().build_add(iv2_s, one_i64); |
| 614 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l2_shead, {next_iv2_s, next_sumsq, m2_s}); |
| 615 | |||
| 616 | // Pass 2 exit -> compute rstd | ||
| 617 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l2_exit); |
| 618 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* final_sumsq = kb.builder().add_block_param(l2_exit, Type::f32()); |
| 619 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* final_mean = kb.builder().add_block_param(l2_exit, Type::f32()); |
| 620 | 8 | kb.position_at_end(l2_exit); | |
| 621 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* var = kb.mul(final_sumsq, inv_d); |
| 622 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* var_eps = kb.add(var, eps); |
| 623 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | Value* rstd = kb.builder().build_call("rsqrtf", Type::f32(), {var_eps}); |
| 624 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vmean3 = kb.vbroadcast(Type::f32x8(), final_mean); |
| 625 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vrstd3 = kb.vbroadcast(Type::f32x8(), rstd); |
| 626 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l3_vhead, {zero_i64, final_mean, rstd, vmean3, vrstd3}); |
| 627 | |||
| 628 | // Pass 3: Vector affine y[i] = gamma[i] * ((x[i] - mean) * rstd) + beta[i] | ||
| 629 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l3_vhead); |
| 630 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv3_v = kb.builder().add_block_param(l3_vhead, Type::i64()); |
| 631 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* m3_s = kb.builder().add_block_param(l3_vhead, Type::f32()); |
| 632 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* r3_s = kb.builder().add_block_param(l3_vhead, Type::f32()); |
| 633 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vm3 = kb.builder().add_block_param(l3_vhead, Type::f32x8()); |
| 634 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vr3 = kb.builder().add_block_param(l3_vhead, Type::f32x8()); |
| 635 | 8 | kb.position_at_end(l3_vhead); | |
| 636 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond3_v = kb.builder().build_slt(iv3_v, vec_d); |
| 637 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond3_v, l3_vbody, {}, l3_shead, {iv3_v, m3_s, r3_s}); |
| 638 | |||
| 639 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l3_vbody); |
| 640 | 8 | kb.position_at_end(l3_vbody); | |
| 641 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* byte_off3 = kb.builder().build_shl(iv3_v, two_i64); |
| 642 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* px3 = kb.builder().build_add(x, byte_off3); |
| 643 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* pg3 = kb.builder().build_add(gamma, byte_off3); |
| 644 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* pb3 = kb.builder().build_add(beta, byte_off3); |
| 645 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* py3 = kb.builder().build_add(y, byte_off3); |
| 646 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vx3 = kb.vload_f32x8(px3); |
| 647 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vg3 = kb.vload_f32x8(pg3); |
| 648 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vb3 = kb.vload_f32x8(pb3); |
| 649 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vdiff3 = kb.vsub(vx3, vm3); |
| 650 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vxhat3 = kb.vmul(vdiff3, vr3); |
| 651 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* vy3 = kb.vfma(vg3, vxhat3, vb3); |
| 652 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.vstore_f32x8(py3, vy3); |
| 653 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv3_v = kb.builder().build_add(iv3_v, eight_i64); |
| 654 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l3_vhead, {next_iv3_v, m3_s, r3_s, vm3, vr3}); |
| 655 | |||
| 656 | // Pass 3: Scalar remainder | ||
| 657 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l3_shead); |
| 658 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* iv3_s = kb.builder().add_block_param(l3_shead, Type::i64()); |
| 659 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* m3_sc = kb.builder().add_block_param(l3_shead, Type::f32()); |
| 660 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* r3_sc = kb.builder().add_block_param(l3_shead, Type::f32()); |
| 661 | 8 | kb.position_at_end(l3_shead); | |
| 662 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* cond3_s = kb.builder().build_slt(iv3_s, d); |
| 663 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br_if(cond3_s, l3_sbody, l3_exit); |
| 664 | |||
| 665 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l3_sbody); |
| 666 | 8 | kb.position_at_end(l3_sbody); | |
| 667 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* x3 = kb.load_f32_indexed(x, iv3_s, 4, 0); |
| 668 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* g3 = kb.load_f32_indexed(gamma, iv3_s, 4, 0); |
| 669 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* b3 = kb.load_f32_indexed(beta, iv3_s, 4, 0); |
| 670 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* diff3 = kb.sub(x3, m3_sc); |
| 671 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* xhat3 = kb.mul(diff3, r3_sc); |
| 672 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* y3 = kb.builder().build_fma_f32(g3, xhat3, b3); |
| 673 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.store_f32_indexed(y, iv3_s, y3, 4, 0); |
| 674 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | Value* next_iv3_s = kb.builder().build_add(iv3_s, one_i64); |
| 675 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_br(l3_shead, {next_iv3_s, m3_sc, r3_sc}); |
| 676 | |||
| 677 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | fn->append_block(l3_exit); |
| 678 | 8 | kb.position_at_end(l3_exit); | |
| 679 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kb.builder().build_ret_void(); |
| 680 | |||
| 681 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | return jit.compile(mod, "layernorm_row"); |
| 682 | 8 | } | |
| 683 | |||
| 684 | // ── Kernel Initialization & JIT Engine Management ─────────────────────────── | ||
| 685 | 2273 | static const JitKernels& get_kernels() { | |
| 686 |
3/4✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2265 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
|
2273 | static JitKernels kernels; |
| 687 | static std::once_flag init_flag; | ||
| 688 | 2281 | std::call_once(init_flag, [] { | |
| 689 | try { | ||
| 690 | 8 | KernelOptions opts; | |
| 691 | 8 | opts.enable_optimizations = true; | |
| 692 | 8 | opts.enable_avx2 = true; | |
| 693 | 8 | opts.enable_fma = true; | |
| 694 | 8 | opts.enable_vectorize = true; | |
| 695 | 8 | opts.enable_unroll = true; | |
| 696 | 8 | opts.unroll_factor = 4; | |
| 697 | 8 | opts.enable_fp_reassociation = true; | |
| 698 | |||
| 699 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | KernelJit jit(opts); |
| 700 | |||
| 701 | // (1. SwiGLU runs the reference loop; see swiglu_forward below.) | ||
| 702 | |||
| 703 | // 2. Vectorized AdaLN Modulate | ||
| 704 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | KernelFunction k_adaln = build_adaln_modulate(jit); |
| 705 | 8 | kernels.adaln_fn = k_adaln.as<JitKernels::AdaLNModulateFn>(); | |
| 706 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kernels.engines.push_back(k_adaln.engine()); |
| 707 | |||
| 708 | // 3. Vectorized Broadcast Mul | ||
| 709 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | KernelFunction k_bmul = build_broadcast_mul(jit); |
| 710 | 8 | kernels.broadcast_mul_fn = k_bmul.as<JitKernels::BroadcastMulFn>(); | |
| 711 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kernels.engines.push_back(k_bmul.engine()); |
| 712 | |||
| 713 | // 4. Vectorized RMSNorm row | ||
| 714 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | KernelFunction k_rmsnorm = build_rms_norm_row(jit); |
| 715 | 8 | kernels.rms_norm_row_fn = k_rmsnorm.as<JitKernels::RmsNormRowFn>(); | |
| 716 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kernels.engines.push_back(k_rmsnorm.engine()); |
| 717 | |||
| 718 | // 5. Vectorized LayerNorm row | ||
| 719 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | KernelFunction k_ln = build_layernorm_row(jit); |
| 720 | 8 | kernels.layernorm_row_fn = k_ln.as<JitKernels::LayerNormRowFn>(); | |
| 721 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kernels.engines.push_back(k_ln.engine()); |
| 722 | |||
| 723 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
16 | kernels.available = (kernels.adaln_fn != nullptr && |
| 724 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | kernels.broadcast_mul_fn != nullptr && |
| 725 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | kernels.rms_norm_row_fn != nullptr && |
| 726 | 8 | kernels.layernorm_row_fn != nullptr); | |
| 727 |
0/2✗ Branch 0 not taken.
✗ Branch 1 not taken.
|
8 | } catch (const std::exception& e) { |
| 728 | ✗ | std::cerr << "brotensor: Brass JIT initialization failed: " << e.what() << "\n"; | |
| 729 | ✗ | kernels.available = false; | |
| 730 | ✗ | } | |
| 731 | 8 | }); | |
| 732 | 2273 | return kernels; | |
| 733 | } | ||
| 734 | |||
| 735 | } // namespace | ||
| 736 | |||
| 737 | 1262 | bool is_jit_available() { | |
| 738 | 1262 | return get_kernels().available; | |
| 739 | } | ||
| 740 | |||
| 741 | 254 | void rms_norm_forward(const float* X, const float* gamma, float eps, float* Y, int B, int D) { | |
| 742 | 254 | const auto& k = get_kernels(); | |
| 743 |
2/4✓ Branch 0 taken 254 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 254 times.
|
254 | if (!k.available || k.rms_norm_row_fn == nullptr) { |
| 744 | ✗ | ref::rms_norm(X, gamma, eps, Y, B, D); | |
| 745 | ✗ | return; | |
| 746 | } | ||
| 747 | |||
| 748 | 254 | const float inv_D = 1.0f / static_cast<float>(D); | |
| 749 | |||
| 750 | // Single-thread vectorized loop executes in sub-microsecond latency. | ||
| 751 | // Multi-threaded pool dispatch only when workload amortizes thread wake-up overhead. | ||
| 752 |
3/4✓ Branch 0 taken 192 times.
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 192 times.
✗ Branch 3 not taken.
|
254 | if (B > 1 && static_cast<int64_t>(B) * D >= 262144) { |
| 753 | ✗ | detail::cpu::parallel_for(static_cast<std::size_t>(B), [&](std::size_t bi) { | |
| 754 | ✗ | const int b = static_cast<int>(bi); | |
| 755 | ✗ | const float* xr = X + static_cast<std::size_t>(b) * D; | |
| 756 | ✗ | float* yr = Y + static_cast<std::size_t>(b) * D; | |
| 757 | ✗ | k.rms_norm_row_fn(xr, gamma, yr, static_cast<uint64_t>(D), eps, inv_D); | |
| 758 | ✗ | }); | |
| 759 | ✗ | } else { | |
| 760 |
2/2✓ Branch 0 taken 2769 times.
✓ Branch 1 taken 254 times.
|
3023 | for (int b = 0; b < B; ++b) { |
| 761 | 2769 | const float* xr = X + static_cast<std::size_t>(b) * D; | |
| 762 | 2769 | float* yr = Y + static_cast<std::size_t>(b) * D; | |
| 763 | 2769 | k.rms_norm_row_fn(xr, gamma, yr, static_cast<uint64_t>(D), eps, inv_D); | |
| 764 | 2769 | } | |
| 765 | } | ||
| 766 | 254 | } | |
| 767 | |||
| 768 | // SwiGLU has no JIT kernel. brass's MlFusionCompiler::compile_swiglu evaluates | ||
| 769 | // exp(-g) as a degree-5 Taylor polynomial of -g/16 raised to the 16th power; | ||
| 770 | // the polynomial turns negative once -g/16 passes about -3.5, so every gate | ||
| 771 | // above ~55 came out with silu(g) ~ 0 instead of g, and ViT-H SwiGLU FFNs | ||
| 772 | // (DINOv3) put gates well past that. A JIT kernel calling the exact expf per | ||
| 773 | // element measured ~4x slower than this loop (MSVC vectorizes std::exp), so | ||
| 774 | // the row-parallel reference is the kernel. | ||
| 775 | 247 | void swiglu_forward(const float* X, float* Y, int B, int D) { | |
| 776 | 247 | ref::swiglu(X, Y, B, D); | |
| 777 | 247 | } | |
| 778 | |||
| 779 | // modulate and broadcast_mul are one load-op-store stream per element, which | ||
| 780 | // the compiler already vectorizes: on AArch64 clang's NEON loop is unrolled | ||
| 781 | // and runs these about 2x faster than the JIT's f32x8 (a q-register pair per | ||
| 782 | // iteration, not unrolled), so there the row-parallel reference is the kernel. | ||
| 783 | // The norms keep the JIT everywhere: their reductions are where it wins. | ||
| 784 | #if defined(__aarch64__) || defined(_M_ARM64) | ||
| 785 | constexpr bool kJitStreamingKernels = false; | ||
| 786 | #else | ||
| 787 | constexpr bool kJitStreamingKernels = true; | ||
| 788 | #endif | ||
| 789 | |||
| 790 | 246 | void modulate(const float* X, const float* scale, const float* shift, float* Y, int L, int D) { | |
| 791 | 246 | const auto& k = get_kernels(); | |
| 792 | if (!kJitStreamingKernels || !k.available || k.adaln_fn == nullptr) { | ||
| 793 | 246 | ref::modulate(X, scale, shift, Y, L, D); | |
| 794 | 246 | return; | |
| 795 | } | ||
| 796 | |||
| 797 | if (L > 1 && static_cast<int64_t>(L) * D >= 262144) { | ||
| 798 | detail::cpu::parallel_for(static_cast<std::size_t>(L), [&](std::size_t li) { | ||
| 799 | const int l = static_cast<int>(li); | ||
| 800 | const float* xr = X + static_cast<std::size_t>(l) * D; | ||
| 801 | float* yr = Y + static_cast<std::size_t>(l) * D; | ||
| 802 | k.adaln_fn(xr, scale, shift, yr, static_cast<uint64_t>(D)); | ||
| 803 | }); | ||
| 804 | } else { | ||
| 805 | for (int l = 0; l < L; ++l) { | ||
| 806 | const float* xr = X + static_cast<std::size_t>(l) * D; | ||
| 807 | float* yr = Y + static_cast<std::size_t>(l) * D; | ||
| 808 | k.adaln_fn(xr, scale, shift, yr, static_cast<uint64_t>(D)); | ||
| 809 | } | ||
| 810 | } | ||
| 811 | } | ||
| 812 | |||
| 813 | 245 | void broadcast_mul(const float* X, const float* v, float* Y, int L, int D) { | |
| 814 | 245 | const auto& k = get_kernels(); | |
| 815 | if (!kJitStreamingKernels || !k.available || k.broadcast_mul_fn == nullptr) { | ||
| 816 | 245 | ref::broadcast_mul(X, v, Y, L, D); | |
| 817 | 245 | return; | |
| 818 | } | ||
| 819 | |||
| 820 | if (L > 1 && static_cast<int64_t>(L) * D >= 262144) { | ||
| 821 | detail::cpu::parallel_for(static_cast<std::size_t>(L), [&](std::size_t li) { | ||
| 822 | const int l = static_cast<int>(li); | ||
| 823 | const float* xr = X + static_cast<std::size_t>(l) * D; | ||
| 824 | float* yr = Y + static_cast<std::size_t>(l) * D; | ||
| 825 | k.broadcast_mul_fn(xr, v, yr, static_cast<uint64_t>(D)); | ||
| 826 | }); | ||
| 827 | } else { | ||
| 828 | for (int l = 0; l < L; ++l) { | ||
| 829 | const float* xr = X + static_cast<std::size_t>(l) * D; | ||
| 830 | float* yr = Y + static_cast<std::size_t>(l) * D; | ||
| 831 | k.broadcast_mul_fn(xr, v, yr, static_cast<uint64_t>(D)); | ||
| 832 | } | ||
| 833 | } | ||
| 834 | } | ||
| 835 | |||
| 836 | 266 | void layernorm_forward_inference_batched(const float* X, const float* gamma, const float* beta, float eps, float* Y, int R, int D) { | |
| 837 | 266 | const auto& k = get_kernels(); | |
| 838 |
2/4✓ Branch 0 taken 266 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 266 times.
|
266 | if (!k.available || k.layernorm_row_fn == nullptr) { |
| 839 | ✗ | ref::layernorm_forward_inference_batched(X, gamma, beta, eps, Y, R, D); | |
| 840 | ✗ | return; | |
| 841 | } | ||
| 842 | |||
| 843 | 266 | const float inv_D = 1.0f / static_cast<float>(D); | |
| 844 | |||
| 845 |
3/4✓ Branch 0 taken 188 times.
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 188 times.
✗ Branch 3 not taken.
|
266 | if (R > 1 && static_cast<int64_t>(R) * D >= 262144) { |
| 846 | ✗ | detail::cpu::parallel_for(static_cast<std::size_t>(R), [&](std::size_t ri) { | |
| 847 | ✗ | const int r = static_cast<int>(ri); | |
| 848 | ✗ | const float* xr = X + static_cast<std::size_t>(r) * D; | |
| 849 | ✗ | float* yr = Y + static_cast<std::size_t>(r) * D; | |
| 850 | ✗ | k.layernorm_row_fn(xr, gamma, beta, yr, static_cast<uint64_t>(D), eps, inv_D); | |
| 851 | ✗ | }); | |
| 852 | ✗ | } else { | |
| 853 |
2/2✓ Branch 0 taken 3183 times.
✓ Branch 1 taken 266 times.
|
3449 | for (int r = 0; r < R; ++r) { |
| 854 | 3183 | const float* xr = X + static_cast<std::size_t>(r) * D; | |
| 855 | 3183 | float* yr = Y + static_cast<std::size_t>(r) * D; | |
| 856 | 3183 | k.layernorm_row_fn(xr, gamma, beta, yr, static_cast<uint64_t>(D), eps, inv_D); | |
| 857 | 3183 | } | |
| 858 | } | ||
| 859 | 266 | } | |
| 860 | |||
| 861 | } // namespace brotensor::detail::cpu::jit | ||
| 862 | |||
| 863 | #else // !BROTENSOR_HAS_BRASS_JIT | ||
| 864 | |||
| 865 | namespace brotensor::detail::cpu::jit { | ||
| 866 | |||
| 867 | bool is_jit_available() { return false; } | ||
| 868 | void rms_norm_forward(const float* X, const float* gamma, float eps, float* Y, int B, int D) { | ||
| 869 | ref::rms_norm(X, gamma, eps, Y, B, D); | ||
| 870 | } | ||
| 871 | void swiglu_forward(const float* X, float* Y, int B, int D) { | ||
| 872 | ref::swiglu(X, Y, B, D); | ||
| 873 | } | ||
| 874 | void modulate(const float* X, const float* scale, const float* shift, float* Y, int L, int D) { | ||
| 875 | ref::modulate(X, scale, shift, Y, L, D); | ||
| 876 | } | ||
| 877 | void broadcast_mul(const float* X, const float* v, float* Y, int L, int D) { | ||
| 878 | ref::broadcast_mul(X, v, Y, L, D); | ||
| 879 | } | ||
| 880 | void layernorm_forward_inference_batched(const float* X, const float* gamma, const float* beta, float eps, float* Y, int R, int D) { | ||
| 881 | ref::layernorm_forward_inference_batched(X, gamma, beta, eps, Y, R, D); | ||
| 882 | } | ||
| 883 | |||
| 884 | } // namespace brotensor::detail::cpu::jit | ||
| 885 | |||
| 886 | #endif // BROTENSOR_HAS_BRASS_JIT | ||
| 887 |