Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/core/src/op/loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "itt.hpp"
#include "openvino/core/validation_util.hpp"
#include "openvino/op/tensor_iterator.hpp"
#include "openvino/op/util/validation_depth_guard.hpp"
#include "openvino/reference/loop.hpp"
#include "openvino/runtime/tensor.hpp"

Expand All @@ -33,6 +34,8 @@ bool Loop::visit_attributes(AttributeVisitor& visitor) {
void Loop::validate_and_infer_types() {
OV_OP_SCOPE(v5_Loop_validate_and_infer_types);

OV_VALIDATION_DEPTH_GUARD(this, "Loop");

NODE_VALIDATION_CHECK(this, m_bodies.size() == 1, "Number of bodies for loop is greater than 1");

NODE_VALIDATION_CHECK(this, m_input_descriptions.size() == 1, "Loop contains input descriptions for other bodies");
Expand Down
34 changes: 34 additions & 0 deletions src/core/tests/type_prop/loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <gtest/gtest.h>

#include "common_test_utils/test_assertions.hpp"
#include "common_test_utils/type_prop.hpp"
#include "openvino/core/model.hpp"
#include "openvino/op/add.hpp"
Expand All @@ -19,6 +20,7 @@

using namespace std;
using namespace ov;
using testing::HasSubstr;

// trip_count = 10
// execution_condition = true
Expand Down Expand Up @@ -1580,3 +1582,35 @@ TEST(type_prop, loop_merged_static_seed_relaxed_when_body_value_dynamic) {
EXPECT_EQ(body_carried->get_partial_shape(), (PartialShape{1, Dimension::dynamic()}));
EXPECT_TRUE(loop->get_output_partial_shape(0)[1].is_dynamic());
}

static shared_ptr<Node> make_nested_loop(size_t depth) {
auto trip = op::v0::Constant::create(element::i64, Shape{1}, {1});
auto cond = op::v0::Constant::create(element::boolean, Shape{1}, {true});
auto loop = make_shared<op::v5::Loop>(trip, cond);

auto body_data = make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
auto body_cond = make_shared<op::v0::Parameter>(element::boolean, Shape{1});
shared_ptr<Node> body_out;
if (depth > 0) {
body_out = make_nested_loop(depth - 1);
} else {
body_out = body_data;
}
auto body_cond_res = make_shared<op::v0::Result>(body_cond);
auto body_data_res = make_shared<op::v0::Result>(body_out);
auto body = make_shared<Model>(OutputVector{body_cond_res, body_data_res}, ParameterVector{body_data, body_cond});

loop->set_function(body);
loop->set_special_body_ports({-1, 0});
loop->set_invariant_input(body_data, loop->input_value(0));
loop->set_invariant_input(body_cond, cond);
loop->get_iter_value(body_data_res, -1);
return loop;
}

TEST(type_prop, loop_nested_depth_limit_is_rejected) {
const size_t excessive_depth = 1024;
OV_EXPECT_THROW(std::ignore = make_nested_loop(excessive_depth),
ov::NodeValidationFailure,
HasSubstr("nesting depth exceeds"));
}
44 changes: 44 additions & 0 deletions src/core/tests/type_prop/tensor_iterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <map>

#include "common_test_utils/node_builders/reshape.hpp"
#include "common_test_utils/test_assertions.hpp"
#include "common_test_utils/type_prop.hpp"
#include "openvino/core/model.hpp"
#include "openvino/op/add.hpp"
Expand All @@ -15,6 +16,7 @@

using namespace std;
using namespace ov;
using testing::HasSubstr;

TEST(type_prop, tensor_iterator_lstm) {
// That which we iterate over
Expand Down Expand Up @@ -264,3 +266,45 @@ TEST(type_prop, tensor_iterator_dyn_slice) {
PartialShape ref_ps = {N, part_size, I};
EXPECT_EQ(X->get_partial_shape(), ref_ps);
}

static shared_ptr<Node> make_nested_ti(size_t depth) {
auto ti = make_shared<op::v0::TensorIterator>();

auto body_param = make_shared<op::v0::Parameter>(element::f32, PartialShape{1, 1});
shared_ptr<Node> body_out;
if (depth > 0) {
// Nest another TI whose input comes from the outer body parameter
auto inner_ti = make_shared<op::v0::TensorIterator>();
auto inner_param = make_shared<op::v0::Parameter>(element::f32, PartialShape{1, 1});
shared_ptr<Node> inner_out;
if (depth > 1) {
inner_out = make_nested_ti(depth - 1);
} else {
inner_out = inner_param;
}
auto inner_res = make_shared<op::v0::Result>(inner_out);
auto inner_body = make_shared<Model>(OutputVector{inner_res}, ParameterVector{inner_param});
inner_ti->set_body(inner_body);
inner_ti->set_invariant_input(inner_param, body_param);
inner_ti->get_iter_value(inner_res, -1);
body_out = inner_ti;
} else {
body_out = body_param;
}
auto body_res = make_shared<op::v0::Result>(body_out);
auto body = make_shared<Model>(OutputVector{body_res}, ParameterVector{body_param});

ti->set_body(body);
auto data = make_shared<op::v0::Parameter>(element::f32, PartialShape{1, 1});
ti->set_invariant_input(body_param, data);
ti->get_iter_value(body_res, -1);
return ti;
}

// TI already blocks nesting via a hard assert in revalidate_and_infer_types_for_body_ops.
TEST(type_prop, tensor_iterator_nested_is_rejected) {
const size_t excessive_depth = 2;
OV_EXPECT_THROW(std::ignore = make_nested_ti(excessive_depth),
ov::AssertFailure,
HasSubstr("No nested TensorIterator"));
}
Loading