| 332 | template <typename LhsScalar, typename RhsScalar, typename DstScalar, |
| 333 | typename Spec> |
| 334 | void ReferenceMul(const Matrix<LhsScalar>& lhs, const Matrix<RhsScalar>& rhs, |
| 335 | const Spec& spec, Matrix<DstScalar>* dst) { |
| 336 | gemmlowp::ScopedProfilingLabel label("ReferenceMul"); |
| 337 | for (int i = 0; i < lhs.layout.rows; i++) { |
| 338 | for (int j = 0; j < rhs.layout.cols; j++) { |
| 339 | using AccumScalar = typename Spec::AccumScalar; |
| 340 | AccumScalar accum = 0; |
| 341 | for (int k = 0; k < lhs.layout.cols; k++) { |
| 342 | AccumScalar lhs_val = Element(lhs, i, k); |
| 343 | AccumScalar rhs_val = Element(rhs, k, j); |
| 344 | accum += (lhs_val - lhs.zero_point) * (rhs_val - rhs.zero_point); |
| 345 | } |
| 346 | if (spec.bias) { |
| 347 | accum += spec.bias[i]; |
| 348 | } |
| 349 | ApplyMultiplier(spec, i, &accum); |
| 350 | accum += dst->zero_point; |
| 351 | accum = std::min<AccumScalar>(accum, spec.clamp_max); |
| 352 | accum = std::max<AccumScalar>(accum, spec.clamp_min); |
| 353 | *ElementPtr(dst, i, j) = static_cast<DstScalar>(accum); |
| 354 | } |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | // Compile-time dispatch to ReferenceMul. This allows us to statically ensure |
| 359 | // that there is no call to ReferenceMul in the user's binary. |
no test coverage detected