TfLiteStatus EvalAddQuantized(TfLiteContext* context, TfLiteNode* node,
                              TfLiteAddParams* params, const OpData* data,
                              const TfLiteTensor* input1,
                              const TfLiteTensor* input2,
                              TfLiteTensor* output) {
  if (output->type == kTfLiteUInt8 || output->type == kTfLiteInt8 ||
      !data->pot_scale_int16) {
    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);
    bool need_broadcast = optimized_ops::ProcessBroadcastShapes(
        GetTensorShape(input1), GetTensorShape(input2), &op_params);
#define TF_LITE_ADD(type, opname, dtype)                             \
  type::opname(op_params, GetTensorShape(input1),                    \
               GetTensorData<dtype>(input1), GetTensorShape(input2), \
               GetTensorData<dtype>(input2), GetTensorShape(output), \
               GetTensorData<dtype>(output));
    if (output->type == kTfLiteInt8) {
      if (kernel_type == kReference) {
        if (need_broadcast) {
          TF_LITE_ADD(reference_integer_ops, BroadcastAdd4DSlow, int8_t);
        } else {
          TF_LITE_ADD(reference_integer_ops, Add, int8_t);
        }
      } else {
        if (need_broadcast) {
          TF_LITE_ADD(optimized_integer_ops, BroadcastAddDispatch, int8_t);
        } else {
          TF_LITE_ADD(optimized_integer_ops, Add, int8_t);
        }
      }
    } else if (output->type == kTfLiteInt16) {
      if (need_broadcast) {
        TF_LITE_ADD(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 (kernel_type == kReference) {
        if (need_broadcast) {
          TF_LITE_ADD(reference_ops, BroadcastAdd4DSlow, uint8_t);
        } else {
          TF_LITE_ADD(reference_ops, Add, uint8_t);
        }
      } else {
        if (need_broadcast) {
          TF_LITE_ADD(optimized_ops, BroadcastAddDispatch, uint8_t);
        } else {
          TF_LITE_ADD(optimized_ops, Add, uint8_t);
        }
      }
    }
#undef TF_LITE_ADD
  } else if (output->type == kTfLiteInt16) {
    tflite::ArithmeticParams op_params;
    op_params.input1_shift = data->input1_shift;
    op_params.input2_shift = data->input2_shift;
    SetActivationParams(data->output_activation_min,
                        data->output_activation_max, &op_params);
#define TF_LITE_ADD(type, opname)                                      \
  type::opname(op_params, GetTensorShape(input1),                      \
               GetTensorData<int16_t>(input1), GetTensorShape(input2), \
               GetTensorData<int16_t>(input2), GetTensorShape(output), \
               GetTensorData<int16_t>(output))
    // The quantized version of Add doesn't support activations, so we
    // always use BroadcastAdd.
    if (kernel_type == kReference) {
      TF_LITE_ADD(reference_ops, Add);
    } else {
      TF_LITE_ADD(optimized_ops, Add);
    }
#undef TF_LITE_ADD
  }

  return kTfLiteOk;
}