| 200 | } |
| 201 | |
| 202 | void compute_embedding_node(GraphNode& node, const std::vector<std::unique_ptr<GraphNode>>& nodes, const std::unordered_map<size_t, size_t>& node_index_map) { |
| 203 | const auto& embeddings_buffer = get_input(node, 0, nodes, node_index_map); |
| 204 | const auto& indices_buffer = get_input(node, 1, nodes, node_index_map); |
| 205 | |
| 206 | size_t hidden_dim = embeddings_buffer.shape[1]; |
| 207 | size_t num_indices = indices_buffer.total_size; |
| 208 | size_t vocab_size = embeddings_buffer.original_N > 0 |
| 209 | ? embeddings_buffer.original_N |
| 210 | : embeddings_buffer.shape[0]; |
| 211 | |
| 212 | std::vector<float> indices_float; |
| 213 | const float* indices_ptr; |
| 214 | if (indices_buffer.precision == Precision::FP32) { |
| 215 | indices_ptr = indices_buffer.data_as<float>(); |
| 216 | } else { |
| 217 | indices_float.resize(num_indices); |
| 218 | const int8_t* int_indices = indices_buffer.data_as<int8_t>(); |
| 219 | for (size_t i = 0; i < num_indices; i++) { |
| 220 | indices_float[i] = static_cast<float>(int_indices[i]); |
| 221 | } |
| 222 | indices_ptr = indices_float.data(); |
| 223 | } |
| 224 | |
| 225 | __fp16* output = node.output_buffer.data_as<__fp16>(); |
| 226 | |
| 227 | Precision emb_prec = embeddings_buffer.precision; |
| 228 | if (PrecisionTraits::is_integer(emb_prec) && embeddings_buffer.group_size > 0) { |
| 229 | const int8_t* embeddings = embeddings_buffer.data_as<int8_t>(); |
| 230 | const __fp16* scales = embeddings_buffer.scales_as_fp16(); |
| 231 | size_t group_size = embeddings_buffer.group_size; |
| 232 | size_t num_groups = embeddings_buffer.num_groups; |
| 233 | |
| 234 | static const uint8_t gather_indices[4][16] = { |
| 235 | {0, 1, 2, 3, 16, 17, 18, 19, 32, 33, 34, 35, 48, 49, 50, 51}, // lane 0 |
| 236 | {4, 5, 6, 7, 20, 21, 22, 23, 36, 37, 38, 39, 52, 53, 54, 55}, // lane 1 |
| 237 | {8, 9, 10, 11, 24, 25, 26, 27, 40, 41, 42, 43, 56, 57, 58, 59}, // lane 2 |
| 238 | {12, 13, 14, 15, 28, 29, 30, 31, 44, 45, 46, 47, 60, 61, 62, 63} // lane 3 |
| 239 | }; |
| 240 | |
| 241 | auto load_table = [emb_prec](const int8_t* base) -> int8x16x4_t { |
| 242 | if (emb_prec == Precision::INT4) { |
| 243 | const uint8_t* ubase = reinterpret_cast<const uint8_t*>(base); |
| 244 | int8x16_t low_a, high_a, low_b, high_b; |
| 245 | unpack_int4_as_int8x16x2(ubase, high_a, low_a); |
| 246 | unpack_int4_as_int8x16x2(ubase + 16, high_b, low_b); |
| 247 | return {low_a, high_a, low_b, high_b}; |
| 248 | } |
| 249 | return vld1q_s8_x4(base); |
| 250 | }; |
| 251 | |
| 252 | for (size_t i = 0; i < num_indices; i++) { |
| 253 | size_t idx = static_cast<size_t>(indices_ptr[i]); |
| 254 | if (idx >= vocab_size) { |
| 255 | throw std::runtime_error("Embedding index out of bounds: " + std::to_string(idx) + " >= " + std::to_string(vocab_size)); |
| 256 | } |
| 257 | |
| 258 | size_t block = idx / 4; |
| 259 | size_t lane = idx % 4; |
nothing calls this directly
no test coverage detected