blob: 8e43100655e69da7c6ff5512a0bd195c5c01331c [file] [log] [blame]
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <limits>
#include "crt/kelvin.h"
#include "tflm/opt/opt.h"
#include "tflm/opt/util.h"
namespace kelvin::opt {
void LeakyReluS8(const tflite::LeakyReluParams& params,
const tflite::RuntimeShape& input_shape, const int8_t* input,
const tflite::RuntimeShape& output_shape, int8_t* output) {
const int32_t input_zero_point = params.input_offset;
const int32_t output_zero_point = params.output_offset;
const int32_t output_multiplier_alpha = params.output_multiplier_alpha;
const int32_t output_shift_alpha = params.output_shift_alpha;
const int32_t output_multiplier_identity = params.output_multiplier_identity;
const int32_t output_shift_identity = params.output_shift_identity;
const int block_size = MatchingFlatSize(input_shape, output_shape);
constexpr int32_t quantized_output_min = std::numeric_limits<int16_t>::min();
constexpr int32_t quantized_output_max = std::numeric_limits<int16_t>::max();
int32_t right_shift_identity = std::min(output_shift_identity, 0L);
int32_t left_shift_identity = std::max(output_shift_identity, 0L);
int32_t right_shift_alpha = std::min(output_shift_alpha, 0L);
int32_t left_shift_alpha = std::max(output_shift_alpha, 0L);
int blocks = block_size;
int vl;
getmaxvl_b(vl);
while (blocks) {
int count = std::min(blocks, vl);
// Load data from the input, and widen (now we can use vm0).
vld_b_lp_xx(v0, input, count);
vaddw_h_vx(v0, v0, 0);
vaddw_w_vx(v2, v1, 0);
vaddw_w_vx(v0, v0, 0);
// Subtract out the provided offset from the inputs.
vsub_w_vx_m(vm0, vm0, input_zero_point);
// Compute the Relu on all inputs, as if they were >=0.
vsll_w_vx_m(vm2, vm0, left_shift_identity);
vdmulh_w_r_vx_m(vm2, vm2, output_multiplier_identity);
vsha_w_vx_m(vm2, vm2, RIGHT_SHIFT(right_shift_identity));
vadd_w_vx_m(vm2, vm2, output_zero_point);
vmax_w_vx_m(vm2, vm2, quantized_output_min);
vmin_w_vx_m(vm2, vm2, quantized_output_max);
// Compute the Relu on all inputs, as if they were <0.
vsll_w_vx_m(vm1, vm0, left_shift_alpha);
vdmulh_w_r_vx_m(vm1, vm1, output_multiplier_alpha);
vsha_w_vx_m(vm1, vm1, RIGHT_SHIFT(right_shift_alpha));
vadd_w_vx_m(vm1, vm1, output_zero_point);
vmax_w_vx_m(vm1, vm1, quantized_output_min);
vmin_w_vx_m(vm1, vm1, quantized_output_max);
// Compute a boolean vector for inputs >=0.
vge_w_vx_m(vm3, vm0, 0);
// Compute a boolean vector for inputs <0.
vlt_w_vx_m(vm0, vm0, 0);
// Multiply the `identity` results by the >=0 vector.
vmul_w_vv_m(vm2, vm2, vm3);
// Multiply the `alpha` results by the <0 vector.
vmul_w_vv_m(vm0, vm1, vm0);
// Sum the two resulting vectors.
vadd_w_vv_m(vm0, vm0, vm2);
// Narrow/swizzle, and store to output.
vsraqs_b_vx(v0, v0, 0);
vst_b_lp_xx(v0, output, count);
blocks -= count;
}
}
} // namespace kelvin::opt