diff --git a/onnxruntime/core/common/type_list.h b/onnxruntime/core/common/type_list.h index ad7a4256ca..3c0dc47e7a 100644 --- a/onnxruntime/core/common/type_list.h +++ b/onnxruntime/core/common/type_list.h @@ -9,4 +9,9 @@ namespace onnxruntime { template struct TypeList {}; -} +} // namespace onnxruntime + +// type list type containing the given types +// Note: this is useful for passing TypeLists to macros which don't accept the +// comma-separated template arguments +#define ORT_TYPE_LIST(...) ::onnxruntime::TypeList<__VA_ARGS__> diff --git a/onnxruntime/core/providers/cpu/generator/constant_of_shape.cc b/onnxruntime/core/providers/cpu/generator/constant_of_shape.cc index dd5596c581..a3c3e17416 100644 --- a/onnxruntime/core/providers/cpu/generator/constant_of_shape.cc +++ b/onnxruntime/core/providers/cpu/generator/constant_of_shape.cc @@ -1,130 +1,37 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#include "core/framework/tensorprotoutils.h" -#include "core/providers/cpu/generator/constant_of_shape.h" -#include "gsl/gsl" +#include "core/providers/cpu/generator/constant_of_shape_base.h" +#include "core/providers/op_kernel_type_control.h" -using namespace ::onnxruntime::common; -using namespace ONNX_NAMESPACE; namespace onnxruntime { -ONNX_CPU_OPERATOR_KERNEL( - ConstantOfShape, - 9, - KernelDefBuilder() - .TypeConstraint("T1", DataTypeImpl::GetTensorType()) - .TypeConstraint("T2", std::vector{ - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType(), - DataTypeImpl::GetTensorType()}), - ConstantOfShape); - -#define FETCH_VALUE_DATA(c_type) \ - { \ - c_type val; \ - auto unpack_status = UnpackTensor(t_proto, raw_data, raw_data_len, &val, 1); \ - ORT_ENFORCE(unpack_status.IsOK(), "Value attribute unpacking failed:", unpack_status.ErrorMessage()); \ - SetValue(sizeof(c_type), reinterpret_cast(&val)); \ - } - -void onnxruntime::ConstantOfShapeBase::SetValueFromTensorProto(const ONNX_NAMESPACE::TensorProto& t_proto) { - using namespace utils; - ORT_ENFORCE(utils::HasDataType(t_proto)); - ORT_ENFORCE(TensorProto::DataType_IsValid(t_proto.data_type())); - ORT_ENFORCE(!utils::HasExternalData(t_proto), "Tensor proto with external data for value attribute is not supported."); - const auto tensor_type = static_cast(t_proto.data_type()); - const void* const raw_data = utils::HasRawData(t_proto) ? t_proto.raw_data().data() : nullptr; - const size_t raw_data_len = utils::HasRawData(t_proto) ? t_proto.raw_data().size() : 0; - switch (tensor_type) { - case TensorProto::BOOL: - FETCH_VALUE_DATA(bool); - break; - case TensorProto::FLOAT: - FETCH_VALUE_DATA(float); - break; - case TensorProto::FLOAT16: - FETCH_VALUE_DATA(MLFloat16); - break; - case TensorProto::DOUBLE: - FETCH_VALUE_DATA(double); - break; - case TensorProto::INT8: - FETCH_VALUE_DATA(int8_t); - break; - case TensorProto::INT16: - FETCH_VALUE_DATA(int16_t); - break; - case TensorProto::INT32: - FETCH_VALUE_DATA(int32_t); - break; - case TensorProto::INT64: - FETCH_VALUE_DATA(int64_t); - break; - case TensorProto::UINT8: - FETCH_VALUE_DATA(uint8_t); - break; - case TensorProto::UINT16: - FETCH_VALUE_DATA(uint16_t); - break; - case TensorProto::UINT32: - FETCH_VALUE_DATA(uint32_t); - break; - case TensorProto::UINT64: - FETCH_VALUE_DATA(uint64_t); - break; - default: - ORT_THROW("Unsupported value attribute datatype: ", tensor_type); - break; - } +namespace op_kernel_type_control { +ORT_SPECIFY_OP_KERNEL_ARG_SUPPORTED_TYPE_LIST_ALL_OPSETS( + kCpuExecutionProvider, kOnnxDomain, ConstantOfShape, Output, 0, + ConstantOfShapeDefaultOutputTypes); } -#undef FETCH_VALUE_DATA +namespace { + +using SupportedOutputTypes = + ORT_OP_KERNEL_ARG_SUPPORTED_TYPE_LIST_ALL_OPSETS( + kCpuExecutionProvider, kOnnxDomain, ConstantOfShape, Output, 0); + +using EnabledOutputTypes = + ORT_OP_KERNEL_ARG_ENABLED_TYPE_LIST_ALL_OPSETS( + kCpuExecutionProvider, kOnnxDomain, ConstantOfShape, Output, 0); + +class ConstantOfShape final : public ConstantOfShapeBase, public OpKernel { + public: + explicit ConstantOfShape(const OpKernelInfo& info) : ConstantOfShapeBase(info), OpKernel(info) {} + + Status Compute(OpKernelContext* ctx) const override; +}; template inline void FilloutOutput(T value, void* output_data, size_t size) { - auto out = gsl::make_span(reinterpret_cast(output_data), size); - std::fill(out.begin(), out.end(), value); -} - -ConstantOfShapeBase::ConstantOfShapeBase(const OpKernelInfo& info) { - TensorProto t_proto; - if (info.GetAttr("value", &t_proto).IsOK()) { - ORT_ENFORCE(t_proto.dims_size() == 1, "Must have a single dimension"); - ORT_ENFORCE(t_proto.dims()[0] == 1, "Must have a single dimension of 1"); - SetValueFromTensorProto(t_proto); - } else { - float f_value = 0.f; - SetValue(sizeof(float), reinterpret_cast(&f_value)); - } -} - -Status ConstantOfShapeBase::PrepareCompute(OpKernelContext* ctx, Tensor** output_tensor) const { - const auto shape_tensor = ctx->Input(0); - const auto& input_shape = shape_tensor->Shape(); - - // If empty the output is a scalar with empty shape - // TensorShape::Size() will still return 1 and we will output - // one value - std::vector output_dims; - ORT_ENFORCE(input_shape.NumDimensions() > 0, "Must have a valid input shape."); - - const auto span = gsl::make_span(shape_tensor->Data(), input_shape.Size()); - output_dims.insert(output_dims.end(), span.cbegin(), span.cend()); - - TensorShape output_shape(output_dims); - (*output_tensor) = ctx->Output(0, output_shape); - - return Status::OK(); + std::fill_n(reinterpret_cast(output_data), size, value); } Status ConstantOfShape::Compute(OpKernelContext* ctx) const { @@ -149,10 +56,22 @@ Status ConstantOfShape::Compute(OpKernelContext* ctx) const { FilloutOutput(*(reinterpret_cast(value_ptr)), output_data, size); break; default: - ORT_THROW("Unsupported value attribute datatype with sizeof=: ", element_size); - break; + return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Unsupported output datatype with size: ", element_size); } return Status::OK(); } + +} // namespace + +ONNX_CPU_OPERATOR_KERNEL( + ConstantOfShape, + 9, + KernelDefBuilder() + .TypeConstraint("T1", DataTypeImpl::GetTensorType()) + .TypeConstraint("T2", + BuildKernelDefConstraintsFunctorFromTypeList{}(), + BuildKernelDefConstraintsFunctorFromTypeList{}()), + ConstantOfShape); + } // namespace onnxruntime diff --git a/onnxruntime/core/providers/cpu/generator/constant_of_shape.h b/onnxruntime/core/providers/cpu/generator/constant_of_shape.h deleted file mode 100644 index 70efadef3f..0000000000 --- a/onnxruntime/core/providers/cpu/generator/constant_of_shape.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -#include "core/common/common.h" -#include "core/framework/data_types.h" -#include "core/framework/op_kernel.h" - -namespace onnxruntime { - -class ConstantOfShapeBase { - - protected: - ConstantOfShapeBase(const OpKernelInfo& info); - - Status PrepareCompute(OpKernelContext* ctx, Tensor** output_tensor) const; - - void* GetValuePtr() const { return p_value_; } - - private: - union SizeBasedValue { - int8_t int8_; - int16_t int16_; - int32_t int32_; - int64_t int64_; - } s_value_; - void* p_value_; - - void SetValue(size_t size, void* value) { - switch (size) { - case sizeof(int8_t): - s_value_.int8_ = *(reinterpret_cast(value)); - p_value_ = reinterpret_cast(&(s_value_.int8_)); - break; - case sizeof(int16_t): - s_value_.int16_ = *(reinterpret_cast(value)); - p_value_ = reinterpret_cast(&(s_value_.int16_)); - break; - case sizeof(int32_t): - s_value_.int32_ = *(reinterpret_cast(value)); - p_value_ = reinterpret_cast(&(s_value_.int32_)); - break; - case sizeof(int64_t): - s_value_.int64_ = *(reinterpret_cast(value)); - p_value_ = reinterpret_cast(&(s_value_.int64_)); - break; - default: - ORT_THROW("Unsupported value attribute datatype with sizeof=: ", size); - break; - } - } - - void SetValueFromTensorProto(const ONNX_NAMESPACE::TensorProto&); -}; - -class ConstantOfShape final : public ConstantOfShapeBase, public OpKernel { - public: - explicit ConstantOfShape(const OpKernelInfo& info) : ConstantOfShapeBase(info), OpKernel(info) {}; - - Status Compute(OpKernelContext* ctx) const override; -}; - -} // namespace onnxruntime diff --git a/onnxruntime/core/providers/cpu/generator/constant_of_shape_base.h b/onnxruntime/core/providers/cpu/generator/constant_of_shape_base.h new file mode 100644 index 0000000000..2e72f97e69 --- /dev/null +++ b/onnxruntime/core/providers/cpu/generator/constant_of_shape_base.h @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "core/common/common.h" +#include "core/common/type_list.h" +#include "core/framework/data_types.h" +#include "core/framework/data_types_internal.h" +#include "core/framework/op_kernel.h" +#include "core/framework/tensorprotoutils.h" +#include "core/providers/op_kernel_type_control_utils.h" + +namespace onnxruntime { + +using ConstantOfShapeDefaultOutputTypes = + TypeList< + MLFloat16, + float, double, + int8_t, int16_t, int32_t, int64_t, + uint8_t, uint16_t, uint32_t, uint64_t, + bool>; + +template +class ConstantOfShapeBase { + protected: + ConstantOfShapeBase(const OpKernelInfo& info) { + ONNX_NAMESPACE::TensorProto t_proto; + if (info.GetAttr("value", &t_proto).IsOK()) { + ORT_ENFORCE(t_proto.dims_size() == 1, "Must have a single dimension"); + ORT_ENFORCE(t_proto.dims()[0] == 1, "Must have a single dimension of 1"); + SetValueFromTensorProto(t_proto); + } else { + float f_value = 0.f; + SetValue(sizeof(float), reinterpret_cast(&f_value)); + } + } + + void* GetValuePtr() const { return p_value_; } + + static Status PrepareCompute(OpKernelContext* ctx, Tensor** output_tensor) { + const auto shape_tensor = ctx->Input(0); + const auto& input_shape = shape_tensor->Shape(); + + // If empty the output is a scalar with empty shape + // TensorShape::Size() will still return 1 and we will output + // one value + ORT_RETURN_IF_NOT(input_shape.NumDimensions() > 0, "Must have a valid input shape."); + + const auto span = shape_tensor->DataAsSpan(); + + TensorShape output_shape(span.begin(), span.size()); + (*output_tensor) = ctx->Output(0, output_shape); + + return Status::OK(); + } + + private: + union SizeBasedValue { + int8_t int8_; + int16_t int16_; + int32_t int32_; + int64_t int64_; + } s_value_; + void* p_value_; + + void SetValue(size_t size, void* value) { + switch (size) { + case sizeof(int8_t): + s_value_.int8_ = *(reinterpret_cast(value)); + p_value_ = reinterpret_cast(&(s_value_.int8_)); + break; + case sizeof(int16_t): + s_value_.int16_ = *(reinterpret_cast(value)); + p_value_ = reinterpret_cast(&(s_value_.int16_)); + break; + case sizeof(int32_t): + s_value_.int32_ = *(reinterpret_cast(value)); + p_value_ = reinterpret_cast(&(s_value_.int32_)); + break; + case sizeof(int64_t): + s_value_.int64_ = *(reinterpret_cast(value)); + p_value_ = reinterpret_cast(&(s_value_.int64_)); + break; + default: + ORT_THROW("Unsupported value attribute datatype with size: ", size); + } + } + + void SetValueFromTensorProto(const ONNX_NAMESPACE::TensorProto&); +}; + +#define CASE_FETCH_VALUE_DATA(c_type) \ + case utils::ToTensorProtoElementType(): { \ + if (utils::HasType()) { \ + c_type val; \ + ORT_THROW_IF_ERROR(utils::UnpackTensor(t_proto, raw_data, raw_data_len, &val, 1)); \ + SetValue(sizeof(c_type), reinterpret_cast(&val)); \ + handled = true; \ + } \ + break; \ + } + +template +void ConstantOfShapeBase::SetValueFromTensorProto(const ONNX_NAMESPACE::TensorProto& t_proto) { + ORT_ENFORCE(utils::HasDataType(t_proto)); + ORT_ENFORCE(ONNX_NAMESPACE::TensorProto::DataType_IsValid(t_proto.data_type())); + ORT_ENFORCE(!utils::HasExternalData(t_proto), + "Tensor proto with external data for value attribute is not supported."); + const auto tensor_type = static_cast(t_proto.data_type()); + const void* const raw_data = utils::HasRawData(t_proto) ? t_proto.raw_data().data() : nullptr; + const size_t raw_data_len = utils::HasRawData(t_proto) ? t_proto.raw_data().size() : 0; + bool handled = false; + switch (tensor_type) { + CASE_FETCH_VALUE_DATA(bool) + CASE_FETCH_VALUE_DATA(float) + CASE_FETCH_VALUE_DATA(MLFloat16) + CASE_FETCH_VALUE_DATA(double) + CASE_FETCH_VALUE_DATA(int8_t) + CASE_FETCH_VALUE_DATA(int16_t) + CASE_FETCH_VALUE_DATA(int32_t) + CASE_FETCH_VALUE_DATA(int64_t) + CASE_FETCH_VALUE_DATA(uint8_t) + CASE_FETCH_VALUE_DATA(uint16_t) + CASE_FETCH_VALUE_DATA(uint32_t) + CASE_FETCH_VALUE_DATA(uint64_t) + default: + ORT_THROW("Unsupported value attribute datatype: ", tensor_type); + } + + ORT_ENFORCE(handled, "Unsupported value attribute datatype in this build: ", tensor_type); +} + +#undef CASE_FETCH_VALUE_DATA + +} // namespace onnxruntime diff --git a/onnxruntime/core/providers/cuda/generator/constant_of_shape.h b/onnxruntime/core/providers/cuda/generator/constant_of_shape.h index 5a878d66aa..a0870d915a 100644 --- a/onnxruntime/core/providers/cuda/generator/constant_of_shape.h +++ b/onnxruntime/core/providers/cuda/generator/constant_of_shape.h @@ -6,15 +6,15 @@ #include "core/common/common.h" #include "core/framework/data_types.h" #include "core/providers/cuda/cuda_kernel.h" -#include "core/providers/cpu/generator/constant_of_shape.h" +#include "core/providers/cpu/generator/constant_of_shape_base.h" #include "core/providers/cuda/shared_inc/cuda_utils.h" namespace onnxruntime { namespace cuda { -class ConstantOfShape final : public ConstantOfShapeBase, public CudaKernel { +class ConstantOfShape final : public ConstantOfShapeBase<>, public CudaKernel { public: - explicit ConstantOfShape(const OpKernelInfo& info) : ConstantOfShapeBase(info), CudaKernel(info) {}; + explicit ConstantOfShape(const OpKernelInfo& info) : ConstantOfShapeBase(info), CudaKernel(info) {} ORT_DISALLOW_COPY_ASSIGNMENT_AND_MOVE(ConstantOfShape); diff --git a/onnxruntime/core/providers/op_kernel_type_control.h b/onnxruntime/core/providers/op_kernel_type_control.h index 6c9ea94262..e2649ae362 100644 --- a/onnxruntime/core/providers/op_kernel_type_control.h +++ b/onnxruntime/core/providers/op_kernel_type_control.h @@ -165,10 +165,10 @@ struct EnabledTypes { * @param OpSet The opset that this set of supported types applies to. * @param ArgDirection Direction of the given Op kernel argument - Input or Output. * @param ArgIndex Index of the given Op kernel argument. - * @param ... The types. + * @param SupportedTypeList The types. */ -#define ORT_SPECIFY_OP_KERNEL_ARG_SUPPORTED_TYPES( \ - OpProvider, OpDomain, OpName, OpSet, ArgDirection, ArgIndex, ...) \ +#define ORT_SPECIFY_OP_KERNEL_ARG_SUPPORTED_TYPE_LIST( \ + OpProvider, OpDomain, OpName, OpSet, ArgDirection, ArgIndex, SupportedTypeList) \ class ORT_OP_KERNEL_TYPE_CTRL_INTERNAL_OP_TAG_CLASS_NAME(OpDomain, OpName); \ class ORT_OP_KERNEL_TYPE_CTRL_INTERNAL_PROVIDER_TAG_CLASS_NAME(OpProvider); \ template <> \ @@ -177,9 +177,48 @@ struct EnabledTypes { ORT_OP_KERNEL_TYPE_CTRL_INTERNAL_OP_KERNEL_ARG_TAG(OpDomain, OpName, ArgDirection, ArgIndex), \ ORT_OP_KERNEL_TYPE_CTRL_INTERNAL_PROVIDER_TAG_CLASS_NAME(OpProvider), \ OpSet>> { \ - using types = ::onnxruntime::TypeList<__VA_ARGS__>; \ + using types = SupportedTypeList; \ }; +/** + * Specifies a supported set of types for a given Op kernel argument that is valid for all opsets. + * This should be specified with the Op kernel implementation. + * + * Note: This should be called from the onnxruntime::op_kernel_type_control namespace. + * + * @param OpProvider The Op provider. + * @param OpDomain The Op domain. + * @param OpName The Op name. + * @param ArgDirection Direction of the given Op kernel argument - Input or Output. + * @param ArgIndex Index of the given Op kernel argument. + * @param SupportedTypeList The types. + */ +#define ORT_SPECIFY_OP_KERNEL_ARG_SUPPORTED_TYPE_LIST_ALL_OPSETS( \ + OpProvider, OpDomain, OpName, ArgDirection, ArgIndex, SupportedTypeList) \ + ORT_SPECIFY_OP_KERNEL_ARG_SUPPORTED_TYPE_LIST(OpProvider, OpDomain, OpName, \ + ::onnxruntime::op_kernel_type_control::kAllOpSets, \ + ArgDirection, ArgIndex, SupportedTypeList) + +/** + * Specifies a supported set of types for a given Op kernel argument. + * This should be specified with the Op kernel implementation. + * + * Note: This should be called from the onnxruntime::op_kernel_type_control namespace. + * + * @param OpProvider The Op provider. + * @param OpDomain The Op domain. + * @param OpName The Op name. + * @param OpSet The opset that this set of supported types applies to. + * @param ArgDirection Direction of the given Op kernel argument - Input or Output. + * @param ArgIndex Index of the given Op kernel argument. + * @param ... The types. + */ +#define ORT_SPECIFY_OP_KERNEL_ARG_SUPPORTED_TYPES( \ + OpProvider, OpDomain, OpName, OpSet, ArgDirection, ArgIndex, ...) \ + ORT_SPECIFY_OP_KERNEL_ARG_SUPPORTED_TYPE_LIST( \ + OpProvider, OpDomain, OpName, OpSet, ArgDirection, ArgIndex, \ + ORT_TYPE_LIST(__VA_ARGS__)) + /** * Specifies a supported set of types for a given Op kernel argument that is valid for all opsets. * This should be specified with the Op kernel implementation.