From a8098c06885e6fb5ce3239830e1cd344c98262a4 Mon Sep 17 00:00:00 2001 From: Eric Kunze Date: Thu, 7 Sep 2023 00:31:54 +0000 Subject: Support new RESCALE attributes input_unsigned and output_unsigned were added to the specification. Older TOSA files with uint data types are still supported. Signed-off-by: Eric Kunze Change-Id: I125886ffc92975d99971e56e2075dd5d96bdbdc4 --- reference_model/src/ops/type_conversion.cc | 179 +++++++++++++++++++++++------ 1 file changed, 143 insertions(+), 36 deletions(-) (limited to 'reference_model/src/ops') diff --git a/reference_model/src/ops/type_conversion.cc b/reference_model/src/ops/type_conversion.cc index 9464fd9..0135d1b 100644 --- a/reference_model/src/ops/type_conversion.cc +++ b/reference_model/src/ops/type_conversion.cc @@ -102,6 +102,18 @@ int OpRescale::checkTensorAttributes() return 0; } +// helpers to convert types +static int64_t zero_extend(int8_t val) +{ + uint8_t* rval = reinterpret_cast(&val); + return static_cast(*rval); +} +static int64_t zero_extend(int16_t val) +{ + uint16_t* rval = reinterpret_cast(&val); + return static_cast(*rval); +} + template int OpRescale::eval() { @@ -112,6 +124,8 @@ int OpRescale::eval() bool scale32 = attribute->scale32(); bool double_round = attribute->double_round(); bool per_channel = attribute->per_channel(); + bool input_unsigned = attribute->input_unsigned(); + bool output_unsigned = attribute->output_unsigned(); // reshape [d0, d1, ..., dn] into [d0 * d1 ..., dn] Eigen::array shape_2d; @@ -143,22 +157,68 @@ int OpRescale::eval() { for (int32_t i = 0; i < shape_2d[1]; i++) { - begin = Eigen::array({ 0, i }); - curr_channel_slice_prescaled = input_reshaped.slice(begin, size); - channel_multiplier = multiplier[i]; - channel_shift = shift[i]; - curr_channel_slice_postscaled = - curr_channel_slice_prescaled.unaryExpr([input_zp, output_zp, channel_multiplier, channel_shift, - double_round, scale32](InEigenType in_val) -> OutEigenType { - InEigenType input_zp_shifted = in_val - (InEigenType)input_zp; + begin = Eigen::array({ 0, i }); + curr_channel_slice_prescaled = input_reshaped.slice(begin, size); + channel_multiplier = multiplier[i]; + channel_shift = shift[i]; + curr_channel_slice_postscaled = curr_channel_slice_prescaled.unaryExpr( + [input_zp, output_zp, channel_multiplier, channel_shift, double_round, scale32, input_unsigned, + output_unsigned](InEigenType in_val) -> OutEigenType { + int64_t input_zp_shifted; + if (input_unsigned) + { + int64_t in_val64; + int64_t in_zp64; + switch (GetNumBits::value) + { + case 8: + in_val64 = zero_extend(static_cast(in_val)); + in_zp64 = zero_extend(static_cast(input_zp)); + break; + case 16: + in_val64 = zero_extend(static_cast(in_val)); + in_zp64 = zero_extend(static_cast(input_zp)); + break; + default: + in_val64 = static_cast(in_val); + in_zp64 = static_cast(input_zp); + break; + } + input_zp_shifted = in_val64 - in_zp64; + } + else + { + input_zp_shifted = in_val - input_zp; + } int32_t scaled; if (scale32) - scaled = TosaReference::QuantUtil::apply_scale_32(input_zp_shifted, channel_multiplier, - channel_shift, double_round); + scaled = TosaReference::QuantUtil::apply_scale_32(static_cast(input_zp_shifted), + channel_multiplier, channel_shift, + double_round); else scaled = TosaReference::QuantUtil::apply_scale_16(input_zp_shifted, channel_multiplier, channel_shift); - int64_t res_in_64 = static_cast(scaled) + output_zp; + int64_t output_zp_extended; + if (output_unsigned) + { + switch (GetNumBits::value) + { + case 8: + output_zp_extended = zero_extend(static_cast(output_zp)); + break; + case 16: + output_zp_extended = zero_extend(static_cast(output_zp)); + break; + default: + output_zp_extended = static_cast(output_zp); + break; + } + } + else + { + output_zp_extended = static_cast(output_zp); + } + int64_t res_in_64 = static_cast(scaled) + output_zp_extended; int64_t i32_max_in_64 = static_cast(std::numeric_limits::max()); int64_t i32_min_in_64 = static_cast(std::numeric_limits::min()); if (res_in_64 > i32_max_in_64 || res_in_64 < i32_min_in_64) @@ -190,31 +250,78 @@ int OpRescale::eval() int32_t tensor_shift = shift[0]; try { - output_2d = input_reshaped.unaryExpr([input_zp, output_zp, tensor_multiplier, tensor_shift, double_round, - scale32](InEigenType in_val) -> OutEigenType { - InEigenType input_zp_shifted = in_val - (InEigenType)input_zp; - int32_t scaled; - if (scale32) - scaled = TosaReference::QuantUtil::apply_scale_32(input_zp_shifted, tensor_multiplier, tensor_shift, - double_round); - else - scaled = - TosaReference::QuantUtil::apply_scale_16(input_zp_shifted, tensor_multiplier, tensor_shift); - int64_t res_in_64 = static_cast(scaled) + output_zp; - int64_t i32_max_in_64 = static_cast(std::numeric_limits::max()); - int64_t i32_min_in_64 = static_cast(std::numeric_limits::min()); - if (res_in_64 > i32_max_in_64 || res_in_64 < i32_min_in_64) - { - std::string desc = "scaling result [" + std::to_string(scaled) + "] plus output_zp [" + - std::to_string(output_zp) + "] not in i32 range"; - throw desc; - } - - OutEigenType out_val = static_cast(res_in_64); - out_val = std::max(out_val, QMin); - out_val = std::min(out_val, QMax); - return out_val; - }); + output_2d = + input_reshaped.unaryExpr([input_zp, output_zp, tensor_multiplier, tensor_shift, double_round, scale32, + input_unsigned, output_unsigned](InEigenType in_val) -> OutEigenType { + int64_t input_zp_shifted; + if (input_unsigned) + { + int64_t in_val64; + int64_t in_zp64; + switch (GetNumBits::value) + { + case 8: + in_val64 = zero_extend(static_cast(in_val)); + in_zp64 = zero_extend(static_cast(input_zp)); + break; + case 16: + in_val64 = zero_extend(static_cast(in_val)); + in_zp64 = zero_extend(static_cast(input_zp)); + break; + default: + in_val64 = static_cast(in_val); + in_zp64 = static_cast(input_zp); + break; + } + input_zp_shifted = in_val64 - in_zp64; + } + else + { + input_zp_shifted = in_val - input_zp; + } + int32_t scaled; + if (scale32) + scaled = TosaReference::QuantUtil::apply_scale_32(input_zp_shifted, tensor_multiplier, + tensor_shift, double_round); + else + scaled = + TosaReference::QuantUtil::apply_scale_16(input_zp_shifted, tensor_multiplier, tensor_shift); + + int64_t output_zp_extended; + if (output_unsigned) + { + switch (GetNumBits::value) + { + case 8: + output_zp_extended = zero_extend(static_cast(output_zp)); + break; + case 16: + output_zp_extended = zero_extend(static_cast(output_zp)); + break; + default: + output_zp_extended = static_cast(output_zp); + break; + } + } + else + { + output_zp_extended = static_cast(output_zp); + } + int64_t res_in_64 = static_cast(scaled) + output_zp_extended; + int64_t i32_max_in_64 = static_cast(std::numeric_limits::max()); + int64_t i32_min_in_64 = static_cast(std::numeric_limits::min()); + if (res_in_64 > i32_max_in_64 || res_in_64 < i32_min_in_64) + { + std::string desc = "scaling result [" + std::to_string(scaled) + "] plus output_zp [" + + std::to_string(output_zp) + "] not in i32 range"; + throw desc; + } + + OutEigenType out_val = static_cast(res_in_64); + out_val = std::max(out_val, QMin); + out_val = std::min(out_val, QMax); + return out_val; + }); } catch (std::string desc) { -- cgit v1.2.1