From 7974de934f95d15626e2879c41be9d783f4c32d6 Mon Sep 17 00:00:00 2001 From: zhanmyz Date: Mon, 17 Aug 2026 15:22:40 +0800 Subject: [PATCH] [GPU] Prevent unsafe in-place crop optimization for 2D-to-1D bbox splits Commit 3f24992895e41aa669b3743c8b6a7f499f3d789a introduced runtime padding propagation for the TransposeSplitMatcher QKV pattern. Its axis-1 rank-reducing reshape check also matched object-detection bbox splits: [N, 4] -> [N, 1] -> [N] The resulting in-place crop exposed a padded/strided one-dimensional view to bbox decode arithmetic, producing incorrect box coordinates and NMS results. For example, TF_V2_Faster_RCNN_Inception_ResNet_v2_atrous_coco returned 25 detections instead of the reference 5 detections on GPU. Restrict axis-1 runtime padding propagation to reshape outputs with rank >= 2. This keeps the intended TransposeSplitMatcher path enabled: [-1, 3, H, S] -> [-1, 1, H, S] -> [-1, H, S] while rejecting the unsafe 2D-to-1D bbox-coordinate path. Apply the same rank guard in the VLSDPA-specific check to keep build-time and runtime behavior consistent. Add unit test: prepare_buffer_fusing.in_place_crop_axis1_rank_reducing_to_1d_not_optimized - CVS-192143, CVS-192144, CVS-192235 Signed-off-by: zhanmyz --- .../src/graph/include/reshape_inst.h | 10 +++- .../transformations/transpose_fusion.cpp | 4 +- .../passes/prepare_buffer_fusing_test.cpp | 46 +++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/plugins/intel_gpu/src/graph/include/reshape_inst.h b/src/plugins/intel_gpu/src/graph/include/reshape_inst.h index a1f7b0fedb6289..3747e2d73d4bea 100644 --- a/src/plugins/intel_gpu/src/graph/include/reshape_inst.h +++ b/src/plugins/intel_gpu/src/graph/include/reshape_inst.h @@ -79,6 +79,7 @@ struct typed_program_node : public typed_program_node_base { axis == 1 && !input_pshape[1].is_dynamic() && input_pshape[1].get_length() == 1 && + prim->output_partial_shape.size() >= 2 && prim->output_partial_shape.size() + 1 == input_pshape.size(); if (!is_axis1_size1_squeeze) return false; @@ -134,8 +135,15 @@ struct typed_program_node : public typed_program_node_base { // shape tensor (not a compile-time constant) for the Reshape second input, // leaving output_pattern empty — requiring it would incorrectly block this // case even though conditions 1–3 are fully sufficient. + // Only the TransposeSplitMatcher pattern is safe: crop [batch, 1, H, S] → reshape + // drops the size-1 dim → [batch, H, S]. The output must stay ≥ 2D so that the + // downstream CM/OCL kernel still has at least one inner stride to carry the offset. + // A 2D → 1D squeeze (e.g. NMS bbox-coord split [-1, 4] → [-1, 1] → [-1]) does NOT + // meet this condition and must not be optimized — its downstream arithmetic kernels + // are compiled without dynamic-padding support. if (axis == 1 && !input_pshape[1].is_dynamic() && input_pshape[1].get_length() == 1) { - if (prim->output_partial_shape.size() + 1 == input_pshape.size()) { + if (prim->output_partial_shape.size() >= 2 && + prim->output_partial_shape.size() + 1 == input_pshape.size()) { return true; } } diff --git a/src/plugins/intel_gpu/src/plugin/transformations/transpose_fusion.cpp b/src/plugins/intel_gpu/src/plugin/transformations/transpose_fusion.cpp index 29f7dd436f3067..7dd6774b0edc15 100644 --- a/src/plugins/intel_gpu/src/plugin/transformations/transpose_fusion.cpp +++ b/src/plugins/intel_gpu/src/plugin/transformations/transpose_fusion.cpp @@ -586,8 +586,8 @@ TransposeSplitMatcher::TransposeSplitMatcher() { // This produces 3 outputs of shape [-1, 1, H, S] instead of [1, -1, H, S] auto new_split_axis = ov::op::v0::Constant::create(ov::element::i64, ov::Shape{}, {1}); auto new_split = std::make_shared(input_node, new_split_axis, split->get_num_splits()); - ov::copy_runtime_info(m.get_matched_nodes(), new_split); - ov::replace_node(split, new_split); + ov::copy_runtime_info(m.get_matched_nodes(), new_split); + ov::replace_node(split, new_split); return true; }; diff --git a/src/plugins/intel_gpu/tests/unit/passes/prepare_buffer_fusing_test.cpp b/src/plugins/intel_gpu/tests/unit/passes/prepare_buffer_fusing_test.cpp index 8961df18480fcb..65cb7481ebe43e 100644 --- a/src/plugins/intel_gpu/tests/unit/passes/prepare_buffer_fusing_test.cpp +++ b/src/plugins/intel_gpu/tests/unit/passes/prepare_buffer_fusing_test.cpp @@ -1014,6 +1014,52 @@ TEST(prepare_buffer_fusing, do_runtime_in_place_crop_skips_non_propagatable_resh ASSERT_FALSE(crop_inst->can_be_optimized()); } +TEST(prepare_buffer_fusing, in_place_crop_axis1_rank_reducing_to_1d_not_optimized) { + auto& engine = get_test_engine(); + + constexpr int64_t rows = 8; + const auto input_layout_dynamic = layout{ov::PartialShape{-1, 4}, data_types::f32, format::bfyx}; + auto input_memory = engine.allocate_memory({{rows, 4}, data_types::f32, format::bfyx}); + auto axis_memory = engine.allocate_memory({{}, data_types::i64, format::bfyx}); + auto split_lengths_memory = engine.allocate_memory({{4}, data_types::i64, format::bfyx}); + auto scale_memory = engine.allocate_memory({{1}, data_types::f32, format::bfyx}); + + set_values(input_memory, std::vector(rows * 4, 1.0f)); + set_values(axis_memory, {1}); + set_values(split_lengths_memory, {1, 1, 1, 1}); + set_values(scale_memory, {1.0f}); + + topology topology( + input_layout("input", input_layout_dynamic), + data("axis", axis_memory), + data("split_lengths", split_lengths_memory), + data("scale", scale_memory), + crop("bbox_coordinate", {input_info("input"), input_info("axis"), input_info("split_lengths")}, + tensor(1), + tensor(0), + crop_ngraph_op_mode::variadic_split, + 0, + 1), + reshape("bbox_coordinate_1d", + input_info("bbox_coordinate"), + false, + {-1}, + ov::PartialShape{-1}, + reshape::reshape_mode::base), + eltwise("bbox_coordinate_scale", {input_info("bbox_coordinate_1d"), input_info("scale")}, eltwise_mode::prod), + reorder("output", input_info("bbox_coordinate_scale"), format::bfyx, data_types::f32)); + + ExecutionConfig config = get_test_default_config(engine); + config.set_property(ov::intel_gpu::allow_new_shape_infer(true)); + config.set_property(ov::intel_gpu::optimize_data(true)); + + network network(engine, topology, config); + network.set_input_data("input", input_memory); + network.execute(); + + ASSERT_FALSE(network.get_primitive("bbox_coordinate")->can_be_optimized()); +} + TEST(prepare_buffer_fusing, in_place_crop_dynamic_reshape_unsqueeze) { auto& engine = get_test_engine();