1// Copyright 2015 Google Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// output_stages.h: public definitions of the output stages that can 16// be assembled into an output pipeline, to control how internal 17// 32-bit accumulators are transformed to obtain the final uint8 18// result matrix entries. 19 20#ifndef GEMMLOWP_PUBLIC_OUTPUT_STAGES_H_ 21#define GEMMLOWP_PUBLIC_OUTPUT_STAGES_H_ 22 23#include <tuple> 24 25#include "../internal/common.h" 26 27namespace gemmlowp { 28 29// This output stage takes int32 values and returns still int32 values, 30// but "quantized down" to the uint8 scale; in other words, its output 31// is typically what one would then clamp to [0..255] and cast to uint8 32// (see OutputStageSaturatingCastToUint8). 33// 34// This "quantization down" process depends on 3 parameters, 35// result_offset, result_mult_int, result_shift, 36// and the result is: 37// ((input + result_offset) * result_mult_int + rounding) >> result_shift 38// where 39// rounding = (result_shift < 1) ? 0 : (1 << (result_shift - 1)); 40struct OutputStageQuantizeDownInt32ToUint8Scale { 41 std::int32_t result_offset; 42 std::int32_t result_mult_int; 43 std::int32_t result_shift; 44}; 45 46// This output stage takes int32 values and returns still int32 values, 47// but "quantized down" to the uint8 scale; in other words, its output 48// is typically what one would then clamp to [0..255] and cast to uint8 49// (see OutputStageSaturatingCastToUint8). 50// 51// This "quantization down" process depends on 3 parameters, 52// result_offset, result_mult_int, result_shift, 53// and the result is: 54// ((input + result_offset) * result_mult_int + rounding) >> result_shift 55// where 56// rounding = (result_shift < 1) ? 0 : (1 << (result_shift - 1)); 57// 58// Difference from OutputStageQuantizeDownInt32ToUint8Scale here is that each 59// row or column of the output (depending on tShape) has its own result_offset 60// and result_mult_int numbers. 61template <VectorShape tShape> 62struct OutputStageQuantizeDownInt32ToUint8ScalePC { 63 VectorMap<const std::int32_t, tShape> result_offset; 64 VectorMap<const std::int32_t, tShape> result_mult_int; 65 std::int32_t result_shift; 66}; 67 68// This output stage takes int32 values that are expected to be already 69// on the final uint8 scale, but not necessarily in the [0..255] range. 70// It clamps them to the [0..255] range and returns them casted to uint8. 71struct OutputStageSaturatingCastToUint8 {}; 72 73// This output stage depends on a "bias vector" that should contain int32 74// entries, and be either a row-vector of the same number of columns as the 75// result matrix, or a column-vector of the same number of rows as the 76// result matrix. This output stage takes int32 values and adds to them 77// the corresponding entry of the bias vector (broadcasted in the other 78// direction to fit the matrix's shape), outputting int32 values. 79template <typename VectorType> 80struct OutputStageBiasAddition { 81 VectorType bias_vector; 82}; 83 84// This output stage clamps value between the specified min and max bounds. 85// It can be used to implement "rectified linear unit" activation functions 86// in neural networks. 87struct OutputStageClamp { 88 std::int32_t min; 89 std::int32_t max; 90}; 91 92struct OutputStageTanh { 93 std::int32_t real_zero_as_int32; 94 std::int32_t real_amplitude_as_int32; 95}; 96 97// An output pipeline is just a std::tuple of output stages. 98// This function generates a standard output pipeline consisting of two stages: 99// OutputStageQuantizeDownInt32ToUint8Scale, OutputStageSaturatingCastToUint8. 100inline std::tuple<OutputStageQuantizeDownInt32ToUint8Scale, 101 OutputStageSaturatingCastToUint8> 102MakeStandardOutputPipeline(std::int32_t result_offset, 103 std::int32_t result_mult_int, 104 std::int32_t result_shift) { 105 OutputStageQuantizeDownInt32ToUint8Scale quantize_down_stage; 106 quantize_down_stage.result_offset = result_offset; 107 quantize_down_stage.result_mult_int = result_mult_int; 108 quantize_down_stage.result_shift = result_shift; 109 OutputStageSaturatingCastToUint8 saturating_cast_stage; 110 return std::make_tuple(quantize_down_stage, saturating_cast_stage); 111} 112 113// An output pipeline is just a std::tuple of output stages. 114// This function generates a standard output pipeline consisting of two stages: 115// OutputStageQuantizeDownInt32ToUint8ScalePC, OutputStageSaturatingCastToUint8. 116template <VectorShape tShape> 117inline std::tuple<OutputStageQuantizeDownInt32ToUint8ScalePC<tShape>, 118 OutputStageSaturatingCastToUint8> 119MakeStandardOutputPipeline(const VectorMap<const std::int32_t, tShape>& 120 result_offset, 121 const VectorMap<const std::int32_t, tShape>& 122 result_mult_int, 123 std::int32_t result_shift) { 124 OutputStageQuantizeDownInt32ToUint8ScalePC<tShape> quantize_down_stage; 125 quantize_down_stage.result_offset = result_offset; 126 quantize_down_stage.result_mult_int = result_mult_int; 127 quantize_down_stage.result_shift = result_shift; 128 OutputStageSaturatingCastToUint8 saturating_cast_stage; 129 return std::make_tuple(quantize_down_stage, saturating_cast_stage); 130} 131 132} // namespace gemmlowp 133 134#endif // GEMMLOWP_PUBLIC_OUTPUT_STAGES_H_ 135