| 144 | } |
| 145 | |
| 146 | TfLiteStatus PrepareInt16SubOp(TfLiteContext* context, |
| 147 | const TfLiteTensor* input1, |
| 148 | const TfLiteTensor* input2, TfLiteTensor* output, |
| 149 | TfLiteSubParams* params, OpData* data) { |
| 150 | // 16bit -> 16bit special quantized path, supporting only a rather |
| 151 | // narrow case of quantization parameters: zero_points must all be 0 |
| 152 | // ("symmetric quantization") and scales must be power-of-two (which |
| 153 | // we abbreviate as "POT" below). The intended use case for this path |
| 154 | // is in LSTM cells, where, due to the constraints of implementing |
| 155 | // some of the math in these LSTM cells in fixed-point arithmetic, |
| 156 | // we need to have such symmetric, power-of-two quantization |
| 157 | // (Fixed-point formats are inherently symmetric, power-of-two). |
| 158 | TF_LITE_ENSURE_EQ(context, input1->params.zero_point, 0); |
| 159 | TF_LITE_ENSURE_EQ(context, input2->params.zero_point, 0); |
| 160 | TF_LITE_ENSURE_EQ(context, output->params.zero_point, 0); |
| 161 | |
| 162 | int input1_scale_log2_rounded; |
| 163 | bool input1_scale_is_pot = |
| 164 | CheckedLog2(input1->params.scale, &input1_scale_log2_rounded); |
| 165 | TF_LITE_ENSURE(context, input1_scale_is_pot); |
| 166 | |
| 167 | int input2_scale_log2_rounded; |
| 168 | bool input2_scale_is_pot = |
| 169 | CheckedLog2(input2->params.scale, &input2_scale_log2_rounded); |
| 170 | TF_LITE_ENSURE(context, input2_scale_is_pot); |
| 171 | |
| 172 | int output_scale_log2_rounded; |
| 173 | bool output_scale_is_pot = |
| 174 | CheckedLog2(output->params.scale, &output_scale_log2_rounded); |
| 175 | TF_LITE_ENSURE(context, output_scale_is_pot); |
| 176 | |
| 177 | data->input1_shift = input1_scale_log2_rounded - output_scale_log2_rounded; |
| 178 | data->input2_shift = input2_scale_log2_rounded - output_scale_log2_rounded; |
| 179 | |
| 180 | // Shifting of one input is supported. The graph quantization should ensure |
| 181 | // that the other input matches the output. |
| 182 | TF_LITE_ENSURE(context, data->input1_shift == 0 || data->input2_shift == 0); |
| 183 | TF_LITE_ENSURE(context, data->input1_shift <= 0); |
| 184 | TF_LITE_ENSURE(context, data->input2_shift <= 0); |
| 185 | |
| 186 | CalculateActivationRangeQuantized(context, params->activation, output, |
| 187 | &data->output_activation_min, |
| 188 | &data->output_activation_max); |
| 189 | return kTfLiteOk; |
| 190 | } |
| 191 | |
| 192 | TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) { |
| 193 | OpData* data = reinterpret_cast<OpData*>(node->user_data); |
no test coverage detected