void EvalQuantized(TfLiteContext* context, TfLiteNode* node,
                   TfLiteSubParams* params, const OpData* data,
                   const TfLiteTensor* input1, const TfLiteTensor* input2,
                   TfLiteTensor* output) {
  tflite::ArithmeticParams op_params;
  op_params.left_shift = data->left_shift;
  op_params.input1_offset = data->input1_offset;
  op_params.input1_multiplier = data->input1_multiplier;
  op_params.input1_shift = data->input1_shift;
  op_params.input2_offset = data->input2_offset;
  op_params.input2_multiplier = data->input2_multiplier;
  op_params.input2_shift = data->input2_shift;
  op_params.output_offset = data->output_offset;
  op_params.output_multiplier = data->output_multiplier;
  op_params.output_shift = data->output_shift;
  SetActivationParams(data->output_activation_min, data->output_activation_max,
                      &op_params);

  const bool need_broadcast = optimized_ops::ProcessBroadcastShapes(
      GetTensorShape(input1), GetTensorShape(input2), &op_params);

#define TF_LITE_SUB(type, opname, data_type)                             \
  type::opname(op_params, GetTensorShape(input1),                        \
               GetTensorData<data_type>(input1), GetTensorShape(input2), \
               GetTensorData<data_type>(input2), GetTensorShape(output), \
               GetTensorData<data_type>(output))
  // NOTE: We are using the add kernels. This is possible as the second values
  // multiplier is negated before being passed down.
  if (output->type == kTfLiteInt8) {
    if (need_broadcast) {
      TF_LITE_SUB(reference_integer_ops, BroadcastAdd4DSlow, int8_t);
    } else {
      TF_LITE_SUB(reference_integer_ops, Add, int8_t);
    }
  } else if (!data->pot_scale_int16) {
    if (need_broadcast) {
      TF_LITE_SUB(reference_ops, BroadcastAdd4DSlow, int16_t);
    } else {
      reference_ops::Add(op_params, GetTensorShape(input1),
                         GetTensorData<int16_t>(input1), GetTensorShape(input2),
                         GetTensorData<int16_t>(input2), GetTensorShape(output),
                         GetTensorData<int16_t>(output), false);
    }
  } else if (output->type == kTfLiteUInt8) {
    if (kernel_type == kReference) {
      if (need_broadcast) {
        TF_LITE_SUB(reference_ops, BroadcastAdd4DSlow, uint8_t);
      } else {
        TF_LITE_SUB(reference_ops, Add, uint8_t);
      }
    } else {
      if (need_broadcast) {
        optimized_ops::BroadcastAddDispatch(
            op_params, GetTensorShape(input1), GetTensorData<uint8_t>(input1),
            GetTensorShape(input2), GetTensorData<uint8_t>(input2),
            GetTensorShape(output), GetTensorData<uint8_t>(output));
      } else {
        TF_LITE_SUB(optimized_ops, Add, uint8_t);
      }
    }
  } else {
    if (kernel_type == kReference) {
      if (need_broadcast) {
        TF_LITE_SUB(reference_ops, BroadcastSubSlow, int16_t);
      } else {
        TF_LITE_SUB(reference_ops, Sub16, int16_t);
      }
    } else {
      if (need_broadcast) {
        TF_LITE_SUB(optimized_ops, BroadcastSubSlow, int16_t);
      } else {
        TF_LITE_SUB(optimized_ops, Sub16, int16_t);
      }
    }
  }
#undef TF_LITE_SUB
}