@@ -33,22 +33,43 @@ Status PadOpBuilder::ProcessInputs(QnnModelWrapper& qnn_model_wrapper,
3333 std::vector<std::string>& input_names,
3434 bool do_op_validation) const {
3535 const auto & inputs = node_unit.Inputs ();
36- // QNN Pad only has 1 input, the pads input & constant_value input need to be initializer and set as Qnn node parameter, axes input is not supported.
36+ // QNN Pad only has 1 input, the pads input & constant_value input need to be initializer (opset >= 11) and set as Qnn node
37+ // parameter, axes input is not supported.
3738 if (do_op_validation) {
38- ORT_RETURN_IF (inputs.size () > 3 , " QNN Pad doesn't support axes." );
39- ORT_RETURN_IF (inputs.size () < 2 , " QNN Pad requires the pads input." );
39+ const int opset_version = node_unit.SinceVersion ();
40+ const std::string domain = node_unit.Domain ();
41+
42+ if (domain == kMSDomain ) {
43+ // Pad in the com.microsoft domain accepts 2-3 inputs (data, pads, value).
44+ ORT_RETURN_IF (inputs.size () < 2 , " QNN Pad requires the pads input." );
45+ } else {
46+ // Pad in the ONNX domain accepts only 1 input (data) before opset 11.
47+ // For opset 11 and after, it accepts 2-4 inputs (data, pads, constant_value, axes), although QNN pad
48+ // does not support the axes input.
49+
50+ // Reject Pad opset 1, which differs slightly from Pad for 2 <= opset < 11.
51+ // We could support it, but nodes below opset 7 should be rejected earlier by ORT, anyway.
52+ ORT_RETURN_IF (opset_version < 2 , " Pad with opset < 2 is not supported" );
53+
54+ ORT_RETURN_IF (opset_version < 11 && inputs.size () > 1 ,
55+ " Pads should be specified in an attribute for opset < 11" );
56+ ORT_RETURN_IF (opset_version >= 11 && inputs.size () > 3 , " QNN Pad doesn't support axes." );
57+ ORT_RETURN_IF (opset_version >= 11 && inputs.size () < 2 , " QNN Pad requires the pads input." );
58+ }
4059
4160 std::vector<uint32_t > input_shape;
4261 ORT_RETURN_IF_NOT (qnn_model_wrapper.GetOnnxShape (inputs[0 ].node_arg , input_shape), " Cannot get shape of input 0." );
43- ORT_RETURN_IF (input_shape.size () > 5 , " QNN Pad doesn't support more than 5 dimension" );
44-
45- auto & pads_input_name = inputs[1 ].node_arg .Name ();
46- ORT_RETURN_IF_NOT (qnn_model_wrapper.IsConstantInput (pads_input_name),
47- " Qnn doesn't support dynamic pad input" );
48- if (inputs.size () > 2 && inputs[2 ].node_arg .Exists ()) {
49- auto & constant_value_input_name = inputs[2 ].node_arg .Name ();
50- ORT_RETURN_IF_NOT (qnn_model_wrapper.IsConstantInput (constant_value_input_name),
51- " Qnn doesn't support dynamic constant_value input" );
62+ ORT_RETURN_IF (input_shape.size () > 5 , " QNN Pad doesn't support more than 5 dimensions" );
63+
64+ if (opset_version >= 11 || domain == kMSDomain ) {
65+ auto & pads_input_name = inputs[1 ].node_arg .Name ();
66+ ORT_RETURN_IF_NOT (qnn_model_wrapper.IsConstantInput (pads_input_name),
67+ " Qnn doesn't support dynamic pad input" );
68+ if (inputs.size () > 2 && inputs[2 ].node_arg .Exists ()) {
69+ auto & constant_value_input_name = inputs[2 ].node_arg .Name ();
70+ ORT_RETURN_IF_NOT (qnn_model_wrapper.IsConstantInput (constant_value_input_name),
71+ " Qnn doesn't support dynamic constant_value input" );
72+ }
5273 }
5374 }
5475
@@ -175,18 +196,35 @@ Status PadOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wrap
175196 const logging::Logger& logger,
176197 bool do_op_validation) const {
177198 std::vector<std::string> param_tensor_names;
178- // Process pads input
179- // Already confirmed pads input is initializer in ProcessInputs()
199+ const int opset_version = node_unit.SinceVersion ();
200+ const std::string domain = node_unit.Domain ();
201+
180202 const auto & inputs = node_unit.Inputs ();
181- const auto & pads_input_name = inputs[1 ].node_arg .Name ();
203+ NodeAttrHelper node_helper (node_unit);
204+
205+ const int64_t * tensor_data = nullptr ;
206+ size_t size = 0 ;
182207
208+ const auto pads_attr = node_helper.GetInt64s (" pads" );
183209 std::vector<uint8_t > unpacked_tensor;
184- const auto & input_tensor = qnn_model_wrapper.GetConstantTensor (pads_input_name);
185- ORT_RETURN_IF_ERROR (qnn_model_wrapper.UnpackInitializerData (*input_tensor, unpacked_tensor));
186- // Onnx Pads are int64, Qnn use uint32
187- const int64_t * tensor_data = reinterpret_cast <const int64_t *>(unpacked_tensor.data ());
188- size_t tensor_byte_size = unpacked_tensor.size ();
189- size_t size = tensor_byte_size / sizeof (int64_t );
210+
211+ if (opset_version < 11 && domain != kMSDomain ) {
212+ // Process pads attribute
213+ // NodeAttrHelper::GetInt64s returns an allocated copy, not a view, so we must call it in the outer scope
214+ ORT_RETURN_IF_NOT (pads_attr.has_value (), " Failed to get pads attribute." );
215+ tensor_data = pads_attr.value ().data ();
216+ size = pads_attr.value ().size ();
217+ } else {
218+ // Process pads input
219+ // Already confirmed pads input is initializer in ProcessInputs()
220+ const auto & pads_input_name = inputs[1 ].node_arg .Name ();
221+
222+ const auto & input_tensor = qnn_model_wrapper.GetConstantTensor (pads_input_name);
223+ ORT_RETURN_IF_ERROR (qnn_model_wrapper.UnpackInitializerData (*input_tensor, unpacked_tensor));
224+ tensor_data = reinterpret_cast <const int64_t *>(unpacked_tensor.data ());
225+ size_t tensor_byte_size = unpacked_tensor.size ();
226+ size = tensor_byte_size / sizeof (int64_t );
227+ }
190228
191229 bool has_negative = std::any_of (tensor_data, tensor_data + size, [](int64_t item) { return item < 0 ; });
192230 bool has_positive = std::any_of (tensor_data, tensor_data + size, [](int64_t item) { return item > 0 ; });
@@ -197,6 +235,7 @@ Status PadOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wrap
197235 return ORT_MAKE_STATUS (ONNXRUNTIME , FAIL , " Got QNN invalid zero only padding value." );
198236 }
199237
238+ // Onnx Pads are int64, Qnn uses uint32
200239 std::vector<uint32_t > pad_amount;
201240 std::transform (tensor_data, tensor_data + size, std::back_inserter (pad_amount),
202241 [](int64_t item) { return item < 0 ? SafeInt<uint32_t >(0 ) : SafeInt<uint32_t >(item); });
@@ -208,7 +247,6 @@ Status PadOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wrap
208247 std::vector<uint32_t > input_shape;
209248 ORT_RETURN_IF_NOT (qnn_model_wrapper.GetOnnxShape (inputs[0 ].node_arg , input_shape), " Cannot get shape of input 0." );
210249
211- NodeAttrHelper node_helper (node_unit);
212250 std::string mode = node_helper.Get (" mode" , " constant" );
213251
214252 if (" reflect" == mode && has_negative) {
@@ -241,10 +279,21 @@ Status PadOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wrap
241279 param_tensor_names.push_back (pad_amount_param.GetParamTensorName ());
242280 qnn_model_wrapper.AddParamWrapper (std::move (pad_amount_param));
243281
244- // Process optional input constant_value
245- if (inputs.size () > 2 && inputs[2 ].node_arg .Exists ()) {
282+ if (opset_version < 11 && domain != kMSDomain && node_helper.HasAttr (" value" )) {
283+ // Process optional attribute value
284+ Qnn_Scalar_t constant_value_qnn_scalar = QNN_SCALAR_INIT ;
285+ constant_value_qnn_scalar.dataType = QNN_DATATYPE_FLOAT_32 ;
286+ constant_value_qnn_scalar.floatValue = node_helper.GetFloat (" value" ).value ();
287+ QnnParamWrapper constant_value_param (node_unit.Index (),
288+ node_unit.Name (),
289+ QNN_OP_PAD_PARAM_PAD_CONSTANT_VALUE ,
290+ constant_value_qnn_scalar);
291+ param_tensor_names.push_back (constant_value_param.GetParamTensorName ());
292+ qnn_model_wrapper.AddParamWrapper (std::move (constant_value_param));
293+ } else if ((opset_version >= 11 || domain == kMSDomain ) && inputs.size () > 2 && inputs[2 ].node_arg .Exists ()) {
294+ // Process optional input constant_value
246295 ORT_RETURN_IF_ERROR (ProcessConstantValue (qnn_model_wrapper, param_tensor_names, node_unit, inputs[2 ]));
247- } // constant_value
296+ }
248297
249298 if (!has_negative) {
250299 // Non-negative pads maps directly onto QNN pad.
0 commit comments