diff --git a/runtime/compute/cker/include/cker/Shape.h b/runtime/compute/cker/include/cker/Shape.h index 4c02f77f42b..37732b3a2ad 100644 --- a/runtime/compute/cker/include/cker/Shape.h +++ b/runtime/compute/cker/include/cker/Shape.h @@ -333,6 +333,18 @@ inline int Offset(const Shape &shape, int i0, int i1, int i2, int i3) return ((i0 * dims_data[1] + i1) * dims_data[2] + i2) * dims_data[3] + i3; } +inline int Offset(const Shape &shape, int i0, int i1, int i2, int i3, int i4) +{ + assert(shape.DimensionsCount() == 5); + const int *dim = shape.DimsDataUpTo6D(); + assert(i0 >= 0 && i0 < dim[0]); + assert(i1 >= 0 && i1 < dim[1]); + assert(i2 >= 0 && i2 < dim[2]); + assert(i3 >= 0 && i3 < dim[3]); + assert(i4 >= 0 && i4 < dim[4]); + return ((((i0 * dim[1] + i1) * dim[2] + i2) * dim[3] + i3) * dim[4]) + i4; +} + inline int Offset(const Shape &shape, int i0, int i1, int i2, int i3, int i4, int i5) { assert(shape.DimensionsCount() == 6); diff --git a/runtime/compute/cker/include/cker/Types.h b/runtime/compute/cker/include/cker/Types.h index c497305c926..5457cbadaf8 100644 --- a/runtime/compute/cker/include/cker/Types.h +++ b/runtime/compute/cker/include/cker/Types.h @@ -367,11 +367,11 @@ struct SliceParams struct StridedSliceParams { int8_t start_indices_count; - int16_t start_indices[4]; + int32_t start_indices[5]; int8_t stop_indices_count; - int16_t stop_indices[4]; + int32_t stop_indices[5]; int8_t strides_count; - int16_t strides[4]; + int32_t strides[5]; int16_t begin_mask; int16_t ellipsis_mask; diff --git a/runtime/compute/cker/include/cker/operation/StridedSlice.h b/runtime/compute/cker/include/cker/operation/StridedSlice.h index 894118a5e54..c817aab02f8 100644 --- a/runtime/compute/cker/include/cker/operation/StridedSlice.h +++ b/runtime/compute/cker/include/cker/operation/StridedSlice.h @@ -42,7 +42,7 @@ inline int Clamp(const int v, const int lo, const int hi) inline void StridedSlicePadIndices(StridedSliceParams *p, int dim_count) { // Add indices and mask bits to fully include extra dimensions - assert(dim_count <= 4); + assert(dim_count <= 5); assert(dim_count >= p->start_indices_count); assert(p->start_indices_count == p->stop_indices_count); assert(p->stop_indices_count == p->strides_count); @@ -258,8 +258,8 @@ template inline void StridedSlice(const StridedSliceParams &op_params, const Shape &unextended_input_shape, const T *input_data, const Shape &unextended_output_shape, T *output_data) { - assert(unextended_input_shape.DimensionsCount() <= 4); - assert(unextended_output_shape.DimensionsCount() <= 4); + assert(unextended_input_shape.DimensionsCount() <= 5); + assert(unextended_output_shape.DimensionsCount() <= 5); bool optimize = true; int st_count = op_params.strides_count; @@ -293,36 +293,42 @@ inline void StridedSlice(const StridedSliceParams &op_params, const Shape &unext // Note that the output_shape is not used herein. StridedSliceParams params_copy = op_params; - const Shape input_shape = Shape::ExtendedShape(4, unextended_input_shape); - const Shape output_shape = Shape::ExtendedShape(4, unextended_output_shape); + const Shape input_shape = Shape::ExtendedShape(5, unextended_input_shape); + const Shape output_shape = Shape::ExtendedShape(5, unextended_output_shape); - // Reverse and pad to 4 dimensions because that is what the runtime code - // requires (ie. all shapes must be 4D and are given backwards). - StridedSlicePadIndices(¶ms_copy, 4); + // Reverse and pad to 5 dimensions because that is what the runtime code + // requires (ie. all shapes must be 5D and are given backwards). + StridedSlicePadIndices(¶ms_copy, 5); - const int start_b = StartForAxis(params_copy, input_shape, 0); - const int stop_b = StopForAxis(params_copy, input_shape, 0, start_b); - const int start_h = StartForAxis(params_copy, input_shape, 1); - const int stop_h = StopForAxis(params_copy, input_shape, 1, start_h); - const int start_w = StartForAxis(params_copy, input_shape, 2); - const int stop_w = StopForAxis(params_copy, input_shape, 2, start_w); - const int start_d = StartForAxis(params_copy, input_shape, 3); - const int stop_d = StopForAxis(params_copy, input_shape, 3, start_d); + const int start_0 = StartForAxis(params_copy, input_shape, 0); + const int stop_0 = StopForAxis(params_copy, input_shape, 0, start_0); + const int start_1 = StartForAxis(params_copy, input_shape, 1); + const int stop_1 = StopForAxis(params_copy, input_shape, 1, start_1); + const int start_2 = StartForAxis(params_copy, input_shape, 2); + const int stop_2 = StopForAxis(params_copy, input_shape, 2, start_2); + const int start_3 = StartForAxis(params_copy, input_shape, 3); + const int stop_3 = StopForAxis(params_copy, input_shape, 3, start_3); + const int start_4 = StartForAxis(params_copy, input_shape, 4); + const int stop_4 = StopForAxis(params_copy, input_shape, 4, start_4); T *out_ptr = output_data; - for (int in_b = start_b; !LoopCondition(in_b, stop_b, params_copy.strides[0]); - in_b += params_copy.strides[0]) + for (int in_0 = start_0; !LoopCondition(in_0, stop_0, params_copy.strides[0]); + in_0 += params_copy.strides[0]) { - for (int in_h = start_h; !LoopCondition(in_h, stop_h, params_copy.strides[1]); - in_h += params_copy.strides[1]) + for (int in_1 = start_1; !LoopCondition(in_1, stop_1, params_copy.strides[1]); + in_1 += params_copy.strides[1]) { - for (int in_w = start_w; !LoopCondition(in_w, stop_w, params_copy.strides[2]); - in_w += params_copy.strides[2]) + for (int in_2 = start_2; !LoopCondition(in_2, stop_2, params_copy.strides[2]); + in_2 += params_copy.strides[2]) { - for (int in_d = start_d; !LoopCondition(in_d, stop_d, params_copy.strides[3]); - in_d += params_copy.strides[3]) + for (int in_3 = start_3; !LoopCondition(in_3, stop_3, params_copy.strides[3]); + in_3 += params_copy.strides[3]) { - *out_ptr++ = input_data[Offset(input_shape, in_b, in_h, in_w, in_d)]; + for (int in_4 = start_4; !LoopCondition(in_4, stop_4, params_copy.strides[4]); + in_4 += params_copy.strides[4]) + { + *out_ptr++ = input_data[Offset(input_shape, in_0, in_1, in_2, in_3, in_4)]; + } } } } diff --git a/runtime/compute/cker/src/Shape.test.cc b/runtime/compute/cker/src/Shape.test.cc index 6bc87a7d45f..26d94380efa 100644 --- a/runtime/compute/cker/src/Shape.test.cc +++ b/runtime/compute/cker/src/Shape.test.cc @@ -286,6 +286,18 @@ TEST(ShapeTest, neg_Offset4DNegativeIndex) ".*"); } +// Test that calling Offset on a 5D shape with a negative index triggers an assertion. +TEST(ShapeTest, neg_Offset5DNegativeIndex) +{ + Shape s{2, 3, 4, 5, 6}; + EXPECT_EXIT_BY_ABRT_DEBUG_ONLY( + { + int offset = Offset(s, 1, 1, -1, 1, 1); + (void)offset; + }, + ".*"); +} + // Test that calling Offset on a 6D shape with an out-of-range index triggers an assertion. TEST(ShapeTest, neg_Offset6DIndexOutOfRange) { diff --git a/runtime/compute/cker/src/StridedSlice.test.cc b/runtime/compute/cker/src/StridedSlice.test.cc new file mode 100644 index 00000000000..722b68b542b --- /dev/null +++ b/runtime/compute/cker/src/StridedSlice.test.cc @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved + * + * 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 +#include + +#include +#include + +#include "DeathTestMacro.h" + +TEST(CKer_Operation, StridedSlice5D) +{ + nnfw::cker::StridedSliceParams op_params{}; + op_params.start_indices_count = 5; + op_params.stop_indices_count = 5; + op_params.strides_count = 5; + + op_params.stop_indices[0] = 1; + op_params.stop_indices[1] = 1; + op_params.stop_indices[2] = 2; + op_params.stop_indices[3] = 2; + op_params.stop_indices[4] = 2; + + op_params.strides[0] = 1; + op_params.strides[1] = 1; + op_params.strides[2] = 1; + op_params.strides[3] = 1; + op_params.strides[4] = 1; + + nnfw::cker::Shape input_shape{2, 1, 2, 2, 2}; + std::vector input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + nnfw::cker::Shape output_shape{1, 1, 2, 2, 2}; + std::vector output(output_shape.FlatSize()); + + nnfw::cker::StridedSlice(op_params, input_shape, input.data(), output_shape, output.data()); + + std::vector expected_output = {0, 1, 2, 3, 4, 5, 6, 7}; + for (size_t i = 0; i < expected_output.size(); ++i) + EXPECT_NEAR(output[i], expected_output[i], 1e-5f); +} + +TEST(CKer_Operation, neg_StridedSliceNotSupportedDims) +{ + nnfw::cker::StridedSliceParams op_params{}; + op_params.start_indices_count = 5; + op_params.stop_indices_count = 5; + op_params.strides_count = 5; + + op_params.stop_indices[0] = 1; + op_params.stop_indices[1] = 1; + op_params.stop_indices[2] = 2; + op_params.stop_indices[3] = 2; + op_params.stop_indices[4] = 2; + + op_params.strides[0] = 1; + op_params.strides[1] = 1; + op_params.strides[2] = 1; + op_params.strides[3] = 1; + op_params.strides[4] = 1; + + nnfw::cker::Shape input_shape{2, 1, 2, 2, 2, 1}; + std::vector input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + nnfw::cker::Shape output_shape{1, 1, 2, 2, 2, 1}; + std::vector output(output_shape.FlatSize()); + + EXPECT_EXIT_BY_ABRT_DEBUG_ONLY( + { + nnfw::cker::StridedSlice(op_params, input_shape, input.data(), output_shape, output.data()); + }, + ".*"); +} diff --git a/runtime/onert/core/include/util/ShapeInference.h b/runtime/onert/core/include/util/ShapeInference.h index 1e0f2656968..7ff509740be 100644 --- a/runtime/onert/core/include/util/ShapeInference.h +++ b/runtime/onert/core/include/util/ShapeInference.h @@ -111,11 +111,11 @@ ir::Shape inferSqueezeShape(const ir::Shape &in_shape, const ir::operation::Sque struct StridedSliceParams { int8_t start_indices_count; - int16_t start_indices[4]; + int32_t start_indices[5]; int8_t stop_indices_count; - int16_t stop_indices[4]; + int32_t stop_indices[5]; int8_t strides_count; - int16_t strides[4]; + int32_t strides[5]; int16_t begin_mask; int16_t ellipsis_mask; diff --git a/runtime/onert/core/src/compiler/ShapeValidator.cc b/runtime/onert/core/src/compiler/ShapeValidator.cc index bc91f17c768..69017cd64a0 100644 --- a/runtime/onert/core/src/compiler/ShapeValidator.cc +++ b/runtime/onert/core/src/compiler/ShapeValidator.cc @@ -977,7 +977,7 @@ void ShapeValidator::visit(const ir::operation::StridedSlice &node) if (operands.at(output_index).info().isDynamic()) return; - OP_REQUIRES(operands.at(input_index).shape().rank() <= 4); + OP_REQUIRES(operands.at(input_index).shape().rank() <= 5); } void ShapeValidator::visit(const ir::operation::Split &node) diff --git a/runtime/tests/nnfw_api/src/GenModelTests/one_op_tests/StridedSlice.test.cc b/runtime/tests/nnfw_api/src/GenModelTests/one_op_tests/StridedSlice.test.cc index 9c00e6d89f3..36e1964e354 100644 --- a/runtime/tests/nnfw_api/src/GenModelTests/one_op_tests/StridedSlice.test.cc +++ b/runtime/tests/nnfw_api/src/GenModelTests/one_op_tests/StridedSlice.test.cc @@ -42,6 +42,57 @@ TEST_F(GenModelTest, OneOp_StridedSlice_LastDim) SUCCEED(); } +TEST_F(GenModelTest, OneOp_StridedSlice_5D) +{ + CircleGen cgen; + std::vector begin_data{0, 0, 0, 0, 0}; + std::vector end_data{1, 1, 2, 2, 2}; + std::vector strides_data{1, 1, 1, 1, 1}; + uint32_t begin_buf = cgen.addBuffer(begin_data); + uint32_t end_buf = cgen.addBuffer(end_data); + uint32_t strides_buf = cgen.addBuffer(strides_data); + int input = cgen.addTensor({{2, 1, 2, 2, 2}, circle::TensorType::TensorType_FLOAT32}); + int begin = cgen.addTensor({{5}, circle::TensorType::TensorType_INT32, begin_buf}); + int end = cgen.addTensor({{5}, circle::TensorType::TensorType_INT32, end_buf}); + int strides = cgen.addTensor({{5}, circle::TensorType::TensorType_INT32, strides_buf}); + int out = cgen.addTensor({{1, 1, 2, 2, 2}, circle::TensorType::TensorType_FLOAT32}); + cgen.addOperatorStridedSlice({{input, begin, end, strides}, {out}}, 0, 0); + cgen.setInputsAndOutputs({input}, {out}); + + _context = std::make_unique(cgen.finish()); + _context->addTestCase(uniformTCD({{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, + {{0, 1, 2, 3, 4, 5, 6, 7}})); + _context->setBackends({"acl_cl", "acl_neon", "cpu"}); + + SUCCEED(); +} + +TEST_F(GenModelTest, neg_OneOp_StridedSlice_UnsupportedDims) +{ + CircleGen cgen; + std::vector begin_data{0, 0, 0, 0, 0}; + std::vector end_data{1, 1, 2, 2, 2}; + std::vector strides_data{1, 1, 1, 1, 1}; + uint32_t begin_buf = cgen.addBuffer(begin_data); + uint32_t end_buf = cgen.addBuffer(end_data); + uint32_t strides_buf = cgen.addBuffer(strides_data); + int input = cgen.addTensor({{2, 1, 2, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32}); + int begin = cgen.addTensor({{5}, circle::TensorType::TensorType_INT32, begin_buf}); + int end = cgen.addTensor({{5}, circle::TensorType::TensorType_INT32, end_buf}); + int strides = cgen.addTensor({{5}, circle::TensorType::TensorType_INT32, strides_buf}); + int out = cgen.addTensor({{1, 1, 2, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32}); + cgen.addOperatorStridedSlice({{input, begin, end, strides}, {out}}, 0, 0); + cgen.setInputsAndOutputs({input}, {out}); + + _context = std::make_unique(cgen.finish()); + _context->addTestCase(uniformTCD({{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, + {{0, 1, 2, 3, 4, 5, 6, 7}})); + _context->setBackends({"acl_cl", "acl_neon", "cpu"}); + _context->expectFailCompile(); + + SUCCEED(); +} + TEST_F(GenModelTest, neg_OneOp_StridedSlice_DifferentType) { CircleGen cgen;