@@ -229,14 +229,62 @@ static void CreateConstantOp(ProgramBuilder& p, const std::shared_ptr<ov::op::v0
229229
230230 return false ;
231231 };
232- // WA to inconsistency between input and const 1d tensors
233- // For Concat along batch we go with batch interpretation
234- // For Gather input we go with batch interpretation
235- // Also check if constant users is a backprop convolution - in that case O and I need to be swapped.
236- for (auto & node : constUsers) {
237- auto outOp = node.get_node ();
238- bool apply_rank2_matmul_wa = false ;
232+
233+ auto apply_common_consumer_adaptation = [&](const ov::Input<ov::Node>& node, ov::Node* outOp) -> bool {
234+ if (auto castedOp = ov::as_type<ov::op::v0::Concat>(outOp)) {
235+ if (castedOp->get_axis () == 0 ) {
236+ consts[op].needsBatchInterpretation = constDims.size () == 1 ;
237+ }
238+ return true ;
239+ }
240+
241+ if (((is_binary_eltwise (outOp) || ov::is_type<ov::op::v0::SquaredDifference>(outOp)) && is_all_inputs_1d (outOp)) ||
242+ is_convert_into_binary_eltwise (outOp)) {
243+ consts[op].needsBatchInterpretation = constDims.size () == 1 ;
244+ return true ;
245+ }
246+
247+ if (ov::is_type<ov::op::v1::Gather>(outOp) ||
248+ ov::is_type<ov::op::v7::Gather>(outOp) ||
249+ ov::is_type<ov::op::v8::Gather>(outOp) ||
250+ ov::is_type<ov::op::v5::GatherND>(outOp) ||
251+ ov::is_type<ov::op::v8::GatherND>(outOp) ||
252+ ov::is_type<ov::op::v1::Split>(outOp) ||
253+ ov::is_type<ov::op::v1::VariadicSplit>(outOp)) {
254+ consts[op].needsBatchInterpretation = constDims.size () == 1 ;
255+ return true ;
256+ }
257+
258+ if (ov::is_type<ov::op::v0::PRelu>(outOp) && node.get_index () == 1 ) {
259+ auto input_shape = outOp->get_input_partial_shape (0 );
260+ if ((constDims.size () != 1 && constDims.size () < input_shape.size ()) ||
261+ (constDims.size () == 1 && input_shape.is_static () && input_shape.size () > 1 &&
262+ static_cast <int64_t >(constDims[0 ]) != input_shape[1 ].get_length ())) {
263+ ov::Shape slope_shape (input_shape.size (), 1 );
264+ for (size_t j = 1 ; j <= constDims.size (); j++)
265+ slope_shape[slope_shape.size () - j] = constDims[constDims.size () - j];
266+ constDims = slope_shape;
267+ }
268+ return true ;
269+ }
270+
271+ if (ov::is_type<ov::op::v3::ROIAlign>(outOp) || ov::is_type<ov::op::v9::ROIAlign>(outOp) ||
272+ ov::is_type<ov::op::v15::ROIAlignRotated>(outOp)) {
273+ consts[op].needsBatchInterpretation = constDims.size () == 1 ;
274+ return true ;
275+ }
276+
277+ if (ov::is_type<ov::op::v5::Loop>(outOp) || ov::is_type<ov::op::v0::TensorIterator>(outOp)) {
278+ consts[op].needsBatchInterpretation = constDims.size () == 1 ;
279+ return true ;
280+ }
281+
282+ return false ;
283+ };
284+
285+ auto apply_legacy_shape_layout_wa = [&](const ov::Input<ov::Node>& node, ov::Node* outOp) -> bool {
239286 size_t user_index = node.get_index ();
287+ bool apply_rank2_matmul_wa = false ;
240288 auto is_convert_matmul_pattern = [&](ov::Node* convert_node, size_t & matmul_input_index_ref) -> bool {
241289 if (ov::is_type<ov::op::v0::Convert>(convert_node) && !p.use_new_shape_infer ()) {
242290 auto convert_consumers = convert_node->get_output_target_inputs (0 );
@@ -253,89 +301,54 @@ static void CreateConstantOp(ProgramBuilder& p, const std::shared_ptr<ov::op::v0
253301 }
254302 return false ;
255303 };
256- if (auto castedOp = ov::as_type<ov::op::v0::Concat>(outOp)) {
257- if (castedOp->get_axis () == 0 ) {
258- consts[op].needsBatchInterpretation = constDims.size () == 1 ;
259- }
260- } else if (((is_binary_eltwise (outOp) || ov::is_type<ov::op::v0::SquaredDifference>(outOp)) && is_all_inputs_1d (outOp)) ||
261- is_convert_into_binary_eltwise (outOp)) {
262- consts[op].needsBatchInterpretation = constDims.size () == 1 ;
263- } else if (ov::is_type<ov::op::v1::Gather>(outOp) ||
264- ov::is_type<ov::op::v7::Gather>(outOp) ||
265- ov::is_type<ov::op::v8::Gather>(outOp) ||
266- ov::is_type<ov::op::v5::GatherND>(outOp) ||
267- ov::is_type<ov::op::v8::GatherND>(outOp) ||
268- ov::is_type<ov::op::v1::Split>(outOp) ||
269- ov::is_type<ov::op::v1::VariadicSplit>(outOp)) {
270- consts[op].needsBatchInterpretation = constDims.size () == 1 ;
271- } else if (ov::is_type<ov::op::v0::PRelu>(outOp) && node.get_index () == 1 ) {
272- // PReLU slope tensor reshape policy
273- //
274- // 1. 1-dim slope is handled by 'getConstTensor' (if slope dimension is equal to the feature dimension of input).
275- // ex) [1] --> [1, 1, 1, 1]
276- // [N] --> [1, N, 1, 1]
277- //
278- // 2. Multi-dims slope tensor is handled by the numpy broadcasting rule that is defined at
279- // 'https://docs.openvino.ai/2023.0/openvino_docs_ops_broadcast_rules.html'.
280- // ex) [N, 1, 1] --> [1, N, 1, 1]
281- // [N, M, 1] --> [1, N, M, 1]
282- auto input_shape = outOp->get_input_partial_shape (0 );
283- if ((constDims.size () != 1 && constDims.size () < input_shape.size ()) ||
284- (constDims.size () == 1 && input_shape.is_static () && input_shape.size () > 1 &&
285- static_cast <int64_t >(constDims[0 ]) != input_shape[1 ].get_length ())) {
286- // Reshape 'constDims' according to the numpy broadcasting rule.
287- ov::Shape slope_shape (input_shape.size (), 1 );
288- for (size_t j = 1 ; j <= constDims.size (); j++)
289- slope_shape[slope_shape.size () - j] = constDims[constDims.size () - j];
290- constDims = slope_shape;
291- }
292- } else if (is_grouped_conv (outOp) && node.get_index () == 1 && !p.use_new_shape_infer ()) {
304+
305+ if (is_grouped_conv (outOp) && node.get_index () == 1 && !p.use_new_shape_infer ()) {
293306 auto input_shape = outOp->get_input_partial_shape (0 );
294- if (constDims.size () == 4 && input_shape.size () == 3 ) { // In case of weight dim 4 and input dim 3,
295- constDims.push_back (1 ); // The weight cldnn tensor adds 1d to the end as the input cldnn tensor does
307+ if (constDims.size () == 4 && input_shape.size () == 3 ) {
308+ constDims.push_back (1 );
296309 }
297- } else if (ov::is_type<ov::op::v3::ROIAlign>(outOp) || ov::is_type<ov::op::v9::ROIAlign>(outOp) ||
298- ov::is_type<ov::op::v15::ROIAlignRotated>(outOp)) { // < Hacks...
299- consts[op].needsBatchInterpretation = constDims.size () == 1 ;
300- } else if ((ov::is_type<ov::op::v5::Loop>(outOp) || ov::is_type<ov::op::v0::TensorIterator>(outOp))) {
301- // when inner network has 1d parameter which is connected to outer loop's constant 1d data,
302- // outer constant 1d data and inner 1d parameter has same bytes_count but layout is different
303- // (outer constant is [1, N, 1, 1] but inner parameter is [N, 1, 1, 1]).
304- // To pass check_memory_to_set in input_layout::set_data for this case, Set constDims to [N, 1, 1, 1]
305- // when constDims is one dim and user op is Loop or TensorIterator.
306- consts[op].needsBatchInterpretation = constDims.size () == 1 ;
307- } else if (ov::is_type<ov::op::v0::Result>(outOp) && !p.use_new_shape_infer () && p.is_inner_program ()) {
308- // When IF-operation generates branch-true and branch-false,
309- // simple nodes for both can be created such as Parameter->Result, Constant->Result
310- // And each layout will be like Parameter->Result [N, 1, 1, 1], Constant->Result [1, N, 1, 1], that produces layout mismatch error.
311- // For that case, Constant->Result needs to be [N, 1, 1, 1]
310+ return true ;
311+ }
312+
313+ if (ov::is_type<ov::op::v0::Result>(outOp) && !p.use_new_shape_infer () && p.is_inner_program ()) {
312314 consts[op].needsBatchInterpretation = constDims.size () == 1 ;
313- } else if (is_convert_matmul_pattern (outOp, user_index)) {
315+ return true ;
316+ }
317+
318+ if (is_convert_matmul_pattern (outOp, user_index)) {
314319 const size_t const_static_max_dims = 4 ;
315- // MatMul constant reshape WA (legacy shape infer path):
316- // Only reshape 1D and 2D constants to fix getConstTensor batch/feature
317- // mis-mapping. For rank >= 3, getConstTensor already produces layouts
318- // compatible with gemm_inst::transform_input_layouts (trailing 1s align
319- // with weight_rank extraction). Reshaping rank >= 3 would prepend 1s,
320- // breaking the first-N-dims extraction in transform_input_layouts.
321320 if (constDims.size () == 1 ) {
322321 ov::Shape reshaped_const_dims (const_static_max_dims, 1 );
323- const size_t const_idx = (user_index == 0 )?
322+ const size_t const_idx = (user_index == 0 ) ?
324323 (const_static_max_dims - 1 )
325324 : (const_static_max_dims - 2 );
326325 reshaped_const_dims[const_idx] = constDims[0 ];
327326 constDims = std::move (reshaped_const_dims);
328327 } else if (constDims.size () == 2 && user_index == 0 && apply_rank2_matmul_wa) {
329328 ov::Shape reshaped_const_dims (const_static_max_dims, 1 );
330- // For MatMul input0, gemm::transform_input_layouts takes the first input_rank dims.
331- // Keep [M, K] in leading positions and append trailing 1s.
332329 const size_t offset = 0 ;
333330 for (size_t i = 0 ; i < constDims.size (); ++i) {
334331 reshaped_const_dims[offset + i] = constDims[i];
335332 }
336333 constDims = std::move (reshaped_const_dims);
337334 }
335+ return true ;
336+ }
337+
338+ return false ;
339+ };
340+
341+ // WA to inconsistency between input and const 1d tensors
342+ // For Concat along batch we go with batch interpretation
343+ // For Gather input we go with batch interpretation
344+ // Also check if constant users is a backprop convolution - in that case O and I need to be swapped.
345+ for (auto & node : constUsers) {
346+ auto outOp = node.get_node ();
347+ if (apply_common_consumer_adaptation (node, outOp)) {
348+ continue ;
338349 }
350+
351+ apply_legacy_shape_layout_wa (node, outOp);
339352 }
340353
341354 for (auto & it : consts) {
0 commit comments