From b786717773c2bf1b707ad0c607a936058afbff7a Mon Sep 17 00:00:00 2001 From: Rickard Bolin Date: Fri, 27 Dec 2024 14:03:28 +0000 Subject: [PATCH] MLBEDSW-10088: Cast shapes and attributes from NumPy to Python types - When using NumPy 2.0 and above, calculations involving both a Python and NumPy integer are no longer implicitly cast to an int64 data type, which can result in overflows. - Cast all shapes and attributes to Python integer as early as possible to avoid accidentally mixing NumPy and Python types in calculations. Change-Id: I11502a58ada8361954af0cf7b1d8c3b5585291a0 Signed-off-by: Rickard Bolin --- ethosu/vela/operation.py | 11 +++++++---- ethosu/vela/operation_util.py | 11 +++++++++-- ethosu/vela/tflite_reader.py | 19 ++++++++++++++++--- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/ethosu/vela/operation.py b/ethosu/vela/operation.py index 9a917f22..f9bf7fe6 100644 --- a/ethosu/vela/operation.py +++ b/ethosu/vela/operation.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2020-2024 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2020-2025 Arm Limited and/or its affiliates # # SPDX-License-Identifier: Apache-2.0 # @@ -752,9 +752,8 @@ class Operation: assert len(self.inputs) == self.attrs["values_count"] else: assert len(axis_tensor.ops) == 1 and axis_tensor.ops[0].type == Op.Const - axis = int(axis_tensor.values) - - return inputs, axis + axis = axis_tensor.values + return inputs, int(axis) def get_dilation_h_w(self): _, dilation_h, dilation_w, _ = self.attrs.get("dilation", (1, 1, 1, 1)) @@ -835,6 +834,10 @@ class Operation: else: assert False + # Cast to Python int + axis = int(axis) if axis is not None else None + offset_start = [int(e) for e in offset_start] if offset_start else None + offset_end = [int(e) for e in offset_end] if offset_end else None return input_tens, outputs, axis, offset_start, offset_end, strides_tens def set_activation_lut(self, lut_tensor): diff --git a/ethosu/vela/operation_util.py b/ethosu/vela/operation_util.py index a7798891..bfe1c9b8 100644 --- a/ethosu/vela/operation_util.py +++ b/ethosu/vela/operation_util.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2020-2024 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2020-2025 Arm Limited and/or its affiliates # # SPDX-License-Identifier: Apache-2.0 # @@ -371,4 +371,11 @@ def create_binary_elementwise( def get_pad_values_from_input(padding) -> Tuple: """Returns top, left, bottom, right, near, far padding from input values in a Pad input tensor""" - return (padding[-3][0], padding[-2][0], padding[-3][1], padding[-2][1], padding[-1][0], padding[-1][1]) + return ( + int(padding[-3][0]), + int(padding[-2][0]), + int(padding[-3][1]), + int(padding[-2][1]), + int(padding[-1][0]), + int(padding[-1][1]), + ) diff --git a/ethosu/vela/tflite_reader.py b/ethosu/vela/tflite_reader.py index ee00ef75..40c48189 100644 --- a/ethosu/vela/tflite_reader.py +++ b/ethosu/vela/tflite_reader.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2020-2024 Arm Limited and/or its affiliates +# SPDX-FileCopyrightText: Copyright 2020-2025 Arm Limited and/or its affiliates # # SPDX-License-Identifier: Apache-2.0 # @@ -84,7 +84,7 @@ class TFLiteSubgraph: def parse_tensor(self, tens_data): np_shape = tens_data.ShapeAsNumpy() - shape = list(np_shape) if isinstance(np_shape, np.ndarray) else [] + shape = [int(i) for i in np_shape] if isinstance(np_shape, (list, np.ndarray)) else [] name = decode_str(tens_data.Name()) tens_dtype = tens_data.Type() dtype = datatype_map[tens_dtype] @@ -207,19 +207,32 @@ class TFLiteSubgraph: op.attrs["out_data_type"] = outputs[0].dtype if "stride_w" in op.attrs: + assert isinstance(op.attrs["stride_h"], (int, np.integer)), "Height stride must be an integer" + assert isinstance(op.attrs["stride_w"], (int, np.integer)), "Width stride must be an integer" + op.attrs["stride_h"] = int(op.attrs["stride_h"]) + op.attrs["stride_w"] = int(op.attrs["stride_w"]) op.attrs["strides"] = (1, op.attrs["stride_h"], op.attrs["stride_w"], 1) if "filter_width" in op.attrs: + assert isinstance(op.attrs["filter_height"], (int, np.integer)), "Filter height must be an integer" + assert isinstance(op.attrs["filter_width"], (int, np.integer)), "Filter width must be an integer" + op.attrs["filter_height"] = int(op.attrs["filter_height"]) + op.attrs["filter_width"] = int(op.attrs["filter_width"]) op.attrs["ksize"] = (1, op.attrs["filter_height"], op.attrs["filter_width"], 1) if "dilation_w_factor" in op.attrs: + assert isinstance(op.attrs["dilation_h_factor"], (int, np.integer)), "Height dilation must be an integer" + assert isinstance(op.attrs["dilation_w_factor"], (int, np.integer)), "Width dilation must be an integer" + op.attrs["dilation_h_factor"] = int(op.attrs["dilation_h_factor"]) + op.attrs["dilation_w_factor"] = int(op.attrs["dilation_w_factor"]) op.attrs["dilation"] = (1, op.attrs["dilation_h_factor"], op.attrs["dilation_w_factor"], 1) if "depth_multiplier" in op.attrs: + op.attrs["depth_multiplier"] = int(op.attrs["depth_multiplier"]) op.attrs["channel_multiplier"] = op.attrs["depth_multiplier"] if op_type == Op.DepthwiseConv2DBias and op.attrs["depth_multiplier"] == 0: # The depth multiplier is implicit and is calculated as weight channels / ifm channels # Note however that the weights have been reshaped above. # The original value is cached above in channel_multiplier - op.attrs["depth_multiplier"] = op.weights.shape[2] // op.ifm.shape[-1] + op.attrs["depth_multiplier"] = int(op.weights.shape[2] // op.ifm.shape[-1]) # The fused_activation_function attribute needs to be retained so that the # tflite_writer can correctly pass through operators that run on the CPU. -- GitLab