From c65cd9ccff6000be01ee0742319009f0061879ce Mon Sep 17 00:00:00 2001 From: Kevin Petit Date: Wed, 31 Jan 2024 10:24:14 +0000 Subject: pseudocode: move all helpers to separate files Change-Id: I75ea94d42c63e862af8e492da7f47ecd6caa1055 Signed-off-by: Kevin Petit --- chapters/pseudocode.adoc | 466 +-------------------- pseudocode/library/apply_broadcast.tosac | 24 ++ pseudocode/library/arithmetic_helpers.tosac | 150 +++++++ pseudocode/library/broadcast_shape.tosac | 21 + pseudocode/library/flatten.tosac | 17 + pseudocode/library/generic_helpers.tosac | 71 ++++ pseudocode/library/numeric_accuracy_helpers.tosac | 52 +++ .../library/numeric_conversion_helpers.tosac | 28 ++ pseudocode/library/tensor_read.tosac | 13 + pseudocode/library/tensor_utils.tosac | 47 +++ pseudocode/library/tensor_write.tosac | 13 + pseudocode/library/tosa_reference_check.tosac | 38 ++ pseudocode/library/type_conversion_helpers.tosac | 63 +++ pseudocode/library/variable_tensor_allocate.tosac | 19 + pseudocode/library/variable_tensor_lookup.tosac | 19 + 15 files changed, 589 insertions(+), 452 deletions(-) create mode 100644 pseudocode/library/apply_broadcast.tosac create mode 100644 pseudocode/library/arithmetic_helpers.tosac create mode 100644 pseudocode/library/broadcast_shape.tosac create mode 100644 pseudocode/library/flatten.tosac create mode 100644 pseudocode/library/generic_helpers.tosac create mode 100644 pseudocode/library/numeric_accuracy_helpers.tosac create mode 100644 pseudocode/library/numeric_conversion_helpers.tosac create mode 100644 pseudocode/library/tensor_read.tosac create mode 100644 pseudocode/library/tensor_utils.tosac create mode 100644 pseudocode/library/tensor_write.tosac create mode 100644 pseudocode/library/tosa_reference_check.tosac create mode 100644 pseudocode/library/type_conversion_helpers.tosac create mode 100644 pseudocode/library/variable_tensor_allocate.tosac create mode 100644 pseudocode/library/variable_tensor_lookup.tosac diff --git a/chapters/pseudocode.adoc b/chapters/pseudocode.adoc index 8954503..acce9c9 100644 --- a/chapters/pseudocode.adoc +++ b/chapters/pseudocode.adoc @@ -62,44 +62,7 @@ void LEVEL_CHECK(condition) { [source,c++] ---- -// Convert tensor index coordinates to an element offset -size_t tensor_index_to_offset(shape_t shape, shape_t index) { - size_t size = tensor_size(shape); // check tensor shape is valid - size_t offset = 0; - for (int32_t i = 0; i < rank(shape); i++) { - REQUIRE(index[i] >= 0 && index[i] < shape[i]); - offset = offset * shape[i] + index[i]; - } - return offset; -} - -// Convert an element offset to tensor index coordinates -shape_t tensor_offset_to_index(shape_t shape, size_t offset) { - size_t size = tensor_size(shape); // check tensor shape is valid - REQUIRE(offset < size); - shape_t index(rank(shape)); // index has rank(shape) indicies - for(int32_t i = rank(shape) - 1; i >= 0; i--) { - index[i] = offset % shape[i]; - offset /= shape[i]; - } - return index; -} - -// Check the tensor shape is valid and return the tensor size in elements -size_t tensor_size(shape_t shape) { - size_t size = 1; - for (int32_t i = 0; i < rank(shape); i++) { - REQUIRE(1 <= shape[i] && shape[i] <= maximum / size); - size *= shape[i]; - } - return size; -} - -// Return the size of the tensor in the given axis -// For a rank=0 tensor, returns 1 for all axes -size_t shape_dim(shape_t shape, int axis) { - return (axis >= rank(shape)) ? 1 : shape[axis]; -} +include::{pseudocode}/library/tensor_utils.tosac[lines=10..-1] ---- ==== Tensor Read @@ -110,10 +73,7 @@ Index is the coordinates within the tensor of the value to be read. [source,c++] ---- -in_t tensor_read(in_t *address, shape_t shape, shape_t index) { - size_t offset = tensor_index_to_offset(shape, index); - return address[offset]; -} +include::{pseudocode}/library/tensor_read.tosac[lines=10..-1] ---- ==== Tensor Write @@ -125,10 +85,7 @@ value is the value to be written to the given coordinate. [source,c++] ---- -void tensor_write( *address, shape_t shape, shape_t index, value) { - size_t offset = tensor_index_to_offset(shape, index); - address[offset] = value; -} +include::{pseudocode}/library/tensor_write.tosac[lines=10..-1] ---- ==== Variable Tensor Allocate @@ -139,16 +96,7 @@ The uid argument is a globally unique identifier for variable tensors. [source,c++] ---- -tensor_t* variable_tensor_allocate(shape_t shape, int32_t uid) { - size_t size = tensor_size(shape); - tensor_t *allocated_tensor = new tensor_t; - allocated_tensor->data = new in_t[size]; - allocated_tensor->uid = uid; - allocated_tensor->is_written = false; - allocated_tensor->shape = shape; - allocated_tensor->type = in_t; - return allocated_tensor; -} +include::{pseudocode}/library/variable_tensor_allocate.tosac[lines=10..-1] ---- ==== Variable Tensor Lookup @@ -158,16 +106,7 @@ The uid argument is a globally unique identifier for variable tensors. [source,c++] ---- -tensor_t variable_tensor_lookup(int32_t uid) { - // The global all_allocated_variable_tensors was instantiated at the first - // time of executing the tosa graph - for_each(tensor_t allocated_tensor in all_allocated_variable_tensors) { - if (allocated_tensor.uid == uid) { - return allocated_tensor; - } - } - return NULL; -} +include::{pseudocode}/library/variable_tensor_lookup.tosac[lines=10..-1] ---- ==== Broadcast Helpers @@ -176,39 +115,14 @@ The following function derives the broadcast output shape from the input shapes. [source,c++] ---- -shape_t broadcast_shape(shape_t shape1, shape_t shape2) { - ERROR_IF(rank(shape1) != rank(shape2)); - shape_t shape = shape1; - for (int32_t i = 0; i < rank(shape); i++) { - if (shape[i] == 1) { - shape[i] = shape2[i]; - } else { - ERROR_IF(shape2[i] != 1 && shape2[i] != shape[i]); - } - } - return shape; -} +include::{pseudocode}/library/broadcast_shape.tosac[lines=10..-1] ---- The following function maps an index in the output tensor to an index in the input tensor. [source,c++] ---- -// The index argument should be a valid location within out_shape. -// The function returns the location within in_shape that contributes -// to the output based on broadcasting rules. - -shape_t apply_broadcast(shape_t out_shape, shape_t in_shape, shape_t index) { - ERROR_IF(rank(out_shape) != rank(in_shape)); - ERROR_IF(rank(out_shape) != rank(index)); - for (int32_t i = 0; i < rank(out_shape); i++) { - if (out_shape[i] != in_shape[i]) { - ERROR_IF(in_shape[i] != 1); - index[i] = 0; - } - } - return index; -} +include::{pseudocode}/library/apply_broadcast.tosac[lines=10..-1] ---- === General Pseudocode Helpers @@ -221,147 +135,7 @@ The following functions provide arithmetic while defining requirements such that [source,c++] ---- -in_t apply_add_s(in_t a, in_t b) { - if (is_floating_point(in_t)) return a + b; - int64_t c = sign_extend(a) + sign_extend(b); - REQUIRE(c >= minimum_s && c <= maximum_s); - return static_cast(c); -} - -in_t apply_add_u(in_t a, in_t b) { - if (is_floating_point(in_t)) return a + b; - uint64_t c = zero_extend(a) + zero_extend(b); - REQUIRE(c >= minimum_u && c <= maximum_u); - return truncate(c); -} - -in_t apply_arith_rshift(in_t a, in_t b) { - int32_t c = sign_extend(a) >> sign_extend(b); - return static_cast(c); -} - -in_t apply_intdiv_s(in_t a, in_t b) { - int64_t c = sign_extend(a) / sign_extend(b); - REQUIRE(c >= minimum_s && c <= maximum_s); - return static_cast(c); -} - -in_t apply_ceil(in_t input) { - return input value rounded up to nearest integer -} - -in_t apply_clip_s(in_t value, in_t min_val, in_t max_val) { - if (is_floating_point(in_t>) { - REQUIRE(min_val <= max_val); - } - else { - REQUIRE(sign_extend(min_val) <= sign_extend(max_val)); - } - value = apply_max_s(value, min_val); - value = apply_min_s(value, max_val); - return value; -} - -in_t apply_clip_u(in_t value, in_t min_val, in_t max_val) { - REQUIRE(zero_extend(min_val) <= zero_extend(max_val)); - value = apply_max_u(value, min_val); - value = apply_min_u(value, max_val); - return value; -} - -in_t apply_exp(in_t input) { - return e to the power input -} - -in_t apply_floor(in_t input) { - return input value rounded down to nearest integer -} - -in_t apply_log(in_t input) { - if (input == 0) { - return -INFINITY - } - else if (input < 0) { - return NaN; - } - return the natural logarithm of input -} - -in_t apply_logical_rshift(in_t a, in_t b) { - uint64_t c = zero_extend(a) >> zero_extend(b); - return static_cast(c); -} - -in_t apply_max_s(in_t a, in_t b) { - if (is_floating_point(in_t)) { - if (isNaN(a) || isNaN(b)) { - return NaN; - } - if (a >= b) return a; else return b; - } - // Integer version - if (sign_extend(a) >= sign_extend(b)) return a; else return b; -} - -in_t apply_max_u(in_t a, in_t b) { - if (zero_extend(a) >= zero_extend(b)) return a; else return b; -} - -in_t apply_min_s(in_t a, in_t b) { - if (is_floating_point(in_t)) { - if (isNaN(a) || isNaN(b)) { - return NaN; - } - if (a < b) return a; else return b; - } - // Integer version - if (sign_extend(a) < sign_extend(b)) return a; else return b; -} - -in_t apply_min_u(in_t a, in_t b) { - if (zero_extend(a) < zero_extend(b)) return a; else return b; -} - -in_t apply_mul_s(in_t a, in_t b) { - if (is_floating_point(in_t)) return a * b; - int64_t c = sign_extend(a) * sign_extend(b); - return static_cast(c); -} - -in_t apply_pow(in_t a, in_t b) { - return a ** b; // a raised to the power b -} - -in_t apply_sqrt(in_t input) { - return the square root of input -} - -in_t apply_sub_s(in_t a, in_t b) { - if (is_floating_point(in_t)) return a - b; - int64_t c = sign_extend(a) - sign_extend(b); - REQUIRE(c >= minimum_s && c <= maximum_s); - return static_cast(c); -} - -in_t apply_sub_u(in_t a, in_t b) { - uint64_t c = zero_extend(a) - zero_extend(b); - REQUIRE(c >= minimum_u && c <= maximum_u); - return truncate(c); -} - -int32_t count_leading_zeros(int32_t a) { - int32_t acc = 32; - if (a != 0) { - uint32_t mask; - mask = 1 << (32 - 1); // width of int32_t - 1 - acc = 0; - while ((mask & a) == 0) { - mask = mask >> 1; - acc = acc + 1; - } - } - return acc; -} +include::{pseudocode}/library/arithmetic_helpers.tosac[lines=10..-1] ---- ==== Type Conversion Helpers @@ -370,61 +144,7 @@ The following definitions indicate the type to be used when the given parameters [source,c++] ---- - -// Returns a signed version of the given type -// A no-op for floating-point types -Type make_signed(Type in_t) -{ - switch(in_t) { - case bool_t: - return bool_t; - case i8_t: - return int8_t; - case i16_t: - return int16_t; - case i32_t: - return int32_t; - case i48_t: - return int48_t; - case fp16_t: - return fp16_t; - case bf16_t: - return bf16_t; - case fp32_t: - return fp32_t; - } -} - -// Returns the usigned type of the given type -// Error to call this with anything but i8_t or i16_t - -Type make_unsigned(Type in_t) -{ - ERROR_IF(in_t != i8_t && in_t != i16_t); - switch(in_t) { - case i8_t: - return uint8_t; - case i16_t: - return uint16_t; - } -} - -out_t static_cast(in_t value) -{ - // Operates similar to the c++ standard static_cast - // Limited to simple numeric conversion for TOSA. - // Sign extends signed integer input types if needed - // Zero extends unsigned integer input types if needed - // Truncates when converting to a smaller width data type - // Conversion from integer to floating-point is exact if possible - // If converting between signless integer types, treated as signed integer -} - -out_t bitcast(in_t value) -{ - // Treats the bits of value as if they were of type out_t - // Only supported for integer types of the same bit width -} +include::{pseudocode}/library/type_conversion_helpers.tosac[lines=10..-1] ---- ==== Numeric Accuracy Helpers @@ -436,49 +156,7 @@ The functions below return the ranges according to type. [source,c++] ---- -fp64_t exp2(int n) { - if (n < -1075) { - return 0.0; // smaller than smallest denormal - } - REQUIRE(n <= 1023); - fp64_t v = 1.0; - while (n > 0) { v = v*2.0; n--; } - while (n < 0) { v = v/2.0; n++; } - return v; -} - -int ilog2(fp64_t v) { - REQURE(0 < v && v < infinity); - int n = 0; - while (v >= 2.0) { v = v/2.0; n++; } - while (v < 1.0) { v = v*2.0; n--; } - return n; -} - -fp64_t normal_min() { - switch (in_t) { - case fp32_t: return exp2(-126); - case bf16_t: return exp2(-126); - case fp16_t: return exp2( -14); - } -} - -fp64_t normal_max() { - switch (in_t) { - case fp32_t: return exp2(128) - exp2(127-23); - case bf16_t: return exp2(128) - exp2(127- 7); - case fp16_t: return exp2( 16) - exp2( 15-10); - } -} - -// Number of fractional (mantissa bits) -int normal_frac () { - switch (in_t) { - case fp32_t: return 23; - case fp16_t: return 10; - case bf16_t: return 7; - } -} +include::{pseudocode}/library/numeric_accuracy_helpers.tosac[lines=10..-1] ---- The following functions check if a test value in floating-point format in_t is within an error range compared to a reference value. @@ -488,36 +166,7 @@ For the second function, the permitted error range is specified as an absolute e [source,c++] ---- -bool tosa_reference_check_fp(in_t test_value, fp64_t ref_value, fp64_t num_ulp) { - fp64_t err_bnd = 0.0; - if (is_normal_fp64(ref_value) && abs(ref_value) != 0) { - int ref_exp = ilog2(abs(ref_value)); - fp64_t ref_pow2 = max(exp2(ref_exp), normal_min); - fp64_t val_ulp = ref_pow2 * exp2(-normal_frac); - err_bnd = val_ulp * num_ulp; - } - return tosa_reference_check_fp_bnd(test_value, ref_value, err_bnd); -} - -bool tosa_reference_check_fp_bnd(in_t test_value, fp64_t ref_value, fp64_t err_bnd) { - if (is_a_NaN(ref_value)) { - return is_a_NaN(test_value); - } - REQUIRE(err_bnd >= 0.0); - if (ref_value < 0) { - ref_value = -ref_value; - test_value = -test_value; - } - fp64_t ref_max = ref_value + err_bnd; - fp64_t ref_min = ref_value - err_bnd; - if (ref_max > normal_max) ref_max = infinity; - if (ref_min > normal_max) ref_min = infinity; - if (ref_max < normal_min) ref_max = normal_min; - if (ref_min < normal_min) ref_min = 0; - return (static_cast(test_value) >= ref_min && - static_cast(test_value) <= ref_max); -} - +include::{pseudocode}/library/tosa_reference_check.tosac[lines=10..-1] ---- ==== Numeric Conversion Helpers @@ -528,106 +177,19 @@ See <> for details on the floating-point formats. [source,c++] ---- -int round_to_nearest_int(float_t f) - Converts the floating-point value to f, with rounding to the nearest integer value. - For the required precision see the section: Main inference precision requirements. - -float_t round_to_nearest_float(in_t f) - Converts the input value into floating-point, rounding to the nearest representable value. - For the required precision see the section: Main inference precision requirements. - -out_t sign_extend(in_t input) - Floating point values are unchanged. - For two's complement integer values where out_t has more bits than in_t, replicate the top bit of input for all bits between the top bit of input and the top bit of output. - -out_t zero_extend(in_t input) - Floating point values are unchanged. - For two's complement integer values where out_t has more bits than in_t, insert zero values for all bits between the top bit of input and the top bit of output. - -out_t truncate(in_t input) - output is the sizeof(out_t) least significant bits in input. - Nop for floating-point types +include::{pseudocode}/library/numeric_conversion_helpers.tosac[lines=10..-1] ---- The following definition is used to flatten a list of lists into a single list. [source,c++] ---- -in_t* flatten(in_t lists[]) { - in_t output = []; - for_each(list in lists) { - for_each(element in list) { - output.append(element); - } - } -} +include::{pseudocode}/library/flatten.tosac[lines=10..-1] ---- Generic helper functions used to keep the pseudocode concise. [source,c++] ---- - -bool_t is_floating_point(type) { - if (type == fp16_t || type == fp32_t || type == bf16_t) - return true; - return false; -} - -int32_t idiv(int32_t input1, int32_t input2) { - return input1 / input2; // Integer divide that truncates towards zero -} - -// Integer division that checks input1 is a multiple of input2 - -int32_t idiv_check(int32_t input1, int32_t input2) { - ERROR_IF(input1 % input2 != 0); // input1 must be a multiple of input2 - return input1 / input2; // exact quotient without rounding -} - -// perform an integer division with rounding towards minus infinity - -int32_t idiv_floor(int32_t input1, int32_t input2) { - int32_t rval = input1 / input2; - if (rval * input2 > input1) { - rval--; - } - return rval; -} - -int32_t length(in_t input) - return number of elements in input list - -int32_t rank(in_t input) - return rank of an input tensor - -int32_t sum(in_t input[]) - return the sum of values of an input list - -bool isNaN(float input) - return True if floating-point input value is NaN - -float_t pi() - returns value of pi - -float_t sin(angle) - return sine of angle given in radians - -float_t cos(angle) - return cosine of angle given in radians - -bool power_of_two(int32_t value) - return true if value is a power of two, false otherwise - -in_out_t maximum_s - return the maximum value when interpreting type T as a signed value as returned by the make_signed helper. - -in_out_t minimum_s - return the minimum value when interpreting type T as a signed value as returned by the make_signed helper. - -in_out_t maximum_u - return the maximum value when interpreting type T as an unsigned value as returned by the make_unsigned helper. - -in_out_t minimum_u - return the minimum value when interpreting type T as an unsigned value as returned by the make_unsigned helper. +include::{pseudocode}/library/generic_helpers.tosac[lines=10..-1] ---- diff --git a/pseudocode/library/apply_broadcast.tosac b/pseudocode/library/apply_broadcast.tosac new file mode 100644 index 0000000..4a86a42 --- /dev/null +++ b/pseudocode/library/apply_broadcast.tosac @@ -0,0 +1,24 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +// The index argument should be a valid location within out_shape. +// The function returns the location within in_shape that contributes +// to the output based on broadcasting rules. + +shape_t apply_broadcast(shape_t out_shape, shape_t in_shape, shape_t index) { + ERROR_IF(rank(out_shape) != rank(in_shape)); + ERROR_IF(rank(out_shape) != rank(index)); + for (int32_t i = 0; i < rank(out_shape); i++) { + if (out_shape[i] != in_shape[i]) { + ERROR_IF(in_shape[i] != 1); + index[i] = 0; + } + } + return index; +} diff --git a/pseudocode/library/arithmetic_helpers.tosac b/pseudocode/library/arithmetic_helpers.tosac new file mode 100644 index 0000000..be82ef5 --- /dev/null +++ b/pseudocode/library/arithmetic_helpers.tosac @@ -0,0 +1,150 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +in_t apply_add_s(in_t a, in_t b) { + if (is_floating_point(in_t)) return a + b; + int64_t c = sign_extend(a) + sign_extend(b); + REQUIRE(c >= minimum_s && c <= maximum_s); + return static_cast(c); +} + +in_t apply_add_u(in_t a, in_t b) { + if (is_floating_point(in_t)) return a + b; + uint64_t c = zero_extend(a) + zero_extend(b); + REQUIRE(c >= minimum_u && c <= maximum_u); + return truncate(c); +} + +in_t apply_arith_rshift(in_t a, in_t b) { + int32_t c = sign_extend(a) >> sign_extend(b); + return static_cast(c); +} + +in_t apply_intdiv_s(in_t a, in_t b) { + int64_t c = sign_extend(a) / sign_extend(b); + REQUIRE(c >= minimum_s && c <= maximum_s); + return static_cast(c); +} + +in_t apply_ceil(in_t input) { + return input value rounded up to nearest integer +} + +in_t apply_clip_s(in_t value, in_t min_val, in_t max_val) { + if (is_floating_point(in_t>) { + REQUIRE(min_val <= max_val); + } + else { + REQUIRE(sign_extend(min_val) <= sign_extend(max_val)); + } + value = apply_max_s(value, min_val); + value = apply_min_s(value, max_val); + return value; +} + +in_t apply_clip_u(in_t value, in_t min_val, in_t max_val) { + REQUIRE(zero_extend(min_val) <= zero_extend(max_val)); + value = apply_max_u(value, min_val); + value = apply_min_u(value, max_val); + return value; +} + +in_t apply_exp(in_t input) { + return e to the power input +} + +in_t apply_floor(in_t input) { + return input value rounded down to nearest integer +} + +in_t apply_log(in_t input) { + if (input == 0) { + return -INFINITY + } + else if (input < 0) { + return NaN; + } + return the natural logarithm of input +} + +in_t apply_logical_rshift(in_t a, in_t b) { + uint64_t c = zero_extend(a) >> zero_extend(b); + return static_cast(c); +} + +in_t apply_max_s(in_t a, in_t b) { + if (is_floating_point(in_t)) { + if (isNaN(a) || isNaN(b)) { + return NaN; + } + if (a >= b) return a; else return b; + } + // Integer version + if (sign_extend(a) >= sign_extend(b)) return a; else return b; +} + +in_t apply_max_u(in_t a, in_t b) { + if (zero_extend(a) >= zero_extend(b)) return a; else return b; +} + +in_t apply_min_s(in_t a, in_t b) { + if (is_floating_point(in_t)) { + if (isNaN(a) || isNaN(b)) { + return NaN; + } + if (a < b) return a; else return b; + } + // Integer version + if (sign_extend(a) < sign_extend(b)) return a; else return b; +} + +in_t apply_min_u(in_t a, in_t b) { + if (zero_extend(a) < zero_extend(b)) return a; else return b; +} + +in_t apply_mul_s(in_t a, in_t b) { + if (is_floating_point(in_t)) return a * b; + int64_t c = sign_extend(a) * sign_extend(b); + return static_cast(c); +} + +in_t apply_pow(in_t a, in_t b) { + return a ** b; // a raised to the power b +} + +in_t apply_sqrt(in_t input) { + return the square root of input +} + +in_t apply_sub_s(in_t a, in_t b) { + if (is_floating_point(in_t)) return a - b; + int64_t c = sign_extend(a) - sign_extend(b); + REQUIRE(c >= minimum_s && c <= maximum_s); + return static_cast(c); +} + +in_t apply_sub_u(in_t a, in_t b) { + uint64_t c = zero_extend(a) - zero_extend(b); + REQUIRE(c >= minimum_u && c <= maximum_u); + return truncate(c); +} + +int32_t count_leading_zeros(int32_t a) { + int32_t acc = 32; + if (a != 0) { + uint32_t mask; + mask = 1 << (32 - 1); // width of int32_t - 1 + acc = 0; + while ((mask & a) == 0) { + mask = mask >> 1; + acc = acc + 1; + } + } + return acc; +} diff --git a/pseudocode/library/broadcast_shape.tosac b/pseudocode/library/broadcast_shape.tosac new file mode 100644 index 0000000..bc3e3ac --- /dev/null +++ b/pseudocode/library/broadcast_shape.tosac @@ -0,0 +1,21 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +shape_t broadcast_shape(shape_t shape1, shape_t shape2) { + ERROR_IF(rank(shape1) != rank(shape2)); + shape_t shape = shape1; + for (int32_t i = 0; i < rank(shape); i++) { + if (shape[i] == 1) { + shape[i] = shape2[i]; + } else { + ERROR_IF(shape2[i] != 1 && shape2[i] != shape[i]); + } + } + return shape; +} diff --git a/pseudocode/library/flatten.tosac b/pseudocode/library/flatten.tosac new file mode 100644 index 0000000..be51833 --- /dev/null +++ b/pseudocode/library/flatten.tosac @@ -0,0 +1,17 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +in_t* flatten(in_t lists[]) { + in_t output = []; + for_each(list in lists) { + for_each(element in list) { + output.append(element); + } + } +} diff --git a/pseudocode/library/generic_helpers.tosac b/pseudocode/library/generic_helpers.tosac new file mode 100644 index 0000000..a9d71ec --- /dev/null +++ b/pseudocode/library/generic_helpers.tosac @@ -0,0 +1,71 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +bool_t is_floating_point(type) { + if (type == fp16_t || type == fp32_t || type == bf16_t) + return true; + return false; +} + +int32_t idiv(int32_t input1, int32_t input2) { + return input1 / input2; // Integer divide that truncates towards zero +} + +// Integer division that checks input1 is a multiple of input2 + +int32_t idiv_check(int32_t input1, int32_t input2) { + ERROR_IF(input1 % input2 != 0); // input1 must be a multiple of input2 + return input1 / input2; // exact quotient without rounding +} + +// perform an integer division with rounding towards minus infinity + +int32_t idiv_floor(int32_t input1, int32_t input2) { + int32_t rval = input1 / input2; + if (rval * input2 > input1) { + rval--; + } + return rval; +} + +int32_t length(in_t input) + return number of elements in input list + +int32_t rank(in_t input) + return rank of an input tensor + +int32_t sum(in_t input[]) + return the sum of values of an input list + +bool isNaN(float input) + return True if floating-point input value is NaN + +float_t pi() + returns value of pi + +float_t sin(angle) + return sine of angle given in radians + +float_t cos(angle) + return cosine of angle given in radians + +bool power_of_two(int32_t value) + return true if value is a power of two, false otherwise + +in_out_t maximum_s + return the maximum value when interpreting type T as a signed value as returned by the make_signed helper. + +in_out_t minimum_s + return the minimum value when interpreting type T as a signed value as returned by the make_signed helper. + +in_out_t maximum_u + return the maximum value when interpreting type T as an unsigned value as returned by the make_unsigned helper. + +in_out_t minimum_u + return the minimum value when interpreting type T as an unsigned value as returned by the make_unsigned helper. diff --git a/pseudocode/library/numeric_accuracy_helpers.tosac b/pseudocode/library/numeric_accuracy_helpers.tosac new file mode 100644 index 0000000..4a2b111 --- /dev/null +++ b/pseudocode/library/numeric_accuracy_helpers.tosac @@ -0,0 +1,52 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +fp64_t exp2(int n) { + if (n < -1075) { + return 0.0; // smaller than smallest denormal + } + REQUIRE(n <= 1023); + fp64_t v = 1.0; + while (n > 0) { v = v*2.0; n--; } + while (n < 0) { v = v/2.0; n++; } + return v; +} + +int ilog2(fp64_t v) { + REQURE(0 < v && v < infinity); + int n = 0; + while (v >= 2.0) { v = v/2.0; n++; } + while (v < 1.0) { v = v*2.0; n--; } + return n; +} + +fp64_t normal_min() { + switch (in_t) { + case fp32_t: return exp2(-126); + case bf16_t: return exp2(-126); + case fp16_t: return exp2( -14); + } +} + +fp64_t normal_max() { + switch (in_t) { + case fp32_t: return exp2(128) - exp2(127-23); + case bf16_t: return exp2(128) - exp2(127- 7); + case fp16_t: return exp2( 16) - exp2( 15-10); + } +} + +// Number of fractional (mantissa bits) +int normal_frac () { + switch (in_t) { + case fp32_t: return 23; + case fp16_t: return 10; + case bf16_t: return 7; + } +} diff --git a/pseudocode/library/numeric_conversion_helpers.tosac b/pseudocode/library/numeric_conversion_helpers.tosac new file mode 100644 index 0000000..fac7078 --- /dev/null +++ b/pseudocode/library/numeric_conversion_helpers.tosac @@ -0,0 +1,28 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +int round_to_nearest_int(float_t f) + Converts the floating-point value to f, with rounding to the nearest integer value. + For the required precision see the section: Main inference precision requirements. + +float_t round_to_nearest_float(in_t f) + Converts the input value into floating-point, rounding to the nearest representable value. + For the required precision see the section: Main inference precision requirements. + +out_t sign_extend(in_t input) + Floating point values are unchanged. + For two's complement integer values where out_t has more bits than in_t, replicate the top bit of input for all bits between the top bit of input and the top bit of output. + +out_t zero_extend(in_t input) + Floating point values are unchanged. + For two's complement integer values where out_t has more bits than in_t, insert zero values for all bits between the top bit of input and the top bit of output. + +out_t truncate(in_t input) + output is the sizeof(out_t) least significant bits in input. + Nop for floating-point types diff --git a/pseudocode/library/tensor_read.tosac b/pseudocode/library/tensor_read.tosac new file mode 100644 index 0000000..9d2a552 --- /dev/null +++ b/pseudocode/library/tensor_read.tosac @@ -0,0 +1,13 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +in_t tensor_read(in_t *address, shape_t shape, shape_t index) { + size_t offset = tensor_index_to_offset(shape, index); + return address[offset]; +} diff --git a/pseudocode/library/tensor_utils.tosac b/pseudocode/library/tensor_utils.tosac new file mode 100644 index 0000000..e8c7c6d --- /dev/null +++ b/pseudocode/library/tensor_utils.tosac @@ -0,0 +1,47 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +// Convert tensor index coordinates to an element offset +size_t tensor_index_to_offset(shape_t shape, shape_t index) { + size_t size = tensor_size(shape); // check tensor shape is valid + size_t offset = 0; + for (int32_t i = 0; i < rank(shape); i++) { + REQUIRE(index[i] >= 0 && index[i] < shape[i]); + offset = offset * shape[i] + index[i]; + } + return offset; +} + +// Convert an element offset to tensor index coordinates +shape_t tensor_offset_to_index(shape_t shape, size_t offset) { + size_t size = tensor_size(shape); // check tensor shape is valid + REQUIRE(offset < size); + shape_t index(rank(shape)); // index has rank(shape) indicies + for(int32_t i = rank(shape) - 1; i >= 0; i--) { + index[i] = offset % shape[i]; + offset /= shape[i]; + } + return index; +} + +// Check the tensor shape is valid and return the tensor size in elements +size_t tensor_size(shape_t shape) { + size_t size = 1; + for (int32_t i = 0; i < rank(shape); i++) { + REQUIRE(1 <= shape[i] && shape[i] <= maximum / size); + size *= shape[i]; + } + return size; +} + +// Return the size of the tensor in the given axis +// For a rank=0 tensor, returns 1 for all axes +size_t shape_dim(shape_t shape, int axis) { + return (axis >= rank(shape)) ? 1 : shape[axis]; +} diff --git a/pseudocode/library/tensor_write.tosac b/pseudocode/library/tensor_write.tosac new file mode 100644 index 0000000..ab634fb --- /dev/null +++ b/pseudocode/library/tensor_write.tosac @@ -0,0 +1,13 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +void tensor_write( *address, shape_t shape, shape_t index, value) { + size_t offset = tensor_index_to_offset(shape, index); + address[offset] = value; +} diff --git a/pseudocode/library/tosa_reference_check.tosac b/pseudocode/library/tosa_reference_check.tosac new file mode 100644 index 0000000..79bd4a6 --- /dev/null +++ b/pseudocode/library/tosa_reference_check.tosac @@ -0,0 +1,38 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +bool tosa_reference_check_fp(in_t test_value, fp64_t ref_value, fp64_t num_ulp) { + fp64_t err_bnd = 0.0; + if (is_normal_fp64(ref_value) && abs(ref_value) != 0) { + int ref_exp = ilog2(abs(ref_value)); + fp64_t ref_pow2 = max(exp2(ref_exp), normal_min); + fp64_t val_ulp = ref_pow2 * exp2(-normal_frac); + err_bnd = val_ulp * num_ulp; + } + return tosa_reference_check_fp_bnd(test_value, ref_value, err_bnd); +} + +bool tosa_reference_check_fp_bnd(in_t test_value, fp64_t ref_value, fp64_t err_bnd) { + if (is_a_NaN(ref_value)) { + return is_a_NaN(test_value); + } + REQUIRE(err_bnd >= 0.0); + if (ref_value < 0) { + ref_value = -ref_value; + test_value = -test_value; + } + fp64_t ref_max = ref_value + err_bnd; + fp64_t ref_min = ref_value - err_bnd; + if (ref_max > normal_max) ref_max = infinity; + if (ref_min > normal_max) ref_min = infinity; + if (ref_max < normal_min) ref_max = normal_min; + if (ref_min < normal_min) ref_min = 0; + return (static_cast(test_value) >= ref_min && + static_cast(test_value) <= ref_max); +} diff --git a/pseudocode/library/type_conversion_helpers.tosac b/pseudocode/library/type_conversion_helpers.tosac new file mode 100644 index 0000000..f26c589 --- /dev/null +++ b/pseudocode/library/type_conversion_helpers.tosac @@ -0,0 +1,63 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +// Returns a signed version of the given type +// A no-op for floating-point types +Type make_signed(Type in_t) +{ + switch(in_t) { + case bool_t: + return bool_t; + case i8_t: + return int8_t; + case i16_t: + return int16_t; + case i32_t: + return int32_t; + case i48_t: + return int48_t; + case fp16_t: + return fp16_t; + case bf16_t: + return bf16_t; + case fp32_t: + return fp32_t; + } +} + +// Returns the usigned type of the given type +// Error to call this with anything but i8_t or i16_t + +Type make_unsigned(Type in_t) +{ + ERROR_IF(in_t != i8_t && in_t != i16_t); + switch(in_t) { + case i8_t: + return uint8_t; + case i16_t: + return uint16_t; + } +} + +out_t static_cast(in_t value) +{ + // Operates similar to the c++ standard static_cast + // Limited to simple numeric conversion for TOSA. + // Sign extends signed integer input types if needed + // Zero extends unsigned integer input types if needed + // Truncates when converting to a smaller width data type + // Conversion from integer to floating-point is exact if possible + // If converting between signless integer types, treated as signed integer +} + +out_t bitcast(in_t value) +{ + // Treats the bits of value as if they were of type out_t + // Only supported for integer types of the same bit width +} diff --git a/pseudocode/library/variable_tensor_allocate.tosac b/pseudocode/library/variable_tensor_allocate.tosac new file mode 100644 index 0000000..4ac83e7 --- /dev/null +++ b/pseudocode/library/variable_tensor_allocate.tosac @@ -0,0 +1,19 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +tensor_t* variable_tensor_allocate(shape_t shape, int32_t uid) { + size_t size = tensor_size(shape); + tensor_t *allocated_tensor = new tensor_t; + allocated_tensor->data = new in_t[size]; + allocated_tensor->uid = uid; + allocated_tensor->is_written = false; + allocated_tensor->shape = shape; + allocated_tensor->type = in_t; + return allocated_tensor; +} diff --git a/pseudocode/library/variable_tensor_lookup.tosac b/pseudocode/library/variable_tensor_lookup.tosac new file mode 100644 index 0000000..4483ab8 --- /dev/null +++ b/pseudocode/library/variable_tensor_lookup.tosac @@ -0,0 +1,19 @@ +// +// This confidential and proprietary software may be used only as +// authorised by a licensing agreement from ARM Limited +// (C) COPYRIGHT 2020-2024 ARM Limited +// ALL RIGHTS RESERVED +// The entire notice above must be reproduced on all authorised +// copies and copies may only be made to the extent permitted +// by a licensing agreement from ARM Limited. + +tensor_t variable_tensor_lookup(int32_t uid) { + // The global all_allocated_variable_tensors was instantiated at the first + // time of executing the tosa graph + for_each(tensor_t allocated_tensor in all_allocated_variable_tensors) { + if (allocated_tensor.uid == uid) { + return allocated_tensor; + } + } + return NULL; +} -- cgit v1.2.1