Skip to content
Closed
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
19 changes: 14 additions & 5 deletions onnxruntime/core/graph/contrib_ops/contrib_defs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,19 @@ void convTransposeWithDynamicPadsShapeInference(InferenceContext& ctx) {
}

int64_t group = getAttribute(ctx, "group", 1);
if (group <= 0) {
fail_shape_inference("group must be positive");
Comment on lines +59 to +60
}

auto input_shape = ctx.getInputType(0)->tensor_type().shape();
if (input_shape.dim_size() < 2) {
return; // Input tensor should have at least two dimensions.
fail_shape_inference("Input X must have at least 2 dimensions. Got: ", input_shape.dim_size());
}

// W must also have at least rank 2 (C_in, C_out/group, spatial dims...)
auto w_shape = ctx.getInputType(1)->tensor_type().shape();
if (w_shape.dim_size() < 2) {
fail_shape_inference("Filter W must have at least 2 dimensions. Got: ", w_shape.dim_size());
}

// first dim is the batch axis and the next is the number of channels.
Expand Down Expand Up @@ -147,7 +156,7 @@ void convTransposeWithDynamicPadsShapeInference(InferenceContext& ctx) {

*final_output_shape->add_dim() = input_shape.dim(0);
*final_output_shape->add_dim() =
ctx.getInputType(1)->tensor_type().shape().dim(1) *
w_shape.dim(1) *
group; // channels should be the second dim of second input multiply
// group.

Expand All @@ -157,9 +166,9 @@ void convTransposeWithDynamicPadsShapeInference(InferenceContext& ctx) {
for (int i = 0; i < size_of_output; ++i) {
if (input_shape.dim(i + 2).has_dim_value()) {
if (output_shape[i] < input_shape.dim(i + 2).dim_value()) {
// TODO: throw exception?
return; // output shape value cannot be smaller than the input shape
// value
fail_shape_inference("output_shape[", i, "] value (", output_shape[i],
") cannot be smaller than the input spatial dimension (",
input_shape.dim(i + 2).dim_value(), ")");
}
}
final_output_shape->add_dim()->set_dim_value(output_shape[i]);
Expand Down
65 changes: 62 additions & 3 deletions onnxruntime/core/providers/cpu/nn/conv_transpose_attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#pragma once

#include "core/common/safeint.h"
#include "core/providers/cpu/nn/conv_attributes.h"

namespace onnxruntime {
Expand Down Expand Up @@ -61,6 +62,16 @@ struct ConvTransposeAttributes : public ConvAttributes {
const Tensor* B = has_bias ? (dynamic_padding ? context->Input<Tensor>(3) : context->Input<Tensor>(2)) : nullptr;

const int rank = static_cast<int>(X->Shape().NumDimensions());
if (rank < 2) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Input X must have at least 2 dimensions. Got: ", rank);
}

if (static_cast<int>(F_Shape.NumDimensions()) < 2) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Filter W must have at least 2 dimensions. Got: ", F_Shape.NumDimensions());
}

TensorShape input_shape = X->Shape().Slice(is_nhwc ? 1 : 2, is_nhwc ? rank - 1 : rank);
const int64_t num_input_channels = is_nhwc ? X->Shape()[rank - 1] : X->Shape()[1];
const int64_t N = X->Shape()[0];
Expand Down Expand Up @@ -118,12 +129,35 @@ struct ConvTransposeAttributes : public ConvAttributes {
TensorShapeVector local_output_padding(output_padding);
if (local_output_padding.empty()) {
local_output_padding.resize(kernel_shape.size(), 0);
} else if (local_output_padding.size() != kernel_shape.size()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"output_padding size (", local_output_padding.size(),
") must match the number of spatial dimensions (", kernel_shape.size(), ").");
}
ConvPadVector local_pads;
local_pads.reserve(2 * (input_shape.NumDimensions()));
if (dynamic_padding) {
for (int64_t i = 0; i < Pads->Shape().SizeFromDimension(0); ++i) {
local_pads.push_back(Pads->Data<int64_t>()[i]);
if (Pads == nullptr) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Pads input is required for dynamic padding mode.");
}
if (Pads->Shape().NumDimensions() != 1) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Pads input must be a 1D tensor. Got rank: ", Pads->Shape().NumDimensions());
}
const int64_t expected_pads_size = SafeInt<int64_t>(kernel_shape.size()) * 2;
if (Pads->Shape()[0] != expected_pads_size) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Pads input must have ", expected_pads_size, " elements (2 * num_spatial_dims). Got: ",
Pads->Shape()[0]);
}
for (int64_t i = 0; i < Pads->Shape()[0]; ++i) {
const int64_t pad_val = Pads->Data<int64_t>()[i];
if (pad_val < 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Pad values must be non-negative. Got: ", pad_val, " at index ", i);
}
local_pads.push_back(pad_val);
Comment on lines +140 to +160
}
} else {
local_pads.assign(pads.begin(), pads.end());
Expand All @@ -134,10 +168,30 @@ struct ConvTransposeAttributes : public ConvAttributes {
TensorShapeVector local_dilations(dilations);
if (local_dilations.empty()) {
local_dilations.resize(kernel_shape.size(), 1);
} else if (local_dilations.size() != kernel_shape.size()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"dilations size (", local_dilations.size(),
") must match the number of spatial dimensions (", kernel_shape.size(), ").");
}
TensorShapeVector local_strides(strides);
if (local_strides.empty()) {
local_strides.resize(kernel_shape.size(), 1);
} else if (local_strides.size() != kernel_shape.size()) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"strides size (", local_strides.size(),
") must match the number of spatial dimensions (", kernel_shape.size(), ").");
}

// Validate output_padding < stride per ONNX spec:
// "Additional size added to one side of the output shape. This must be less
// than either stride or dilation in each spatial dimension."
for (size_t i = 0; i < local_output_padding.size(); ++i) {
if (local_output_padding[i] >= local_strides[i] && local_output_padding[i] >= local_dilations[i]) {
Comment thread
yuslepukhin marked this conversation as resolved.
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"output_padding[", i, "] (", local_output_padding[i],
") must be less than stride (", local_strides[i],
") or dilation (", local_dilations[i], ").");
}
}

TensorShapeVector Y_dims;
Expand Down Expand Up @@ -215,6 +269,11 @@ struct ConvTransposeAttributes : public ConvAttributes {
int64_t* pad_head,
int64_t* pad_tail,
int64_t* out_size) const {
ORT_ENFORCE(in_size > 0, "Input spatial dimension must be positive. Got: ", in_size);
ORT_ENFORCE(stride > 0, "Stride must be positive. Got: ", stride);
ORT_ENFORCE(kernel > 0, "Kernel size must be positive. Got: ", kernel);
ORT_ENFORCE(dilation > 0, "Dilation must be positive. Got: ", dilation);
ORT_ENFORCE(adj >= 0, "Output padding must be non-negative. Got: ", adj);
// Output shape is explicitly provided - pad values will have to be computed
if (*out_size != -1) {
ORT_ENFORCE(*out_size >= 0);
Expand All @@ -237,7 +296,7 @@ struct ConvTransposeAttributes : public ConvAttributes {
}

*out_size =
(in_size - 1) * stride + adj + (kernel - 1) * dilation + 1 - *pad_head - *pad_tail;
SafeInt<int64_t>(in_size - 1) * stride + adj + SafeInt<int64_t>(kernel - 1) * dilation + 1 - *pad_head - *pad_tail;
}
};

Expand Down
60 changes: 57 additions & 3 deletions onnxruntime/core/providers/cuda/nn/conv_transpose.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <string>
#include <utility>

#include "core/common/safeint.h"
#include "conv_transpose.h"
#include "core/providers/cuda/tensor/transpose.h"

Expand Down Expand Up @@ -272,7 +273,9 @@ Status ConvTranspose<T, Layout>::UpdateState(OpKernelContext* context, bool dyna

bool input_dims_changed = (s_.last_x_dims != x_dims);
bool w_dims_changed = (s_.last_w_dims != w_dims);
if (input_dims_changed || w_dims_changed) {
// When dynamic_padding is enabled, Pads may change between calls even if X/W
// shapes are unchanged, so we must always recompute the output shape.
if (input_dims_changed || w_dims_changed || dynamic_padding) {
Comment on lines +276 to +278
if (input_dims_changed)
s_.last_x_dims = gsl::make_span(x_dims);

Expand All @@ -283,6 +286,16 @@ Status ConvTranspose<T, Layout>::UpdateState(OpKernelContext* context, bool dyna
// The following code is from ConvTransposeAttributes::PrepareForCompute

const int rank = static_cast<int>(X->Shape().NumDimensions());
if (rank < 2) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Input X must have at least 2 dimensions. Got: ", rank);
}

if (static_cast<int>(w_shape.NumDimensions()) < 2) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Filter W must have at least 2 dimensions. Got: ", w_shape.NumDimensions());
}

TensorShape input_shape = X->Shape().Slice(channels_last ? 1 : 2, channels_last ? rank - 1 : rank);
const int64_t num_input_channels = channels_last ? X->Shape()[rank - 1] : X->Shape()[1];
const int64_t N = X->Shape()[0];
Expand Down Expand Up @@ -335,12 +348,35 @@ Status ConvTranspose<T, Layout>::UpdateState(OpKernelContext* context, bool dyna
TensorShapeVector local_output_padding(conv_transpose_attrs_.output_padding);
if (local_output_padding.empty()) {
local_output_padding.resize(kernel_shape.size(), 0);
} else if (local_output_padding.size() != kernel_rank) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"output_padding size (", local_output_padding.size(),
") must match the number of spatial dimensions (", kernel_rank, ").");
}
ConvPadVector pads;
pads.reserve(2 * (input_shape.NumDimensions()));
if (dynamic_padding) {
for (int64_t i = 0; i < Pads->Shape().SizeFromDimension(0); ++i) {
pads.push_back(Pads->Data<int64_t>()[i]);
if (Pads == nullptr) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Pads input is required for dynamic padding mode.");
}
if (Pads->Shape().NumDimensions() != 1) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Pads input must be a 1D tensor. Got rank: ", Pads->Shape().NumDimensions());
}
const int64_t expected_pads_size = SafeInt<int64_t>(kernel_shape.size()) * 2;
if (Pads->Shape()[0] != expected_pads_size) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Pads input must have ", expected_pads_size, " elements (2 * num_spatial_dims). Got: ",
Pads->Shape()[0]);
}
for (int64_t i = 0; i < Pads->Shape()[0]; ++i) {
Comment thread
yuslepukhin marked this conversation as resolved.
const int64_t pad_val = Pads->Data<int64_t>()[i];
if (pad_val < 0) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"Pad values must be non-negative. Got: ", pad_val, " at index ", i);
}
pads.push_back(pad_val);
}
} else {
pads.assign(conv_transpose_attrs_.pads.begin(), conv_transpose_attrs_.pads.end());
Expand All @@ -351,10 +387,28 @@ Status ConvTranspose<T, Layout>::UpdateState(OpKernelContext* context, bool dyna
TensorShapeVector dilations(conv_transpose_attrs_.dilations);
if (dilations.empty()) {
dilations.resize(kernel_shape.size(), 1);
} else if (dilations.size() != kernel_rank) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"dilations size (", dilations.size(),
") must match the number of spatial dimensions (", kernel_rank, ").");
}
TensorShapeVector strides(conv_transpose_attrs_.strides);
if (strides.empty()) {
strides.resize(kernel_shape.size(), 1);
} else if (strides.size() != kernel_rank) {
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"strides size (", strides.size(),
") must match the number of spatial dimensions (", kernel_rank, ").");
}

// Validate output_padding < stride per ONNX spec
for (size_t i = 0; i < local_output_padding.size(); ++i) {
if (local_output_padding[i] >= strides[i] && local_output_padding[i] >= dilations[i]) {
Comment thread
yuslepukhin marked this conversation as resolved.
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
"output_padding[", i, "] (", local_output_padding[i],
") must be less than stride (", strides[i],
") or dilation (", dilations[i], ").");
}
}

TensorShapeVector y_dims;
Expand Down
Loading
Loading