From dbc8f1b0e75f88bb77a4a2b0f00f2abea548da12 Mon Sep 17 00:00:00 2001 From: unmeshna Date: Fri, 31 Jul 2026 03:44:37 -0700 Subject: [PATCH 1/3] Add HiFi int8 optimized LeakyRelu for Xtensa Add an int8 (asym8s) accelerated path using xa_nn_vec_leaky_relu_asym8s_asym8s for HIFI3/HIFI4/HIFI5/HIFI_IQ targets, mirroring the existing int16 optimized path. Falls back to the reference QuantizeLeakyRelu on other targets. --- .../lite/micro/kernels/xtensa/leaky_relu.cc | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tensorflow/lite/micro/kernels/xtensa/leaky_relu.cc b/tensorflow/lite/micro/kernels/xtensa/leaky_relu.cc index c1ed1d6b9bd..eae82c81e99 100644 --- a/tensorflow/lite/micro/kernels/xtensa/leaky_relu.cc +++ b/tensorflow/lite/micro/kernels/xtensa/leaky_relu.cc @@ -71,12 +71,35 @@ TfLiteStatus LeakyReluEval(TfLiteContext* context, TfLiteNode* node) { tflite::micro::GetTensorData(output)); return kTfLiteOk; } break; +#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) + case kTfLiteInt8: { + int err; + const signed char *input_data_ptr; + signed char *output_data_ptr; + const int flat_size = MatchingFlatSize(tflite::micro::GetTensorShape(input), tflite::micro::GetTensorShape(output)); + input_data_ptr = tflite::micro::GetTensorData(input); + output_data_ptr = tflite::micro::GetTensorData(output); + + err = xa_nn_vec_leaky_relu_asym8s_asym8s(output_data_ptr, + input_data_ptr, + data.input_zero_point, + data.output_multiplier_alpha, + data.output_shift_alpha, + data.output_multiplier_identity, + data.output_shift_identity, + data.output_zero_point, + flat_size); + TF_LITE_ENSURE(context, err == 0); + return kTfLiteOk; + } break; +#else case kTfLiteInt8: { QuantizeLeakyRelu(data, input, output); return kTfLiteOk; } break; +#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) case kTfLiteInt16: { -#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) const RuntimeShape& input_shape = tflite::micro::GetTensorShape(input); const RuntimeShape& output_shape = tflite::micro::GetTensorShape(output); const int flat_size = MatchingFlatSize(input_shape, output_shape); @@ -89,7 +112,7 @@ TfLiteStatus LeakyReluEval(TfLiteContext* context, TfLiteNode* node) { if (err != 0) return kTfLiteError; #else QuantizeLeakyRelu(data, input, output); -#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) return kTfLiteOk; } break; default: From a3edca69e8c86026bf0b005d3de1bac91be61e40 Mon Sep 17 00:00:00 2001 From: unmeshna Date: Fri, 31 Jul 2026 03:44:37 -0700 Subject: [PATCH 2/3] Add HiFi int8/int32 optimized Pad for Xtensa Add int8 (xa_nn_pad_8_8) and int32 (xa_nn_pad_32_32) accelerated Pad paths for HIFI targets, complementing the existing int16 path. Inputs with more than 4 dimensions fall back to reference_ops. The int32 path is gated on HIFI3/HIFI4/HIFI5. --- tensorflow/lite/micro/kernels/xtensa/pad.cc | 83 +++++++++++++++++++-- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/tensorflow/lite/micro/kernels/xtensa/pad.cc b/tensorflow/lite/micro/kernels/xtensa/pad.cc index b8ebf3bde0b..2ac7fd7d905 100644 --- a/tensorflow/lite/micro/kernels/xtensa/pad.cc +++ b/tensorflow/lite/micro/kernels/xtensa/pad.cc @@ -190,13 +190,54 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { case kTfLiteInt8: { #if defined(VISION_P6) PadEvalVision(*op_data_xtensa, input, output); -#else +#else // defined(VISION_P6) int8_t pad_value; if (constant_values == nullptr) { pad_value = static_cast(data->output_zero_point); } else { pad_value = *tflite::micro::GetTensorData(constant_values); } +#if (defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ)) + if((input->dims->size <= 4)) + { + int err; + const int8_t *input_data_ptr; + int8_t *output_data_ptr; + + input_data_ptr = tflite::micro::GetTensorData(input); + output_data_ptr = tflite::micro::GetTensorData(output); + + const TfLiteEvalTensor* paddings = tflite::micro::GetEvalInput(context, node, /*index=*/1); + const int32_t* paddings_data_ptr = tflite::micro::GetTensorData(paddings); + + err = xa_nn_pad_8_8(output_data_ptr + ,output->dims->data + ,input_data_ptr + ,input->dims->data + ,paddings_data_ptr + ,paddings->dims->data + ,output->dims->size + ,input->dims->size + ,paddings->dims->size + ,pad_value); + TF_LITE_ENSURE(context, (err==0) ); + } + else + { + if (data->params.resizing_category == ResizingCategory::kImageStyle) { + reference_ops::PadImageStyle( + data->params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), &pad_value, + tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + } else { + reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + &pad_value, tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + } + } +#else // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) if (data->params.resizing_category == ResizingCategory::kImageStyle) { reference_ops::PadImageStyle( data->params, tflite::micro::GetTensorShape(input), @@ -209,16 +250,17 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { &pad_value, tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); } -#endif +#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) +#endif // defined(VISION_P6) } break; case kTfLiteInt16: { int16_t pad_value = constant_values == nullptr ? 0 : *tflite::micro::GetTensorData(constant_values); -#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#if (defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ)) /* NNLib currently only supports up to 4D input tensors */ - if (tflite::micro::GetTensorShape(input).DimensionsCount() == 4) { + if (tflite::micro::GetTensorShape(input).DimensionsCount() <= 4) { const TfLiteEvalTensor* paddings = tflite::micro::GetEvalInput(context, node, /*index=*/1); int32_t err = xa_nn_pad_16_16( @@ -234,24 +276,51 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) { pad_value); if (err != 0) return kTfLiteError; } else { -#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input), tflite::micro::GetTensorData(input), &pad_value, tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); -#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#if (defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ)) } -#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) } break; case kTfLiteInt32: { int32_t pad_value = constant_values == nullptr ? 0 : *tflite::micro::GetTensorData(constant_values); +#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5) + if(tflite::micro::GetTensorShape(input).DimensionsCount() <= 4) + { + const TfLiteEvalTensor* paddings = + tflite::micro::GetEvalInput(context, node, /*index=*/1); + int32_t err = xa_nn_pad_32_32( + tflite::micro::GetTensorData(output), + tflite::micro::GetTensorShape(output).DimsData(), + tflite::micro::GetTensorData(input), + tflite::micro::GetTensorShape(input).DimsData(), + tflite::micro::GetTensorData(paddings), + tflite::micro::GetTensorShape(paddings).DimsData(), + tflite::micro::GetTensorShape(output).DimensionsCount(), + tflite::micro::GetTensorShape(input).DimensionsCount(), + tflite::micro::GetTensorShape(paddings).DimensionsCount(), + pad_value); + if (err != 0) return kTfLiteError; + } + else + { + reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input), + tflite::micro::GetTensorData(input), + &pad_value, tflite::micro::GetTensorShape(output), + tflite::micro::GetTensorData(output)); + } +#else // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input), tflite::micro::GetTensorData(input), &pad_value, tflite::micro::GetTensorShape(output), tflite::micro::GetTensorData(output)); +#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) } break; default: From aa4e828f7d4bec312fa8e1fade1e7e291ab02e6f Mon Sep 17 00:00:00 2001 From: unmeshna Date: Fri, 31 Jul 2026 03:44:37 -0700 Subject: [PATCH 3/3] Enable Xtensa SVDF optimized paths for HIFI_IQ target Extend existing HIFI kernel guards to include HIFI_IQ so the optimized SVDF paths are compiled for the HIFI_IQ target. No functional change on other targets. --- tensorflow/lite/micro/kernels/xtensa/svdf.cc | 22 +++++++++----------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tensorflow/lite/micro/kernels/xtensa/svdf.cc b/tensorflow/lite/micro/kernels/xtensa/svdf.cc index da34e094706..5f8dc3e5ab5 100644 --- a/tensorflow/lite/micro/kernels/xtensa/svdf.cc +++ b/tensorflow/lite/micro/kernels/xtensa/svdf.cc @@ -33,7 +33,7 @@ limitations under the License. namespace tflite { namespace { -#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#if defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) TfLiteStatus EvalIntegerSvdfHifi(TfLiteContext* context, TfLiteNode* node, const TfLiteEvalTensor* input_tensor, @@ -60,11 +60,11 @@ TfLiteStatus EvalIntegerSvdfHifi(TfLiteContext* context, TfLiteNode* node, // Left shift the activation_state. int num_bytes = sizeof(*state_ptr) * (n_batch * n_filter * n_memory - 1); -#if defined(HIFI5) +#if defined(HIFI5) || defined(HIFI_IQ) memcpy(state_ptr, state_ptr + 1, num_bytes); #else xa_nn_memmove_16(state_ptr, state_ptr + 1, (num_bytes >> 1)); -#endif // defined(HIFI5) +#endif // defined(HIFI5) || defined(HIFI_IQ) // Note: no need to clear the latest activation, matmul is not accumulative. @@ -108,7 +108,7 @@ TfLiteStatus EvalIntegerSvdfHifi(TfLiteContext* context, TfLiteNode* node, } return kTfLiteOk; } -#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) void* Init(TfLiteContext* context, const char* buffer, size_t length) { TFLITE_DCHECK(context != nullptr); @@ -116,7 +116,7 @@ void* Init(TfLiteContext* context, const char* buffer, size_t length) { } TfLiteStatus PrepareInt8(TfLiteContext* context, TfLiteNode* node) { -#if defined(HIFIMINI) || defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#if defined(HIFIMINI) || defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) TFLITE_DCHECK(node->builtin_data != nullptr); const auto* params = static_cast(node->builtin_data); @@ -252,12 +252,11 @@ TfLiteStatus PrepareInt8(TfLiteContext* context, TfLiteNode* node) { return kTfLiteOk; #else return PrepareSvdf(context, node); -#endif // defined(HIFIMINI) || defined(HIFI3) || defined(HIFI4) || - // defined(HIFI5) +#endif // defined(HIFIMINI) || defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) } TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { -#if defined(HIFIMINI) || defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#if defined(HIFIMINI) || defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) MicroContext* micro_context = GetMicroContext(context); TfLiteTensor* input = @@ -278,8 +277,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { return status; #else return PrepareSvdf(context, node); -#endif // defined(HIFIMINI) || defined(HIFI3) || defined(HIFI4) || - // defined(HIFI5) +#endif } TfLiteStatus EvalInt8(TfLiteContext* context, TfLiteNode* node) { @@ -308,7 +306,7 @@ TfLiteStatus EvalInt8(TfLiteContext* context, TfLiteNode* node) { return EvalIntegerSvdfHifimini(context, node, input, weights_feature, weights_time, bias, params, activation_state, output, data); -#elif defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#elif defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) return EvalIntegerSvdfHifi(context, node, input, weights_feature, weights_time, bias, params, activation_state, output, data); @@ -316,7 +314,7 @@ TfLiteStatus EvalInt8(TfLiteContext* context, TfLiteNode* node) { EvalInt16SvdfReference(context, node, input, weights_feature, weights_time, bias, params, activation_state, output, data); return kTfLiteOk; -#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) +#endif // defined(HIFI3) || defined(HIFI4) || defined(HIFI5) || defined(HIFI_IQ) } TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {