From d4943e458fbccda7773d1238909dffcf5c62554f Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Fri, 19 Dec 2025 10:53:53 +0000 Subject: [PATCH 001/140] Add an implementation an NHWC implementation of convolution to avoid transposes * Modification to the CPU EP to specify channels_last when data format is NWHC * Added a FusedNhwcConv kernel * Implementation of the kernel in mlas * Added compiler guards so it is inly used with KleidiAi (for now, can be removed if needed) * Added unittests Signed-off-by: Orlaith Monahan --- .../contrib_ops/cpu/cpu_contrib_kernels.cc | 2 + onnxruntime/contrib_ops/cpu/fused_conv.cc | 8 + .../framework/kernel_type_str_resolver.cc | 12 ++ .../graph/contrib_ops/nhwc_schema_defs.cc | 2 +- onnxruntime/core/mlas/inc/mlas.h | 2 + onnxruntime/core/mlas/lib/convolve.cpp | 4 +- .../mlas/lib/kleidiai/convolve_kleidiai.cpp | 31 ++-- .../core/mlas/lib/kleidiai/mlasi_kleidiai.h | 1 + onnxruntime/core/mlas/lib/mlasi.h | 2 + .../core/optimizer/conv_activation_fusion.cc | 5 +- .../core/optimizer/conv_add_act_fusion.cc | 10 +- .../layout_transformation.cc | 1 + .../core/optimizer/nhwc_transformer.cc | 158 +++++++++++++++++- onnxruntime/core/optimizer/nhwc_transformer.h | 5 + onnxruntime/core/providers/cpu/nn/conv.cc | 153 ++++++++++++++--- onnxruntime/core/providers/cpu/nn/conv.h | 3 +- onnxruntime/core/util/math_cpu.cc | 1 + .../test/framework/ort_model_only_test.cc | 61 ++++++- .../internal_testing_tests.cc | 69 +++++--- onnxruntime/test/mlas/bench/bench_sconv.cpp | 1 + onnxruntime/test/mlas/unittest/test_conv2d.h | 1 + .../test/optimizer/conv_add_act_test.cc | 5 +- .../fuse_initializers_transformer_test.cc | 5 +- .../test/optimizer/nhwc_transformer_test.cc | 22 +++ 24 files changed, 488 insertions(+), 76 deletions(-) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index d959d11e3fd43..fc5f3a459e616 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -18,6 +18,7 @@ class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, EmbedLayerNormalization); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, ExpandDims); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, FusedConv); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, NhwcFusedConv); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, FusedGemm); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, GreedySearch); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, MultiHeadAttention); @@ -302,6 +303,7 @@ Status RegisterCpuContribKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, + BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, diff --git a/onnxruntime/contrib_ops/cpu/fused_conv.cc b/onnxruntime/contrib_ops/cpu/fused_conv.cc index 5374222dbabcc..d77efc26d0e2f 100644 --- a/onnxruntime/contrib_ops/cpu/fused_conv.cc +++ b/onnxruntime/contrib_ops/cpu/fused_conv.cc @@ -26,5 +26,13 @@ ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( .TypeConstraint("T", DataTypeImpl::GetTensorType()), FusedConvFloat); +ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( + NhwcFusedConv, + 1, + float, + KernelDefBuilder() + .TypeConstraint("T", DataTypeImpl::GetTensorType()), + FusedConvFloat); + } // namespace contrib } // namespace onnxruntime diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index 3142f94f289b3..aacbc8fc0a4fb 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -36,6 +36,18 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } +#ifdef USE_KLEIDIAI + // Klediai specific block for NhwcFusedConvolutions + if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { + const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; + op_it = map.find(fused_conv_op_id); + if (op_it == map.end()) { + const auto conv_op_id = OpIdentifier{std::string{kOnnxDomain}, "Conv", op_id.since_version}; + op_it = map.find(conv_op_id); + } + } +#endif + return op_it; } diff --git a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc index 8fe3a4d5f3b6f..5a57a58360ddf 100644 --- a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc +++ b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc @@ -403,7 +403,7 @@ Only has fp16 implementation as of 2023/04/15. .Input(2, "B", "", "T", OpSchema::Optional) .Input(3, "Z", "Tensor to be added to the output, must be the same shape and format as the output tensor.", "T", OpSchema::Optional) .Output(0, "Y", "", "T") - .TypeConstraint("T", {"tensor(float16)"}, "Constrain input and output types to float tensors") + .TypeConstraint("T", {"tensor(float16)", "tensor(float)"}, "Constrain input and output types to float tensors") .TypeAndShapeInferenceFunction([](InferenceContext& ctx) { ONNX_NAMESPACE::propagateElemTypeFromInputToOutput(ctx, 0, 0); convPoolShapeInferenceNhwc(ctx, true, false, 0, 1); diff --git a/onnxruntime/core/mlas/inc/mlas.h b/onnxruntime/core/mlas/inc/mlas.h index 248c6d74e6cbd..adfdf363295fd 100644 --- a/onnxruntime/core/mlas/inc/mlas.h +++ b/onnxruntime/core/mlas/inc/mlas.h @@ -851,6 +851,7 @@ struct MLAS_CONV_PARAMETERS { size_t BatchCount; size_t GroupCount; size_t InputChannels; + bool ChannelsLast; size_t InputShape[3]; size_t KernelShape[3]; size_t DilationShape[3]; @@ -890,6 +891,7 @@ MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool); diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index 9518134631f2d..f0c1d870d6cd9 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1146,6 +1146,7 @@ MlasConvPrepare( size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool ) @@ -1204,7 +1205,7 @@ Return Value: if (GetMlasPlatform().MlasConvPrepareOverride != nullptr && GetMlasPlatform().MlasConvPrepareOverride(Parameters, Dimensions, BatchCount, GroupCount, InputChannels, InputShape,KernelShape,DilationShape, Padding, StrideShape, OutputShape, FilterCount, - Activation, WorkingBufferSize, Beta, ThreadPool)){ + Activation, WorkingBufferSize, ChannelsLast, Beta, ThreadPool)){ return; } // @@ -1215,6 +1216,7 @@ Return Value: Parameters->BatchCount = BatchCount; Parameters->GroupCount = GroupCount; Parameters->InputChannels = InputChannels; + Parameters->ChannelsLast = ChannelsLast; Parameters->FilterCount = FilterCount; Parameters->Beta = Beta; diff --git a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp index 487e1533f5967..60c8e9b562aec 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp +++ b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp @@ -448,6 +448,7 @@ static std::shared_ptr LhsPtrFill(const size_t ci, const size_t i static std::unique_ptr LhsPackImageDataSme(const size_t ci, const size_t ih, const size_t iw, const size_t kh, const size_t kw, const size_t sh, const size_t sw, const size_t padding, const float* in, + bool input_is_channels_last, MLAS_THREADPOOL* ThreadPool) { size_t padsize = 256; @@ -472,7 +473,14 @@ static std::unique_ptr LhsPackImageDataSme(const size_t ci, const s const auto lhs_size = kai_get_lhs_packed_size_lhs_imatmul_pack_x32p2vlx1_x32p_sme(m,kh*kw,ci); auto lhs = std::make_unique(lhs_size); - auto nhwc = NChwToNhwc(1, ci, ih, iw, in, 1, 1, false, ThreadPool); + std::unique_ptr nhwc_holder; + const float* activation_src = nullptr; + if (input_is_channels_last) { + activation_src = in; + } else { + nhwc_holder = NChwToNhwc(1, ci, ih, iw, in, 1, 1, false, ThreadPool); + activation_src = nhwc_holder.get(); + } // Cache of computed lhs ptr offsets. thread_local to prevent interference from parallel sessions. thread_local std::unordered_map> lhs_ptrs_cache; @@ -485,7 +493,7 @@ static std::unique_ptr LhsPackImageDataSme(const size_t ci, const s lhs_ptrs_cache[key] = lhs_ptrs; } - MultiThreadedLHSPackSme(ThreadPool, ci, m, kh, kw, &lhs_ptrs[0], &lhs[0], &nhwc[0], &pad_ptr[0]); + MultiThreadedLHSPackSme(ThreadPool, ci, m, kh, kw, &lhs_ptrs[0], &lhs[0], activation_src, &pad_ptr[0]); return lhs; } @@ -507,6 +515,7 @@ static void ConvolveSme(const size_t co, //channels out const float* in, //in image data float* out, //out image data float* tmp_mlas_aligned, //intermediate buffer if we need to perform a transpose + bool input_is_channels_last, MLAS_THREADPOOL* ThreadPool) { //RhsPackWeightsBiasSme() - to perform dilation increases kernel size and masks unused weights @@ -546,17 +555,13 @@ static void ConvolveSme(const size_t co, //channels out for (size_t g = 0; g < groups; ++g) { - auto result{out}; - //do we require a post matmul transpose ? - //output is m x n or image_data x co or hw x co - //MLAS require it as n x m (or co x hw), transpose required - if (co > 1) { - //intermediate buffer required, pre-transpose - //Note: because we are calling MlasTranspose() need to ensure we use a MLAS aligned buffer + auto result = out; + const bool need_transpose = (!input_is_channels_last) && (co > 1); + if (need_transpose) { result = tmp_mlas_aligned; } - auto lhs = LhsPackImageDataSme(ci, ih, iw, d_kh, d_kw, sh, sw, padding, in, ThreadPool); + auto lhs = LhsPackImageDataSme(ci, ih, iw, d_kh, d_kw, sh, sw, padding, in, input_is_channels_last, ThreadPool); auto rhs = RhsPackWeightsBiasSme(co, ci, kh, kw, dilationh, dilationw, weights, bias, ThreadPool); MlasTrySimpleParallel(ThreadPool, static_cast(dim[0] * dim[1] * dim[2]), [&](ptrdiff_t tid) { @@ -604,7 +609,7 @@ static void ConvolveSme(const size_t co, //channels out } }); - if (result == tmp_mlas_aligned) { + if (need_transpose) { //Note: this could be absorbed into post conv activation MlasTranspose(tmp_mlas_aligned, out, m, co, ThreadPool); } @@ -633,6 +638,7 @@ ArmKleidiAI::MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool) { @@ -646,6 +652,7 @@ ArmKleidiAI::MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, Parameters->BatchCount = BatchCount; Parameters->GroupCount = GroupCount; Parameters->InputChannels = InputChannels; + Parameters->ChannelsLast = ChannelsLast; Parameters->FilterCount = FilterCount; Parameters->Beta = Beta; @@ -711,7 +718,7 @@ ArmKleidiAI::MlasConv( Parameters->DilationShape[0], Parameters->DilationShape[1], // kernel dilation Parameters->Padding[0], // image padding Parameters->GroupCount, // filter groups - Filter, Bias, Input, Output, WorkingBuffer, ThreadPool); + Filter, Bias, Input, Output, WorkingBuffer, Parameters->ChannelsLast, ThreadPool); MlasActivation(Parameters->Activation, Output, nullptr, Parameters->FilterCount, Parameters->OutputSize, Parameters->OutputSize); diff --git a/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h b/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h index ca81b9fa426ee..99eb88fcf4d2d 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h +++ b/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h @@ -147,6 +147,7 @@ MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool); diff --git a/onnxruntime/core/mlas/lib/mlasi.h b/onnxruntime/core/mlas/lib/mlasi.h index ad62cccbfb9c7..1186d5b939d7e 100644 --- a/onnxruntime/core/mlas/lib/mlasi.h +++ b/onnxruntime/core/mlas/lib/mlasi.h @@ -827,6 +827,7 @@ void size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool ); @@ -847,6 +848,7 @@ bool size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool ); diff --git a/onnxruntime/core/optimizer/conv_activation_fusion.cc b/onnxruntime/core/optimizer/conv_activation_fusion.cc index b7f5af5888be0..a53099937a94a 100644 --- a/onnxruntime/core/optimizer/conv_activation_fusion.cc +++ b/onnxruntime/core/optimizer/conv_activation_fusion.cc @@ -140,9 +140,12 @@ class FuseConvActivationAction : public ReplaceWithNew { return "FusedConv"; } } else if (domain == kMSDomain) { - if (op_type == "NhwcConv") { + if (op_type == "NhwcConv" || op_type == "NhwcFusedConv") { return "NhwcFusedConv"; } + if (op_type == "FusedConv") { + return "FusedConv"; + } } else if (domain == kMSInternalNHWCDomain) { if (op_type == "Conv") { return "Conv"; diff --git a/onnxruntime/core/optimizer/conv_add_act_fusion.cc b/onnxruntime/core/optimizer/conv_add_act_fusion.cc index 6f90eaf07ef4d..478e7529cb667 100644 --- a/onnxruntime/core/optimizer/conv_add_act_fusion.cc +++ b/onnxruntime/core/optimizer/conv_add_act_fusion.cc @@ -211,7 +211,15 @@ class FuseConvAddActivationAction : public ReplaceWithNew { private: std::string OpType(const RuntimeState& runtimeState) const override { - return (runtimeState.selected_nodes.Target().OpType() == "Conv") ? "FusedConv" : "NhwcFusedConv"; + const auto& target = runtimeState.selected_nodes.Target(); + const auto* channels_last_attr = graph_utils::GetNodeAttribute(target, "channels_last"); + const bool channels_last = channels_last_attr != nullptr && channels_last_attr->i() != 0; + + if (target.OpType() == "Conv") { + return channels_last ? "NhwcFusedConv" : "FusedConv"; + } + + return "NhwcFusedConv"; } std::string Domain(const RuntimeState&) const override { return kMSDomain; } diff --git a/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc b/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc index f611c992e0f57..5d51c855d13ba 100644 --- a/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc +++ b/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc @@ -68,6 +68,7 @@ const std::unordered_set& GetORTLayoutSensitiveOps() { // Define a static local string array so we can refer to the elements with string_views. static const std::string layout_sensitive_contrib_ops[]{ MakeORTLayoutSensitiveOpId(kMSDomain, "FusedConv"), + MakeORTLayoutSensitiveOpId(kMSDomain, "NhwcFusedConv"), MakeORTLayoutSensitiveOpId(kMSDomain, "GridSample"), MakeORTLayoutSensitiveOpId(kMSDomain, "QLinearAveragePool"), MakeORTLayoutSensitiveOpId(kMSDomain, "QLinearGlobalAveragePool"), diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index cd654991c92d5..9544cf7395025 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -2,7 +2,10 @@ // SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates // Licensed under the MIT License. +#include #include +#include +#include "core/graph/constants.h" #include "core/mlas/inc/mlas.h" #include "core/graph/graph_utils.h" #include "core/optimizer/initializer.h" @@ -21,6 +24,72 @@ namespace onnxruntime { using namespace layout_transformation; +#ifdef USE_KLEIDIAI +bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& graph, + onnx_transpose_optimization::api::NodeRef& node) { + auto& base_node = NodeFromApiNode(node); + + ORT_UNUSED_PARAMETER(graph); + if (base_node.InputDefs().size() < 2) { + return false; + } + + const auto* input_shape = base_node.InputDefs()[0]->Shape(); + if (input_shape == nullptr || input_shape->dim_size() != 4) { + return false; + } + + const auto& batch_dim = input_shape->dim(0); + if (!utils::HasDimValue(batch_dim) || batch_dim.dim_value() != 1) { + return false; + } + + const auto pads_attr = node.GetAttributeInts("pads"); + if (pads_attr.has_value()) { + const auto& pads = pads_attr.value(); + if (pads.size() != 4 || pads[0] != pads[2] || pads[1] != pads[3]) { + return false; + } + } + + const auto inputs = node.Inputs(); + if (inputs.size() > 3 && !inputs[3].empty()) { + return false; + } + + const auto* weight_shape = base_node.InputDefs()[1]->Shape(); + if (weight_shape == nullptr || weight_shape->dim_size() != 4) { + return false; + } + + const auto& filter_dim = weight_shape->dim(0); + const auto& kernel_h_dim = weight_shape->dim(2); + const auto& kernel_w_dim = weight_shape->dim(3); + + if (!utils::HasDimValue(filter_dim) || filter_dim.dim_value() <= 1 || + !utils::HasDimValue(kernel_h_dim) || kernel_h_dim.dim_value() < 3 || + !utils::HasDimValue(kernel_w_dim) || kernel_w_dim.dim_value() < 3) { + return false; + } + + const auto dilations_opt = node.GetAttributeInts("dilations"); + if (dilations_opt.has_value()) { + const auto& dilations = dilations_opt.value(); + if ((dilations.size() >= 1 && dilations[0] != 1) || + (dilations.size() >= 2 && dilations[1] != 1)) { + return false; + } + } + + const auto group_opt = node.GetAttributeInt("group"); + if (group_opt.has_value() && group_opt.value() != 1) { + return false; + } + + return true; +} +#endif + static inline const OpTransformInfo* NhwcConvLookup( const OpTransformMap& conv_table, @@ -41,6 +110,13 @@ NhwcConvLookup( if (iter == conv_table.end()) { return nullptr; } + + if (iter->second.filter_ != nullptr) { + if (!iter->second.filter_(graph, node)) { + return nullptr; + } + } + return &(iter->second); } @@ -108,15 +184,62 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, nhwc_conv_fp16.version_, nhwc_conv_fp16.type_constraints_, logger, &kernel_create_info); if (status.IsOK() && kernel_create_info != nullptr) { kernel_create_info = nullptr; + const auto filter = [](const api::GraphRef&, api::NodeRef& node) { + const auto dilations_opt = node.GetAttributeInts("dilations"); + if (dilations_opt.has_value()) { + const auto& dilations = dilations_opt.value(); + if ((dilations.size() >= 1 && dilations[0] != 1) || + (dilations.size() >= 2 && dilations[1] != 1)) { + return false; + } + } + + const auto group_opt = node.GetAttributeInt("group"); + if (group_opt.has_value() && group_opt.value() != 1) { + return false; + } + + return true; + }; + conv_table_.emplace( OpIdInfo("Conv", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, filter}); conv_table_.emplace( OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, filter}); } } +#ifdef USE_KLEIDIAI + // Klediai specific block for NhwcFusedConvolutions + { + // F32 Conv -> F32 NHWC Conv + OpKernelRegistryId nhwc_conv_fp32{ + "NhwcFusedConv", kMSDomain, 1, {{"T", {DataTypeImpl::GetTensorType()}}}}; + + const KernelCreateInfo* kernel_create_info{}; + const auto status = cpu_kernel_registry->TryFindKernel( + kCpuExecutionProvider, nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, + nhwc_conv_fp32.version_, nhwc_conv_fp32.type_constraints_, logger, &kernel_create_info); + + if (status.IsOK() && kernel_create_info != nullptr) { + kernel_create_info = nullptr; + + const auto filter = [](const api::GraphRef& graph, api::NodeRef& node) { + return KleidiFp32NhwcFilter(graph, node); + }; + + conv_table_.emplace( + OpIdInfo("Conv", kOnnxDomain, api::DataType::FLOAT), + OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, filter}); + conv_table_.emplace( + OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT), + OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, filter}); + } + } +#endif + { // fp16 MaxPool -> fp16 nhwc MaxPool OpKernelRegistryId nhwc_maxpool_fp16{ @@ -214,10 +337,39 @@ Status NhwcTransformer::ApplyImpl(Graph& graph, bool& modified, int graph_level, if (transform->has_channels_last_attrib_) { node->SetAttributeInt("channels_last", 1); } + + if (node->OpType() == "Conv" || node->OpType() == "FusedConv") { + const auto group_opt = node->GetAttributeInt("group"); + if (group_opt.has_value() && group_opt.value() != 1) { + continue; + } + + const auto dilations_opt = node->GetAttributeInts("dilations"); + if (dilations_opt.has_value()) { + const auto& dilations = dilations_opt.value(); + if ((dilations.size() >= 1 && dilations[0] != 1) || + (dilations.size() >= 2 && dilations[1] != 1)) { + continue; + } + } + } + size_t rank = shape->dim_size(); std::vector input_perm = ChannelFirstToLastPerm(rank); std::vector output_perm = ChannelLastToFirstPerm(rank); - WrapTransposesAroundNode(*api_graph, *node, {&input_perm}, {&output_perm}); + const auto inputs = node->Inputs(); + std::vector*> input_perms(inputs.size(), nullptr); + if (!inputs.empty()) { + input_perms[0] = &input_perm; + } + // Optional Sum (Z) input for FusedConv variants resides at index 3. When present, + // it must be converted to NHWC alongside the activation tensor. + const bool has_fused_sum_input = (node->Domain() == kMSDomain && node->OpType() == "FusedConv"); + if (has_fused_sum_input && inputs.size() > 3 && !inputs[3].empty()) { + input_perms[3] = &input_perm; + } + + WrapTransposesAroundNode(*api_graph, *node, input_perms, {&output_perm}); // Replace the operator if needed if (node->Domain() != transform->domain_ || diff --git a/onnxruntime/core/optimizer/nhwc_transformer.h b/onnxruntime/core/optimizer/nhwc_transformer.h index c65f851fdab9d..6dd11bdba6bdd 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.h +++ b/onnxruntime/core/optimizer/nhwc_transformer.h @@ -3,6 +3,7 @@ #pragma once +#include #include "core/common/common.h" #include "core/framework/execution_provider.h" #include "core/framework/kernel_registry.h" @@ -54,10 +55,14 @@ class OpIdHash { * @brief Information needed for operator layout transformation */ struct OpTransformInfo { + using FilterFn = std::function; + const std::string optype_; const std::string domain_; const int version_; const bool has_channels_last_attrib_; + const FilterFn filter_{nullptr}; }; using OpTransformMap = std::unordered_map; diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index d10213f55d5d4..4cc0df42d2969 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -15,6 +15,8 @@ */ /* Modifications Copyright (c) Microsoft. */ +#include + #include "core/providers/cpu/nn/conv.h" #include "core/common/narrow.h" @@ -24,6 +26,44 @@ namespace onnxruntime { using ConvPadVector = ConvAttributes::ConvPadVector; +namespace { + +template +void ConvertNHWCToNCHW(const T* src, T* dst, + int64_t n, int64_t c, int64_t h, int64_t w) { + const int64_t hw = (SafeInt(h) * w); + for (int64_t n_idx = 0; n_idx < n; ++n_idx) { + const int64_t n_src_offset = n_idx * hw * c; + const int64_t n_dst_offset = n_idx * c * hw; + for (int64_t c_idx = 0; c_idx < c; ++c_idx) { + const T* src_ptr = src + n_src_offset + c_idx; + T* dst_ptr = dst + n_dst_offset + c_idx * hw; + for (int64_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + dst_ptr[hw_idx] = src_ptr[hw_idx * c]; + } + } + } +} + +template +void ConvertNCHWToNHWC(const T* src, T* dst, + int64_t n, int64_t c, int64_t h, int64_t w) { + const int64_t hw = (SafeInt(h) * w); + for (int64_t n_idx = 0; n_idx < n; ++n_idx) { + const int64_t n_src_offset = n_idx * c * hw; + const int64_t n_dst_offset = n_idx * hw * c; + for (int64_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + const T* src_ptr = src + n_src_offset + hw_idx; + T* dst_ptr = dst + n_dst_offset + hw_idx * c; + for (int64_t c_idx = 0; c_idx < c; ++c_idx) { + dst_ptr[c_idx] = src_ptr[c_idx * hw]; + } + } + } +} + +} // namespace + template Status Conv::Compute(OpKernelContext* context) const { const auto* X = context->Input(0); @@ -160,11 +200,10 @@ Status Conv::Compute(OpKernelContext* context) const { const Tensor* B = num_inputs >= 3 ? context->Input(2) : nullptr; const Tensor* Sum = num_inputs >= 4 ? context->Input(3) : nullptr; const int64_t N = X->Shape()[0]; - const int64_t C = X->Shape()[1]; + const int64_t C = X->Shape()[channels_last_ ? 3 : 1]; const int64_t M = W->Shape()[0]; - ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X, W)); + ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X->Shape(), W->Shape(), channels_last_)); - // kernel_shape is an optional attribute and has to be inferred from W if not provided TensorShapeVector kernel_shape; ORT_RETURN_IF_ERROR(conv_attrs_.ComputeKernelShape(W->Shape(), kernel_shape)); @@ -182,12 +221,14 @@ Status Conv::Compute(OpKernelContext* context) const { } TensorShapeVector Y_dims({N, M}); - TensorShape input_shape = X->Shape().Slice(2); + TensorShape input_shape = channels_last_ ? X->Shape().Slice(1, 3) : X->Shape().Slice(2); ORT_RETURN_IF_ERROR(conv_attrs_.InferPadsAndOutputShape(input_shape, kernel_shape, strides, dilations, pads, Y_dims)); + if (channels_last_) { + Y_dims = {Y_dims[0], Y_dims[2], Y_dims[3], Y_dims[1]}; + } Tensor* Y = context->Output(0, TensorShape(Y_dims)); - TensorShape output_shape = Y->Shape().Slice(2); + TensorShape output_shape = channels_last_ ? TensorShape(Y_dims).Slice(1, 3) : Y->Shape().Slice(2); - // Bail out early if one of the dimensions is zero. if (Y->Shape().Size() == 0) { return Status::OK(); } @@ -198,20 +239,39 @@ Status Conv::Compute(OpKernelContext* context) const { auto Xdata = X->DataAsSpan(); const auto* Bdata = B != nullptr ? B->Data() : nullptr; auto Ydata = Y->MutableDataAsSpan(); - // Check for the optional Conv/Sum fusion. + const size_t kernel_rank = kernel_shape.size(); + concurrency::ThreadPool* thread_pool = context->GetOperatorThreadPool(); + + if (channels_last_) { + ORT_RETURN_IF_NOT(kernel_rank == 2, "NhwcFusedConv currently supports 2D kernels."); + ORT_RETURN_IF_NOT(dilations[0] == 1 && dilations[1] == 1, "NhwcFusedConv currently supports dilation == 1."); + } + + const bool wants_channels_last = channels_last_; + const bool sum_present = Sum != nullptr; + const bool nhwc_fastpath = + wants_channels_last && kernel_rank == 2 && conv_attrs_.group == 1 && + dilations[0] == 1 && dilations[1] == 1 && !sum_present; + const bool manual_sum = wants_channels_last && !nhwc_fastpath && sum_present; + + std::vector sum_manual_buffer; + const float* sum_manual_data = nullptr; + float Beta = 0.0f; - if (Sum != nullptr) { + if (sum_present) { const auto& sum_shape = Sum->Shape(); ORT_RETURN_IF_NOT(Y->Shape() == sum_shape, "output and sum shape must match"); - // If the output was not allocated inplace with the sum tensor, then copy here. - auto sum_data = Sum->DataAsSpan(); - if (Ydata.data() != sum_data.data()) { - gsl::copy(sum_data, Ydata); + if (manual_sum) { + sum_manual_buffer.assign(Sum->Data(), Sum->Data() + Y->Shape().Size()); + sum_manual_data = sum_manual_buffer.data(); + } else { + auto sum_span = Sum->DataAsSpan(); + if (Ydata.data() != sum_span.data()) { + gsl::copy(sum_span, Ydata); + } + Beta = 1.0f; } - Beta = 1.0f; } - const size_t kernel_rank = kernel_shape.size(); - concurrency::ThreadPool* thread_pool = context->GetOperatorThreadPool(); if (kernel_rank >= 1 && kernel_rank <= 3) { MLAS_CONV_PARAMETERS Parameters; @@ -230,20 +290,66 @@ Status Conv::Compute(OpKernelContext* context) const { narrow(M / conv_attrs_.group), &activation_, &WorkingBufferSize, - Beta, + nhwc_fastpath, + nhwc_fastpath ? 0.0f : Beta, thread_pool); - auto* working_data = WorkingBufferSize > 0 ? alloc->Alloc(sizeof(float) * SafeInt(WorkingBufferSize)) - : nullptr; - BufferUniquePtr working_buffer(working_data, BufferDeleter(std::move(alloc))); + float* working_data = nullptr; + BufferUniquePtr working_buffer; + if (WorkingBufferSize > 0) { + working_data = static_cast(alloc->Alloc(sizeof(float) * SafeInt(WorkingBufferSize))); + working_buffer = BufferUniquePtr(working_data, BufferDeleter(alloc)); + } + + float* output_compute = Ydata.data(); + BufferUniquePtr output_temp; + if (wants_channels_last && !nhwc_fastpath) { + const SafeInt output_compute_size = + SafeInt(Y->Shape()[0]) * SafeInt(M) * + SafeInt(output_shape[0]) * SafeInt(output_shape[1]); + float* temp_output = static_cast(alloc->Alloc(sizeof(float) * output_compute_size)); + output_temp = BufferUniquePtr(temp_output, BufferDeleter(alloc)); + output_compute = temp_output; + } + + const float* input_compute = Xdata.data(); + BufferUniquePtr input_temp; + if (wants_channels_last && !nhwc_fastpath) { + ORT_RETURN_IF_NOT(X->Shape().NumDimensions() == 4, "Nhwc fallback expects 4D input."); + const auto& x_dims = X->Shape().GetDims(); + const int64_t input_n = x_dims[0]; + const int64_t input_h = x_dims[1]; + const int64_t input_w = x_dims[2]; + const int64_t input_c = x_dims[3]; + const SafeInt input_elements = SafeInt(X->Shape().Size()); + float* temp_input = static_cast(alloc->Alloc(sizeof(float) * input_elements)); + input_temp = BufferUniquePtr(temp_input, BufferDeleter(alloc)); + ConvertNHWCToNCHW(X->Data(), temp_input, + input_n, input_c, input_h, input_w); + input_compute = temp_input; + } MlasConv(&Parameters, - Xdata.data(), + input_compute, W->Data(), Bdata, - static_cast(working_buffer.get()), - Ydata.data(), + working_data, + output_compute, thread_pool); + + if (wants_channels_last && !nhwc_fastpath) { + const auto& y_dims = Y->Shape().GetDims(); + ORT_RETURN_IF_NOT(y_dims.size() == 4, "Nhwc fallback expects 4D output."); + ConvertNCHWToNHWC(output_compute, + Ydata.data(), + y_dims[0], y_dims[3], y_dims[1], y_dims[2]); + if (manual_sum) { + auto y_span = gsl::make_span(Ydata.data(), Ydata.size()); + for (size_t i = 0; i < y_span.size(); ++i) { + y_span[i] += sum_manual_data[i]; + } + } + } } else { const int64_t input_image_size = input_shape.Size(); const int64_t output_image_size = output_shape.Size(); @@ -284,7 +390,8 @@ Status Conv::Compute(OpKernelContext* context) const { thread_pool); } - MlasActivation(&activation_, Ydata.data(), Bdata, narrow(M), narrow(output_image_size), narrow(output_image_size)); + MlasActivation(&activation_, Ydata.data(), Bdata, narrow(M), + narrow(output_image_size), narrow(output_image_size)); Xdata = Xdata.subspan(X_offset * conv_attrs_.group); Ydata = Ydata.subspan(Y_offset * conv_attrs_.group); diff --git a/onnxruntime/core/providers/cpu/nn/conv.h b/onnxruntime/core/providers/cpu/nn/conv.h index 5ed5d2ca91def..78912d3146a1e 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.h +++ b/onnxruntime/core/providers/cpu/nn/conv.h @@ -24,7 +24,7 @@ class Conv : public OpKernel { template <> class Conv : public OpKernel { public: - Conv(const OpKernelInfo& info) : OpKernel(info), conv_attrs_(info) { + Conv(const OpKernelInfo& info) : OpKernel(info), conv_attrs_(info), channels_last_(info.GetKernelDef().OpName() == "NhwcFusedConv") { activation_.ActivationKind = MlasIdentityActivation; } @@ -34,6 +34,7 @@ class Conv : public OpKernel { MLAS_ACTIVATION activation_; ConvAttributes conv_attrs_; + bool channels_last_{false}; }; } // namespace onnxruntime diff --git a/onnxruntime/core/util/math_cpu.cc b/onnxruntime/core/util/math_cpu.cc index 045dc98a3501e..03b2067eadc2e 100644 --- a/onnxruntime/core/util/math_cpu.cc +++ b/onnxruntime/core/util/math_cpu.cc @@ -770,6 +770,7 @@ void Im2col::operator()( template struct Im2col; template struct Im2col; template struct Im2col; +template struct Im2col; template <> void Col2im(const float* data_col, int64_t channels, int64_t height, diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 3032b3170a6e0..91266d81b4f91 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -17,6 +17,8 @@ #include "test/util/include/asserts.h" #include "test/util/include/inference_session_wrapper.h" +#include +#include #include "flatbuffers/idl.h" #include "flatbuffers/util.h" @@ -27,6 +29,28 @@ using namespace ONNX_NAMESPACE; namespace onnxruntime { namespace test { +namespace { +std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { + if (path.is_absolute() || path.empty()) { + return path; + } + + std::filesystem::path workspace_candidate = std::filesystem::current_path() / path; + if (std::filesystem::exists(workspace_candidate)) { + return workspace_candidate; + } + + static const std::filesystem::path kSourceTestRoot = + std::filesystem::path{ORT_TSTR(__FILE__)}.parent_path().parent_path(); + std::filesystem::path source_candidate = kSourceTestRoot / path; + if (std::filesystem::exists(source_candidate)) { + return source_candidate; + } + + return workspace_candidate; +} +} // namespace + struct OrtModelTestInfo { std::basic_string model_filename; std::string logid; @@ -59,17 +83,21 @@ static void RunOrtModel(const OrtModelTestInfo& test_info) { std::vector model_data; InferenceSessionWrapper session_object{so, GetEnvironment()}; + std::filesystem::path model_path = ResolveTestPath(std::filesystem::path{test_info.model_filename}); + + std::cerr << "RunOrtModel cwd: " << std::filesystem::current_path() << " loading: " << model_path << std::endl; + const auto& model_path_str = model_path.native(); if (test_info.run_use_buffer) { // Load the file into a buffer and use the buffer to create inference session size_t num_bytes = 0; - ASSERT_STATUS_OK(Env::Default().GetFileLength(test_info.model_filename.c_str(), num_bytes)); + ASSERT_STATUS_OK(Env::Default().GetFileLength(model_path_str.c_str(), num_bytes)); model_data.resize(num_bytes); - std::ifstream bytes_stream(test_info.model_filename, std::ifstream::in | std::ifstream::binary); + std::ifstream bytes_stream(model_path, std::ifstream::in | std::ifstream::binary); bytes_stream.read(model_data.data(), num_bytes); bytes_stream.close(); ASSERT_STATUS_OK(session_object.Load(model_data.data(), static_cast(num_bytes))); } else { - ASSERT_STATUS_OK(session_object.Load(test_info.model_filename)); // infer type from filename + ASSERT_STATUS_OK(session_object.Load(model_path_str)); // infer type from filename } ASSERT_STATUS_OK(session_object.Initialize()); @@ -145,7 +173,7 @@ static void CompareGraphAndSessionState(const InferenceSessionWrapper& session_o for (const auto& pair : i1) { auto iter = i2.find(pair.first); - ASSERT_NE(iter, i2.cend()); + ASSERT_NE(iter, i2.cend()) << "Missing initializer " << pair.first; const OrtValue& left = pair.second; const OrtValue& right = iter->second; @@ -213,9 +241,28 @@ static void CompareSessionMetadata(const InferenceSessionWrapper& session_object static void SaveAndCompareModels(const PathString& orig_file, const PathString& ort_file, TransformerLevel optimization_level = TransformerLevel::Level3) { + std::filesystem::path orig_path = ResolveTestPath(std::filesystem::path{orig_file}); + std::filesystem::path ort_path = ResolveTestPath(std::filesystem::path{ort_file}); + if (ort_path.has_parent_path()) { + std::filesystem::create_directories(ort_path.parent_path()); + } + + const bool orig_is_ort_format = orig_path.extension() == ORT_TSTR(".ort"); + if (orig_is_ort_format) { + SessionOptions so; + so.session_logid = "SerializeToOrtFormat"; + so.optimized_model_filepath = ort_path.native(); + so.graph_optimization_level = optimization_level; + ASSERT_STATUS_OK(so.config_options.AddConfigEntry(kOrtSessionOptionsConfigSaveModelFormat, "ORT")); + InferenceSessionWrapper session_object{so, GetEnvironment()}; + ASSERT_STATUS_OK(session_object.Load(orig_path.native())); + ASSERT_STATUS_OK(session_object.Initialize()); + return; + } + SessionOptions so; so.session_logid = "SerializeToOrtFormat"; - so.optimized_model_filepath = ort_file; + so.optimized_model_filepath = ort_path.native(); so.graph_optimization_level = optimization_level; // not strictly necessary - type should be inferred from the filename @@ -223,7 +270,7 @@ static void SaveAndCompareModels(const PathString& orig_file, InferenceSessionWrapper session_object{so, GetEnvironment()}; // create .ort file during Initialize due to values in SessionOptions - ASSERT_STATUS_OK(session_object.Load(orig_file)); + ASSERT_STATUS_OK(session_object.Load(orig_path.native())); ASSERT_STATUS_OK(session_object.Initialize()); SessionOptions so2; @@ -234,7 +281,7 @@ static void SaveAndCompareModels(const PathString& orig_file, // load serialized version InferenceSessionWrapper session_object2{so2, GetEnvironment()}; - ASSERT_STATUS_OK(session_object2.Load(ort_file)); + ASSERT_STATUS_OK(session_object2.Load(ort_path.native())); ASSERT_STATUS_OK(session_object2.Initialize()); CompareSessionMetadata(session_object, session_object2); diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index 74a812062875a..b9c58ca386b12 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -22,6 +22,7 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" +#include using namespace ONNX_NAMESPACE; using namespace onnxruntime::logging; @@ -36,12 +37,35 @@ using namespace onnxruntime::internal_testing_ep; #define ORT_MODEL_FOLDER ORT_TSTR("testdata/") +namespace { +std::filesystem::path ResolveInternalTestPath(const std::filesystem::path& path) { + if (path.is_absolute() || path.empty()) { + return path; + } + + std::filesystem::path candidate = std::filesystem::current_path() / path; + if (std::filesystem::exists(candidate)) { + return candidate; + } + + static const std::filesystem::path kSourceTestRoot = + std::filesystem::path{ORT_TSTR(__FILE__)}.parent_path().parent_path().parent_path(); + return kSourceTestRoot / path; +} + +std::basic_string ResolveInternalTestPathString(const ORTCHAR_T* path) { + return ResolveInternalTestPath(std::filesystem::path{path}).native(); +} +} // namespace + static Status CreateSession(const SessionOptions& so, std::unique_ptr& session, const ORTCHAR_T* model_path = ORT_MODEL_FOLDER "mnist.onnx", // arbitrary test model bool enable_custom_ep = true, const std::unordered_set* override_supported_ops = nullptr) { session = std::make_unique(so, GetEnvironment()); + std::filesystem::path resolved_model_path = ResolveInternalTestPath(std::filesystem::path{model_path}); + // set supported ops to ops that are ideally found consecutively in the model. // we can say the EP potentially handles them all, but can also test removing handling of one or more ops // at runtime to simulate a lower spec device where not all ops can be handled. this allows us to test @@ -55,7 +79,7 @@ static Status CreateSession(const SessionOptions& so, std::unique_ptr(*supported_ops))); } - ORT_RETURN_IF_ERROR(session->Load(model_path)); + ORT_RETURN_IF_ERROR(session->Load(resolved_model_path.c_str())); ORT_RETURN_IF_ERROR(session->Initialize()); return Status::OK(); } @@ -98,7 +122,7 @@ static void ExecuteMnist(InferenceSessionWrapper& session, bool custom_ep_enable #if !defined(ORT_MINIMAL_BUILD) TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.test_output.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.test_output.ort"); // // First load the onnx format model and save as an ORT model. @@ -121,10 +145,10 @@ TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { so.optimized_model_filepath.clear(); bool enable_custom_ep = false; - ASSERT_STATUS_OK(CreateSession(so, session2, ort_model_path, enable_custom_ep)); + ASSERT_STATUS_OK(CreateSession(so, session2, ort_model_path.c_str(), enable_custom_ep)); const auto& graph1 = session2->GetGraph(); - // model should have all the original nodes and we should be able to execute with the fallback to CPU EP - ASSERT_EQ(graph1.NumberOfNodes(), num_nodes); + // ensure we can execute with the fallback to CPU EP even if additional nodes are introduced during loading + ASSERT_GE(graph1.NumberOfNodes(), num_nodes); ExecuteMnist(*session2, enable_custom_ep); session2 = nullptr; @@ -133,7 +157,7 @@ TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { // for the ORT format model. // enable_custom_ep = true; - ASSERT_STATUS_OK(CreateSession(so, session2, ort_model_path, enable_custom_ep)); + ASSERT_STATUS_OK(CreateSession(so, session2, ort_model_path.c_str(), enable_custom_ep)); const auto& graph2 = session2->GetGraph(); // model should be able to be loaded, and we should compile using custom ep. that will result in one node for the // custom EP (with Conv/Add/Relu/MaxPool), one for a reshape, and one for the fused MatMul+Add. @@ -142,7 +166,7 @@ TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { } TEST(InternalTestingEP, PreventSaveOfModelWithCompiledOps) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"); // make sure we can't save a model with compiled ops. input/output model format doesn't matter SessionOptions so; @@ -154,7 +178,7 @@ TEST(InternalTestingEP, PreventSaveOfModelWithCompiledOps) { ASSERT_STATUS_OK(session->RegisterExecutionProvider( std::make_unique(supported_ops))); - ASSERT_STATUS_OK(session->Load(ort_model_path)); + ASSERT_STATUS_OK(session->Load(ort_model_path.c_str())); ASSERT_STATUS_NOT_OK_AND_HAS_SUBSTR(session->Initialize(), "Unable to serialize model as it contains compiled nodes"); } @@ -163,7 +187,7 @@ TEST(InternalTestingEP, PreventSaveOfModelWithCompiledOps) { // version of the ONNX operator when matching a static kernel, those are required. #if !defined(DISABLE_CONTRIB_OPS) TEST(InternalTestingEP, TestMixOfStaticAndCompiledKernels) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "transform/fusion/conv_relu_opset12.onnx"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "transform/fusion/conv_relu_opset12.onnx"); SessionOptions so; InferenceSessionWrapper session(so, GetEnvironment()); @@ -175,7 +199,7 @@ TEST(InternalTestingEP, TestMixOfStaticAndCompiledKernels) { ep->EnableStaticKernels(); ASSERT_STATUS_OK(session.RegisterExecutionProvider(std::move(ep))); - ASSERT_STATUS_OK(session.Load(ort_model_path)); + ASSERT_STATUS_OK(session.Load(ort_model_path.c_str())); ASSERT_STATUS_OK(session.Initialize()); TensorShape input_shape_x{1, 1, 7, 7}; @@ -204,7 +228,8 @@ TEST(InternalTestingEP, TestMixOfStaticAndCompiledKernels) { TEST(InternalTestingEP, TestNhwcConversionOfStaticKernels) { auto run_test = [&](const ORTCHAR_T* model_path) { - SCOPED_TRACE("model path: " + ToUTF8String(model_path)); + auto resolved_model_path = ResolveInternalTestPathString(model_path); + SCOPED_TRACE("model path: " + ToUTF8String(resolved_model_path.c_str())); SessionOptions so; // set this if you want to manually inspect the optimized model @@ -218,7 +243,7 @@ TEST(InternalTestingEP, TestNhwcConversionOfStaticKernels) { ep->EnableStaticKernels(); ASSERT_STATUS_OK(session.RegisterExecutionProvider(std::move(ep))); - ASSERT_STATUS_OK(session.Load(model_path)); + ASSERT_STATUS_OK(session.Load(resolved_model_path.c_str())); ASSERT_STATUS_OK(session.Initialize()); const auto& graph = session.GetGraph(); @@ -249,13 +274,11 @@ TEST(InternalTestingEP, TestNhwcConversionOfStaticKernels) { }; // the internal NHWC domain supports opset 11 and later - const ORTCHAR_T* onnx_model_path = ORT_MODEL_FOLDER "squeezenet/model_opset11.onnx"; - run_test(onnx_model_path); + run_test(ORT_MODEL_FOLDER "squeezenet/model_opset11.onnx"); // Note: Using ORT format model with runtime optimizations so that the Conv nodes are preserved in the graph, // not converted into FusedConv nodes. The InternalTestingExecutionProvider handles Conv nodes. - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "squeezenet/model_opset11.with_runtime_opt.ort"; - run_test(ort_model_path); + run_test(ORT_MODEL_FOLDER "squeezenet/model_opset11.with_runtime_opt.ort"); } // make sure allocators returned by SessionState::GetAllocator are valid when IExecutionProvider::ReplaceAllocator @@ -283,8 +306,8 @@ TEST(InternalTestingEP, TestReplaceAllocatorDoesntBreakDueToLocalAllocatorStorag ASSERT_STATUS_OK(session.RegisterExecutionProvider(ep)); } - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "squeezenet/model.onnx"; - ASSERT_STATUS_OK(session.Load(ort_model_path)); + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "squeezenet/model.onnx"); + ASSERT_STATUS_OK(session.Load(ort_model_path.c_str())); ASSERT_STATUS_OK(session.Initialize()); // Need to undo the wrapping that happens in Environment::RegisterAllocator to be able to compare the pointers @@ -301,25 +324,25 @@ TEST(InternalTestingEP, TestReplaceAllocatorDoesntBreakDueToLocalAllocatorStorag // test to validate a minimal build TEST(InternalTestingEP, TestLoadOrtModel) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"); std::unique_ptr session; bool enable_custom_ep = true; - ASSERT_STATUS_OK(CreateSession(SessionOptions{}, session, ort_model_path, enable_custom_ep)); + ASSERT_STATUS_OK(CreateSession(SessionOptions{}, session, ort_model_path.c_str(), enable_custom_ep)); ExecuteMnist(*session, enable_custom_ep); } // test that if the custom EP cannot take all nodes due to device limitations // that we fallback to the CPU implementations and can execute the model TEST(InternalTestingEP, TestLoadOrtModelWithReducedOpCoverage) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"); const std::unordered_set supported_ops{"Conv", "Add", "Relu" /*, "MaxPool"*/}; std::unique_ptr session; bool enable_custom_ep = true; - ASSERT_STATUS_OK(CreateSession(SessionOptions{}, session, ort_model_path, enable_custom_ep, &supported_ops)); + ASSERT_STATUS_OK(CreateSession(SessionOptions{}, session, ort_model_path.c_str(), enable_custom_ep, &supported_ops)); const auto& graph = session->GetGraph(); // Conv+Add gets fused by level 1 optimizer into single node. The 'Conv'/'Add'/'Relu' nodes should be compiled and @@ -454,7 +477,7 @@ TEST(InternalTestingEP, TestOrtModelWithCompileFailure) { // the layout transformation for this EP is already done at this stage and reverting // can result in more failures. // This is to test the model initialization fails if compile fails. - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"); const std::unordered_set& supported_ops{"Conv", "Gemm"}; const std::unordered_set& compile_failure_ops{"Gemm"}; diff --git a/onnxruntime/test/mlas/bench/bench_sconv.cpp b/onnxruntime/test/mlas/bench/bench_sconv.cpp index dc37980002978..163f7f1dc2f16 100644 --- a/onnxruntime/test/mlas/bench/bench_sconv.cpp +++ b/onnxruntime/test/mlas/bench/bench_sconv.cpp @@ -110,6 +110,7 @@ void SCONV_NCHW(benchmark::State& state, const char* /*dummy*/) { static_cast(output_channels_per_group), &activation, &WorkingBufferSize, + false, 0.0f, nullptr); diff --git a/onnxruntime/test/mlas/unittest/test_conv2d.h b/onnxruntime/test/mlas/unittest/test_conv2d.h index 20bf0ec84f5bf..736d8587b2546 100644 --- a/onnxruntime/test/mlas/unittest/test_conv2d.h +++ b/onnxruntime/test/mlas/unittest/test_conv2d.h @@ -57,6 +57,7 @@ class MlasConv2DTest : public MlasTestBase { FilterCount, &Activation, &WorkingBufferSize, + false, 0.0f, threadpool_); diff --git a/onnxruntime/test/optimizer/conv_add_act_test.cc b/onnxruntime/test/optimizer/conv_add_act_test.cc index f61f9b29d9cce..704d7ac907450 100644 --- a/onnxruntime/test/optimizer/conv_add_act_test.cc +++ b/onnxruntime/test/optimizer/conv_add_act_test.cc @@ -30,9 +30,10 @@ void TestConvPath(const std::vector& input_shape, const std::vector disabled_optimizers = {"NchwcTransformer"}; + InlinedHashSet disabled_optimizers = {"NchwcTransformer", "NhwcTransformer"}; TransformerTester(build_test_case, check_graph, TransformerLevel::Default, diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index de973679c8f80..7bb492c4854d9 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -363,6 +363,7 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); @@ -402,6 +403,7 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); @@ -443,6 +445,7 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); @@ -494,7 +497,7 @@ TEST(TransformerTest, FuseFp16InitializersWithGraphOutputs) { // by folding it with Add node. This will not allow us to test the // scenario where Cast node is producing graph output and need to // kept untouched by FuseInitializersTransformer. - ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"ConstantFolding"})); + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"ConstantFolding", "NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); _graph_structure_at_load(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 21ea7af4e7389..4d270ba014eae 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -224,6 +224,28 @@ TEST(NhwcTransformerTests, ConvGlobalAveragePool) { TransformerLevel::Level3); } +TEST(NhwcTransformerTests, ConvDepthwiseFloat) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({8, 1, 3, 3}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& conv_node = builder.AddConvNode(input_arg, weight_arg, output_arg); + conv_node.AddAttribute("group", static_cast(8)); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3); +} + TEST(NhwcTransformerTests, ConvAveragePool) { DNNL_GTEST_SKIP(); From 1606a1c473ca281d63952e4314fe068c8a6e8b0c Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Fri, 19 Dec 2025 13:20:49 +0000 Subject: [PATCH 002/140] Add a value for channels_last to bench_sconv.cpp Signed-off-by: Orlaith Monahan --- onnxruntime/test/mlas/bench/bench_sconv.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/onnxruntime/test/mlas/bench/bench_sconv.cpp b/onnxruntime/test/mlas/bench/bench_sconv.cpp index 163f7f1dc2f16..e5559a8f838b0 100644 --- a/onnxruntime/test/mlas/bench/bench_sconv.cpp +++ b/onnxruntime/test/mlas/bench/bench_sconv.cpp @@ -218,6 +218,7 @@ void SCONV_NCHW_THREADED(benchmark::State& state, const char* /*dummy*/) { static_cast(output_channels_per_group), &activation, &WorkingBufferSize, + false, 0.0f, tp); From 2dd199e1051bf7c0b575ee6514ed3516090c9613 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 12 Jan 2026 10:57:00 +0000 Subject: [PATCH 003/140] Update internal_testing_tests.cc Update to the internal_testings_tests helper macros for file expansion so it works on other platforms --- onnxruntime/test/internal_testing_ep/internal_testing_tests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index b9c58ca386b12..e8bab013de97a 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -49,7 +49,7 @@ std::filesystem::path ResolveInternalTestPath(const std::filesystem::path& path) } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{ORT_TSTR(__FILE__)}.parent_path().parent_path().parent_path(); + std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path().parent_path(); return kSourceTestRoot / path; } From 4df9cea096cce5c595fb25fb07d0be7f10af5b36 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 14 Jan 2026 16:05:28 +0000 Subject: [PATCH 004/140] Update nhwc_transformer_test.cc Fix for failing ConvDepthwiseFloat test, allows for a small tolerance when running on different hardware --- onnxruntime/test/optimizer/nhwc_transformer_test.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 4d270ba014eae..3ad70b7f6ff5e 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -243,7 +243,11 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat) { TransformerTester(build_test_case, check_nhwc_graph, TransformerLevel::Level2, - TransformerLevel::Level3); + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); + } TEST(NhwcTransformerTests, ConvAveragePool) { From b133782d1e4eb97276e98318d123db36e3c97252 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 14 Jan 2026 16:07:22 +0000 Subject: [PATCH 005/140] Update internal_testing_tests.cc For for failing TestSaveAndLoadOrtModel test Make sure the model being saved / loaded is being done from a writeable location --- .../test/internal_testing_ep/internal_testing_tests.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index e8bab013de97a..83fb3f07c8e76 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -122,7 +122,9 @@ static void ExecuteMnist(InferenceSessionWrapper& session, bool custom_ep_enable #if !defined(ORT_MINIMAL_BUILD) TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { - const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.test_output.ort"); + const auto ort_model_dir = ResolveInternalTestPath(std::filesystem::path{ORT_MODEL_FOLDER}); + const std::basic_string ort_model_path = + (ort_model_dir / ORT_TSTR("mnist.internal_testing_ep.test_output.ort")).native(); // // First load the onnx format model and save as an ORT model. From 0c2d1cd4b7abd6d33e5231b77db41358b2dceee0 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 14 Jan 2026 16:09:10 +0000 Subject: [PATCH 006/140] Update ort_model_only_test.cc Fix for undeclared identifier linker error --- onnxruntime/test/framework/ort_model_only_test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 91266d81b4f91..0de93a25f89f1 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -24,6 +24,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#define WIDEN2(x) L##x +#define WIDEN(x) WIDEN2(x) using namespace ONNX_NAMESPACE; @@ -41,7 +43,7 @@ std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{ORT_TSTR(__FILE__)}.parent_path().parent_path(); + std::filesystem::path{WIDEN(__FILE__)}.parent_path().parent_path(); std::filesystem::path source_candidate = kSourceTestRoot / path; if (std::filesystem::exists(source_candidate)) { return source_candidate; From 25c0be7081bc52310679a4339540cc3c4b1ea4a7 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 14 Jan 2026 18:00:49 +0000 Subject: [PATCH 007/140] Lintrunner fixes Signed-off-by: Orlaith Monahan --- onnxruntime/test/framework/ort_model_only_test.cc | 2 +- onnxruntime/test/optimizer/nhwc_transformer_test.cc | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 0de93a25f89f1..72f3c6e08095b 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -43,7 +43,7 @@ std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{WIDEN(__FILE__)}.parent_path().parent_path(); + std::filesystem::path{WIDEN(__FILE__)}.parent_path().parent_path(); std::filesystem::path source_candidate = kSourceTestRoot / path; if (std::filesystem::exists(source_candidate)) { return source_candidate; diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 3ad70b7f6ff5e..87afd865a60a5 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -247,7 +247,6 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat) { /*opset_version*/ 12, /*per_sample_tolerance*/ 1e-6, /*relative_per_sample_tolerance*/ 1e-6); - } TEST(NhwcTransformerTests, ConvAveragePool) { From 04821506db7767e7afbd3d262c866d5c73cf5d70 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:07:16 +0000 Subject: [PATCH 008/140] Update onnxruntime/core/optimizer/nhwc_transformer.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/core/optimizer/nhwc_transformer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 9544cf7395025..5bd592f8ef01d 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -212,7 +212,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, } #ifdef USE_KLEIDIAI - // Klediai specific block for NhwcFusedConvolutions + // KleidiAI specific block for NhwcFusedConvolutions { // F32 Conv -> F32 NHWC Conv OpKernelRegistryId nhwc_conv_fp32{ From f9606cdddf31c4320216c69e7df8fc46dabddfbe Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:07:31 +0000 Subject: [PATCH 009/140] Update onnxruntime/core/framework/kernel_type_str_resolver.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/core/framework/kernel_type_str_resolver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index aacbc8fc0a4fb..f73550c14ebc0 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -37,7 +37,7 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } #ifdef USE_KLEIDIAI - // Klediai specific block for NhwcFusedConvolutions + // KleidiAI specific block for NhwcFusedConvolutions if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; op_it = map.find(fused_conv_op_id); From 63d9c555b8b05e59fc4862a77e92264232cd35e2 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:08:13 +0000 Subject: [PATCH 010/140] Update onnxruntime/core/providers/cpu/nn/conv.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/core/providers/cpu/nn/conv.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 4cc0df42d2969..f5615015366d0 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -243,8 +243,8 @@ Status Conv::Compute(OpKernelContext* context) const { concurrency::ThreadPool* thread_pool = context->GetOperatorThreadPool(); if (channels_last_) { - ORT_RETURN_IF_NOT(kernel_rank == 2, "NhwcFusedConv currently supports 2D kernels."); - ORT_RETURN_IF_NOT(dilations[0] == 1 && dilations[1] == 1, "NhwcFusedConv currently supports dilation == 1."); + ORT_RETURN_IF_NOT(kernel_rank == 2, "Conv with channels_last layout currently supports 2D kernels."); + ORT_RETURN_IF_NOT(dilations[0] == 1 && dilations[1] == 1, "Conv with channels_last layout currently supports dilation == 1."); } const bool wants_channels_last = channels_last_; From 457513b5fee415a1f4ff0ab2ba67d23f744bd0a6 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:08:41 +0000 Subject: [PATCH 011/140] Update onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index fc5f3a459e616..2d604a86561df 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -18,7 +18,9 @@ class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, EmbedLayerNormalization); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, ExpandDims); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, FusedConv); +#ifdef USE_KLEIDIAI class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, NhwcFusedConv); +#endif class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, FusedGemm); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, GreedySearch); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, MultiHeadAttention); From b836bd3b0584b15f3b153a98f036c0b6c008d010 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:08:56 +0000 Subject: [PATCH 012/140] Update onnxruntime/test/framework/ort_model_only_test.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/test/framework/ort_model_only_test.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 72f3c6e08095b..da1622dfd1af9 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -87,7 +87,6 @@ static void RunOrtModel(const OrtModelTestInfo& test_info) { InferenceSessionWrapper session_object{so, GetEnvironment()}; std::filesystem::path model_path = ResolveTestPath(std::filesystem::path{test_info.model_filename}); - std::cerr << "RunOrtModel cwd: " << std::filesystem::current_path() << " loading: " << model_path << std::endl; const auto& model_path_str = model_path.native(); if (test_info.run_use_buffer) { // Load the file into a buffer and use the buffer to create inference session From 891dad554f4ca41b3db8826fcaa7468508844c4a Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 4 Feb 2026 12:57:14 +0000 Subject: [PATCH 013/140] Additional guards to not include KLEIDIAI specific kernels Signed-off-by: Orlaith Monahan --- onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index 2d604a86561df..692412a8efcce 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -305,7 +305,9 @@ Status RegisterCpuContribKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, +#ifdef USE_KLEIDIAI BuildKernelCreateInfo, +#endif BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, From 2467ca9f52d560a7605eca037b1f0df8196cb191 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 3 Mar 2026 16:20:03 +0000 Subject: [PATCH 014/140] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc | 2 ++ onnxruntime/contrib_ops/cpu/fused_conv.cc | 3 +++ 2 files changed, 5 insertions(+) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index 692412a8efcce..af54d048b218f 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -305,8 +305,10 @@ Status RegisterCpuContribKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, + BuildKernelCreateInfo, #ifdef USE_KLEIDIAI BuildKernelCreateInfo, +#endif #endif BuildKernelCreateInfo, BuildKernelCreateInfo, diff --git a/onnxruntime/contrib_ops/cpu/fused_conv.cc b/onnxruntime/contrib_ops/cpu/fused_conv.cc index d77efc26d0e2f..f4caebf04f899 100644 --- a/onnxruntime/contrib_ops/cpu/fused_conv.cc +++ b/onnxruntime/contrib_ops/cpu/fused_conv.cc @@ -31,6 +31,9 @@ ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( 1, float, KernelDefBuilder() + // Allow the optional "sum" input (index 3) to be reused as the output buffer (index 0), + // consistent with the FusedConv kernel registration. + .MayInplace(3, 0) .TypeConstraint("T", DataTypeImpl::GetTensorType()), FusedConvFloat); From 2df52ff2e58875bcdbce0e394c5887daa483cb32 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 3 Mar 2026 19:32:21 +0000 Subject: [PATCH 015/140] Adding some extra tests and removing unnecessary code Signed-off-by: Orlaith Monahan --- .../contrib_ops/cpu/cpu_contrib_kernels.cc | 2 -- .../framework/kernel_type_str_resolver.cc | 2 -- .../kernel_type_str_resolver_utils_test.cc | 34 +++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index 42c6ab1516df6..cccd44bc1ac2d 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -313,10 +313,8 @@ Status RegisterCpuContribKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, - BuildKernelCreateInfo, #ifdef USE_KLEIDIAI BuildKernelCreateInfo, -#endif #endif BuildKernelCreateInfo, #if !defined(DISABLE_GENERATION_OPS) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index f73550c14ebc0..ac49f50751cb5 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -36,7 +36,6 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } -#ifdef USE_KLEIDIAI // KleidiAI specific block for NhwcFusedConvolutions if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; @@ -46,7 +45,6 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, op_it = map.find(conv_op_id); } } -#endif return op_it; } diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 86ffef6c49dc9..518010625a2b4 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -9,7 +9,10 @@ #include "gtest/gtest.h" #include "core/flatbuffers/schema/ort.fbs.h" +#include "core/graph/constants.h" +#include "core/graph/model.h" #include "core/graph/schema_registry.h" +#include "test/test_environment.h" #include "test/util/include/asserts.h" namespace onnxruntime::test { @@ -49,6 +52,37 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { + SchemaRegistryManager schema_registry; + const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); + ASSERT_NE(fused_conv_schema, nullptr); + + KernelTypeStrResolver resolver; + ASSERT_STATUS_OK(resolver.RegisterOpSchema(*fused_conv_schema)); + + Model model("nhwc_fused_conv_resolver_test", false, DefaultLoggingManager().DefaultLogger()); + auto& graph = model.MainGraph(); + + ONNX_NAMESPACE::TypeProto float_tensor; + auto* tensor_type = float_tensor.mutable_tensor_type(); + tensor_type->set_elem_type(ONNX_NAMESPACE::TensorProto_DataType_FLOAT); + tensor_type->mutable_shape()->add_dim()->set_dim_value(1); + + auto& x = graph.GetOrCreateNodeArg("x", &float_tensor); + auto& w = graph.GetOrCreateNodeArg("w", &float_tensor); + auto& y = graph.GetOrCreateNodeArg("y", &float_tensor); + + auto& nhwc_fused_conv = graph.AddNode( + "nhwc_fused_conv", "NhwcFusedConv", "test node", {&x, &w}, {&y}, nullptr, kMSDomain); + nhwc_fused_conv.SetSinceVersion(1); + + gsl::span resolved_args; + ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); + ASSERT_FALSE(resolved_args.empty()); +} +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) + // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in // onnxruntime/core/framework/kernel_type_str_resolver_utils.cc From ec13eef417b6e1e5dd97106c886f3697bbf5f16d Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 10 Mar 2026 12:47:11 +0000 Subject: [PATCH 016/140] Adding additional USE_KLEIDIAI guards Signed-off-by: Orlaith Monahan --- onnxruntime/contrib_ops/cpu/fused_conv.cc | 2 ++ onnxruntime/core/framework/kernel_type_str_resolver.cc | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/onnxruntime/contrib_ops/cpu/fused_conv.cc b/onnxruntime/contrib_ops/cpu/fused_conv.cc index f4caebf04f899..76451232421da 100644 --- a/onnxruntime/contrib_ops/cpu/fused_conv.cc +++ b/onnxruntime/contrib_ops/cpu/fused_conv.cc @@ -26,6 +26,7 @@ ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( .TypeConstraint("T", DataTypeImpl::GetTensorType()), FusedConvFloat); +#ifdef USE_KLEIDIAI ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( NhwcFusedConv, 1, @@ -36,6 +37,7 @@ ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( .MayInplace(3, 0) .TypeConstraint("T", DataTypeImpl::GetTensorType()), FusedConvFloat); +#endif } // namespace contrib } // namespace onnxruntime diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index ac49f50751cb5..f995caec22cf9 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -35,7 +35,7 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } } - +#ifdef USE_KLEIDIAI // KleidiAI specific block for NhwcFusedConvolutions if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; @@ -45,6 +45,7 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, op_it = map.find(conv_op_id); } } +#endif return op_it; } From 621806b9f253df3ddd12bb28e57aae5448f51716 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 10 Mar 2026 12:50:16 +0000 Subject: [PATCH 017/140] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/core/optimizer/conv_add_act_fusion.cc | 13 ++++++++++--- onnxruntime/core/optimizer/nhwc_transformer.cc | 15 --------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/onnxruntime/core/optimizer/conv_add_act_fusion.cc b/onnxruntime/core/optimizer/conv_add_act_fusion.cc index dfdc61c1b76c3..d51ea894725fa 100644 --- a/onnxruntime/core/optimizer/conv_add_act_fusion.cc +++ b/onnxruntime/core/optimizer/conv_add_act_fusion.cc @@ -214,12 +214,19 @@ class FuseConvAddActivationAction : public ReplaceWithNew { const auto& target = runtimeState.selected_nodes.Target(); const auto* channels_last_attr = graph_utils::GetNodeAttribute(target, "channels_last"); const bool channels_last = channels_last_attr != nullptr && channels_last_attr->i() != 0; + const std::string& op_type = target.OpType(); - if (target.OpType() == "Conv") { - return channels_last ? "NhwcFusedConv" : "FusedConv"; + // If channels_last is set, use NHWC fused convolution regardless of original op type. + if (channels_last) { + return "NhwcFusedConv"; } - return "NhwcFusedConv"; + // Without channels_last, convert Conv to FusedConv, and leave other op types unchanged. + if (op_type == "Conv") { + return "FusedConv"; + } + + return op_type; } std::string Domain(const RuntimeState&) const override { return kMSDomain; } diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 5bd592f8ef01d..5250eeb9102c1 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -338,21 +338,6 @@ Status NhwcTransformer::ApplyImpl(Graph& graph, bool& modified, int graph_level, node->SetAttributeInt("channels_last", 1); } - if (node->OpType() == "Conv" || node->OpType() == "FusedConv") { - const auto group_opt = node->GetAttributeInt("group"); - if (group_opt.has_value() && group_opt.value() != 1) { - continue; - } - - const auto dilations_opt = node->GetAttributeInts("dilations"); - if (dilations_opt.has_value()) { - const auto& dilations = dilations_opt.value(); - if ((dilations.size() >= 1 && dilations[0] != 1) || - (dilations.size() >= 2 && dilations[1] != 1)) { - continue; - } - } - } size_t rank = shape->dim_size(); std::vector input_perm = ChannelFirstToLastPerm(rank); From 78c7728d8f86c73c2aa25aa710230895b325195f Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 10 Mar 2026 14:14:59 +0000 Subject: [PATCH 018/140] Adding USE_KLEIDIAI guard to kleidiai specific kernel_type_str_resolver_utils_test Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 518010625a2b4..84a7233325023 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -52,7 +52,7 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } -#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +#if defined(USE_KLEIDIAI) && !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { SchemaRegistryManager schema_registry; const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); From 055c62c89d66fd57365ccf2a65a9bc15fcd253e3 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 10 Mar 2026 17:59:23 +0000 Subject: [PATCH 019/140] Updates to address comments and codex issues Signed-off-by: Orlaith Monahan --- .../core/optimizer/nhwc_transformer.cc | 11 +++--- onnxruntime/core/providers/cpu/nn/conv.cc | 2 +- .../test/framework/ort_model_only_test.cc | 36 +++++++++---------- .../internal_testing_tests.cc | 2 +- .../test/optimizer/nhwc_transformer_test.cc | 2 +- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 5250eeb9102c1..611ee1cc498a2 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -25,6 +25,12 @@ namespace onnxruntime { using namespace layout_transformation; #ifdef USE_KLEIDIAI +// KleidiFp32NhwcFilter ensures the compatibility of Conv/FusedConv nodes before performing data conversion +// Checks the following: +// Must have 4D Input +// Must have symmetric 2d padding (if applicable) +// No add input +// Dilation and Group sizes must be 1 bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& graph, onnx_transpose_optimization::api::NodeRef& node) { auto& base_node = NodeFromApiNode(node); @@ -52,11 +58,6 @@ bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& grap } } - const auto inputs = node.Inputs(); - if (inputs.size() > 3 && !inputs[3].empty()) { - return false; - } - const auto* weight_shape = base_node.InputDefs()[1]->Shape(); if (weight_shape == nullptr || weight_shape->dim_size() != 4) { return false; diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 99e7f7de225ca..d82bd4893c785 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -199,10 +199,10 @@ Status Conv::Compute(OpKernelContext* context) const { const Tensor* W = context->Input(1); const Tensor* B = num_inputs >= 3 ? context->Input(2) : nullptr; const Tensor* Sum = num_inputs >= 4 ? context->Input(3) : nullptr; + ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X->Shape(), W->Shape(), channels_last_)); const int64_t N = X->Shape()[0]; const int64_t C = X->Shape()[channels_last_ ? 3 : 1]; const int64_t M = W->Shape()[0]; - ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X->Shape(), W->Shape(), channels_last_)); TensorShapeVector kernel_shape; ORT_RETURN_IF_ERROR(conv_attrs_.ComputeKernelShape(W->Shape(), kernel_shape)); diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 83f568a2b2c54..22f4a088a2c93 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -24,8 +24,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#define WIDEN2(x) L##x -#define WIDEN(x) WIDEN2(x) using namespace ONNX_NAMESPACE; @@ -38,17 +36,21 @@ std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { } std::filesystem::path workspace_candidate = std::filesystem::current_path() / path; - if (std::filesystem::exists(workspace_candidate)) { + std::error_code ec; + if (std::filesystem::exists(workspace_candidate, ec) && !ec) { return workspace_candidate; } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{WIDEN(__FILE__)}.parent_path().parent_path(); + std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path(); std::filesystem::path source_candidate = kSourceTestRoot / path; - if (std::filesystem::exists(source_candidate)) { + ec.clear(); + if (std::filesystem::exists(source_candidate, ec) && !ec) { return source_candidate; } + // Keep the original relative-to-workdir intent so the downstream file-open + // error points to the path we actually tried first. return workspace_candidate; } } // namespace @@ -241,26 +243,14 @@ static void CompareSessionMetadata(const InferenceSessionWrapper& session_object static void SaveAndCompareModels(const PathString& orig_file, const PathString& ort_file, - TransformerLevel optimization_level = TransformerLevel::Level3) { + TransformerLevel optimization_level = TransformerLevel::Level3, + bool compare_saved_model = true) { std::filesystem::path orig_path = ResolveTestPath(std::filesystem::path{orig_file}); std::filesystem::path ort_path = ResolveTestPath(std::filesystem::path{ort_file}); if (ort_path.has_parent_path()) { std::filesystem::create_directories(ort_path.parent_path()); } - const bool orig_is_ort_format = orig_path.extension() == ORT_TSTR(".ort"); - if (orig_is_ort_format) { - SessionOptions so; - so.session_logid = "SerializeToOrtFormat"; - so.optimized_model_filepath = ort_path.native(); - so.graph_optimization_level = optimization_level; - ASSERT_STATUS_OK(so.config_options.AddConfigEntry(kOrtSessionOptionsConfigSaveModelFormat, "ORT")); - InferenceSessionWrapper session_object{so, GetEnvironment()}; - ASSERT_STATUS_OK(session_object.Load(orig_path.native())); - ASSERT_STATUS_OK(session_object.Initialize()); - return; - } - SessionOptions so; so.session_logid = "SerializeToOrtFormat"; so.optimized_model_filepath = ort_path.native(); @@ -285,6 +275,10 @@ static void SaveAndCompareModels(const PathString& orig_file, ASSERT_STATUS_OK(session_object2.Load(ort_path.native())); ASSERT_STATUS_OK(session_object2.Initialize()); + if (!compare_saved_model) { + return; + } + CompareSessionMetadata(session_object, session_object2); CompareGraphAndSessionState(session_object, session_object2); } @@ -410,7 +404,9 @@ void TestOrtModelUpdate(const PathString& onnx_file, // ort_file_v4 is ORT format model using v4 where we used kernel hashes instead of constraints // update v4 model and save as v5. do not run optimizations in order to preserve the model as-is. - SaveAndCompareModels(ort_file_v4, generated_ort_file_v5, TransformerLevel::Default); + // Loading a v4 ORT model updates it as part of deserialization, so the in-memory graph/session state is not + // expected to match a separately reloaded v5 model exactly. Just validate that we can save and reload it. + SaveAndCompareModels(ort_file_v4, generated_ort_file_v5, TransformerLevel::Default, false); // run the original, v4 and v5 models and check the output is the same OrtModelTestInfo test_info; diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index 83fb3f07c8e76..4cb7cf397d95d 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -49,7 +49,7 @@ std::filesystem::path ResolveInternalTestPath(const std::filesystem::path& path) } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path().parent_path(); + std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path(); return kSourceTestRoot / path; } diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 87afd865a60a5..f9dd1d721bf74 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -224,7 +224,7 @@ TEST(NhwcTransformerTests, ConvGlobalAveragePool) { TransformerLevel::Level3); } -TEST(NhwcTransformerTests, ConvDepthwiseFloat) { +TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto build_test_case = [&](ModelTestBuilder& builder) { auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); auto* weight_arg = builder.MakeInitializer({8, 1, 3, 3}, -1.0f, 1.0f); From 8eef03ff528be9875f0dd68f6b48e648a70f2c4b Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 12 Mar 2026 09:44:15 +0000 Subject: [PATCH 020/140] Fix for errors around NHWC and FusedSum Signed-off-by: Orlaith Monahan --- .../core/optimizer/nhwc_transformer.cc | 29 +++++++++---------- onnxruntime/core/optimizer/nhwc_transformer.h | 1 + 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 611ee1cc498a2..7bd2beed1ee03 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -148,10 +148,10 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("QLinearConv", kOnnxDomain, api::DataType::INT8), - OpTransformInfo{qconv_int8.op_type_, qconv_int8.domain_, qconv_int8.version_, true}); + OpTransformInfo{qconv_int8.op_type_, qconv_int8.domain_, qconv_int8.version_, true, false}); conv_table_.emplace( OpIdInfo("QLinearConv", kMSDomain, api::DataType::INT8), - OpTransformInfo{qconv_int8.op_type_, qconv_int8.domain_, qconv_int8.version_, true}); + OpTransformInfo{qconv_int8.op_type_, qconv_int8.domain_, qconv_int8.version_, true, false}); } } @@ -167,10 +167,10 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("QLinearConv", kOnnxDomain, api::DataType::UINT8), - OpTransformInfo{qconv_uint8.op_type_, qconv_uint8.domain_, qconv_uint8.version_, true}); + OpTransformInfo{qconv_uint8.op_type_, qconv_uint8.domain_, qconv_uint8.version_, true, false}); conv_table_.emplace( OpIdInfo("QLinearConv", kMSDomain, api::DataType::UINT8), - OpTransformInfo{qconv_uint8.op_type_, qconv_uint8.domain_, qconv_uint8.version_, true}); + OpTransformInfo{qconv_uint8.op_type_, qconv_uint8.domain_, qconv_uint8.version_, true, false}); } } @@ -205,10 +205,10 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, conv_table_.emplace( OpIdInfo("Conv", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, filter}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, false, filter}); conv_table_.emplace( OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, filter}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, false, filter}); } } @@ -233,10 +233,10 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, conv_table_.emplace( OpIdInfo("Conv", kOnnxDomain, api::DataType::FLOAT), - OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, filter}); + OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, true, filter}); conv_table_.emplace( OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT), - OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, filter}); + OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, true, filter}); } } #endif @@ -254,7 +254,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("MaxPool", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_maxpool_fp16.op_type_, nhwc_maxpool_fp16.domain_, nhwc_maxpool_fp16.version_, false}); + OpTransformInfo{nhwc_maxpool_fp16.op_type_, nhwc_maxpool_fp16.domain_, nhwc_maxpool_fp16.version_, false, false}); } } @@ -271,7 +271,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("AveragePool", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_avgpool_fp16.op_type_, nhwc_avgpool_fp16.domain_, nhwc_avgpool_fp16.version_, false}); + OpTransformInfo{nhwc_avgpool_fp16.op_type_, nhwc_avgpool_fp16.domain_, nhwc_avgpool_fp16.version_, false, false}); } } @@ -288,7 +288,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("GlobalAveragePool", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_gavgpool_fp16.op_type_, nhwc_gavgpool_fp16.domain_, nhwc_gavgpool_fp16.version_, false}); + OpTransformInfo{nhwc_gavgpool_fp16.op_type_, nhwc_gavgpool_fp16.domain_, nhwc_gavgpool_fp16.version_, false, false}); } } }; @@ -348,10 +348,9 @@ Status NhwcTransformer::ApplyImpl(Graph& graph, bool& modified, int graph_level, if (!inputs.empty()) { input_perms[0] = &input_perm; } - // Optional Sum (Z) input for FusedConv variants resides at index 3. When present, - // it must be converted to NHWC alongside the activation tensor. - const bool has_fused_sum_input = (node->Domain() == kMSDomain && node->OpType() == "FusedConv"); - if (has_fused_sum_input && inputs.size() > 3 && !inputs[3].empty()) { + // Some transformed operators require the optional fused Sum (Z) input at index 3 + // to be converted alongside the activation tensor. + if (transform->transpose_fused_sum_input_ && inputs.size() > 3 && !inputs[3].empty()) { input_perms[3] = &input_perm; } diff --git a/onnxruntime/core/optimizer/nhwc_transformer.h b/onnxruntime/core/optimizer/nhwc_transformer.h index 6dd11bdba6bdd..24d4cb3069b30 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.h +++ b/onnxruntime/core/optimizer/nhwc_transformer.h @@ -62,6 +62,7 @@ struct OpTransformInfo { const std::string domain_; const int version_; const bool has_channels_last_attrib_; + const bool transpose_fused_sum_input_; const FilterFn filter_{nullptr}; }; From 768a7936748c875fbeb9848d0a490f82de659f52 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 12 Mar 2026 13:07:59 +0000 Subject: [PATCH 021/140] Extensive Refactor of NHWC Convolution to try and fix tests * Moved all changes to be KLEIDIAI specific * Removed the existence of a NHWCFusedConv from non KLEIDIAI code * All checks for useability of the feature now happen much earlier * Added more tests * Includes supports for much more convolutions now Signed-off-by: Orlaith Monahan --- .../kernel_type_str_resolver_utils.cc | 14 ++ onnxruntime/core/mlas/inc/mlas.h | 14 ++ onnxruntime/core/mlas/lib/convolve.cpp | 56 ++++++++ .../mlas/lib/kleidiai/convolve_kleidiai.cpp | 57 ++++---- .../layout_transformation.cc | 1 - ...out_transformation_potentially_added_ops.h | 1 + .../core/optimizer/nhwc_transformer.cc | 53 +------ onnxruntime/core/providers/cpu/nn/conv.cc | 61 ++++++-- .../test/contrib_ops/fused_conv_test.cc | 110 ++++++++++++++ .../kernel_type_str_resolver_utils_test.cc | 27 ++++ .../test/optimizer/nhwc_transformer_test.cc | 135 ++++++++++++++++++ .../optimizer/transpose_optimizer_test.cc | 58 ++++++++ 12 files changed, 504 insertions(+), 83 deletions(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc index 65ad60f478d78..50722f930228c 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc @@ -9,6 +9,7 @@ #include "core/common/common.h" #include "core/flatbuffers/schema/ort.fbs.h" +#include "core/graph/schema_registry.h" #include "core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h" namespace onnxruntime::kernel_type_str_resolver_utils { @@ -45,6 +46,18 @@ Status LoadKernelTypeStrResolverFromBuffer(KernelTypeStrResolver& kernel_type_st } Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrResolver& kernel_type_str_resolver) { +#if !defined(ORT_MINIMAL_BUILD) + const auto required_op_ids = GetLayoutTransformationRequiredOpIdentifiers(); + const auto schema_registry = SchemaRegistryManager{}; + for (const auto& op_id : required_op_ids) { + const auto* op_schema = schema_registry.GetSchema(std::string{op_id.op_type}, op_id.since_version, + std::string{op_id.domain}); + ORT_RETURN_IF(op_schema == nullptr, "Failed to get op schema."); + ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); + } + + return Status::OK(); +#else KernelTypeStrResolver resolver_with_required_ops{}; // to generate kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes, run the test: @@ -422,6 +435,7 @@ Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrRe kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes)); kernel_type_str_resolver.Merge(std::move(resolver_with_required_ops)); return Status::OK(); +#endif } } // namespace onnxruntime::kernel_type_str_resolver_utils diff --git a/onnxruntime/core/mlas/inc/mlas.h b/onnxruntime/core/mlas/inc/mlas.h index 07d05a2770272..9e9d25d794c7c 100644 --- a/onnxruntime/core/mlas/inc/mlas.h +++ b/onnxruntime/core/mlas/inc/mlas.h @@ -933,6 +933,20 @@ MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, float Beta, MLAS_THREADPOOL* ThreadPool); +bool +MLASCALL +MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + size_t Dimensions, + size_t BatchCount, + size_t GroupCount, + const size_t* InputShape, + const size_t* KernelShape, + const size_t* DilationShape, + const size_t* Padding, + const size_t* StrideShape, + size_t FilterCount, + float Beta); + void MLASCALL MlasConv( diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index bc1c296d8c88a..abfa3f2c73412 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1245,6 +1245,62 @@ Return Value: // Chance of arithmetic overflow could be reduced #pragma warning(disable : 26451) #endif + +namespace { + +static constexpr size_t ComputeChannelsLastDilatedKernelSize(size_t dilation, size_t kernel) { + return (dilation * kernel) - (dilation - 1); +} + +static constexpr size_t ComputeChannelsLastConvOutSize(size_t input, size_t kernel, size_t padding, size_t stride) { + if (stride > 0 && (input + 2 * padding) >= kernel) { + return (((input - kernel) + (2 * padding)) / stride) + 1; + } + + return 0; +} + +} // namespace + +bool +MLASCALL +MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + size_t Dimensions, + size_t BatchCount, + size_t GroupCount, + const size_t* InputShape, + const size_t* KernelShape, + const size_t* DilationShape, + const size_t* Padding, + const size_t* StrideShape, + size_t FilterCount, + float Beta) +{ + if (Dimensions != 2 || BatchCount != 1 || GroupCount != 1 || Beta != 0.0f) { + return false; + } + + if (Padding[0] != Padding[2] || Padding[1] != Padding[3]) { + return false; + } + + const size_t output_h = + ComputeChannelsLastConvOutSize(InputShape[0], ComputeChannelsLastDilatedKernelSize(DilationShape[0], KernelShape[0]), + Padding[0], StrideShape[0]); + const size_t output_w = + ComputeChannelsLastConvOutSize(InputShape[1], ComputeChannelsLastDilatedKernelSize(DilationShape[1], KernelShape[1]), + Padding[1], StrideShape[1]); + if (output_h == 0 || output_w == 0) { + return false; + } + + if (FilterCount <= 1 || KernelShape[0] < 3 || KernelShape[1] < 3) { + return false; + } + + return true; +} + void MLASCALL MlasConvPrepare( diff --git a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp index a4cb9171a45f8..b4dcc65cd7eb4 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp +++ b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp @@ -27,13 +27,17 @@ const KaiF32IMatmulKernel& imatmul_conv = GetKleidiAIF32IMatmulUKernel(); // Right-hand-side (weights) cache key struct RhsCacheKey { size_t co, ci, kh, kw, dilationh, dilationw; + bool has_bias; size_t weights_hash; + size_t bias_hash; bool operator==(const RhsCacheKey& other) const { return co == other.co && ci == other.ci && kh == other.kh && kw == other.kw && dilationh == other.dilationh && dilationw == other.dilationw && - weights_hash == other.weights_hash; + has_bias == other.has_bias && + weights_hash == other.weights_hash && + bias_hash == other.bias_hash; } }; @@ -59,7 +63,12 @@ struct LhsCacheKey { // Based on Knuth's multiplicative hashing method constexpr size_t HASH_GOLDEN_RATIO_CONST = 0x9e3779b9; -size_t HashWeights(const float* data, size_t count = 16) { +size_t HashTensorPrefix(const float* data, size_t element_count, size_t prefix_count = 16) { + if (data == nullptr || element_count == 0 || prefix_count == 0) { + return 0; + } + + const size_t count = std::min(element_count, prefix_count); size_t h = 0; for (size_t i = 0; i < count; ++i) { h ^= std::hash()(data[i]) + HASH_GOLDEN_RATIO_CONST + (h << 6) + (h >> 2); @@ -81,7 +90,9 @@ namespace std { (std::hash()(k.kh) << 3) ^ (std::hash()(k.kw) << 4) ^ (std::hash()(k.dilationh) << 5) ^ - (std::hash()(k.dilationw) << 6); + (std::hash()(k.dilationw) << 6) ^ + (std::hash()(k.has_bias) << 7) ^ + (std::hash()(k.bias_hash) << 8); } }; @@ -151,29 +162,21 @@ static size_t ComputeMlasWorkingBufferSize(const size_t co, } static bool CheckCapabilitiesSme(const MLAS_CONV_PARAMETERS* Parameters) { - - //functional checks - logically can the conv be performed - if ((Parameters->Dimensions != 2) || - (Parameters->BatchCount != 1) || - (Parameters->Beta != 0.f) || - (Parameters->Padding[0] != Parameters->Padding[1]) || - (Parameters->Padding[0] != Parameters->Padding[2]) || - (Parameters->Padding[0] != Parameters->Padding[3]) || - (ComputeConvOutSize(Parameters->InputShape[0], - ComputeKernelSize(Parameters->DilationShape[0],Parameters->KernelShape[0]), - Parameters->Padding[0], Parameters->StrideShape[0]) * - ComputeConvOutSize(Parameters->InputShape[1], - ComputeKernelSize(Parameters->DilationShape[1],Parameters->KernelShape[1]), - Parameters->Padding[1], Parameters->StrideShape[1]) == 0)) { - KLEIDIAI_DEBUG_LOG("CheckCapabilitiesSme returning false on functional checks."); + if (!MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + Parameters->Dimensions, + Parameters->BatchCount, + Parameters->GroupCount, + Parameters->InputShape, + Parameters->KernelShape, + Parameters->DilationShape, + Parameters->Padding, + Parameters->StrideShape, + Parameters->FilterCount, + Parameters->Beta)) { + KLEIDIAI_DEBUG_LOG("CheckCapabilitiesSme returning false on shared capability checks."); return false; } - auto N = Parameters->FilterCount; - if (N == 1 || Parameters->KernelShape[0] < 3 || Parameters->KernelShape[1] < 3) { - KLEIDIAI_DEBUG_LOG("CheckCapabilitiesSme returning false on optimization checks."); - return false; - } return true; } @@ -341,7 +344,11 @@ static std::shared_ptr RhsPackWeightsBiasSme(const size_t co, const // Cache of prepacked kai rhs weights and biases. thread_local to prevent interference from parallel sessions. thread_local std::unordered_map> rhs_cache; - RhsCacheKey key = { co, ci, kh, kw, dilationh, dilationw, HashWeights(weights) }; + // The packed RHS buffer includes both weights and bias, so both must participate in the cache key. + const size_t weights_hash = HashTensorPrefix(weights, co * ci * kh * kw); + const bool has_bias = bias != nullptr; + const size_t bias_hash = has_bias ? HashTensorPrefix(bias, co) : 0; + RhsCacheKey key = { co, ci, kh, kw, dilationh, dilationw, has_bias, weights_hash, bias_hash }; auto found = rhs_cache.find(key); if (found != rhs_cache.end()) { @@ -515,7 +522,7 @@ static std::unique_ptr LhsPackImageDataSme(const size_t ci, const s padding, sh, sw, kh, kw, 1, 1, - HashWeights(in) + HashTensorPrefix(in, ci * ih * iw) }; auto& lhs_ptrs_cache = lhs_ptrs_cache_by_pad[cur_pad_ptr]; diff --git a/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc b/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc index 5d51c855d13ba..f611c992e0f57 100644 --- a/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc +++ b/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc @@ -68,7 +68,6 @@ const std::unordered_set& GetORTLayoutSensitiveOps() { // Define a static local string array so we can refer to the elements with string_views. static const std::string layout_sensitive_contrib_ops[]{ MakeORTLayoutSensitiveOpId(kMSDomain, "FusedConv"), - MakeORTLayoutSensitiveOpId(kMSDomain, "NhwcFusedConv"), MakeORTLayoutSensitiveOpId(kMSDomain, "GridSample"), MakeORTLayoutSensitiveOpId(kMSDomain, "QLinearAveragePool"), MakeORTLayoutSensitiveOpId(kMSDomain, "QLinearGlobalAveragePool"), diff --git a/onnxruntime/core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h b/onnxruntime/core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h index 81eb9f59eeada..3e3a010728086 100644 --- a/onnxruntime/core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h +++ b/onnxruntime/core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h @@ -70,6 +70,7 @@ inline constexpr std::array kLayoutTransformationPotentiallyAddedOps = { #if !defined(DISABLE_CONTRIB_OPS) // kMSDomain ops OpIdentifierWithStringViews{kMSDomain, "DequantizeLinear", 1}, + OpIdentifierWithStringViews{kMSDomain, "NhwcFusedConv", 1}, OpIdentifierWithStringViews{kMSDomain, "NhwcMaxPool", 1}, OpIdentifierWithStringViews{kMSDomain, "QLinearConv", 1}, OpIdentifierWithStringViews{kMSDomain, "QuantizeLinear", 1}, diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 7bd2beed1ee03..f585b57285e41 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates // Licensed under the MIT License. +#include #include #include #include @@ -25,14 +26,10 @@ namespace onnxruntime { using namespace layout_transformation; #ifdef USE_KLEIDIAI -// KleidiFp32NhwcFilter ensures the compatibility of Conv/FusedConv nodes before performing data conversion -// Checks the following: -// Must have 4D Input -// Must have symmetric 2d padding (if applicable) -// No add input -// Dilation and Group sizes must be 1 -bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& graph, - onnx_transpose_optimization::api::NodeRef& node) { +// Float NHWC Conv wrappers are only safe for 4D tensors today because the runtime fallback path +// only converts between NHWC and NCHW for 2D convolutions. +bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& graph, + onnx_transpose_optimization::api::NodeRef& node) { auto& base_node = NodeFromApiNode(node); ORT_UNUSED_PARAMETER(graph); @@ -45,48 +42,11 @@ bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& grap return false; } - const auto& batch_dim = input_shape->dim(0); - if (!utils::HasDimValue(batch_dim) || batch_dim.dim_value() != 1) { - return false; - } - - const auto pads_attr = node.GetAttributeInts("pads"); - if (pads_attr.has_value()) { - const auto& pads = pads_attr.value(); - if (pads.size() != 4 || pads[0] != pads[2] || pads[1] != pads[3]) { - return false; - } - } - const auto* weight_shape = base_node.InputDefs()[1]->Shape(); if (weight_shape == nullptr || weight_shape->dim_size() != 4) { return false; } - const auto& filter_dim = weight_shape->dim(0); - const auto& kernel_h_dim = weight_shape->dim(2); - const auto& kernel_w_dim = weight_shape->dim(3); - - if (!utils::HasDimValue(filter_dim) || filter_dim.dim_value() <= 1 || - !utils::HasDimValue(kernel_h_dim) || kernel_h_dim.dim_value() < 3 || - !utils::HasDimValue(kernel_w_dim) || kernel_w_dim.dim_value() < 3) { - return false; - } - - const auto dilations_opt = node.GetAttributeInts("dilations"); - if (dilations_opt.has_value()) { - const auto& dilations = dilations_opt.value(); - if ((dilations.size() >= 1 && dilations[0] != 1) || - (dilations.size() >= 2 && dilations[1] != 1)) { - return false; - } - } - - const auto group_opt = node.GetAttributeInt("group"); - if (group_opt.has_value() && group_opt.value() != 1) { - return false; - } - return true; } #endif @@ -228,7 +188,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; const auto filter = [](const api::GraphRef& graph, api::NodeRef& node) { - return KleidiFp32NhwcFilter(graph, node); + return FloatNhwcWrapperFilter(graph, node); }; conv_table_.emplace( @@ -339,7 +299,6 @@ Status NhwcTransformer::ApplyImpl(Graph& graph, bool& modified, int graph_level, node->SetAttributeInt("channels_last", 1); } - size_t rank = shape->dim_size(); std::vector input_perm = ChannelFirstToLastPerm(rank); std::vector output_perm = ChannelLastToFirstPerm(rank); diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index d82bd4893c785..464c112fbdd07 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -244,15 +244,44 @@ Status Conv::Compute(OpKernelContext* context) const { if (channels_last_) { ORT_RETURN_IF_NOT(kernel_rank == 2, "Conv with channels_last layout currently supports 2D kernels."); - ORT_RETURN_IF_NOT(dilations[0] == 1 && dilations[1] == 1, "Conv with channels_last layout currently supports dilation == 1."); } const bool wants_channels_last = channels_last_; const bool sum_present = Sum != nullptr; + std::array input_shape_size_t{}; + std::array kernel_shape_size_t{}; + std::array dilations_size_t{}; + std::array pads_size_t{}; + std::array strides_size_t{}; + if (wants_channels_last) { + ORT_RETURN_IF_NOT(input_shape.NumDimensions() == 2, "Nhwc Conv fast-path expects 2D input shape."); + for (size_t i = 0; i < 2; ++i) { + input_shape_size_t[i] = narrow(input_shape[i]); + kernel_shape_size_t[i] = narrow(kernel_shape[i]); + dilations_size_t[i] = narrow(dilations[i]); + strides_size_t[i] = narrow(strides[i]); + pads_size_t[i] = narrow(pads[i]); + pads_size_t[i + 2] = narrow(pads[i + 2]); + } + } const bool nhwc_fastpath = - wants_channels_last && kernel_rank == 2 && conv_attrs_.group == 1 && - dilations[0] == 1 && dilations[1] == 1 && !sum_present; + wants_channels_last && !sum_present && + MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + kernel_rank, + narrow(N), + narrow(conv_attrs_.group), + input_shape_size_t.data(), + kernel_shape_size_t.data(), + dilations_size_t.data(), + pads_size_t.data(), + strides_size_t.data(), + narrow(M / conv_attrs_.group), + /*Beta*/ 0.0f); const bool manual_sum = wants_channels_last && !nhwc_fastpath && sum_present; + MLAS_ACTIVATION pre_sum_activation = activation_; + if (manual_sum) { + pre_sum_activation.ActivationKind = MlasIdentityActivation; + } std::vector sum_manual_buffer; const float* sum_manual_data = nullptr; @@ -290,7 +319,7 @@ Status Conv::Compute(OpKernelContext* context) const { strides.data(), output_shape.GetDims().data(), narrow(M / conv_attrs_.group), - &activation_, + manual_sum ? &pre_sum_activation : &activation_, &WorkingBufferSize, nhwc_fastpath, nhwc_fastpath ? 0.0f : Beta, @@ -342,15 +371,27 @@ Status Conv::Compute(OpKernelContext* context) const { if (wants_channels_last && !nhwc_fastpath) { const auto& y_dims = Y->Shape().GetDims(); ORT_RETURN_IF_NOT(y_dims.size() == 4, "Nhwc fallback expects 4D output."); - ConvertNCHWToNHWC(output_compute, - Ydata.data(), - y_dims[0], y_dims[3], y_dims[1], y_dims[2]); if (manual_sum) { - auto y_span = gsl::make_span(Ydata.data(), Ydata.size()); - for (size_t i = 0; i < y_span.size(); ++i) { - y_span[i] += sum_manual_data[i]; + const SafeInt output_elements = SafeInt(Y->Shape().Size()); + float* sum_nchw = static_cast(alloc->Alloc(sizeof(float) * output_elements)); + BufferUniquePtr sum_nchw_buffer(sum_nchw, BufferDeleter(alloc)); + ConvertNHWCToNCHW(sum_manual_data, + sum_nchw, + y_dims[0], y_dims[3], y_dims[1], y_dims[2]); + + auto output_span = gsl::make_span(output_compute, static_cast(output_elements)); + auto sum_span = gsl::make_span(sum_nchw, static_cast(output_elements)); + for (size_t i = 0; i < output_span.size(); ++i) { + output_span[i] += sum_span[i]; } + + MlasActivation(&activation_, output_compute, nullptr, narrow(M), + narrow(output_shape.Size()), narrow(output_shape.Size())); } + + ConvertNCHWToNHWC(output_compute, + Ydata.data(), + y_dims[0], y_dims[3], y_dims[1], y_dims[2]); } } else { const int64_t input_image_size = input_shape.Size(); diff --git a/onnxruntime/test/contrib_ops/fused_conv_test.cc b/onnxruntime/test/contrib_ops/fused_conv_test.cc index 9df222db43501..fa19c0266c9a0 100644 --- a/onnxruntime/test/contrib_ops/fused_conv_test.cc +++ b/onnxruntime/test/contrib_ops/fused_conv_test.cc @@ -120,6 +120,58 @@ void RunConvOp(const ConvOpAndTestAttributes& attributes, disable_cpu, disable_cuda, disable_webgpu, use_float16, weight_is_initializer); } +#ifdef USE_KLEIDIAI +void TestNhwcFusedConvFloatOp(const ConvOpAndTestAttributes& attributes, + const vector>& inputs, + const vector>& input_shapes, + const std::initializer_list& expected_output, + const vector& expected_output_shape, + bool weight_is_initializer = false) { + auto cpu_ep = DefaultCpuExecutionProvider(); + if (cpu_ep == nullptr) { + return; + } + + OpTester test("NhwcFusedConv", 1, onnxruntime::kMSDomain); + test.AddAttribute("group", attributes.group); + test.AddAttribute("kernel_shape", attributes.kernel_shape); + test.AddAttribute("activation", attributes.activation); + + if (!attributes.dilations.empty()) { + test.AddAttribute("dilations", attributes.dilations); + } + + if (!attributes.pads.empty()) { + test.AddAttribute("pads", attributes.pads); + } else { + test.AddAttribute("auto_pad", attributes.auto_pad); + } + + if (!attributes.strides.empty()) { + test.AddAttribute("strides", attributes.strides); + } + + if (!attributes.activation_parameters.empty()) { + test.AddAttribute("activation_params", attributes.activation_parameters); + } + + const char* szNames[] = {"X", "W", "B", "Z"}; + test.AddInput(szNames[0], input_shapes[0], inputs[0]); + test.AddInput(szNames[1], input_shapes[1], inputs[1], weight_is_initializer); + if (inputs.size() >= 3) { + test.AddInput(szNames[2], input_shapes[2], inputs[2]); + } + if (inputs.size() >= 4) { + test.AddInput(szNames[3], input_shapes[3], inputs[3]); + } + test.AddOutput("Y", expected_output_shape, expected_output); + + std::vector> execution_providers; + execution_providers.push_back(std::move(cpu_ep)); + test.Run(OpTester::ExpectResult::kExpectSuccess, "", {}, nullptr, &execution_providers); +} +#endif + TEST(FusedConvTest, Conv2D_HardSigmoid) { ConvOpAndTestAttributes attrs = { "", // auto_pad @@ -235,6 +287,64 @@ TEST(FusedConvTest, Cpu_Conv2D_Bias_Z_Relu) { RunConvOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape, false, true, true); } +#ifdef USE_KLEIDIAI +TEST(FusedConvTest, Cpu_NhwcConv2D_Bias_Z_Relu) { + ConvOpAndTestAttributes attrs = { + "", // auto_pad + vector{1, 1}, // dilations + 1, // group + vector{2, 2}, // kernel_shape + vector{0, 0, 0, 0}, // pads + vector{1, 1}, // strides + "Relu" // activation + }; + + vector X = {1.0f, 2.0f, 3.0f, + 4.0f, 5.0f, 6.0f, + 7.0f, 8.0f, 9.0f}; + vector X_shape = {1, 3, 3, 1}; + vector W = {1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f}; + vector W_shape = {2, 1, 2, 2}; + vector Y_shape = {1, 2, 2, 2}; + vector B = {1.0f, -1.0f}; + vector B_shape = {2}; + vector Z = {-1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + vector Z_shape = {1, 2, 2, 2}; + auto expected_vals = {12.0f, 11.0f, 17.0f, 15.0f, 25.0f, 23.0f, 29.0f, 28.0f}; + TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape); + TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape, true); +} + +TEST(FusedConvTest, Cpu_NhwcConv2D_AutoPadSameUpper) { + ConvOpAndTestAttributes attrs = { + "SAME_UPPER", // auto_pad + vector{1, 1}, // dilations + 1, // group + vector{3, 3}, // kernel_shape + {}, // pads + vector{1, 1}, // strides + "Relu" // activation + }; + + vector X(25, 1.0f); + vector X_shape = {1, 5, 5, 1}; + vector W = {0.0f, 1.0f, 2.0f, + 3.0f, 4.0f, 5.0f, + 6.0f, 7.0f, 8.0f}; + vector W_shape = {1, 1, 3, 3}; + vector Y_shape = {1, 5, 5, 1}; + auto expected_vals = {24.0f, 33.0f, 33.0f, 33.0f, 20.0f, + 27.0f, 36.0f, 36.0f, 36.0f, 21.0f, + 27.0f, 36.0f, 36.0f, 36.0f, 21.0f, + 27.0f, 36.0f, 36.0f, 36.0f, 21.0f, + 12.0f, 15.0f, 15.0f, 15.0f, 8.0f}; + TestNhwcFusedConvFloatOp(attrs, {X, W}, {X_shape, W_shape}, expected_vals, Y_shape); + TestNhwcFusedConvFloatOp(attrs, {X, W}, {X_shape, W_shape}, expected_vals, Y_shape, true); +} +#endif + #endif } // namespace test diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 84a7233325023..be3a411cca8f3 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -81,6 +81,33 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); } +#endif // defined(USE_KLEIDIAI) && !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) + +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { + KernelTypeStrResolver resolver; + ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); + + Model model("nhwc_fused_conv_layout_transform_resolver_test", false, DefaultLoggingManager().DefaultLogger()); + auto& graph = model.MainGraph(); + + ONNX_NAMESPACE::TypeProto float_tensor; + auto* tensor_type = float_tensor.mutable_tensor_type(); + tensor_type->set_elem_type(ONNX_NAMESPACE::TensorProto_DataType_FLOAT); + tensor_type->mutable_shape()->add_dim()->set_dim_value(1); + + auto& x = graph.GetOrCreateNodeArg("x", &float_tensor); + auto& w = graph.GetOrCreateNodeArg("w", &float_tensor); + auto& y = graph.GetOrCreateNodeArg("y", &float_tensor); + + auto& nhwc_fused_conv = graph.AddNode( + "nhwc_fused_conv", "NhwcFusedConv", "test node", {&x, &w}, {&y}, nullptr, kMSDomain); + nhwc_fused_conv.SetSinceVersion(1); + + gsl::span resolved_args; + ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); + ASSERT_FALSE(resolved_args.empty()); +} #endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) // run this test manually to output a hard-coded byte array. diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index f9dd1d721bf74..464c378caa3b3 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -236,8 +236,143 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 2); +#else + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); +#endif + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); +} + +TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& conv_node = builder.AddConvNode(input_arg, weight_arg, output_arg); + conv_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 2); +#else + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); +#endif + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); +} + +TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* bias_arg = builder.MakeInitializer({16}, -0.5f, 0.5f); + auto* output_arg = builder.MakeOutput(); + + Node& fused_conv_node = builder.AddNode("FusedConv", {input_arg, weight_arg, bias_arg}, {output_arg}, kMSDomain); + fused_conv_node.AddAttribute("activation", "Relu"); + fused_conv_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); + fused_conv_node.AddAttribute("strides", std::vector{1, 1}); + fused_conv_node.AddAttribute("kernel_shape", std::vector{3, 3}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 2); +#else + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); +#endif + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); +} + +TEST(NhwcTransformerTests, FusedConvWithSumFloat_SkipNhwc) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* bias_arg = builder.MakeInitializer({16}, -0.5f, 0.5f); + auto* sum_arg = builder.MakeInput({1, 16, 5, 5}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& fused_conv_node = builder.AddNode("FusedConv", {input_arg, weight_arg, bias_arg, sum_arg}, {output_arg}, kMSDomain); + fused_conv_node.AddAttribute("activation", "Relu"); + fused_conv_node.AddAttribute("pads", std::vector{0, 0, 0, 0}); + fused_conv_node.AddAttribute("strides", std::vector{1, 1}); + fused_conv_node.AddAttribute("kernel_shape", std::vector{3, 3}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 3); +#else + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); +#endif + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); +} + +TEST(NhwcTransformerTests, ConvAutoPadFloat_SkipNhwc) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 6, 6}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& conv_node = builder.AddConvNode(input_arg, weight_arg, output_arg); + conv_node.AddAttribute("auto_pad", "SAME_UPPER"); + conv_node.AddAttribute("strides", std::vector{2, 2}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 2); +#else EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); EXPECT_EQ(op_to_count["Transpose"], 0); +#endif }; TransformerTester(build_test_case, diff --git a/onnxruntime/test/optimizer/transpose_optimizer_test.cc b/onnxruntime/test/optimizer/transpose_optimizer_test.cc index b57118bbb4ba3..c79587ed8a736 100644 --- a/onnxruntime/test/optimizer/transpose_optimizer_test.cc +++ b/onnxruntime/test/optimizer/transpose_optimizer_test.cc @@ -4604,6 +4604,64 @@ TEST(TransposeOptimizerTests, QnnTransposeReshape) { } } +TEST(TransposeOptimizerTests, LayoutTransformDoesNotRetargetNhwcFusedConv) { + std::unordered_map domain_to_version{{kOnnxDomain, 13}, {kMSDomain, 1}}; + Model model("LayoutTransformDoesNotRetargetNhwcFusedConv", false, ModelMetaData(), PathString(), + IOnnxRuntimeOpSchemaRegistryList(), domain_to_version, {}, + DefaultLoggingManager().DefaultLogger()); + Graph& graph = model.MainGraph(); + ModelTestBuilder builder(graph); + + auto* input_arg = builder.MakeInput({1, 7, 7, 8}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 3, 3, 8}, -1.0f, 1.0f); + auto* bias_arg = builder.MakeInitializer({16}, -0.5f, 0.5f); + auto* output_arg = builder.MakeOutput(); + + auto& nhwc_fused_conv = builder.AddNode("NhwcFusedConv", {input_arg, weight_arg, bias_arg}, {output_arg}, kMSDomain); + nhwc_fused_conv.AddAttribute("activation", "Relu"); + nhwc_fused_conv.AddAttribute("pads", std::vector{1, 1, 1, 1}); + nhwc_fused_conv.AddAttribute("strides", std::vector{1, 1}); + nhwc_fused_conv.AddAttribute("kernel_shape", std::vector{3, 3}); + + builder.SetGraphOutputs(); + ASSERT_STATUS_OK(graph.Resolve()); + + std::string model_data; + model.ToProto().SerializeToString(&model_data); + + SessionOptions so; + using InternalTestingEP = internal_testing_ep::InternalTestingExecutionProvider; + const std::unordered_set empty_set; + auto internal_testing_ep = std::make_unique(empty_set, empty_set, DataLayout::NHWC); + internal_testing_ep->EnableStaticKernels().TakeAllNodes(); + + InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.RegisterExecutionProvider(std::move(internal_testing_ep))); + ASSERT_STATUS_OK(session.Load(model_data.data(), static_cast(model_data.size()))); + ASSERT_STATUS_OK(session.Initialize()); + + const auto& optimized_graph = session.GetGraph(); + const auto op_to_count = CountOpsInGraph(optimized_graph); + const auto get_op_count = [&op_to_count](std::string_view op_type) { + const auto it = op_to_count.find(std::string{op_type}); + return it == op_to_count.end() ? 0 : it->second; + }; + + EXPECT_EQ(get_op_count("com.microsoft.NhwcFusedConv"), 1); + EXPECT_EQ(get_op_count("Transpose"), 0); + + int nhwc_fused_conv_count = 0; + for (const auto& node : optimized_graph.Nodes()) { + if (node.OpType() == "NhwcFusedConv") { + ++nhwc_fused_conv_count; + EXPECT_EQ(node.Domain(), kMSDomain); + EXPECT_EQ(node.GetExecutionProviderType(), internal_testing_ep::kInternalTestingExecutionProvider); + } + } + + EXPECT_EQ(nhwc_fused_conv_count, 1); +} + TEST(TransposeOptimizerTests, QnnTransposeReshapeQDQ) { Status status; auto model_uri = ORT_TSTR("testdata/layout_transform_reshape.qdq.onnx"); From 601029c74774411526bea80ac9d9f8b1992452de Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 18 Mar 2026 09:59:13 +0000 Subject: [PATCH 022/140] Update onnxruntime/test/framework/ort_model_only_test.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/test/framework/ort_model_only_test.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 22f4a088a2c93..6e7c38b3c4411 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -18,7 +18,6 @@ #include "test/util/include/inference_session_wrapper.h" #include -#include #include "flatbuffers/idl.h" #include "flatbuffers/util.h" From ba355ea10155e6a3ebfb19fb0a13cead53c0a352 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 23 Mar 2026 14:05:38 +0000 Subject: [PATCH 023/140] Fixes for failing unittests: * Ensure that NCHWc Conv Kernel when fused with sum is handled correctly * Update for nhwc transofrmer tests to check the correct kernel is available Signed-off-by: Orlaith Monahan --- .../core/optimizer/nchwc_transformer.cc | 8 ++ .../test/optimizer/nhwc_transformer_test.cc | 82 +++++++++++-------- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/onnxruntime/core/optimizer/nchwc_transformer.cc b/onnxruntime/core/optimizer/nchwc_transformer.cc index 4e03450077718..729fa284bb5b4 100644 --- a/onnxruntime/core/optimizer/nchwc_transformer.cc +++ b/onnxruntime/core/optimizer/nchwc_transformer.cc @@ -325,6 +325,14 @@ void NchwcTransformerImpl::TransformConv(Node& node) { auto& input_defs = node.MutableInputDefs(); auto& output_defs = node.MutableOutputDefs(); + // The internal NCHWc Conv kernel can consume an optional fused Sum input, but it expects + // that tensor to already be in NCHWc layout. This transform only legalizes the main Conv + // input and static weights/bias, so a pre-existing FusedConv(X, W, B, Sum) would feed a + // plain NCHW tensor into the NCHWc kernel and produce incorrect results. + if (node.OpType() == "FusedConv" && input_defs.size() >= 4 && input_defs[3] != nullptr && input_defs[3]->Exists()) { + return; + } + // Require that the weights tensor be static. const ONNX_NAMESPACE::TensorProto* conv_W_tensor_proto = nullptr; if (!graph_utils::NodeArgIsConstant(graph_, *input_defs[1]) || diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 464c378caa3b3..509241fc7ec6b 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -5,10 +5,12 @@ #include #include "gtest/gtest.h" +#include "core/framework/kernel_registry.h" #include "test/unittest_util/graph_transform_test_builder.h" #include "core/mlas/inc/mlas.h" #include "core/graph/graph.h" #include "test/common/dnnl_op_test_utils.h" +#include "test/util/include/test_environment.h" namespace onnxruntime { namespace test { @@ -33,6 +35,30 @@ NodeArg* NhwcMakeInitializer(ModelTestBuilder& builder, const std::vector::max_value); } +static bool HasFloatNhwcFusedConvKernel() { + auto* cpu_ep = TestCPUExecutionProvider(); + auto kernel_registry = cpu_ep->GetKernelRegistry(); + if (!kernel_registry) { + return false; + } + + KernelRegistry::TypeConstraintMap type_constraints{ + {"T", DataTypeImpl::GetTensorType()}, + }; + + const KernelCreateInfo* kernel_create_info{}; + const auto status = kernel_registry->TryFindKernel( + kCpuExecutionProvider, + "NhwcFusedConv", + kMSDomain, + 1, + type_constraints, + DefaultLoggingManager().DefaultLogger(), + &kernel_create_info); + + return status.IsOK() && kernel_create_info != nullptr; +} + #ifndef DISABLE_CONTRIB_OPS TEST(NhwcTransformerTests, Conv) { @@ -236,13 +262,10 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 2); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, @@ -266,13 +289,10 @@ TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 2); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, @@ -300,13 +320,10 @@ TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 2); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, @@ -335,13 +352,11 @@ TEST(NhwcTransformerTests, FusedConvWithSumFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 3); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + EXPECT_EQ(op_to_count["com.microsoft.nchwc.Conv"], 0); + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 3 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, @@ -366,13 +381,10 @@ TEST(NhwcTransformerTests, ConvAutoPadFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 2); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, From f91dd759750dc95462ea53d9cebb0642afa5287a Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 23 Mar 2026 14:11:18 +0000 Subject: [PATCH 024/140] Update onnxruntime/test/optimizer/transpose_optimizer_test.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/test/optimizer/transpose_optimizer_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/optimizer/transpose_optimizer_test.cc b/onnxruntime/test/optimizer/transpose_optimizer_test.cc index c79587ed8a736..9fafe7f578954 100644 --- a/onnxruntime/test/optimizer/transpose_optimizer_test.cc +++ b/onnxruntime/test/optimizer/transpose_optimizer_test.cc @@ -4613,7 +4613,7 @@ TEST(TransposeOptimizerTests, LayoutTransformDoesNotRetargetNhwcFusedConv) { ModelTestBuilder builder(graph); auto* input_arg = builder.MakeInput({1, 7, 7, 8}, -1.0f, 1.0f); - auto* weight_arg = builder.MakeInitializer({16, 3, 3, 8}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); auto* bias_arg = builder.MakeInitializer({16}, -0.5f, 0.5f); auto* output_arg = builder.MakeOutput(); From 452a4572dbbba859d6872c1716759077b4064f2e Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 23 Mar 2026 14:32:09 +0000 Subject: [PATCH 025/140] Fix for copilot suggestion around fp16 intrinsics Signed-off-by: Orlaith Monahan --- .../core/optimizer/nhwc_transformer.cc | 2 +- .../test/optimizer/nhwc_transformer_test.cc | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index f585b57285e41..17682a79087ae 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -168,7 +168,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, false, filter}); conv_table_.emplace( OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, false, filter}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, true, filter}); } } diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 509241fc7ec6b..7c82c6189b070 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -59,6 +59,32 @@ static bool HasFloatNhwcFusedConvKernel() { return status.IsOK() && kernel_create_info != nullptr; } +#ifdef MLAS_F16VEC_INTRINSICS_SUPPORTED +static bool HasFp16NhwcFusedConvKernel() { + auto* cpu_ep = TestCPUExecutionProvider(); + auto kernel_registry = cpu_ep->GetKernelRegistry(); + if (!kernel_registry) { + return false; + } + + KernelRegistry::TypeConstraintMap type_constraints{ + {"T", DataTypeImpl::GetTensorType()}, + }; + + const KernelCreateInfo* kernel_create_info{}; + const auto status = kernel_registry->TryFindKernel( + kCpuExecutionProvider, + "NhwcFusedConv", + kMSDomain, + 1, + type_constraints, + DefaultLoggingManager().DefaultLogger(), + &kernel_create_info); + + return status.IsOK() && kernel_create_info != nullptr; +} +#endif + #ifndef DISABLE_CONTRIB_OPS TEST(NhwcTransformerTests, Conv) { @@ -770,6 +796,35 @@ TEST_F(NhwcTransformerTestsFp16, ConvFp16) { test_case({1, 22, 11, 13, 15}, {30, 22, 5, 3, 3}); } +TEST_F(NhwcTransformerTestsFp16, FusedConvWithSumFp16) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = MakeInputARangeFP16(builder, {1, 8, 7, 7}, MLFloat16(-1.0f), MLFloat16(1.0f)); + auto* weight_arg = MakeInitializerARangeFP16(builder, {16, 8, 3, 3}, MLFloat16(-1.0f), MLFloat16(1.0f)); + auto* bias_arg = MakeInitializerARangeFP16(builder, {16}, MLFloat16(-0.5f), MLFloat16(0.5f)); + auto* sum_arg = MakeInputARangeFP16(builder, {1, 16, 5, 5}, MLFloat16(-1.0f), MLFloat16(1.0f)); + auto* output_arg = builder.MakeOutput(); + + Node& fused_conv_node = builder.AddNode("FusedConv", {input_arg, weight_arg, bias_arg, sum_arg}, {output_arg}, kMSDomain); + fused_conv_node.AddAttribute("activation", "Relu"); + fused_conv_node.AddAttribute("pads", std::vector{0, 0, 0, 0}); + fused_conv_node.AddAttribute("strides", std::vector{1, 1}); + fused_conv_node.AddAttribute("kernel_shape", std::vector{3, 3}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); + const int expected_nhwc_fused_conv = HasFp16NhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFp16NhwcFusedConvKernel() ? 3 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3); +} + TEST_F(NhwcTransformerTestsFp16, ConvMaxPoolFp16) { auto test_case = [&](const std::vector& input_shape, const std::vector& weights_shape) { auto build_test_case = [&](ModelTestBuilder& builder) { From 3c5d2eebd25f8aaf663c905e0a37dd738e8af5a7 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 23 Mar 2026 15:55:31 +0000 Subject: [PATCH 026/140] Further codex fixes and added another regression test Signed-off-by: Orlaith Monahan --- onnxruntime/core/providers/cpu/nn/conv.cc | 6 ++-- .../test/contrib_ops/fused_conv_test.cc | 29 +++++++++++++++++++ .../fuse_initializers_transformer_test.cc | 2 +- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 464c112fbdd07..4a679750600fb 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -385,8 +385,10 @@ Status Conv::Compute(OpKernelContext* context) const { output_span[i] += sum_span[i]; } - MlasActivation(&activation_, output_compute, nullptr, narrow(M), - narrow(output_shape.Size()), narrow(output_shape.Size())); + const auto activation_rows = narrow(SafeInt(y_dims[0]) * y_dims[3]); + const auto activation_cols = narrow(output_shape.Size()); + MlasActivation(&activation_, output_compute, nullptr, activation_rows, + activation_cols, activation_cols); } ConvertNCHWToNHWC(output_compute, diff --git a/onnxruntime/test/contrib_ops/fused_conv_test.cc b/onnxruntime/test/contrib_ops/fused_conv_test.cc index fa19c0266c9a0..7bfacb996526f 100644 --- a/onnxruntime/test/contrib_ops/fused_conv_test.cc +++ b/onnxruntime/test/contrib_ops/fused_conv_test.cc @@ -317,6 +317,35 @@ TEST(FusedConvTest, Cpu_NhwcConv2D_Bias_Z_Relu) { TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape, true); } +TEST(FusedConvTest, Cpu_NhwcConv2D_Z_Relu_Batch2) { + ConvOpAndTestAttributes attrs = { + "", // auto_pad + vector{1, 1}, // dilations + 1, // group + vector{1, 1}, // kernel_shape + vector{0, 0, 0, 0}, // pads + vector{1, 1}, // strides + "Relu" // activation + }; + + vector X = {1.0f, 2.0f, 3.0f, 4.0f, + 1.0f, 1.0f, 1.0f, 1.0f}; + vector X_shape = {2, 2, 2, 1}; + vector W = {1.0f}; + vector W_shape = {1, 1, 1, 1}; + vector B = {0.0f}; + vector B_shape = {1}; + vector Z = {0.0f, 0.0f, 0.0f, 0.0f, + -2.0f, -3.0f, -4.0f, -5.0f}; + vector Z_shape = {2, 2, 2, 1}; + vector Y_shape = {2, 2, 2, 1}; + auto expected_vals = {1.0f, 2.0f, 3.0f, 4.0f, + 0.0f, 0.0f, 0.0f, 0.0f}; + + TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape); + TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape, true); +} + TEST(FusedConvTest, Cpu_NhwcConv2D_AutoPadSameUpper) { ConvOpAndTestAttributes attrs = { "SAME_UPPER", // auto_pad diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index 7bb492c4854d9..b238a0c849fea 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -493,7 +493,7 @@ TEST(TransformerTest, FuseFp16InitializersWithGraphOutputs) { so.graph_optimization_level = TransformerLevel::MaxLevel; // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; - // Disabling ConstantFolding optimizer as it will remove the Cast node + // Disabling ConstantFolding and NhwcTransformer optimizer as it will remove the Cast node // by folding it with Add node. This will not allow us to test the // scenario where Cast node is producing graph output and need to // kept untouched by FuseInitializersTransformer. From a799cac675e0982b609b80efb3c49b0b20a44fff Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 24 Mar 2026 09:54:50 +0000 Subject: [PATCH 027/140] Update the nhwc transformer tests to check according to supported hardware instead of assuming based on compile time definitions Fix for co-pilot suggestion in kernel_type_str_resolver_utils_test.cc Signed-off-by: Orlaith Monahan --- .../kernel_type_str_resolver_utils_test.cc | 5 ++- .../test/optimizer/nhwc_transformer_test.cc | 31 +++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index be3a411cca8f3..6a6c9ea084b77 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -23,7 +23,10 @@ static Status LoadLayoutTransformationRequiredOpsFromOpSchemas(KernelTypeStrReso for (const auto& op_id : required_op_ids) { const auto* op_schema = schema_registry.GetSchema(std::string{op_id.op_type}, op_id.since_version, std::string{op_id.domain}); - ORT_RETURN_IF(op_schema == nullptr, "Failed to get op schema."); + ORT_RETURN_IF(op_schema == nullptr, + "Failed to get op schema for domain='", op_id.domain, + "', op_type='", op_id.op_type, + "', since_version=", op_id.since_version, "."); ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); } return Status::OK(); diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 7c82c6189b070..223fe336d144f 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -8,6 +8,9 @@ #include "core/framework/kernel_registry.h" #include "test/unittest_util/graph_transform_test_builder.h" #include "core/mlas/inc/mlas.h" +#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#include "core/mlas/lib/mlasi.h" +#endif #include "core/graph/graph.h" #include "test/common/dnnl_op_test_utils.h" #include "test/util/include/test_environment.h" @@ -59,6 +62,14 @@ static bool HasFloatNhwcFusedConvKernel() { return status.IsOK() && kernel_create_info != nullptr; } +static bool HasFloatNhwcRuntimeSupport() { +#if defined(USE_KLEIDIAI) && defined(__aarch64__) + return HasFloatNhwcFusedConvKernel() && MLAS_CPUIDINFO::GetCPUIDInfo().HasArm_SME(); +#else + return false; +#endif +} + #ifdef MLAS_F16VEC_INTRINSICS_SUPPORTED static bool HasFp16NhwcFusedConvKernel() { auto* cpu_ep = TestCPUExecutionProvider(); @@ -288,8 +299,8 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -315,8 +326,8 @@ TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -346,8 +357,8 @@ TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -379,8 +390,8 @@ TEST(NhwcTransformerTests, FusedConvWithSumFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); EXPECT_EQ(op_to_count["com.microsoft.nchwc.Conv"], 0); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 3 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 3 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -407,8 +418,8 @@ TEST(NhwcTransformerTests, ConvAutoPadFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; From 7970ee062359a3bd306414994155b1296d2ed916 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 24 Mar 2026 11:06:46 +0000 Subject: [PATCH 028/140] Add further checks to ensure mlas paths for convolution are correct for non SME hardware Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/lib/convolve.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index abfa3f2c73412..9e932b730e8bb 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1276,6 +1276,16 @@ MlasConvSupportsSymmetricChannelsLast2DFloatKernel( size_t FilterCount, float Beta) { +#if !defined(USE_KLEIDIAI) || !defined(__aarch64__) + return false; +#else + // Channels-last float convolution is only implemented by the KleidiAI + // override. The generic MLAS convolution path assumes NCHW layout. + if (GetMlasPlatform().MlasConvPrepareOverride == nullptr || + GetMlasPlatform().MlasConvOverride == nullptr) { + return false; + } + if (Dimensions != 2 || BatchCount != 1 || GroupCount != 1 || Beta != 0.0f) { return false; } @@ -1299,6 +1309,7 @@ MlasConvSupportsSymmetricChannelsLast2DFloatKernel( } return true; +#endif } void From 107661c6648e110e6e391458eddb9e33466a6c88 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 24 Mar 2026 17:55:17 +0000 Subject: [PATCH 029/140] Guard the KleidiAi specific functions in android to fix -wunused errors Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/lib/convolve.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index 9e932b730e8bb..b0f9691e8eda0 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1248,6 +1248,7 @@ Return Value: namespace { +#if defined(USE_KLEIDIAI) && defined(__aarch64__) static constexpr size_t ComputeChannelsLastDilatedKernelSize(size_t dilation, size_t kernel) { return (dilation * kernel) - (dilation - 1); } @@ -1259,6 +1260,7 @@ static constexpr size_t ComputeChannelsLastConvOutSize(size_t input, size_t kern return 0; } +#endif } // namespace From e6b52b6ebcc48e14f433253cde5cbe55544a2c88 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 10:14:28 +0000 Subject: [PATCH 030/140] Further guards and checks for nwhctransformer tests on x86 Compile time checks for android builds Signed-off-by: Orlaith Monahan --- onnxruntime/core/optimizer/nhwc_transformer.cc | 9 +++++++++ onnxruntime/test/optimizer/nhwc_transformer_test.cc | 2 ++ 2 files changed, 11 insertions(+) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 17682a79087ae..4f16b408c16f1 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -6,6 +6,7 @@ #include #include #include +#include "core/common/cpuid_info.h" #include "core/graph/constants.h" #include "core/mlas/inc/mlas.h" #include "core/graph/graph_utils.h" @@ -33,6 +34,13 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr auto& base_node = NodeFromApiNode(node); ORT_UNUSED_PARAMETER(graph); +#if !defined(__aarch64__) + return false; +#else + if (!CPUIDInfo::GetCPUIDInfo().HasArm_SME()) { + return false; + } + if (base_node.InputDefs().size() < 2) { return false; } @@ -48,6 +56,7 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr } return true; +#endif } #endif diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 223fe336d144f..163488c8db4e9 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -38,6 +38,7 @@ NodeArg* NhwcMakeInitializer(ModelTestBuilder& builder, const std::vector::max_value); } +#if defined(USE_KLEIDIAI) && defined(__aarch64__) static bool HasFloatNhwcFusedConvKernel() { auto* cpu_ep = TestCPUExecutionProvider(); auto kernel_registry = cpu_ep->GetKernelRegistry(); @@ -61,6 +62,7 @@ static bool HasFloatNhwcFusedConvKernel() { return status.IsOK() && kernel_create_info != nullptr; } +#endif static bool HasFloatNhwcRuntimeSupport() { #if defined(USE_KLEIDIAI) && defined(__aarch64__) From 4c4d1689912fa06a07c30e6ee98a2fc57c171ec2 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 10:30:09 +0000 Subject: [PATCH 031/140] Add a check for unused params when USE_KLEIDIAI is off to convolve.cpp Add an assertion around nchwc optimzer tests for situations where they don't apply Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/lib/convolve.cpp | 10 ++++++++++ onnxruntime/test/optimizer/nchwc_optimizer_test.cc | 1 + 2 files changed, 11 insertions(+) diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index b0f9691e8eda0..090886f29d01e 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1279,6 +1279,16 @@ MlasConvSupportsSymmetricChannelsLast2DFloatKernel( float Beta) { #if !defined(USE_KLEIDIAI) || !defined(__aarch64__) + MLAS_UNREFERENCED_PARAMETER(Dimensions); + MLAS_UNREFERENCED_PARAMETER(BatchCount); + MLAS_UNREFERENCED_PARAMETER(GroupCount); + MLAS_UNREFERENCED_PARAMETER(InputShape); + MLAS_UNREFERENCED_PARAMETER(KernelShape); + MLAS_UNREFERENCED_PARAMETER(DilationShape); + MLAS_UNREFERENCED_PARAMETER(Padding); + MLAS_UNREFERENCED_PARAMETER(StrideShape); + MLAS_UNREFERENCED_PARAMETER(FilterCount); + MLAS_UNREFERENCED_PARAMETER(Beta); return false; #else // Channels-last float convolution is only implemented by the KleidiAI diff --git a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc index cd210f7bc70ba..46312834aec9e 100644 --- a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc +++ b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc @@ -202,6 +202,7 @@ void NchwcOptimizerTester(const std::function& bu session_options.session_logid = "NchwcOptimizerTests"; InferenceSessionWrapper session{session_options, GetEnvironment()}; ASSERT_STATUS_OK(session.Load(model_data.data(), static_cast(model_data.size()))); + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Initialize()); RunOptions run_options; From fa098db7492c58531910958908886f34ec301e62 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 13:42:48 +0000 Subject: [PATCH 032/140] Co-pilot fixes Macos build specific tweaks Signed-off-by: Orlaith Monahan --- cmake/onnxruntime_unittests.cmake | 17 +++++--- onnxruntime/core/providers/cpu/nn/conv.cc | 43 +++++++++++-------- .../internal_testing_tests.cc | 3 +- .../test/mlas/bench/bench_transcendental.cpp | 2 + 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index 8137f8b3a2529..f18610b2ba16d 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -50,6 +50,15 @@ function(filter_test_srcs test_srcs_var) endfunction() set(disabled_warnings) + +function(onnxruntime_disable_gtest_character_conversion_as_error target_name) + if (APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(${target_name} PRIVATE "$<$:-Wno-error=character-conversion>") + elseif (HAS_CHARACTER_CONVERSION) + target_compile_options(${target_name} PRIVATE "$<$:-Wno-error=character-conversion>") + endif() +endfunction() + function(AddTest) cmake_parse_arguments(_UT "DYN" "TARGET" "LIBS;SOURCES;DEPENDS;TEST_ARGS" ${ARGN}) list(REMOVE_DUPLICATES _UT_SOURCES) @@ -170,9 +179,7 @@ function(AddTest) if (${HAS_NOERROR}) target_compile_options(${_UT_TARGET} PRIVATE "$<$:-Wno-error=uninitialized>") endif() - if (${HAS_CHARACTER_CONVERSION}) - target_compile_options(${_UT_TARGET} PRIVATE "$<$:-Wno-error=character-conversion>") - endif() + onnxruntime_disable_gtest_character_conversion_as_error(${_UT_TARGET}) endif() set(TEST_ARGS ${_UT_TEST_ARGS}) @@ -829,9 +836,7 @@ if(MSVC) "$<$>:/wd6326>") else() target_include_directories(onnxruntime_test_utils PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${ONNXRUNTIME_ROOT}) - if (HAS_CHARACTER_CONVERSION) - target_compile_options(onnxruntime_test_utils PRIVATE "$<$:-Wno-error=character-conversion>") - endif() + onnxruntime_disable_gtest_character_conversion_as_error(onnxruntime_test_utils) endif() if (onnxruntime_USE_NCCL) target_include_directories(onnxruntime_test_utils PRIVATE ${NCCL_INCLUDE_DIRS}) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 4a679750600fb..0389e95eee8e8 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -31,15 +31,19 @@ namespace { template void ConvertNHWCToNCHW(const T* src, T* dst, int64_t n, int64_t c, int64_t h, int64_t w) { - const int64_t hw = (SafeInt(h) * w); - for (int64_t n_idx = 0; n_idx < n; ++n_idx) { - const int64_t n_src_offset = n_idx * hw * c; - const int64_t n_dst_offset = n_idx * c * hw; - for (int64_t c_idx = 0; c_idx < c; ++c_idx) { + const size_t n_count = narrow(n); + const size_t c_count = narrow(c); + const size_t hw = narrow(SafeInt(h) * w); + for (size_t n_idx = 0; n_idx < n_count; ++n_idx) { + const size_t n_src_offset = SafeInt(SafeInt(n_idx) * hw) * c_count; + const size_t n_dst_offset = SafeInt(SafeInt(n_idx) * c_count) * hw; + for (size_t c_idx = 0; c_idx < c_count; ++c_idx) { + const size_t c_dst_offset = SafeInt(c_idx) * hw; const T* src_ptr = src + n_src_offset + c_idx; - T* dst_ptr = dst + n_dst_offset + c_idx * hw; - for (int64_t hw_idx = 0; hw_idx < hw; ++hw_idx) { - dst_ptr[hw_idx] = src_ptr[hw_idx * c]; + T* dst_ptr = dst + n_dst_offset + c_dst_offset; + for (size_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + const size_t src_hw_offset = SafeInt(hw_idx) * c_count; + dst_ptr[hw_idx] = src_ptr[src_hw_offset]; } } } @@ -48,15 +52,19 @@ void ConvertNHWCToNCHW(const T* src, T* dst, template void ConvertNCHWToNHWC(const T* src, T* dst, int64_t n, int64_t c, int64_t h, int64_t w) { - const int64_t hw = (SafeInt(h) * w); - for (int64_t n_idx = 0; n_idx < n; ++n_idx) { - const int64_t n_src_offset = n_idx * c * hw; - const int64_t n_dst_offset = n_idx * hw * c; - for (int64_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + const size_t n_count = narrow(n); + const size_t c_count = narrow(c); + const size_t hw = narrow(SafeInt(h) * w); + for (size_t n_idx = 0; n_idx < n_count; ++n_idx) { + const size_t n_src_offset = SafeInt(SafeInt(n_idx) * c_count) * hw; + const size_t n_dst_offset = SafeInt(SafeInt(n_idx) * hw) * c_count; + for (size_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + const size_t hw_dst_offset = SafeInt(hw_idx) * c_count; const T* src_ptr = src + n_src_offset + hw_idx; - T* dst_ptr = dst + n_dst_offset + hw_idx * c; - for (int64_t c_idx = 0; c_idx < c; ++c_idx) { - dst_ptr[c_idx] = src_ptr[c_idx * hw]; + T* dst_ptr = dst + n_dst_offset + hw_dst_offset; + for (size_t c_idx = 0; c_idx < c_count; ++c_idx) { + const size_t src_c_offset = SafeInt(c_idx) * hw; + dst_ptr[c_idx] = src_ptr[src_c_offset]; } } } @@ -291,7 +299,8 @@ Status Conv::Compute(OpKernelContext* context) const { const auto& sum_shape = Sum->Shape(); ORT_RETURN_IF_NOT(Y->Shape() == sum_shape, "output and sum shape must match"); if (manual_sum) { - sum_manual_buffer.assign(Sum->Data(), Sum->Data() + Y->Shape().Size()); + auto sum_span = Sum->DataAsSpan(); + sum_manual_buffer.assign(sum_span.begin(), sum_span.end()); sum_manual_data = sum_manual_buffer.data(); } else { auto sum_span = Sum->DataAsSpan(); diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index 4cb7cf397d95d..5850afd2f84e8 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -44,7 +44,8 @@ std::filesystem::path ResolveInternalTestPath(const std::filesystem::path& path) } std::filesystem::path candidate = std::filesystem::current_path() / path; - if (std::filesystem::exists(candidate)) { + std::error_code ec; + if (std::filesystem::exists(candidate, ec)) { return candidate; } diff --git a/onnxruntime/test/mlas/bench/bench_transcendental.cpp b/onnxruntime/test/mlas/bench/bench_transcendental.cpp index f7e461c29843a..3d42c0f84e6cc 100644 --- a/onnxruntime/test/mlas/bench/bench_transcendental.cpp +++ b/onnxruntime/test/mlas/bench/bench_transcendental.cpp @@ -19,7 +19,9 @@ constexpr float kSiluMaxValue = 20.0f; constexpr float kGeluMinValue = -10.0f; constexpr float kGeluMaxValue = 10.0f; constexpr float kInvSqrt2 = 0.7071067811865475244f; +#if defined(MLAS_TARGET_AMD64) constexpr int64_t kFusedBytesPerElement = 2; +#endif constexpr int64_t kSiluUnfusedBytesPerElement = 5; constexpr int64_t kGeluUnfusedBytesPerElement = 7; From 80a80c135f1519cf7d16a589a5b952fdc8035e0c Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 14:47:28 +0000 Subject: [PATCH 033/140] Tighten up nhwc params prune uneeded changes Signed-off-by: Orlaith Monahan --- .../core/optimizer/nhwc_transformer.cc | 178 +++++++++++++++++- .../kernel_type_str_resolver_utils_test.cc | 5 +- .../test/optimizer/nhwc_transformer_test.cc | 151 +++++++++++++-- 3 files changed, 314 insertions(+), 20 deletions(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 4f16b408c16f1..c0e1e34bd8580 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -16,6 +16,7 @@ #include "core/optimizer/layout_transformation/layout_transformation.h" #include "core/optimizer/transpose_optimization/ort_optimizer_utils.h" #include "core/optimizer/transpose_optimization/ort_transpose_optimization.h" +#include "core/providers/common.h" using namespace ONNX_NAMESPACE; using namespace ::onnxruntime::common; @@ -27,8 +28,128 @@ namespace onnxruntime { using namespace layout_transformation; #ifdef USE_KLEIDIAI -// Float NHWC Conv wrappers are only safe for 4D tensors today because the runtime fallback path -// only converts between NHWC and NCHW for 2D convolutions. +bool TryGetDimValueAsSizeT(const ONNX_NAMESPACE::TensorShapeProto& shape, int index, size_t& value) { + if (shape.dim_size() <= index || !shape.dim(index).has_dim_value()) { + return false; + } + + const int64_t dim_value = shape.dim(index).dim_value(); + if (dim_value < 0) { + return false; + } + + value = narrow(dim_value); + return true; +} + +bool TryReadPositiveOrZeroInts(const std::vector& values, std::array& out) { + if (values.size() != out.size()) { + return false; + } + + for (size_t i = 0; i < out.size(); ++i) { + if (values[i] < 0) { + return false; + } + + out[i] = narrow(values[i]); + } + + return true; +} + +bool TryReadPositiveOrZeroInts(const std::vector& values, std::array& out) { + if (values.size() != out.size()) { + return false; + } + + for (size_t i = 0; i < out.size(); ++i) { + if (values[i] < 0) { + return false; + } + + out[i] = narrow(values[i]); + } + + return true; +} + +bool TryParseAutoPadType(std::string_view value, AutoPadType& auto_pad_type) { + if (value.empty() || value == "NOTSET") { + auto_pad_type = AutoPadType::NOTSET; + return true; + } + + if (value == "VALID") { + auto_pad_type = AutoPadType::VALID; + return true; + } + + if (value == "SAME_UPPER") { + auto_pad_type = AutoPadType::SAME_UPPER; + return true; + } + + if (value == "SAME_LOWER") { + auto_pad_type = AutoPadType::SAME_LOWER; + return true; + } + + return false; +} + +bool TryComputeFloatNhwcPads(const api::NodeRef& node, + const std::array& input_shape, + const std::array& kernel_shape, + const std::array& strides, + const std::array& dilations, + std::array& pads) { + const auto auto_pad_value = node.GetAttributeString("auto_pad"); + AutoPadType auto_pad = AutoPadType::NOTSET; + if (!TryParseAutoPadType(auto_pad_value.value_or("NOTSET"), auto_pad)) { + return false; + } + + if (auto_pad == AutoPadType::NOTSET) { + const auto pads_opt = node.GetAttributeInts("pads"); + if (!pads_opt.has_value()) { + pads.fill(0); + return true; + } + + return TryReadPositiveOrZeroInts(*pads_opt, pads); + } + + std::array pads_int64{}; + for (size_t i = 0; i < 2; ++i) { + int64_t pad_head = 0; + int64_t pad_tail = 0; + int64_t out_dim = 0; + const auto status = ComputePadAndOutputShape( + narrow(input_shape[i]), + narrow(strides[i]), + narrow(kernel_shape[i]), + narrow(dilations[i]), + auto_pad, + pad_head, + pad_tail, + out_dim, + /*force_symmetric_auto_padding*/ false); + if (!status.IsOK() || pad_head < 0 || pad_tail < 0 || out_dim < 0) { + return false; + } + + pads_int64[i] = pad_head; + pads_int64[i + 2] = pad_tail; + } + + for (size_t i = 0; i < pads.size(); ++i) { + pads[i] = narrow(pads_int64[i]); + } + + return true; +} + bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& graph, onnx_transpose_optimization::api::NodeRef& node) { auto& base_node = NodeFromApiNode(node); @@ -55,7 +176,58 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr return false; } - return true; + const auto inputs = node.Inputs(); + if (base_node.OpType() == "FusedConv" && inputs.size() > 3 && !inputs[3].empty()) { + return false; + } + + const auto group = node.GetAttributeInt("group").value_or(1); + if (group != 1) { + return false; + } + + std::array input_spatial_shape{}; + std::array kernel_spatial_shape{}; + std::array dilations{1, 1}; + std::array strides{1, 1}; + std::array pads{}; + size_t batch_count = 0; + size_t filter_count = 0; + + if (!TryGetDimValueAsSizeT(*input_shape, 0, batch_count) || + !TryGetDimValueAsSizeT(*input_shape, 2, input_spatial_shape[0]) || + !TryGetDimValueAsSizeT(*input_shape, 3, input_spatial_shape[1]) || + !TryGetDimValueAsSizeT(*weight_shape, 0, filter_count) || + !TryGetDimValueAsSizeT(*weight_shape, 2, kernel_spatial_shape[0]) || + !TryGetDimValueAsSizeT(*weight_shape, 3, kernel_spatial_shape[1])) { + return false; + } + + const auto dilations_opt = node.GetAttributeInts("dilations"); + if (dilations_opt.has_value() && !TryReadPositiveOrZeroInts(*dilations_opt, dilations)) { + return false; + } + + const auto strides_opt = node.GetAttributeInts("strides"); + if (strides_opt.has_value() && !TryReadPositiveOrZeroInts(*strides_opt, strides)) { + return false; + } + + if (!TryComputeFloatNhwcPads(node, input_spatial_shape, kernel_spatial_shape, strides, dilations, pads)) { + return false; + } + + return MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + /*Dimensions*/ 2, + batch_count, + /*GroupCount*/ 1, + input_spatial_shape.data(), + kernel_spatial_shape.data(), + dilations.data(), + pads.data(), + strides.data(), + filter_count, + /*Beta*/ 0.0f); #endif } #endif diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 6a6c9ea084b77..09a49fb309d18 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -55,7 +55,7 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } -#if defined(USE_KLEIDIAI) && !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { SchemaRegistryManager schema_registry; const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); @@ -84,9 +84,6 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); } -#endif // defined(USE_KLEIDIAI) && !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) - -#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { KernelTypeStrResolver resolver; ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 163488c8db4e9..8f97febaaae78 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -1,13 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#include #include +#include #include #include "gtest/gtest.h" #include "core/framework/kernel_registry.h" -#include "test/unittest_util/graph_transform_test_builder.h" #include "core/mlas/inc/mlas.h" +#include "core/providers/common.h" +#include "test/unittest_util/graph_transform_test_builder.h" #if defined(USE_KLEIDIAI) && defined(__aarch64__) #include "core/mlas/lib/mlasi.h" #endif @@ -64,10 +67,124 @@ static bool HasFloatNhwcFusedConvKernel() { } #endif -static bool HasFloatNhwcRuntimeSupport() { +static bool HasFloatNhwcNoTransposeSupport(const std::vector& input_shape, + const std::vector& weight_shape, + std::vector pads = {}, + std::vector strides = {}, + std::vector dilations = {}, + int64_t group = 1, + bool has_sum_input = false, + std::string_view auto_pad = "NOTSET") { #if defined(USE_KLEIDIAI) && defined(__aarch64__) - return HasFloatNhwcFusedConvKernel() && MLAS_CPUIDINFO::GetCPUIDInfo().HasArm_SME(); + if (!HasFloatNhwcFusedConvKernel() || !MLAS_CPUIDINFO::GetCPUIDInfo().HasArm_SME()) { + return false; + } + + if (has_sum_input || group != 1 || input_shape.size() != 4 || weight_shape.size() != 4) { + return false; + } + + std::array input_spatial_shape{ + narrow(input_shape[2]), + narrow(input_shape[3]), + }; + std::array kernel_spatial_shape{ + narrow(weight_shape[2]), + narrow(weight_shape[3]), + }; + std::array strides_size_t{1, 1}; + std::array dilations_size_t{1, 1}; + std::array pads_size_t{}; + + if (!strides.empty()) { + if (strides.size() != strides_size_t.size()) { + return false; + } + + for (size_t i = 0; i < strides_size_t.size(); ++i) { + if (strides[i] < 0) { + return false; + } + + strides_size_t[i] = narrow(strides[i]); + } + } + + if (!dilations.empty()) { + if (dilations.size() != dilations_size_t.size()) { + return false; + } + + for (size_t i = 0; i < dilations_size_t.size(); ++i) { + if (dilations[i] < 0) { + return false; + } + + dilations_size_t[i] = narrow(dilations[i]); + } + } + + const AutoPadType auto_pad_type = StringToAutoPadType(std::string(auto_pad)); + if (auto_pad_type == AutoPadType::NOTSET) { + if (pads.empty()) { + pads_size_t.fill(0); + } else { + if (pads.size() != pads_size_t.size()) { + return false; + } + + for (size_t i = 0; i < pads_size_t.size(); ++i) { + if (pads[i] < 0) { + return false; + } + + pads_size_t[i] = narrow(pads[i]); + } + } + } else { + for (size_t i = 0; i < 2; ++i) { + int64_t pad_head = 0; + int64_t pad_tail = 0; + int64_t out_dim = 0; + const auto status = ComputePadAndOutputShape( + input_shape[2 + i], + narrow(strides_size_t[i]), + weight_shape[2 + i], + narrow(dilations_size_t[i]), + auto_pad_type, + pad_head, + pad_tail, + out_dim, + /*force_symmetric_auto_padding*/ false); + if (!status.IsOK() || pad_head < 0 || pad_tail < 0 || out_dim < 0) { + return false; + } + + pads_size_t[i] = narrow(pad_head); + pads_size_t[i + 2] = narrow(pad_tail); + } + } + + return MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + /*Dimensions*/ 2, + narrow(input_shape[0]), + /*GroupCount*/ 1, + input_spatial_shape.data(), + kernel_spatial_shape.data(), + dilations_size_t.data(), + pads_size_t.data(), + strides_size_t.data(), + narrow(weight_shape[0]), + /*Beta*/ 0.0f); #else + ORT_UNUSED_PARAMETER(input_shape); + ORT_UNUSED_PARAMETER(weight_shape); + ORT_UNUSED_PARAMETER(pads); + ORT_UNUSED_PARAMETER(strides); + ORT_UNUSED_PARAMETER(dilations); + ORT_UNUSED_PARAMETER(group); + ORT_UNUSED_PARAMETER(has_sum_input); + ORT_UNUSED_PARAMETER(auto_pad); return false; #endif } @@ -301,8 +418,9 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport({1, 8, 7, 7}, {8, 1, 3, 3}, {}, {}, {}, 8); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -328,8 +446,9 @@ TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport({1, 8, 7, 7}, {16, 8, 3, 3}, {1, 1, 1, 1}); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -359,8 +478,10 @@ TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport( + {1, 8, 7, 7}, {16, 8, 3, 3}, {1, 1, 1, 1}, {1, 1}); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -392,8 +513,10 @@ TEST(NhwcTransformerTests, FusedConvWithSumFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); EXPECT_EQ(op_to_count["com.microsoft.nchwc.Conv"], 0); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 3 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport( + {1, 8, 7, 7}, {16, 8, 3, 3}, {0, 0, 0, 0}, {1, 1}, {}, 1, true); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 3 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -420,8 +543,10 @@ TEST(NhwcTransformerTests, ConvAutoPadFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport( + {1, 8, 6, 6}, {16, 8, 3, 3}, {}, {2, 2}, {}, 1, false, "SAME_UPPER"); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; From a00dcc40fbdc46fddbba75af619f45c062c82384 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 15:17:53 +0000 Subject: [PATCH 034/140] One more test guard Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 09a49fb309d18..e9cd79f53870d 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -55,7 +55,7 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } -#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { SchemaRegistryManager schema_registry; const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); @@ -84,6 +84,7 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); } + TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { KernelTypeStrResolver resolver; ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); @@ -108,7 +109,7 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); } -#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in From ac364006471344f2e272885bb7c61b4fb252cf73 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 16:42:02 +0000 Subject: [PATCH 035/140] Small tweak to apple clang flags for macos builds Signed-off-by: Orlaith Monahan --- cmake/CMakeLists.txt | 1 + cmake/onnxruntime_unittests.cmake | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 385342479913a..4fd6ad72f00a8 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -635,6 +635,7 @@ else() check_cxx_compiler_flag(-Wcatch-value HAS_CATCH_VALUE) check_cxx_compiler_flag(-Wclass-memaccess HAS_CLASS_MEMACCESS) check_cxx_compiler_flag(-Wcharacter-conversion HAS_CHARACTER_CONVERSION) + check_cxx_compiler_flag(-Wno-error=character-conversion HAS_NO_ERROR_CHARACTER_CONVERSION) check_cxx_compiler_flag(-Wdangling-reference HAS_DANGLING_REFERENCE) check_cxx_compiler_flag(-Wdeprecated-anon-enum-enum-conversion HAS_DEPRECATED_ANON_ENUM_ENUM_CONVERSION) check_cxx_compiler_flag(-Wdeprecated-builtins HAS_DEPRECATED_BUILTINS) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index f18610b2ba16d..5d7858cc9a5d5 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -52,9 +52,7 @@ endfunction() set(disabled_warnings) function(onnxruntime_disable_gtest_character_conversion_as_error target_name) - if (APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") - target_compile_options(${target_name} PRIVATE "$<$:-Wno-error=character-conversion>") - elseif (HAS_CHARACTER_CONVERSION) + if (HAS_NO_ERROR_CHARACTER_CONVERSION) target_compile_options(${target_name} PRIVATE "$<$:-Wno-error=character-conversion>") endif() endfunction() From db137fcdc689c3c8234b6fbc9eae8215f8d78391 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 7 Apr 2026 16:04:40 +0100 Subject: [PATCH 036/140] Updates to use MLAS_TARGET_ARM64 instead of __aarch64__ Tweak to be future proofed around nhwc in cases of non-4d tensors Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/lib/convolve.cpp | 4 ++-- onnxruntime/core/optimizer/nhwc_transformer.cc | 2 +- onnxruntime/core/providers/cpu/nn/conv.cc | 3 ++- onnxruntime/test/optimizer/nhwc_transformer_test.cc | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index 090886f29d01e..7542db140728c 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1248,7 +1248,7 @@ Return Value: namespace { -#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) static constexpr size_t ComputeChannelsLastDilatedKernelSize(size_t dilation, size_t kernel) { return (dilation * kernel) - (dilation - 1); } @@ -1278,7 +1278,7 @@ MlasConvSupportsSymmetricChannelsLast2DFloatKernel( size_t FilterCount, float Beta) { -#if !defined(USE_KLEIDIAI) || !defined(__aarch64__) +#if !defined(USE_KLEIDIAI) || !defined(MLAS_TARGET_ARM64) MLAS_UNREFERENCED_PARAMETER(Dimensions); MLAS_UNREFERENCED_PARAMETER(BatchCount); MLAS_UNREFERENCED_PARAMETER(GroupCount); diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index c0e1e34bd8580..4bd7b81c6e211 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -155,7 +155,7 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr auto& base_node = NodeFromApiNode(node); ORT_UNUSED_PARAMETER(graph); -#if !defined(__aarch64__) +#if !defined(MLAS_TARGET_ARM64) return false; #else if (!CPUIDInfo::GetCPUIDInfo().HasArm_SME()) { diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 0389e95eee8e8..4968afe46106b 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -209,7 +209,8 @@ Status Conv::Compute(OpKernelContext* context) const { const Tensor* Sum = num_inputs >= 4 ? context->Input(3) : nullptr; ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X->Shape(), W->Shape(), channels_last_)); const int64_t N = X->Shape()[0]; - const int64_t C = X->Shape()[channels_last_ ? 3 : 1]; + // If channels_last_ we should get the back dim for channels instead of [1] + const int64_t C = channels_last_ ? X->Shape().GetDims().back() : X->Shape()[1]; const int64_t M = W->Shape()[0]; TensorShapeVector kernel_shape; diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 8f97febaaae78..a24a4f492d2cd 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -11,7 +11,7 @@ #include "core/mlas/inc/mlas.h" #include "core/providers/common.h" #include "test/unittest_util/graph_transform_test_builder.h" -#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) #include "core/mlas/lib/mlasi.h" #endif #include "core/graph/graph.h" @@ -41,7 +41,7 @@ NodeArg* NhwcMakeInitializer(ModelTestBuilder& builder, const std::vector::max_value); } -#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) static bool HasFloatNhwcFusedConvKernel() { auto* cpu_ep = TestCPUExecutionProvider(); auto kernel_registry = cpu_ep->GetKernelRegistry(); @@ -75,7 +75,7 @@ static bool HasFloatNhwcNoTransposeSupport(const std::vector& input_sha int64_t group = 1, bool has_sum_input = false, std::string_view auto_pad = "NOTSET") { -#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) if (!HasFloatNhwcFusedConvKernel() || !MLAS_CPUIDINFO::GetCPUIDInfo().HasArm_SME()) { return false; } From 69ce23e7b5ae3cefbe0d10a4841c22be0495ad78 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 7 Apr 2026 16:21:11 +0100 Subject: [PATCH 037/140] Tweak to update conv tests to comply with new function decl Signed-off-by: Orlaith Monahan --- onnxruntime/test/mlas/unittest/test_conv2d.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/mlas/unittest/test_conv2d.h b/onnxruntime/test/mlas/unittest/test_conv2d.h index 41d1c2f28d5d7..6ac47c69ae0b8 100644 --- a/onnxruntime/test/mlas/unittest/test_conv2d.h +++ b/onnxruntime/test/mlas/unittest/test_conv2d.h @@ -71,7 +71,6 @@ class MlasConv2DTest : public MlasTestBase { &Activation, &WorkingBufferSize, false, - 0.0f, Beta, threadpool_); @@ -368,6 +367,7 @@ class MlasConv2DTest : public MlasTestBase { FilterCount, &Activation, &WorkingBufferSize, + false, 0.0f, threadpool_); From 034284154059e80533f7343e9994cfbe591ce90d Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 21 Apr 2026 10:47:24 +0100 Subject: [PATCH 038/140] Update NHWC implementation to honour use_kleidiai flag * Co-Pilot fixes Signed-off-by: Orlaith Monahan --- .../core/optimizer/graph_transformer_utils.cc | 4 +- .../core/optimizer/nhwc_transformer.cc | 13 ++++++- onnxruntime/core/optimizer/nhwc_transformer.h | 3 +- .../test/optimizer/nhwc_transformer_test.cc | 37 +++++++++++++++++++ 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/onnxruntime/core/optimizer/graph_transformer_utils.cc b/onnxruntime/core/optimizer/graph_transformer_utils.cc index 9ed1d5e9e84fa..83e2f7563db3d 100644 --- a/onnxruntime/core/optimizer/graph_transformer_utils.cc +++ b/onnxruntime/core/optimizer/graph_transformer_utils.cc @@ -453,7 +453,7 @@ InlinedVector> GenerateTransformers( auto cpu_registry = cpu_execution_provider.GetKernelRegistry(); auto nhwc_transformer = std::make_unique(std::move(cpu_allocator), std::move(cpu_registry), - logger); + logger, session_options.config_options); if (nhwc_transformer->IsActive()) { transformers.emplace_back(std::move(nhwc_transformer)); } @@ -556,7 +556,7 @@ InlinedVector> GenerateTransformersForMinimalB AllocatorPtr cpu_allocator = CPUAllocator::DefaultInstance(); auto cpu_registry = cpu_execution_provider.GetKernelRegistry(); auto nhwc_transformer = std::make_unique(std::move(cpu_allocator), std::move(cpu_registry), - logger); + logger, session_options.config_options); if (nhwc_transformer->IsActive()) { transformers.emplace_back(std::move(nhwc_transformer)); } diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 4bd7b81c6e211..61d951f3f0625 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -17,6 +17,7 @@ #include "core/optimizer/transpose_optimization/ort_optimizer_utils.h" #include "core/optimizer/transpose_optimization/ort_transpose_optimization.h" #include "core/providers/common.h" +#include "core/providers/cpu/mlas_backend_kernel_selector_config_utils.h" using namespace ONNX_NAMESPACE; using namespace ::onnxruntime::common; @@ -28,6 +29,8 @@ namespace onnxruntime { using namespace layout_transformation; #ifdef USE_KLEIDIAI +namespace { + bool TryGetDimValueAsSizeT(const ONNX_NAMESPACE::TensorShapeProto& shape, int index, size_t& value) { if (shape.dim_size() <= index || !shape.dim(index).has_dim_value()) { return false; @@ -230,6 +233,8 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr /*Beta*/ 0.0f); #endif } + +} // namespace #endif static inline const OpTransformInfo* @@ -264,13 +269,17 @@ NhwcConvLookup( NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, std::shared_ptr cpu_kernel_registry, - const logging::Logger& logger) noexcept + const logging::Logger& logger, + const ConfigOptions& config_options) noexcept : GraphTransformer("NhwcTransformer"), cpu_allocator_(std::move(cpu_allocator)) { if (!cpu_kernel_registry) { // This is a CPU op nodes optimizer, not useful if cpu EP is not available. return; } + MLAS_BACKEND_KERNEL_SELECTOR_CONFIG mlas_backend_kernel_selector_config{}; + SetupMlasBackendKernelSelectorFromConfigOptions(mlas_backend_kernel_selector_config, config_options); + // // Constructing a mapping table from operators to be transformed to their target. // Make sure that the new nodes we are about to create during graph transformation, @@ -355,7 +364,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, #ifdef USE_KLEIDIAI // KleidiAI specific block for NhwcFusedConvolutions - { + if (mlas_backend_kernel_selector_config.use_kleidiai) { // F32 Conv -> F32 NHWC Conv OpKernelRegistryId nhwc_conv_fp32{ "NhwcFusedConv", kMSDomain, 1, {{"T", {DataTypeImpl::GetTensorType()}}}}; diff --git a/onnxruntime/core/optimizer/nhwc_transformer.h b/onnxruntime/core/optimizer/nhwc_transformer.h index 24d4cb3069b30..4755ceb316fef 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.h +++ b/onnxruntime/core/optimizer/nhwc_transformer.h @@ -5,6 +5,7 @@ #include #include "core/common/common.h" +#include "core/framework/config_options.h" #include "core/framework/execution_provider.h" #include "core/framework/kernel_registry.h" #include "core/optimizer/graph_transformer.h" @@ -82,7 +83,7 @@ class NhwcTransformer : public GraphTransformer { private: public: explicit NhwcTransformer(AllocatorPtr cpu_allocator, std::shared_ptr cpu_kernel_registry, - const logging::Logger& logger) noexcept; + const logging::Logger& logger, const ConfigOptions& config_options) noexcept; /** * @brief Usually called right after constructor, it shows whether diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index a24a4f492d2cd..b73929efab8a6 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -10,6 +10,7 @@ #include "core/framework/kernel_registry.h" #include "core/mlas/inc/mlas.h" #include "core/providers/common.h" +#include "core/session/onnxruntime_session_options_config_keys.h" #include "test/unittest_util/graph_transform_test_builder.h" #if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) #include "core/mlas/lib/mlasi.h" @@ -462,6 +463,42 @@ TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { /*relative_per_sample_tolerance*/ 1e-6); } +TEST(NhwcTransformerTests, ConvFloat_RespectsKleidiDisableConfig) { + if (!HasFloatNhwcNoTransposeSupport({1, 8, 7, 7}, {16, 8, 3, 3}, {1, 1, 1, 1})) { + GTEST_SKIP() << "Float NHWC KleidiAI path is not available on this configuration."; + } + + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& conv_node = builder.AddConvNode(input_arg, weight_arg, output_arg); + conv_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); + }; + + auto add_session_options = [](SessionOptions& session_options) { + const auto status = session_options.config_options.AddConfigEntry(kOrtSessionOptionsMlasDisableKleidiAi, "1"); + ASSERT_TRUE(status.IsOK()) << status.ErrorMessage(); + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6, + nullptr, + add_session_options); +} + TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { auto build_test_case = [&](ModelTestBuilder& builder) { auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); From 69d91c3e847fbc36d4e06be46a5dfa7033c242e4 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 21 Apr 2026 11:47:29 +0100 Subject: [PATCH 039/140] Add a comment to LayoutTransformDoesNotRetargetNhwcFusedConv test to indicate what is being tested * Make the checks in TestConvPath stricter Signed-off-by: Orlaith Monahan --- onnxruntime/test/optimizer/conv_add_act_test.cc | 4 ++-- onnxruntime/test/optimizer/transpose_optimizer_test.cc | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/onnxruntime/test/optimizer/conv_add_act_test.cc b/onnxruntime/test/optimizer/conv_add_act_test.cc index 660f14c752886..03ca950050d64 100644 --- a/onnxruntime/test/optimizer/conv_add_act_test.cc +++ b/onnxruntime/test/optimizer/conv_add_act_test.cc @@ -30,8 +30,8 @@ void TestConvPath(const std::vector& input_shape, const std::vector disabled_optimizers = {"NchwcTransformer", "NhwcTransformer"}; TransformerTester(build_test_case, diff --git a/onnxruntime/test/optimizer/transpose_optimizer_test.cc b/onnxruntime/test/optimizer/transpose_optimizer_test.cc index 9fafe7f578954..080c382db5d93 100644 --- a/onnxruntime/test/optimizer/transpose_optimizer_test.cc +++ b/onnxruntime/test/optimizer/transpose_optimizer_test.cc @@ -4604,6 +4604,8 @@ TEST(TransposeOptimizerTests, QnnTransposeReshape) { } } +// Verifies that layout transformation preserves an existing NHWC-native +// NhwcFusedConv as-is instead of retargeting it or inserting Transpose nodes. TEST(TransposeOptimizerTests, LayoutTransformDoesNotRetargetNhwcFusedConv) { std::unordered_map domain_to_version{{kOnnxDomain, 13}, {kMSDomain, 1}}; Model model("LayoutTransformDoesNotRetargetNhwcFusedConv", false, ModelMetaData(), PathString(), From 648d68f479e515c5329d125354ee76362885f375 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 21 Apr 2026 15:46:57 +0100 Subject: [PATCH 040/140] Tighten up the checks around NCHWc and add a unittest Signed-off-by: Orlaith Monahan --- .../core/optimizer/nchwc_transformer.cc | 17 ++++++++--- .../test/optimizer/nchwc_optimizer_test.cc | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/onnxruntime/core/optimizer/nchwc_transformer.cc b/onnxruntime/core/optimizer/nchwc_transformer.cc index f28172444709b..a971a058f43b7 100644 --- a/onnxruntime/core/optimizer/nchwc_transformer.cc +++ b/onnxruntime/core/optimizer/nchwc_transformer.cc @@ -324,13 +324,17 @@ void NchwcTransformerImpl::ConvPoolShapeInference(const Node& node, void NchwcTransformerImpl::TransformConv(Node& node) { auto& input_defs = node.MutableInputDefs(); auto& output_defs = node.MutableOutputDefs(); + NchwcArgument* nchwc_sum_input = nullptr; // The internal NCHWc Conv kernel can consume an optional fused Sum input, but it expects - // that tensor to already be in NCHWc layout. This transform only legalizes the main Conv - // input and static weights/bias, so a pre-existing FusedConv(X, W, B, Sum) would feed a - // plain NCHW tensor into the NCHWc kernel and produce incorrect results. + // that tensor to already be in NCHWc layout. Only allow transforming a pre-existing + // FusedConv(X, W, B, Sum) when the Sum input already has a tracked NCHWc variant that + // can be wired through directly. if (node.OpType() == "FusedConv" && input_defs.size() >= 4 && input_defs[3] != nullptr && input_defs[3]->Exists()) { - return; + nchwc_sum_input = LookupNchwcArgument(input_defs[3]); + if (nchwc_sum_input == nullptr) { + return; + } } // Require that the weights tensor be static. @@ -498,6 +502,11 @@ void NchwcTransformerImpl::TransformConv(Node& node) { nchwc_node.MutableInputDefs()[2] = nchwc_conv_B_arg; } + if (nchwc_sum_input != nullptr) { + nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; + nchwc_sum_input->remaining_original_uses_--; + } + NchwcArgument::Shape output_shape(output_defs[0]); if (do_reorder_input) { diff --git a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc index 46312834aec9e..427b8e90449ca 100644 --- a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc +++ b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc @@ -644,6 +644,35 @@ TEST(NchwcOptimizerTests, FusedConvAddFusion) { test_case(true, true, 1); } +TEST(NchwcOptimizerTests, PreExistingFusedConvWithNchwcSumInput) { + auto build_test_case = [&](NchwcTestHelper& helper) { + auto* input_arg = helper.MakeInput({1, 32, 28, 28}); + auto* sum_arg = helper.MakeIntermediate(); + auto* output_arg = helper.MakeOutput(); + + helper.AddConvNode(input_arg, sum_arg, {32, 32, 3, 3}); + + auto* weights_arg = helper.MakeInitializer({32, 32, 3, 3}); + auto* bias_arg = helper.MakeInitializer({32}); + auto& fused_conv_node = + helper.AddNode("FusedConv", {input_arg, weights_arg, bias_arg, sum_arg}, {output_arg}, kMSDomain); + fused_conv_node.AddAttribute("activation", "Relu"); + fused_conv_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); + fused_conv_node.AddAttribute("strides", std::vector{1, 1}); + fused_conv_node.AddAttribute("kernel_shape", std::vector{3, 3}); + }; + + auto check_nchwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); + EXPECT_EQ(op_to_count["com.microsoft.nchwc.Conv"], 2); + EXPECT_EQ(op_to_count["com.microsoft.nchwc.ReorderInput"], 1); + EXPECT_EQ(op_to_count["com.microsoft.nchwc.ReorderOutput"], 1); + EXPECT_EQ(op_to_count["com.microsoft.FusedConv"], 0); + }; + + NchwcOptimizerTester(build_test_case, check_nchwc_graph); +} + TEST(NchwcOptimizerTests, ConvBinary) { auto test_case = [&](const std::string& op_type) { auto build_test_case = [&](NchwcTestHelper& helper) { From cf8d08f3d0fd1c582179610730a22b2cc9801bbc Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 22 Apr 2026 10:50:41 +0100 Subject: [PATCH 041/140] Add missing padding to PreExistingFusedConvWithNchwcSumInput Signed-off-by: Orlaith Monahan --- onnxruntime/test/optimizer/nchwc_optimizer_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc index 427b8e90449ca..a07f173950051 100644 --- a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc +++ b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc @@ -650,7 +650,8 @@ TEST(NchwcOptimizerTests, PreExistingFusedConvWithNchwcSumInput) { auto* sum_arg = helper.MakeIntermediate(); auto* output_arg = helper.MakeOutput(); - helper.AddConvNode(input_arg, sum_arg, {32, 32, 3, 3}); + auto& sum_node = helper.AddConvNode(input_arg, sum_arg, {32, 32, 3, 3}); + sum_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); auto* weights_arg = helper.MakeInitializer({32, 32, 3, 3}); auto* bias_arg = helper.MakeInitializer({32}); From 5a6f11006e72526c99650044539b6007fe889fd2 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 09:21:33 +0100 Subject: [PATCH 042/140] Changing ort_model_only_test.cc to use the same function for all models * Keeps code consistent * Tests are a little more vulnerable to testdata not being where expected Signed-off-by: Orlaith Monahan --- .../test/framework/ort_model_only_test.cc | 31 ++----------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 6e7c38b3c4411..1c72368ee2511 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -28,31 +28,6 @@ using namespace ONNX_NAMESPACE; namespace onnxruntime { namespace test { -namespace { -std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { - if (path.is_absolute() || path.empty()) { - return path; - } - - std::filesystem::path workspace_candidate = std::filesystem::current_path() / path; - std::error_code ec; - if (std::filesystem::exists(workspace_candidate, ec) && !ec) { - return workspace_candidate; - } - - static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path(); - std::filesystem::path source_candidate = kSourceTestRoot / path; - ec.clear(); - if (std::filesystem::exists(source_candidate, ec) && !ec) { - return source_candidate; - } - - // Keep the original relative-to-workdir intent so the downstream file-open - // error points to the path we actually tried first. - return workspace_candidate; -} -} // namespace struct OrtModelTestInfo { std::basic_string model_filename; @@ -86,7 +61,7 @@ static void RunOrtModel(const OrtModelTestInfo& test_info) { std::vector model_data; InferenceSessionWrapper session_object{so, GetEnvironment()}; - std::filesystem::path model_path = ResolveTestPath(std::filesystem::path{test_info.model_filename}); + std::filesystem::path model_path{test_info.model_filename}; const auto& model_path_str = model_path.native(); if (test_info.run_use_buffer) { @@ -244,8 +219,8 @@ static void SaveAndCompareModels(const PathString& orig_file, const PathString& ort_file, TransformerLevel optimization_level = TransformerLevel::Level3, bool compare_saved_model = true) { - std::filesystem::path orig_path = ResolveTestPath(std::filesystem::path{orig_file}); - std::filesystem::path ort_path = ResolveTestPath(std::filesystem::path{ort_file}); + std::filesystem::path orig_path{orig_file}; + std::filesystem::path ort_path{ort_file}; if (ort_path.has_parent_path()) { std::filesystem::create_directories(ort_path.parent_path()); } From 522a861c9a3dea589f80e168e2c860bc164393ff Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 09:33:48 +0100 Subject: [PATCH 043/140] Change the fallback convert functions in conv.cc to use more optimised MLAS routines Signed-off-by: Orlaith Monahan --- onnxruntime/core/providers/cpu/nn/conv.cc | 32 +++++++---------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 4968afe46106b..4b6e4178f20c8 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -30,43 +30,29 @@ namespace { template void ConvertNHWCToNCHW(const T* src, T* dst, - int64_t n, int64_t c, int64_t h, int64_t w) { + int64_t n, int64_t c, int64_t h, int64_t w, + concurrency::ThreadPool* thread_pool) { const size_t n_count = narrow(n); const size_t c_count = narrow(c); const size_t hw = narrow(SafeInt(h) * w); for (size_t n_idx = 0; n_idx < n_count; ++n_idx) { const size_t n_src_offset = SafeInt(SafeInt(n_idx) * hw) * c_count; const size_t n_dst_offset = SafeInt(SafeInt(n_idx) * c_count) * hw; - for (size_t c_idx = 0; c_idx < c_count; ++c_idx) { - const size_t c_dst_offset = SafeInt(c_idx) * hw; - const T* src_ptr = src + n_src_offset + c_idx; - T* dst_ptr = dst + n_dst_offset + c_dst_offset; - for (size_t hw_idx = 0; hw_idx < hw; ++hw_idx) { - const size_t src_hw_offset = SafeInt(hw_idx) * c_count; - dst_ptr[hw_idx] = src_ptr[src_hw_offset]; - } - } + MlasTranspose(src + n_src_offset, dst + n_dst_offset, hw, c_count, thread_pool); } } template void ConvertNCHWToNHWC(const T* src, T* dst, - int64_t n, int64_t c, int64_t h, int64_t w) { + int64_t n, int64_t c, int64_t h, int64_t w, + concurrency::ThreadPool* thread_pool) { const size_t n_count = narrow(n); const size_t c_count = narrow(c); const size_t hw = narrow(SafeInt(h) * w); for (size_t n_idx = 0; n_idx < n_count; ++n_idx) { const size_t n_src_offset = SafeInt(SafeInt(n_idx) * c_count) * hw; const size_t n_dst_offset = SafeInt(SafeInt(n_idx) * hw) * c_count; - for (size_t hw_idx = 0; hw_idx < hw; ++hw_idx) { - const size_t hw_dst_offset = SafeInt(hw_idx) * c_count; - const T* src_ptr = src + n_src_offset + hw_idx; - T* dst_ptr = dst + n_dst_offset + hw_dst_offset; - for (size_t c_idx = 0; c_idx < c_count; ++c_idx) { - const size_t src_c_offset = SafeInt(c_idx) * hw; - dst_ptr[c_idx] = src_ptr[src_c_offset]; - } - } + MlasTranspose(src + n_src_offset, dst + n_dst_offset, c_count, hw, thread_pool); } } @@ -366,7 +352,7 @@ Status Conv::Compute(OpKernelContext* context) const { float* temp_input = static_cast(alloc->Alloc(sizeof(float) * input_elements)); input_temp = BufferUniquePtr(temp_input, BufferDeleter(alloc)); ConvertNHWCToNCHW(X->Data(), temp_input, - input_n, input_c, input_h, input_w); + input_n, input_c, input_h, input_w, thread_pool); input_compute = temp_input; } @@ -387,7 +373,7 @@ Status Conv::Compute(OpKernelContext* context) const { BufferUniquePtr sum_nchw_buffer(sum_nchw, BufferDeleter(alloc)); ConvertNHWCToNCHW(sum_manual_data, sum_nchw, - y_dims[0], y_dims[3], y_dims[1], y_dims[2]); + y_dims[0], y_dims[3], y_dims[1], y_dims[2], thread_pool); auto output_span = gsl::make_span(output_compute, static_cast(output_elements)); auto sum_span = gsl::make_span(sum_nchw, static_cast(output_elements)); @@ -403,7 +389,7 @@ Status Conv::Compute(OpKernelContext* context) const { ConvertNCHWToNHWC(output_compute, Ydata.data(), - y_dims[0], y_dims[3], y_dims[1], y_dims[2]); + y_dims[0], y_dims[3], y_dims[1], y_dims[2], thread_pool); } } else { const int64_t input_image_size = input_shape.Size(); From 371058126320ecb97291ac91064d77fb953d3616 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 09:47:39 +0100 Subject: [PATCH 044/140] Remove the filter for NhwcTransformer as it is no longer needed Signed-off-by: Orlaith Monahan --- .../test/optimizer/fuse_initializers_transformer_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index b238a0c849fea..17d73d521c4a2 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -493,11 +493,11 @@ TEST(TransformerTest, FuseFp16InitializersWithGraphOutputs) { so.graph_optimization_level = TransformerLevel::MaxLevel; // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; - // Disabling ConstantFolding and NhwcTransformer optimizer as it will remove the Cast node + // Disabling ConstantFolding as it will remove the Cast node // by folding it with Add node. This will not allow us to test the // scenario where Cast node is producing graph output and need to // kept untouched by FuseInitializersTransformer. - ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"ConstantFolding", "NhwcTransformer"})); + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"ConstantFolding"})); ASSERT_STATUS_OK(session.Load(model_uri)); _graph_structure_at_load(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); From 0f0a114ff24503d9828ca97b9034ef44358488ad Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 09:58:58 +0100 Subject: [PATCH 045/140] Adding comments to fuse_initializers_transformer_test.cc to explain filtered NhwcTransformer Signed-off-by: Orlaith Monahan --- .../test/optimizer/fuse_initializers_transformer_test.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index 17d73d521c4a2..5b39c032353df 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -363,6 +363,8 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + // Keep this test focused on FuseInitializersTransformer/NCHWC behavior. NhwcTransformer is + // hardware/kernel dependent and can otherwise change the post-init node counts this test asserts on. ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); @@ -403,6 +405,8 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + // Keep this test focused on FuseInitializersTransformer/NCHWC behavior. NhwcTransformer is + // hardware/kernel dependent and can otherwise change the post-init node counts this test asserts on. ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); @@ -445,6 +449,8 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + // Keep this test focused on FuseInitializersTransformer/NCHWC behavior. NhwcTransformer is + // hardware/kernel dependent and can otherwise change the post-init node counts this test asserts on. ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); From 9356f98dce8a6c10020bb3412fd0e95c93765510 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 10:43:36 +0100 Subject: [PATCH 046/140] Cleanup of com.microsoft.NhwcFusedConv to be available in minimal builds * Added to the serializer * Can always be handled even if not always supported * Regenerated kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes Signed-off-by: Orlaith Monahan --- .../framework/kernel_type_str_resolver.cc | 12 - .../kernel_type_str_resolver_utils.cc | 691 +++++++++--------- onnxruntime/core/session/inference_session.cc | 8 + .../kernel_type_str_resolver_utils_test.cc | 49 +- 4 files changed, 397 insertions(+), 363 deletions(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index f995caec22cf9..69a482ae05bdb 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -35,18 +35,6 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } } -#ifdef USE_KLEIDIAI - // KleidiAI specific block for NhwcFusedConvolutions - if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { - const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; - op_it = map.find(fused_conv_op_id); - if (op_it == map.end()) { - const auto conv_op_id = OpIdentifier{std::string{kOnnxDomain}, "Conv", op_id.since_version}; - op_it = map.find(conv_op_id); - } - } -#endif - return op_it; } diff --git a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc index 50722f930228c..94920f7fa4b01 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc @@ -66,368 +66,377 @@ Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrRe // clang-format off constexpr uint8_t kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes[] = { 0x10, 0x00, 0x00, 0x00, 0x6b, 0x74, 0x73, 0x72, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x7c, 0x15, 0x00, 0x00, - 0xf8, 0x0a, 0x00, 0x00, 0x64, 0x04, 0x00, 0x00, 0x44, 0x14, 0x00, 0x00, 0xc0, 0x09, 0x00, 0x00, - 0xbc, 0x14, 0x00, 0x00, 0x78, 0x0b, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x00, 0xbc, 0x13, 0x00, 0x00, - 0x44, 0x08, 0x00, 0x00, 0x94, 0x0d, 0x00, 0x00, 0xd8, 0x0d, 0x00, 0x00, 0xa0, 0x07, 0x00, 0x00, - 0xe8, 0x06, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x00, 0x34, 0x0d, 0x00, 0x00, 0xdc, 0x07, 0x00, 0x00, - 0x70, 0x12, 0x00, 0x00, 0x88, 0x06, 0x00, 0x00, 0x50, 0x0e, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, - 0xa0, 0x0c, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x8c, 0x10, 0x00, 0x00, 0x38, 0x02, 0x00, 0x00, - 0x88, 0x0f, 0x00, 0x00, 0x3c, 0x0f, 0x00, 0x00, 0x64, 0x05, 0x00, 0x00, 0x84, 0x11, 0x00, 0x00, - 0xf4, 0x06, 0x00, 0x00, 0xf0, 0x08, 0x00, 0x00, 0x10, 0x0c, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, - 0x0c, 0x13, 0x00, 0x00, 0xc8, 0x0d, 0x00, 0x00, 0x44, 0x08, 0x00, 0x00, 0x20, 0x0a, 0x00, 0x00, - 0xd4, 0x11, 0x00, 0x00, 0x84, 0x08, 0x00, 0x00, 0xe8, 0x05, 0x00, 0x00, 0x8c, 0x15, 0x00, 0x00, - 0xd8, 0x0f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x68, 0x05, 0x00, 0x00, - 0x84, 0x0e, 0x00, 0x00, 0x8c, 0x04, 0x00, 0x00, 0x30, 0x04, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, - 0x44, 0x12, 0x00, 0x00, 0x74, 0xea, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, - 0xa0, 0xea, 0xff, 0xff, 0x54, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xf0, 0xea, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xbc, 0xea, 0xff, 0xff, - 0x54, 0x15, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xaa, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xa4, 0xea, 0xff, 0xff, - 0xe0, 0xea, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, - 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x08, 0xeb, 0xff, 0xff, 0x08, 0x15, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, + 0x88, 0x0a, 0x00, 0x00, 0x60, 0x15, 0x00, 0x00, 0x6c, 0x13, 0x00, 0x00, 0x64, 0x11, 0x00, 0x00, + 0x7c, 0x04, 0x00, 0x00, 0xe4, 0x0e, 0x00, 0x00, 0xdc, 0x13, 0x00, 0x00, 0xb0, 0x02, 0x00, 0x00, + 0x10, 0x0b, 0x00, 0x00, 0xf8, 0x14, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0xa0, 0x08, 0x00, 0x00, + 0xa0, 0x10, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0xe4, 0x04, 0x00, 0x00, + 0xb0, 0x0b, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x14, 0x0e, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00, + 0xe4, 0x02, 0x00, 0x00, 0x34, 0x0c, 0x00, 0x00, 0x18, 0x12, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x2c, 0x0f, 0x00, 0x00, 0x08, 0x05, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, + 0x44, 0x05, 0x00, 0x00, 0x70, 0x15, 0x00, 0x00, 0xa4, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, + 0x70, 0x09, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x9c, 0x10, 0x00, 0x00, 0xb0, 0x0b, 0x00, 0x00, + 0xd8, 0x13, 0x00, 0x00, 0x1c, 0x03, 0x00, 0x00, 0x84, 0x05, 0x00, 0x00, 0x08, 0x09, 0x00, 0x00, + 0x50, 0x0d, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x60, 0x12, 0x00, 0x00, 0x58, 0x11, 0x00, 0x00, + 0xd4, 0x0c, 0x00, 0x00, 0x64, 0x08, 0x00, 0x00, 0x4c, 0x0c, 0x00, 0x00, 0xdc, 0x0a, 0x00, 0x00, + 0x60, 0x06, 0x00, 0x00, 0x9c, 0x15, 0x00, 0x00, 0xfc, 0xe9, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x31, 0x00, 0x20, 0xea, 0xff, 0xff, + 0x34, 0x15, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0e, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x48, 0xea, 0xff, 0xff, + 0x44, 0xea, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, + 0x32, 0x34, 0x00, 0x00, 0x78, 0xea, 0xff, 0xff, 0xa4, 0x15, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0xea, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x94, 0xea, 0xff, 0xff, 0x50, 0x15, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xb0, 0xea, 0xff, 0xff, 0xac, 0xea, 0xff, 0xff, 0x40, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xf6, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xf0, 0xea, 0xff, 0xff, 0x2c, 0xeb, 0xff, 0xff, - 0xc8, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x7c, 0xeb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x48, 0xeb, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, - 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x7c, 0xeb, 0xff, 0xff, - 0x64, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x58, 0xeb, 0xff, 0xff, 0x94, 0xeb, 0xff, 0xff, 0xf0, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe4, 0xeb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xb0, 0xeb, 0xff, 0xff, 0x0c, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9e, 0xeb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x0c, 0xec, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xd8, 0xeb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, - 0x33, 0x00, 0x00, 0x00, 0x04, 0xec, 0xff, 0xff, 0xf0, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0xec, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0x20, 0xec, 0xff, 0xff, 0xf0, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, 0xec, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x08, 0xec, 0xff, 0xff, 0x44, 0xec, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, - 0x65, 0x61, 0x72, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x78, 0xec, 0xff, 0xff, 0x94, 0x12, 0x00, 0x00, + 0x9a, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x94, 0xea, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0xd4, 0xea, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x73, 0x65, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0xfc, 0xea, 0xff, 0xff, 0x58, 0x14, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x66, 0xec, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xd4, 0xec, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, - 0xa0, 0xec, 0xff, 0xff, 0x40, 0x12, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x7c, 0xec, 0xff, 0xff, 0xb8, 0xec, 0xff, 0xff, 0x04, 0x12, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xed, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0xd4, 0xec, 0xff, 0xff, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, - 0x74, 0x3a, 0x51, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x3a, 0x31, 0x00, - 0x20, 0xed, 0xff, 0xff, 0x9c, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x74, 0xed, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, - 0x7c, 0xed, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x48, 0xed, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x34, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xa0, 0xed, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x6c, 0xed, 0xff, 0xff, - 0x74, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xc0, 0xed, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x54, 0xed, 0xff, 0xff, - 0x90, 0xed, 0xff, 0xff, 0xd8, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xe0, 0xed, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xac, 0xed, 0xff, 0xff, - 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x77, 0x5f, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xee, 0xff, 0xff, - 0x04, 0x00, 0x00, 0x00, 0xd4, 0xed, 0xff, 0xff, 0xb0, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0xee, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, - 0xf0, 0xed, 0xff, 0xff, 0x1c, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xed, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x4c, 0xee, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x18, 0xee, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, - 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x39, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xee, 0xff, 0xff, - 0x94, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xa0, 0xee, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x34, 0xee, 0xff, 0xff, - 0x70, 0xee, 0xff, 0xff, 0x4c, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x5e, 0xee, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xcc, 0xee, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x98, 0xee, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x4e, 0x68, - 0x77, 0x63, 0x4d, 0x61, 0x78, 0x50, 0x6f, 0x6f, 0x6c, 0x3a, 0x31, 0x00, 0xcc, 0xee, 0xff, 0xff, - 0x44, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xba, 0xee, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xb4, 0xee, 0xff, 0xff, - 0xf0, 0xee, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, - 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, - 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x00, - 0x30, 0xef, 0xff, 0xff, 0xb0, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x84, 0xef, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, - 0x18, 0xef, 0xff, 0xff, 0x54, 0xef, 0xff, 0xff, 0x68, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0xef, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0xb0, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x7c, 0xef, 0xff, 0xff, + 0xea, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x24, 0xeb, 0xff, 0xff, 0x20, 0xeb, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x48, 0xeb, 0xff, 0xff, 0xf8, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0xeb, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x70, 0xeb, 0xff, 0xff, 0x6c, 0xeb, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, + 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x30, 0x00, 0x00, 0x00, 0x00, + 0xa4, 0xeb, 0xff, 0xff, 0x60, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x8e, 0xeb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xeb, 0xff, 0xff, + 0x8c, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x9c, 0xeb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xdc, 0xeb, 0xff, 0xff, 0x78, 0x13, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xbc, 0xeb, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x04, 0xec, 0xff, 0xff, 0x00, 0xec, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x3a, + 0x31, 0x31, 0x00, 0x00, 0x28, 0xec, 0xff, 0xff, 0x38, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0xec, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x44, 0xec, 0xff, 0xff, 0x10, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0xec, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x6c, 0xec, 0xff, 0xff, 0x68, 0xec, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, + 0x31, 0x39, 0x00, 0x00, 0x98, 0xec, 0xff, 0xff, 0x84, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x86, 0xec, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x80, 0xec, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xc0, 0xec, 0xff, 0xff, + 0x24, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xa0, 0xec, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xe8, 0xec, 0xff, 0xff, + 0xe4, 0xec, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x73, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, 0x0c, 0xed, 0xff, 0xff, 0x48, 0x12, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xfa, 0xec, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x34, 0xed, 0xff, 0xff, 0x30, 0xed, 0xff, 0xff, + 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, + 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, + 0x64, 0xed, 0xff, 0xff, 0xb0, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0xed, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x80, 0xed, 0xff, 0xff, + 0x9c, 0x12, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6e, 0xed, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x68, 0xed, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0xa8, 0xed, 0xff, 0xff, 0x3c, 0x12, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc4, 0xed, 0xff, 0xff, 0xc0, 0xed, 0xff, 0xff, + 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, + 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x34, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0xed, 0xff, 0xff, 0xf4, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xed, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x14, 0xee, 0xff, 0xff, 0xd0, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf4, 0xed, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0x3c, 0xee, 0xff, 0xff, 0x38, 0xee, 0xff, 0xff, 0xe4, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14, 0xee, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x54, 0xee, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x3a, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xee, 0xff, 0xff, 0xc4, 0x0b, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6a, 0xee, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xa4, 0xee, 0xff, 0xff, 0xa0, 0xee, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x31, 0x00, - 0xa0, 0xef, 0xff, 0xff, 0x70, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8e, 0xef, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x88, 0xef, 0xff, 0xff, 0xc4, 0xef, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, - 0xf0, 0xef, 0xff, 0xff, 0x20, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xef, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd8, 0xef, 0xff, 0xff, 0x14, 0xf0, 0xff, 0xff, 0xe0, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0x30, 0xf0, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, - 0x7a, 0x65, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x00, 0x58, 0xf0, 0xff, 0xff, 0xb8, 0x0f, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x00, 0x00, + 0xc4, 0xee, 0xff, 0xff, 0x90, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb2, 0xee, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xec, 0xee, 0xff, 0xff, 0xe8, 0xee, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x10, 0xef, 0xff, 0xff, + 0x6c, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xec, 0xee, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x2c, 0xef, 0xff, 0xff, 0x28, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x46, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x40, 0xf0, 0xff, 0xff, 0x7c, 0xf0, 0xff, 0xff, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x35, - 0x00, 0x00, 0x00, 0x00, 0xa4, 0xf0, 0xff, 0xff, 0xec, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x92, 0xf0, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x8c, 0xf0, 0xff, 0xff, 0xc8, 0xf0, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x36, 0x00, 0x00, 0x00, 0x00, - 0xf0, 0xf0, 0xff, 0xff, 0xa0, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd8, 0xf0, 0xff, 0xff, 0x14, 0xf1, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x3c, 0xf1, 0xff, 0xff, - 0xd4, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x2a, 0xf1, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x24, 0xf1, 0xff, 0xff, - 0x60, 0xf1, 0xff, 0xff, 0x94, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xb0, 0xf1, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x7c, 0xf1, 0xff, 0xff, + 0x1a, 0xef, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x54, 0xef, 0xff, 0xff, 0x50, 0xef, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x34, - 0x00, 0x00, 0x00, 0x00, 0xa4, 0xf1, 0xff, 0xff, 0xec, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x92, 0xf1, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x8c, 0xf1, 0xff, 0xff, 0xc8, 0xf1, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x78, 0xef, 0xff, 0xff, 0xdc, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x66, 0xef, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xa0, 0xef, 0xff, 0xff, 0x9c, 0xef, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, - 0xf0, 0xf1, 0xff, 0xff, 0xa0, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xf1, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd8, 0xf1, 0xff, 0xff, 0x14, 0xf2, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, - 0x3a, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x3c, 0xf2, 0xff, 0xff, - 0xa0, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x8c, 0xf2, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x58, 0xf2, 0xff, 0xff, 0xb8, 0x0d, 0x00, 0x00, + 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0xef, 0xff, 0xff, 0x90, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb2, 0xef, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xec, 0xef, 0xff, 0xff, 0xe8, 0xef, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, + 0x14, 0xf0, 0xff, 0xff, 0x68, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xf0, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0xf0, 0xff, 0xff, + 0x24, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x1e, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x58, 0xf0, 0xff, 0xff, + 0x54, 0xf0, 0xff, 0xff, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xd4, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x51, 0x4c, + 0x69, 0x6e, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x3a, 0x31, 0x00, 0xa0, 0xf0, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x54, 0x34, 0x00, 0x00, 0x84, 0xf0, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, + 0xc4, 0xf0, 0xff, 0xff, 0x50, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xa0, 0xf0, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0xe0, 0xf0, 0xff, 0xff, + 0x6c, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xbc, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfc, 0xf0, 0xff, 0xff, 0xe8, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x46, 0xf2, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x40, 0xf2, 0xff, 0xff, 0x7c, 0xf2, 0xff, 0xff, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, - 0x31, 0x00, 0x00, 0x00, 0xa4, 0xf2, 0xff, 0xff, 0x6c, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x92, 0xf2, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x8c, 0xf2, 0xff, 0xff, 0xc8, 0xf2, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, - 0xf0, 0xf2, 0xff, 0xff, 0x20, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xf2, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd8, 0xf2, 0xff, 0xff, 0x14, 0xf3, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xdc, 0xf0, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x24, 0xf1, 0xff, 0xff, 0x20, 0xf1, 0xff, 0xff, + 0xfc, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0xf1, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x08, 0xf1, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x48, 0xf1, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x77, 0x5f, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x00, 0x30, 0xf1, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x70, 0xf1, 0xff, 0xff, + 0x7c, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x5e, 0xf1, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x58, 0xf1, 0xff, 0xff, + 0x07, 0x00, 0x00, 0x00, 0x98, 0xf1, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x33, 0x00, 0x3c, 0xf3, 0xff, 0xff, - 0xb8, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x8c, 0xf3, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x58, 0xf3, 0xff, 0xff, 0xb8, 0x0c, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x35, 0x00, 0xc0, 0xf1, 0xff, 0xff, + 0xbc, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x9c, 0xf1, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xdc, 0xf1, 0xff, 0xff, 0x78, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x46, 0xf3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x40, 0xf3, 0xff, 0xff, 0x7c, 0xf3, 0xff, 0xff, - 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, - 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x33, - 0x00, 0x00, 0x00, 0x00, 0xb4, 0xf3, 0xff, 0xff, 0x58, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9e, 0xf3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd0, 0xf3, 0xff, 0xff, 0x10, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0xf4, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, - 0xb8, 0xf3, 0xff, 0xff, 0xf4, 0xf3, 0xff, 0xff, 0xc8, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x44, 0xf4, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0x10, 0xf4, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x3a, 0x31, 0x39, 0x00, 0x00, 0x00, 0x00, 0x38, 0xf4, 0xff, 0xff, 0x58, 0x08, 0x00, 0x00, + 0xca, 0xf1, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x04, 0xf2, 0xff, 0xff, 0x00, 0xf2, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x28, 0xf2, 0xff, 0xff, 0x18, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16, 0xf2, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x50, 0xf2, 0xff, 0xff, 0x4c, 0xf2, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, + 0x74, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, + 0x61, 0x72, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xf2, 0xff, 0xff, 0x90, 0x0d, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x7a, 0xf2, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x74, 0xf2, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xb4, 0xf2, 0xff, 0xff, 0x30, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xf2, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0xdc, 0xf2, 0xff, 0xff, 0xd8, 0xf2, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, + 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0xf3, 0xff, 0xff, + 0x54, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xf2, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x28, 0xf3, 0xff, 0xff, + 0x24, 0xf3, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x73, 0x65, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xf3, 0xff, 0xff, 0x08, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x26, 0xf4, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x20, 0xf4, 0xff, 0xff, 0x5c, 0xf4, 0xff, 0xff, + 0x3a, 0xf3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x74, 0xf3, 0xff, 0xff, 0x70, 0xf3, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, - 0x33, 0x00, 0x00, 0x00, 0x84, 0xf4, 0xff, 0xff, 0x8c, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x72, 0xf4, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x6c, 0xf4, 0xff, 0xff, 0xa8, 0xf4, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x98, 0xf3, 0xff, 0xff, 0xa8, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x86, 0xf3, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xc0, 0xf3, 0xff, 0xff, 0xbc, 0xf3, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0x00, - 0xe0, 0xf4, 0xff, 0xff, 0xb0, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xca, 0xf4, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xfc, 0xf4, 0xff, 0xff, - 0x14, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x50, 0xf5, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xe4, 0xf4, 0xff, 0xff, - 0x20, 0xf5, 0xff, 0xff, 0x48, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x70, 0xf5, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x3c, 0xf5, 0xff, 0xff, - 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, - 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x35, - 0x00, 0x00, 0x00, 0x00, 0x74, 0xf5, 0xff, 0xff, 0x6c, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc8, 0xf5, 0xff, 0xff, - 0x02, 0x00, 0x00, 0x00, 0x5c, 0xf5, 0xff, 0xff, 0x98, 0xf5, 0xff, 0xff, 0x74, 0x09, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x82, 0xf5, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0xb4, 0xf5, 0xff, 0xff, 0x08, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0xf6, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xd0, 0xf5, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3a, 0x47, 0x61, 0x74, - 0x68, 0x65, 0x72, 0x3a, 0x31, 0x00, 0x00, 0x00, 0xf8, 0xf5, 0xff, 0xff, 0xe4, 0x07, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0xf6, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x14, 0xf6, 0xff, 0xff, 0xfc, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0xf6, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0xfc, 0xf5, 0xff, 0xff, 0x38, 0xf6, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x34, 0x00, - 0x60, 0xf6, 0xff, 0xff, 0x94, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xb0, 0xf6, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x7c, 0xf6, 0xff, 0xff, - 0x94, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6a, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x64, 0xf6, 0xff, 0xff, - 0xa0, 0xf6, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, - 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x39, 0x00, 0x00, - 0xd0, 0xf6, 0xff, 0xff, 0x10, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0xf7, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xb8, 0xf6, 0xff, 0xff, 0xf4, 0xf6, 0xff, 0xff, 0xc8, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xf6, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x50, 0xf7, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x1c, 0xf7, 0xff, 0xff, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x44, 0xf7, 0xff, 0xff, 0x4c, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0xf7, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x2c, 0xf7, 0xff, 0xff, 0x68, 0xf7, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x00, 0x8c, 0xf7, 0xff, 0xff, - 0x84, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x7a, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x74, 0xf7, 0xff, 0xff, - 0xb0, 0xf7, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xf4, 0xf3, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0xe6, 0xf3, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x18, 0xf4, 0xff, 0xff, 0x3c, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xf3, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x40, 0xf4, 0xff, 0xff, 0x3c, 0xf4, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x78, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x24, 0xf4, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x64, 0xf4, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x47, 0x61, 0x74, + 0x68, 0x65, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x8c, 0xf4, 0xff, 0xff, 0xd4, 0x08, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, 0xf4, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0xa8, 0xf4, 0xff, 0xff, 0xac, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x96, 0xf4, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xd0, 0xf4, 0xff, 0xff, 0xcc, 0xf4, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x4e, 0x68, + 0x77, 0x63, 0x4d, 0x61, 0x78, 0x50, 0x6f, 0x6f, 0x6c, 0x3a, 0x31, 0x00, 0x00, 0xf5, 0xff, 0xff, + 0x54, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xf4, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x28, 0xf5, 0xff, 0xff, + 0x24, 0xf5, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0x00, 0xd8, 0xf7, 0xff, 0xff, 0x38, 0x08, 0x00, 0x00, + 0x79, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xf5, 0xff, 0xff, 0xf4, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xc6, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xf7, 0xff, 0xff, 0xfc, 0xf7, 0xff, 0xff, + 0x3a, 0xf5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x74, 0xf5, 0xff, 0xff, 0x70, 0xf5, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x31, - 0x33, 0x00, 0x00, 0x00, 0x24, 0xf8, 0xff, 0xff, 0xec, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x12, 0xf8, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x0c, 0xf8, 0xff, 0xff, 0x48, 0xf8, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, - 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x30, 0x00, 0x00, 0x7c, 0xf8, 0xff, 0xff, - 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x79, 0x5f, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd8, 0xf8, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0xa4, 0xf8, 0xff, 0xff, 0x18, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x92, 0xf8, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x00, 0xf9, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xcc, 0xf8, 0xff, 0xff, - 0x14, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xa8, 0xf8, 0xff, 0xff, 0xe4, 0xf8, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, - 0x10, 0xf9, 0xff, 0xff, 0x00, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xfe, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xf8, 0xf8, 0xff, 0xff, 0x34, 0xf9, 0xff, 0xff, 0xc0, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x84, 0xf9, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0x50, 0xf9, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, - 0x3a, 0x31, 0x00, 0x00, 0x74, 0xf9, 0xff, 0xff, 0x9c, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x62, 0xf9, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x5c, 0xf9, 0xff, 0xff, 0x98, 0xf9, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, - 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x35, 0x00, 0x00, 0xcc, 0xf9, 0xff, 0xff, - 0x14, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xa8, 0xf9, 0xff, 0xff, 0xe4, 0xf9, 0xff, 0xff, 0x28, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd2, 0xf9, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x40, 0xfa, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x0c, 0xfa, 0xff, 0xff, - 0xb0, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x5c, 0xfa, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x28, 0xfa, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, - 0x33, 0x00, 0x00, 0x00, 0x54, 0xfa, 0xff, 0xff, 0xbc, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x3c, 0xfa, 0xff, 0xff, 0x78, 0xfa, 0xff, 0xff, 0x7c, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc8, 0xfa, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x94, 0xfa, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, + 0x33, 0x00, 0x00, 0x00, 0x98, 0xf5, 0xff, 0xff, 0xbc, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x86, 0xf5, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xc0, 0xf5, 0xff, 0xff, 0xbc, 0xf5, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, - 0x65, 0x61, 0x72, 0x3a, 0x32, 0x33, 0x00, 0x00, 0xc8, 0xfa, 0xff, 0xff, 0xf4, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0xfb, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0xe4, 0xfa, 0xff, 0xff, 0x28, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd2, 0xfa, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x40, 0xfb, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x0c, 0xfb, 0xff, 0xff, - 0xd4, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xe8, 0xfa, 0xff, 0xff, 0x24, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, - 0x32, 0x31, 0x00, 0x00, 0x54, 0xfb, 0xff, 0xff, 0x68, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0xfb, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0xb0, 0xfb, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x7c, 0xfb, 0xff, 0xff, - 0x64, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xd0, 0xfb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x64, 0xfb, 0xff, 0xff, - 0xa0, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, - 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x33, 0x00, 0xc8, 0xfb, 0xff, 0xff, 0x48, 0x04, 0x00, 0x00, + 0x65, 0x61, 0x72, 0x3a, 0x32, 0x31, 0x00, 0x00, 0xec, 0xf5, 0xff, 0xff, 0xf8, 0x09, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xcc, 0xf5, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0xf6, 0xff, 0xff, 0x10, 0xf6, 0xff, 0xff, + 0x0c, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xfe, 0xf5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xf8, 0xf5, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x38, 0xf6, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, + 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x4e, 0x68, 0x77, 0x63, 0x46, 0x75, + 0x73, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x76, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x70, 0xf6, 0xff, 0xff, + 0xe4, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6a, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x64, 0xf6, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x6c, 0xf6, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x74, 0xf6, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xbc, 0xf6, 0xff, 0xff, 0xb8, 0xf6, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, + 0xe4, 0xf6, 0xff, 0xff, 0x98, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xc0, 0xf6, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xff, + 0x54, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x28, 0xf7, 0xff, 0xff, + 0x24, 0xf7, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, + 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0x50, 0xf7, 0xff, 0xff, + 0x2c, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x2c, 0xf7, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x6c, 0xf7, 0xff, 0xff, 0xe8, 0x07, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x5a, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x94, 0xf7, 0xff, 0xff, 0x90, 0xf7, 0xff, 0xff, + 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, + 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x30, 0x00, 0x00, + 0xc4, 0xf7, 0xff, 0xff, 0x58, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb2, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xac, 0xf7, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xec, 0xf7, 0xff, 0xff, 0xf8, 0x07, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xf8, 0xff, 0xff, + 0x04, 0xf8, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x79, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, + 0xec, 0xf7, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x2c, 0xf8, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, + 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x64, 0xf8, 0xff, 0xff, 0xb8, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0xf8, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x80, 0xf8, 0xff, 0xff, + 0x6c, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6a, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x9c, 0xf8, 0xff, 0xff, 0x48, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xb6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xb0, 0xfb, 0xff, 0xff, 0xec, 0xfb, 0xff, 0xff, - 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x61, 0x78, 0x65, 0x73, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0xfc, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x14, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x7c, 0xf8, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xc4, 0xf8, 0xff, 0xff, 0xc0, 0xf8, 0xff, 0xff, + 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, + 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x35, 0x00, 0x00, + 0xf4, 0xf8, 0xff, 0xff, 0xf8, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xdc, 0xf8, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x1c, 0xf9, 0xff, 0xff, 0xc8, 0x06, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x38, 0xf9, 0xff, 0xff, + 0x34, 0xf9, 0xff, 0xff, 0xe8, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x10, 0xf9, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x50, 0xf9, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, + 0x3a, 0x32, 0x34, 0x00, 0x78, 0xf9, 0xff, 0xff, 0x04, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0xf9, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x94, 0xf9, 0xff, 0xff, 0xc0, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x82, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xbc, 0xf9, 0xff, 0xff, 0xb8, 0xf9, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf9, 0xff, 0xff, + 0x60, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xce, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x08, 0xfa, 0xff, 0xff, + 0x04, 0xfa, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x3a, 0x31, 0x36, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xfa, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x22, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x5c, 0xfa, 0xff, 0xff, 0x58, 0xfa, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, 0x3c, 0xfc, 0xff, 0xff, + 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, 0x80, 0xfa, 0xff, 0xff, + 0xd4, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6e, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xa8, 0xfa, 0xff, 0xff, + 0xa4, 0xfa, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, + 0x72, 0x3a, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xfa, 0xff, 0xff, 0x10, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc6, 0xfa, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xf8, 0xfa, 0xff, 0xff, 0xec, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd8, 0xfa, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x20, 0xfb, 0xff, 0xff, 0x1c, 0xfb, 0xff, 0xff, 0x00, 0x05, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xfa, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x38, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, + 0x64, 0xfb, 0xff, 0xff, 0x18, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0xfb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x80, 0xfb, 0xff, 0xff, 0xd4, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x2a, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x24, 0xfc, 0xff, 0xff, - 0x60, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, 0x00, 0x88, 0xfc, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7e, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x78, 0xfc, 0xff, 0xff, 0xb4, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, - 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x51, 0x75, - 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x00, 0x00, - 0xf0, 0xfc, 0xff, 0xff, 0xf0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x44, 0xfd, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xd8, 0xfc, 0xff, 0xff, 0x14, 0xfd, 0xff, 0xff, 0xa8, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0xfd, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x70, 0xfd, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x3c, 0xfd, 0xff, 0xff, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x64, 0xfd, 0xff, 0xff, 0xac, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x52, 0xfd, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x4c, 0xfd, 0xff, 0xff, 0x88, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x3a, 0x31, 0x31, 0x00, 0x00, - 0xb0, 0xfd, 0xff, 0xff, 0x60, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9e, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x98, 0xfd, 0xff, 0xff, 0xd4, 0xfd, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x54, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x30, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfc, 0xfd, 0xff, 0xff, - 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, - 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, 0x00, - 0x30, 0xfe, 0xff, 0xff, 0x8c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x8c, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x58, 0xfe, 0xff, 0xff, 0x88, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6e, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xa8, 0xfb, 0xff, 0xff, + 0xa4, 0xfb, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, + 0x32, 0x33, 0x00, 0x00, 0xd8, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x54, 0x33, 0x00, 0x00, 0xce, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xc8, 0xfb, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x08, 0xfc, 0xff, 0xff, 0x14, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe4, 0xfb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x24, 0xfc, 0xff, 0xff, 0xc0, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0xfc, 0xff, 0xff, 0x3c, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, + 0x33, 0x00, 0x00, 0x00, 0x68, 0xfc, 0xff, 0xff, 0x14, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x44, 0xfc, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x84, 0xfc, 0xff, 0xff, 0xd0, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x72, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xac, 0xfc, 0xff, 0xff, 0xa8, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, + 0x72, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xfc, 0xff, 0xff, 0x40, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xac, 0xfe, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x40, 0xfe, 0xff, 0xff, 0x7c, 0xfe, 0xff, 0xff, - 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, - 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x34, - 0x00, 0x00, 0x00, 0x00, 0xb4, 0xfe, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x54, 0x32, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x0c, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xd8, 0xfe, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x31, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, - 0xc8, 0xfe, 0xff, 0xff, 0x04, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x54, 0x33, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xf6, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x28, 0xff, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, - 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x60, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x78, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xbc, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x88, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x7a, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xac, 0xff, 0xff, 0xff, - 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x31, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xca, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xc4, 0xfc, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x04, 0xfd, 0xff, 0xff, 0xe0, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe4, 0xfc, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0x2c, 0xfd, 0xff, 0xff, 0x28, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3a, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x50, 0xfd, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x54, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x38, 0xfd, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x78, 0xfd, 0xff, 0xff, 0xdc, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x66, 0xfd, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xa0, 0xfd, 0xff, 0xff, 0x9c, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, + 0xc4, 0xfd, 0xff, 0xff, 0x90, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb2, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xec, 0xfd, 0xff, 0xff, 0xe8, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x33, 0x00, 0x10, 0xfe, 0xff, 0xff, + 0x44, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xfe, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x38, 0xfe, 0xff, 0xff, + 0x34, 0xfe, 0xff, 0xff, 0x48, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x10, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x50, 0xfe, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x00, + 0x74, 0xfe, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x62, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x9c, 0xfe, 0xff, 0xff, 0x98, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, + 0x72, 0x3a, 0x31, 0x39, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xfe, 0xff, 0xff, 0x50, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xba, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xb4, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xf4, 0xfe, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd4, 0xfe, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0xff, 0xff, 0xff, 0x18, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x33, 0x00, 0x40, 0xff, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x36, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x70, 0xff, 0xff, 0xff, 0x6c, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x61, 0x78, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x54, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x94, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, + 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, + 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x31, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x54, 0x32, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, }; // clang-format on diff --git a/onnxruntime/core/session/inference_session.cc b/onnxruntime/core/session/inference_session.cc index 5b5b38176d357..4f8024e0f87c9 100644 --- a/onnxruntime/core/session/inference_session.cc +++ b/onnxruntime/core/session/inference_session.cc @@ -1052,6 +1052,14 @@ common::Status InferenceSession::SaveToOrtFormat(const std::filesystem::path& fi ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); } +#if !defined(ORT_MINIMAL_BUILD) || defined(ORT_EXTENDED_MINIMAL_BUILD) + // Persist resolver entries for ops that layout transformation may add so newly saved ORT models do not + // rely on load-time aliasing to resolve their kernel type strings. + ORT_RETURN_IF_ERROR( + kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver( + kernel_type_str_resolver)); +#endif + ORT_RETURN_IF_ERROR( kernel_type_str_resolver.SaveToOrtFormat(builder, fbs_kernel_type_str_resolver)); diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index e9cd79f53870d..4c3ede49c6125 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -12,8 +12,13 @@ #include "core/graph/constants.h" #include "core/graph/model.h" #include "core/graph/schema_registry.h" +#include "core/session/onnxruntime_session_options_config_keys.h" #include "test/test_environment.h" #include "test/util/include/asserts.h" +#include "test/util/include/inference_session_wrapper.h" + +#include +#include namespace onnxruntime::test { @@ -56,15 +61,11 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv } #if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) -TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { - SchemaRegistryManager schema_registry; - const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); - ASSERT_NE(fused_conv_schema, nullptr); - +TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { KernelTypeStrResolver resolver; - ASSERT_STATUS_OK(resolver.RegisterOpSchema(*fused_conv_schema)); + ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); - Model model("nhwc_fused_conv_resolver_test", false, DefaultLoggingManager().DefaultLogger()); + Model model("nhwc_fused_conv_layout_transform_resolver_test", false, DefaultLoggingManager().DefaultLogger()); auto& graph = model.MainGraph(); ONNX_NAMESPACE::TypeProto float_tensor; @@ -85,11 +86,37 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { ASSERT_FALSE(resolved_args.empty()); } -TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { +TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) { + const auto ort_model_path = std::filesystem::temp_directory_path() / "nhwc_fused_conv_resolver_test.ort"; + std::error_code remove_ec; + std::filesystem::remove(ort_model_path, remove_ec); + + SessionOptions so; + so.optimized_model_filepath = ort_model_path.native(); + ASSERT_STATUS_OK(so.config_options.AddConfigEntry(kOrtSessionOptionsConfigSaveModelFormat, "ORT")); + + InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.Load(ORT_TSTR("testdata/mnist.onnx"))); + ASSERT_STATUS_OK(session.Initialize()); + + std::ifstream ort_model_stream(ort_model_path, std::ios::in | std::ios::binary); + ASSERT_TRUE(ort_model_stream.good()); + const std::string ort_model_data((std::istreambuf_iterator(ort_model_stream)), + std::istreambuf_iterator()); + ort_model_stream.close(); + ASSERT_FALSE(ort_model_data.empty()); + + flatbuffers::Verifier verifier(reinterpret_cast(ort_model_data.data()), ort_model_data.size()); + ASSERT_TRUE(fbs::VerifyInferenceSessionBuffer(verifier)); + + const auto* fbs_session = fbs::GetInferenceSession(ort_model_data.data()); + ASSERT_NE(fbs_session, nullptr); + ASSERT_NE(fbs_session->kernel_type_str_resolver(), nullptr); + KernelTypeStrResolver resolver; - ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); + ASSERT_STATUS_OK(resolver.LoadFromOrtFormat(*fbs_session->kernel_type_str_resolver())); - Model model("nhwc_fused_conv_layout_transform_resolver_test", false, DefaultLoggingManager().DefaultLogger()); + Model model("nhwc_fused_conv_saved_ort_model_resolver_test", false, DefaultLoggingManager().DefaultLogger()); auto& graph = model.MainGraph(); ONNX_NAMESPACE::TypeProto float_tensor; @@ -108,6 +135,8 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio gsl::span resolved_args; ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); + + std::filesystem::remove(ort_model_path, remove_ec); } #endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) From f8b85ad4bb2798bf91f97c9e6e5af78dbf7ce99e Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 15:30:04 +0100 Subject: [PATCH 047/140] Remove unneeded comment Signed-off-by: Orlaith Monahan --- onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc index 5a57a58360ddf..beb020eab6848 100644 --- a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc +++ b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc @@ -388,7 +388,6 @@ ONNX_MS_OPERATOR_SET_SCHEMA(NhwcFusedConv, 1, OpSchema() .SetDoc(R"DOC( NhwcFusedConv is a Conv operator with optional activation and add operators fused in. -Only has fp16 implementation as of 2023/04/15. )DOC") .Attr("auto_pad", "", AttributeProto::STRING, std::string("NOTSET")) .Attr("kernel_shape", "", AttributeProto::INTS, OPTIONAL_VALUE) From 7d08d0315859c7ae5ab118d72437d9ee7066724a Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 15:38:49 +0100 Subject: [PATCH 048/140] Added descriptions to the nhwc_schema_defs for the nhwcfusedconv operator Signed-off-by: Orlaith Monahan --- .../graph/contrib_ops/nhwc_schema_defs.cc | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc index beb020eab6848..cb015a3a3c500 100644 --- a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc +++ b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc @@ -397,11 +397,26 @@ NhwcFusedConv is a Conv operator with optional activation and add operators fuse .Attr("group", "", AttributeProto::INT, static_cast(1)) .Attr("activation", "", AttributeProto::STRING, OPTIONAL_VALUE) .Attr("activation_params", "", AttributeProto::FLOATS, OPTIONAL_VALUE) - .Input(0, "X", "", "T") - .Input(1, "W", "", "T") - .Input(2, "B", "", "T", OpSchema::Optional) - .Input(3, "Z", "Tensor to be added to the output, must be the same shape and format as the output tensor.", "T", OpSchema::Optional) - .Output(0, "Y", "", "T") + .Input(0, "X", + "Input activation tensor in channels-last layout. For 2D convolution this is " + "[N, H, W, C], where N is batch size, H/W are spatial dimensions, and C is " + "the number of input channels.", + "T") + .Input(1, "W", + "Convolution weight tensor in the standard ONNX Conv filter layout " + "[M, C/group, kH, kW], where M is the number of output channels.", + "T") + .Input(2, "B", + "Optional 1D bias tensor of shape [M].", + "T", OpSchema::Optional) + .Input(3, "Z", + "Optional residual/add tensor in the same channels-last layout and shape as " + "the output tensor Y. For 2D convolution this is [N, out_H, out_W, M].", + "T", OpSchema::Optional) + .Output(0, "Y", + "Output tensor in channels-last layout. For 2D convolution this is " + "[N, out_H, out_W, M], where M is the number of output channels.", + "T") .TypeConstraint("T", {"tensor(float16)", "tensor(float)"}, "Constrain input and output types to float tensors") .TypeAndShapeInferenceFunction([](InferenceContext& ctx) { ONNX_NAMESPACE::propagateElemTypeFromInputToOutput(ctx, 0, 0); From 9f7d63196f3014fbc6e1e3e5d0657fd5e1dd9734 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 15:53:08 +0100 Subject: [PATCH 049/140] Fix for CoPilot issue to prevent dividing by 0 when strides or dilations are 0 Signed-off-by: Orlaith Monahan --- onnxruntime/core/optimizer/nhwc_transformer.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 61d951f3f0625..6c0717865b135 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -45,13 +45,13 @@ bool TryGetDimValueAsSizeT(const ONNX_NAMESPACE::TensorShapeProto& shape, int in return true; } -bool TryReadPositiveOrZeroInts(const std::vector& values, std::array& out) { +bool TryReadPositiveInts(const std::vector& values, std::array& out) { if (values.size() != out.size()) { return false; } for (size_t i = 0; i < out.size(); ++i) { - if (values[i] < 0) { + if (values[i] <= 0) { return false; } @@ -107,6 +107,12 @@ bool TryComputeFloatNhwcPads(const api::NodeRef& node, const std::array& strides, const std::array& dilations, std::array& pads) { + for (size_t i = 0; i < 2; ++i) { + if (kernel_shape[i] == 0 || strides[i] == 0 || dilations[i] == 0) { + return false; + } + } + const auto auto_pad_value = node.GetAttributeString("auto_pad"); AutoPadType auto_pad = AutoPadType::NOTSET; if (!TryParseAutoPadType(auto_pad_value.value_or("NOTSET"), auto_pad)) { @@ -207,12 +213,12 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr } const auto dilations_opt = node.GetAttributeInts("dilations"); - if (dilations_opt.has_value() && !TryReadPositiveOrZeroInts(*dilations_opt, dilations)) { + if (dilations_opt.has_value() && !TryReadPositiveInts(*dilations_opt, dilations)) { return false; } const auto strides_opt = node.GetAttributeInts("strides"); - if (strides_opt.has_value() && !TryReadPositiveOrZeroInts(*strides_opt, strides)) { + if (strides_opt.has_value() && !TryReadPositiveInts(*strides_opt, strides)) { return false; } From fff37d96343292203d6a05b3ee00d4a08d583adf Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 15:59:54 +0100 Subject: [PATCH 050/140] Moving the channels last check earlier in conv.cc so it returns sooner if needed Signed-off-by: Orlaith Monahan --- onnxruntime/core/providers/cpu/nn/conv.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 4b6e4178f20c8..6cd01a5fb23fb 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -201,6 +201,11 @@ Status Conv::Compute(OpKernelContext* context) const { TensorShapeVector kernel_shape; ORT_RETURN_IF_ERROR(conv_attrs_.ComputeKernelShape(W->Shape(), kernel_shape)); + const size_t kernel_rank = kernel_shape.size(); + + if (channels_last_) { + ORT_RETURN_IF_NOT(kernel_rank == 2, "Conv with channels_last layout currently supports 2D kernels."); + } ConvPadVector pads(conv_attrs_.pads); if (pads.empty()) { @@ -234,13 +239,8 @@ Status Conv::Compute(OpKernelContext* context) const { auto Xdata = X->DataAsSpan(); const auto* Bdata = B != nullptr ? B->Data() : nullptr; auto Ydata = Y->MutableDataAsSpan(); - const size_t kernel_rank = kernel_shape.size(); concurrency::ThreadPool* thread_pool = context->GetOperatorThreadPool(); - if (channels_last_) { - ORT_RETURN_IF_NOT(kernel_rank == 2, "Conv with channels_last layout currently supports 2D kernels."); - } - const bool wants_channels_last = channels_last_; const bool sum_present = Sum != nullptr; std::array input_shape_size_t{}; From 9c62be6471f7f92915d04d44513b1fcf374038a1 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 09:40:13 +0100 Subject: [PATCH 051/140] Update kernel_type_str_resolver.cc whitespace change --- onnxruntime/core/framework/kernel_type_str_resolver.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index 69a482ae05bdb..043f145dadd82 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -36,6 +36,7 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } return op_it; + } Status KernelTypeStrResolver::ResolveKernelTypeStr(const Node& node, std::string_view kernel_type_str, From 3167f6547efce19202bb52b8e97a846667accc6c Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 09:43:03 +0100 Subject: [PATCH 052/140] Update kernel_type_str_resolver.cc --- onnxruntime/core/framework/kernel_type_str_resolver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index 043f145dadd82..3142f94f289b3 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -35,8 +35,8 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } } - return op_it; + return op_it; } Status KernelTypeStrResolver::ResolveKernelTypeStr(const Node& node, std::string_view kernel_type_str, From fcd3f148539e98b9582c74f8d4023dc88f4eceec Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 10:03:51 +0100 Subject: [PATCH 053/140] Update fuse_initializers_transformer_test.cc --- .../test/optimizer/fuse_initializers_transformer_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index 5b39c032353df..b1997701e132f 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -499,7 +499,7 @@ TEST(TransformerTest, FuseFp16InitializersWithGraphOutputs) { so.graph_optimization_level = TransformerLevel::MaxLevel; // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; - // Disabling ConstantFolding as it will remove the Cast node + // Disabling ConstantFolding optimizer as it will remove the Cast node // by folding it with Add node. This will not allow us to test the // scenario where Cast node is producing graph output and need to // kept untouched by FuseInitializersTransformer. From 38f9a2bca3c1ede4bff8ddf2e08668310c20c004 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 10:08:03 +0100 Subject: [PATCH 054/140] Remove the conditional ifdef from kernel_type_str_resolver_utils.cc * NhwcFusedConv should now be available in minimal builds as the resolver bytes contain it Signed-off-by: Orlaith Monahan --- .../kernel_type_str_resolver_utils.cc | 19 ------------------- .../kernel_type_str_resolver_utils.h | 7 ------- .../kernel_type_str_resolver_utils_test.cc | 4 ++-- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc index 94920f7fa4b01..27312a2f0ed37 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc @@ -9,8 +9,6 @@ #include "core/common/common.h" #include "core/flatbuffers/schema/ort.fbs.h" -#include "core/graph/schema_registry.h" -#include "core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h" namespace onnxruntime::kernel_type_str_resolver_utils { @@ -18,10 +16,6 @@ static constexpr auto* kStandaloneKernelTypeStrResolverFileIdentifier = "ktsr"; #if !defined(ORT_MINIMAL_BUILD) -gsl::span GetLayoutTransformationRequiredOpIdentifiers() { - return kLayoutTransformationPotentiallyAddedOps; -} - Status SaveKernelTypeStrResolverToBuffer(const KernelTypeStrResolver& kernel_type_str_resolver, flatbuffers::DetachedBuffer& buffer, gsl::span& buffer_span) { flatbuffers::FlatBufferBuilder builder; @@ -46,18 +40,6 @@ Status LoadKernelTypeStrResolverFromBuffer(KernelTypeStrResolver& kernel_type_st } Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrResolver& kernel_type_str_resolver) { -#if !defined(ORT_MINIMAL_BUILD) - const auto required_op_ids = GetLayoutTransformationRequiredOpIdentifiers(); - const auto schema_registry = SchemaRegistryManager{}; - for (const auto& op_id : required_op_ids) { - const auto* op_schema = schema_registry.GetSchema(std::string{op_id.op_type}, op_id.since_version, - std::string{op_id.domain}); - ORT_RETURN_IF(op_schema == nullptr, "Failed to get op schema."); - ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); - } - - return Status::OK(); -#else KernelTypeStrResolver resolver_with_required_ops{}; // to generate kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes, run the test: @@ -444,7 +426,6 @@ Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrRe kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes)); kernel_type_str_resolver.Merge(std::move(resolver_with_required_ops)); return Status::OK(); -#endif } } // namespace onnxruntime::kernel_type_str_resolver_utils diff --git a/onnxruntime/core/framework/kernel_type_str_resolver_utils.h b/onnxruntime/core/framework/kernel_type_str_resolver_utils.h index 5daab7c1159be..488970890a1a0 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver_utils.h +++ b/onnxruntime/core/framework/kernel_type_str_resolver_utils.h @@ -8,8 +8,6 @@ #include #include "core/common/status.h" #include "core/framework/kernel_type_str_resolver.h" -#include "core/graph/op_identifier.h" - namespace flatbuffers { class DetachedBuffer; } @@ -18,11 +16,6 @@ namespace onnxruntime::kernel_type_str_resolver_utils { #if !defined(ORT_MINIMAL_BUILD) -/** - * Gets the ops that the layout transformation may potentially add. - */ -gsl::span GetLayoutTransformationRequiredOpIdentifiers(); - /** * Saves `kernel_type_str_resolver` to a byte buffer owned by `buffer` and referenced by `buffer_span`. */ diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 4c3ede49c6125..97cec9288d3cb 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -12,6 +12,7 @@ #include "core/graph/constants.h" #include "core/graph/model.h" #include "core/graph/schema_registry.h" +#include "core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h" #include "core/session/onnxruntime_session_options_config_keys.h" #include "test/test_environment.h" #include "test/util/include/asserts.h" @@ -23,9 +24,8 @@ namespace onnxruntime::test { static Status LoadLayoutTransformationRequiredOpsFromOpSchemas(KernelTypeStrResolver& kernel_type_str_resolver) { - const auto required_op_ids = kernel_type_str_resolver_utils::GetLayoutTransformationRequiredOpIdentifiers(); const auto schema_registry = SchemaRegistryManager{}; - for (const auto& op_id : required_op_ids) { + for (const auto& op_id : kLayoutTransformationPotentiallyAddedOps) { const auto* op_schema = schema_registry.GetSchema(std::string{op_id.op_type}, op_id.since_version, std::string{op_id.domain}); ORT_RETURN_IF(op_schema == nullptr, From 057f493b1174a5dd61cd4b2139684e483e470774 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 11:13:59 +0100 Subject: [PATCH 055/140] Update the hashing in convolve_kleidi to reduce the aliasing risk * RHS hashing now uses the full tensor to ensure uniqueness * LHS no longer uses hashing as it's unnecessary Signed-off-by: Orlaith Monahan --- .../mlas/lib/kleidiai/convolve_kleidiai.cpp | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp index b4dcc65cd7eb4..57e2c61a96682 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp +++ b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp @@ -48,34 +48,41 @@ struct LhsCacheKey { size_t padding, sh, sw; size_t kh, kw; size_t dilationh, dilationw; - size_t data_hash; bool operator==(const LhsCacheKey& other) const { return ci == other.ci && ih == other.ih && iw == other.iw && padding == other.padding && sh == other.sh && sw == other.sw && kh == other.kh && kw == other.kw && - dilationh == other.dilationh && dilationw == other.dilationw && - data_hash == other.data_hash; + dilationh == other.dilationh && dilationw == other.dilationw; } }; -// Derived from 2^32 * (sqrt(5) - 1) / 2 ≈ 0.6180339887 (reciprocal of the golden ratio) -// Based on Knuth's multiplicative hashing method -constexpr size_t HASH_GOLDEN_RATIO_CONST = 0x9e3779b9; +constexpr size_t kFnvOffsetBasis = sizeof(size_t) == 8 + ? 14695981039346656037ull + : 2166136261u; +constexpr size_t kFnvPrime = sizeof(size_t) == 8 + ? 1099511628211ull + : 16777619u; -size_t HashTensorPrefix(const float* data, size_t element_count, size_t prefix_count = 16) { - if (data == nullptr || element_count == 0 || prefix_count == 0) { +size_t HashBytes(const void* data, size_t byte_count) { + if (data == nullptr || byte_count == 0) { return 0; } - const size_t count = std::min(element_count, prefix_count); - size_t h = 0; - for (size_t i = 0; i < count; ++i) { - h ^= std::hash()(data[i]) + HASH_GOLDEN_RATIO_CONST + (h << 6) + (h >> 2); + const auto* bytes = static_cast(data); + size_t h = kFnvOffsetBasis; + for (size_t i = 0; i < byte_count; ++i) { + h ^= static_cast(bytes[i]); + h *= kFnvPrime; } + return h; } +size_t HashTensorContents(const float* data, size_t element_count) { + return HashBytes(data, element_count * sizeof(float)); +} + namespace std { // Specialize hash type for cache keys and do it within namespace std. // Doing this allows standard containers like std::unordered_map to find @@ -99,17 +106,16 @@ namespace std { template<> struct hash { size_t operator()(const LhsCacheKey& k) const { - return k.data_hash ^ - (std::hash()(k.ci) << 1) ^ - (std::hash()(k.ih) << 2) ^ - (std::hash()(k.iw) << 3) ^ - (std::hash()(k.padding) << 4) ^ - (std::hash()(k.sh) << 5) ^ - (std::hash()(k.sw) << 6) ^ - (std::hash()(k.kh) << 7) ^ - (std::hash()(k.kw) << 8) ^ - (std::hash()(k.dilationh) << 9) ^ - (std::hash()(k.dilationw) << 10); + return std::hash()(k.ci) ^ + (std::hash()(k.ih) << 1) ^ + (std::hash()(k.iw) << 2) ^ + (std::hash()(k.padding) << 3) ^ + (std::hash()(k.sh) << 4) ^ + (std::hash()(k.sw) << 5) ^ + (std::hash()(k.kh) << 6) ^ + (std::hash()(k.kw) << 7) ^ + (std::hash()(k.dilationh) << 8) ^ + (std::hash()(k.dilationw) << 9); } }; @@ -345,9 +351,9 @@ static std::shared_ptr RhsPackWeightsBiasSme(const size_t co, const thread_local std::unordered_map> rhs_cache; // The packed RHS buffer includes both weights and bias, so both must participate in the cache key. - const size_t weights_hash = HashTensorPrefix(weights, co * ci * kh * kw); + const size_t weights_hash = HashTensorContents(weights, co * ci * kh * kw); const bool has_bias = bias != nullptr; - const size_t bias_hash = has_bias ? HashTensorPrefix(bias, co) : 0; + const size_t bias_hash = has_bias ? HashTensorContents(bias, co) : 0; RhsCacheKey key = { co, ci, kh, kw, dilationh, dilationw, has_bias, weights_hash, bias_hash }; auto found = rhs_cache.find(key); @@ -521,8 +527,7 @@ static std::unique_ptr LhsPackImageDataSme(const size_t ci, const s ci, ih, iw, padding, sh, sw, kh, kw, - 1, 1, - HashTensorPrefix(in, ci * ih * iw) + 1, 1 }; auto& lhs_ptrs_cache = lhs_ptrs_cache_by_pad[cur_pad_ptr]; From 7d37596541b27852d180fc12b3b138a5aef3aff5 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 17:31:16 +0100 Subject: [PATCH 056/140] Remove an unnecessary copy in conv.cc Signed-off-by: Orlaith Monahan --- onnxruntime/core/providers/cpu/nn/conv.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 6cd01a5fb23fb..e628c719a8c15 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -278,7 +278,6 @@ Status Conv::Compute(OpKernelContext* context) const { pre_sum_activation.ActivationKind = MlasIdentityActivation; } - std::vector sum_manual_buffer; const float* sum_manual_data = nullptr; float Beta = 0.0f; @@ -286,9 +285,7 @@ Status Conv::Compute(OpKernelContext* context) const { const auto& sum_shape = Sum->Shape(); ORT_RETURN_IF_NOT(Y->Shape() == sum_shape, "output and sum shape must match"); if (manual_sum) { - auto sum_span = Sum->DataAsSpan(); - sum_manual_buffer.assign(sum_span.begin(), sum_span.end()); - sum_manual_data = sum_manual_buffer.data(); + sum_manual_data = Sum->Data(); } else { auto sum_span = Sum->DataAsSpan(); if (Ydata.data() != sum_span.data()) { From 54415bff59c1e860678031b2fbd545ca490a7fc6 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 19:58:26 +0100 Subject: [PATCH 057/140] Generated Docs update Signed-off-by: Orlaith Monahan --- docs/ContribOperators.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/ContribOperators.md b/docs/ContribOperators.md index 9aa44a1600ae6..f3ed4fc87c4c2 100644 --- a/docs/ContribOperators.md +++ b/docs/ContribOperators.md @@ -3569,7 +3569,6 @@ This version of the operator has been available since version 1 of the 'com.micr ### **com.microsoft.NhwcFusedConv** NhwcFusedConv is a Conv operator with optional activation and add operators fused in. - Only has fp16 implementation as of 2023/04/15. #### Version @@ -3600,26 +3599,26 @@ This version of the operator has been available since version 1 of the 'com.micr
X : T
-
+
Input activation tensor in channels-last layout. For 2D convolution this is [N, H, W, C], where N is batch size, H/W are spatial dimensions, and C is the number of input channels.
W : T
-
+
Convolution weight tensor in the standard ONNX Conv filter layout [M, C/group, kH, kW], where M is the number of output channels.
B (optional) : T
-
+
Optional 1D bias tensor of shape [M].
Z (optional) : T
-
Tensor to be added to the output, must be the same shape and format as the output tensor.
+
Optional residual/add tensor in the same channels-last layout and shape as the output tensor Y. For 2D convolution this is [N, out_H, out_W, M].
#### Outputs
Y : T
-
+
Output tensor in channels-last layout. For 2D convolution this is [N, out_H, out_W, M], where M is the number of output channels.
#### Type Constraints
-
T : tensor(float16)
+
T : tensor(float16), tensor(float)
Constrain input and output types to float tensors
From 39edc59af79d75ad4786df22dd4680ec9b60fca9 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Fri, 1 May 2026 10:34:03 +0100 Subject: [PATCH 058/140] Removing unneeded nullptr chwck in nchwc_transformer.cc Signed-off-by: Orlaith Monahan --- onnxruntime/core/optimizer/nchwc_transformer.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/onnxruntime/core/optimizer/nchwc_transformer.cc b/onnxruntime/core/optimizer/nchwc_transformer.cc index a971a058f43b7..719cf57ef8d81 100644 --- a/onnxruntime/core/optimizer/nchwc_transformer.cc +++ b/onnxruntime/core/optimizer/nchwc_transformer.cc @@ -502,10 +502,8 @@ void NchwcTransformerImpl::TransformConv(Node& node) { nchwc_node.MutableInputDefs()[2] = nchwc_conv_B_arg; } - if (nchwc_sum_input != nullptr) { - nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; - nchwc_sum_input->remaining_original_uses_--; - } + nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; + nchwc_sum_input->remaining_original_uses_--; NchwcArgument::Shape output_shape(output_defs[0]); From b87fab41f9e264ad77c0d1d92b9264d46a1969fa Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Fri, 1 May 2026 11:20:15 +0100 Subject: [PATCH 059/140] =?UTF-8?q?Patch=20to=20improve=20hashing=20in=20c?= =?UTF-8?q?onvolve=5Fkleidi=20*=20The=20change=20replaces=20the=20unsafe?= =?UTF-8?q?=20long-lived=20thread=5Flocal=20RHS=20cache=20with=20a=20kerne?= =?UTF-8?q?l-owned=20packed-filter=20cache=20for=20NHWC=20float=20conv=20w?= =?UTF-8?q?hen=20the=20filter=20and=20optional=20bias=20are=20constant=20i?= =?UTF-8?q?nitializers.=20The=20packed=20RHS=20is=20built=20once=20per=20k?= =?UTF-8?q?ernel=20instance=20and=20then=20reused=20safely=20for=20that=20?= =?UTF-8?q?kernel=E2=80=99s=20lifetime,=20which=20avoids=20pointer/hash=20?= =?UTF-8?q?aliasing=20issues=20without=20pulling=20in=20the=20larger=20ORT?= =?UTF-8?q?=20prepack=20machinery.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/inc/mlas.h | 3 + .../mlas/lib/kleidiai/convolve_kleidiai.cpp | 168 +++++++----------- .../core/mlas/lib/kleidiai/mlasi_kleidiai.h | 24 +++ onnxruntime/core/providers/cpu/nn/conv.cc | 77 +++++++- onnxruntime/core/providers/cpu/nn/conv.h | 33 ++++ 5 files changed, 204 insertions(+), 101 deletions(-) diff --git a/onnxruntime/core/mlas/inc/mlas.h b/onnxruntime/core/mlas/inc/mlas.h index bd7f45a4e0622..8f85da5e91800 100644 --- a/onnxruntime/core/mlas/inc/mlas.h +++ b/onnxruntime/core/mlas/inc/mlas.h @@ -907,6 +907,9 @@ struct MLAS_CONV_PARAMETERS { MLAS_CONV_ALGORITHM Algorithm; ptrdiff_t ThreadCount; const MLAS_BACKEND_KERNEL_SELECTOR_CONFIG* BackendKernelSelectorConfig = nullptr; + const void* PackedFilter = nullptr; + size_t PackedFilterGroupStride = 0; + bool FilterIsPacked = false; union { struct { CBLAS_TRANSPOSE TransB; diff --git a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp index 57e2c61a96682..cca4f5a19c417 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp +++ b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp @@ -24,24 +24,6 @@ const KaiF32IMatmulKernel& imatmul_conv = GetKleidiAIF32IMatmulUKernel(); -// Right-hand-side (weights) cache key -struct RhsCacheKey { - size_t co, ci, kh, kw, dilationh, dilationw; - bool has_bias; - size_t weights_hash; - size_t bias_hash; - - bool operator==(const RhsCacheKey& other) const { - return co == other.co && ci == other.ci && - kh == other.kh && kw == other.kw && - dilationh == other.dilationh && dilationw == other.dilationw && - has_bias == other.has_bias && - weights_hash == other.weights_hash && - bias_hash == other.bias_hash; - } -}; - - // Left-hand-side (input indirection) cache key struct LhsCacheKey { size_t ci, ih, iw; @@ -57,52 +39,11 @@ struct LhsCacheKey { } }; -constexpr size_t kFnvOffsetBasis = sizeof(size_t) == 8 - ? 14695981039346656037ull - : 2166136261u; -constexpr size_t kFnvPrime = sizeof(size_t) == 8 - ? 1099511628211ull - : 16777619u; - -size_t HashBytes(const void* data, size_t byte_count) { - if (data == nullptr || byte_count == 0) { - return 0; - } - - const auto* bytes = static_cast(data); - size_t h = kFnvOffsetBasis; - for (size_t i = 0; i < byte_count; ++i) { - h ^= static_cast(bytes[i]); - h *= kFnvPrime; - } - - return h; -} - -size_t HashTensorContents(const float* data, size_t element_count) { - return HashBytes(data, element_count * sizeof(float)); -} - namespace std { // Specialize hash type for cache keys and do it within namespace std. // Doing this allows standard containers like std::unordered_map to find // the appropriate hash function via template specialization, as ADL // (argument-dependent lookup) does not apply to std::hash. - template<> - struct hash { - size_t operator()(const RhsCacheKey& k) const { - return k.weights_hash ^ - (std::hash()(k.co) << 1) ^ - (std::hash()(k.ci) << 2) ^ - (std::hash()(k.kh) << 3) ^ - (std::hash()(k.kw) << 4) ^ - (std::hash()(k.dilationh) << 5) ^ - (std::hash()(k.dilationw) << 6) ^ - (std::hash()(k.has_bias) << 7) ^ - (std::hash()(k.bias_hash) << 8); - } - }; - template<> struct hash { size_t operator()(const LhsCacheKey& k) const { @@ -341,56 +282,65 @@ static void MultiThreadedLHSPackSme(MLAS_THREADPOOL* ThreadPool, const size_t ci }); } -static std::shared_ptr RhsPackWeightsBiasSme(const size_t co, const size_t ci, - const size_t kh, const size_t kw, - const size_t dilationh, const size_t dilationw, - const float* weights, const float* bias, - MLAS_THREADPOOL* ThreadPool) -{ - // Cache of prepacked kai rhs weights and biases. thread_local to prevent interference from parallel sessions. - thread_local std::unordered_map> rhs_cache; - - // The packed RHS buffer includes both weights and bias, so both must participate in the cache key. - const size_t weights_hash = HashTensorContents(weights, co * ci * kh * kw); - const bool has_bias = bias != nullptr; - const size_t bias_hash = has_bias ? HashTensorContents(bias, co) : 0; - RhsCacheKey key = { co, ci, kh, kw, dilationh, dilationw, has_bias, weights_hash, bias_hash }; - - auto found = rhs_cache.find(key); - if (found != rhs_cache.end()) { - return found->second; - } else { - // prepare mlas filter weights for kai rhs packing - // dilated nhwc format - auto nhwc = NChwToNhwc(co, ci, kh, kw, weights, dilationh, dilationw, true, ThreadPool); - +size_t +MLASCALL +ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackWSize( + size_t FilterCount, + size_t InputChannels, + const int64_t* KernelShape, + const int64_t* DilationShape) { + const auto d_kh = ComputeKernelSize(static_cast(DilationShape[0]), static_cast(KernelShape[0])); + const auto d_kw = ComputeKernelSize(static_cast(DilationShape[1]), static_cast(KernelShape[1])); + return kai_get_rhs_packed_size_rhs_imatmul_pack_kxn_x32p2vlx1b_x32_x32_sme(FilterCount, d_kh * d_kw, + InputChannels); +} - //dilation, axis swap (n x k -> k x n) where n == co, k == d_kh x d_kw x ci - const auto d_kh = ComputeKernelSize(dilationh,kh); - const auto d_kw = ComputeKernelSize(dilationw,kw); +void +MLASCALL +ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackW( + size_t FilterCount, + size_t InputChannels, + const int64_t* KernelShape, + const int64_t* DilationShape, + size_t GroupCount, + const float* Filter, + const float* Bias, + void* PackedFilter, + size_t PackedFilterGroupStride, + MLAS_THREADPOOL* ThreadPool) { + const size_t kh = static_cast(KernelShape[0]); + const size_t kw = static_cast(KernelShape[1]); + const size_t dilationh = static_cast(DilationShape[0]); + const size_t dilationw = static_cast(DilationShape[1]); + const auto d_kh = ComputeKernelSize(dilationh, kh); + const auto d_kw = ComputeKernelSize(dilationw, kw); - //t_weights[d_kh][d_kw][ci][co] = nhwc[co][d_kh][d_kw][ci] - auto t_weights = Transpose4D({co,d_kh,d_kw,ci},&nhwc[0],{1,2,3,0}); + for (size_t group_idx = 0; group_idx < GroupCount; ++group_idx) { + const float* weights = Filter + group_idx * FilterCount * InputChannels * kh * kw; + const float* bias = Bias ? Bias + group_idx * FilterCount : nullptr; + auto* packed_group = reinterpret_cast(PackedFilter) + group_idx * PackedFilterGroupStride; - const auto packed_size = kai_get_rhs_packed_size_rhs_imatmul_pack_kxn_x32p2vlx1b_x32_x32_sme(co,d_kh*d_kw,ci); - auto packed = std::shared_ptr(new std::byte[packed_size], std::default_delete()); + // prepare mlas filter weights for kai rhs packing + // dilated nhwc format + auto nhwc = NChwToNhwc(FilterCount, InputChannels, kh, kw, weights, dilationh, dilationw, true, ThreadPool); - rhs_cache[key] = packed; + //t_weights[d_kh][d_kw][ci][co] = nhwc[co][d_kh][d_kw][ci] + auto t_weights = Transpose4D({FilterCount, d_kh, d_kw, InputChannels}, &nhwc[0], {1, 2, 3, 0}); std::vector bias_copy; if (bias) { - bias_copy.assign(bias, bias + co); + bias_copy.assign(bias, bias + FilterCount); } else { - bias_copy.resize(co, 0.0f); + bias_copy.resize(FilterCount, 0.0f); } KLEIDIAI_KERNEL_LOG("kai_run_rhs_imatmul_pack_kxn_x32p2vlx1b_x32_x32_sme" - << " N=" << co << " k_chunk_count=" << (d_kh*d_kw) << " k_chunk_length=" << ci << " rhs_stride_row=" << (co * sizeof(float))); + << " N=" << FilterCount << " k_chunk_count=" << (d_kh * d_kw) + << " k_chunk_length=" << InputChannels + << " rhs_stride_row=" << (FilterCount * sizeof(float))); kai_run_rhs_imatmul_pack_kxn_x32p2vlx1b_x32_x32_sme( - co, d_kh*d_kw, ci, co * sizeof(float), &t_weights[0], bias_copy.data(), packed.get() - ); - - return packed; + FilterCount, d_kh * d_kw, InputChannels, FilterCount * sizeof(float), &t_weights[0], bias_copy.data(), + packed_group); } } @@ -559,6 +509,8 @@ static void ConvolveSme(const size_t co, //channels out const size_t groups, //number of filter groups const float* weights, //kernel weights [co,ci,ih,iw] const float* bias, //kernel biases + const std::byte* packed_rhs, + const size_t packed_rhs_group_stride, const float* in, //in image data float* out, //out image data float* tmp_mlas_aligned, //intermediate buffer if we need to perform a transpose @@ -607,7 +559,20 @@ static void ConvolveSme(const size_t co, //channels out } auto lhs = LhsPackImageDataSme(ci, ih, iw, d_kh, d_kw, sh, sw, padding, in, input_is_channels_last, ThreadPool); - auto rhs = RhsPackWeightsBiasSme(co, ci, kh, kw, dilationh, dilationw, weights, bias, ThreadPool); + const std::byte* rhs_data = packed_rhs ? packed_rhs + g * packed_rhs_group_stride : nullptr; + std::unique_ptr rhs_storage; + if (rhs_data == nullptr) { + const std::array kernel_shape{static_cast(kh), static_cast(kw)}; + const std::array dilation_shape{static_cast(dilationh), static_cast(dilationw)}; + const size_t packed_size = + ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackWSize(co, ci, kernel_shape.data(), + dilation_shape.data()); + rhs_storage = std::make_unique(packed_size); + ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackW(co, ci, kernel_shape.data(), dilation_shape.data(), 1, + weights, bias, rhs_storage.get(), packed_size, + ThreadPool); + rhs_data = rhs_storage.get(); + } MlasTrySimpleParallel(ThreadPool, static_cast(dim[0] * dim[1] * dim[2]), [&](ptrdiff_t tid) { //compute B,M,N index from iteration index @@ -620,7 +585,7 @@ static void ConvolveSme(const size_t co, //channels out imatmul_conv.ukernel.get_rhs_packed_offset(NIdx * n_step, d_kh * d_kw, ci); auto BTile = reinterpret_cast( - reinterpret_cast(rhs.get()) + rhs_packed_offset + rhs_data + rhs_packed_offset ); // Get lhs tile, A @@ -769,7 +734,10 @@ ArmKleidiAI::MlasConv( Parameters->DilationShape[0], Parameters->DilationShape[1], // kernel dilation Parameters->Padding[0], // image padding Parameters->GroupCount, // filter groups - Filter, Bias, Input, Output, WorkingBuffer, Parameters->ChannelsLast, ThreadPool); + Filter, Bias, + reinterpret_cast(Parameters->PackedFilter), + Parameters->PackedFilterGroupStride, + Input, Output, WorkingBuffer, Parameters->ChannelsLast, ThreadPool); MlasActivation(Parameters->Activation, Output, nullptr, Parameters->FilterCount, Parameters->OutputSize, Parameters->OutputSize); diff --git a/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h b/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h index e4c3e9ef8d98b..3c9f398ece887 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h +++ b/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h @@ -200,6 +200,30 @@ MlasConv( float* Output, MLAS_THREADPOOL* ThreadPool ); + +size_t +MLASCALL +MlasConvSymmetricChannelsLast2DFloatPackWSize( + size_t FilterCount, + size_t InputChannels, + const int64_t* KernelShape, + const int64_t* DilationShape + ); + +void +MLASCALL +MlasConvSymmetricChannelsLast2DFloatPackW( + size_t FilterCount, + size_t InputChannels, + const int64_t* KernelShape, + const int64_t* DilationShape, + size_t GroupCount, + const float* Filter, + const float* Bias, + void* PackedFilter, + size_t PackedFilterGroupStride, + MLAS_THREADPOOL* ThreadPool + ); } /*++ diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index e628c719a8c15..87ce1b05caae2 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -23,6 +23,10 @@ #include "core/common/safeint.h" #include "core/util/math_cpuonly.h" +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) +#include "core/mlas/lib/kleidiai/mlasi_kleidiai.h" +#endif + namespace onnxruntime { using ConvPadVector = ConvAttributes::ConvPadVector; @@ -187,6 +191,58 @@ Status Conv::Compute(OpKernelContext* context) const { return Status::OK(); } +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) +Status Conv::EnsurePackedChannelsLastFilter(concurrency::ThreadPool* thread_pool, + size_t filter_count_per_group, + size_t input_channels_per_group, + const TensorShapeVector& kernel_shape, + const TensorShapeVector& dilations) const { + if (!can_cache_packed_filter_) { + return Status::OK(); + } + + std::call_once(packed_filter_once_, [&] { + packed_filter_status_ = Status::OK(); + + auto alloc = Info().GetAllocator(OrtMemTypeDefault); + if (alloc == nullptr) { + packed_filter_status_ = ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, + "Failed to get allocator for cached KleidiAI packed filter."); + return; + } + + packed_filter_group_stride_ = + ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackWSize(filter_count_per_group, + input_channels_per_group, + kernel_shape.data(), + dilations.data()); + if (packed_filter_group_stride_ == 0) { + packed_filter_status_ = ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, + "Failed to get KleidiAI packed filter size."); + return; + } + + const size_t packed_filter_size = + packed_filter_group_stride_ * onnxruntime::narrow(conv_attrs_.group); + packed_filter_ = IAllocator::MakeUniquePtr(alloc, packed_filter_size, true); + memset(packed_filter_.get(), 0, packed_filter_size); + + ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackW(filter_count_per_group, + input_channels_per_group, + kernel_shape.data(), + dilations.data(), + onnxruntime::narrow(conv_attrs_.group), + constant_filter_tensor_->Data(), + constant_bias_tensor_ ? constant_bias_tensor_->Data() : nullptr, + packed_filter_.get(), + packed_filter_group_stride_, + thread_pool); + }); + + return packed_filter_status_; +} +#endif + Status Conv::Compute(OpKernelContext* context) const { size_t num_inputs = OpKernel::Node().InputDefs().size(); const Tensor* X = context->Input(0); @@ -272,6 +328,17 @@ Status Conv::Compute(OpKernelContext* context) const { strides_size_t.data(), narrow(M / conv_attrs_.group), /*Beta*/ 0.0f); + +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) + if (nhwc_fastpath && can_cache_packed_filter_) { + ORT_RETURN_IF_ERROR(EnsurePackedChannelsLastFilter(thread_pool, + narrow(M / conv_attrs_.group), + narrow(C / conv_attrs_.group), + kernel_shape, + dilations)); + } +#endif + const bool manual_sum = wants_channels_last && !nhwc_fastpath && sum_present; MLAS_ACTIVATION pre_sum_activation = activation_; if (manual_sum) { @@ -296,7 +363,7 @@ Status Conv::Compute(OpKernelContext* context) const { } if (kernel_rank >= 1 && kernel_rank <= 3) { - MLAS_CONV_PARAMETERS Parameters; + MLAS_CONV_PARAMETERS Parameters{}; Parameters.BackendKernelSelectorConfig = &mlas_backend_kernel_selector_config_; size_t WorkingBufferSize; @@ -318,6 +385,14 @@ Status Conv::Compute(OpKernelContext* context) const { nhwc_fastpath ? 0.0f : Beta, thread_pool); +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) + if (nhwc_fastpath && packed_filter_ != nullptr) { + Parameters.FilterIsPacked = true; + Parameters.PackedFilter = packed_filter_.get(); + Parameters.PackedFilterGroupStride = packed_filter_group_stride_; + } +#endif + float* working_data = nullptr; BufferUniquePtr working_buffer; if (WorkingBufferSize > 0) { diff --git a/onnxruntime/core/providers/cpu/nn/conv.h b/onnxruntime/core/providers/cpu/nn/conv.h index 5633739b43e4b..1cbe417cdbd96 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.h +++ b/onnxruntime/core/providers/cpu/nn/conv.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "core/framework/op_kernel.h" #include "core/providers/cpu/nn/conv_attributes.h" #include "core/providers/cpu/mlas_backend_kernel_selector_config_utils.h" @@ -28,6 +30,20 @@ class Conv : public OpKernel { Conv(const OpKernelInfo& info) : OpKernel(info), conv_attrs_(info), channels_last_(info.GetKernelDef().OpName() == "NhwcFusedConv") { activation_.ActivationKind = MlasIdentityActivation; SetupMlasBackendKernelSelectorFromConfigOptions(mlas_backend_kernel_selector_config_, info.GetConfigOptions()); + +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) + if (channels_last_) { + const auto& input_defs = info.node().InputDefs(); + const bool has_bias_input = input_defs.size() >= 3 && input_defs[2] != nullptr; + info.TryGetConstantInput(1, &constant_filter_tensor_); + if (has_bias_input) { + info.TryGetConstantInput(2, &constant_bias_tensor_); + } + + can_cache_packed_filter_ = + constant_filter_tensor_ != nullptr && (!has_bias_input || constant_bias_tensor_ != nullptr); + } +#endif } Status Compute(OpKernelContext* context) const override; @@ -39,6 +55,23 @@ class Conv : public OpKernel { ConvAttributes conv_attrs_; bool channels_last_{false}; + +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) + private: + Status EnsurePackedChannelsLastFilter(concurrency::ThreadPool* thread_pool, + size_t filter_count_per_group, + size_t input_channels_per_group, + const TensorShapeVector& kernel_shape, + const TensorShapeVector& dilations) const; + + const Tensor* constant_filter_tensor_{nullptr}; + const Tensor* constant_bias_tensor_{nullptr}; + bool can_cache_packed_filter_{false}; + mutable std::once_flag packed_filter_once_; + mutable Status packed_filter_status_; + mutable IAllocatorUniquePtr packed_filter_; + mutable size_t packed_filter_group_stride_{0}; +#endif }; } // namespace onnxruntime From 6fbae2f335986a121e3f1437bc8bbc8fa4d76c77 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 09:04:36 +0100 Subject: [PATCH 060/140] Revert "Removing unneeded nullptr chwck in nchwc_transformer.cc" This reverts commit 39edc59af79d75ad4786df22dd4680ec9b60fca9. --- onnxruntime/core/optimizer/nchwc_transformer.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/onnxruntime/core/optimizer/nchwc_transformer.cc b/onnxruntime/core/optimizer/nchwc_transformer.cc index 719cf57ef8d81..a971a058f43b7 100644 --- a/onnxruntime/core/optimizer/nchwc_transformer.cc +++ b/onnxruntime/core/optimizer/nchwc_transformer.cc @@ -502,8 +502,10 @@ void NchwcTransformerImpl::TransformConv(Node& node) { nchwc_node.MutableInputDefs()[2] = nchwc_conv_B_arg; } - nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; - nchwc_sum_input->remaining_original_uses_--; + if (nchwc_sum_input != nullptr) { + nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; + nchwc_sum_input->remaining_original_uses_--; + } NchwcArgument::Shape output_shape(output_defs[0]); From 4f67ba56f6732d84c80156f78c1af406f07b3b85 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 10:28:32 +0100 Subject: [PATCH 061/140] Removing unnecessary add ops from inference_session.cc Signed-off-by: Orlaith Monahan --- onnxruntime/core/session/inference_session.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/onnxruntime/core/session/inference_session.cc b/onnxruntime/core/session/inference_session.cc index c48c933dee426..f6f0d5d80b9e9 100644 --- a/onnxruntime/core/session/inference_session.cc +++ b/onnxruntime/core/session/inference_session.cc @@ -1052,14 +1052,6 @@ common::Status InferenceSession::SaveToOrtFormat(const std::filesystem::path& fi ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); } -#if !defined(ORT_MINIMAL_BUILD) || defined(ORT_EXTENDED_MINIMAL_BUILD) - // Persist resolver entries for ops that layout transformation may add so newly saved ORT models do not - // rely on load-time aliasing to resolve their kernel type strings. - ORT_RETURN_IF_ERROR( - kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver( - kernel_type_str_resolver)); -#endif - ORT_RETURN_IF_ERROR( kernel_type_str_resolver.SaveToOrtFormat(builder, fbs_kernel_type_str_resolver)); From d5f98ea003a9aa7c1114e3723224f010d45201d1 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 11:31:06 +0100 Subject: [PATCH 062/140] Removing USE_KLEIDIAI from the ResolveNhwcFusedConvFromLayoutTransformationRequiredOps test Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 97cec9288d3cb..f455bd1a5163d 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -60,7 +60,7 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } -#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { KernelTypeStrResolver resolver; ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); @@ -138,7 +138,7 @@ TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) std::filesystem::remove(ort_model_path, remove_ec); } -#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in From 0a5e524ea806eda5f3b6374f24d5304f5dab0cd0 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 12:04:59 +0100 Subject: [PATCH 063/140] Change SavedOrtModelResolverContainsNhwcFusedConv to LoadedOrtModelResolverCanResolveNhwcFusedConv and have it check load time instead of the saved model Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index f455bd1a5163d..96f0ff182f2f5 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -86,7 +86,7 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio ASSERT_FALSE(resolved_args.empty()); } -TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) { +TEST(KernelTypeStrResolverUtilsTest, LoadedOrtModelResolverCanResolveNhwcFusedConv) { const auto ort_model_path = std::filesystem::temp_directory_path() / "nhwc_fused_conv_resolver_test.ort"; std::error_code remove_ec; std::filesystem::remove(ort_model_path, remove_ec); @@ -99,6 +99,10 @@ TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) ASSERT_STATUS_OK(session.Load(ORT_TSTR("testdata/mnist.onnx"))); ASSERT_STATUS_OK(session.Initialize()); + InferenceSessionWrapper loaded_session{SessionOptions{}, GetEnvironment()}; + ASSERT_STATUS_OK(loaded_session.Load(ort_model_path.native())); + ASSERT_STATUS_OK(loaded_session.Initialize()); + std::ifstream ort_model_stream(ort_model_path, std::ios::in | std::ios::binary); ASSERT_TRUE(ort_model_stream.good()); const std::string ort_model_data((std::istreambuf_iterator(ort_model_stream)), @@ -115,6 +119,8 @@ TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) KernelTypeStrResolver resolver; ASSERT_STATUS_OK(resolver.LoadFromOrtFormat(*fbs_session->kernel_type_str_resolver())); + ASSERT_STATUS_OK( + kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); Model model("nhwc_fused_conv_saved_ort_model_resolver_test", false, DefaultLoggingManager().DefaultLogger()); auto& graph = model.MainGraph(); From 8acb10a28ed87c70d6106fe579f3ca8716ecb5ae Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 20:36:27 +0100 Subject: [PATCH 064/140] Remove LoadedOrtModelResolverCanResolveNhwcFusedConv as it's no longer needed Signed-off-by: Orlaith Monahan --- .../kernel_type_str_resolver_utils_test.cc | 60 ------------------- 1 file changed, 60 deletions(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 96f0ff182f2f5..306b4494a7821 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -86,66 +86,6 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio ASSERT_FALSE(resolved_args.empty()); } -TEST(KernelTypeStrResolverUtilsTest, LoadedOrtModelResolverCanResolveNhwcFusedConv) { - const auto ort_model_path = std::filesystem::temp_directory_path() / "nhwc_fused_conv_resolver_test.ort"; - std::error_code remove_ec; - std::filesystem::remove(ort_model_path, remove_ec); - - SessionOptions so; - so.optimized_model_filepath = ort_model_path.native(); - ASSERT_STATUS_OK(so.config_options.AddConfigEntry(kOrtSessionOptionsConfigSaveModelFormat, "ORT")); - - InferenceSessionWrapper session{so, GetEnvironment()}; - ASSERT_STATUS_OK(session.Load(ORT_TSTR("testdata/mnist.onnx"))); - ASSERT_STATUS_OK(session.Initialize()); - - InferenceSessionWrapper loaded_session{SessionOptions{}, GetEnvironment()}; - ASSERT_STATUS_OK(loaded_session.Load(ort_model_path.native())); - ASSERT_STATUS_OK(loaded_session.Initialize()); - - std::ifstream ort_model_stream(ort_model_path, std::ios::in | std::ios::binary); - ASSERT_TRUE(ort_model_stream.good()); - const std::string ort_model_data((std::istreambuf_iterator(ort_model_stream)), - std::istreambuf_iterator()); - ort_model_stream.close(); - ASSERT_FALSE(ort_model_data.empty()); - - flatbuffers::Verifier verifier(reinterpret_cast(ort_model_data.data()), ort_model_data.size()); - ASSERT_TRUE(fbs::VerifyInferenceSessionBuffer(verifier)); - - const auto* fbs_session = fbs::GetInferenceSession(ort_model_data.data()); - ASSERT_NE(fbs_session, nullptr); - ASSERT_NE(fbs_session->kernel_type_str_resolver(), nullptr); - - KernelTypeStrResolver resolver; - ASSERT_STATUS_OK(resolver.LoadFromOrtFormat(*fbs_session->kernel_type_str_resolver())); - ASSERT_STATUS_OK( - kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); - - Model model("nhwc_fused_conv_saved_ort_model_resolver_test", false, DefaultLoggingManager().DefaultLogger()); - auto& graph = model.MainGraph(); - - ONNX_NAMESPACE::TypeProto float_tensor; - auto* tensor_type = float_tensor.mutable_tensor_type(); - tensor_type->set_elem_type(ONNX_NAMESPACE::TensorProto_DataType_FLOAT); - tensor_type->mutable_shape()->add_dim()->set_dim_value(1); - - auto& x = graph.GetOrCreateNodeArg("x", &float_tensor); - auto& w = graph.GetOrCreateNodeArg("w", &float_tensor); - auto& y = graph.GetOrCreateNodeArg("y", &float_tensor); - - auto& nhwc_fused_conv = graph.AddNode( - "nhwc_fused_conv", "NhwcFusedConv", "test node", {&x, &w}, {&y}, nullptr, kMSDomain); - nhwc_fused_conv.SetSinceVersion(1); - - gsl::span resolved_args; - ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); - ASSERT_FALSE(resolved_args.empty()); - - std::filesystem::remove(ort_model_path, remove_ec); -} -#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) - // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in // onnxruntime/core/framework/kernel_type_str_resolver_utils.cc From 47bb37d6f98a7434841cc396c3a730862450f416 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 7 May 2026 11:00:23 +0100 Subject: [PATCH 065/140] Add a mising trailing endif to kernel_type_str_resolver.cc Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 306b4494a7821..92efdbd9fb972 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -117,4 +117,6 @@ TEST(KernelTypeStrResolverUtilsTest, DISABLED_PrintExpectedLayoutTransformationR #endif // defined(DISABLE_CONTRIB_OPS) } +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) + } // namespace onnxruntime::test From df15c7dd54529ad868ce3eeeb27328645e55d754 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 7 May 2026 16:40:38 +0100 Subject: [PATCH 066/140] Move endif in kernel_type_str_resolver_tests.cc to only exclude needed tests Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 92efdbd9fb972..9c1f8dbfcb2a3 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -86,6 +86,8 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio ASSERT_FALSE(resolved_args.empty()); } +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) + // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in // onnxruntime/core/framework/kernel_type_str_resolver_utils.cc @@ -117,6 +119,5 @@ TEST(KernelTypeStrResolverUtilsTest, DISABLED_PrintExpectedLayoutTransformationR #endif // defined(DISABLE_CONTRIB_OPS) } -#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) } // namespace onnxruntime::test From f8fd4b2d30ee84104e84870318f46e8f1b9ac998 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 7 May 2026 18:40:10 +0100 Subject: [PATCH 067/140] Update kernel_type_str_resolver_utils_test.cc Remove extraneous whitespace --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 9c1f8dbfcb2a3..32720e42988f6 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -119,5 +119,4 @@ TEST(KernelTypeStrResolverUtilsTest, DISABLED_PrintExpectedLayoutTransformationR #endif // defined(DISABLE_CONTRIB_OPS) } - } // namespace onnxruntime::test From 90e5c770f33a73eb32bbc17f4344bc844760d84b Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 11 May 2026 10:57:59 +0100 Subject: [PATCH 068/140] Regen OperatorKernels.md Signed-off-by: Orlaith Monahan --- docs/OperatorKernels.md | 962 ---------------------------------------- 1 file changed, 962 deletions(-) diff --git a/docs/OperatorKernels.md b/docs/OperatorKernels.md index 7596ab7592b25..a3ec3f522de54 100644 --- a/docs/OperatorKernels.md +++ b/docs/OperatorKernels.md @@ -13,8 +13,6 @@ The **OpSet Version** column uses the following notation: ## Execution Providers - [CPUExecutionProvider](#cpuexecutionprovider) -- [CUDAExecutionProvider](#cudaexecutionprovider) -- [DmlExecutionProvider](#dmlexecutionprovider) --------------- @@ -636,963 +634,3 @@ The **OpSet Version** column uses the following notation: |WordConvEmbedding|*in* Sequence:**T**
*in* W:**T1**
*in* B:**T1**
*in* C:**T1**
*out* Y:**T1**|1+|**T** = tensor(int32)
**T1** = tensor(float)| | | | | -|**Operator Domain:** *com.microsoft.nchwc*|||| -|AveragePool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|Conv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*in* Sum:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|GlobalAveragePool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|GlobalMaxPool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|MaxPool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|ReorderInput|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|ReorderOutput|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|Upsample|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -| | -| | - - - - -## Operators implemented by CUDAExecutionProvider - -| Op Name | Parameters | OpSet Version | Types Supported | -|---------|------------|---------------|-----------------| -|**Operator Domain:** *ai.onnx*|||| -|Abs|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Add|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Affine|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|And|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)
**T1** = tensor(bool)| -|ArgMax|*in* data:**T**
*out* reduced:**tensor(int64)**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||12|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 11]|**T** = tensor(double), tensor(float), tensor(float16)| -|ArgMin|*in* data:**T**
*out* reduced:**tensor(int64)**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||12|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 11]|**T** = tensor(double), tensor(float), tensor(float16)| -|Attention|*in* Q:**T1**
*in* K:**T1**
*in* V:**T2**
*in* attn_mask:**U**
*in* past_key:**T1**
*in* past_value:**T2**
*in* nonpad_kv_seqlen:**tensor(int64)**
*out* Y:**T1**
*out* present_key:**T1**
*out* present_value:**T2**
*out* qk_matmul_output:**T1**

or

*in* Q:**T1**
*in* K:**T1**
*in* V:**T2**
*in* attn_mask:**U**
*in* past_key:**T1**
*in* past_value:**T2**
*out* Y:**T1**
*out* present_key:**T1**
*out* present_value:**T2**
*out* qk_matmul_output:**T1**|24+|**T1** = tensor(bfloat16), tensor(float), tensor(float16)
**T2** = tensor(bfloat16), tensor(float), tensor(float16)
**U** = tensor(bfloat16), tensor(bool), tensor(float), tensor(float16)| -|||23|**T1** = tensor(bfloat16), tensor(float), tensor(float16)
**T2** = tensor(bfloat16), tensor(float), tensor(float16)
**U** = tensor(bfloat16), tensor(bool), tensor(float), tensor(float16)| -|AveragePool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[19, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 18]|**T** = tensor(double), tensor(float), tensor(float16)| -|||10|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 9]|**T** = tensor(double), tensor(float), tensor(float16)| -|BatchNormalization|*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* input_mean:**U**
*in* input_var:**U**
*out* Y:**T**
*out* running_mean:**U**
*out* running_var:**U**

or

*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* mean:**T**
*in* var:**T**
*out* Y:**T**
*out* mean:**T**
*out* var:**T**
*out* saved_mean:**T**
*out* saved_var:**T**

or

*in* X:**T**
*in* scale:**T1**
*in* B:**T1**
*in* input_mean:**T2**
*in* input_var:**T2**
*out* Y:**T**
*out* running_mean:**T2**
*out* running_var:**T2**|15+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(double), tensor(float), tensor(float16)| -|||14|**T** = tensor(double), tensor(float), tensor(float16)
**U** = tensor(double), tensor(float), tensor(float16)| -|||[9, 13]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|Cast|*in* input:**T1**
*out* output:**T2**|25+|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 18]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[9, 12]|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[6, 8]|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Ceil|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Clip|*in* input:**T**
*in* min:**T**
*in* max:**T**
*out* output:**T**

or

*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int64), tensor(int8), tensor(uint64), tensor(uint8)| -|||12|**T** = tensor(double), tensor(float), tensor(float16), tensor(int64), tensor(int8), tensor(uint64), tensor(uint8)| -|||11|**T** = tensor(float)| -|||[6, 10]|**T** = tensor(float)| -|Compress|*in* input:**T**
*in* condition:**T1**
*out* output:**T**|11+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||[9, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|Concat|*in* inputs:**T**
*out* concat_result:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[4, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ConcatFromSequence|*in* input_sequence:**S**
*out* concat_result:**T**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|ConstantOfShape|*in* input:**T1**
*out* output:**T2**|25+|**T1** = tensor(int64)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**T1** = tensor(int64)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T1** = tensor(int64)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[9, 20]|**T1** = tensor(int64)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Conv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[11, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|ConvTranspose|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|11+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|Cos|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(double), tensor(float), tensor(float16)| -|Crop|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|CumSum|*in* x:**T**
*in* axis:**T2**
*out* y:**T**|14+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T2** = tensor(int32), tensor(int64)| -|||[11, 13]|**T** = tensor(double), tensor(float), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T2** = tensor(int32), tensor(int64)| -|DeformConv|*in* X:**T**
*in* W:**T**
*in* offset:**T**
*in* B:**T**
*in* mask:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[19, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|DepthToSpace|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|DequantizeLinear|*in* x:**T**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T**
*out* y:**tensor(float)**

or

*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T2**

or

*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T3**|25+|**T1** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)
**T2** = tensor(float), tensor(float16)
**T3** = tensor(float), tensor(float16)| -|||[23, 24]|**T1** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)
**T2** = tensor(float), tensor(float16)
**T3** = tensor(float), tensor(float16)| -|||[21, 22]|**T1** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||[19, 20]|**T1** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||[13, 18]|**T** = tensor(int8), tensor(uint8)| -|||[10, 12]|**T** = tensor(int8), tensor(uint8)| -|Div|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Dropout|*in* data:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T2**

or

*in* data:**T**
*out* output:**T**
*out* mask:**T**

or

*in* data:**T**
*out* output:**T**
*out* mask:**T1**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||12|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||[10, 11]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bool)| -|||[7, 9]|**T** = tensor(double), tensor(float), tensor(float16)| -|DynamicSlice|*in* data:**T**
*in* starts:**Tind**
*in* ends:**Tind**
*in* axes:**Tind**
*out* output:**T**|1+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|Einsum|*in* Inputs:**T**
*out* Output:**T**|12+|**T** = tensor(double), tensor(float), tensor(float16)| -|Elu|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(double), tensor(float), tensor(float16)| -|Equal|*in* A:**T**
*in* B:**T**
*out* C:**T1**|19+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[13, 18]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[11, 12]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 10]|**T** = tensor(bool), tensor(int32), tensor(int64)| -|Erf|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[9, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Exp|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Expand|*in* input:**T**
*in* shape:**tensor(int64)**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[8, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|EyeLike|*in* input:**T1**
*out* output:**T2**|9+|**T1** = tensor(double), tensor(float), tensor(int32), tensor(int64), tensor(uint64)
**T2** = tensor(double), tensor(float), tensor(int32), tensor(int64), tensor(uint64)| -|Flatten|*in* input:**T**
*out* output:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[9, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 8]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Floor|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|GRU|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*out* Y:**T**
*out* Y_h:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[14, 21]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[7, 13]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|Gather|*in* data:**T**
*in* indices:**Tind**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[1, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|GatherElements|*in* data:**T**
*in* indices:**Tind**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|GatherND|*in* data:**T**
*in* indices:**tensor(int64)**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int64)
**indices** = tensor(int64)| -|||12|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int64)
**indices** = tensor(int64)| -|||11|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int64)
**indices** = tensor(int64)| -|Gelu|*in* X:**T**
*out* Y:**T**|20+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Gemm|*in* A:**T**
*in* B:**T**
*in* C:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[9, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|GlobalAveragePool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|GlobalMaxPool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|Greater|*in* A:**T**
*in* B:**T**
*out* C:**T1**|13+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[9, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|GreaterOrEqual|*in* A:**T**
*in* B:**T**
*out* C:**T1**|16+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[12, 15]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|GridSample|*in* X:**T1**
*in* grid:**T2**
*out* Y:**T1**|22+|**T1** = tensor(float)
**T2** = tensor(float)| -|||[20, 21]|**T1** = tensor(float)
**T2** = tensor(float)| -|||[16, 19]|**T1** = tensor(float)
**T2** = tensor(float)| -|HardSigmoid|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|HardSwish|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[14, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|Identity|*in* input:**T**
*out* output:**T**

or

*in* input:**V**
*out* output:**V**|25+|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[14, 18]|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|If|*in* cond:**B**
*out* outputs:**V**|25+|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 18]|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**B** = tensor(bool)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 10]|**B** = tensor(bool)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ImageScaler|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|InstanceNormalization|*in* input:**T**
*in* scale:**T**
*in* B:**T**
*out* output:**T**|6+|**T** = tensor(double), tensor(float), tensor(float16)| -|IsInf|*in* X:**T1**
*out* Y:**T2**|20+|**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz)
**T2** = tensor(bool)| -|||[10, 19]|**T1** = tensor(double), tensor(float)
**T2** = tensor(bool)| -|IsNaN|*in* X:**T1**
*out* Y:**T2**|20+|**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz)
**T2** = tensor(bool)| -|||[13, 19]|**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||[9, 12]|**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|LRN|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|LSTM|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*in* initial_c:**T**
*in* P:**T**
*out* Y:**T**
*out* Y_h:**T**
*out* Y_c:**T**|14+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[7, 13]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|LayerNormalization|*in* X:**T**
*in* Scale:**T**
*in* B:**T**
*out* Y:**T**
*out* Mean:**U**
*out* InvStdDev:**U**

or

*in* X:**T**
*in* Scale:**V**
*in* B:**V**
*out* Y:**V**
*out* Mean:**U**
*out* InvStdDev:**U**|17+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**U** = tensor(float)| -|||[1, 16]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**U** = tensor(double), tensor(float)
**V** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|LeakyRelu|*in* X:**T**
*out* Y:**T**|16+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 15]|**T** = tensor(double), tensor(float), tensor(float16)| -|Less|*in* A:**T**
*in* B:**T**
*out* C:**T1**|13+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[9, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|LessOrEqual|*in* A:**T**
*in* B:**T**
*out* C:**T1**|16+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[12, 15]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|Log|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|LogSoftmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|Loop|*in* M:**I**
*in* cond:**B**
*in* v_initial:**V**
*out* v_final_and_scan_outputs:**V**|25+|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 18]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 10]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|MatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[9, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|MatMulInteger|*in* A:**T1**
*in* B:**T2**
*in* a_zero_point:**T1**
*in* b_zero_point:**T2**
*out* Y:**T3**|10+|**T1** = tensor(int8)
**T2** = tensor(int8)
**T3** = tensor(int32)| -|Max|*in* data_0:**T**
*out* max:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||12|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[6, 11]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|MaxPool|*in* X:**T**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**
*out* Indices:**I**|12+|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||11|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16)| -|||10|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16)| -|||[8, 9]|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 7]|**T** = tensor(double), tensor(float), tensor(float16)| -|MemcpyFromHost|*in* X:**T**
*out* Y:**T**|1+|**T** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|MemcpyToHost|*in* X:**T**
*out* Y:**T**|1+|**T** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Min|*in* data_0:**T**
*out* min:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||12|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[6, 11]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Mod|*in* A:**T**
*in* B:**T**
*out* C:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[10, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Mul|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Neg|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|NonZero|*in* X:**T**
*out* Y:**tensor(int64)**|13+|**T** = tensor(bool), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint8)| -|||[9, 12]|**T** = tensor(bool), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint8)| -|Not|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(bool)| -|OneHot|*in* indices:**T1**
*in* depth:**T2**
*in* values:**T3**
*out* output:**T3**|11+|**T1** = tensor(int32), tensor(int64)
**T2** = tensor(int32), tensor(int64)
**T3** = tensor(float), tensor(float16), tensor(int64)| -|Or|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)
**T1** = tensor(bool)| -|PRelu|*in* X:**T**
*in* slope:**T**
*out* Y:**T**|16+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[9, 15]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|Pad|*in* data:**T**
*in* pads:**tensor(int64)**
*in* constant_value:**T**
*in* axes:**Tind**
*out* output:**T**

or

*in* data:**T**
*in* pads:**tensor(int64)**
*in* constant_value:**T**
*out* output:**T**

or

*in* data:**T**
*out* output:**T**|25+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||24|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||23|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||[21, 22]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||[19, 20]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||18|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||[13, 17]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[2, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|ParametricSoftplus|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Pow|*in* X:**T**
*in* Y:**T**
*out* Z:**T**

or

*in* X:**T**
*in* Y:**T1**
*out* Z:**T**|15+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||[13, 14]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||12|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||[7, 11]|**T** = tensor(double), tensor(float), tensor(float16)| -|QuantizeLinear|*in* x:**T1**
*in* y_scale:**T1**
*in* y_zero_point:**T2**
*out* y:**T2**

or

*in* x:**T1**
*in* y_scale:**T2**
*in* y_zero_point:**T3**
*out* y:**T3**

or

*in* x:**T1**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T2**
*out* y:**T2**|25+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(float), tensor(float16)
**T3** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)| -|||[23, 24]|**T1** = tensor(float), tensor(float16)
**T2** = tensor(float), tensor(float16)
**T3** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)| -|||[21, 22]|**T1** = tensor(float), tensor(float16)
**T2** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)| -|||[19, 20]|**T1** = tensor(float), tensor(float16)
**T2** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int8), tensor(uint8)| -|||[13, 18]|**T1** = tensor(float)
**T2** = tensor(int8), tensor(uint8)| -|||[10, 12]|**T1** = tensor(float)
**T2** = tensor(int8), tensor(uint8)| -|RMSNormalization|*in* X:**T**
*in* scale:**V**
*out* Y:**V**|23+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**V** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|RNN|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*out* Y:**T**
*out* Y_h:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[14, 21]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[7, 13]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|RandomNormal|*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|RandomNormalLike|*in* input:**T1**
*out* output:**T2**|1+|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(double), tensor(float), tensor(float16)| -|RandomUniform|*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|RandomUniformLike|*in* input:**T1**
*out* output:**T2**|1+|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(double), tensor(float), tensor(float16)| -|Range|*in* start:**T**
*in* limit:**T**
*in* delta:**T**
*out* output:**T**|11+|**T** = tensor(double), tensor(float), tensor(int16), tensor(int32), tensor(int64)| -|Reciprocal|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|ReduceL1|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|ReduceL2|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|ReduceLogSum|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16)| -|ReduceLogSumExp|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16)| -|ReduceMax|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|20+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[18, 19]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|ReduceMean|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|ReduceMin|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|20+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[18, 19]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|ReduceProd|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|ReduceSum|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|ReduceSumSquare|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16)| -|Relu|*in* X:**T**
*out* Y:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Reshape|*in* data:**T**
*in* shape:**tensor(int64)**
*out* reshaped:**T**

or

*in* data:**T**
*out* reshaped:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[19, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[14, 18]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||13|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[5, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[1, 4]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Resize|*in* X:**T**
*in* scales:**tensor(float)**
*out* Y:**T**

or

*in* X:**T1**
*in* roi:**T2**
*in* scales:**tensor(float)**
*in* sizes:**tensor(int64)**
*out* Y:**T1**|19+|**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||18|**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||[13, 17]|**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||[11, 12]|**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||10|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|ReverseSequence|*in* input:**T**
*in* sequence_lens:**tensor(int64)**
*out* Y:**T**|10+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|RoiAlign|*in* X:**T1**
*in* rois:**T1**
*in* batch_indices:**T2**
*out* Y:**T1**|22+|**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(int64)| -|||[16, 21]|**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(int64)| -|||[10, 15]|**T1** = tensor(double), tensor(float)
**T2** = tensor(int64)| -|RotaryEmbedding|*in* X:**T**
*in* cos_cache:**T**
*in* sin_cache:**T**
*in* position_ids:**M**
*out* Y:**T**|23+|**M** = tensor(int64)
**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|Round|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|ScaledTanh|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Scan|*in* initial_state_and_scan_inputs:**V**
*out* final_state_and_scan_outputs:**V**

or

*in* sequence_lens:**I**
*in* initial_state_and_scan_inputs:**V**
*out* final_state_and_scan_outputs:**V**|25+|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[16, 18]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 15]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[9, 10]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||8|**I** = tensor(int64)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Scatter|*in* data:**T**
*in* indices:**Tind**
*in* updates:**T**
*out* output:**T**|[9, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|ScatterElements|*in* data:**T**
*in* indices:**Tind**
*in* updates:**T**
*out* output:**T**|18+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[16, 17]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[13, 15]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|ScatterND|*in* data:**T**
*in* indices:**tensor(int64)**
*in* updates:**T**
*out* output:**T**|18+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[16, 17]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 15]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Selu|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(double), tensor(float), tensor(float16)| -|SequenceAt|*in* input_sequence:**S**
*in* position:**I**
*out* tensor:**T**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))
**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SequenceConstruct|*in* inputs:**T**
*out* output_sequence:**S**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))
**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SequenceEmpty|*out* output:**S**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceErase|*in* input_sequence:**S**
*in* position:**I**
*out* output_sequence:**S**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceInsert|*in* input_sequence:**S**
*in* tensor:**T**
*in* position:**I**
*out* output_sequence:**S**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceLength|*in* input_sequence:**S**
*out* length:**I**|11+|**I** = tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|Shape|*in* data:**T**
*out* shape:**T1**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[19, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[15, 18]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[13, 14]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[1, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|Shrink|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sigmoid|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Sign|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SimplifiedLayerNormalization|*in* X:**T**
*in* scale:**V**
*out* Y:**V**
*out* inv_std_var:**U**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**U** = tensor(double), tensor(float)
**V** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Sin|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(double), tensor(float), tensor(float16)| -|Size|*in* data:**T**
*out* size:**T1**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[1, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|Slice|*in* data:**T**
*in* starts:**Tind**
*in* ends:**Tind**
*in* axes:**Tind**
*in* steps:**Tind**
*out* output:**T**

or

*in* data:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||10|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[1, 9]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Softmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|Softplus|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Softsign|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|SpaceToDepth|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Split|*in* input:**T**
*in* split:**T**
*out* outputs...:**T**

or

*in* input:**T**
*in* split:**tensor(int64)**
*out* outputs:**T**

or

*in* input:**T**
*out* outputs:**T**|18+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 17]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[2, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sqrt|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Squeeze|*in* data:**T**
*in* axes:**tensor(int64)**
*out* squeezed:**T**

or

*in* data:**T**
*out* squeezed:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||24|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||23|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sub|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Sum|*in* data_0:**T**
*out* sum:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[8, 12]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 7]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Tanh|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|TensorScatter|*in* past_cache:**T**
*in* update:**T**
*in* write_indices:**tensor(int64)**
*out* present_cache:**T**|24+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ThresholdedRelu|*in* X:**T**
*out* Y:**T**|10+|**T** = tensor(double), tensor(float), tensor(float16)| -|||1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Tile|*in* input:**T**
*in* repeats:**T1**
*out* output:**T**

or

*in* input:**T**
*in* tiles:**T**
*in* axis:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(int64)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(int64)| -|TopK|*in* X:**T**
*in* K:**tensor(int64)**
*out* Values:**T**
*out* Indices:**I**

or

*in* X:**T**
*out* Values:**T**
*out* Indices:**I**|24+|**I** = tensor(int64)
**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[11, 23]|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||10|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||[1, 9]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|Transpose|*in* data:**T**
*out* transposed:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Trilu|*in* input:**T**
*in* k:**tensor(int64)**
*out* output:**T**|14+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Unsqueeze|*in* data:**T**
*in* axes:**tensor(int64)**
*out* expanded:**T**

or

*in* data:**T**
*out* expanded:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||24|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||23|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Upsample|*in* X:**T**
*in* scales:**tensor(float)**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**|9|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|Where|*in* condition:**B**
*in* X:**T**
*in* Y:**T**
*out* output:**T**|16+|**B** = tensor(bool)
**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint8)| -|||[9, 15]|**B** = tensor(bool)
**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint8)| -|Xor|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)
**T1** = tensor(bool)| -| | -| | -|**Operator Domain:** *ai.onnx.ml*|||| -|LabelEncoder|*in* X:**T1**
*out* Y:**T2**|4+|**T1** = tensor(double), tensor(float), tensor(int64)
**T2** = tensor(double), tensor(float), tensor(int64)| -|||[2, 3]|**T1** = tensor(float), tensor(int64)
**T2** = tensor(float), tensor(int64)| -| | -| | -|**Operator Domain:** *com.microsoft*|||| -|Attention|*in* input:**T**
*in* weights:**T**
*in* bias:**T**
*in* mask_index:**M**
*in* past:**T**
*in* attention_bias:**T**
*in* past_sequence_length:**M**
*out* output:**T**
*out* present:**T**|1+|**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|BeamSearch|*in* input_ids:**F**
*in* max_length:**I**
*in* min_length:**I**
*in* num_beams:**I**
*in* num_return_sequences:**I**
*in* length_penalty:**T**
*in* repetition_penalty:**T**
*in* vocab_mask:**M**
*in* prefix_vocab_mask:**M**
*in* attention_mask:**I**
*in* decoder_input_ids:**I**
*in* logits_processor:**I**
*out* sequences:**I**
*out* sequences_scores:**T**
*out* scores:**T**|1+|**T** = tensor(float), tensor(float16)| -|BiasAdd|*in* X:**T**
*in* bias:**T**
*in* skip:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|BiasDropout|*in* data:**T**
*in* bias:**T**
*in* residual:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T2**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|BiasGelu|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|BiasSoftmax|*in* data:**T**
*in* bias:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|BiasSplitGelu|*in* X:**T**
*in* bias:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|BitmaskBiasDropout|*in* data:**T**
*in* bias:**T**
*in* residual:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T3**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)
**T3** = tensor(uint32)| -|BitmaskDropout|*in* data:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T3**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)
**T3** = tensor(uint32)| -|CausalConvWithState|*in* input:**T**
*in* weight:**T**
*in* bias:**T**
*in* past_state:**T**
*out* output:**T**
*out* present_state:**T**|1+|**T** = tensor(float), tensor(float16)| -|ComplexMul|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(float), tensor(float16)| -|ComplexMulConj|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(float), tensor(float16)| -|ConvTransposeWithDynamicPads|*in* X:**T**
*in* W:**T**
*in* Pads:**tensor(int64)**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|DecoderAttention|*in* query:**T**
*in* key:**T**
*in* q_weight:**T**
*in* kv_weight:**T**
*in* bias:**T**
*in* key_padding_mask:**B**
*in* key_cache:**T**
*in* value_cache:**T**
*in* static_kv:**B**
*in* use_past:**B**
*in* has_layer_state:**B**
*in* has_key_padding_mask:**B**
*out* output:**T**
*out* new_key_cache:**T**
*out* new_value_cache:**T**|1+|**T** = tensor(float), tensor(float16)| -|DecoderMaskedMultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* mask_index:**M**
*in* attention_bias:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* past_sequence_length:**M**
*in* beam_width:**M**
*in* cache_indirection:**M**
*in* bias:**T**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**
*out* qk:**QK**|1+|**QK** = tensor(float), tensor(float16)
**T** = tensor(float), tensor(float16)| -|DecoderMaskedSelfAttention|*in* input:**T**
*in* weights:**T**
*in* bias:**T**
*in* mask_index:**M**
*in* past:**T**
*in* attention_bias:**T**
*in* past_sequence_length:**M**
*in* beam_width:**M**
*in* cache_indirection:**M**
*out* output:**T**
*out* present:**T**|1+|**T** = tensor(float), tensor(float16)| -|DequantizeLinear|*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T2**|1+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(float16)| -|DequantizeWithOrder|*in* input:**Q**
*in* scale_input:**S**
*out* output:**F**|1+|**F** = tensor(float), tensor(float16)
**Q** = tensor(int8)
**S** = tensor(float)| -|DynamicTimeWarping|*in* input:**F**
*out* output:**I**|1+|**F** = tensor(float)
**I** = tensor(int32)| -|EmbedLayerNormalization|*in* input_ids:**T1**
*in* segment_ids:**T1**
*in* word_embedding:**T**
*in* position_embedding:**T**
*in* segment_embedding:**T**
*in* gamma:**T**
*in* beta:**T**
*in* mask:**T1**
*in* position_ids:**T1**
*out* output:**T**
*out* mask_index:**T1**
*out* embedding_sum:**T**|1+|**T** = tensor(float), tensor(float16)| -|FastGelu|*in* X:**T**
*in* bias:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|FusedConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*in* Z:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|FusedMatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|GatedRelativePositionBias|*in* query_layer:**T**
*in* query_bias:**T**
*in* rel_pos:**T**
*in* weight:**T**
*in* bias:**T**
*in* eco_a:**T**
*in* token_offset:**M**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|GatherBlockQuantized|*in* data:**T1**
*in* indices:**Tind**
*in* scales:**T2**
*in* zero_points:**T1**
*out* output:**T2**|1+|**T1** = tensor(int4), tensor(uint4), tensor(uint8)
**T2** = tensor(bfloat16), tensor(float), tensor(float16)
**Tind** = tensor(int32), tensor(int64)| -|Gelu|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|GemmFloat8|*in* A:**TA**
*in* B:**TB**
*in* C:**TC**
*in* scaleA:**TS**
*in* scaleB:**TS**
*in* scaleY:**TS**
*out* Y:**TR**|1+|**TA** = tensor(bfloat16), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2)
**TB** = tensor(bfloat16), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2)
**TR** = tensor(bfloat16), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2)
**TS** = tensor(float)| -|GemmaRotaryEmbedding|*in* emb:**U**
*in* q:**T**
*in* q_rot:**T**
*in* k:**T**
*in* k_rot:**T**
*out* output1:**T**
*out* output2:**T**|1+|**T** = tensor(float16)
**U** = tensor(float)| -|GreedySearch|*in* input_ids:**I**
*in* max_length:**I**
*in* min_length:**I**
*in* repetition_penalty:**T**
*in* vocab_mask:**I**
*in* prefix_vocab_mask:**I**
*in* attention_mask:**I**
*out* sequences:**I**|1+|**T** = tensor(float), tensor(float16)| -|GridSample|*in* X:**T1**
*in* Grid:**T1**
*out* Y:**T2**|1+|**T1** = tensor(float)
**T2** = tensor(float)| -|GroupNorm|*in* X:**T**
*in* gamma:**M**
*in* beta:**M**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|GroupQueryAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* past_key:**T_CACHE**
*in* past_value:**T_CACHE**
*in* seqlens_k:**M**
*in* total_sequence_length:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*in* position_ids:**tensor(int64)**
*in* attention_bias:**T**
*in* head_sink:**T**
*in* k_scale:**T_KV_SCALE**
*in* v_scale:**T_KV_SCALE**
*out* output:**T**
*out* present_key:**T_CACHE**
*out* present_value:**T_CACHE**
*out* output_qk:**T**|1+|**M** = tensor(int32)
**T** = tensor(bfloat16), tensor(float16)
**T_CACHE** = tensor(bfloat16), tensor(float16), tensor(float8e4m3fn), tensor(int8)
**T_KV_SCALE** = tensor(float)| -|Inverse|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Irfft|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|LinearAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* past_state:**S**
*in* decay:**T**
*in* beta:**T**
*out* output:**T**
*out* present_state:**S**|1+|**T** = tensor(float), tensor(float16)| -|LongformerAttention|*in* input:**T**
*in* weight:**T**
*in* bias:**T**
*in* mask:**T**
*in* global_weight:**T**
*in* global_bias:**T**
*in* global:**G**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|MatMulBnb4|*in* A:**T1**
*in* B:**T2**
*in* absmax:**T1**
*out* Y:**T1**|1+|**T1** = tensor(bfloat16), tensor(float), tensor(float16)
**T2** = tensor(uint8)| -|MatMulNBits|*in* A:**T1**
*in* B:**T2**
*in* scales:**T1**
*in* zero_points:**T3**
*in* g_idx:**T4**
*in* bias:**T1**
*out* Y:**T1**|1+|**T1** = tensor(bfloat16), tensor(float), tensor(float16)
**T2** = tensor(uint8)
**T3** = tensor(bfloat16), tensor(float), tensor(float16), tensor(uint8)| -|MoE|*in* input:**T**
*in* router_probs:**T**
*in* fc1_experts_weights:**T**
*in* fc1_experts_bias:**T**
*in* fc2_experts_weights:**T**
*in* fc2_experts_bias:**T**
*in* fc3_experts_weights:**T**
*in* fc3_experts_bias:**T**
*out* output:**T**|1+|**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|MultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* bias:**T**
*in* key_padding_mask:**M**
*in* attention_bias:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* past_sequence_length:**M**
*in* cache_indirection:**M**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**
*out* qk:**QK**|1+|**QK** = tensor(bfloat16), tensor(float), tensor(float16)
**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|NGramRepeatBlock|*in* input_ids:**Tid**
*in* scores:**T**
*out* scores_out:**T**|1+|**T** = tensor(float)
**Tid** = tensor(int64)| -|NhwcConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|PackedAttention|*in* input:**T**
*in* weights:**T**
*in* bias:**T**
*in* token_offset:**M**
*in* cumulative_sequence_length:**M**
*in* attention_bias:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|PackedMultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* bias:**T**
*in* token_offset:**M**
*in* cumulative_sequence_length:**M**
*in* attention_bias:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|PagedAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* key_cache:**T**
*in* value_cache:**T**
*in* cumulative_sequence_length:**S**
*in* past_seqlens:**S**
*in* block_table:**S**
*in* cos_cache:**T**
*in* sin_cache:**T**
*out* output:**T**
*out* key_cache_out:**T**
*out* value_cache_out:**T**|1+|**S** = tensor(int32)
**T** = tensor(bfloat16), tensor(float16)| -|QAttention|*in* input:**T1**
*in* weight:**T2**
*in* bias:**T3**
*in* input_scale:**T3**
*in* weight_scale:**T3**
*in* mask_index:**T4**
*in* input_zero_point:**T1**
*in* weight_zero_point:**T2**
*in* past:**T3**
*out* output:**T3**
*out* present:**T3**|1+|**T1** = tensor(int8)
**T2** = tensor(int8)
**T3** = tensor(float), tensor(float16)
**T4** = tensor(int32)| -|QMoE|*in* input:**T**
*in* router_probs:**T**
*in* fc1_experts_weights:**T1**
*in* fc1_scales:**T2**
*in* fc1_experts_bias:**T**
*in* fc2_experts_weights:**T1**
*in* fc2_scales:**T2**
*in* fc2_experts_bias:**T**
*in* fc3_experts_weights:**T1**
*in* fc3_scales:**T2**
*in* fc3_experts_bias:**T**
*in* fc1_zero_points:**T1**
*in* fc2_zero_points:**T1**
*in* fc3_zero_points:**T1**
*in* router_weights:**T**
*out* output:**T**|1+|**T** = tensor(bfloat16), tensor(float16)
**T1** = tensor(uint8)
**T2** = tensor(bfloat16), tensor(float16)| -|QOrderedAttention|*in* input:**Q**
*in* scale_input:**S**
*in* scale_Q_gemm:**S**
*in* scale_K_gemm:**S**
*in* scale_V_gemm:**S**
*in* Q_weight:**Q**
*in* K_weight:**Q**
*in* V_weight:**Q**
*in* scale_Q_weight:**S**
*in* scale_K_weight:**S**
*in* scale_V_weight:**S**
*in* Q_bias:**S**
*in* K_bias:**S**
*in* V_bias:**S**
*in* scale_QKT_gemm:**S**
*in* scale_QKT_softmax:**S**
*in* scale_values_gemm:**S**
*in* mask_index:**G**
*in* past:**Q**
*in* attention_bias:**S**
*out* output:**Q**|1+|**G** = tensor(int32)
**Q** = tensor(int8)
**S** = tensor(float)| -|QOrderedGelu|*in* X:**Q**
*in* scale_X:**S**
*in* scale_Y:**S**
*out* Y:**Q**|1+|**Q** = tensor(int8)
**S** = tensor(float)| -|QOrderedLayerNormalization|*in* X:**Q**
*in* scale_X:**S**
*in* scale:**F**
*in* B:**F**
*in* scale_Y:**S**
*out* Y:**Q**|1+|**F** = tensor(float), tensor(float16)
**Q** = tensor(int8)
**S** = tensor(float)| -|QOrderedLongformerAttention|*in* input:**Q**
*in* scale_input:**S**
*in* weight:**Q**
*in* scale_weight:**S**
*in* bias:**S**
*in* scale_bias:**S**
*in* scale_qkv_gemm:**S**
*in* mask:**F**
*in* global_weight:**Q**
*in* scale_global_weight:**S**
*in* global_bias:**S**
*in* scale_global_gemm:**S**
*in* global:**G**
*in* scale_output:**S**
*out* output:**Q**|1+|**F** = tensor(float16)
**G** = tensor(int32)
**Q** = tensor(int8)
**S** = tensor(float)| -|QOrderedMatMul|*in* A:**Q**
*in* scale_A:**S**
*in* B:**Q**
*in* scale_B:**S**
*in* scale_Y:**S**
*in* bias:**S**
*in* C:**Q**
*in* scale_C:**S**
*out* Y:**Q**|1+|**Q** = tensor(int8)
**S** = tensor(float)| -|QuantizeLinear|*in* x:**T1**
*in* y_scale:**T1**
*in* y_zero_point:**T2**
*out* y:**T2**|1+|**T1** = tensor(float16)
**T2** = tensor(int8), tensor(uint8)| -|QuantizeWithOrder|*in* input:**F**
*in* scale_input:**S**
*out* output:**Q**|1+|**F** = tensor(float), tensor(float16)
**Q** = tensor(int8)
**S** = tensor(float)| -|QuickGelu|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|RelativePositionBias|*in* bias_table:**T**
*in* query_length:**U**
*in* key_length:**U**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|RemovePadding|*in* input:**T**
*in* sequence_token_count:**M**
*out* output:**T**
*out* token_offset:**M**
*out* cumulated_seq_len:**M**
*out* max_seq_len:**M**|1+|**T** = tensor(float), tensor(float16)| -|RestorePadding|*in* input:**T**
*in* token_offset:**M**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|Rfft|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|RotaryEmbedding|*in* input:**T**
*in* position_ids:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*out* output:**T**|1+|**M** = tensor(int64)
**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|Sampling|*in* input_ids:**I**
*in* max_length:**I**
*in* min_length:**I**
*in* repetition_penalty:**T**
*in* vocab_mask:**I**
*in* prefix_vocab_mask:**I**
*in* attention_mask:**I**
*in* presence_mask:**I**
*in* seed:**I**
*out* sequences:**I**
*out* filtered_logits:**T**|1+|**T** = tensor(float), tensor(float16)| -|SkipGroupNorm|*in* X:**T**
*in* gamma:**M**
*in* beta:**M**
*in* skip:**T**
*in* bias:**T**
*out* Y:**T**
*out* S:**T**|1+|**T** = tensor(float), tensor(float16)| -|SkipLayerNormalization|*in* input:**T**
*in* skip:**T**
*in* gamma:**T**
*in* beta:**T**
*in* bias:**T**
*out* output:**T**
*out* mean:**U**
*out* inv_std_var:**U**
*out* input_skip_bias_sum:**T**|1+|**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|SkipSimplifiedLayerNormalization|*in* input:**T**
*in* skip:**T**
*in* gamma:**T**
*in* bias:**T**
*out* output:**T**
*out* mean:**U**
*out* inv_std_var:**U**
*out* input_skip_bias_sum:**T**|1+|**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|SparseAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* block_row_indices:**M**
*in* block_col_indices:**M**
*in* total_sequence_length:**M**
*in* key_total_sequence_lengths:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**|1+|**M** = tensor(int32)
**T** = tensor(bfloat16), tensor(float16)| -|TransposeMatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Trilu|*in* X:**T**
*in* k:**tensor(int64)**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|UnfoldTensor|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|WhisperBeamSearch|*in* input_ids:**F**
*in* max_length:**I**
*in* min_length:**I**
*in* num_beams:**I**
*in* num_return_sequences:**I**
*in* length_penalty:**T**
*in* repetition_penalty:**T**
*in* vocab_mask:**M**
*in* prefix_vocab_mask:**M**
*in* attention_mask:**I**
*in* decoder_input_ids:**I**
*in* logits_processor:**I**
*in* cross_qk_layer_head:**I**
*in* extra_decoding_ids:**I**
*in* temperature:**T**
*out* sequences:**I**
*out* sequences_scores:**T**
*out* scores:**T**
*out* cross_qk:**V**
*out* non_speech_probs:**T**|1+|**T** = tensor(float), tensor(float16)| -| | -| | -|**Operator Domain:** *com.ms.internal.nhwc*|||| -|AveragePool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(float), tensor(float16)| -|||[19, 21]|**T** = tensor(float), tensor(float16)| -|||[11, 18]|**T** = tensor(float), tensor(float16)| -|||10|**T** = tensor(float), tensor(float16)| -|||[7, 9]|**T** = tensor(float), tensor(float16)| -|BatchNormalization|*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* input_mean:**U**
*in* input_var:**U**
*out* Y:**T**
*out* running_mean:**U**
*out* running_var:**U**

or

*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* mean:**T**
*in* var:**T**
*out* Y:**T**
*out* mean:**T**
*out* var:**T**
*out* saved_mean:**T**
*out* saved_var:**T**

or

*in* X:**T**
*in* scale:**T1**
*in* B:**T1**
*in* input_mean:**T2**
*in* input_var:**T2**
*out* Y:**T**
*out* running_mean:**T2**
*out* running_var:**T2**|15+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(double), tensor(float), tensor(float16)| -|||14|**T** = tensor(double), tensor(float), tensor(float16)
**U** = tensor(double), tensor(float), tensor(float16)| -|||[9, 13]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|Conv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|22+|**T** = tensor(float), tensor(float16)| -|||[11, 21]|**T** = tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(float), tensor(float16)| -|ConvTranspose|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|11+|**T** = tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(float), tensor(float16)| -|DepthToSpace|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|GlobalAveragePool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(float), tensor(float16)| -|||[1, 21]|**T** = tensor(float), tensor(float16)| -|GlobalMaxPool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(float), tensor(float16)| -|||[1, 21]|**T** = tensor(float), tensor(float16)| -|GridSample|*in* X:**T1**
*in* grid:**T2**
*out* Y:**T1**|22+|**T1** = tensor(float)
**T2** = tensor(float)| -|||[20, 21]|**T1** = tensor(float)
**T2** = tensor(float)| -|||[16, 19]|**T1** = tensor(float)
**T2** = tensor(float)| -|LRN|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|MaxPool|*in* X:**T**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**
*out* Indices:**I**|12+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||11|**I** = tensor(int64)
**T** = tensor(float), tensor(float16)| -|||10|**I** = tensor(int64)
**T** = tensor(float), tensor(float16)| -|||[8, 9]|**I** = tensor(int64)
**T** = tensor(float), tensor(float16)| -|||[1, 7]|**T** = tensor(float), tensor(float16)| -|SpaceToDepth|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -| | -| | - - -
- -## Operators implemented by DmlExecutionProvider - -| Op Name | Parameters | OpSet Version | Types Supported | -|---------|------------|---------------|-----------------| -|**Operator Domain:** *ai.onnx*|||| -|Abs|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|||6+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|Acos|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Acosh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|Add|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Affine|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|And|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)| -|ArgMax|*in* data:**T**
*out* reduced:**tensor(int64)**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ArgMin|*in* data:**T**
*out* reduced:**tensor(int64)**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Asin|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Asinh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|Atan|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Atanh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|AveragePool|*in* X:**T**
*out* Y:**T**|19+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||10+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|BatchNormalization|*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* input_mean:**U**
*in* input_var:**U**
*out* Y:**T**
*out* running_mean:**U**
*out* running_var:**U**

or

*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* mean:**T**
*in* var:**T**
*out* Y:**T**
*out* mean:**T**
*out* var:**T**
*out* saved_mean:**T**
*out* saved_var:**T**

or

*in* X:**T**
*in* scale:**T1**
*in* B:**T1**
*in* input_mean:**T2**
*in* input_var:**T2**
*out* Y:**T**
*out* running_mean:**T2**
*out* running_var:**T2**|15+|**T** = tensor(float), tensor(float16)| -|||14+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|BitShift|*in* X:**T**
*in* Y:**T**
*out* Z:**T**|11+|**T** = tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|BitwiseAnd|*in* A:**T**
*in* B:**T**
*out* C:**T**|18+|**T** = tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|BitwiseNot|*in* X:**T**
*out* Y:**T**|18+|**T** = tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|BitwiseOr|*in* A:**T**
*in* B:**T**
*out* C:**T**|18+|**T** = tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|BitwiseXor|*in* A:**T**
*in* B:**T**
*out* C:**T**|18+|**T** = tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Cast|*in* input:**T1**
*out* output:**T2**|21+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||6+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|CastLike|*in* input:**T1**
*in* target_type:**T2**
*out* output:**T2**|21+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||15+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Ceil|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Celu|*in* X:**T**
*out* Y:**T**|12+|**T** = tensor(float), tensor(float16)| -|Clip|*in* input:**T**
*in* min:**T**
*in* max:**T**
*out* output:**T**

or

*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Col2Im|*in* input:**T**
*in* image_shape:**tensor(int64)**
*in* block_shape:**tensor(int64)**
*out* output:**T**|18+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Concat|*in* inputs:**T**
*out* concat_result:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||4+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ConcatFromSequence|*in* input_sequence:**S**
*out* concat_result:**T**|11+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ConstantOfShape|*in* input:**T1**
*out* output:**T2**|21+|**T1** = tensor(int64)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T1** = tensor(int64)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Conv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ConvInteger|*in* x:**T1**
*in* w:**T2**
*in* x_zero_point:**T1**
*in* w_zero_point:**T2**
*out* y:**T3**|10+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int32)| -|ConvTranspose|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|Cos|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Cosh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|Crop|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|CumSum|*in* x:**T**
*in* axis:**T2**
*out* y:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|DFT|*in* input:**T1**
*in* dft_length:**T2**
*in* axis:**tensor(int64)**
*out* output:**T1**

or

*in* input:**T1**
*in* dft_length:**T2**
*out* output:**T1**|20+|**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|||17+|**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|DepthToSpace|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|DequantizeLinear|*in* x:**T**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T**
*out* y:**tensor(float)**

or

*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T2**

or

*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T3**|21+|**T1** = tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||19+|**T1** = tensor(int32), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||13+|**T** = tensor(int32), tensor(int8), tensor(uint8)| -|||10+|**T** = tensor(int32), tensor(int8), tensor(uint8)| -|Div|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Dropout|*in* data:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T2**

or

*in* data:**T**
*out* output:**T**
*out* mask:**T**

or

*in* data:**T**
*out* output:**T**
*out* mask:**T1**|7+|**T** = tensor(float), tensor(float16)| -|DynamicQuantizeLinear|*in* x:**T1**
*out* y:**T2**
*out* y_scale:**tensor(float)**
*out* y_zero_point:**T2**|11+|**T1** = tensor(float)
**T2** = tensor(int8), tensor(uint8)| -|Einsum|*in* Inputs:**T**
*out* Output:**T**|12+|**T** = tensor(float), tensor(float16)| -|Elu|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(float), tensor(float16)| -|Equal|*in* A:**T**
*in* B:**T**
*out* C:**T1**|19+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||7+|**T** = tensor(float), tensor(float16)
**T1** = tensor(bool)| -|Erf|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|Exp|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Expand|*in* input:**T**
*in* shape:**tensor(int64)**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||8+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|EyeLike|*in* input:**T1**
*out* output:**T2**|9+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Flatten|*in* input:**T**
*out* output:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Floor|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|GRU|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*out* Y:**T**
*out* Y_h:**T**|14+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|Gather|*in* data:**T**
*in* indices:**Tind**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|GatherElements|*in* data:**T**
*in* indices:**Tind**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|GatherND|*in* data:**T**
*in* indices:**tensor(int64)**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Gemm|*in* A:**T**
*in* B:**T**
*in* C:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|GlobalAveragePool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|GlobalLpPool|*in* X:**T**
*out* Y:**T**|2+|**T** = tensor(float), tensor(float16)| -|GlobalMaxPool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|Greater|*in* A:**T**
*in* B:**T**
*out* C:**T1**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||7+|**T** = tensor(float), tensor(float16)
**T1** = tensor(bool)| -|GreaterOrEqual|*in* A:**T**
*in* B:**T**
*out* C:**T1**|16+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|GridSample|*in* X:**T1**
*in* grid:**T2**
*out* Y:**T1**|16+|**T1** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|GroupNorm||21+|**M** = tensor(float), tensor(float16)
**T** = tensor(float), tensor(float16)| -|HardSigmoid|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(float), tensor(float16)| -|Hardmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|Identity|*in* input:**T**
*out* output:**T**

or

*in* input:**V**
*out* output:**V**|21+|**V** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**V** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||16+|**V** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||14+|**V** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|If|*in* cond:**B**
*out* outputs:**V**|19+|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||16+|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**B** = tensor(bool)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**B** = tensor(bool)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ImageScaler|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|InstanceNormalization|*in* input:**T**
*in* scale:**T**
*in* B:**T**
*out* output:**T**|6+|**T** = tensor(float), tensor(float16)| -|IsInf|*in* X:**T1**
*out* Y:**T2**|20+|**T1** = tensor(float)
**T2** = tensor(bool)| -|||10+|**T1** = tensor(float)
**T2** = tensor(bool)| -|IsNaN|*in* X:**T1**
*out* Y:**T2**|20+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||13+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||9+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(bool)| -|LRN|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|LSTM|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*in* initial_c:**T**
*in* P:**T**
*out* Y:**T**
*out* Y_h:**T**
*out* Y_c:**T**|14+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|LayerNormalization|*in* X:**T**
*in* Scale:**T**
*in* B:**T**
*out* Y:**T**
*out* Mean:**U**
*out* InvStdDev:**U**

or

*in* X:**T**
*in* Scale:**V**
*in* B:**V**
*out* Y:**V**
*out* Mean:**U**
*out* InvStdDev:**U**|17+|**T** = tensor(float), tensor(float16)
**U** = tensor(float)| -|||1+|**T** = tensor(float), tensor(float16)
**U** = tensor(float), tensor(float16)
**V** = tensor(float), tensor(float16)| -|LeakyRelu|*in* X:**T**
*out* Y:**T**|16+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Less|*in* A:**T**
*in* B:**T**
*out* C:**T1**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||7+|**T** = tensor(float), tensor(float16)
**T1** = tensor(bool)| -|LessOrEqual|*in* A:**T**
*in* B:**T**
*out* C:**T1**|16+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|Log|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|LogSoftmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|LpNormalization|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|LpPool|*in* X:**T**
*out* Y:**T**|18+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||2+|**T** = tensor(float), tensor(float16)| -|MatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|MatMulInteger|*in* A:**T1**
*in* B:**T2**
*in* a_zero_point:**T1**
*in* b_zero_point:**T2**
*out* Y:**T3**|10+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int32)| -|Max|*in* data_0:**T**
*out* max:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||8+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|MaxPool|*in* X:**T**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**
*out* Indices:**I**|12+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||11+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||10+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||8+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||1+|**T** = tensor(float), tensor(float16)| -|MaxRoiPool|*in* X:**T**
*in* rois:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|MaxUnpool|*in* X:**T1**
*in* I:**T2**
*in* output_shape:**T2**
*out* output:**T1**|11+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int64)| -|||9+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int64)| -|Mean|*in* data_0:**T**
*out* mean:**T**|13+|**T** = tensor(float), tensor(float16)| -|||8+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|MeanVarianceNormalization|*in* X:**T**
*out* Y:**T**

or

*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|MemcpyFromHost|*in* X:**T**
*out* Y:**T**|1+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)| -|MemcpyToHost|*in* X:**T**
*out* Y:**T**|1+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)| -|Min|*in* data_0:**T**
*out* min:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||8+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Mod|*in* A:**T**
*in* B:**T**
*out* C:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||10+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|Mul|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Neg|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|||6+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|NonZero|*in* X:**T**
*out* Y:**tensor(int64)**|13+|**T** = tensor(bool), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||9+|**T** = tensor(bool), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|Not|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(bool)| -|OneHot|*in* indices:**T1**
*in* depth:**T2**
*in* values:**T3**
*out* output:**T3**|11+|**T1** = tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T3** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T1** = tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T3** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|OptionalGetElement|*in* input:**O**
*out* output:**V**|18+|**O** = optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint64)), optional(tensor(uint8)), seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||15+|**O** = optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint64)), optional(tensor(uint8))
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|OptionalHasElement|*in* input:**O**
*out* output:**B**|18+|**B** = tensor(bool)
**O** = optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint64)), optional(tensor(uint8)), seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||15+|**B** = tensor(bool)
**O** = optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint64)), optional(tensor(uint8))| -|Or|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)| -|PRelu|*in* X:**T**
*in* slope:**T**
*out* Y:**T**|16+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8)| -|||9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8)| -|||7+|**T** = tensor(float), tensor(float16)| -|Pad|*in* data:**T**
*in* pads:**tensor(int64)**
*in* constant_value:**T**
*in* axes:**Tind**
*out* output:**T**

or

*in* data:**T**
*in* pads:**tensor(int64)**
*in* constant_value:**T**
*out* output:**T**

or

*in* data:**T**
*out* output:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||18+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||2+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ParametricSoftplus|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|Pow|*in* X:**T**
*in* Y:**T**
*out* Z:**T**

or

*in* X:**T**
*in* Y:**T1**
*out* Z:**T**|15+|**T** = tensor(float), tensor(float16), tensor(int32)
**T1** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int32)
**T1** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int32)
**T1** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16)| -|QLinearConv|*in* x:**T1**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T1**
*in* w:**T2**
*in* w_scale:**tensor(float)**
*in* w_zero_point:**T2**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T3**
*in* B:**T4**
*out* y:**T3**|10+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int8), tensor(uint8)
**T4** = tensor(int32)| -|QLinearMatMul|*in* a:**T1**
*in* a_scale:**TS**
*in* a_zero_point:**T1**
*in* b:**T2**
*in* b_scale:**TS**
*in* b_zero_point:**T2**
*in* y_scale:**TS**
*in* y_zero_point:**T3**
*out* y:**T3**

or

*in* a:**T1**
*in* a_scale:**tensor(float)**
*in* a_zero_point:**T1**
*in* b:**T2**
*in* b_scale:**tensor(float)**
*in* b_zero_point:**T2**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T3**
*out* y:**T3**|21+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int8), tensor(uint8)| -|||10+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int8), tensor(uint8)| -|QuantizeLinear|*in* x:**T1**
*in* y_scale:**T1**
*in* y_zero_point:**T2**
*out* y:**T2**

or

*in* x:**T1**
*in* y_scale:**T2**
*in* y_zero_point:**T3**
*out* y:**T3**

or

*in* x:**T1**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T2**
*out* y:**T2**|21+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)| -|||19+|**T1** = tensor(float), tensor(float16), tensor(int32)
**T2** = tensor(int8), tensor(uint8)| -|||13+|**T1** = tensor(float), tensor(int32)
**T2** = tensor(int8), tensor(uint8)| -|||10+|**T1** = tensor(float), tensor(int32)
**T2** = tensor(int8), tensor(uint8)| -|RNN|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*out* Y:**T**
*out* Y_h:**T**|14+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|Range|*in* start:**T**
*in* limit:**T**
*in* delta:**T**
*out* output:**T**|11+|**T** = tensor(float), tensor(int16), tensor(int32), tensor(int64)| -|Reciprocal|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|ReduceL1|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|ReduceL2|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceLogSum|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceLogSumExp|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceMax|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|20+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceMean|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceMin|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|20+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceProd|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|ReduceSum|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|ReduceSumSquare|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Relu|*in* X:**T**
*out* Y:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8)| -|||13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Reshape|*in* data:**T**
*in* shape:**tensor(int64)**
*out* reshaped:**T**

or

*in* data:**T**
*out* reshaped:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||14+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||5+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Resize|*in* X:**T**
*in* scales:**tensor(float)**
*out* Y:**T**

or

*in* X:**T1**
*in* roi:**T2**
*in* scales:**tensor(float)**
*in* sizes:**tensor(int64)**
*out* Y:**T1**|19+|**T1** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||18+|**T1** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||13+|**T1** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||11+|**T1** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||10+|**T** = tensor(float), tensor(float16)| -|ReverseSequence|*in* input:**T**
*in* sequence_lens:**tensor(int64)**
*out* Y:**T**|10+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|RoiAlign|*in* X:**T1**
*in* rois:**T1**
*in* batch_indices:**T2**
*out* Y:**T1**|16+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|||10+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|Round|*in* X:**T**
*out* Y:**T**|11+|**T** = tensor(float), tensor(float16)| -|STFT|*in* signal:**T1**
*in* frame_step:**T2**
*in* window:**T1**
*in* frame_length:**T2**
*out* output:**T1**|17+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|ScaledTanh|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|Scatter|*in* data:**T**
*in* indices:**Tind**
*in* updates:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||9+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|ScatterElements|*in* data:**T**
*in* indices:**Tind**
*in* updates:**T**
*out* output:**T**|16+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|ScatterND|*in* data:**T**
*in* indices:**tensor(int64)**
*in* updates:**T**
*out* output:**T**|16+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Selu|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(float), tensor(float16)| -|SequenceAt|*in* input_sequence:**S**
*in* position:**I**
*out* tensor:**T**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))
**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SequenceConstruct|*in* inputs:**T**
*out* output_sequence:**S**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))
**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SequenceEmpty|*out* output:**S**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceErase|*in* input_sequence:**S**
*in* position:**I**
*out* output_sequence:**S**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceInsert|*in* input_sequence:**S**
*in* tensor:**T**
*in* position:**I**
*out* output_sequence:**S**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceLength|*in* input_sequence:**S**
*out* length:**I**|11+|**I** = tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|Shape|*in* data:**T**
*out* shape:**T1**|21+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||19+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||15+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||13+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||1+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|Shrink|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|Sigmoid|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Sign|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SimplifiedLayerNormalization|*in* X:**T**
*in* scale:**V**
*out* Y:**V**
*out* inv_std_var:**U**|1+|**T** = tensor(float), tensor(float16)
**U** = tensor(float), tensor(float16)
**V** = tensor(float), tensor(float16)| -|Sin|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Sinh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|Size|*in* data:**T**
*out* size:**T1**|21+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||19+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||13+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||1+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|Slice|*in* data:**T**
*in* starts:**Tind**
*in* ends:**Tind**
*in* axes:**Tind**
*in* steps:**Tind**
*out* output:**T**

or

*in* data:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||10+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Softmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|Softplus|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|Softsign|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|SpaceToDepth|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Split|*in* input:**T**
*in* split:**T**
*out* outputs...:**T**

or

*in* input:**T**
*in* split:**tensor(int64)**
*out* outputs:**T**

or

*in* input:**T**
*out* outputs:**T**|18+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||2+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sqrt|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Squeeze|*in* data:**T**
*in* axes:**tensor(int64)**
*out* squeezed:**T**

or

*in* data:**T**
*out* squeezed:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sub|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sum|*in* data_0:**T**
*out* sum:**T**|13+|**T** = tensor(float), tensor(float16)| -|||8+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Tan|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Tanh|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|ThresholdedRelu|*in* X:**T**
*out* Y:**T**|10+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|Tile|*in* input:**T**
*in* repeats:**T1**
*out* output:**T**

or

*in* input:**T**
*in* tiles:**T**
*in* axis:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||6+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|TopK|*in* X:**T**
*in* K:**tensor(int64)**
*out* Values:**T**
*out* Indices:**I**

or

*in* X:**T**
*out* Values:**T**
*out* Indices:**I**|11+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||10+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Transpose|*in* data:**T**
*out* transposed:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Trilu|*in* input:**T**
*in* k:**tensor(int64)**
*out* output:**T**|14+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Unsqueeze|*in* data:**T**
*in* axes:**tensor(int64)**
*out* expanded:**T**

or

*in* data:**T**
*out* expanded:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Upsample|*in* X:**T**
*in* scales:**tensor(float)**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**|10+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|Where|*in* condition:**B**
*in* X:**T**
*in* Y:**T**
*out* output:**T**|16+|**B** = tensor(bool)
**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**B** = tensor(bool)
**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Xor|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)| -| | -| | -|**Operator Domain:** *com.microsoft*|||| -|Attention|*in* input:**T**
*in* weights:**T**
*in* bias:**T**
*in* mask_index:**M**
*in* past:**T**
*in* attention_bias:**T**
*in* past_sequence_length:**M**
*out* output:**T**
*out* present:**T**|1+|**M** = tensor(int32)
**T** = tensor(float), tensor(float16)| -|BiasAdd|*in* X:**T**
*in* bias:**T**
*in* skip:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|BiasGelu|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(float), tensor(float16)| -|BiasSplitGelu|*in* X:**T**
*in* bias:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|ConvTransposeWithDynamicPads|*in* X:**T**
*in* W:**T**
*in* Pads:**tensor(int64)**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DequantizeLinear|*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T2**|1+|**T1** = tensor(int32), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|DynamicQuantizeMatMul|*in* A:**T1**
*in* B:**T2**
*in* b_scale:**T1**
*in* b_zero_point:**T2**
*in* bias:**T1**
*out* Y:**T1**|1+|**T1** = tensor(float)
**T2** = tensor(int8), tensor(uint8)| -|EmbedLayerNormalization|*in* input_ids:**T1**
*in* segment_ids:**T1**
*in* word_embedding:**T**
*in* position_embedding:**T**
*in* segment_embedding:**T**
*in* gamma:**T**
*in* beta:**T**
*in* mask:**T1**
*in* position_ids:**T1**
*out* output:**T**
*out* mask_index:**T1**
*out* embedding_sum:**T**|1+|**T** = tensor(float), tensor(float16)| -|FastGelu|*in* X:**T**
*in* bias:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|FusedMatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|FusedMatMulActivation|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|Gelu|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|GroupNorm|*in* X:**T**
*in* gamma:**M**
*in* beta:**M**
*out* Y:**T**|1+|**M** = tensor(float), tensor(float16)
**T** = tensor(float), tensor(float16)| -|GroupQueryAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* past_key:**T_CACHE**
*in* past_value:**T_CACHE**
*in* seqlens_k:**M**
*in* total_sequence_length:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*in* position_ids:**tensor(int64)**
*in* attention_bias:**T**
*in* head_sink:**T**
*in* k_scale:**T_KV_SCALE**
*in* v_scale:**T_KV_SCALE**
*out* output:**T**
*out* present_key:**T_CACHE**
*out* present_value:**T_CACHE**
*out* output_qk:**T**|1+|**M** = tensor(int32)
**T** = tensor(float), tensor(float16)| -|MatMulIntegerToFloat|*in* A:**T1**
*in* B:**T2**
*in* a_scale:**T3**
*in* b_scale:**T3**
*in* a_zero_point:**T1**
*in* b_zero_point:**T2**
*in* bias:**T3**
*out* Y:**T3**|1+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(float), tensor(float16)| -|MatMulNBits|*in* A:**T1**
*in* B:**T2**
*in* scales:**T1**
*in* zero_points:**T3**
*in* g_idx:**T4**
*in* bias:**T1**
*out* Y:**T1**|1+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(uint8)| -|MultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* bias:**T**
*in* key_padding_mask:**M**
*in* attention_bias:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* past_sequence_length:**M**
*in* cache_indirection:**M**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**
*out* qk:**QK**|1+|**M** = tensor(int32)
**T** = tensor(float), tensor(float16)| -|NhwcConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|QAttention|*in* input:**T1**
*in* weight:**T2**
*in* bias:**T3**
*in* input_scale:**T3**
*in* weight_scale:**T3**
*in* mask_index:**T4**
*in* input_zero_point:**T1**
*in* weight_zero_point:**T2**
*in* past:**T3**
*out* output:**T3**
*out* present:**T3**|1+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(float), tensor(float16)
**T4** = tensor(int32)| -|QLinearAdd|*in* A:**T**
*in* A_scale:**tensor(float)**
*in* A_zero_point:**T**
*in* B:**T**
*in* B_scale:**tensor(float)**
*in* B_zero_point:**T**
*in* C_scale:**tensor(float)**
*in* C_zero_point:**T**
*out* C:**T**|1+|**T** = tensor(int8), tensor(uint8)| -|QLinearAveragePool|*in* X:**T**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T**
*out* Y:**T**|1+|**T** = tensor(int8), tensor(uint8)| -|QLinearConcat|*in* Y_scale:**TF**
*in* Y_zero_point:**T8**
*in* inputs:**TV**
*out* Y:**T8**|1+|**T8** = tensor(int8), tensor(uint8)
**TF** = tensor(float)
**TV** = tensor(float), tensor(int8), tensor(uint8)| -|QLinearGlobalAveragePool|*in* X:**T**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T**
*out* Y:**T**|1+|**T** = tensor(int8), tensor(uint8)| -|QLinearSigmoid|*in* X:**T**
*in* X_scale:**tensor(float)**
*in* X_zero_point:**T**
*in* Y_scale:**tensor(float)**
*in* Y_zero_point:**T**
*out* Y:**T**|1+|**T** = tensor(int8), tensor(uint8)| -|QuantizeLinear|*in* x:**T1**
*in* y_scale:**T1**
*in* y_zero_point:**T2**
*out* y:**T2**|1+|**T1** = tensor(float), tensor(float16), tensor(int32)
**T2** = tensor(int8), tensor(uint8)| -|QuickGelu|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|RotaryEmbedding|*in* input:**T**
*in* position_ids:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*out* output:**T**|1+|**M** = tensor(int64)
**T** = tensor(float), tensor(float16)| -|SkipLayerNormalization|*in* input:**T**
*in* skip:**T**
*in* gamma:**T**
*in* beta:**T**
*in* bias:**T**
*out* output:**T**
*out* mean:**U**
*out* inv_std_var:**U**
*out* input_skip_bias_sum:**T**|1+|**T** = tensor(float), tensor(float16)| -|SkipSimplifiedLayerNormalization|*in* input:**T**
*in* skip:**T**
*in* gamma:**T**
*in* bias:**T**
*out* output:**T**
*out* mean:**U**
*out* inv_std_var:**U**
*out* input_skip_bias_sum:**T**|1+|**T** = tensor(float), tensor(float16)| -| | -| | -|**Operator Domain:** *com.microsoft.dml*|||| -|DmlFusedAdd|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedBatchNormalization|*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* mean:**T**
*in* var:**T**
*out* Y:**T**
*out* mean:**T**
*out* var:**T**
*out* saved_mean:**T**
*out* saved_var:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedConvTranspose|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedGemm|*in* A:**T**
*in* B:**T**
*in* C:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedInstanceNormalization|*in* input:**T**
*in* scale:**T**
*in* B:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedMatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedMeanVarianceNormalization|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedSum|*in* data_0:**T**
*out* sum:**T**|1+|**T** = tensor(float), tensor(float16)| -| | -| | From d5c14ae3598e94659430b29dff1145d68028222b Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Fri, 19 Dec 2025 10:53:53 +0000 Subject: [PATCH 069/140] Add an implementation an NHWC implementation of convolution to avoid transposes * Modification to the CPU EP to specify channels_last when data format is NWHC * Added a FusedNhwcConv kernel * Implementation of the kernel in mlas * Added compiler guards so it is inly used with KleidiAi (for now, can be removed if needed) * Added unittests Signed-off-by: Orlaith Monahan --- .../contrib_ops/cpu/cpu_contrib_kernels.cc | 2 + onnxruntime/contrib_ops/cpu/fused_conv.cc | 8 + .../framework/kernel_type_str_resolver.cc | 12 + .../graph/contrib_ops/nhwc_schema_defs.cc | 2 +- onnxruntime/core/mlas/inc/mlas.h | 2 + onnxruntime/core/mlas/lib/convolve.cpp | 4 +- .../mlas/lib/kleidiai/convolve_kleidiai.cpp | 31 +- .../core/mlas/lib/kleidiai/mlasi_kleidiai.h | 1 + onnxruntime/core/mlas/lib/mlasi.h | 2 + .../core/optimizer/conv_activation_fusion.cc | 5 +- .../core/optimizer/conv_add_act_fusion.cc | 10 +- .../layout_transformation.cc | 1 + .../core/optimizer/nhwc_transformer.cc | 158 ++++- onnxruntime/core/optimizer/nhwc_transformer.h | 5 + onnxruntime/core/providers/cpu/nn/conv.cc | 153 ++++- onnxruntime/core/providers/cpu/nn/conv.h | 3 +- onnxruntime/core/util/math_cpu.cc | 1 + .../test/framework/ort_model_only_test.cc | 61 +- .../internal_testing_tests.cc | 69 +- onnxruntime/test/mlas/bench/bench_sconv.cpp | 1 + onnxruntime/test/mlas/unittest/test_conv2d.h | 590 +----------------- .../test/optimizer/conv_add_act_test.cc | 5 +- .../fuse_initializers_transformer_test.cc | 5 +- .../test/optimizer/nhwc_transformer_test.cc | 22 + 24 files changed, 520 insertions(+), 633 deletions(-) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index cc652ed52ee72..d497285db572d 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -20,6 +20,7 @@ class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, EmbedLayerNormalization); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, ExpandDims); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, FusedConv); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, NhwcFusedConv); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, FusedGemm); #if !defined(DISABLE_GENERATION_OPS) class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, GreedySearch); @@ -313,6 +314,7 @@ Status RegisterCpuContribKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, + BuildKernelCreateInfo, BuildKernelCreateInfo, #if !defined(DISABLE_GENERATION_OPS) BuildKernelCreateInfo, diff --git a/onnxruntime/contrib_ops/cpu/fused_conv.cc b/onnxruntime/contrib_ops/cpu/fused_conv.cc index 5374222dbabcc..d77efc26d0e2f 100644 --- a/onnxruntime/contrib_ops/cpu/fused_conv.cc +++ b/onnxruntime/contrib_ops/cpu/fused_conv.cc @@ -26,5 +26,13 @@ ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( .TypeConstraint("T", DataTypeImpl::GetTensorType()), FusedConvFloat); +ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( + NhwcFusedConv, + 1, + float, + KernelDefBuilder() + .TypeConstraint("T", DataTypeImpl::GetTensorType()), + FusedConvFloat); + } // namespace contrib } // namespace onnxruntime diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index 3142f94f289b3..aacbc8fc0a4fb 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -36,6 +36,18 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } +#ifdef USE_KLEIDIAI + // Klediai specific block for NhwcFusedConvolutions + if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { + const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; + op_it = map.find(fused_conv_op_id); + if (op_it == map.end()) { + const auto conv_op_id = OpIdentifier{std::string{kOnnxDomain}, "Conv", op_id.since_version}; + op_it = map.find(conv_op_id); + } + } +#endif + return op_it; } diff --git a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc index 8fe3a4d5f3b6f..5a57a58360ddf 100644 --- a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc +++ b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc @@ -403,7 +403,7 @@ Only has fp16 implementation as of 2023/04/15. .Input(2, "B", "", "T", OpSchema::Optional) .Input(3, "Z", "Tensor to be added to the output, must be the same shape and format as the output tensor.", "T", OpSchema::Optional) .Output(0, "Y", "", "T") - .TypeConstraint("T", {"tensor(float16)"}, "Constrain input and output types to float tensors") + .TypeConstraint("T", {"tensor(float16)", "tensor(float)"}, "Constrain input and output types to float tensors") .TypeAndShapeInferenceFunction([](InferenceContext& ctx) { ONNX_NAMESPACE::propagateElemTypeFromInputToOutput(ctx, 0, 0); convPoolShapeInferenceNhwc(ctx, true, false, 0, 1); diff --git a/onnxruntime/core/mlas/inc/mlas.h b/onnxruntime/core/mlas/inc/mlas.h index 04e99d206bd06..2b341044a1d8a 100644 --- a/onnxruntime/core/mlas/inc/mlas.h +++ b/onnxruntime/core/mlas/inc/mlas.h @@ -892,6 +892,7 @@ struct MLAS_CONV_PARAMETERS { size_t BatchCount; size_t GroupCount; size_t InputChannels; + bool ChannelsLast; size_t InputShape[3]; size_t KernelShape[3]; size_t DilationShape[3]; @@ -932,6 +933,7 @@ MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool); diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index 1d1a742cb14a5..71ef70123dfed 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1341,6 +1341,7 @@ MlasConvPrepare( size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool ) @@ -1399,7 +1400,7 @@ Return Value: if (GetMlasPlatform().MlasConvPrepareOverride != nullptr && GetMlasPlatform().MlasConvPrepareOverride(Parameters, Dimensions, BatchCount, GroupCount, InputChannels, InputShape,KernelShape,DilationShape, Padding, StrideShape, OutputShape, FilterCount, - Activation, WorkingBufferSize, Beta, ThreadPool)){ + Activation, WorkingBufferSize, ChannelsLast, Beta, ThreadPool)){ return; } // @@ -1410,6 +1411,7 @@ Return Value: Parameters->BatchCount = BatchCount; Parameters->GroupCount = GroupCount; Parameters->InputChannels = InputChannels; + Parameters->ChannelsLast = ChannelsLast; Parameters->FilterCount = FilterCount; Parameters->Beta = Beta; diff --git a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp index a37dcb37a17ac..110e24e8166b4 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp +++ b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp @@ -491,6 +491,7 @@ static std::shared_ptr LhsPtrFill(const size_t ci, const size_t i static std::unique_ptr LhsPackImageDataSme(const size_t ci, const size_t ih, const size_t iw, const size_t kh, const size_t kw, const size_t sh, const size_t sw, const size_t padding, const float* in, + bool input_is_channels_last, MLAS_THREADPOOL* ThreadPool) { size_t padsize = 256; @@ -517,7 +518,14 @@ static std::unique_ptr LhsPackImageDataSme(const size_t ci, const s const auto lhs_size = kai_get_lhs_packed_size_lhs_imatmul_pack_x32p2vlx1_x32p_sme(m,kh*kw,ci); auto lhs = std::make_unique(lhs_size); - auto nhwc = NChwToNhwc(1, ci, ih, iw, in, 1, 1, false, ThreadPool); + std::unique_ptr nhwc_holder; + const float* activation_src = nullptr; + if (input_is_channels_last) { + activation_src = in; + } else { + nhwc_holder = NChwToNhwc(1, ci, ih, iw, in, 1, 1, false, ThreadPool); + activation_src = nhwc_holder.get(); + } // Cache of computed lhs ptr offsets. thread_local to prevent interference from parallel sessions. // @@ -549,7 +557,7 @@ static std::unique_ptr LhsPackImageDataSme(const size_t ci, const s lhs_ptrs_cache[key] = lhs_ptrs; } - MultiThreadedLHSPackSme(ThreadPool, ci, m, kh, kw, &lhs_ptrs[0], &lhs[0], &nhwc[0], &pad_ptr[0]); + MultiThreadedLHSPackSme(ThreadPool, ci, m, kh, kw, &lhs_ptrs[0], &lhs[0], activation_src, &pad_ptr[0]); return lhs; } @@ -571,6 +579,7 @@ static void ConvolveSme(const size_t co, //channels out const float* in, //in image data float* out, //out image data float* tmp_mlas_aligned, //intermediate buffer if we need to perform a transpose + bool input_is_channels_last, MLAS_THREADPOOL* ThreadPool) { //RhsPackWeightsBiasSme() - to perform dilation increases kernel size and masks unused weights @@ -608,17 +617,13 @@ static void ConvolveSme(const size_t co, //channels out for (size_t g = 0; g < groups; ++g) { - auto result{out}; - //do we require a post matmul transpose ? - //output is m x n or image_data x co or hw x co - //MLAS require it as n x m (or co x hw), transpose required - if (co > 1) { - //intermediate buffer required, pre-transpose - //Note: because we are calling MlasTranspose() need to ensure we use a MLAS aligned buffer + auto result = out; + const bool need_transpose = (!input_is_channels_last) && (co > 1); + if (need_transpose) { result = tmp_mlas_aligned; } - auto lhs = LhsPackImageDataSme(ci, ih, iw, d_kh, d_kw, sh, sw, padding, in, ThreadPool); + auto lhs = LhsPackImageDataSme(ci, ih, iw, d_kh, d_kw, sh, sw, padding, in, input_is_channels_last, ThreadPool); auto rhs = RhsPackWeightsBiasSme(co, ci, kh, kw, dilationh, dilationw, weights, bias, ThreadPool); MlasTrySimpleParallel(ThreadPool, static_cast(dim[0] * dim[1] * dim[2]), [&](ptrdiff_t tid) { @@ -660,7 +665,7 @@ static void ConvolveSme(const size_t co, //channels out ); }); - if (result == tmp_mlas_aligned) { + if (need_transpose) { //Note: this could be absorbed into post conv activation MlasTranspose(tmp_mlas_aligned, out, m, co, ThreadPool); } @@ -689,6 +694,7 @@ ArmKleidiAI::MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool) { @@ -708,6 +714,7 @@ ArmKleidiAI::MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, Parameters->BatchCount = BatchCount; Parameters->GroupCount = GroupCount; Parameters->InputChannels = InputChannels; + Parameters->ChannelsLast = ChannelsLast; Parameters->FilterCount = FilterCount; Parameters->Beta = Beta; @@ -779,7 +786,7 @@ ArmKleidiAI::MlasConv( Parameters->DilationShape[0], Parameters->DilationShape[1], // kernel dilation Parameters->Padding[0], // image padding Parameters->GroupCount, // filter groups - Filter, Bias, Input, Output, WorkingBuffer, ThreadPool); + Filter, Bias, Input, Output, WorkingBuffer, Parameters->ChannelsLast, ThreadPool); MlasActivation(Parameters->Activation, Output, nullptr, Parameters->FilterCount, Parameters->OutputSize, Parameters->OutputSize); diff --git a/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h b/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h index 1d620388bb5f0..cba812a64822d 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h +++ b/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h @@ -185,6 +185,7 @@ MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool); diff --git a/onnxruntime/core/mlas/lib/mlasi.h b/onnxruntime/core/mlas/lib/mlasi.h index cba1fba68b91f..0bee47e97e90d 100644 --- a/onnxruntime/core/mlas/lib/mlasi.h +++ b/onnxruntime/core/mlas/lib/mlasi.h @@ -853,6 +853,7 @@ void size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool ); @@ -873,6 +874,7 @@ bool size_t FilterCount, const MLAS_ACTIVATION* Activation, size_t* WorkingBufferSize, + bool ChannelsLast, float Beta, MLAS_THREADPOOL* ThreadPool ); diff --git a/onnxruntime/core/optimizer/conv_activation_fusion.cc b/onnxruntime/core/optimizer/conv_activation_fusion.cc index 44a20428a09e0..35f3683b77764 100644 --- a/onnxruntime/core/optimizer/conv_activation_fusion.cc +++ b/onnxruntime/core/optimizer/conv_activation_fusion.cc @@ -140,9 +140,12 @@ class FuseConvActivationAction : public ReplaceWithNew { return "FusedConv"; } } else if (domain == kMSDomain) { - if (op_type == "NhwcConv") { + if (op_type == "NhwcConv" || op_type == "NhwcFusedConv") { return "NhwcFusedConv"; } + if (op_type == "FusedConv") { + return "FusedConv"; + } } else if (domain == kMSInternalNHWCDomain) { if (op_type == "Conv") { return "Conv"; diff --git a/onnxruntime/core/optimizer/conv_add_act_fusion.cc b/onnxruntime/core/optimizer/conv_add_act_fusion.cc index 45441d20a4112..dfdc61c1b76c3 100644 --- a/onnxruntime/core/optimizer/conv_add_act_fusion.cc +++ b/onnxruntime/core/optimizer/conv_add_act_fusion.cc @@ -211,7 +211,15 @@ class FuseConvAddActivationAction : public ReplaceWithNew { private: std::string OpType(const RuntimeState& runtimeState) const override { - return (runtimeState.selected_nodes.Target().OpType() == "Conv") ? "FusedConv" : "NhwcFusedConv"; + const auto& target = runtimeState.selected_nodes.Target(); + const auto* channels_last_attr = graph_utils::GetNodeAttribute(target, "channels_last"); + const bool channels_last = channels_last_attr != nullptr && channels_last_attr->i() != 0; + + if (target.OpType() == "Conv") { + return channels_last ? "NhwcFusedConv" : "FusedConv"; + } + + return "NhwcFusedConv"; } std::string Domain(const RuntimeState&) const override { return kMSDomain; } diff --git a/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc b/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc index f611c992e0f57..5d51c855d13ba 100644 --- a/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc +++ b/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc @@ -68,6 +68,7 @@ const std::unordered_set& GetORTLayoutSensitiveOps() { // Define a static local string array so we can refer to the elements with string_views. static const std::string layout_sensitive_contrib_ops[]{ MakeORTLayoutSensitiveOpId(kMSDomain, "FusedConv"), + MakeORTLayoutSensitiveOpId(kMSDomain, "NhwcFusedConv"), MakeORTLayoutSensitiveOpId(kMSDomain, "GridSample"), MakeORTLayoutSensitiveOpId(kMSDomain, "QLinearAveragePool"), MakeORTLayoutSensitiveOpId(kMSDomain, "QLinearGlobalAveragePool"), diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index cd654991c92d5..9544cf7395025 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -2,7 +2,10 @@ // SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates // Licensed under the MIT License. +#include #include +#include +#include "core/graph/constants.h" #include "core/mlas/inc/mlas.h" #include "core/graph/graph_utils.h" #include "core/optimizer/initializer.h" @@ -21,6 +24,72 @@ namespace onnxruntime { using namespace layout_transformation; +#ifdef USE_KLEIDIAI +bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& graph, + onnx_transpose_optimization::api::NodeRef& node) { + auto& base_node = NodeFromApiNode(node); + + ORT_UNUSED_PARAMETER(graph); + if (base_node.InputDefs().size() < 2) { + return false; + } + + const auto* input_shape = base_node.InputDefs()[0]->Shape(); + if (input_shape == nullptr || input_shape->dim_size() != 4) { + return false; + } + + const auto& batch_dim = input_shape->dim(0); + if (!utils::HasDimValue(batch_dim) || batch_dim.dim_value() != 1) { + return false; + } + + const auto pads_attr = node.GetAttributeInts("pads"); + if (pads_attr.has_value()) { + const auto& pads = pads_attr.value(); + if (pads.size() != 4 || pads[0] != pads[2] || pads[1] != pads[3]) { + return false; + } + } + + const auto inputs = node.Inputs(); + if (inputs.size() > 3 && !inputs[3].empty()) { + return false; + } + + const auto* weight_shape = base_node.InputDefs()[1]->Shape(); + if (weight_shape == nullptr || weight_shape->dim_size() != 4) { + return false; + } + + const auto& filter_dim = weight_shape->dim(0); + const auto& kernel_h_dim = weight_shape->dim(2); + const auto& kernel_w_dim = weight_shape->dim(3); + + if (!utils::HasDimValue(filter_dim) || filter_dim.dim_value() <= 1 || + !utils::HasDimValue(kernel_h_dim) || kernel_h_dim.dim_value() < 3 || + !utils::HasDimValue(kernel_w_dim) || kernel_w_dim.dim_value() < 3) { + return false; + } + + const auto dilations_opt = node.GetAttributeInts("dilations"); + if (dilations_opt.has_value()) { + const auto& dilations = dilations_opt.value(); + if ((dilations.size() >= 1 && dilations[0] != 1) || + (dilations.size() >= 2 && dilations[1] != 1)) { + return false; + } + } + + const auto group_opt = node.GetAttributeInt("group"); + if (group_opt.has_value() && group_opt.value() != 1) { + return false; + } + + return true; +} +#endif + static inline const OpTransformInfo* NhwcConvLookup( const OpTransformMap& conv_table, @@ -41,6 +110,13 @@ NhwcConvLookup( if (iter == conv_table.end()) { return nullptr; } + + if (iter->second.filter_ != nullptr) { + if (!iter->second.filter_(graph, node)) { + return nullptr; + } + } + return &(iter->second); } @@ -108,15 +184,62 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, nhwc_conv_fp16.version_, nhwc_conv_fp16.type_constraints_, logger, &kernel_create_info); if (status.IsOK() && kernel_create_info != nullptr) { kernel_create_info = nullptr; + const auto filter = [](const api::GraphRef&, api::NodeRef& node) { + const auto dilations_opt = node.GetAttributeInts("dilations"); + if (dilations_opt.has_value()) { + const auto& dilations = dilations_opt.value(); + if ((dilations.size() >= 1 && dilations[0] != 1) || + (dilations.size() >= 2 && dilations[1] != 1)) { + return false; + } + } + + const auto group_opt = node.GetAttributeInt("group"); + if (group_opt.has_value() && group_opt.value() != 1) { + return false; + } + + return true; + }; + conv_table_.emplace( OpIdInfo("Conv", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, filter}); conv_table_.emplace( OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, filter}); } } +#ifdef USE_KLEIDIAI + // Klediai specific block for NhwcFusedConvolutions + { + // F32 Conv -> F32 NHWC Conv + OpKernelRegistryId nhwc_conv_fp32{ + "NhwcFusedConv", kMSDomain, 1, {{"T", {DataTypeImpl::GetTensorType()}}}}; + + const KernelCreateInfo* kernel_create_info{}; + const auto status = cpu_kernel_registry->TryFindKernel( + kCpuExecutionProvider, nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, + nhwc_conv_fp32.version_, nhwc_conv_fp32.type_constraints_, logger, &kernel_create_info); + + if (status.IsOK() && kernel_create_info != nullptr) { + kernel_create_info = nullptr; + + const auto filter = [](const api::GraphRef& graph, api::NodeRef& node) { + return KleidiFp32NhwcFilter(graph, node); + }; + + conv_table_.emplace( + OpIdInfo("Conv", kOnnxDomain, api::DataType::FLOAT), + OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, filter}); + conv_table_.emplace( + OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT), + OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, filter}); + } + } +#endif + { // fp16 MaxPool -> fp16 nhwc MaxPool OpKernelRegistryId nhwc_maxpool_fp16{ @@ -214,10 +337,39 @@ Status NhwcTransformer::ApplyImpl(Graph& graph, bool& modified, int graph_level, if (transform->has_channels_last_attrib_) { node->SetAttributeInt("channels_last", 1); } + + if (node->OpType() == "Conv" || node->OpType() == "FusedConv") { + const auto group_opt = node->GetAttributeInt("group"); + if (group_opt.has_value() && group_opt.value() != 1) { + continue; + } + + const auto dilations_opt = node->GetAttributeInts("dilations"); + if (dilations_opt.has_value()) { + const auto& dilations = dilations_opt.value(); + if ((dilations.size() >= 1 && dilations[0] != 1) || + (dilations.size() >= 2 && dilations[1] != 1)) { + continue; + } + } + } + size_t rank = shape->dim_size(); std::vector input_perm = ChannelFirstToLastPerm(rank); std::vector output_perm = ChannelLastToFirstPerm(rank); - WrapTransposesAroundNode(*api_graph, *node, {&input_perm}, {&output_perm}); + const auto inputs = node->Inputs(); + std::vector*> input_perms(inputs.size(), nullptr); + if (!inputs.empty()) { + input_perms[0] = &input_perm; + } + // Optional Sum (Z) input for FusedConv variants resides at index 3. When present, + // it must be converted to NHWC alongside the activation tensor. + const bool has_fused_sum_input = (node->Domain() == kMSDomain && node->OpType() == "FusedConv"); + if (has_fused_sum_input && inputs.size() > 3 && !inputs[3].empty()) { + input_perms[3] = &input_perm; + } + + WrapTransposesAroundNode(*api_graph, *node, input_perms, {&output_perm}); // Replace the operator if needed if (node->Domain() != transform->domain_ || diff --git a/onnxruntime/core/optimizer/nhwc_transformer.h b/onnxruntime/core/optimizer/nhwc_transformer.h index c65f851fdab9d..6dd11bdba6bdd 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.h +++ b/onnxruntime/core/optimizer/nhwc_transformer.h @@ -3,6 +3,7 @@ #pragma once +#include #include "core/common/common.h" #include "core/framework/execution_provider.h" #include "core/framework/kernel_registry.h" @@ -54,10 +55,14 @@ class OpIdHash { * @brief Information needed for operator layout transformation */ struct OpTransformInfo { + using FilterFn = std::function; + const std::string optype_; const std::string domain_; const int version_; const bool has_channels_last_attrib_; + const FilterFn filter_{nullptr}; }; using OpTransformMap = std::unordered_map; diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index e2db5ac238f52..7e73f09151a92 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -15,6 +15,8 @@ */ /* Modifications Copyright (c) Microsoft. */ +#include + #include "core/providers/cpu/nn/conv.h" #include "core/common/narrow.h" @@ -24,6 +26,44 @@ namespace onnxruntime { using ConvPadVector = ConvAttributes::ConvPadVector; +namespace { + +template +void ConvertNHWCToNCHW(const T* src, T* dst, + int64_t n, int64_t c, int64_t h, int64_t w) { + const int64_t hw = (SafeInt(h) * w); + for (int64_t n_idx = 0; n_idx < n; ++n_idx) { + const int64_t n_src_offset = n_idx * hw * c; + const int64_t n_dst_offset = n_idx * c * hw; + for (int64_t c_idx = 0; c_idx < c; ++c_idx) { + const T* src_ptr = src + n_src_offset + c_idx; + T* dst_ptr = dst + n_dst_offset + c_idx * hw; + for (int64_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + dst_ptr[hw_idx] = src_ptr[hw_idx * c]; + } + } + } +} + +template +void ConvertNCHWToNHWC(const T* src, T* dst, + int64_t n, int64_t c, int64_t h, int64_t w) { + const int64_t hw = (SafeInt(h) * w); + for (int64_t n_idx = 0; n_idx < n; ++n_idx) { + const int64_t n_src_offset = n_idx * c * hw; + const int64_t n_dst_offset = n_idx * hw * c; + for (int64_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + const T* src_ptr = src + n_src_offset + hw_idx; + T* dst_ptr = dst + n_dst_offset + hw_idx * c; + for (int64_t c_idx = 0; c_idx < c; ++c_idx) { + dst_ptr[c_idx] = src_ptr[c_idx * hw]; + } + } + } +} + +} // namespace + template Status Conv::Compute(OpKernelContext* context) const { const auto* X = context->Input(0); @@ -160,11 +200,10 @@ Status Conv::Compute(OpKernelContext* context) const { const Tensor* B = num_inputs >= 3 ? context->Input(2) : nullptr; const Tensor* Sum = num_inputs >= 4 ? context->Input(3) : nullptr; const int64_t N = X->Shape()[0]; - const int64_t C = X->Shape()[1]; + const int64_t C = X->Shape()[channels_last_ ? 3 : 1]; const int64_t M = W->Shape()[0]; - ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X, W)); + ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X->Shape(), W->Shape(), channels_last_)); - // kernel_shape is an optional attribute and has to be inferred from W if not provided TensorShapeVector kernel_shape; ORT_RETURN_IF_ERROR(conv_attrs_.ComputeKernelShape(W->Shape(), kernel_shape)); @@ -182,12 +221,14 @@ Status Conv::Compute(OpKernelContext* context) const { } TensorShapeVector Y_dims({N, M}); - TensorShape input_shape = X->Shape().Slice(2); + TensorShape input_shape = channels_last_ ? X->Shape().Slice(1, 3) : X->Shape().Slice(2); ORT_RETURN_IF_ERROR(conv_attrs_.InferPadsAndOutputShape(input_shape, kernel_shape, strides, dilations, pads, Y_dims)); + if (channels_last_) { + Y_dims = {Y_dims[0], Y_dims[2], Y_dims[3], Y_dims[1]}; + } Tensor* Y = context->Output(0, TensorShape(Y_dims)); - TensorShape output_shape = Y->Shape().Slice(2); + TensorShape output_shape = channels_last_ ? TensorShape(Y_dims).Slice(1, 3) : Y->Shape().Slice(2); - // Bail out early if one of the dimensions is zero. if (Y->Shape().Size() == 0) { return Status::OK(); } @@ -198,20 +239,39 @@ Status Conv::Compute(OpKernelContext* context) const { auto Xdata = X->DataAsSpan(); const auto* Bdata = B != nullptr ? B->Data() : nullptr; auto Ydata = Y->MutableDataAsSpan(); - // Check for the optional Conv/Sum fusion. + const size_t kernel_rank = kernel_shape.size(); + concurrency::ThreadPool* thread_pool = context->GetOperatorThreadPool(); + + if (channels_last_) { + ORT_RETURN_IF_NOT(kernel_rank == 2, "NhwcFusedConv currently supports 2D kernels."); + ORT_RETURN_IF_NOT(dilations[0] == 1 && dilations[1] == 1, "NhwcFusedConv currently supports dilation == 1."); + } + + const bool wants_channels_last = channels_last_; + const bool sum_present = Sum != nullptr; + const bool nhwc_fastpath = + wants_channels_last && kernel_rank == 2 && conv_attrs_.group == 1 && + dilations[0] == 1 && dilations[1] == 1 && !sum_present; + const bool manual_sum = wants_channels_last && !nhwc_fastpath && sum_present; + + std::vector sum_manual_buffer; + const float* sum_manual_data = nullptr; + float Beta = 0.0f; - if (Sum != nullptr) { + if (sum_present) { const auto& sum_shape = Sum->Shape(); ORT_RETURN_IF_NOT(Y->Shape() == sum_shape, "output and sum shape must match"); - // If the output was not allocated inplace with the sum tensor, then copy here. - auto sum_data = Sum->DataAsSpan(); - if (Ydata.data() != sum_data.data()) { - gsl::copy(sum_data, Ydata); + if (manual_sum) { + sum_manual_buffer.assign(Sum->Data(), Sum->Data() + Y->Shape().Size()); + sum_manual_data = sum_manual_buffer.data(); + } else { + auto sum_span = Sum->DataAsSpan(); + if (Ydata.data() != sum_span.data()) { + gsl::copy(sum_span, Ydata); + } + Beta = 1.0f; } - Beta = 1.0f; } - const size_t kernel_rank = kernel_shape.size(); - concurrency::ThreadPool* thread_pool = context->GetOperatorThreadPool(); if (kernel_rank >= 1 && kernel_rank <= 3) { MLAS_CONV_PARAMETERS Parameters; @@ -232,20 +292,66 @@ Status Conv::Compute(OpKernelContext* context) const { narrow(M / conv_attrs_.group), &activation_, &WorkingBufferSize, - Beta, + nhwc_fastpath, + nhwc_fastpath ? 0.0f : Beta, thread_pool); - auto* working_data = WorkingBufferSize > 0 ? alloc->Alloc(sizeof(float) * SafeInt(WorkingBufferSize)) - : nullptr; - BufferUniquePtr working_buffer(working_data, BufferDeleter(std::move(alloc))); + float* working_data = nullptr; + BufferUniquePtr working_buffer; + if (WorkingBufferSize > 0) { + working_data = static_cast(alloc->Alloc(sizeof(float) * SafeInt(WorkingBufferSize))); + working_buffer = BufferUniquePtr(working_data, BufferDeleter(alloc)); + } + + float* output_compute = Ydata.data(); + BufferUniquePtr output_temp; + if (wants_channels_last && !nhwc_fastpath) { + const SafeInt output_compute_size = + SafeInt(Y->Shape()[0]) * SafeInt(M) * + SafeInt(output_shape[0]) * SafeInt(output_shape[1]); + float* temp_output = static_cast(alloc->Alloc(sizeof(float) * output_compute_size)); + output_temp = BufferUniquePtr(temp_output, BufferDeleter(alloc)); + output_compute = temp_output; + } + + const float* input_compute = Xdata.data(); + BufferUniquePtr input_temp; + if (wants_channels_last && !nhwc_fastpath) { + ORT_RETURN_IF_NOT(X->Shape().NumDimensions() == 4, "Nhwc fallback expects 4D input."); + const auto& x_dims = X->Shape().GetDims(); + const int64_t input_n = x_dims[0]; + const int64_t input_h = x_dims[1]; + const int64_t input_w = x_dims[2]; + const int64_t input_c = x_dims[3]; + const SafeInt input_elements = SafeInt(X->Shape().Size()); + float* temp_input = static_cast(alloc->Alloc(sizeof(float) * input_elements)); + input_temp = BufferUniquePtr(temp_input, BufferDeleter(alloc)); + ConvertNHWCToNCHW(X->Data(), temp_input, + input_n, input_c, input_h, input_w); + input_compute = temp_input; + } MlasConv(&Parameters, - Xdata.data(), + input_compute, W->Data(), Bdata, - static_cast(working_buffer.get()), - Ydata.data(), + working_data, + output_compute, thread_pool); + + if (wants_channels_last && !nhwc_fastpath) { + const auto& y_dims = Y->Shape().GetDims(); + ORT_RETURN_IF_NOT(y_dims.size() == 4, "Nhwc fallback expects 4D output."); + ConvertNCHWToNHWC(output_compute, + Ydata.data(), + y_dims[0], y_dims[3], y_dims[1], y_dims[2]); + if (manual_sum) { + auto y_span = gsl::make_span(Ydata.data(), Ydata.size()); + for (size_t i = 0; i < y_span.size(); ++i) { + y_span[i] += sum_manual_data[i]; + } + } + } } else { const int64_t input_image_size = input_shape.Size(); const int64_t output_image_size = output_shape.Size(); @@ -287,7 +393,8 @@ Status Conv::Compute(OpKernelContext* context) const { &mlas_backend_kernel_selector_config_); } - MlasActivation(&activation_, Ydata.data(), Bdata, narrow(M), narrow(output_image_size), narrow(output_image_size)); + MlasActivation(&activation_, Ydata.data(), Bdata, narrow(M), + narrow(output_image_size), narrow(output_image_size)); Xdata = Xdata.subspan(X_offset * conv_attrs_.group); Ydata = Ydata.subspan(Y_offset * conv_attrs_.group); diff --git a/onnxruntime/core/providers/cpu/nn/conv.h b/onnxruntime/core/providers/cpu/nn/conv.h index 8992af9792d13..5633739b43e4b 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.h +++ b/onnxruntime/core/providers/cpu/nn/conv.h @@ -25,7 +25,7 @@ class Conv : public OpKernel { template <> class Conv : public OpKernel { public: - Conv(const OpKernelInfo& info) : OpKernel(info), conv_attrs_(info) { + Conv(const OpKernelInfo& info) : OpKernel(info), conv_attrs_(info), channels_last_(info.GetKernelDef().OpName() == "NhwcFusedConv") { activation_.ActivationKind = MlasIdentityActivation; SetupMlasBackendKernelSelectorFromConfigOptions(mlas_backend_kernel_selector_config_, info.GetConfigOptions()); } @@ -38,6 +38,7 @@ class Conv : public OpKernel { MLAS_BACKEND_KERNEL_SELECTOR_CONFIG mlas_backend_kernel_selector_config_; ConvAttributes conv_attrs_; + bool channels_last_{false}; }; } // namespace onnxruntime diff --git a/onnxruntime/core/util/math_cpu.cc b/onnxruntime/core/util/math_cpu.cc index 3c7dc53b1098f..608d30c12b587 100644 --- a/onnxruntime/core/util/math_cpu.cc +++ b/onnxruntime/core/util/math_cpu.cc @@ -845,6 +845,7 @@ void Im2col::operator()( template struct Im2col; template struct Im2col; template struct Im2col; +template struct Im2col; template <> void Col2im(const float* data_col, int64_t channels, int64_t height, diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index ec4f8967fd2a3..ca40a34974e09 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -17,6 +17,8 @@ #include "test/util/include/asserts.h" #include "test/util/include/inference_session_wrapper.h" +#include +#include #include "flatbuffers/idl.h" #include "flatbuffers/util.h" @@ -27,6 +29,28 @@ using namespace ONNX_NAMESPACE; namespace onnxruntime { namespace test { +namespace { +std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { + if (path.is_absolute() || path.empty()) { + return path; + } + + std::filesystem::path workspace_candidate = std::filesystem::current_path() / path; + if (std::filesystem::exists(workspace_candidate)) { + return workspace_candidate; + } + + static const std::filesystem::path kSourceTestRoot = + std::filesystem::path{ORT_TSTR(__FILE__)}.parent_path().parent_path(); + std::filesystem::path source_candidate = kSourceTestRoot / path; + if (std::filesystem::exists(source_candidate)) { + return source_candidate; + } + + return workspace_candidate; +} +} // namespace + struct OrtModelTestInfo { std::basic_string model_filename; std::string logid; @@ -65,17 +89,21 @@ static void RunOrtModel(const OrtModelTestInfo& test_info) { std::vector model_data; InferenceSessionWrapper session_object{so, GetEnvironment()}; + std::filesystem::path model_path = ResolveTestPath(std::filesystem::path{test_info.model_filename}); + + std::cerr << "RunOrtModel cwd: " << std::filesystem::current_path() << " loading: " << model_path << std::endl; + const auto& model_path_str = model_path.native(); if (test_info.run_use_buffer) { // Load the file into a buffer and use the buffer to create inference session size_t num_bytes = 0; - ASSERT_STATUS_OK(Env::Default().GetFileLength(test_info.model_filename.c_str(), num_bytes)); + ASSERT_STATUS_OK(Env::Default().GetFileLength(model_path_str.c_str(), num_bytes)); model_data.resize(num_bytes); - std::ifstream bytes_stream(test_info.model_filename, std::ifstream::in | std::ifstream::binary); + std::ifstream bytes_stream(model_path, std::ifstream::in | std::ifstream::binary); bytes_stream.read(model_data.data(), num_bytes); bytes_stream.close(); ASSERT_STATUS_OK(session_object.Load(model_data.data(), static_cast(num_bytes))); } else { - ASSERT_STATUS_OK(session_object.Load(test_info.model_filename)); // infer type from filename + ASSERT_STATUS_OK(session_object.Load(model_path_str)); // infer type from filename } ASSERT_STATUS_OK(session_object.Initialize()); @@ -151,7 +179,7 @@ static void CompareGraphAndSessionState(const InferenceSessionWrapper& session_o for (const auto& pair : i1) { auto iter = i2.find(pair.first); - ASSERT_NE(iter, i2.cend()); + ASSERT_NE(iter, i2.cend()) << "Missing initializer " << pair.first; const OrtValue& left = pair.second; const OrtValue& right = iter->second; @@ -219,9 +247,28 @@ static void CompareSessionMetadata(const InferenceSessionWrapper& session_object static void SaveAndCompareModels(const PathString& orig_file, const PathString& ort_file, TransformerLevel optimization_level = TransformerLevel::Level3) { + std::filesystem::path orig_path = ResolveTestPath(std::filesystem::path{orig_file}); + std::filesystem::path ort_path = ResolveTestPath(std::filesystem::path{ort_file}); + if (ort_path.has_parent_path()) { + std::filesystem::create_directories(ort_path.parent_path()); + } + + const bool orig_is_ort_format = orig_path.extension() == ORT_TSTR(".ort"); + if (orig_is_ort_format) { + SessionOptions so; + so.session_logid = "SerializeToOrtFormat"; + so.optimized_model_filepath = ort_path.native(); + so.graph_optimization_level = optimization_level; + ASSERT_STATUS_OK(so.config_options.AddConfigEntry(kOrtSessionOptionsConfigSaveModelFormat, "ORT")); + InferenceSessionWrapper session_object{so, GetEnvironment()}; + ASSERT_STATUS_OK(session_object.Load(orig_path.native())); + ASSERT_STATUS_OK(session_object.Initialize()); + return; + } + SessionOptions so; so.session_logid = "SerializeToOrtFormat"; - so.optimized_model_filepath = ort_file; + so.optimized_model_filepath = ort_path.native(); so.graph_optimization_level = optimization_level; // not strictly necessary - type should be inferred from the filename @@ -229,7 +276,7 @@ static void SaveAndCompareModels(const PathString& orig_file, InferenceSessionWrapper session_object{so, GetEnvironment()}; // create .ort file during Initialize due to values in SessionOptions - ASSERT_STATUS_OK(session_object.Load(orig_file)); + ASSERT_STATUS_OK(session_object.Load(orig_path.native())); ASSERT_STATUS_OK(session_object.Initialize()); SessionOptions so2; @@ -240,7 +287,7 @@ static void SaveAndCompareModels(const PathString& orig_file, // load serialized version InferenceSessionWrapper session_object2{so2, GetEnvironment()}; - ASSERT_STATUS_OK(session_object2.Load(ort_file)); + ASSERT_STATUS_OK(session_object2.Load(ort_path.native())); ASSERT_STATUS_OK(session_object2.Initialize()); CompareSessionMetadata(session_object, session_object2); diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index 74a812062875a..b9c58ca386b12 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -22,6 +22,7 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" +#include using namespace ONNX_NAMESPACE; using namespace onnxruntime::logging; @@ -36,12 +37,35 @@ using namespace onnxruntime::internal_testing_ep; #define ORT_MODEL_FOLDER ORT_TSTR("testdata/") +namespace { +std::filesystem::path ResolveInternalTestPath(const std::filesystem::path& path) { + if (path.is_absolute() || path.empty()) { + return path; + } + + std::filesystem::path candidate = std::filesystem::current_path() / path; + if (std::filesystem::exists(candidate)) { + return candidate; + } + + static const std::filesystem::path kSourceTestRoot = + std::filesystem::path{ORT_TSTR(__FILE__)}.parent_path().parent_path().parent_path(); + return kSourceTestRoot / path; +} + +std::basic_string ResolveInternalTestPathString(const ORTCHAR_T* path) { + return ResolveInternalTestPath(std::filesystem::path{path}).native(); +} +} // namespace + static Status CreateSession(const SessionOptions& so, std::unique_ptr& session, const ORTCHAR_T* model_path = ORT_MODEL_FOLDER "mnist.onnx", // arbitrary test model bool enable_custom_ep = true, const std::unordered_set* override_supported_ops = nullptr) { session = std::make_unique(so, GetEnvironment()); + std::filesystem::path resolved_model_path = ResolveInternalTestPath(std::filesystem::path{model_path}); + // set supported ops to ops that are ideally found consecutively in the model. // we can say the EP potentially handles them all, but can also test removing handling of one or more ops // at runtime to simulate a lower spec device where not all ops can be handled. this allows us to test @@ -55,7 +79,7 @@ static Status CreateSession(const SessionOptions& so, std::unique_ptr(*supported_ops))); } - ORT_RETURN_IF_ERROR(session->Load(model_path)); + ORT_RETURN_IF_ERROR(session->Load(resolved_model_path.c_str())); ORT_RETURN_IF_ERROR(session->Initialize()); return Status::OK(); } @@ -98,7 +122,7 @@ static void ExecuteMnist(InferenceSessionWrapper& session, bool custom_ep_enable #if !defined(ORT_MINIMAL_BUILD) TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.test_output.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.test_output.ort"); // // First load the onnx format model and save as an ORT model. @@ -121,10 +145,10 @@ TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { so.optimized_model_filepath.clear(); bool enable_custom_ep = false; - ASSERT_STATUS_OK(CreateSession(so, session2, ort_model_path, enable_custom_ep)); + ASSERT_STATUS_OK(CreateSession(so, session2, ort_model_path.c_str(), enable_custom_ep)); const auto& graph1 = session2->GetGraph(); - // model should have all the original nodes and we should be able to execute with the fallback to CPU EP - ASSERT_EQ(graph1.NumberOfNodes(), num_nodes); + // ensure we can execute with the fallback to CPU EP even if additional nodes are introduced during loading + ASSERT_GE(graph1.NumberOfNodes(), num_nodes); ExecuteMnist(*session2, enable_custom_ep); session2 = nullptr; @@ -133,7 +157,7 @@ TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { // for the ORT format model. // enable_custom_ep = true; - ASSERT_STATUS_OK(CreateSession(so, session2, ort_model_path, enable_custom_ep)); + ASSERT_STATUS_OK(CreateSession(so, session2, ort_model_path.c_str(), enable_custom_ep)); const auto& graph2 = session2->GetGraph(); // model should be able to be loaded, and we should compile using custom ep. that will result in one node for the // custom EP (with Conv/Add/Relu/MaxPool), one for a reshape, and one for the fused MatMul+Add. @@ -142,7 +166,7 @@ TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { } TEST(InternalTestingEP, PreventSaveOfModelWithCompiledOps) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"); // make sure we can't save a model with compiled ops. input/output model format doesn't matter SessionOptions so; @@ -154,7 +178,7 @@ TEST(InternalTestingEP, PreventSaveOfModelWithCompiledOps) { ASSERT_STATUS_OK(session->RegisterExecutionProvider( std::make_unique(supported_ops))); - ASSERT_STATUS_OK(session->Load(ort_model_path)); + ASSERT_STATUS_OK(session->Load(ort_model_path.c_str())); ASSERT_STATUS_NOT_OK_AND_HAS_SUBSTR(session->Initialize(), "Unable to serialize model as it contains compiled nodes"); } @@ -163,7 +187,7 @@ TEST(InternalTestingEP, PreventSaveOfModelWithCompiledOps) { // version of the ONNX operator when matching a static kernel, those are required. #if !defined(DISABLE_CONTRIB_OPS) TEST(InternalTestingEP, TestMixOfStaticAndCompiledKernels) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "transform/fusion/conv_relu_opset12.onnx"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "transform/fusion/conv_relu_opset12.onnx"); SessionOptions so; InferenceSessionWrapper session(so, GetEnvironment()); @@ -175,7 +199,7 @@ TEST(InternalTestingEP, TestMixOfStaticAndCompiledKernels) { ep->EnableStaticKernels(); ASSERT_STATUS_OK(session.RegisterExecutionProvider(std::move(ep))); - ASSERT_STATUS_OK(session.Load(ort_model_path)); + ASSERT_STATUS_OK(session.Load(ort_model_path.c_str())); ASSERT_STATUS_OK(session.Initialize()); TensorShape input_shape_x{1, 1, 7, 7}; @@ -204,7 +228,8 @@ TEST(InternalTestingEP, TestMixOfStaticAndCompiledKernels) { TEST(InternalTestingEP, TestNhwcConversionOfStaticKernels) { auto run_test = [&](const ORTCHAR_T* model_path) { - SCOPED_TRACE("model path: " + ToUTF8String(model_path)); + auto resolved_model_path = ResolveInternalTestPathString(model_path); + SCOPED_TRACE("model path: " + ToUTF8String(resolved_model_path.c_str())); SessionOptions so; // set this if you want to manually inspect the optimized model @@ -218,7 +243,7 @@ TEST(InternalTestingEP, TestNhwcConversionOfStaticKernels) { ep->EnableStaticKernels(); ASSERT_STATUS_OK(session.RegisterExecutionProvider(std::move(ep))); - ASSERT_STATUS_OK(session.Load(model_path)); + ASSERT_STATUS_OK(session.Load(resolved_model_path.c_str())); ASSERT_STATUS_OK(session.Initialize()); const auto& graph = session.GetGraph(); @@ -249,13 +274,11 @@ TEST(InternalTestingEP, TestNhwcConversionOfStaticKernels) { }; // the internal NHWC domain supports opset 11 and later - const ORTCHAR_T* onnx_model_path = ORT_MODEL_FOLDER "squeezenet/model_opset11.onnx"; - run_test(onnx_model_path); + run_test(ORT_MODEL_FOLDER "squeezenet/model_opset11.onnx"); // Note: Using ORT format model with runtime optimizations so that the Conv nodes are preserved in the graph, // not converted into FusedConv nodes. The InternalTestingExecutionProvider handles Conv nodes. - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "squeezenet/model_opset11.with_runtime_opt.ort"; - run_test(ort_model_path); + run_test(ORT_MODEL_FOLDER "squeezenet/model_opset11.with_runtime_opt.ort"); } // make sure allocators returned by SessionState::GetAllocator are valid when IExecutionProvider::ReplaceAllocator @@ -283,8 +306,8 @@ TEST(InternalTestingEP, TestReplaceAllocatorDoesntBreakDueToLocalAllocatorStorag ASSERT_STATUS_OK(session.RegisterExecutionProvider(ep)); } - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "squeezenet/model.onnx"; - ASSERT_STATUS_OK(session.Load(ort_model_path)); + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "squeezenet/model.onnx"); + ASSERT_STATUS_OK(session.Load(ort_model_path.c_str())); ASSERT_STATUS_OK(session.Initialize()); // Need to undo the wrapping that happens in Environment::RegisterAllocator to be able to compare the pointers @@ -301,25 +324,25 @@ TEST(InternalTestingEP, TestReplaceAllocatorDoesntBreakDueToLocalAllocatorStorag // test to validate a minimal build TEST(InternalTestingEP, TestLoadOrtModel) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"); std::unique_ptr session; bool enable_custom_ep = true; - ASSERT_STATUS_OK(CreateSession(SessionOptions{}, session, ort_model_path, enable_custom_ep)); + ASSERT_STATUS_OK(CreateSession(SessionOptions{}, session, ort_model_path.c_str(), enable_custom_ep)); ExecuteMnist(*session, enable_custom_ep); } // test that if the custom EP cannot take all nodes due to device limitations // that we fallback to the CPU implementations and can execute the model TEST(InternalTestingEP, TestLoadOrtModelWithReducedOpCoverage) { - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"); const std::unordered_set supported_ops{"Conv", "Add", "Relu" /*, "MaxPool"*/}; std::unique_ptr session; bool enable_custom_ep = true; - ASSERT_STATUS_OK(CreateSession(SessionOptions{}, session, ort_model_path, enable_custom_ep, &supported_ops)); + ASSERT_STATUS_OK(CreateSession(SessionOptions{}, session, ort_model_path.c_str(), enable_custom_ep, &supported_ops)); const auto& graph = session->GetGraph(); // Conv+Add gets fused by level 1 optimizer into single node. The 'Conv'/'Add'/'Relu' nodes should be compiled and @@ -454,7 +477,7 @@ TEST(InternalTestingEP, TestOrtModelWithCompileFailure) { // the layout transformation for this EP is already done at this stage and reverting // can result in more failures. // This is to test the model initialization fails if compile fails. - const ORTCHAR_T* ort_model_path = ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"; + const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.ort"); const std::unordered_set& supported_ops{"Conv", "Gemm"}; const std::unordered_set& compile_failure_ops{"Gemm"}; diff --git a/onnxruntime/test/mlas/bench/bench_sconv.cpp b/onnxruntime/test/mlas/bench/bench_sconv.cpp index 4b6128f7e19da..dfc7987274baf 100644 --- a/onnxruntime/test/mlas/bench/bench_sconv.cpp +++ b/onnxruntime/test/mlas/bench/bench_sconv.cpp @@ -110,6 +110,7 @@ void SCONV_NCHW(benchmark::State& state, const char* /*dummy*/) { static_cast(output_channels_per_group), &activation, &WorkingBufferSize, + false, 0.0f, nullptr); diff --git a/onnxruntime/test/mlas/unittest/test_conv2d.h b/onnxruntime/test/mlas/unittest/test_conv2d.h index 37a844fdb4b02..736d8587b2546 100644 --- a/onnxruntime/test/mlas/unittest/test_conv2d.h +++ b/onnxruntime/test/mlas/unittest/test_conv2d.h @@ -3,47 +3,33 @@ #pragma once -#include -#include - #include "test_util.h" -#if defined(MLAS_TARGET_AMD64) -#include "core/mlas/lib/mlasi.h" -#endif - -#if defined(USE_KLEIDIAI) && defined(MLAS_ENABLE_TEST_HOOKS) -#include "core/mlas/lib/kleidiai/mlasi_kleidiai.h" -#endif - template class MlasConv2DTest : public MlasTestBase { protected: - void MlasConv2DWithOptions(size_t BatchCount, - size_t GroupCount, - size_t InputChannels, - size_t InputHeight, - size_t InputWidth, - size_t FilterCount, - size_t KernelHeight, - size_t KernelWidth, - size_t PaddingLeftHeight, - size_t PaddingLeftWidth, - size_t PaddingRightHeight, - size_t PaddingRightWidth, - size_t DilationHeight, - size_t DilationWidth, - size_t StrideHeight, - size_t StrideWidth, - size_t OutputHeight, - size_t OutputWidth, - const float* Input, - const float* Filter, - const float* Bias, - const MLAS_ACTIVATION& Activation, - float Beta, - const float* InitialOutput, - float* Output) { + virtual void MlasConv2D(size_t BatchCount, + size_t GroupCount, + size_t InputChannels, + size_t InputHeight, + size_t InputWidth, + size_t FilterCount, + size_t KernelHeight, + size_t KernelWidth, + size_t PaddingLeftHeight, + size_t PaddingLeftWidth, + size_t PaddingRightHeight, + size_t PaddingRightWidth, + size_t DilationHeight, + size_t DilationWidth, + size_t StrideHeight, + size_t StrideWidth, + size_t OutputHeight, + size_t OutputWidth, + const float* Input, + const float* Filter, + const float* Bias, + float* Output) { int64_t InputShape[] = {int64_t(InputHeight), int64_t(InputWidth)}; int64_t KernelShape[] = {int64_t(KernelHeight), int64_t(KernelWidth)}; int64_t DilationShape[] = {int64_t(DilationHeight), int64_t(DilationWidth)}; @@ -51,12 +37,8 @@ class MlasConv2DTest : public MlasTestBase { int64_t StrideShape[] = {int64_t(StrideHeight), int64_t(StrideWidth)}; int64_t OutputShape[] = {int64_t(OutputHeight), int64_t(OutputWidth)}; - const size_t OutputElements = BatchCount * GroupCount * FilterCount * OutputHeight * OutputWidth; - if (InitialOutput != nullptr) { - std::memcpy(Output, InitialOutput, OutputElements * sizeof(float)); - } else { - std::fill_n(Output, OutputElements, 0.0f); - } + MLAS_ACTIVATION Activation; + Activation.ActivationKind = MlasIdentityActivation; MLAS_CONV_PARAMETERS Parameters; size_t WorkingBufferSize; @@ -75,7 +57,8 @@ class MlasConv2DTest : public MlasTestBase { FilterCount, &Activation, &WorkingBufferSize, - Beta, + false, + 0.0f, threadpool_); MlasConv(&Parameters, @@ -87,72 +70,7 @@ class MlasConv2DTest : public MlasTestBase { threadpool_); } - virtual void MlasConv2D(size_t BatchCount, - size_t GroupCount, - size_t InputChannels, - size_t InputHeight, - size_t InputWidth, - size_t FilterCount, - size_t KernelHeight, - size_t KernelWidth, - size_t PaddingLeftHeight, - size_t PaddingLeftWidth, - size_t PaddingRightHeight, - size_t PaddingRightWidth, - size_t DilationHeight, - size_t DilationWidth, - size_t StrideHeight, - size_t StrideWidth, - size_t OutputHeight, - size_t OutputWidth, - const float* Input, - const float* Filter, - const float* Bias, - float* Output) { - MLAS_ACTIVATION Activation; - Activation.ActivationKind = MlasIdentityActivation; - - MlasConv2DWithOptions(BatchCount, - GroupCount, - InputChannels, - InputHeight, - InputWidth, - FilterCount, - KernelHeight, - KernelWidth, - PaddingLeftHeight, - PaddingLeftWidth, - PaddingRightHeight, - PaddingRightWidth, - DilationHeight, - DilationWidth, - StrideHeight, - StrideWidth, - OutputHeight, - OutputWidth, - Input, - Filter, - Bias, - Activation, - 0.0f, - nullptr, - Output); - } - - static float ApplyReferenceActivation(float value, const MLAS_ACTIVATION& Activation) { - switch (Activation.ActivationKind) { - case MlasIdentityActivation: - return value; - case MlasReluActivation: - return std::max(value, 0.0f); - default: - ADD_FAILURE() << "Unsupported activation kind in Conv2D test reference path: " - << static_cast(Activation.ActivationKind); - return value; - } - } - - void ReferenceConv2DWithOptions( + void ReferenceConv2D( size_t BatchCount, size_t GroupCount, size_t InputChannels, @@ -172,24 +90,14 @@ class MlasConv2DTest : public MlasTestBase { const float* Input, const float* Filter, const float* Bias, - const MLAS_ACTIVATION& Activation, - float Beta, - const float* InitialOutput, float* Output) { size_t InputSize = InputHeight * InputWidth; size_t OutputSize = OutputHeight * OutputWidth; size_t KernelSize = KernelHeight * KernelWidth; - size_t OutputElements = BatchCount * GroupCount * FilterCount * OutputSize; size_t K = InputChannels * KernelSize; size_t Im2ColElements = OutputSize * K; - if (InitialOutput != nullptr) { - std::memcpy(Output, InitialOutput, OutputElements * sizeof(float)); - } else { - std::fill_n(Output, OutputElements, 0.0f); - } - for (size_t b = 0; b < BatchCount; b++) { const float* filter = Filter; const float* bias = Bias; @@ -220,81 +128,31 @@ class MlasConv2DTest : public MlasTestBase { Input += InputSize; } - float* output_group = Output; - MlasGemm(CblasNoTrans, CblasNoTrans, FilterCount, OutputSize, K, 1.0f, - filter, K, Im2Col, OutputSize, Beta, output_group, OutputSize, threadpool_, nullptr); + filter, K, Im2Col, OutputSize, 0.0f, Output, OutputSize, threadpool_); + + // + // Apply the bias. + // for (size_t f = 0; f < FilterCount; f++) { float biasValue = *bias++; - float* output_row = output_group + f * OutputSize; for (size_t o = 0; o < OutputSize; o++) { - output_row[o] = ApplyReferenceActivation(output_row[o] + biasValue, Activation); + *Output++ += biasValue; } } filter += FilterCount * InputChannels * KernelSize; - Output += FilterCount * OutputSize; } } } - void ReferenceConv2D( - size_t BatchCount, - size_t GroupCount, - size_t InputChannels, - size_t InputHeight, - size_t InputWidth, - size_t FilterCount, - size_t KernelHeight, - size_t KernelWidth, - size_t PaddingLeftHeight, - size_t PaddingLeftWidth, - size_t DilationHeight, - size_t DilationWidth, - size_t StrideHeight, - size_t StrideWidth, - size_t OutputHeight, - size_t OutputWidth, - const float* Input, - const float* Filter, - const float* Bias, - float* Output) { - MLAS_ACTIVATION Activation; - Activation.ActivationKind = MlasIdentityActivation; - - ReferenceConv2DWithOptions(BatchCount, - GroupCount, - InputChannels, - InputHeight, - InputWidth, - FilterCount, - KernelHeight, - KernelWidth, - PaddingLeftHeight, - PaddingLeftWidth, - DilationHeight, - DilationWidth, - StrideHeight, - StrideWidth, - OutputHeight, - OutputWidth, - Input, - Filter, - Bias, - Activation, - 0.0f, - nullptr, - Output); - } - MatrixGuardBuffer BufferInput; MatrixGuardBuffer BufferFilter; MatrixGuardBuffer BufferBias; MatrixGuardBuffer BufferOutput; MatrixGuardBuffer BufferOutputReference; - MatrixGuardBuffer BufferInitialOutput; MatrixGuardBuffer BufferWorking; MatrixGuardBuffer BufferIm2Col; @@ -308,355 +166,6 @@ class MlasConv2DTest : public MlasTestBase { MlasConv2DTest() : threadpool_(Threaded ? GetMlasThreadPool() : nullptr) {} -#if defined(USE_KLEIDIAI) && defined(MLAS_ENABLE_TEST_HOOKS) - void TestKleidiAILhsCacheIgnoresInputContent() { - if (!ArmKleidiAI::UseSME) { - return; - } - - struct ConvGeometry { - const char* name; - size_t input_channels; - size_t input_height; - size_t input_width; - size_t filter_count; - size_t kernel_height; - size_t kernel_width; - size_t padding; - size_t dilation_height; - size_t dilation_width; - size_t stride_height; - size_t stride_width; - }; - - constexpr size_t BatchCount = 1; - constexpr size_t GroupCount = 1; - constexpr ConvGeometry geometries[] = { - {"padded_3x3", 16, 11, 11, 32, 3, 3, 1, 1, 1, 1, 1}, - {"strided_3x3", 24, 13, 9, 16, 3, 3, 1, 1, 1, 2, 2}, - {"dilated_3x3", 8, 15, 13, 20, 3, 3, 2, 2, 2, 1, 1}, - {"wide_kernel", 12, 10, 12, 24, 3, 5, 2, 1, 1, 1, 2}, - }; - - const auto fill_input = [](std::vector& input, size_t seed) { - for (size_t i = 0; i < input.size(); ++i) { - input[i] = static_cast((static_cast((i * (seed + 3)) % 31) - 15) * 0.075f + - static_cast(seed) * 0.125f); - } - }; - - for (const auto& geometry : geometries) { - SCOPED_TRACE(geometry.name); - - const int64_t output_height64 = - ((int64_t(geometry.input_height) + 2 * int64_t(geometry.padding)) - - (int64_t(geometry.dilation_height) * (int64_t(geometry.kernel_height) - 1) + 1)) / - int64_t(geometry.stride_height) + - 1; - const int64_t output_width64 = - ((int64_t(geometry.input_width) + 2 * int64_t(geometry.padding)) - - (int64_t(geometry.dilation_width) * (int64_t(geometry.kernel_width) - 1) + 1)) / - int64_t(geometry.stride_width) + - 1; - - ASSERT_GT(output_height64, 0); - ASSERT_GT(output_width64, 0); - - const size_t output_height = static_cast(output_height64); - const size_t output_width = static_cast(output_width64); - const size_t input_elements = - BatchCount * GroupCount * geometry.input_channels * geometry.input_height * geometry.input_width; - const size_t filter_elements = GroupCount * geometry.filter_count * geometry.input_channels * - geometry.kernel_height * geometry.kernel_width; - const size_t bias_elements = GroupCount * geometry.filter_count; - const size_t output_elements = BatchCount * GroupCount * geometry.filter_count * output_height * output_width; - - std::vector input_a(input_elements); - std::vector input_a_copy(input_elements); - std::vector input_b(input_elements); - std::vector filter(filter_elements); - std::vector bias(bias_elements); - std::vector output(output_elements); - std::vector output_reference(output_elements); - - fill_input(input_a, 1); - input_a_copy = input_a; - fill_input(input_b, 2); - - for (size_t i = 0; i < filter_elements; ++i) { - filter[i] = static_cast((static_cast((i * 5) % 23) - 11) * 0.05f); - } - - for (size_t i = 0; i < bias_elements; ++i) { - bias[i] = static_cast((static_cast(i % 7) - 3) * 0.02f); - } - - const auto verify_conv = [&](const std::vector& input, const char* label) { - SCOPED_TRACE(label); - - MlasConv2D(BatchCount, - GroupCount, - geometry.input_channels, - geometry.input_height, - geometry.input_width, - geometry.filter_count, - geometry.kernel_height, - geometry.kernel_width, - geometry.padding, - geometry.padding, - geometry.padding, - geometry.padding, - geometry.dilation_height, - geometry.dilation_width, - geometry.stride_height, - geometry.stride_width, - output_height, - output_width, - input.data(), - filter.data(), - bias.data(), - output.data()); - - ReferenceConv2D(BatchCount, - GroupCount, - geometry.input_channels, - geometry.input_height, - geometry.input_width, - geometry.filter_count, - geometry.kernel_height, - geometry.kernel_width, - geometry.padding, - geometry.padding, - geometry.dilation_height, - geometry.dilation_width, - geometry.stride_height, - geometry.stride_width, - output_height, - output_width, - input.data(), - filter.data(), - bias.data(), - output_reference.data()); - - for (size_t i = 0; i < output_elements; ++i) { - ASSERT_TRUE(CloseEnough(output[i], output_reference[i])) - << "Mismatch at output index " << i - << ": actual=" << output[i] - << ", expected=" << output_reference[i]; - } - }; - - ArmKleidiAI::MlasConvClearLhsCacheForTest(); - EXPECT_EQ(ArmKleidiAI::MlasConvLhsCacheEntryCountForTest(), size_t{0}); - - verify_conv(input_a, "initial_input"); - EXPECT_EQ(ArmKleidiAI::MlasConvLhsCacheEntryCountForTest(), size_t{1}); - - verify_conv(input_a_copy, "same_content_different_buffer"); - EXPECT_EQ(ArmKleidiAI::MlasConvLhsCacheEntryCountForTest(), size_t{1}) - << "same geometry with a different input buffer should reuse the LHS indirection cache"; - - verify_conv(input_b, "different_content_different_buffer"); - EXPECT_EQ(ArmKleidiAI::MlasConvLhsCacheEntryCountForTest(), size_t{1}) - << "same geometry with different input content should reuse the LHS indirection cache"; - - fill_input(input_a, 3); - verify_conv(input_a, "different_content_same_buffer"); - EXPECT_EQ(ArmKleidiAI::MlasConvLhsCacheEntryCountForTest(), size_t{1}) - << "same geometry with mutated input content should not add cache entries"; - } - - ArmKleidiAI::MlasConvClearLhsCacheForTest(); - } -#endif - -#if defined(MLAS_TARGET_AMD64) - void TestMobileClipAvx512DispatchSelection(size_t GroupCount, - size_t InputHeight, - size_t InputWidth) { - if (GetMlasPlatform().ConvNchwFloatKernel != MlasConvNchwFloatKernelAvx512F) { - return; - } - - constexpr size_t BatchCount = 1; - constexpr size_t InputChannels = 1; - constexpr size_t FilterCount = 2; - constexpr size_t KernelHeight = 7; - constexpr size_t KernelWidth = 7; - constexpr size_t PaddingLeftHeight = 3; - constexpr size_t PaddingLeftWidth = 3; - constexpr size_t PaddingRightHeight = 3; - constexpr size_t PaddingRightWidth = 3; - constexpr size_t DilationHeight = 1; - constexpr size_t DilationWidth = 1; - constexpr size_t StrideHeight = 2; - constexpr size_t StrideWidth = 2; - - const int64_t OutputHeight64 = - ((int64_t(InputHeight) + int64_t(PaddingLeftHeight) + int64_t(PaddingRightHeight)) - - (int64_t(DilationHeight) * (int64_t(KernelHeight) - 1) + 1)) / - int64_t(StrideHeight) + - 1; - const int64_t OutputWidth64 = - ((int64_t(InputWidth) + int64_t(PaddingLeftWidth) + int64_t(PaddingRightWidth)) - - (int64_t(DilationWidth) * (int64_t(KernelWidth) - 1) + 1)) / - int64_t(StrideWidth) + - 1; - - ASSERT_GT(OutputHeight64, 0); - ASSERT_GT(OutputWidth64, 0); - - int64_t InputShape[] = {int64_t(InputHeight), int64_t(InputWidth)}; - int64_t KernelShape[] = {int64_t(KernelHeight), int64_t(KernelWidth)}; - int64_t DilationShape[] = {int64_t(DilationHeight), int64_t(DilationWidth)}; - int64_t Padding[] = {int64_t(PaddingLeftHeight), int64_t(PaddingLeftWidth), int64_t(PaddingRightHeight), int64_t(PaddingRightWidth)}; - int64_t StrideShape[] = {int64_t(StrideHeight), int64_t(StrideWidth)}; - int64_t OutputShape[] = {OutputHeight64, OutputWidth64}; - - MLAS_ACTIVATION Activation; - Activation.ActivationKind = MlasIdentityActivation; - - MLAS_CONV_PARAMETERS Parameters; - size_t WorkingBufferSize = 0; - - MlasConvPrepare(&Parameters, - 2, - BatchCount, - GroupCount, - InputChannels, - InputShape, - KernelShape, - DilationShape, - Padding, - StrideShape, - OutputShape, - FilterCount, - &Activation, - &WorkingBufferSize, - 0.0f, - threadpool_); - - ASSERT_EQ(Parameters.Algorithm, MlasConvAlgorithmDepthwiseMultiplierGreaterThan1) - << "Expected AVX512 MobileClip dispatch for G" << GroupCount - << "/H" << InputHeight - << "/W" << InputWidth; - } -#endif - - void TestMobileClipBetaActivationRegression(size_t GroupCount, - size_t InputHeight, - size_t InputWidth) { - constexpr size_t BatchCount = 1; - constexpr size_t InputChannels = 1; - constexpr size_t FilterCount = 2; - constexpr size_t KernelHeight = 7; - constexpr size_t KernelWidth = 7; - constexpr size_t PaddingLeftHeight = 3; - constexpr size_t PaddingLeftWidth = 3; - constexpr size_t PaddingRightHeight = 3; - constexpr size_t PaddingRightWidth = 3; - constexpr size_t DilationHeight = 1; - constexpr size_t DilationWidth = 1; - constexpr size_t StrideHeight = 2; - constexpr size_t StrideWidth = 2; - constexpr float Beta = 1.0f; - - const int64_t OutputHeight64 = - ((int64_t(InputHeight) + int64_t(PaddingLeftHeight) + int64_t(PaddingRightHeight)) - - (int64_t(DilationHeight) * (int64_t(KernelHeight) - 1) + 1)) / - int64_t(StrideHeight) + - 1; - const int64_t OutputWidth64 = - ((int64_t(InputWidth) + int64_t(PaddingLeftWidth) + int64_t(PaddingRightWidth)) - - (int64_t(DilationWidth) * (int64_t(KernelWidth) - 1) + 1)) / - int64_t(StrideWidth) + - 1; - - ASSERT_GT(OutputHeight64, 0); - ASSERT_GT(OutputWidth64, 0); - - const size_t OutputHeight = static_cast(OutputHeight64); - const size_t OutputWidth = static_cast(OutputWidth64); - const size_t InputSize = InputHeight * InputWidth; - const size_t KernelSize = KernelHeight * KernelWidth; - const size_t OutputSize = OutputHeight * OutputWidth; - - const size_t InputElements = BatchCount * GroupCount * InputChannels * InputSize; - const size_t FilterElements = GroupCount * FilterCount * InputChannels * KernelSize; - const size_t BiasElements = GroupCount * FilterCount; - const size_t OutputElements = BatchCount * GroupCount * FilterCount * OutputSize; - - const float* Input = BufferInput.GetBuffer(InputElements); - const float* Filter = BufferFilter.GetBuffer(FilterElements); - const float* Bias = BufferBias.GetBuffer(BiasElements); - const float* InitialOutput = BufferInitialOutput.GetBuffer(OutputElements); - float* Output = BufferOutput.GetBuffer(OutputElements); - float* OutputReference = BufferOutputReference.GetBuffer(OutputElements); - - MLAS_ACTIVATION Activation; - Activation.ActivationKind = MlasReluActivation; - - MlasConv2DWithOptions(BatchCount, - GroupCount, - InputChannels, - InputHeight, - InputWidth, - FilterCount, - KernelHeight, - KernelWidth, - PaddingLeftHeight, - PaddingLeftWidth, - PaddingRightHeight, - PaddingRightWidth, - DilationHeight, - DilationWidth, - StrideHeight, - StrideWidth, - OutputHeight, - OutputWidth, - Input, - Filter, - Bias, - Activation, - Beta, - InitialOutput, - Output); - - ReferenceConv2DWithOptions(BatchCount, - GroupCount, - InputChannels, - InputHeight, - InputWidth, - FilterCount, - KernelHeight, - KernelWidth, - PaddingLeftHeight, - PaddingLeftWidth, - DilationHeight, - DilationWidth, - StrideHeight, - StrideWidth, - OutputHeight, - OutputWidth, - Input, - Filter, - Bias, - Activation, - Beta, - InitialOutput, - OutputReference); - - for (size_t i = 0; i < OutputElements; ++i) { - ASSERT_TRUE(CloseEnough(Output[i], OutputReference[i])) - << "Mismatch at output index " << i - << " for G" << GroupCount - << "/H" << InputHeight - << "/W" << InputWidth - << ": actual=" << Output[i] - << ", expected=" << OutputReference[i]; - } - } - void Test( size_t BatchCount, size_t GroupCount, @@ -823,38 +332,5 @@ class MlasConv2DTest : public MlasTestBase { } } } - - // - // Regression test: exercise a KleidiAI Conv2D path when KleidiAI is enabled. - // See https://github.com/microsoft/onnxruntime/issues/26669. - // - // The KleidiAI implementation uses an internal per-thread padding buffer for out-of-bounds pixels - // when constructing the LHS indirection table. Historically, if the buffer was too small for a later - // convolution (larger CI), resizing could invalidate cached indirection pointers and lead to - // non-deterministic corruption. - // - // This sequence forces pad-buffer growth by running a smaller-CI convolution followed by a larger-CI - // convolution (with padding to ensure pad pointers are used), then runs the smaller-CI convolution again. - // Repeat a few times to increase the likelihood of triggering a reallocation and verify the path. - // - for (int i = 0; i < 4; ++i) { - Test(1, 1, 64, 11, 11, 32, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1); // smaller CI - Test(1, 1, 320, 11, 11, 32, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1); // larger CI forces pad buffer growth - Test(1, 1, 64, 11, 11, 32, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1); // sanity: back to smaller CI after growth - } - } - - void ExecuteShort(void) override { -#if defined(USE_KLEIDIAI) && defined(MLAS_ENABLE_TEST_HOOKS) - TestKleidiAILhsCacheIgnoresInputContent(); -#endif -#if defined(MLAS_TARGET_AMD64) - TestMobileClipAvx512DispatchSelection(64, 64, 64); - TestMobileClipAvx512DispatchSelection(128, 32, 32); - TestMobileClipAvx512DispatchSelection(256, 16, 16); -#endif - TestMobileClipBetaActivationRegression(64, 64, 64); - TestMobileClipBetaActivationRegression(128, 32, 32); - TestMobileClipBetaActivationRegression(256, 16, 16); } }; diff --git a/onnxruntime/test/optimizer/conv_add_act_test.cc b/onnxruntime/test/optimizer/conv_add_act_test.cc index bb409a2bbb82e..660f14c752886 100644 --- a/onnxruntime/test/optimizer/conv_add_act_test.cc +++ b/onnxruntime/test/optimizer/conv_add_act_test.cc @@ -30,9 +30,10 @@ void TestConvPath(const std::vector& input_shape, const std::vector disabled_optimizers = {"NchwcTransformer"}; + InlinedHashSet disabled_optimizers = {"NchwcTransformer", "NhwcTransformer"}; TransformerTester(build_test_case, check_graph, TransformerLevel::Default, diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index de973679c8f80..7bb492c4854d9 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -363,6 +363,7 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); @@ -402,6 +403,7 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); @@ -443,6 +445,7 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); @@ -494,7 +497,7 @@ TEST(TransformerTest, FuseFp16InitializersWithGraphOutputs) { // by folding it with Add node. This will not allow us to test the // scenario where Cast node is producing graph output and need to // kept untouched by FuseInitializersTransformer. - ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"ConstantFolding"})); + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"ConstantFolding", "NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); _graph_structure_at_load(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 21ea7af4e7389..4d270ba014eae 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -224,6 +224,28 @@ TEST(NhwcTransformerTests, ConvGlobalAveragePool) { TransformerLevel::Level3); } +TEST(NhwcTransformerTests, ConvDepthwiseFloat) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({8, 1, 3, 3}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& conv_node = builder.AddConvNode(input_arg, weight_arg, output_arg); + conv_node.AddAttribute("group", static_cast(8)); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3); +} + TEST(NhwcTransformerTests, ConvAveragePool) { DNNL_GTEST_SKIP(); From a98ca5bde2f9766f96f3ba50dc9ea67d4667b6eb Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Fri, 19 Dec 2025 13:20:49 +0000 Subject: [PATCH 070/140] Add a value for channels_last to bench_sconv.cpp Signed-off-by: Orlaith Monahan --- onnxruntime/test/mlas/bench/bench_sconv.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/onnxruntime/test/mlas/bench/bench_sconv.cpp b/onnxruntime/test/mlas/bench/bench_sconv.cpp index dfc7987274baf..9df09728ffa17 100644 --- a/onnxruntime/test/mlas/bench/bench_sconv.cpp +++ b/onnxruntime/test/mlas/bench/bench_sconv.cpp @@ -218,6 +218,7 @@ void SCONV_NCHW_THREADED(benchmark::State& state, const char* /*dummy*/) { static_cast(output_channels_per_group), &activation, &WorkingBufferSize, + false, 0.0f, tp); From cabbd151ec8bc4d25b6c28c0f0dca989ac9b4df7 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 12 Jan 2026 10:57:00 +0000 Subject: [PATCH 071/140] Update internal_testing_tests.cc Update to the internal_testings_tests helper macros for file expansion so it works on other platforms --- onnxruntime/test/internal_testing_ep/internal_testing_tests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index b9c58ca386b12..e8bab013de97a 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -49,7 +49,7 @@ std::filesystem::path ResolveInternalTestPath(const std::filesystem::path& path) } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{ORT_TSTR(__FILE__)}.parent_path().parent_path().parent_path(); + std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path().parent_path(); return kSourceTestRoot / path; } From 2313834f4b6058a45d8db097a16826a1f677c4a2 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 14 Jan 2026 16:05:28 +0000 Subject: [PATCH 072/140] Update nhwc_transformer_test.cc Fix for failing ConvDepthwiseFloat test, allows for a small tolerance when running on different hardware --- onnxruntime/test/optimizer/nhwc_transformer_test.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 4d270ba014eae..3ad70b7f6ff5e 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -243,7 +243,11 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat) { TransformerTester(build_test_case, check_nhwc_graph, TransformerLevel::Level2, - TransformerLevel::Level3); + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); + } TEST(NhwcTransformerTests, ConvAveragePool) { From 9a1df35b8122e8ba5e64da5bae4070b32c9250a4 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 14 Jan 2026 16:07:22 +0000 Subject: [PATCH 073/140] Update internal_testing_tests.cc For for failing TestSaveAndLoadOrtModel test Make sure the model being saved / loaded is being done from a writeable location --- .../test/internal_testing_ep/internal_testing_tests.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index e8bab013de97a..83fb3f07c8e76 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -122,7 +122,9 @@ static void ExecuteMnist(InferenceSessionWrapper& session, bool custom_ep_enable #if !defined(ORT_MINIMAL_BUILD) TEST(InternalTestingEP, TestSaveAndLoadOrtModel) { - const auto ort_model_path = ResolveInternalTestPathString(ORT_MODEL_FOLDER "mnist.internal_testing_ep.test_output.ort"); + const auto ort_model_dir = ResolveInternalTestPath(std::filesystem::path{ORT_MODEL_FOLDER}); + const std::basic_string ort_model_path = + (ort_model_dir / ORT_TSTR("mnist.internal_testing_ep.test_output.ort")).native(); // // First load the onnx format model and save as an ORT model. From d3c8fb0436f9f3d4c7e9e2ce1a1449c8868e038c Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 14 Jan 2026 16:09:10 +0000 Subject: [PATCH 074/140] Update ort_model_only_test.cc Fix for undeclared identifier linker error --- onnxruntime/test/framework/ort_model_only_test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index ca40a34974e09..e696438a8bdff 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -24,6 +24,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#define WIDEN2(x) L##x +#define WIDEN(x) WIDEN2(x) using namespace ONNX_NAMESPACE; @@ -41,7 +43,7 @@ std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{ORT_TSTR(__FILE__)}.parent_path().parent_path(); + std::filesystem::path{WIDEN(__FILE__)}.parent_path().parent_path(); std::filesystem::path source_candidate = kSourceTestRoot / path; if (std::filesystem::exists(source_candidate)) { return source_candidate; From 88d91b1854c2203039975a98e8806a7c232377c9 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 14 Jan 2026 18:00:49 +0000 Subject: [PATCH 075/140] Lintrunner fixes Signed-off-by: Orlaith Monahan --- onnxruntime/test/framework/ort_model_only_test.cc | 2 +- onnxruntime/test/optimizer/nhwc_transformer_test.cc | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index e696438a8bdff..779aa12798f0c 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -43,7 +43,7 @@ std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{WIDEN(__FILE__)}.parent_path().parent_path(); + std::filesystem::path{WIDEN(__FILE__)}.parent_path().parent_path(); std::filesystem::path source_candidate = kSourceTestRoot / path; if (std::filesystem::exists(source_candidate)) { return source_candidate; diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 3ad70b7f6ff5e..87afd865a60a5 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -247,7 +247,6 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat) { /*opset_version*/ 12, /*per_sample_tolerance*/ 1e-6, /*relative_per_sample_tolerance*/ 1e-6); - } TEST(NhwcTransformerTests, ConvAveragePool) { From 0819cb9839a0c6064b1d3ffe05d6b3c0f5ce8ada Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:07:16 +0000 Subject: [PATCH 076/140] Update onnxruntime/core/optimizer/nhwc_transformer.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/core/optimizer/nhwc_transformer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 9544cf7395025..5bd592f8ef01d 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -212,7 +212,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, } #ifdef USE_KLEIDIAI - // Klediai specific block for NhwcFusedConvolutions + // KleidiAI specific block for NhwcFusedConvolutions { // F32 Conv -> F32 NHWC Conv OpKernelRegistryId nhwc_conv_fp32{ From 5f69eb542dcf9337ab9d8e6a89ff44ea6c16beec Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:07:31 +0000 Subject: [PATCH 077/140] Update onnxruntime/core/framework/kernel_type_str_resolver.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/core/framework/kernel_type_str_resolver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index aacbc8fc0a4fb..f73550c14ebc0 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -37,7 +37,7 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } #ifdef USE_KLEIDIAI - // Klediai specific block for NhwcFusedConvolutions + // KleidiAI specific block for NhwcFusedConvolutions if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; op_it = map.find(fused_conv_op_id); From 3cdfb281767478cc2a86f45996e0a858c5ebaf05 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:08:13 +0000 Subject: [PATCH 078/140] Update onnxruntime/core/providers/cpu/nn/conv.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/core/providers/cpu/nn/conv.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 7e73f09151a92..99e7f7de225ca 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -243,8 +243,8 @@ Status Conv::Compute(OpKernelContext* context) const { concurrency::ThreadPool* thread_pool = context->GetOperatorThreadPool(); if (channels_last_) { - ORT_RETURN_IF_NOT(kernel_rank == 2, "NhwcFusedConv currently supports 2D kernels."); - ORT_RETURN_IF_NOT(dilations[0] == 1 && dilations[1] == 1, "NhwcFusedConv currently supports dilation == 1."); + ORT_RETURN_IF_NOT(kernel_rank == 2, "Conv with channels_last layout currently supports 2D kernels."); + ORT_RETURN_IF_NOT(dilations[0] == 1 && dilations[1] == 1, "Conv with channels_last layout currently supports dilation == 1."); } const bool wants_channels_last = channels_last_; From 17afc4eefa7295e98581a4550f78e90058f92c37 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:08:41 +0000 Subject: [PATCH 079/140] Update onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index d497285db572d..eee7ac8aab81f 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -20,7 +20,9 @@ class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, EmbedLayerNormalization); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, ExpandDims); class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, FusedConv); +#ifdef USE_KLEIDIAI class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, NhwcFusedConv); +#endif class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, FusedGemm); #if !defined(DISABLE_GENERATION_OPS) class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kMSDomain, 1, float, GreedySearch); From b31808efe985b0b2a886bed7b61c04037b96603a Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 26 Jan 2026 10:08:56 +0000 Subject: [PATCH 080/140] Update onnxruntime/test/framework/ort_model_only_test.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/test/framework/ort_model_only_test.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 779aa12798f0c..2c6f033ce72bf 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -93,7 +93,6 @@ static void RunOrtModel(const OrtModelTestInfo& test_info) { InferenceSessionWrapper session_object{so, GetEnvironment()}; std::filesystem::path model_path = ResolveTestPath(std::filesystem::path{test_info.model_filename}); - std::cerr << "RunOrtModel cwd: " << std::filesystem::current_path() << " loading: " << model_path << std::endl; const auto& model_path_str = model_path.native(); if (test_info.run_use_buffer) { // Load the file into a buffer and use the buffer to create inference session From d2399f09b3d6802c3f683f86b68ab171eb64a261 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 4 Feb 2026 12:57:14 +0000 Subject: [PATCH 081/140] Additional guards to not include KLEIDIAI specific kernels Signed-off-by: Orlaith Monahan --- onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index eee7ac8aab81f..0749457f5a182 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -316,7 +316,9 @@ Status RegisterCpuContribKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, +#ifdef USE_KLEIDIAI BuildKernelCreateInfo, +#endif BuildKernelCreateInfo, #if !defined(DISABLE_GENERATION_OPS) BuildKernelCreateInfo, From 7e081507c0044fd5ecdc2460d0313f927febf43e Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 3 Mar 2026 16:20:03 +0000 Subject: [PATCH 082/140] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc | 2 ++ onnxruntime/contrib_ops/cpu/fused_conv.cc | 3 +++ 2 files changed, 5 insertions(+) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index 0749457f5a182..9d0f7c7238cd0 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -316,8 +316,10 @@ Status RegisterCpuContribKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, + BuildKernelCreateInfo, #ifdef USE_KLEIDIAI BuildKernelCreateInfo, +#endif #endif BuildKernelCreateInfo, #if !defined(DISABLE_GENERATION_OPS) diff --git a/onnxruntime/contrib_ops/cpu/fused_conv.cc b/onnxruntime/contrib_ops/cpu/fused_conv.cc index d77efc26d0e2f..f4caebf04f899 100644 --- a/onnxruntime/contrib_ops/cpu/fused_conv.cc +++ b/onnxruntime/contrib_ops/cpu/fused_conv.cc @@ -31,6 +31,9 @@ ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( 1, float, KernelDefBuilder() + // Allow the optional "sum" input (index 3) to be reused as the output buffer (index 0), + // consistent with the FusedConv kernel registration. + .MayInplace(3, 0) .TypeConstraint("T", DataTypeImpl::GetTensorType()), FusedConvFloat); From 48887d017bad8bd6a48f32568aed73aee3450e94 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 3 Mar 2026 19:32:21 +0000 Subject: [PATCH 083/140] Adding some extra tests and removing unnecessary code Signed-off-by: Orlaith Monahan --- .../contrib_ops/cpu/cpu_contrib_kernels.cc | 2 -- .../framework/kernel_type_str_resolver.cc | 2 -- .../kernel_type_str_resolver_utils_test.cc | 34 +++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc index 9d0f7c7238cd0..0749457f5a182 100644 --- a/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc +++ b/onnxruntime/contrib_ops/cpu/cpu_contrib_kernels.cc @@ -316,10 +316,8 @@ Status RegisterCpuContribKernels(KernelRegistry& kernel_registry) { BuildKernelCreateInfo, BuildKernelCreateInfo, BuildKernelCreateInfo, - BuildKernelCreateInfo, #ifdef USE_KLEIDIAI BuildKernelCreateInfo, -#endif #endif BuildKernelCreateInfo, #if !defined(DISABLE_GENERATION_OPS) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index f73550c14ebc0..ac49f50751cb5 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -36,7 +36,6 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } -#ifdef USE_KLEIDIAI // KleidiAI specific block for NhwcFusedConvolutions if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; @@ -46,7 +45,6 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, op_it = map.find(conv_op_id); } } -#endif return op_it; } diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 86ffef6c49dc9..518010625a2b4 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -9,7 +9,10 @@ #include "gtest/gtest.h" #include "core/flatbuffers/schema/ort.fbs.h" +#include "core/graph/constants.h" +#include "core/graph/model.h" #include "core/graph/schema_registry.h" +#include "test/test_environment.h" #include "test/util/include/asserts.h" namespace onnxruntime::test { @@ -49,6 +52,37 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { + SchemaRegistryManager schema_registry; + const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); + ASSERT_NE(fused_conv_schema, nullptr); + + KernelTypeStrResolver resolver; + ASSERT_STATUS_OK(resolver.RegisterOpSchema(*fused_conv_schema)); + + Model model("nhwc_fused_conv_resolver_test", false, DefaultLoggingManager().DefaultLogger()); + auto& graph = model.MainGraph(); + + ONNX_NAMESPACE::TypeProto float_tensor; + auto* tensor_type = float_tensor.mutable_tensor_type(); + tensor_type->set_elem_type(ONNX_NAMESPACE::TensorProto_DataType_FLOAT); + tensor_type->mutable_shape()->add_dim()->set_dim_value(1); + + auto& x = graph.GetOrCreateNodeArg("x", &float_tensor); + auto& w = graph.GetOrCreateNodeArg("w", &float_tensor); + auto& y = graph.GetOrCreateNodeArg("y", &float_tensor); + + auto& nhwc_fused_conv = graph.AddNode( + "nhwc_fused_conv", "NhwcFusedConv", "test node", {&x, &w}, {&y}, nullptr, kMSDomain); + nhwc_fused_conv.SetSinceVersion(1); + + gsl::span resolved_args; + ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); + ASSERT_FALSE(resolved_args.empty()); +} +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) + // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in // onnxruntime/core/framework/kernel_type_str_resolver_utils.cc From b756e3af976c99090a698d4c2ea788ebcde7ad3d Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 10 Mar 2026 12:47:11 +0000 Subject: [PATCH 084/140] Adding additional USE_KLEIDIAI guards Signed-off-by: Orlaith Monahan --- onnxruntime/contrib_ops/cpu/fused_conv.cc | 2 ++ onnxruntime/core/framework/kernel_type_str_resolver.cc | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/onnxruntime/contrib_ops/cpu/fused_conv.cc b/onnxruntime/contrib_ops/cpu/fused_conv.cc index f4caebf04f899..76451232421da 100644 --- a/onnxruntime/contrib_ops/cpu/fused_conv.cc +++ b/onnxruntime/contrib_ops/cpu/fused_conv.cc @@ -26,6 +26,7 @@ ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( .TypeConstraint("T", DataTypeImpl::GetTensorType()), FusedConvFloat); +#ifdef USE_KLEIDIAI ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( NhwcFusedConv, 1, @@ -36,6 +37,7 @@ ONNX_CPU_OPERATOR_TYPED_MS_KERNEL( .MayInplace(3, 0) .TypeConstraint("T", DataTypeImpl::GetTensorType()), FusedConvFloat); +#endif } // namespace contrib } // namespace onnxruntime diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index ac49f50751cb5..f995caec22cf9 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -35,7 +35,7 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } } - +#ifdef USE_KLEIDIAI // KleidiAI specific block for NhwcFusedConvolutions if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; @@ -45,6 +45,7 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, op_it = map.find(conv_op_id); } } +#endif return op_it; } From ff6f5d70e2a0c468918e59a0fbf301da8840e039 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 10 Mar 2026 12:50:16 +0000 Subject: [PATCH 085/140] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/core/optimizer/conv_add_act_fusion.cc | 13 ++++++++++--- onnxruntime/core/optimizer/nhwc_transformer.cc | 15 --------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/onnxruntime/core/optimizer/conv_add_act_fusion.cc b/onnxruntime/core/optimizer/conv_add_act_fusion.cc index dfdc61c1b76c3..d51ea894725fa 100644 --- a/onnxruntime/core/optimizer/conv_add_act_fusion.cc +++ b/onnxruntime/core/optimizer/conv_add_act_fusion.cc @@ -214,12 +214,19 @@ class FuseConvAddActivationAction : public ReplaceWithNew { const auto& target = runtimeState.selected_nodes.Target(); const auto* channels_last_attr = graph_utils::GetNodeAttribute(target, "channels_last"); const bool channels_last = channels_last_attr != nullptr && channels_last_attr->i() != 0; + const std::string& op_type = target.OpType(); - if (target.OpType() == "Conv") { - return channels_last ? "NhwcFusedConv" : "FusedConv"; + // If channels_last is set, use NHWC fused convolution regardless of original op type. + if (channels_last) { + return "NhwcFusedConv"; } - return "NhwcFusedConv"; + // Without channels_last, convert Conv to FusedConv, and leave other op types unchanged. + if (op_type == "Conv") { + return "FusedConv"; + } + + return op_type; } std::string Domain(const RuntimeState&) const override { return kMSDomain; } diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 5bd592f8ef01d..5250eeb9102c1 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -338,21 +338,6 @@ Status NhwcTransformer::ApplyImpl(Graph& graph, bool& modified, int graph_level, node->SetAttributeInt("channels_last", 1); } - if (node->OpType() == "Conv" || node->OpType() == "FusedConv") { - const auto group_opt = node->GetAttributeInt("group"); - if (group_opt.has_value() && group_opt.value() != 1) { - continue; - } - - const auto dilations_opt = node->GetAttributeInts("dilations"); - if (dilations_opt.has_value()) { - const auto& dilations = dilations_opt.value(); - if ((dilations.size() >= 1 && dilations[0] != 1) || - (dilations.size() >= 2 && dilations[1] != 1)) { - continue; - } - } - } size_t rank = shape->dim_size(); std::vector input_perm = ChannelFirstToLastPerm(rank); From 357b4afc1f044b86c6b8796d2044f376f32a5e41 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 10 Mar 2026 14:14:59 +0000 Subject: [PATCH 086/140] Adding USE_KLEIDIAI guard to kleidiai specific kernel_type_str_resolver_utils_test Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 518010625a2b4..84a7233325023 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -52,7 +52,7 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } -#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +#if defined(USE_KLEIDIAI) && !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { SchemaRegistryManager schema_registry; const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); From f70f9faf0db4a37b5c86117d358bc96f3bc770b9 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 10 Mar 2026 17:59:23 +0000 Subject: [PATCH 087/140] Updates to address comments and codex issues Signed-off-by: Orlaith Monahan --- .../core/optimizer/nhwc_transformer.cc | 11 +++--- onnxruntime/core/providers/cpu/nn/conv.cc | 2 +- .../test/framework/ort_model_only_test.cc | 36 +++++++++---------- .../internal_testing_tests.cc | 2 +- .../test/optimizer/nhwc_transformer_test.cc | 2 +- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 5250eeb9102c1..611ee1cc498a2 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -25,6 +25,12 @@ namespace onnxruntime { using namespace layout_transformation; #ifdef USE_KLEIDIAI +// KleidiFp32NhwcFilter ensures the compatibility of Conv/FusedConv nodes before performing data conversion +// Checks the following: +// Must have 4D Input +// Must have symmetric 2d padding (if applicable) +// No add input +// Dilation and Group sizes must be 1 bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& graph, onnx_transpose_optimization::api::NodeRef& node) { auto& base_node = NodeFromApiNode(node); @@ -52,11 +58,6 @@ bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& grap } } - const auto inputs = node.Inputs(); - if (inputs.size() > 3 && !inputs[3].empty()) { - return false; - } - const auto* weight_shape = base_node.InputDefs()[1]->Shape(); if (weight_shape == nullptr || weight_shape->dim_size() != 4) { return false; diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 99e7f7de225ca..d82bd4893c785 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -199,10 +199,10 @@ Status Conv::Compute(OpKernelContext* context) const { const Tensor* W = context->Input(1); const Tensor* B = num_inputs >= 3 ? context->Input(2) : nullptr; const Tensor* Sum = num_inputs >= 4 ? context->Input(3) : nullptr; + ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X->Shape(), W->Shape(), channels_last_)); const int64_t N = X->Shape()[0]; const int64_t C = X->Shape()[channels_last_ ? 3 : 1]; const int64_t M = W->Shape()[0]; - ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X->Shape(), W->Shape(), channels_last_)); TensorShapeVector kernel_shape; ORT_RETURN_IF_ERROR(conv_attrs_.ComputeKernelShape(W->Shape(), kernel_shape)); diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 2c6f033ce72bf..8b29ee46cb3ab 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -24,8 +24,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#define WIDEN2(x) L##x -#define WIDEN(x) WIDEN2(x) using namespace ONNX_NAMESPACE; @@ -38,17 +36,21 @@ std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { } std::filesystem::path workspace_candidate = std::filesystem::current_path() / path; - if (std::filesystem::exists(workspace_candidate)) { + std::error_code ec; + if (std::filesystem::exists(workspace_candidate, ec) && !ec) { return workspace_candidate; } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{WIDEN(__FILE__)}.parent_path().parent_path(); + std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path(); std::filesystem::path source_candidate = kSourceTestRoot / path; - if (std::filesystem::exists(source_candidate)) { + ec.clear(); + if (std::filesystem::exists(source_candidate, ec) && !ec) { return source_candidate; } + // Keep the original relative-to-workdir intent so the downstream file-open + // error points to the path we actually tried first. return workspace_candidate; } } // namespace @@ -247,26 +249,14 @@ static void CompareSessionMetadata(const InferenceSessionWrapper& session_object static void SaveAndCompareModels(const PathString& orig_file, const PathString& ort_file, - TransformerLevel optimization_level = TransformerLevel::Level3) { + TransformerLevel optimization_level = TransformerLevel::Level3, + bool compare_saved_model = true) { std::filesystem::path orig_path = ResolveTestPath(std::filesystem::path{orig_file}); std::filesystem::path ort_path = ResolveTestPath(std::filesystem::path{ort_file}); if (ort_path.has_parent_path()) { std::filesystem::create_directories(ort_path.parent_path()); } - const bool orig_is_ort_format = orig_path.extension() == ORT_TSTR(".ort"); - if (orig_is_ort_format) { - SessionOptions so; - so.session_logid = "SerializeToOrtFormat"; - so.optimized_model_filepath = ort_path.native(); - so.graph_optimization_level = optimization_level; - ASSERT_STATUS_OK(so.config_options.AddConfigEntry(kOrtSessionOptionsConfigSaveModelFormat, "ORT")); - InferenceSessionWrapper session_object{so, GetEnvironment()}; - ASSERT_STATUS_OK(session_object.Load(orig_path.native())); - ASSERT_STATUS_OK(session_object.Initialize()); - return; - } - SessionOptions so; so.session_logid = "SerializeToOrtFormat"; so.optimized_model_filepath = ort_path.native(); @@ -291,6 +281,10 @@ static void SaveAndCompareModels(const PathString& orig_file, ASSERT_STATUS_OK(session_object2.Load(ort_path.native())); ASSERT_STATUS_OK(session_object2.Initialize()); + if (!compare_saved_model) { + return; + } + CompareSessionMetadata(session_object, session_object2); CompareGraphAndSessionState(session_object, session_object2); } @@ -416,7 +410,9 @@ void TestOrtModelUpdate(const PathString& onnx_file, // ort_file_v4 is ORT format model using v4 where we used kernel hashes instead of constraints // update v4 model and save as v5. do not run optimizations in order to preserve the model as-is. - SaveAndCompareModels(ort_file_v4, generated_ort_file_v5, TransformerLevel::Default); + // Loading a v4 ORT model updates it as part of deserialization, so the in-memory graph/session state is not + // expected to match a separately reloaded v5 model exactly. Just validate that we can save and reload it. + SaveAndCompareModels(ort_file_v4, generated_ort_file_v5, TransformerLevel::Default, false); // run the original, v4 and v5 models and check the output is the same OrtModelTestInfo test_info; diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index 83fb3f07c8e76..4cb7cf397d95d 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -49,7 +49,7 @@ std::filesystem::path ResolveInternalTestPath(const std::filesystem::path& path) } static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path().parent_path(); + std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path(); return kSourceTestRoot / path; } diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 87afd865a60a5..f9dd1d721bf74 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -224,7 +224,7 @@ TEST(NhwcTransformerTests, ConvGlobalAveragePool) { TransformerLevel::Level3); } -TEST(NhwcTransformerTests, ConvDepthwiseFloat) { +TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto build_test_case = [&](ModelTestBuilder& builder) { auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); auto* weight_arg = builder.MakeInitializer({8, 1, 3, 3}, -1.0f, 1.0f); From 7b2e0125bb677e426ec66522b1638749bc16db50 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 12 Mar 2026 09:44:15 +0000 Subject: [PATCH 088/140] Fix for errors around NHWC and FusedSum Signed-off-by: Orlaith Monahan --- .../core/optimizer/nhwc_transformer.cc | 29 +++++++++---------- onnxruntime/core/optimizer/nhwc_transformer.h | 1 + 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 611ee1cc498a2..7bd2beed1ee03 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -148,10 +148,10 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("QLinearConv", kOnnxDomain, api::DataType::INT8), - OpTransformInfo{qconv_int8.op_type_, qconv_int8.domain_, qconv_int8.version_, true}); + OpTransformInfo{qconv_int8.op_type_, qconv_int8.domain_, qconv_int8.version_, true, false}); conv_table_.emplace( OpIdInfo("QLinearConv", kMSDomain, api::DataType::INT8), - OpTransformInfo{qconv_int8.op_type_, qconv_int8.domain_, qconv_int8.version_, true}); + OpTransformInfo{qconv_int8.op_type_, qconv_int8.domain_, qconv_int8.version_, true, false}); } } @@ -167,10 +167,10 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("QLinearConv", kOnnxDomain, api::DataType::UINT8), - OpTransformInfo{qconv_uint8.op_type_, qconv_uint8.domain_, qconv_uint8.version_, true}); + OpTransformInfo{qconv_uint8.op_type_, qconv_uint8.domain_, qconv_uint8.version_, true, false}); conv_table_.emplace( OpIdInfo("QLinearConv", kMSDomain, api::DataType::UINT8), - OpTransformInfo{qconv_uint8.op_type_, qconv_uint8.domain_, qconv_uint8.version_, true}); + OpTransformInfo{qconv_uint8.op_type_, qconv_uint8.domain_, qconv_uint8.version_, true, false}); } } @@ -205,10 +205,10 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, conv_table_.emplace( OpIdInfo("Conv", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, filter}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, false, filter}); conv_table_.emplace( OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, filter}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, false, filter}); } } @@ -233,10 +233,10 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, conv_table_.emplace( OpIdInfo("Conv", kOnnxDomain, api::DataType::FLOAT), - OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, filter}); + OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, true, filter}); conv_table_.emplace( OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT), - OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, filter}); + OpTransformInfo{nhwc_conv_fp32.op_type_, nhwc_conv_fp32.domain_, nhwc_conv_fp32.version_, false, true, filter}); } } #endif @@ -254,7 +254,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("MaxPool", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_maxpool_fp16.op_type_, nhwc_maxpool_fp16.domain_, nhwc_maxpool_fp16.version_, false}); + OpTransformInfo{nhwc_maxpool_fp16.op_type_, nhwc_maxpool_fp16.domain_, nhwc_maxpool_fp16.version_, false, false}); } } @@ -271,7 +271,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("AveragePool", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_avgpool_fp16.op_type_, nhwc_avgpool_fp16.domain_, nhwc_avgpool_fp16.version_, false}); + OpTransformInfo{nhwc_avgpool_fp16.op_type_, nhwc_avgpool_fp16.domain_, nhwc_avgpool_fp16.version_, false, false}); } } @@ -288,7 +288,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; conv_table_.emplace( OpIdInfo("GlobalAveragePool", kOnnxDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_gavgpool_fp16.op_type_, nhwc_gavgpool_fp16.domain_, nhwc_gavgpool_fp16.version_, false}); + OpTransformInfo{nhwc_gavgpool_fp16.op_type_, nhwc_gavgpool_fp16.domain_, nhwc_gavgpool_fp16.version_, false, false}); } } }; @@ -348,10 +348,9 @@ Status NhwcTransformer::ApplyImpl(Graph& graph, bool& modified, int graph_level, if (!inputs.empty()) { input_perms[0] = &input_perm; } - // Optional Sum (Z) input for FusedConv variants resides at index 3. When present, - // it must be converted to NHWC alongside the activation tensor. - const bool has_fused_sum_input = (node->Domain() == kMSDomain && node->OpType() == "FusedConv"); - if (has_fused_sum_input && inputs.size() > 3 && !inputs[3].empty()) { + // Some transformed operators require the optional fused Sum (Z) input at index 3 + // to be converted alongside the activation tensor. + if (transform->transpose_fused_sum_input_ && inputs.size() > 3 && !inputs[3].empty()) { input_perms[3] = &input_perm; } diff --git a/onnxruntime/core/optimizer/nhwc_transformer.h b/onnxruntime/core/optimizer/nhwc_transformer.h index 6dd11bdba6bdd..24d4cb3069b30 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.h +++ b/onnxruntime/core/optimizer/nhwc_transformer.h @@ -62,6 +62,7 @@ struct OpTransformInfo { const std::string domain_; const int version_; const bool has_channels_last_attrib_; + const bool transpose_fused_sum_input_; const FilterFn filter_{nullptr}; }; From a780c55c48244a5d7de3dbea51a092766ee68faa Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 12 Mar 2026 13:07:59 +0000 Subject: [PATCH 089/140] Extensive Refactor of NHWC Convolution to try and fix tests * Moved all changes to be KLEIDIAI specific * Removed the existence of a NHWCFusedConv from non KLEIDIAI code * All checks for useability of the feature now happen much earlier * Added more tests * Includes supports for much more convolutions now Signed-off-by: Orlaith Monahan --- .../kernel_type_str_resolver_utils.cc | 14 ++ onnxruntime/core/mlas/inc/mlas.h | 14 ++ onnxruntime/core/mlas/lib/convolve.cpp | 56 ++++++++ .../mlas/lib/kleidiai/convolve_kleidiai.cpp | 106 ++++++-------- .../layout_transformation.cc | 1 - ...out_transformation_potentially_added_ops.h | 1 + .../core/optimizer/nhwc_transformer.cc | 53 +------ onnxruntime/core/providers/cpu/nn/conv.cc | 61 ++++++-- .../test/contrib_ops/fused_conv_test.cc | 110 ++++++++++++++ .../kernel_type_str_resolver_utils_test.cc | 27 ++++ .../test/optimizer/nhwc_transformer_test.cc | 135 ++++++++++++++++++ .../optimizer/transpose_optimizer_test.cc | 58 ++++++++ 12 files changed, 514 insertions(+), 122 deletions(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc index 65ad60f478d78..50722f930228c 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc @@ -9,6 +9,7 @@ #include "core/common/common.h" #include "core/flatbuffers/schema/ort.fbs.h" +#include "core/graph/schema_registry.h" #include "core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h" namespace onnxruntime::kernel_type_str_resolver_utils { @@ -45,6 +46,18 @@ Status LoadKernelTypeStrResolverFromBuffer(KernelTypeStrResolver& kernel_type_st } Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrResolver& kernel_type_str_resolver) { +#if !defined(ORT_MINIMAL_BUILD) + const auto required_op_ids = GetLayoutTransformationRequiredOpIdentifiers(); + const auto schema_registry = SchemaRegistryManager{}; + for (const auto& op_id : required_op_ids) { + const auto* op_schema = schema_registry.GetSchema(std::string{op_id.op_type}, op_id.since_version, + std::string{op_id.domain}); + ORT_RETURN_IF(op_schema == nullptr, "Failed to get op schema."); + ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); + } + + return Status::OK(); +#else KernelTypeStrResolver resolver_with_required_ops{}; // to generate kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes, run the test: @@ -422,6 +435,7 @@ Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrRe kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes)); kernel_type_str_resolver.Merge(std::move(resolver_with_required_ops)); return Status::OK(); +#endif } } // namespace onnxruntime::kernel_type_str_resolver_utils diff --git a/onnxruntime/core/mlas/inc/mlas.h b/onnxruntime/core/mlas/inc/mlas.h index 2b341044a1d8a..bd7f45a4e0622 100644 --- a/onnxruntime/core/mlas/inc/mlas.h +++ b/onnxruntime/core/mlas/inc/mlas.h @@ -937,6 +937,20 @@ MlasConvPrepare(MLAS_CONV_PARAMETERS* Parameters, float Beta, MLAS_THREADPOOL* ThreadPool); +bool +MLASCALL +MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + size_t Dimensions, + size_t BatchCount, + size_t GroupCount, + const size_t* InputShape, + const size_t* KernelShape, + const size_t* DilationShape, + const size_t* Padding, + const size_t* StrideShape, + size_t FilterCount, + float Beta); + void MLASCALL MlasConv( diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index 71ef70123dfed..4beed18da536d 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1324,6 +1324,62 @@ Return Value: // Chance of arithmetic overflow could be reduced #pragma warning(disable : 26451) #endif + +namespace { + +static constexpr size_t ComputeChannelsLastDilatedKernelSize(size_t dilation, size_t kernel) { + return (dilation * kernel) - (dilation - 1); +} + +static constexpr size_t ComputeChannelsLastConvOutSize(size_t input, size_t kernel, size_t padding, size_t stride) { + if (stride > 0 && (input + 2 * padding) >= kernel) { + return (((input - kernel) + (2 * padding)) / stride) + 1; + } + + return 0; +} + +} // namespace + +bool +MLASCALL +MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + size_t Dimensions, + size_t BatchCount, + size_t GroupCount, + const size_t* InputShape, + const size_t* KernelShape, + const size_t* DilationShape, + const size_t* Padding, + const size_t* StrideShape, + size_t FilterCount, + float Beta) +{ + if (Dimensions != 2 || BatchCount != 1 || GroupCount != 1 || Beta != 0.0f) { + return false; + } + + if (Padding[0] != Padding[2] || Padding[1] != Padding[3]) { + return false; + } + + const size_t output_h = + ComputeChannelsLastConvOutSize(InputShape[0], ComputeChannelsLastDilatedKernelSize(DilationShape[0], KernelShape[0]), + Padding[0], StrideShape[0]); + const size_t output_w = + ComputeChannelsLastConvOutSize(InputShape[1], ComputeChannelsLastDilatedKernelSize(DilationShape[1], KernelShape[1]), + Padding[1], StrideShape[1]); + if (output_h == 0 || output_w == 0) { + return false; + } + + if (FilterCount <= 1 || KernelShape[0] < 3 || KernelShape[1] < 3) { + return false; + } + + return true; +} + void MLASCALL MlasConvPrepare( diff --git a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp index 110e24e8166b4..b4dcc65cd7eb4 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp +++ b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp @@ -27,13 +27,17 @@ const KaiF32IMatmulKernel& imatmul_conv = GetKleidiAIF32IMatmulUKernel(); // Right-hand-side (weights) cache key struct RhsCacheKey { size_t co, ci, kh, kw, dilationh, dilationw; + bool has_bias; size_t weights_hash; + size_t bias_hash; bool operator==(const RhsCacheKey& other) const { return co == other.co && ci == other.ci && kh == other.kh && kw == other.kw && dilationh == other.dilationh && dilationw == other.dilationw && - weights_hash == other.weights_hash; + has_bias == other.has_bias && + weights_hash == other.weights_hash && + bias_hash == other.bias_hash; } }; @@ -44,12 +48,14 @@ struct LhsCacheKey { size_t padding, sh, sw; size_t kh, kw; size_t dilationh, dilationw; + size_t data_hash; bool operator==(const LhsCacheKey& other) const { return ci == other.ci && ih == other.ih && iw == other.iw && padding == other.padding && sh == other.sh && sw == other.sw && kh == other.kh && kw == other.kw && - dilationh == other.dilationh && dilationw == other.dilationw; + dilationh == other.dilationh && dilationw == other.dilationw && + data_hash == other.data_hash; } }; @@ -57,7 +63,12 @@ struct LhsCacheKey { // Based on Knuth's multiplicative hashing method constexpr size_t HASH_GOLDEN_RATIO_CONST = 0x9e3779b9; -size_t HashWeights(const float* data, size_t count = 16) { +size_t HashTensorPrefix(const float* data, size_t element_count, size_t prefix_count = 16) { + if (data == nullptr || element_count == 0 || prefix_count == 0) { + return 0; + } + + const size_t count = std::min(element_count, prefix_count); size_t h = 0; for (size_t i = 0; i < count; ++i) { h ^= std::hash()(data[i]) + HASH_GOLDEN_RATIO_CONST + (h << 6) + (h >> 2); @@ -79,14 +90,17 @@ namespace std { (std::hash()(k.kh) << 3) ^ (std::hash()(k.kw) << 4) ^ (std::hash()(k.dilationh) << 5) ^ - (std::hash()(k.dilationw) << 6); + (std::hash()(k.dilationw) << 6) ^ + (std::hash()(k.has_bias) << 7) ^ + (std::hash()(k.bias_hash) << 8); } }; template<> struct hash { size_t operator()(const LhsCacheKey& k) const { - return (std::hash()(k.ci) << 1) ^ + return k.data_hash ^ + (std::hash()(k.ci) << 1) ^ (std::hash()(k.ih) << 2) ^ (std::hash()(k.iw) << 3) ^ (std::hash()(k.padding) << 4) ^ @@ -101,42 +115,6 @@ namespace std { } -namespace { - -using LhsPtrsCache = std::unordered_map>; - -thread_local std::unordered_map lhs_ptrs_cache_by_pad; -thread_local const float* last_pad_ptr = nullptr; - -size_t LhsPtrsCacheEntryCount() { - size_t count = 0; - for (const auto& cache_group : lhs_ptrs_cache_by_pad) { - count += cache_group.second.size(); - } - return count; -} - -void ClearLhsPtrsCache() { - lhs_ptrs_cache_by_pad.clear(); - last_pad_ptr = nullptr; -} - -} // namespace - -#if defined(MLAS_ENABLE_TEST_HOOKS) -size_t -MLASCALL -ArmKleidiAI::MlasConvLhsCacheEntryCountForTest() { - return LhsPtrsCacheEntryCount(); -} - -void -MLASCALL -ArmKleidiAI::MlasConvClearLhsCacheForTest() { - ClearLhsPtrsCache(); -} -#endif - static constexpr size_t ComputeKernelSize(const size_t D, const size_t K) { // D - dilation size @@ -184,29 +162,21 @@ static size_t ComputeMlasWorkingBufferSize(const size_t co, } static bool CheckCapabilitiesSme(const MLAS_CONV_PARAMETERS* Parameters) { - - //functional checks - logically can the conv be performed - if ((Parameters->Dimensions != 2) || - (Parameters->BatchCount != 1) || - (Parameters->Beta != 0.f) || - (Parameters->Padding[0] != Parameters->Padding[1]) || - (Parameters->Padding[0] != Parameters->Padding[2]) || - (Parameters->Padding[0] != Parameters->Padding[3]) || - (ComputeConvOutSize(Parameters->InputShape[0], - ComputeKernelSize(Parameters->DilationShape[0],Parameters->KernelShape[0]), - Parameters->Padding[0], Parameters->StrideShape[0]) * - ComputeConvOutSize(Parameters->InputShape[1], - ComputeKernelSize(Parameters->DilationShape[1],Parameters->KernelShape[1]), - Parameters->Padding[1], Parameters->StrideShape[1]) == 0)) { - KLEIDIAI_DEBUG_LOG("CheckCapabilitiesSme returning false on functional checks."); + if (!MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + Parameters->Dimensions, + Parameters->BatchCount, + Parameters->GroupCount, + Parameters->InputShape, + Parameters->KernelShape, + Parameters->DilationShape, + Parameters->Padding, + Parameters->StrideShape, + Parameters->FilterCount, + Parameters->Beta)) { + KLEIDIAI_DEBUG_LOG("CheckCapabilitiesSme returning false on shared capability checks."); return false; } - auto N = Parameters->FilterCount; - if (N == 1 || Parameters->KernelShape[0] < 3 || Parameters->KernelShape[1] < 3) { - KLEIDIAI_DEBUG_LOG("CheckCapabilitiesSme returning false on optimization checks."); - return false; - } return true; } @@ -374,7 +344,11 @@ static std::shared_ptr RhsPackWeightsBiasSme(const size_t co, const // Cache of prepacked kai rhs weights and biases. thread_local to prevent interference from parallel sessions. thread_local std::unordered_map> rhs_cache; - RhsCacheKey key = { co, ci, kh, kw, dilationh, dilationw, HashWeights(weights) }; + // The packed RHS buffer includes both weights and bias, so both must participate in the cache key. + const size_t weights_hash = HashTensorPrefix(weights, co * ci * kh * kw); + const bool has_bias = bias != nullptr; + const size_t bias_hash = has_bias ? HashTensorPrefix(bias, co) : 0; + RhsCacheKey key = { co, ci, kh, kw, dilationh, dilationw, has_bias, weights_hash, bias_hash }; auto found = rhs_cache.find(key); if (found != rhs_cache.end()) { @@ -532,19 +506,23 @@ static std::unique_ptr LhsPackImageDataSme(const size_t ci, const s // Entries include pointers to the pad buffer for out-of-bounds pixels, so we must not reuse entries after the // pad buffer is reallocated. To avoid clearing the entire cache, we group caches by pad buffer identity and // invalidate only the old group when the pad buffer moves. + using LhsPtrsCache = std::unordered_map>; + thread_local std::unordered_map lhs_ptrs_cache_by_pad; + // If pad_ptr moved (vector reallocation), drop only the old group to avoid accumulating unreachable entries. + thread_local const float* last_pad_ptr = nullptr; const float* cur_pad_ptr = pad_ptr.data(); if (last_pad_ptr != nullptr && last_pad_ptr != cur_pad_ptr) { lhs_ptrs_cache_by_pad.erase(last_pad_ptr); } last_pad_ptr = cur_pad_ptr; - // LhsPtrFill stores geometry offsets only; the current input base is supplied when packing. LhsCacheKey key = { ci, ih, iw, padding, sh, sw, kh, kw, - 1, 1 + 1, 1, + HashTensorPrefix(in, ci * ih * iw) }; auto& lhs_ptrs_cache = lhs_ptrs_cache_by_pad[cur_pad_ptr]; diff --git a/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc b/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc index 5d51c855d13ba..f611c992e0f57 100644 --- a/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc +++ b/onnxruntime/core/optimizer/layout_transformation/layout_transformation.cc @@ -68,7 +68,6 @@ const std::unordered_set& GetORTLayoutSensitiveOps() { // Define a static local string array so we can refer to the elements with string_views. static const std::string layout_sensitive_contrib_ops[]{ MakeORTLayoutSensitiveOpId(kMSDomain, "FusedConv"), - MakeORTLayoutSensitiveOpId(kMSDomain, "NhwcFusedConv"), MakeORTLayoutSensitiveOpId(kMSDomain, "GridSample"), MakeORTLayoutSensitiveOpId(kMSDomain, "QLinearAveragePool"), MakeORTLayoutSensitiveOpId(kMSDomain, "QLinearGlobalAveragePool"), diff --git a/onnxruntime/core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h b/onnxruntime/core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h index 81eb9f59eeada..3e3a010728086 100644 --- a/onnxruntime/core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h +++ b/onnxruntime/core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h @@ -70,6 +70,7 @@ inline constexpr std::array kLayoutTransformationPotentiallyAddedOps = { #if !defined(DISABLE_CONTRIB_OPS) // kMSDomain ops OpIdentifierWithStringViews{kMSDomain, "DequantizeLinear", 1}, + OpIdentifierWithStringViews{kMSDomain, "NhwcFusedConv", 1}, OpIdentifierWithStringViews{kMSDomain, "NhwcMaxPool", 1}, OpIdentifierWithStringViews{kMSDomain, "QLinearConv", 1}, OpIdentifierWithStringViews{kMSDomain, "QuantizeLinear", 1}, diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 7bd2beed1ee03..f585b57285e41 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: Copyright 2024 Arm Limited and/or its affiliates // Licensed under the MIT License. +#include #include #include #include @@ -25,14 +26,10 @@ namespace onnxruntime { using namespace layout_transformation; #ifdef USE_KLEIDIAI -// KleidiFp32NhwcFilter ensures the compatibility of Conv/FusedConv nodes before performing data conversion -// Checks the following: -// Must have 4D Input -// Must have symmetric 2d padding (if applicable) -// No add input -// Dilation and Group sizes must be 1 -bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& graph, - onnx_transpose_optimization::api::NodeRef& node) { +// Float NHWC Conv wrappers are only safe for 4D tensors today because the runtime fallback path +// only converts between NHWC and NCHW for 2D convolutions. +bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& graph, + onnx_transpose_optimization::api::NodeRef& node) { auto& base_node = NodeFromApiNode(node); ORT_UNUSED_PARAMETER(graph); @@ -45,48 +42,11 @@ bool KleidiFp32NhwcFilter(const onnx_transpose_optimization::api::GraphRef& grap return false; } - const auto& batch_dim = input_shape->dim(0); - if (!utils::HasDimValue(batch_dim) || batch_dim.dim_value() != 1) { - return false; - } - - const auto pads_attr = node.GetAttributeInts("pads"); - if (pads_attr.has_value()) { - const auto& pads = pads_attr.value(); - if (pads.size() != 4 || pads[0] != pads[2] || pads[1] != pads[3]) { - return false; - } - } - const auto* weight_shape = base_node.InputDefs()[1]->Shape(); if (weight_shape == nullptr || weight_shape->dim_size() != 4) { return false; } - const auto& filter_dim = weight_shape->dim(0); - const auto& kernel_h_dim = weight_shape->dim(2); - const auto& kernel_w_dim = weight_shape->dim(3); - - if (!utils::HasDimValue(filter_dim) || filter_dim.dim_value() <= 1 || - !utils::HasDimValue(kernel_h_dim) || kernel_h_dim.dim_value() < 3 || - !utils::HasDimValue(kernel_w_dim) || kernel_w_dim.dim_value() < 3) { - return false; - } - - const auto dilations_opt = node.GetAttributeInts("dilations"); - if (dilations_opt.has_value()) { - const auto& dilations = dilations_opt.value(); - if ((dilations.size() >= 1 && dilations[0] != 1) || - (dilations.size() >= 2 && dilations[1] != 1)) { - return false; - } - } - - const auto group_opt = node.GetAttributeInt("group"); - if (group_opt.has_value() && group_opt.value() != 1) { - return false; - } - return true; } #endif @@ -228,7 +188,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, kernel_create_info = nullptr; const auto filter = [](const api::GraphRef& graph, api::NodeRef& node) { - return KleidiFp32NhwcFilter(graph, node); + return FloatNhwcWrapperFilter(graph, node); }; conv_table_.emplace( @@ -339,7 +299,6 @@ Status NhwcTransformer::ApplyImpl(Graph& graph, bool& modified, int graph_level, node->SetAttributeInt("channels_last", 1); } - size_t rank = shape->dim_size(); std::vector input_perm = ChannelFirstToLastPerm(rank); std::vector output_perm = ChannelLastToFirstPerm(rank); diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index d82bd4893c785..464c112fbdd07 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -244,15 +244,44 @@ Status Conv::Compute(OpKernelContext* context) const { if (channels_last_) { ORT_RETURN_IF_NOT(kernel_rank == 2, "Conv with channels_last layout currently supports 2D kernels."); - ORT_RETURN_IF_NOT(dilations[0] == 1 && dilations[1] == 1, "Conv with channels_last layout currently supports dilation == 1."); } const bool wants_channels_last = channels_last_; const bool sum_present = Sum != nullptr; + std::array input_shape_size_t{}; + std::array kernel_shape_size_t{}; + std::array dilations_size_t{}; + std::array pads_size_t{}; + std::array strides_size_t{}; + if (wants_channels_last) { + ORT_RETURN_IF_NOT(input_shape.NumDimensions() == 2, "Nhwc Conv fast-path expects 2D input shape."); + for (size_t i = 0; i < 2; ++i) { + input_shape_size_t[i] = narrow(input_shape[i]); + kernel_shape_size_t[i] = narrow(kernel_shape[i]); + dilations_size_t[i] = narrow(dilations[i]); + strides_size_t[i] = narrow(strides[i]); + pads_size_t[i] = narrow(pads[i]); + pads_size_t[i + 2] = narrow(pads[i + 2]); + } + } const bool nhwc_fastpath = - wants_channels_last && kernel_rank == 2 && conv_attrs_.group == 1 && - dilations[0] == 1 && dilations[1] == 1 && !sum_present; + wants_channels_last && !sum_present && + MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + kernel_rank, + narrow(N), + narrow(conv_attrs_.group), + input_shape_size_t.data(), + kernel_shape_size_t.data(), + dilations_size_t.data(), + pads_size_t.data(), + strides_size_t.data(), + narrow(M / conv_attrs_.group), + /*Beta*/ 0.0f); const bool manual_sum = wants_channels_last && !nhwc_fastpath && sum_present; + MLAS_ACTIVATION pre_sum_activation = activation_; + if (manual_sum) { + pre_sum_activation.ActivationKind = MlasIdentityActivation; + } std::vector sum_manual_buffer; const float* sum_manual_data = nullptr; @@ -290,7 +319,7 @@ Status Conv::Compute(OpKernelContext* context) const { strides.data(), output_shape.GetDims().data(), narrow(M / conv_attrs_.group), - &activation_, + manual_sum ? &pre_sum_activation : &activation_, &WorkingBufferSize, nhwc_fastpath, nhwc_fastpath ? 0.0f : Beta, @@ -342,15 +371,27 @@ Status Conv::Compute(OpKernelContext* context) const { if (wants_channels_last && !nhwc_fastpath) { const auto& y_dims = Y->Shape().GetDims(); ORT_RETURN_IF_NOT(y_dims.size() == 4, "Nhwc fallback expects 4D output."); - ConvertNCHWToNHWC(output_compute, - Ydata.data(), - y_dims[0], y_dims[3], y_dims[1], y_dims[2]); if (manual_sum) { - auto y_span = gsl::make_span(Ydata.data(), Ydata.size()); - for (size_t i = 0; i < y_span.size(); ++i) { - y_span[i] += sum_manual_data[i]; + const SafeInt output_elements = SafeInt(Y->Shape().Size()); + float* sum_nchw = static_cast(alloc->Alloc(sizeof(float) * output_elements)); + BufferUniquePtr sum_nchw_buffer(sum_nchw, BufferDeleter(alloc)); + ConvertNHWCToNCHW(sum_manual_data, + sum_nchw, + y_dims[0], y_dims[3], y_dims[1], y_dims[2]); + + auto output_span = gsl::make_span(output_compute, static_cast(output_elements)); + auto sum_span = gsl::make_span(sum_nchw, static_cast(output_elements)); + for (size_t i = 0; i < output_span.size(); ++i) { + output_span[i] += sum_span[i]; } + + MlasActivation(&activation_, output_compute, nullptr, narrow(M), + narrow(output_shape.Size()), narrow(output_shape.Size())); } + + ConvertNCHWToNHWC(output_compute, + Ydata.data(), + y_dims[0], y_dims[3], y_dims[1], y_dims[2]); } } else { const int64_t input_image_size = input_shape.Size(); diff --git a/onnxruntime/test/contrib_ops/fused_conv_test.cc b/onnxruntime/test/contrib_ops/fused_conv_test.cc index 9df222db43501..fa19c0266c9a0 100644 --- a/onnxruntime/test/contrib_ops/fused_conv_test.cc +++ b/onnxruntime/test/contrib_ops/fused_conv_test.cc @@ -120,6 +120,58 @@ void RunConvOp(const ConvOpAndTestAttributes& attributes, disable_cpu, disable_cuda, disable_webgpu, use_float16, weight_is_initializer); } +#ifdef USE_KLEIDIAI +void TestNhwcFusedConvFloatOp(const ConvOpAndTestAttributes& attributes, + const vector>& inputs, + const vector>& input_shapes, + const std::initializer_list& expected_output, + const vector& expected_output_shape, + bool weight_is_initializer = false) { + auto cpu_ep = DefaultCpuExecutionProvider(); + if (cpu_ep == nullptr) { + return; + } + + OpTester test("NhwcFusedConv", 1, onnxruntime::kMSDomain); + test.AddAttribute("group", attributes.group); + test.AddAttribute("kernel_shape", attributes.kernel_shape); + test.AddAttribute("activation", attributes.activation); + + if (!attributes.dilations.empty()) { + test.AddAttribute("dilations", attributes.dilations); + } + + if (!attributes.pads.empty()) { + test.AddAttribute("pads", attributes.pads); + } else { + test.AddAttribute("auto_pad", attributes.auto_pad); + } + + if (!attributes.strides.empty()) { + test.AddAttribute("strides", attributes.strides); + } + + if (!attributes.activation_parameters.empty()) { + test.AddAttribute("activation_params", attributes.activation_parameters); + } + + const char* szNames[] = {"X", "W", "B", "Z"}; + test.AddInput(szNames[0], input_shapes[0], inputs[0]); + test.AddInput(szNames[1], input_shapes[1], inputs[1], weight_is_initializer); + if (inputs.size() >= 3) { + test.AddInput(szNames[2], input_shapes[2], inputs[2]); + } + if (inputs.size() >= 4) { + test.AddInput(szNames[3], input_shapes[3], inputs[3]); + } + test.AddOutput("Y", expected_output_shape, expected_output); + + std::vector> execution_providers; + execution_providers.push_back(std::move(cpu_ep)); + test.Run(OpTester::ExpectResult::kExpectSuccess, "", {}, nullptr, &execution_providers); +} +#endif + TEST(FusedConvTest, Conv2D_HardSigmoid) { ConvOpAndTestAttributes attrs = { "", // auto_pad @@ -235,6 +287,64 @@ TEST(FusedConvTest, Cpu_Conv2D_Bias_Z_Relu) { RunConvOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape, false, true, true); } +#ifdef USE_KLEIDIAI +TEST(FusedConvTest, Cpu_NhwcConv2D_Bias_Z_Relu) { + ConvOpAndTestAttributes attrs = { + "", // auto_pad + vector{1, 1}, // dilations + 1, // group + vector{2, 2}, // kernel_shape + vector{0, 0, 0, 0}, // pads + vector{1, 1}, // strides + "Relu" // activation + }; + + vector X = {1.0f, 2.0f, 3.0f, + 4.0f, 5.0f, 6.0f, + 7.0f, 8.0f, 9.0f}; + vector X_shape = {1, 3, 3, 1}; + vector W = {1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f}; + vector W_shape = {2, 1, 2, 2}; + vector Y_shape = {1, 2, 2, 2}; + vector B = {1.0f, -1.0f}; + vector B_shape = {2}; + vector Z = {-1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + vector Z_shape = {1, 2, 2, 2}; + auto expected_vals = {12.0f, 11.0f, 17.0f, 15.0f, 25.0f, 23.0f, 29.0f, 28.0f}; + TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape); + TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape, true); +} + +TEST(FusedConvTest, Cpu_NhwcConv2D_AutoPadSameUpper) { + ConvOpAndTestAttributes attrs = { + "SAME_UPPER", // auto_pad + vector{1, 1}, // dilations + 1, // group + vector{3, 3}, // kernel_shape + {}, // pads + vector{1, 1}, // strides + "Relu" // activation + }; + + vector X(25, 1.0f); + vector X_shape = {1, 5, 5, 1}; + vector W = {0.0f, 1.0f, 2.0f, + 3.0f, 4.0f, 5.0f, + 6.0f, 7.0f, 8.0f}; + vector W_shape = {1, 1, 3, 3}; + vector Y_shape = {1, 5, 5, 1}; + auto expected_vals = {24.0f, 33.0f, 33.0f, 33.0f, 20.0f, + 27.0f, 36.0f, 36.0f, 36.0f, 21.0f, + 27.0f, 36.0f, 36.0f, 36.0f, 21.0f, + 27.0f, 36.0f, 36.0f, 36.0f, 21.0f, + 12.0f, 15.0f, 15.0f, 15.0f, 8.0f}; + TestNhwcFusedConvFloatOp(attrs, {X, W}, {X_shape, W_shape}, expected_vals, Y_shape); + TestNhwcFusedConvFloatOp(attrs, {X, W}, {X_shape, W_shape}, expected_vals, Y_shape, true); +} +#endif + #endif } // namespace test diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 84a7233325023..be3a411cca8f3 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -81,6 +81,33 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); } +#endif // defined(USE_KLEIDIAI) && !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) + +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { + KernelTypeStrResolver resolver; + ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); + + Model model("nhwc_fused_conv_layout_transform_resolver_test", false, DefaultLoggingManager().DefaultLogger()); + auto& graph = model.MainGraph(); + + ONNX_NAMESPACE::TypeProto float_tensor; + auto* tensor_type = float_tensor.mutable_tensor_type(); + tensor_type->set_elem_type(ONNX_NAMESPACE::TensorProto_DataType_FLOAT); + tensor_type->mutable_shape()->add_dim()->set_dim_value(1); + + auto& x = graph.GetOrCreateNodeArg("x", &float_tensor); + auto& w = graph.GetOrCreateNodeArg("w", &float_tensor); + auto& y = graph.GetOrCreateNodeArg("y", &float_tensor); + + auto& nhwc_fused_conv = graph.AddNode( + "nhwc_fused_conv", "NhwcFusedConv", "test node", {&x, &w}, {&y}, nullptr, kMSDomain); + nhwc_fused_conv.SetSinceVersion(1); + + gsl::span resolved_args; + ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); + ASSERT_FALSE(resolved_args.empty()); +} #endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) // run this test manually to output a hard-coded byte array. diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index f9dd1d721bf74..464c378caa3b3 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -236,8 +236,143 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 2); +#else + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); +#endif + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); +} + +TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& conv_node = builder.AddConvNode(input_arg, weight_arg, output_arg); + conv_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 2); +#else + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); +#endif + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); +} + +TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* bias_arg = builder.MakeInitializer({16}, -0.5f, 0.5f); + auto* output_arg = builder.MakeOutput(); + + Node& fused_conv_node = builder.AddNode("FusedConv", {input_arg, weight_arg, bias_arg}, {output_arg}, kMSDomain); + fused_conv_node.AddAttribute("activation", "Relu"); + fused_conv_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); + fused_conv_node.AddAttribute("strides", std::vector{1, 1}); + fused_conv_node.AddAttribute("kernel_shape", std::vector{3, 3}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 2); +#else + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); +#endif + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); +} + +TEST(NhwcTransformerTests, FusedConvWithSumFloat_SkipNhwc) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* bias_arg = builder.MakeInitializer({16}, -0.5f, 0.5f); + auto* sum_arg = builder.MakeInput({1, 16, 5, 5}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& fused_conv_node = builder.AddNode("FusedConv", {input_arg, weight_arg, bias_arg, sum_arg}, {output_arg}, kMSDomain); + fused_conv_node.AddAttribute("activation", "Relu"); + fused_conv_node.AddAttribute("pads", std::vector{0, 0, 0, 0}); + fused_conv_node.AddAttribute("strides", std::vector{1, 1}); + fused_conv_node.AddAttribute("kernel_shape", std::vector{3, 3}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 3); +#else + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); +#endif + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6); +} + +TEST(NhwcTransformerTests, ConvAutoPadFloat_SkipNhwc) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 6, 6}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& conv_node = builder.AddConvNode(input_arg, weight_arg, output_arg); + conv_node.AddAttribute("auto_pad", "SAME_UPPER"); + conv_node.AddAttribute("strides", std::vector{2, 2}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); +#if defined(USE_KLEIDIAI) + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); + EXPECT_EQ(op_to_count["Transpose"], 2); +#else EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); EXPECT_EQ(op_to_count["Transpose"], 0); +#endif }; TransformerTester(build_test_case, diff --git a/onnxruntime/test/optimizer/transpose_optimizer_test.cc b/onnxruntime/test/optimizer/transpose_optimizer_test.cc index b57118bbb4ba3..c79587ed8a736 100644 --- a/onnxruntime/test/optimizer/transpose_optimizer_test.cc +++ b/onnxruntime/test/optimizer/transpose_optimizer_test.cc @@ -4604,6 +4604,64 @@ TEST(TransposeOptimizerTests, QnnTransposeReshape) { } } +TEST(TransposeOptimizerTests, LayoutTransformDoesNotRetargetNhwcFusedConv) { + std::unordered_map domain_to_version{{kOnnxDomain, 13}, {kMSDomain, 1}}; + Model model("LayoutTransformDoesNotRetargetNhwcFusedConv", false, ModelMetaData(), PathString(), + IOnnxRuntimeOpSchemaRegistryList(), domain_to_version, {}, + DefaultLoggingManager().DefaultLogger()); + Graph& graph = model.MainGraph(); + ModelTestBuilder builder(graph); + + auto* input_arg = builder.MakeInput({1, 7, 7, 8}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 3, 3, 8}, -1.0f, 1.0f); + auto* bias_arg = builder.MakeInitializer({16}, -0.5f, 0.5f); + auto* output_arg = builder.MakeOutput(); + + auto& nhwc_fused_conv = builder.AddNode("NhwcFusedConv", {input_arg, weight_arg, bias_arg}, {output_arg}, kMSDomain); + nhwc_fused_conv.AddAttribute("activation", "Relu"); + nhwc_fused_conv.AddAttribute("pads", std::vector{1, 1, 1, 1}); + nhwc_fused_conv.AddAttribute("strides", std::vector{1, 1}); + nhwc_fused_conv.AddAttribute("kernel_shape", std::vector{3, 3}); + + builder.SetGraphOutputs(); + ASSERT_STATUS_OK(graph.Resolve()); + + std::string model_data; + model.ToProto().SerializeToString(&model_data); + + SessionOptions so; + using InternalTestingEP = internal_testing_ep::InternalTestingExecutionProvider; + const std::unordered_set empty_set; + auto internal_testing_ep = std::make_unique(empty_set, empty_set, DataLayout::NHWC); + internal_testing_ep->EnableStaticKernels().TakeAllNodes(); + + InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.RegisterExecutionProvider(std::move(internal_testing_ep))); + ASSERT_STATUS_OK(session.Load(model_data.data(), static_cast(model_data.size()))); + ASSERT_STATUS_OK(session.Initialize()); + + const auto& optimized_graph = session.GetGraph(); + const auto op_to_count = CountOpsInGraph(optimized_graph); + const auto get_op_count = [&op_to_count](std::string_view op_type) { + const auto it = op_to_count.find(std::string{op_type}); + return it == op_to_count.end() ? 0 : it->second; + }; + + EXPECT_EQ(get_op_count("com.microsoft.NhwcFusedConv"), 1); + EXPECT_EQ(get_op_count("Transpose"), 0); + + int nhwc_fused_conv_count = 0; + for (const auto& node : optimized_graph.Nodes()) { + if (node.OpType() == "NhwcFusedConv") { + ++nhwc_fused_conv_count; + EXPECT_EQ(node.Domain(), kMSDomain); + EXPECT_EQ(node.GetExecutionProviderType(), internal_testing_ep::kInternalTestingExecutionProvider); + } + } + + EXPECT_EQ(nhwc_fused_conv_count, 1); +} + TEST(TransposeOptimizerTests, QnnTransposeReshapeQDQ) { Status status; auto model_uri = ORT_TSTR("testdata/layout_transform_reshape.qdq.onnx"); From eb8e2b46897d64a651ec60615d1f12394d41ea69 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 18 Mar 2026 09:59:13 +0000 Subject: [PATCH 090/140] Update onnxruntime/test/framework/ort_model_only_test.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/test/framework/ort_model_only_test.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 8b29ee46cb3ab..4061588346f67 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -18,7 +18,6 @@ #include "test/util/include/inference_session_wrapper.h" #include -#include #include "flatbuffers/idl.h" #include "flatbuffers/util.h" From 9d3c2872f6741e1bf49539291bbf4fb482838139 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 23 Mar 2026 14:05:38 +0000 Subject: [PATCH 091/140] Fixes for failing unittests: * Ensure that NCHWc Conv Kernel when fused with sum is handled correctly * Update for nhwc transofrmer tests to check the correct kernel is available Signed-off-by: Orlaith Monahan --- .../core/optimizer/nchwc_transformer.cc | 8 ++ .../test/optimizer/nhwc_transformer_test.cc | 82 +++++++++++-------- 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/onnxruntime/core/optimizer/nchwc_transformer.cc b/onnxruntime/core/optimizer/nchwc_transformer.cc index b9366ff0abae8..f28172444709b 100644 --- a/onnxruntime/core/optimizer/nchwc_transformer.cc +++ b/onnxruntime/core/optimizer/nchwc_transformer.cc @@ -325,6 +325,14 @@ void NchwcTransformerImpl::TransformConv(Node& node) { auto& input_defs = node.MutableInputDefs(); auto& output_defs = node.MutableOutputDefs(); + // The internal NCHWc Conv kernel can consume an optional fused Sum input, but it expects + // that tensor to already be in NCHWc layout. This transform only legalizes the main Conv + // input and static weights/bias, so a pre-existing FusedConv(X, W, B, Sum) would feed a + // plain NCHW tensor into the NCHWc kernel and produce incorrect results. + if (node.OpType() == "FusedConv" && input_defs.size() >= 4 && input_defs[3] != nullptr && input_defs[3]->Exists()) { + return; + } + // Require that the weights tensor be static. const ONNX_NAMESPACE::TensorProto* conv_W_tensor_proto = nullptr; if (!graph_utils::NodeArgIsConstant(graph_, *input_defs[1]) || diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 464c378caa3b3..509241fc7ec6b 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -5,10 +5,12 @@ #include #include "gtest/gtest.h" +#include "core/framework/kernel_registry.h" #include "test/unittest_util/graph_transform_test_builder.h" #include "core/mlas/inc/mlas.h" #include "core/graph/graph.h" #include "test/common/dnnl_op_test_utils.h" +#include "test/util/include/test_environment.h" namespace onnxruntime { namespace test { @@ -33,6 +35,30 @@ NodeArg* NhwcMakeInitializer(ModelTestBuilder& builder, const std::vector::max_value); } +static bool HasFloatNhwcFusedConvKernel() { + auto* cpu_ep = TestCPUExecutionProvider(); + auto kernel_registry = cpu_ep->GetKernelRegistry(); + if (!kernel_registry) { + return false; + } + + KernelRegistry::TypeConstraintMap type_constraints{ + {"T", DataTypeImpl::GetTensorType()}, + }; + + const KernelCreateInfo* kernel_create_info{}; + const auto status = kernel_registry->TryFindKernel( + kCpuExecutionProvider, + "NhwcFusedConv", + kMSDomain, + 1, + type_constraints, + DefaultLoggingManager().DefaultLogger(), + &kernel_create_info); + + return status.IsOK() && kernel_create_info != nullptr; +} + #ifndef DISABLE_CONTRIB_OPS TEST(NhwcTransformerTests, Conv) { @@ -236,13 +262,10 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 2); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, @@ -266,13 +289,10 @@ TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 2); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, @@ -300,13 +320,10 @@ TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 2); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, @@ -335,13 +352,11 @@ TEST(NhwcTransformerTests, FusedConvWithSumFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 3); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + EXPECT_EQ(op_to_count["com.microsoft.nchwc.Conv"], 0); + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 3 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, @@ -366,13 +381,10 @@ TEST(NhwcTransformerTests, ConvAutoPadFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); -#if defined(USE_KLEIDIAI) - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 1); - EXPECT_EQ(op_to_count["Transpose"], 2); -#else - EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); - EXPECT_EQ(op_to_count["Transpose"], 0); -#endif + const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; TransformerTester(build_test_case, From 60608fdb55453e7a4384bb8ae4429b3b0fdcd277 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 23 Mar 2026 14:11:18 +0000 Subject: [PATCH 092/140] Update onnxruntime/test/optimizer/transpose_optimizer_test.cc Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- onnxruntime/test/optimizer/transpose_optimizer_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/optimizer/transpose_optimizer_test.cc b/onnxruntime/test/optimizer/transpose_optimizer_test.cc index c79587ed8a736..9fafe7f578954 100644 --- a/onnxruntime/test/optimizer/transpose_optimizer_test.cc +++ b/onnxruntime/test/optimizer/transpose_optimizer_test.cc @@ -4613,7 +4613,7 @@ TEST(TransposeOptimizerTests, LayoutTransformDoesNotRetargetNhwcFusedConv) { ModelTestBuilder builder(graph); auto* input_arg = builder.MakeInput({1, 7, 7, 8}, -1.0f, 1.0f); - auto* weight_arg = builder.MakeInitializer({16, 3, 3, 8}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); auto* bias_arg = builder.MakeInitializer({16}, -0.5f, 0.5f); auto* output_arg = builder.MakeOutput(); From 7c80bb3720ff629677d9f6505f6d802ee002ba77 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 23 Mar 2026 14:32:09 +0000 Subject: [PATCH 093/140] Fix for copilot suggestion around fp16 intrinsics Signed-off-by: Orlaith Monahan --- .../core/optimizer/nhwc_transformer.cc | 2 +- .../test/optimizer/nhwc_transformer_test.cc | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index f585b57285e41..17682a79087ae 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -168,7 +168,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, false, filter}); conv_table_.emplace( OpIdInfo("FusedConv", kMSDomain, api::DataType::FLOAT16), - OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, false, filter}); + OpTransformInfo{nhwc_conv_fp16.op_type_, nhwc_conv_fp16.domain_, nhwc_conv_fp16.version_, false, true, filter}); } } diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 509241fc7ec6b..7c82c6189b070 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -59,6 +59,32 @@ static bool HasFloatNhwcFusedConvKernel() { return status.IsOK() && kernel_create_info != nullptr; } +#ifdef MLAS_F16VEC_INTRINSICS_SUPPORTED +static bool HasFp16NhwcFusedConvKernel() { + auto* cpu_ep = TestCPUExecutionProvider(); + auto kernel_registry = cpu_ep->GetKernelRegistry(); + if (!kernel_registry) { + return false; + } + + KernelRegistry::TypeConstraintMap type_constraints{ + {"T", DataTypeImpl::GetTensorType()}, + }; + + const KernelCreateInfo* kernel_create_info{}; + const auto status = kernel_registry->TryFindKernel( + kCpuExecutionProvider, + "NhwcFusedConv", + kMSDomain, + 1, + type_constraints, + DefaultLoggingManager().DefaultLogger(), + &kernel_create_info); + + return status.IsOK() && kernel_create_info != nullptr; +} +#endif + #ifndef DISABLE_CONTRIB_OPS TEST(NhwcTransformerTests, Conv) { @@ -770,6 +796,35 @@ TEST_F(NhwcTransformerTestsFp16, ConvFp16) { test_case({1, 22, 11, 13, 15}, {30, 22, 5, 3, 3}); } +TEST_F(NhwcTransformerTestsFp16, FusedConvWithSumFp16) { + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = MakeInputARangeFP16(builder, {1, 8, 7, 7}, MLFloat16(-1.0f), MLFloat16(1.0f)); + auto* weight_arg = MakeInitializerARangeFP16(builder, {16, 8, 3, 3}, MLFloat16(-1.0f), MLFloat16(1.0f)); + auto* bias_arg = MakeInitializerARangeFP16(builder, {16}, MLFloat16(-0.5f), MLFloat16(0.5f)); + auto* sum_arg = MakeInputARangeFP16(builder, {1, 16, 5, 5}, MLFloat16(-1.0f), MLFloat16(1.0f)); + auto* output_arg = builder.MakeOutput(); + + Node& fused_conv_node = builder.AddNode("FusedConv", {input_arg, weight_arg, bias_arg, sum_arg}, {output_arg}, kMSDomain); + fused_conv_node.AddAttribute("activation", "Relu"); + fused_conv_node.AddAttribute("pads", std::vector{0, 0, 0, 0}); + fused_conv_node.AddAttribute("strides", std::vector{1, 1}); + fused_conv_node.AddAttribute("kernel_shape", std::vector{3, 3}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); + const int expected_nhwc_fused_conv = HasFp16NhwcFusedConvKernel() ? 1 : 0; + const int expected_transposes = HasFp16NhwcFusedConvKernel() ? 3 : 0; + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); + EXPECT_EQ(op_to_count["Transpose"], expected_transposes); + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3); +} + TEST_F(NhwcTransformerTestsFp16, ConvMaxPoolFp16) { auto test_case = [&](const std::vector& input_shape, const std::vector& weights_shape) { auto build_test_case = [&](ModelTestBuilder& builder) { From ec12a58494a53ff54e1d0e3d2d6ea556dfd61bdc Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 23 Mar 2026 15:55:31 +0000 Subject: [PATCH 094/140] Further codex fixes and added another regression test Signed-off-by: Orlaith Monahan --- onnxruntime/core/providers/cpu/nn/conv.cc | 6 ++-- .../test/contrib_ops/fused_conv_test.cc | 29 +++++++++++++++++++ .../fuse_initializers_transformer_test.cc | 2 +- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 464c112fbdd07..4a679750600fb 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -385,8 +385,10 @@ Status Conv::Compute(OpKernelContext* context) const { output_span[i] += sum_span[i]; } - MlasActivation(&activation_, output_compute, nullptr, narrow(M), - narrow(output_shape.Size()), narrow(output_shape.Size())); + const auto activation_rows = narrow(SafeInt(y_dims[0]) * y_dims[3]); + const auto activation_cols = narrow(output_shape.Size()); + MlasActivation(&activation_, output_compute, nullptr, activation_rows, + activation_cols, activation_cols); } ConvertNCHWToNHWC(output_compute, diff --git a/onnxruntime/test/contrib_ops/fused_conv_test.cc b/onnxruntime/test/contrib_ops/fused_conv_test.cc index fa19c0266c9a0..7bfacb996526f 100644 --- a/onnxruntime/test/contrib_ops/fused_conv_test.cc +++ b/onnxruntime/test/contrib_ops/fused_conv_test.cc @@ -317,6 +317,35 @@ TEST(FusedConvTest, Cpu_NhwcConv2D_Bias_Z_Relu) { TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape, true); } +TEST(FusedConvTest, Cpu_NhwcConv2D_Z_Relu_Batch2) { + ConvOpAndTestAttributes attrs = { + "", // auto_pad + vector{1, 1}, // dilations + 1, // group + vector{1, 1}, // kernel_shape + vector{0, 0, 0, 0}, // pads + vector{1, 1}, // strides + "Relu" // activation + }; + + vector X = {1.0f, 2.0f, 3.0f, 4.0f, + 1.0f, 1.0f, 1.0f, 1.0f}; + vector X_shape = {2, 2, 2, 1}; + vector W = {1.0f}; + vector W_shape = {1, 1, 1, 1}; + vector B = {0.0f}; + vector B_shape = {1}; + vector Z = {0.0f, 0.0f, 0.0f, 0.0f, + -2.0f, -3.0f, -4.0f, -5.0f}; + vector Z_shape = {2, 2, 2, 1}; + vector Y_shape = {2, 2, 2, 1}; + auto expected_vals = {1.0f, 2.0f, 3.0f, 4.0f, + 0.0f, 0.0f, 0.0f, 0.0f}; + + TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape); + TestNhwcFusedConvFloatOp(attrs, {X, W, B, Z}, {X_shape, W_shape, B_shape, Z_shape}, expected_vals, Y_shape, true); +} + TEST(FusedConvTest, Cpu_NhwcConv2D_AutoPadSameUpper) { ConvOpAndTestAttributes attrs = { "SAME_UPPER", // auto_pad diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index 7bb492c4854d9..b238a0c849fea 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -493,7 +493,7 @@ TEST(TransformerTest, FuseFp16InitializersWithGraphOutputs) { so.graph_optimization_level = TransformerLevel::MaxLevel; // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; - // Disabling ConstantFolding optimizer as it will remove the Cast node + // Disabling ConstantFolding and NhwcTransformer optimizer as it will remove the Cast node // by folding it with Add node. This will not allow us to test the // scenario where Cast node is producing graph output and need to // kept untouched by FuseInitializersTransformer. From 4b10dfc9d894e35b188ae4f679c7994260091aa0 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 24 Mar 2026 09:54:50 +0000 Subject: [PATCH 095/140] Update the nhwc transformer tests to check according to supported hardware instead of assuming based on compile time definitions Fix for co-pilot suggestion in kernel_type_str_resolver_utils_test.cc Signed-off-by: Orlaith Monahan --- .../kernel_type_str_resolver_utils_test.cc | 5 ++- .../test/optimizer/nhwc_transformer_test.cc | 31 +++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index be3a411cca8f3..6a6c9ea084b77 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -23,7 +23,10 @@ static Status LoadLayoutTransformationRequiredOpsFromOpSchemas(KernelTypeStrReso for (const auto& op_id : required_op_ids) { const auto* op_schema = schema_registry.GetSchema(std::string{op_id.op_type}, op_id.since_version, std::string{op_id.domain}); - ORT_RETURN_IF(op_schema == nullptr, "Failed to get op schema."); + ORT_RETURN_IF(op_schema == nullptr, + "Failed to get op schema for domain='", op_id.domain, + "', op_type='", op_id.op_type, + "', since_version=", op_id.since_version, "."); ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); } return Status::OK(); diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 7c82c6189b070..223fe336d144f 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -8,6 +8,9 @@ #include "core/framework/kernel_registry.h" #include "test/unittest_util/graph_transform_test_builder.h" #include "core/mlas/inc/mlas.h" +#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#include "core/mlas/lib/mlasi.h" +#endif #include "core/graph/graph.h" #include "test/common/dnnl_op_test_utils.h" #include "test/util/include/test_environment.h" @@ -59,6 +62,14 @@ static bool HasFloatNhwcFusedConvKernel() { return status.IsOK() && kernel_create_info != nullptr; } +static bool HasFloatNhwcRuntimeSupport() { +#if defined(USE_KLEIDIAI) && defined(__aarch64__) + return HasFloatNhwcFusedConvKernel() && MLAS_CPUIDINFO::GetCPUIDInfo().HasArm_SME(); +#else + return false; +#endif +} + #ifdef MLAS_F16VEC_INTRINSICS_SUPPORTED static bool HasFp16NhwcFusedConvKernel() { auto* cpu_ep = TestCPUExecutionProvider(); @@ -288,8 +299,8 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -315,8 +326,8 @@ TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -346,8 +357,8 @@ TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -379,8 +390,8 @@ TEST(NhwcTransformerTests, FusedConvWithSumFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); EXPECT_EQ(op_to_count["com.microsoft.nchwc.Conv"], 0); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 3 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 3 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -407,8 +418,8 @@ TEST(NhwcTransformerTests, ConvAutoPadFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcFusedConvKernel() ? 1 : 0; - const int expected_transposes = HasFloatNhwcFusedConvKernel() ? 2 : 0; + const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; + const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; From cc3d9efa60563617463c29320608a7ff4bb2f09e Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 24 Mar 2026 11:06:46 +0000 Subject: [PATCH 096/140] Add further checks to ensure mlas paths for convolution are correct for non SME hardware Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/lib/convolve.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index 4beed18da536d..448bad1431835 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1355,6 +1355,16 @@ MlasConvSupportsSymmetricChannelsLast2DFloatKernel( size_t FilterCount, float Beta) { +#if !defined(USE_KLEIDIAI) || !defined(__aarch64__) + return false; +#else + // Channels-last float convolution is only implemented by the KleidiAI + // override. The generic MLAS convolution path assumes NCHW layout. + if (GetMlasPlatform().MlasConvPrepareOverride == nullptr || + GetMlasPlatform().MlasConvOverride == nullptr) { + return false; + } + if (Dimensions != 2 || BatchCount != 1 || GroupCount != 1 || Beta != 0.0f) { return false; } @@ -1378,6 +1388,7 @@ MlasConvSupportsSymmetricChannelsLast2DFloatKernel( } return true; +#endif } void From 89c7fe253f0036ba2487b9ab1131d3cc9cf1d0bf Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 24 Mar 2026 17:55:17 +0000 Subject: [PATCH 097/140] Guard the KleidiAi specific functions in android to fix -wunused errors Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/lib/convolve.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index 448bad1431835..e0dcb9df51c56 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1327,6 +1327,7 @@ Return Value: namespace { +#if defined(USE_KLEIDIAI) && defined(__aarch64__) static constexpr size_t ComputeChannelsLastDilatedKernelSize(size_t dilation, size_t kernel) { return (dilation * kernel) - (dilation - 1); } @@ -1338,6 +1339,7 @@ static constexpr size_t ComputeChannelsLastConvOutSize(size_t input, size_t kern return 0; } +#endif } // namespace From 9820ac78aa3983e30aa77e72c52a2603802ff776 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 10:14:28 +0000 Subject: [PATCH 098/140] Further guards and checks for nwhctransformer tests on x86 Compile time checks for android builds Signed-off-by: Orlaith Monahan --- onnxruntime/core/optimizer/nhwc_transformer.cc | 9 +++++++++ onnxruntime/test/optimizer/nhwc_transformer_test.cc | 2 ++ 2 files changed, 11 insertions(+) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 17682a79087ae..4f16b408c16f1 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -6,6 +6,7 @@ #include #include #include +#include "core/common/cpuid_info.h" #include "core/graph/constants.h" #include "core/mlas/inc/mlas.h" #include "core/graph/graph_utils.h" @@ -33,6 +34,13 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr auto& base_node = NodeFromApiNode(node); ORT_UNUSED_PARAMETER(graph); +#if !defined(__aarch64__) + return false; +#else + if (!CPUIDInfo::GetCPUIDInfo().HasArm_SME()) { + return false; + } + if (base_node.InputDefs().size() < 2) { return false; } @@ -48,6 +56,7 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr } return true; +#endif } #endif diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 223fe336d144f..163488c8db4e9 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -38,6 +38,7 @@ NodeArg* NhwcMakeInitializer(ModelTestBuilder& builder, const std::vector::max_value); } +#if defined(USE_KLEIDIAI) && defined(__aarch64__) static bool HasFloatNhwcFusedConvKernel() { auto* cpu_ep = TestCPUExecutionProvider(); auto kernel_registry = cpu_ep->GetKernelRegistry(); @@ -61,6 +62,7 @@ static bool HasFloatNhwcFusedConvKernel() { return status.IsOK() && kernel_create_info != nullptr; } +#endif static bool HasFloatNhwcRuntimeSupport() { #if defined(USE_KLEIDIAI) && defined(__aarch64__) From 1e8210862a4c6e5aafbfe15ec46c0a862f5d82e3 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 10:30:09 +0000 Subject: [PATCH 099/140] Add a check for unused params when USE_KLEIDIAI is off to convolve.cpp Add an assertion around nchwc optimzer tests for situations where they don't apply Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/lib/convolve.cpp | 10 ++++++++++ onnxruntime/test/optimizer/nchwc_optimizer_test.cc | 1 + 2 files changed, 11 insertions(+) diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index e0dcb9df51c56..9ff430df27e12 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1358,6 +1358,16 @@ MlasConvSupportsSymmetricChannelsLast2DFloatKernel( float Beta) { #if !defined(USE_KLEIDIAI) || !defined(__aarch64__) + MLAS_UNREFERENCED_PARAMETER(Dimensions); + MLAS_UNREFERENCED_PARAMETER(BatchCount); + MLAS_UNREFERENCED_PARAMETER(GroupCount); + MLAS_UNREFERENCED_PARAMETER(InputShape); + MLAS_UNREFERENCED_PARAMETER(KernelShape); + MLAS_UNREFERENCED_PARAMETER(DilationShape); + MLAS_UNREFERENCED_PARAMETER(Padding); + MLAS_UNREFERENCED_PARAMETER(StrideShape); + MLAS_UNREFERENCED_PARAMETER(FilterCount); + MLAS_UNREFERENCED_PARAMETER(Beta); return false; #else // Channels-last float convolution is only implemented by the KleidiAI diff --git a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc index cd210f7bc70ba..46312834aec9e 100644 --- a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc +++ b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc @@ -202,6 +202,7 @@ void NchwcOptimizerTester(const std::function& bu session_options.session_logid = "NchwcOptimizerTests"; InferenceSessionWrapper session{session_options, GetEnvironment()}; ASSERT_STATUS_OK(session.Load(model_data.data(), static_cast(model_data.size()))); + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Initialize()); RunOptions run_options; From f7d30c7a0988d599389bcbf2684af521e91a0514 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 13:42:48 +0000 Subject: [PATCH 100/140] Co-pilot fixes Macos build specific tweaks Signed-off-by: Orlaith Monahan --- cmake/onnxruntime_unittests.cmake | 17 +++++--- onnxruntime/core/providers/cpu/nn/conv.cc | 43 +++++++++++-------- .../internal_testing_tests.cc | 3 +- .../test/mlas/bench/bench_transcendental.cpp | 2 + 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index 7f361aa63921f..e929155a7cb18 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -50,6 +50,15 @@ function(filter_test_srcs test_srcs_var) endfunction() set(disabled_warnings) + +function(onnxruntime_disable_gtest_character_conversion_as_error target_name) + if (APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(${target_name} PRIVATE "$<$:-Wno-error=character-conversion>") + elseif (HAS_CHARACTER_CONVERSION) + target_compile_options(${target_name} PRIVATE "$<$:-Wno-error=character-conversion>") + endif() +endfunction() + function(AddTest) cmake_parse_arguments(_UT "DYN" "TARGET" "LIBS;SOURCES;DEPENDS;TEST_ARGS" ${ARGN}) list(REMOVE_DUPLICATES _UT_SOURCES) @@ -170,9 +179,7 @@ function(AddTest) if (${HAS_NOERROR}) target_compile_options(${_UT_TARGET} PRIVATE "$<$:-Wno-error=uninitialized>") endif() - if (${HAS_CHARACTER_CONVERSION}) - target_compile_options(${_UT_TARGET} PRIVATE "$<$:-Wno-error=character-conversion>") - endif() + onnxruntime_disable_gtest_character_conversion_as_error(${_UT_TARGET}) endif() set(TEST_ARGS ${_UT_TEST_ARGS}) @@ -847,9 +854,7 @@ if(MSVC) "$<$>:/wd6326>") else() target_include_directories(onnxruntime_test_utils PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${ONNXRUNTIME_ROOT}) - if (HAS_CHARACTER_CONVERSION) - target_compile_options(onnxruntime_test_utils PRIVATE "$<$:-Wno-error=character-conversion>") - endif() + onnxruntime_disable_gtest_character_conversion_as_error(onnxruntime_test_utils) endif() if (onnxruntime_USE_NCCL) target_include_directories(onnxruntime_test_utils PRIVATE ${NCCL_INCLUDE_DIRS}) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 4a679750600fb..0389e95eee8e8 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -31,15 +31,19 @@ namespace { template void ConvertNHWCToNCHW(const T* src, T* dst, int64_t n, int64_t c, int64_t h, int64_t w) { - const int64_t hw = (SafeInt(h) * w); - for (int64_t n_idx = 0; n_idx < n; ++n_idx) { - const int64_t n_src_offset = n_idx * hw * c; - const int64_t n_dst_offset = n_idx * c * hw; - for (int64_t c_idx = 0; c_idx < c; ++c_idx) { + const size_t n_count = narrow(n); + const size_t c_count = narrow(c); + const size_t hw = narrow(SafeInt(h) * w); + for (size_t n_idx = 0; n_idx < n_count; ++n_idx) { + const size_t n_src_offset = SafeInt(SafeInt(n_idx) * hw) * c_count; + const size_t n_dst_offset = SafeInt(SafeInt(n_idx) * c_count) * hw; + for (size_t c_idx = 0; c_idx < c_count; ++c_idx) { + const size_t c_dst_offset = SafeInt(c_idx) * hw; const T* src_ptr = src + n_src_offset + c_idx; - T* dst_ptr = dst + n_dst_offset + c_idx * hw; - for (int64_t hw_idx = 0; hw_idx < hw; ++hw_idx) { - dst_ptr[hw_idx] = src_ptr[hw_idx * c]; + T* dst_ptr = dst + n_dst_offset + c_dst_offset; + for (size_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + const size_t src_hw_offset = SafeInt(hw_idx) * c_count; + dst_ptr[hw_idx] = src_ptr[src_hw_offset]; } } } @@ -48,15 +52,19 @@ void ConvertNHWCToNCHW(const T* src, T* dst, template void ConvertNCHWToNHWC(const T* src, T* dst, int64_t n, int64_t c, int64_t h, int64_t w) { - const int64_t hw = (SafeInt(h) * w); - for (int64_t n_idx = 0; n_idx < n; ++n_idx) { - const int64_t n_src_offset = n_idx * c * hw; - const int64_t n_dst_offset = n_idx * hw * c; - for (int64_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + const size_t n_count = narrow(n); + const size_t c_count = narrow(c); + const size_t hw = narrow(SafeInt(h) * w); + for (size_t n_idx = 0; n_idx < n_count; ++n_idx) { + const size_t n_src_offset = SafeInt(SafeInt(n_idx) * c_count) * hw; + const size_t n_dst_offset = SafeInt(SafeInt(n_idx) * hw) * c_count; + for (size_t hw_idx = 0; hw_idx < hw; ++hw_idx) { + const size_t hw_dst_offset = SafeInt(hw_idx) * c_count; const T* src_ptr = src + n_src_offset + hw_idx; - T* dst_ptr = dst + n_dst_offset + hw_idx * c; - for (int64_t c_idx = 0; c_idx < c; ++c_idx) { - dst_ptr[c_idx] = src_ptr[c_idx * hw]; + T* dst_ptr = dst + n_dst_offset + hw_dst_offset; + for (size_t c_idx = 0; c_idx < c_count; ++c_idx) { + const size_t src_c_offset = SafeInt(c_idx) * hw; + dst_ptr[c_idx] = src_ptr[src_c_offset]; } } } @@ -291,7 +299,8 @@ Status Conv::Compute(OpKernelContext* context) const { const auto& sum_shape = Sum->Shape(); ORT_RETURN_IF_NOT(Y->Shape() == sum_shape, "output and sum shape must match"); if (manual_sum) { - sum_manual_buffer.assign(Sum->Data(), Sum->Data() + Y->Shape().Size()); + auto sum_span = Sum->DataAsSpan(); + sum_manual_buffer.assign(sum_span.begin(), sum_span.end()); sum_manual_data = sum_manual_buffer.data(); } else { auto sum_span = Sum->DataAsSpan(); diff --git a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc index 4cb7cf397d95d..5850afd2f84e8 100644 --- a/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc +++ b/onnxruntime/test/internal_testing_ep/internal_testing_tests.cc @@ -44,7 +44,8 @@ std::filesystem::path ResolveInternalTestPath(const std::filesystem::path& path) } std::filesystem::path candidate = std::filesystem::current_path() / path; - if (std::filesystem::exists(candidate)) { + std::error_code ec; + if (std::filesystem::exists(candidate, ec)) { return candidate; } diff --git a/onnxruntime/test/mlas/bench/bench_transcendental.cpp b/onnxruntime/test/mlas/bench/bench_transcendental.cpp index f7e461c29843a..3d42c0f84e6cc 100644 --- a/onnxruntime/test/mlas/bench/bench_transcendental.cpp +++ b/onnxruntime/test/mlas/bench/bench_transcendental.cpp @@ -19,7 +19,9 @@ constexpr float kSiluMaxValue = 20.0f; constexpr float kGeluMinValue = -10.0f; constexpr float kGeluMaxValue = 10.0f; constexpr float kInvSqrt2 = 0.7071067811865475244f; +#if defined(MLAS_TARGET_AMD64) constexpr int64_t kFusedBytesPerElement = 2; +#endif constexpr int64_t kSiluUnfusedBytesPerElement = 5; constexpr int64_t kGeluUnfusedBytesPerElement = 7; From 2c731d308273a867aa39afcebd38ab2ada0f4959 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 14:47:28 +0000 Subject: [PATCH 101/140] Tighten up nhwc params prune uneeded changes Signed-off-by: Orlaith Monahan --- .../core/optimizer/nhwc_transformer.cc | 178 +++++++++++++++++- .../kernel_type_str_resolver_utils_test.cc | 5 +- .../test/optimizer/nhwc_transformer_test.cc | 151 +++++++++++++-- 3 files changed, 314 insertions(+), 20 deletions(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 4f16b408c16f1..c0e1e34bd8580 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -16,6 +16,7 @@ #include "core/optimizer/layout_transformation/layout_transformation.h" #include "core/optimizer/transpose_optimization/ort_optimizer_utils.h" #include "core/optimizer/transpose_optimization/ort_transpose_optimization.h" +#include "core/providers/common.h" using namespace ONNX_NAMESPACE; using namespace ::onnxruntime::common; @@ -27,8 +28,128 @@ namespace onnxruntime { using namespace layout_transformation; #ifdef USE_KLEIDIAI -// Float NHWC Conv wrappers are only safe for 4D tensors today because the runtime fallback path -// only converts between NHWC and NCHW for 2D convolutions. +bool TryGetDimValueAsSizeT(const ONNX_NAMESPACE::TensorShapeProto& shape, int index, size_t& value) { + if (shape.dim_size() <= index || !shape.dim(index).has_dim_value()) { + return false; + } + + const int64_t dim_value = shape.dim(index).dim_value(); + if (dim_value < 0) { + return false; + } + + value = narrow(dim_value); + return true; +} + +bool TryReadPositiveOrZeroInts(const std::vector& values, std::array& out) { + if (values.size() != out.size()) { + return false; + } + + for (size_t i = 0; i < out.size(); ++i) { + if (values[i] < 0) { + return false; + } + + out[i] = narrow(values[i]); + } + + return true; +} + +bool TryReadPositiveOrZeroInts(const std::vector& values, std::array& out) { + if (values.size() != out.size()) { + return false; + } + + for (size_t i = 0; i < out.size(); ++i) { + if (values[i] < 0) { + return false; + } + + out[i] = narrow(values[i]); + } + + return true; +} + +bool TryParseAutoPadType(std::string_view value, AutoPadType& auto_pad_type) { + if (value.empty() || value == "NOTSET") { + auto_pad_type = AutoPadType::NOTSET; + return true; + } + + if (value == "VALID") { + auto_pad_type = AutoPadType::VALID; + return true; + } + + if (value == "SAME_UPPER") { + auto_pad_type = AutoPadType::SAME_UPPER; + return true; + } + + if (value == "SAME_LOWER") { + auto_pad_type = AutoPadType::SAME_LOWER; + return true; + } + + return false; +} + +bool TryComputeFloatNhwcPads(const api::NodeRef& node, + const std::array& input_shape, + const std::array& kernel_shape, + const std::array& strides, + const std::array& dilations, + std::array& pads) { + const auto auto_pad_value = node.GetAttributeString("auto_pad"); + AutoPadType auto_pad = AutoPadType::NOTSET; + if (!TryParseAutoPadType(auto_pad_value.value_or("NOTSET"), auto_pad)) { + return false; + } + + if (auto_pad == AutoPadType::NOTSET) { + const auto pads_opt = node.GetAttributeInts("pads"); + if (!pads_opt.has_value()) { + pads.fill(0); + return true; + } + + return TryReadPositiveOrZeroInts(*pads_opt, pads); + } + + std::array pads_int64{}; + for (size_t i = 0; i < 2; ++i) { + int64_t pad_head = 0; + int64_t pad_tail = 0; + int64_t out_dim = 0; + const auto status = ComputePadAndOutputShape( + narrow(input_shape[i]), + narrow(strides[i]), + narrow(kernel_shape[i]), + narrow(dilations[i]), + auto_pad, + pad_head, + pad_tail, + out_dim, + /*force_symmetric_auto_padding*/ false); + if (!status.IsOK() || pad_head < 0 || pad_tail < 0 || out_dim < 0) { + return false; + } + + pads_int64[i] = pad_head; + pads_int64[i + 2] = pad_tail; + } + + for (size_t i = 0; i < pads.size(); ++i) { + pads[i] = narrow(pads_int64[i]); + } + + return true; +} + bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& graph, onnx_transpose_optimization::api::NodeRef& node) { auto& base_node = NodeFromApiNode(node); @@ -55,7 +176,58 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr return false; } - return true; + const auto inputs = node.Inputs(); + if (base_node.OpType() == "FusedConv" && inputs.size() > 3 && !inputs[3].empty()) { + return false; + } + + const auto group = node.GetAttributeInt("group").value_or(1); + if (group != 1) { + return false; + } + + std::array input_spatial_shape{}; + std::array kernel_spatial_shape{}; + std::array dilations{1, 1}; + std::array strides{1, 1}; + std::array pads{}; + size_t batch_count = 0; + size_t filter_count = 0; + + if (!TryGetDimValueAsSizeT(*input_shape, 0, batch_count) || + !TryGetDimValueAsSizeT(*input_shape, 2, input_spatial_shape[0]) || + !TryGetDimValueAsSizeT(*input_shape, 3, input_spatial_shape[1]) || + !TryGetDimValueAsSizeT(*weight_shape, 0, filter_count) || + !TryGetDimValueAsSizeT(*weight_shape, 2, kernel_spatial_shape[0]) || + !TryGetDimValueAsSizeT(*weight_shape, 3, kernel_spatial_shape[1])) { + return false; + } + + const auto dilations_opt = node.GetAttributeInts("dilations"); + if (dilations_opt.has_value() && !TryReadPositiveOrZeroInts(*dilations_opt, dilations)) { + return false; + } + + const auto strides_opt = node.GetAttributeInts("strides"); + if (strides_opt.has_value() && !TryReadPositiveOrZeroInts(*strides_opt, strides)) { + return false; + } + + if (!TryComputeFloatNhwcPads(node, input_spatial_shape, kernel_spatial_shape, strides, dilations, pads)) { + return false; + } + + return MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + /*Dimensions*/ 2, + batch_count, + /*GroupCount*/ 1, + input_spatial_shape.data(), + kernel_spatial_shape.data(), + dilations.data(), + pads.data(), + strides.data(), + filter_count, + /*Beta*/ 0.0f); #endif } #endif diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 6a6c9ea084b77..09a49fb309d18 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -55,7 +55,7 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } -#if defined(USE_KLEIDIAI) && !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { SchemaRegistryManager schema_registry; const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); @@ -84,9 +84,6 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); } -#endif // defined(USE_KLEIDIAI) && !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) - -#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { KernelTypeStrResolver resolver; ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 163488c8db4e9..8f97febaaae78 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -1,13 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#include #include +#include #include #include "gtest/gtest.h" #include "core/framework/kernel_registry.h" -#include "test/unittest_util/graph_transform_test_builder.h" #include "core/mlas/inc/mlas.h" +#include "core/providers/common.h" +#include "test/unittest_util/graph_transform_test_builder.h" #if defined(USE_KLEIDIAI) && defined(__aarch64__) #include "core/mlas/lib/mlasi.h" #endif @@ -64,10 +67,124 @@ static bool HasFloatNhwcFusedConvKernel() { } #endif -static bool HasFloatNhwcRuntimeSupport() { +static bool HasFloatNhwcNoTransposeSupport(const std::vector& input_shape, + const std::vector& weight_shape, + std::vector pads = {}, + std::vector strides = {}, + std::vector dilations = {}, + int64_t group = 1, + bool has_sum_input = false, + std::string_view auto_pad = "NOTSET") { #if defined(USE_KLEIDIAI) && defined(__aarch64__) - return HasFloatNhwcFusedConvKernel() && MLAS_CPUIDINFO::GetCPUIDInfo().HasArm_SME(); + if (!HasFloatNhwcFusedConvKernel() || !MLAS_CPUIDINFO::GetCPUIDInfo().HasArm_SME()) { + return false; + } + + if (has_sum_input || group != 1 || input_shape.size() != 4 || weight_shape.size() != 4) { + return false; + } + + std::array input_spatial_shape{ + narrow(input_shape[2]), + narrow(input_shape[3]), + }; + std::array kernel_spatial_shape{ + narrow(weight_shape[2]), + narrow(weight_shape[3]), + }; + std::array strides_size_t{1, 1}; + std::array dilations_size_t{1, 1}; + std::array pads_size_t{}; + + if (!strides.empty()) { + if (strides.size() != strides_size_t.size()) { + return false; + } + + for (size_t i = 0; i < strides_size_t.size(); ++i) { + if (strides[i] < 0) { + return false; + } + + strides_size_t[i] = narrow(strides[i]); + } + } + + if (!dilations.empty()) { + if (dilations.size() != dilations_size_t.size()) { + return false; + } + + for (size_t i = 0; i < dilations_size_t.size(); ++i) { + if (dilations[i] < 0) { + return false; + } + + dilations_size_t[i] = narrow(dilations[i]); + } + } + + const AutoPadType auto_pad_type = StringToAutoPadType(std::string(auto_pad)); + if (auto_pad_type == AutoPadType::NOTSET) { + if (pads.empty()) { + pads_size_t.fill(0); + } else { + if (pads.size() != pads_size_t.size()) { + return false; + } + + for (size_t i = 0; i < pads_size_t.size(); ++i) { + if (pads[i] < 0) { + return false; + } + + pads_size_t[i] = narrow(pads[i]); + } + } + } else { + for (size_t i = 0; i < 2; ++i) { + int64_t pad_head = 0; + int64_t pad_tail = 0; + int64_t out_dim = 0; + const auto status = ComputePadAndOutputShape( + input_shape[2 + i], + narrow(strides_size_t[i]), + weight_shape[2 + i], + narrow(dilations_size_t[i]), + auto_pad_type, + pad_head, + pad_tail, + out_dim, + /*force_symmetric_auto_padding*/ false); + if (!status.IsOK() || pad_head < 0 || pad_tail < 0 || out_dim < 0) { + return false; + } + + pads_size_t[i] = narrow(pad_head); + pads_size_t[i + 2] = narrow(pad_tail); + } + } + + return MlasConvSupportsSymmetricChannelsLast2DFloatKernel( + /*Dimensions*/ 2, + narrow(input_shape[0]), + /*GroupCount*/ 1, + input_spatial_shape.data(), + kernel_spatial_shape.data(), + dilations_size_t.data(), + pads_size_t.data(), + strides_size_t.data(), + narrow(weight_shape[0]), + /*Beta*/ 0.0f); #else + ORT_UNUSED_PARAMETER(input_shape); + ORT_UNUSED_PARAMETER(weight_shape); + ORT_UNUSED_PARAMETER(pads); + ORT_UNUSED_PARAMETER(strides); + ORT_UNUSED_PARAMETER(dilations); + ORT_UNUSED_PARAMETER(group); + ORT_UNUSED_PARAMETER(has_sum_input); + ORT_UNUSED_PARAMETER(auto_pad); return false; #endif } @@ -301,8 +418,9 @@ TEST(NhwcTransformerTests, ConvDepthwiseFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport({1, 8, 7, 7}, {8, 1, 3, 3}, {}, {}, {}, 8); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -328,8 +446,9 @@ TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport({1, 8, 7, 7}, {16, 8, 3, 3}, {1, 1, 1, 1}); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -359,8 +478,10 @@ TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport( + {1, 8, 7, 7}, {16, 8, 3, 3}, {1, 1, 1, 1}, {1, 1}); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -392,8 +513,10 @@ TEST(NhwcTransformerTests, FusedConvWithSumFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); EXPECT_EQ(op_to_count["com.microsoft.nchwc.Conv"], 0); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 3 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport( + {1, 8, 7, 7}, {16, 8, 3, 3}, {0, 0, 0, 0}, {1, 1}, {}, 1, true); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 3 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; @@ -420,8 +543,10 @@ TEST(NhwcTransformerTests, ConvAutoPadFloat_SkipNhwc) { auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { auto op_to_count = CountOpsInGraph(session.GetGraph()); - const int expected_nhwc_fused_conv = HasFloatNhwcRuntimeSupport() ? 1 : 0; - const int expected_transposes = HasFloatNhwcRuntimeSupport() ? 2 : 0; + const bool expect_nhwc = HasFloatNhwcNoTransposeSupport( + {1, 8, 6, 6}, {16, 8, 3, 3}, {}, {2, 2}, {}, 1, false, "SAME_UPPER"); + const int expected_nhwc_fused_conv = expect_nhwc ? 1 : 0; + const int expected_transposes = expect_nhwc ? 2 : 0; EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], expected_nhwc_fused_conv); EXPECT_EQ(op_to_count["Transpose"], expected_transposes); }; From 80e29351b37789fc5ed4c5256def11f713ca0aca Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 15:17:53 +0000 Subject: [PATCH 102/140] One more test guard Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 09a49fb309d18..e9cd79f53870d 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -55,7 +55,7 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } -#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { SchemaRegistryManager schema_registry; const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); @@ -84,6 +84,7 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); } + TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { KernelTypeStrResolver resolver; ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); @@ -108,7 +109,7 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); } -#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in From e88659c139d938eec1655d2db53c654c2c50ef3f Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 25 Mar 2026 16:42:02 +0000 Subject: [PATCH 103/140] Small tweak to apple clang flags for macos builds Signed-off-by: Orlaith Monahan --- cmake/CMakeLists.txt | 1 + cmake/onnxruntime_unittests.cmake | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 83d1751e55543..dde6d44919092 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -638,6 +638,7 @@ else() check_cxx_compiler_flag(-Wcatch-value HAS_CATCH_VALUE) check_cxx_compiler_flag(-Wclass-memaccess HAS_CLASS_MEMACCESS) check_cxx_compiler_flag(-Wcharacter-conversion HAS_CHARACTER_CONVERSION) + check_cxx_compiler_flag(-Wno-error=character-conversion HAS_NO_ERROR_CHARACTER_CONVERSION) check_cxx_compiler_flag(-Wdangling-reference HAS_DANGLING_REFERENCE) check_cxx_compiler_flag(-Wdeprecated-anon-enum-enum-conversion HAS_DEPRECATED_ANON_ENUM_ENUM_CONVERSION) check_cxx_compiler_flag(-Wdeprecated-builtins HAS_DEPRECATED_BUILTINS) diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index e929155a7cb18..a061858fa068f 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -52,9 +52,7 @@ endfunction() set(disabled_warnings) function(onnxruntime_disable_gtest_character_conversion_as_error target_name) - if (APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") - target_compile_options(${target_name} PRIVATE "$<$:-Wno-error=character-conversion>") - elseif (HAS_CHARACTER_CONVERSION) + if (HAS_NO_ERROR_CHARACTER_CONVERSION) target_compile_options(${target_name} PRIVATE "$<$:-Wno-error=character-conversion>") endif() endfunction() From 9dc3094ed400e4cfeb78a9f841b33fe4702a57d7 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 7 Apr 2026 16:04:40 +0100 Subject: [PATCH 104/140] Updates to use MLAS_TARGET_ARM64 instead of __aarch64__ Tweak to be future proofed around nhwc in cases of non-4d tensors Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/lib/convolve.cpp | 4 ++-- onnxruntime/core/optimizer/nhwc_transformer.cc | 2 +- onnxruntime/core/providers/cpu/nn/conv.cc | 3 ++- onnxruntime/test/optimizer/nhwc_transformer_test.cc | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/onnxruntime/core/mlas/lib/convolve.cpp b/onnxruntime/core/mlas/lib/convolve.cpp index 9ff430df27e12..fda522511fa9c 100644 --- a/onnxruntime/core/mlas/lib/convolve.cpp +++ b/onnxruntime/core/mlas/lib/convolve.cpp @@ -1327,7 +1327,7 @@ Return Value: namespace { -#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) static constexpr size_t ComputeChannelsLastDilatedKernelSize(size_t dilation, size_t kernel) { return (dilation * kernel) - (dilation - 1); } @@ -1357,7 +1357,7 @@ MlasConvSupportsSymmetricChannelsLast2DFloatKernel( size_t FilterCount, float Beta) { -#if !defined(USE_KLEIDIAI) || !defined(__aarch64__) +#if !defined(USE_KLEIDIAI) || !defined(MLAS_TARGET_ARM64) MLAS_UNREFERENCED_PARAMETER(Dimensions); MLAS_UNREFERENCED_PARAMETER(BatchCount); MLAS_UNREFERENCED_PARAMETER(GroupCount); diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index c0e1e34bd8580..4bd7b81c6e211 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -155,7 +155,7 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr auto& base_node = NodeFromApiNode(node); ORT_UNUSED_PARAMETER(graph); -#if !defined(__aarch64__) +#if !defined(MLAS_TARGET_ARM64) return false; #else if (!CPUIDInfo::GetCPUIDInfo().HasArm_SME()) { diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 0389e95eee8e8..4968afe46106b 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -209,7 +209,8 @@ Status Conv::Compute(OpKernelContext* context) const { const Tensor* Sum = num_inputs >= 4 ? context->Input(3) : nullptr; ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X->Shape(), W->Shape(), channels_last_)); const int64_t N = X->Shape()[0]; - const int64_t C = X->Shape()[channels_last_ ? 3 : 1]; + // If channels_last_ we should get the back dim for channels instead of [1] + const int64_t C = channels_last_ ? X->Shape().GetDims().back() : X->Shape()[1]; const int64_t M = W->Shape()[0]; TensorShapeVector kernel_shape; diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index 8f97febaaae78..a24a4f492d2cd 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -11,7 +11,7 @@ #include "core/mlas/inc/mlas.h" #include "core/providers/common.h" #include "test/unittest_util/graph_transform_test_builder.h" -#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) #include "core/mlas/lib/mlasi.h" #endif #include "core/graph/graph.h" @@ -41,7 +41,7 @@ NodeArg* NhwcMakeInitializer(ModelTestBuilder& builder, const std::vector::max_value); } -#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) static bool HasFloatNhwcFusedConvKernel() { auto* cpu_ep = TestCPUExecutionProvider(); auto kernel_registry = cpu_ep->GetKernelRegistry(); @@ -75,7 +75,7 @@ static bool HasFloatNhwcNoTransposeSupport(const std::vector& input_sha int64_t group = 1, bool has_sum_input = false, std::string_view auto_pad = "NOTSET") { -#if defined(USE_KLEIDIAI) && defined(__aarch64__) +#if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) if (!HasFloatNhwcFusedConvKernel() || !MLAS_CPUIDINFO::GetCPUIDInfo().HasArm_SME()) { return false; } From 44213420871bd7d50e9afbb57ea33a14e66384a2 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 7 Apr 2026 16:21:11 +0100 Subject: [PATCH 105/140] Tweak to update conv tests to comply with new function decl Signed-off-by: Orlaith Monahan --- onnxruntime/test/mlas/unittest/test_conv2d.h | 420 +++++++++++++++++-- 1 file changed, 388 insertions(+), 32 deletions(-) diff --git a/onnxruntime/test/mlas/unittest/test_conv2d.h b/onnxruntime/test/mlas/unittest/test_conv2d.h index 736d8587b2546..6ac47c69ae0b8 100644 --- a/onnxruntime/test/mlas/unittest/test_conv2d.h +++ b/onnxruntime/test/mlas/unittest/test_conv2d.h @@ -3,33 +3,42 @@ #pragma once +#include + #include "test_util.h" +#if defined(MLAS_TARGET_AMD64) +#include "core/mlas/lib/mlasi.h" +#endif + template class MlasConv2DTest : public MlasTestBase { protected: - virtual void MlasConv2D(size_t BatchCount, - size_t GroupCount, - size_t InputChannels, - size_t InputHeight, - size_t InputWidth, - size_t FilterCount, - size_t KernelHeight, - size_t KernelWidth, - size_t PaddingLeftHeight, - size_t PaddingLeftWidth, - size_t PaddingRightHeight, - size_t PaddingRightWidth, - size_t DilationHeight, - size_t DilationWidth, - size_t StrideHeight, - size_t StrideWidth, - size_t OutputHeight, - size_t OutputWidth, - const float* Input, - const float* Filter, - const float* Bias, - float* Output) { + void MlasConv2DWithOptions(size_t BatchCount, + size_t GroupCount, + size_t InputChannels, + size_t InputHeight, + size_t InputWidth, + size_t FilterCount, + size_t KernelHeight, + size_t KernelWidth, + size_t PaddingLeftHeight, + size_t PaddingLeftWidth, + size_t PaddingRightHeight, + size_t PaddingRightWidth, + size_t DilationHeight, + size_t DilationWidth, + size_t StrideHeight, + size_t StrideWidth, + size_t OutputHeight, + size_t OutputWidth, + const float* Input, + const float* Filter, + const float* Bias, + const MLAS_ACTIVATION& Activation, + float Beta, + const float* InitialOutput, + float* Output) { int64_t InputShape[] = {int64_t(InputHeight), int64_t(InputWidth)}; int64_t KernelShape[] = {int64_t(KernelHeight), int64_t(KernelWidth)}; int64_t DilationShape[] = {int64_t(DilationHeight), int64_t(DilationWidth)}; @@ -37,8 +46,12 @@ class MlasConv2DTest : public MlasTestBase { int64_t StrideShape[] = {int64_t(StrideHeight), int64_t(StrideWidth)}; int64_t OutputShape[] = {int64_t(OutputHeight), int64_t(OutputWidth)}; - MLAS_ACTIVATION Activation; - Activation.ActivationKind = MlasIdentityActivation; + const size_t OutputElements = BatchCount * GroupCount * FilterCount * OutputHeight * OutputWidth; + if (InitialOutput != nullptr) { + std::memcpy(Output, InitialOutput, OutputElements * sizeof(float)); + } else { + std::fill_n(Output, OutputElements, 0.0f); + } MLAS_CONV_PARAMETERS Parameters; size_t WorkingBufferSize; @@ -58,7 +71,7 @@ class MlasConv2DTest : public MlasTestBase { &Activation, &WorkingBufferSize, false, - 0.0f, + Beta, threadpool_); MlasConv(&Parameters, @@ -70,7 +83,72 @@ class MlasConv2DTest : public MlasTestBase { threadpool_); } - void ReferenceConv2D( + virtual void MlasConv2D(size_t BatchCount, + size_t GroupCount, + size_t InputChannels, + size_t InputHeight, + size_t InputWidth, + size_t FilterCount, + size_t KernelHeight, + size_t KernelWidth, + size_t PaddingLeftHeight, + size_t PaddingLeftWidth, + size_t PaddingRightHeight, + size_t PaddingRightWidth, + size_t DilationHeight, + size_t DilationWidth, + size_t StrideHeight, + size_t StrideWidth, + size_t OutputHeight, + size_t OutputWidth, + const float* Input, + const float* Filter, + const float* Bias, + float* Output) { + MLAS_ACTIVATION Activation; + Activation.ActivationKind = MlasIdentityActivation; + + MlasConv2DWithOptions(BatchCount, + GroupCount, + InputChannels, + InputHeight, + InputWidth, + FilterCount, + KernelHeight, + KernelWidth, + PaddingLeftHeight, + PaddingLeftWidth, + PaddingRightHeight, + PaddingRightWidth, + DilationHeight, + DilationWidth, + StrideHeight, + StrideWidth, + OutputHeight, + OutputWidth, + Input, + Filter, + Bias, + Activation, + 0.0f, + nullptr, + Output); + } + + static float ApplyReferenceActivation(float value, const MLAS_ACTIVATION& Activation) { + switch (Activation.ActivationKind) { + case MlasIdentityActivation: + return value; + case MlasReluActivation: + return std::max(value, 0.0f); + default: + ADD_FAILURE() << "Unsupported activation kind in Conv2D test reference path: " + << static_cast(Activation.ActivationKind); + return value; + } + } + + void ReferenceConv2DWithOptions( size_t BatchCount, size_t GroupCount, size_t InputChannels, @@ -90,14 +168,24 @@ class MlasConv2DTest : public MlasTestBase { const float* Input, const float* Filter, const float* Bias, + const MLAS_ACTIVATION& Activation, + float Beta, + const float* InitialOutput, float* Output) { size_t InputSize = InputHeight * InputWidth; size_t OutputSize = OutputHeight * OutputWidth; size_t KernelSize = KernelHeight * KernelWidth; + size_t OutputElements = BatchCount * GroupCount * FilterCount * OutputSize; size_t K = InputChannels * KernelSize; size_t Im2ColElements = OutputSize * K; + if (InitialOutput != nullptr) { + std::memcpy(Output, InitialOutput, OutputElements * sizeof(float)); + } else { + std::fill_n(Output, OutputElements, 0.0f); + } + for (size_t b = 0; b < BatchCount; b++) { const float* filter = Filter; const float* bias = Bias; @@ -128,31 +216,81 @@ class MlasConv2DTest : public MlasTestBase { Input += InputSize; } - MlasGemm(CblasNoTrans, CblasNoTrans, FilterCount, OutputSize, K, 1.0f, - filter, K, Im2Col, OutputSize, 0.0f, Output, OutputSize, threadpool_); + float* output_group = Output; - // - // Apply the bias. - // + MlasGemm(CblasNoTrans, CblasNoTrans, FilterCount, OutputSize, K, 1.0f, + filter, K, Im2Col, OutputSize, Beta, output_group, OutputSize, threadpool_, nullptr); for (size_t f = 0; f < FilterCount; f++) { float biasValue = *bias++; + float* output_row = output_group + f * OutputSize; for (size_t o = 0; o < OutputSize; o++) { - *Output++ += biasValue; + output_row[o] = ApplyReferenceActivation(output_row[o] + biasValue, Activation); } } filter += FilterCount * InputChannels * KernelSize; + Output += FilterCount * OutputSize; } } } + void ReferenceConv2D( + size_t BatchCount, + size_t GroupCount, + size_t InputChannels, + size_t InputHeight, + size_t InputWidth, + size_t FilterCount, + size_t KernelHeight, + size_t KernelWidth, + size_t PaddingLeftHeight, + size_t PaddingLeftWidth, + size_t DilationHeight, + size_t DilationWidth, + size_t StrideHeight, + size_t StrideWidth, + size_t OutputHeight, + size_t OutputWidth, + const float* Input, + const float* Filter, + const float* Bias, + float* Output) { + MLAS_ACTIVATION Activation; + Activation.ActivationKind = MlasIdentityActivation; + + ReferenceConv2DWithOptions(BatchCount, + GroupCount, + InputChannels, + InputHeight, + InputWidth, + FilterCount, + KernelHeight, + KernelWidth, + PaddingLeftHeight, + PaddingLeftWidth, + DilationHeight, + DilationWidth, + StrideHeight, + StrideWidth, + OutputHeight, + OutputWidth, + Input, + Filter, + Bias, + Activation, + 0.0f, + nullptr, + Output); + } + MatrixGuardBuffer BufferInput; MatrixGuardBuffer BufferFilter; MatrixGuardBuffer BufferBias; MatrixGuardBuffer BufferOutput; MatrixGuardBuffer BufferOutputReference; + MatrixGuardBuffer BufferInitialOutput; MatrixGuardBuffer BufferWorking; MatrixGuardBuffer BufferIm2Col; @@ -166,6 +304,194 @@ class MlasConv2DTest : public MlasTestBase { MlasConv2DTest() : threadpool_(Threaded ? GetMlasThreadPool() : nullptr) {} +#if defined(MLAS_TARGET_AMD64) + void TestMobileClipAvx512DispatchSelection(size_t GroupCount, + size_t InputHeight, + size_t InputWidth) { + if (GetMlasPlatform().ConvNchwFloatKernel != MlasConvNchwFloatKernelAvx512F) { + return; + } + + constexpr size_t BatchCount = 1; + constexpr size_t InputChannels = 1; + constexpr size_t FilterCount = 2; + constexpr size_t KernelHeight = 7; + constexpr size_t KernelWidth = 7; + constexpr size_t PaddingLeftHeight = 3; + constexpr size_t PaddingLeftWidth = 3; + constexpr size_t PaddingRightHeight = 3; + constexpr size_t PaddingRightWidth = 3; + constexpr size_t DilationHeight = 1; + constexpr size_t DilationWidth = 1; + constexpr size_t StrideHeight = 2; + constexpr size_t StrideWidth = 2; + + const int64_t OutputHeight64 = + ((int64_t(InputHeight) + int64_t(PaddingLeftHeight) + int64_t(PaddingRightHeight)) - + (int64_t(DilationHeight) * (int64_t(KernelHeight) - 1) + 1)) / + int64_t(StrideHeight) + + 1; + const int64_t OutputWidth64 = + ((int64_t(InputWidth) + int64_t(PaddingLeftWidth) + int64_t(PaddingRightWidth)) - + (int64_t(DilationWidth) * (int64_t(KernelWidth) - 1) + 1)) / + int64_t(StrideWidth) + + 1; + + ASSERT_GT(OutputHeight64, 0); + ASSERT_GT(OutputWidth64, 0); + + int64_t InputShape[] = {int64_t(InputHeight), int64_t(InputWidth)}; + int64_t KernelShape[] = {int64_t(KernelHeight), int64_t(KernelWidth)}; + int64_t DilationShape[] = {int64_t(DilationHeight), int64_t(DilationWidth)}; + int64_t Padding[] = {int64_t(PaddingLeftHeight), int64_t(PaddingLeftWidth), int64_t(PaddingRightHeight), int64_t(PaddingRightWidth)}; + int64_t StrideShape[] = {int64_t(StrideHeight), int64_t(StrideWidth)}; + int64_t OutputShape[] = {OutputHeight64, OutputWidth64}; + + MLAS_ACTIVATION Activation; + Activation.ActivationKind = MlasIdentityActivation; + + MLAS_CONV_PARAMETERS Parameters; + size_t WorkingBufferSize = 0; + + MlasConvPrepare(&Parameters, + 2, + BatchCount, + GroupCount, + InputChannels, + InputShape, + KernelShape, + DilationShape, + Padding, + StrideShape, + OutputShape, + FilterCount, + &Activation, + &WorkingBufferSize, + false, + 0.0f, + threadpool_); + + ASSERT_EQ(Parameters.Algorithm, MlasConvAlgorithmDepthwiseMultiplierGreaterThan1) + << "Expected AVX512 MobileClip dispatch for G" << GroupCount + << "/H" << InputHeight + << "/W" << InputWidth; + } +#endif + + void TestMobileClipBetaActivationRegression(size_t GroupCount, + size_t InputHeight, + size_t InputWidth) { + constexpr size_t BatchCount = 1; + constexpr size_t InputChannels = 1; + constexpr size_t FilterCount = 2; + constexpr size_t KernelHeight = 7; + constexpr size_t KernelWidth = 7; + constexpr size_t PaddingLeftHeight = 3; + constexpr size_t PaddingLeftWidth = 3; + constexpr size_t PaddingRightHeight = 3; + constexpr size_t PaddingRightWidth = 3; + constexpr size_t DilationHeight = 1; + constexpr size_t DilationWidth = 1; + constexpr size_t StrideHeight = 2; + constexpr size_t StrideWidth = 2; + constexpr float Beta = 1.0f; + + const int64_t OutputHeight64 = + ((int64_t(InputHeight) + int64_t(PaddingLeftHeight) + int64_t(PaddingRightHeight)) - + (int64_t(DilationHeight) * (int64_t(KernelHeight) - 1) + 1)) / + int64_t(StrideHeight) + + 1; + const int64_t OutputWidth64 = + ((int64_t(InputWidth) + int64_t(PaddingLeftWidth) + int64_t(PaddingRightWidth)) - + (int64_t(DilationWidth) * (int64_t(KernelWidth) - 1) + 1)) / + int64_t(StrideWidth) + + 1; + + ASSERT_GT(OutputHeight64, 0); + ASSERT_GT(OutputWidth64, 0); + + const size_t OutputHeight = static_cast(OutputHeight64); + const size_t OutputWidth = static_cast(OutputWidth64); + const size_t InputSize = InputHeight * InputWidth; + const size_t KernelSize = KernelHeight * KernelWidth; + const size_t OutputSize = OutputHeight * OutputWidth; + + const size_t InputElements = BatchCount * GroupCount * InputChannels * InputSize; + const size_t FilterElements = GroupCount * FilterCount * InputChannels * KernelSize; + const size_t BiasElements = GroupCount * FilterCount; + const size_t OutputElements = BatchCount * GroupCount * FilterCount * OutputSize; + + const float* Input = BufferInput.GetBuffer(InputElements); + const float* Filter = BufferFilter.GetBuffer(FilterElements); + const float* Bias = BufferBias.GetBuffer(BiasElements); + const float* InitialOutput = BufferInitialOutput.GetBuffer(OutputElements); + float* Output = BufferOutput.GetBuffer(OutputElements); + float* OutputReference = BufferOutputReference.GetBuffer(OutputElements); + + MLAS_ACTIVATION Activation; + Activation.ActivationKind = MlasReluActivation; + + MlasConv2DWithOptions(BatchCount, + GroupCount, + InputChannels, + InputHeight, + InputWidth, + FilterCount, + KernelHeight, + KernelWidth, + PaddingLeftHeight, + PaddingLeftWidth, + PaddingRightHeight, + PaddingRightWidth, + DilationHeight, + DilationWidth, + StrideHeight, + StrideWidth, + OutputHeight, + OutputWidth, + Input, + Filter, + Bias, + Activation, + Beta, + InitialOutput, + Output); + + ReferenceConv2DWithOptions(BatchCount, + GroupCount, + InputChannels, + InputHeight, + InputWidth, + FilterCount, + KernelHeight, + KernelWidth, + PaddingLeftHeight, + PaddingLeftWidth, + DilationHeight, + DilationWidth, + StrideHeight, + StrideWidth, + OutputHeight, + OutputWidth, + Input, + Filter, + Bias, + Activation, + Beta, + InitialOutput, + OutputReference); + + for (size_t i = 0; i < OutputElements; ++i) { + ASSERT_TRUE(CloseEnough(Output[i], OutputReference[i])) + << "Mismatch at output index " << i + << " for G" << GroupCount + << "/H" << InputHeight + << "/W" << InputWidth + << ": actual=" << Output[i] + << ", expected=" << OutputReference[i]; + } + } + void Test( size_t BatchCount, size_t GroupCount, @@ -332,5 +658,35 @@ class MlasConv2DTest : public MlasTestBase { } } } + + // + // Regression test: exercise a KleidiAI Conv2D path when KleidiAI is enabled. + // See https://github.com/microsoft/onnxruntime/issues/26669. + // + // The KleidiAI implementation uses an internal per-thread padding buffer for out-of-bounds pixels + // when constructing the LHS indirection table. Historically, if the buffer was too small for a later + // convolution (larger CI), resizing could invalidate cached indirection pointers and lead to + // non-deterministic corruption. + // + // This sequence forces pad-buffer growth by running a smaller-CI convolution followed by a larger-CI + // convolution (with padding to ensure pad pointers are used), then runs the smaller-CI convolution again. + // Repeat a few times to increase the likelihood of triggering a reallocation and verify the path. + // + for (int i = 0; i < 4; ++i) { + Test(1, 1, 64, 11, 11, 32, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1); // smaller CI + Test(1, 1, 320, 11, 11, 32, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1); // larger CI forces pad buffer growth + Test(1, 1, 64, 11, 11, 32, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1); // sanity: back to smaller CI after growth + } + } + + void ExecuteShort(void) override { +#if defined(MLAS_TARGET_AMD64) + TestMobileClipAvx512DispatchSelection(64, 64, 64); + TestMobileClipAvx512DispatchSelection(128, 32, 32); + TestMobileClipAvx512DispatchSelection(256, 16, 16); +#endif + TestMobileClipBetaActivationRegression(64, 64, 64); + TestMobileClipBetaActivationRegression(128, 32, 32); + TestMobileClipBetaActivationRegression(256, 16, 16); } }; From f91b330f10111606ecb94533d48f340d3a4cd81c Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 21 Apr 2026 10:47:24 +0100 Subject: [PATCH 106/140] Update NHWC implementation to honour use_kleidiai flag * Co-Pilot fixes Signed-off-by: Orlaith Monahan --- .../core/optimizer/graph_transformer_utils.cc | 4 +- .../core/optimizer/nhwc_transformer.cc | 13 ++++++- onnxruntime/core/optimizer/nhwc_transformer.h | 3 +- .../test/optimizer/nhwc_transformer_test.cc | 37 +++++++++++++++++++ 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/onnxruntime/core/optimizer/graph_transformer_utils.cc b/onnxruntime/core/optimizer/graph_transformer_utils.cc index 6c06abe5fcef5..8f3e4dcf6e7e7 100644 --- a/onnxruntime/core/optimizer/graph_transformer_utils.cc +++ b/onnxruntime/core/optimizer/graph_transformer_utils.cc @@ -463,7 +463,7 @@ InlinedVector> GenerateTransformers( auto cpu_registry = cpu_execution_provider.GetKernelRegistry(); auto nhwc_transformer = std::make_unique(std::move(cpu_allocator), std::move(cpu_registry), - logger); + logger, session_options.config_options); if (nhwc_transformer->IsActive()) { transformers.emplace_back(std::move(nhwc_transformer)); } @@ -566,7 +566,7 @@ InlinedVector> GenerateTransformersForMinimalB AllocatorPtr cpu_allocator = CPUAllocator::DefaultInstance(); auto cpu_registry = cpu_execution_provider.GetKernelRegistry(); auto nhwc_transformer = std::make_unique(std::move(cpu_allocator), std::move(cpu_registry), - logger); + logger, session_options.config_options); if (nhwc_transformer->IsActive()) { transformers.emplace_back(std::move(nhwc_transformer)); } diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 4bd7b81c6e211..61d951f3f0625 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -17,6 +17,7 @@ #include "core/optimizer/transpose_optimization/ort_optimizer_utils.h" #include "core/optimizer/transpose_optimization/ort_transpose_optimization.h" #include "core/providers/common.h" +#include "core/providers/cpu/mlas_backend_kernel_selector_config_utils.h" using namespace ONNX_NAMESPACE; using namespace ::onnxruntime::common; @@ -28,6 +29,8 @@ namespace onnxruntime { using namespace layout_transformation; #ifdef USE_KLEIDIAI +namespace { + bool TryGetDimValueAsSizeT(const ONNX_NAMESPACE::TensorShapeProto& shape, int index, size_t& value) { if (shape.dim_size() <= index || !shape.dim(index).has_dim_value()) { return false; @@ -230,6 +233,8 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr /*Beta*/ 0.0f); #endif } + +} // namespace #endif static inline const OpTransformInfo* @@ -264,13 +269,17 @@ NhwcConvLookup( NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, std::shared_ptr cpu_kernel_registry, - const logging::Logger& logger) noexcept + const logging::Logger& logger, + const ConfigOptions& config_options) noexcept : GraphTransformer("NhwcTransformer"), cpu_allocator_(std::move(cpu_allocator)) { if (!cpu_kernel_registry) { // This is a CPU op nodes optimizer, not useful if cpu EP is not available. return; } + MLAS_BACKEND_KERNEL_SELECTOR_CONFIG mlas_backend_kernel_selector_config{}; + SetupMlasBackendKernelSelectorFromConfigOptions(mlas_backend_kernel_selector_config, config_options); + // // Constructing a mapping table from operators to be transformed to their target. // Make sure that the new nodes we are about to create during graph transformation, @@ -355,7 +364,7 @@ NhwcTransformer::NhwcTransformer(AllocatorPtr cpu_allocator, #ifdef USE_KLEIDIAI // KleidiAI specific block for NhwcFusedConvolutions - { + if (mlas_backend_kernel_selector_config.use_kleidiai) { // F32 Conv -> F32 NHWC Conv OpKernelRegistryId nhwc_conv_fp32{ "NhwcFusedConv", kMSDomain, 1, {{"T", {DataTypeImpl::GetTensorType()}}}}; diff --git a/onnxruntime/core/optimizer/nhwc_transformer.h b/onnxruntime/core/optimizer/nhwc_transformer.h index 24d4cb3069b30..4755ceb316fef 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.h +++ b/onnxruntime/core/optimizer/nhwc_transformer.h @@ -5,6 +5,7 @@ #include #include "core/common/common.h" +#include "core/framework/config_options.h" #include "core/framework/execution_provider.h" #include "core/framework/kernel_registry.h" #include "core/optimizer/graph_transformer.h" @@ -82,7 +83,7 @@ class NhwcTransformer : public GraphTransformer { private: public: explicit NhwcTransformer(AllocatorPtr cpu_allocator, std::shared_ptr cpu_kernel_registry, - const logging::Logger& logger) noexcept; + const logging::Logger& logger, const ConfigOptions& config_options) noexcept; /** * @brief Usually called right after constructor, it shows whether diff --git a/onnxruntime/test/optimizer/nhwc_transformer_test.cc b/onnxruntime/test/optimizer/nhwc_transformer_test.cc index a24a4f492d2cd..b73929efab8a6 100644 --- a/onnxruntime/test/optimizer/nhwc_transformer_test.cc +++ b/onnxruntime/test/optimizer/nhwc_transformer_test.cc @@ -10,6 +10,7 @@ #include "core/framework/kernel_registry.h" #include "core/mlas/inc/mlas.h" #include "core/providers/common.h" +#include "core/session/onnxruntime_session_options_config_keys.h" #include "test/unittest_util/graph_transform_test_builder.h" #if defined(USE_KLEIDIAI) && defined(MLAS_TARGET_ARM64) #include "core/mlas/lib/mlasi.h" @@ -462,6 +463,42 @@ TEST(NhwcTransformerTests, ConvFloat_UsesNhwcOnlyWithKleidi) { /*relative_per_sample_tolerance*/ 1e-6); } +TEST(NhwcTransformerTests, ConvFloat_RespectsKleidiDisableConfig) { + if (!HasFloatNhwcNoTransposeSupport({1, 8, 7, 7}, {16, 8, 3, 3}, {1, 1, 1, 1})) { + GTEST_SKIP() << "Float NHWC KleidiAI path is not available on this configuration."; + } + + auto build_test_case = [&](ModelTestBuilder& builder) { + auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); + auto* weight_arg = builder.MakeInitializer({16, 8, 3, 3}, -1.0f, 1.0f); + auto* output_arg = builder.MakeOutput(); + + Node& conv_node = builder.AddConvNode(input_arg, weight_arg, output_arg); + conv_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); + }; + + auto check_nhwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); + EXPECT_EQ(op_to_count["com.microsoft.NhwcFusedConv"], 0); + EXPECT_EQ(op_to_count["Transpose"], 0); + }; + + auto add_session_options = [](SessionOptions& session_options) { + const auto status = session_options.config_options.AddConfigEntry(kOrtSessionOptionsMlasDisableKleidiAi, "1"); + ASSERT_TRUE(status.IsOK()) << status.ErrorMessage(); + }; + + TransformerTester(build_test_case, + check_nhwc_graph, + TransformerLevel::Level2, + TransformerLevel::Level3, + /*opset_version*/ 12, + /*per_sample_tolerance*/ 1e-6, + /*relative_per_sample_tolerance*/ 1e-6, + nullptr, + add_session_options); +} + TEST(NhwcTransformerTests, FusedConvFloat_UsesNhwcOnlyWithKleidi) { auto build_test_case = [&](ModelTestBuilder& builder) { auto* input_arg = builder.MakeInput({1, 8, 7, 7}, -1.0f, 1.0f); From b9add7c7e8d8a0538f81a15cfe32a5686ac33b96 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 21 Apr 2026 11:47:29 +0100 Subject: [PATCH 107/140] Add a comment to LayoutTransformDoesNotRetargetNhwcFusedConv test to indicate what is being tested * Make the checks in TestConvPath stricter Signed-off-by: Orlaith Monahan --- onnxruntime/test/optimizer/conv_add_act_test.cc | 4 ++-- onnxruntime/test/optimizer/transpose_optimizer_test.cc | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/onnxruntime/test/optimizer/conv_add_act_test.cc b/onnxruntime/test/optimizer/conv_add_act_test.cc index 660f14c752886..03ca950050d64 100644 --- a/onnxruntime/test/optimizer/conv_add_act_test.cc +++ b/onnxruntime/test/optimizer/conv_add_act_test.cc @@ -30,8 +30,8 @@ void TestConvPath(const std::vector& input_shape, const std::vector disabled_optimizers = {"NchwcTransformer", "NhwcTransformer"}; TransformerTester(build_test_case, diff --git a/onnxruntime/test/optimizer/transpose_optimizer_test.cc b/onnxruntime/test/optimizer/transpose_optimizer_test.cc index 9fafe7f578954..080c382db5d93 100644 --- a/onnxruntime/test/optimizer/transpose_optimizer_test.cc +++ b/onnxruntime/test/optimizer/transpose_optimizer_test.cc @@ -4604,6 +4604,8 @@ TEST(TransposeOptimizerTests, QnnTransposeReshape) { } } +// Verifies that layout transformation preserves an existing NHWC-native +// NhwcFusedConv as-is instead of retargeting it or inserting Transpose nodes. TEST(TransposeOptimizerTests, LayoutTransformDoesNotRetargetNhwcFusedConv) { std::unordered_map domain_to_version{{kOnnxDomain, 13}, {kMSDomain, 1}}; Model model("LayoutTransformDoesNotRetargetNhwcFusedConv", false, ModelMetaData(), PathString(), From 56495764e2a7f234399ec27b9c1770a530aa4d1a Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 21 Apr 2026 15:46:57 +0100 Subject: [PATCH 108/140] Tighten up the checks around NCHWc and add a unittest Signed-off-by: Orlaith Monahan --- .../core/optimizer/nchwc_transformer.cc | 17 ++++++++--- .../test/optimizer/nchwc_optimizer_test.cc | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/onnxruntime/core/optimizer/nchwc_transformer.cc b/onnxruntime/core/optimizer/nchwc_transformer.cc index f28172444709b..a971a058f43b7 100644 --- a/onnxruntime/core/optimizer/nchwc_transformer.cc +++ b/onnxruntime/core/optimizer/nchwc_transformer.cc @@ -324,13 +324,17 @@ void NchwcTransformerImpl::ConvPoolShapeInference(const Node& node, void NchwcTransformerImpl::TransformConv(Node& node) { auto& input_defs = node.MutableInputDefs(); auto& output_defs = node.MutableOutputDefs(); + NchwcArgument* nchwc_sum_input = nullptr; // The internal NCHWc Conv kernel can consume an optional fused Sum input, but it expects - // that tensor to already be in NCHWc layout. This transform only legalizes the main Conv - // input and static weights/bias, so a pre-existing FusedConv(X, W, B, Sum) would feed a - // plain NCHW tensor into the NCHWc kernel and produce incorrect results. + // that tensor to already be in NCHWc layout. Only allow transforming a pre-existing + // FusedConv(X, W, B, Sum) when the Sum input already has a tracked NCHWc variant that + // can be wired through directly. if (node.OpType() == "FusedConv" && input_defs.size() >= 4 && input_defs[3] != nullptr && input_defs[3]->Exists()) { - return; + nchwc_sum_input = LookupNchwcArgument(input_defs[3]); + if (nchwc_sum_input == nullptr) { + return; + } } // Require that the weights tensor be static. @@ -498,6 +502,11 @@ void NchwcTransformerImpl::TransformConv(Node& node) { nchwc_node.MutableInputDefs()[2] = nchwc_conv_B_arg; } + if (nchwc_sum_input != nullptr) { + nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; + nchwc_sum_input->remaining_original_uses_--; + } + NchwcArgument::Shape output_shape(output_defs[0]); if (do_reorder_input) { diff --git a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc index 46312834aec9e..427b8e90449ca 100644 --- a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc +++ b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc @@ -644,6 +644,35 @@ TEST(NchwcOptimizerTests, FusedConvAddFusion) { test_case(true, true, 1); } +TEST(NchwcOptimizerTests, PreExistingFusedConvWithNchwcSumInput) { + auto build_test_case = [&](NchwcTestHelper& helper) { + auto* input_arg = helper.MakeInput({1, 32, 28, 28}); + auto* sum_arg = helper.MakeIntermediate(); + auto* output_arg = helper.MakeOutput(); + + helper.AddConvNode(input_arg, sum_arg, {32, 32, 3, 3}); + + auto* weights_arg = helper.MakeInitializer({32, 32, 3, 3}); + auto* bias_arg = helper.MakeInitializer({32}); + auto& fused_conv_node = + helper.AddNode("FusedConv", {input_arg, weights_arg, bias_arg, sum_arg}, {output_arg}, kMSDomain); + fused_conv_node.AddAttribute("activation", "Relu"); + fused_conv_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); + fused_conv_node.AddAttribute("strides", std::vector{1, 1}); + fused_conv_node.AddAttribute("kernel_shape", std::vector{3, 3}); + }; + + auto check_nchwc_graph = [&](InferenceSessionWrapper& session) { + auto op_to_count = CountOpsInGraph(session.GetGraph()); + EXPECT_EQ(op_to_count["com.microsoft.nchwc.Conv"], 2); + EXPECT_EQ(op_to_count["com.microsoft.nchwc.ReorderInput"], 1); + EXPECT_EQ(op_to_count["com.microsoft.nchwc.ReorderOutput"], 1); + EXPECT_EQ(op_to_count["com.microsoft.FusedConv"], 0); + }; + + NchwcOptimizerTester(build_test_case, check_nchwc_graph); +} + TEST(NchwcOptimizerTests, ConvBinary) { auto test_case = [&](const std::string& op_type) { auto build_test_case = [&](NchwcTestHelper& helper) { From 881ce587dd64a651b13e2831023c83edc6e187c1 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 22 Apr 2026 10:50:41 +0100 Subject: [PATCH 109/140] Add missing padding to PreExistingFusedConvWithNchwcSumInput Signed-off-by: Orlaith Monahan --- onnxruntime/test/optimizer/nchwc_optimizer_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc index 427b8e90449ca..a07f173950051 100644 --- a/onnxruntime/test/optimizer/nchwc_optimizer_test.cc +++ b/onnxruntime/test/optimizer/nchwc_optimizer_test.cc @@ -650,7 +650,8 @@ TEST(NchwcOptimizerTests, PreExistingFusedConvWithNchwcSumInput) { auto* sum_arg = helper.MakeIntermediate(); auto* output_arg = helper.MakeOutput(); - helper.AddConvNode(input_arg, sum_arg, {32, 32, 3, 3}); + auto& sum_node = helper.AddConvNode(input_arg, sum_arg, {32, 32, 3, 3}); + sum_node.AddAttribute("pads", std::vector{1, 1, 1, 1}); auto* weights_arg = helper.MakeInitializer({32, 32, 3, 3}); auto* bias_arg = helper.MakeInitializer({32}); From 76baa5ed10c6cd2eac9b352c8204de5bc4e1f2fa Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 09:21:33 +0100 Subject: [PATCH 110/140] Changing ort_model_only_test.cc to use the same function for all models * Keeps code consistent * Tests are a little more vulnerable to testdata not being where expected Signed-off-by: Orlaith Monahan --- .../test/framework/ort_model_only_test.cc | 31 ++----------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 4061588346f67..d711e4c47498d 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -28,31 +28,6 @@ using namespace ONNX_NAMESPACE; namespace onnxruntime { namespace test { -namespace { -std::filesystem::path ResolveTestPath(const std::filesystem::path& path) { - if (path.is_absolute() || path.empty()) { - return path; - } - - std::filesystem::path workspace_candidate = std::filesystem::current_path() / path; - std::error_code ec; - if (std::filesystem::exists(workspace_candidate, ec) && !ec) { - return workspace_candidate; - } - - static const std::filesystem::path kSourceTestRoot = - std::filesystem::path{ORT_TSTR_ON_MACRO(__FILE__)}.parent_path().parent_path(); - std::filesystem::path source_candidate = kSourceTestRoot / path; - ec.clear(); - if (std::filesystem::exists(source_candidate, ec) && !ec) { - return source_candidate; - } - - // Keep the original relative-to-workdir intent so the downstream file-open - // error points to the path we actually tried first. - return workspace_candidate; -} -} // namespace struct OrtModelTestInfo { std::basic_string model_filename; @@ -92,7 +67,7 @@ static void RunOrtModel(const OrtModelTestInfo& test_info) { std::vector model_data; InferenceSessionWrapper session_object{so, GetEnvironment()}; - std::filesystem::path model_path = ResolveTestPath(std::filesystem::path{test_info.model_filename}); + std::filesystem::path model_path{test_info.model_filename}; const auto& model_path_str = model_path.native(); if (test_info.run_use_buffer) { @@ -250,8 +225,8 @@ static void SaveAndCompareModels(const PathString& orig_file, const PathString& ort_file, TransformerLevel optimization_level = TransformerLevel::Level3, bool compare_saved_model = true) { - std::filesystem::path orig_path = ResolveTestPath(std::filesystem::path{orig_file}); - std::filesystem::path ort_path = ResolveTestPath(std::filesystem::path{ort_file}); + std::filesystem::path orig_path{orig_file}; + std::filesystem::path ort_path{ort_file}; if (ort_path.has_parent_path()) { std::filesystem::create_directories(ort_path.parent_path()); } From f254b25df6aa5b7ee45ad42edee75d853c60f7aa Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 09:33:48 +0100 Subject: [PATCH 111/140] Change the fallback convert functions in conv.cc to use more optimised MLAS routines Signed-off-by: Orlaith Monahan --- onnxruntime/core/providers/cpu/nn/conv.cc | 32 +++++++---------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 4968afe46106b..4b6e4178f20c8 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -30,43 +30,29 @@ namespace { template void ConvertNHWCToNCHW(const T* src, T* dst, - int64_t n, int64_t c, int64_t h, int64_t w) { + int64_t n, int64_t c, int64_t h, int64_t w, + concurrency::ThreadPool* thread_pool) { const size_t n_count = narrow(n); const size_t c_count = narrow(c); const size_t hw = narrow(SafeInt(h) * w); for (size_t n_idx = 0; n_idx < n_count; ++n_idx) { const size_t n_src_offset = SafeInt(SafeInt(n_idx) * hw) * c_count; const size_t n_dst_offset = SafeInt(SafeInt(n_idx) * c_count) * hw; - for (size_t c_idx = 0; c_idx < c_count; ++c_idx) { - const size_t c_dst_offset = SafeInt(c_idx) * hw; - const T* src_ptr = src + n_src_offset + c_idx; - T* dst_ptr = dst + n_dst_offset + c_dst_offset; - for (size_t hw_idx = 0; hw_idx < hw; ++hw_idx) { - const size_t src_hw_offset = SafeInt(hw_idx) * c_count; - dst_ptr[hw_idx] = src_ptr[src_hw_offset]; - } - } + MlasTranspose(src + n_src_offset, dst + n_dst_offset, hw, c_count, thread_pool); } } template void ConvertNCHWToNHWC(const T* src, T* dst, - int64_t n, int64_t c, int64_t h, int64_t w) { + int64_t n, int64_t c, int64_t h, int64_t w, + concurrency::ThreadPool* thread_pool) { const size_t n_count = narrow(n); const size_t c_count = narrow(c); const size_t hw = narrow(SafeInt(h) * w); for (size_t n_idx = 0; n_idx < n_count; ++n_idx) { const size_t n_src_offset = SafeInt(SafeInt(n_idx) * c_count) * hw; const size_t n_dst_offset = SafeInt(SafeInt(n_idx) * hw) * c_count; - for (size_t hw_idx = 0; hw_idx < hw; ++hw_idx) { - const size_t hw_dst_offset = SafeInt(hw_idx) * c_count; - const T* src_ptr = src + n_src_offset + hw_idx; - T* dst_ptr = dst + n_dst_offset + hw_dst_offset; - for (size_t c_idx = 0; c_idx < c_count; ++c_idx) { - const size_t src_c_offset = SafeInt(c_idx) * hw; - dst_ptr[c_idx] = src_ptr[src_c_offset]; - } - } + MlasTranspose(src + n_src_offset, dst + n_dst_offset, c_count, hw, thread_pool); } } @@ -366,7 +352,7 @@ Status Conv::Compute(OpKernelContext* context) const { float* temp_input = static_cast(alloc->Alloc(sizeof(float) * input_elements)); input_temp = BufferUniquePtr(temp_input, BufferDeleter(alloc)); ConvertNHWCToNCHW(X->Data(), temp_input, - input_n, input_c, input_h, input_w); + input_n, input_c, input_h, input_w, thread_pool); input_compute = temp_input; } @@ -387,7 +373,7 @@ Status Conv::Compute(OpKernelContext* context) const { BufferUniquePtr sum_nchw_buffer(sum_nchw, BufferDeleter(alloc)); ConvertNHWCToNCHW(sum_manual_data, sum_nchw, - y_dims[0], y_dims[3], y_dims[1], y_dims[2]); + y_dims[0], y_dims[3], y_dims[1], y_dims[2], thread_pool); auto output_span = gsl::make_span(output_compute, static_cast(output_elements)); auto sum_span = gsl::make_span(sum_nchw, static_cast(output_elements)); @@ -403,7 +389,7 @@ Status Conv::Compute(OpKernelContext* context) const { ConvertNCHWToNHWC(output_compute, Ydata.data(), - y_dims[0], y_dims[3], y_dims[1], y_dims[2]); + y_dims[0], y_dims[3], y_dims[1], y_dims[2], thread_pool); } } else { const int64_t input_image_size = input_shape.Size(); From 01c976798654f8b87013019b4675db77c2fb02b2 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 09:47:39 +0100 Subject: [PATCH 112/140] Remove the filter for NhwcTransformer as it is no longer needed Signed-off-by: Orlaith Monahan --- .../test/optimizer/fuse_initializers_transformer_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index b238a0c849fea..17d73d521c4a2 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -493,11 +493,11 @@ TEST(TransformerTest, FuseFp16InitializersWithGraphOutputs) { so.graph_optimization_level = TransformerLevel::MaxLevel; // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; - // Disabling ConstantFolding and NhwcTransformer optimizer as it will remove the Cast node + // Disabling ConstantFolding as it will remove the Cast node // by folding it with Add node. This will not allow us to test the // scenario where Cast node is producing graph output and need to // kept untouched by FuseInitializersTransformer. - ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"ConstantFolding", "NhwcTransformer"})); + ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"ConstantFolding"})); ASSERT_STATUS_OK(session.Load(model_uri)); _graph_structure_at_load(session.GetGraph()); ASSERT_STATUS_OK(session.Initialize()); From f603ea310edf0a940bdcc467b6d871b8ac232170 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 09:58:58 +0100 Subject: [PATCH 113/140] Adding comments to fuse_initializers_transformer_test.cc to explain filtered NhwcTransformer Signed-off-by: Orlaith Monahan --- .../test/optimizer/fuse_initializers_transformer_test.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index 17d73d521c4a2..5b39c032353df 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -363,6 +363,8 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + // Keep this test focused on FuseInitializersTransformer/NCHWC behavior. NhwcTransformer is + // hardware/kernel dependent and can otherwise change the post-init node counts this test asserts on. ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); @@ -403,6 +405,8 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + // Keep this test focused on FuseInitializersTransformer/NCHWC behavior. NhwcTransformer is + // hardware/kernel dependent and can otherwise change the post-init node counts this test asserts on. ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); @@ -445,6 +449,8 @@ TEST(TransformerTest, FuseFp16InitializersWithFp32Node_with_graph_optimizations_ // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; + // Keep this test focused on FuseInitializersTransformer/NCHWC behavior. NhwcTransformer is + // hardware/kernel dependent and can otherwise change the post-init node counts this test asserts on. ASSERT_STATUS_OK(session.FilterEnabledOptimizers({"NhwcTransformer"})); ASSERT_STATUS_OK(session.Load(model_uri)); test_graph_structure_at_init(session.GetGraph()); From 070a4ef269568167411e6fedddc7c2142cb6f23d Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 10:43:36 +0100 Subject: [PATCH 114/140] Cleanup of com.microsoft.NhwcFusedConv to be available in minimal builds * Added to the serializer * Can always be handled even if not always supported * Regenerated kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes Signed-off-by: Orlaith Monahan --- .../framework/kernel_type_str_resolver.cc | 12 - .../kernel_type_str_resolver_utils.cc | 691 +++++++++--------- onnxruntime/core/session/inference_session.cc | 8 + .../kernel_type_str_resolver_utils_test.cc | 49 +- 4 files changed, 397 insertions(+), 363 deletions(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index f995caec22cf9..69a482ae05bdb 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -35,18 +35,6 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } } -#ifdef USE_KLEIDIAI - // KleidiAI specific block for NhwcFusedConvolutions - if (op_it == map.end() && op_id.domain == kMSDomain && op_id.op_type == "NhwcFusedConv") { - const auto fused_conv_op_id = OpIdentifier{std::string{kMSDomain}, "FusedConv", op_id.since_version}; - op_it = map.find(fused_conv_op_id); - if (op_it == map.end()) { - const auto conv_op_id = OpIdentifier{std::string{kOnnxDomain}, "Conv", op_id.since_version}; - op_it = map.find(conv_op_id); - } - } -#endif - return op_it; } diff --git a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc index 50722f930228c..94920f7fa4b01 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc @@ -66,368 +66,377 @@ Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrRe // clang-format off constexpr uint8_t kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes[] = { 0x10, 0x00, 0x00, 0x00, 0x6b, 0x74, 0x73, 0x72, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x7c, 0x15, 0x00, 0x00, - 0xf8, 0x0a, 0x00, 0x00, 0x64, 0x04, 0x00, 0x00, 0x44, 0x14, 0x00, 0x00, 0xc0, 0x09, 0x00, 0x00, - 0xbc, 0x14, 0x00, 0x00, 0x78, 0x0b, 0x00, 0x00, 0x08, 0x0c, 0x00, 0x00, 0xbc, 0x13, 0x00, 0x00, - 0x44, 0x08, 0x00, 0x00, 0x94, 0x0d, 0x00, 0x00, 0xd8, 0x0d, 0x00, 0x00, 0xa0, 0x07, 0x00, 0x00, - 0xe8, 0x06, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x00, 0x34, 0x0d, 0x00, 0x00, 0xdc, 0x07, 0x00, 0x00, - 0x70, 0x12, 0x00, 0x00, 0x88, 0x06, 0x00, 0x00, 0x50, 0x0e, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, - 0xa0, 0x0c, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x8c, 0x10, 0x00, 0x00, 0x38, 0x02, 0x00, 0x00, - 0x88, 0x0f, 0x00, 0x00, 0x3c, 0x0f, 0x00, 0x00, 0x64, 0x05, 0x00, 0x00, 0x84, 0x11, 0x00, 0x00, - 0xf4, 0x06, 0x00, 0x00, 0xf0, 0x08, 0x00, 0x00, 0x10, 0x0c, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, - 0x0c, 0x13, 0x00, 0x00, 0xc8, 0x0d, 0x00, 0x00, 0x44, 0x08, 0x00, 0x00, 0x20, 0x0a, 0x00, 0x00, - 0xd4, 0x11, 0x00, 0x00, 0x84, 0x08, 0x00, 0x00, 0xe8, 0x05, 0x00, 0x00, 0x8c, 0x15, 0x00, 0x00, - 0xd8, 0x0f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x68, 0x05, 0x00, 0x00, - 0x84, 0x0e, 0x00, 0x00, 0x8c, 0x04, 0x00, 0x00, 0x30, 0x04, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, - 0x44, 0x12, 0x00, 0x00, 0x74, 0xea, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, - 0xa0, 0xea, 0xff, 0xff, 0x54, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xf0, 0xea, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xbc, 0xea, 0xff, 0xff, - 0x54, 0x15, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xaa, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xa4, 0xea, 0xff, 0xff, - 0xe0, 0xea, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, - 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x08, 0xeb, 0xff, 0xff, 0x08, 0x15, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, + 0x88, 0x0a, 0x00, 0x00, 0x60, 0x15, 0x00, 0x00, 0x6c, 0x13, 0x00, 0x00, 0x64, 0x11, 0x00, 0x00, + 0x7c, 0x04, 0x00, 0x00, 0xe4, 0x0e, 0x00, 0x00, 0xdc, 0x13, 0x00, 0x00, 0xb0, 0x02, 0x00, 0x00, + 0x10, 0x0b, 0x00, 0x00, 0xf8, 0x14, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0xa0, 0x08, 0x00, 0x00, + 0xa0, 0x10, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0xe4, 0x04, 0x00, 0x00, + 0xb0, 0x0b, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x14, 0x0e, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00, + 0xe4, 0x02, 0x00, 0x00, 0x34, 0x0c, 0x00, 0x00, 0x18, 0x12, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, + 0x2c, 0x0f, 0x00, 0x00, 0x08, 0x05, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x48, 0x14, 0x00, 0x00, + 0x44, 0x05, 0x00, 0x00, 0x70, 0x15, 0x00, 0x00, 0xa4, 0x0f, 0x00, 0x00, 0xe8, 0x07, 0x00, 0x00, + 0x70, 0x09, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x9c, 0x10, 0x00, 0x00, 0xb0, 0x0b, 0x00, 0x00, + 0xd8, 0x13, 0x00, 0x00, 0x1c, 0x03, 0x00, 0x00, 0x84, 0x05, 0x00, 0x00, 0x08, 0x09, 0x00, 0x00, + 0x50, 0x0d, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x60, 0x12, 0x00, 0x00, 0x58, 0x11, 0x00, 0x00, + 0xd4, 0x0c, 0x00, 0x00, 0x64, 0x08, 0x00, 0x00, 0x4c, 0x0c, 0x00, 0x00, 0xdc, 0x0a, 0x00, 0x00, + 0x60, 0x06, 0x00, 0x00, 0x9c, 0x15, 0x00, 0x00, 0xfc, 0xe9, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x31, 0x00, 0x20, 0xea, 0xff, 0xff, + 0x34, 0x15, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0e, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x48, 0xea, 0xff, 0xff, + 0x44, 0xea, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, + 0x32, 0x34, 0x00, 0x00, 0x78, 0xea, 0xff, 0xff, 0xa4, 0x15, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0xea, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x94, 0xea, 0xff, 0xff, 0x50, 0x15, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xb0, 0xea, 0xff, 0xff, 0xac, 0xea, 0xff, 0xff, 0x40, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xf6, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xf0, 0xea, 0xff, 0xff, 0x2c, 0xeb, 0xff, 0xff, - 0xc8, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x7c, 0xeb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x48, 0xeb, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, - 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x7c, 0xeb, 0xff, 0xff, - 0x64, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x58, 0xeb, 0xff, 0xff, 0x94, 0xeb, 0xff, 0xff, 0xf0, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe4, 0xeb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xb0, 0xeb, 0xff, 0xff, 0x0c, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9e, 0xeb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x0c, 0xec, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xd8, 0xeb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, - 0x33, 0x00, 0x00, 0x00, 0x04, 0xec, 0xff, 0xff, 0xf0, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0xec, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0x20, 0xec, 0xff, 0xff, 0xf0, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, 0xec, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x08, 0xec, 0xff, 0xff, 0x44, 0xec, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, - 0x65, 0x61, 0x72, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x78, 0xec, 0xff, 0xff, 0x94, 0x12, 0x00, 0x00, + 0x9a, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x94, 0xea, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0xd4, 0xea, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x73, 0x65, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0xfc, 0xea, 0xff, 0xff, 0x58, 0x14, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x66, 0xec, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xd4, 0xec, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, - 0xa0, 0xec, 0xff, 0xff, 0x40, 0x12, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x7c, 0xec, 0xff, 0xff, 0xb8, 0xec, 0xff, 0xff, 0x04, 0x12, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xed, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0xd4, 0xec, 0xff, 0xff, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, - 0x74, 0x3a, 0x51, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x3a, 0x31, 0x00, - 0x20, 0xed, 0xff, 0xff, 0x9c, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x74, 0xed, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, - 0x7c, 0xed, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x48, 0xed, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x34, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xa0, 0xed, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x6c, 0xed, 0xff, 0xff, - 0x74, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xc0, 0xed, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x54, 0xed, 0xff, 0xff, - 0x90, 0xed, 0xff, 0xff, 0xd8, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xe0, 0xed, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xac, 0xed, 0xff, 0xff, - 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x77, 0x5f, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xee, 0xff, 0xff, - 0x04, 0x00, 0x00, 0x00, 0xd4, 0xed, 0xff, 0xff, 0xb0, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0xee, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, - 0xf0, 0xed, 0xff, 0xff, 0x1c, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xed, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x4c, 0xee, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x18, 0xee, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, - 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x39, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xee, 0xff, 0xff, - 0x94, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xa0, 0xee, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x34, 0xee, 0xff, 0xff, - 0x70, 0xee, 0xff, 0xff, 0x4c, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x5e, 0xee, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xcc, 0xee, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x98, 0xee, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x4e, 0x68, - 0x77, 0x63, 0x4d, 0x61, 0x78, 0x50, 0x6f, 0x6f, 0x6c, 0x3a, 0x31, 0x00, 0xcc, 0xee, 0xff, 0xff, - 0x44, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xba, 0xee, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xb4, 0xee, 0xff, 0xff, - 0xf0, 0xee, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, - 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, - 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x00, - 0x30, 0xef, 0xff, 0xff, 0xb0, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x84, 0xef, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, - 0x18, 0xef, 0xff, 0xff, 0x54, 0xef, 0xff, 0xff, 0x68, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0xef, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0xb0, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x7c, 0xef, 0xff, 0xff, + 0xea, 0xea, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x24, 0xeb, 0xff, 0xff, 0x20, 0xeb, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x48, 0xeb, 0xff, 0xff, 0xf8, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0xeb, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x70, 0xeb, 0xff, 0xff, 0x6c, 0xeb, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, + 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x30, 0x00, 0x00, 0x00, 0x00, + 0xa4, 0xeb, 0xff, 0xff, 0x60, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x8e, 0xeb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xeb, 0xff, 0xff, + 0x8c, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x9c, 0xeb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xdc, 0xeb, 0xff, 0xff, 0x78, 0x13, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xbc, 0xeb, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x04, 0xec, 0xff, 0xff, 0x00, 0xec, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x3a, + 0x31, 0x31, 0x00, 0x00, 0x28, 0xec, 0xff, 0xff, 0x38, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0xec, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x44, 0xec, 0xff, 0xff, 0x10, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0xec, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x6c, 0xec, 0xff, 0xff, 0x68, 0xec, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, + 0x31, 0x39, 0x00, 0x00, 0x98, 0xec, 0xff, 0xff, 0x84, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x86, 0xec, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x80, 0xec, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xc0, 0xec, 0xff, 0xff, + 0x24, 0x13, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xa0, 0xec, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xe8, 0xec, 0xff, 0xff, + 0xe4, 0xec, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x73, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, 0x0c, 0xed, 0xff, 0xff, 0x48, 0x12, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xfa, 0xec, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x34, 0xed, 0xff, 0xff, 0x30, 0xed, 0xff, 0xff, + 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, + 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, + 0x64, 0xed, 0xff, 0xff, 0xb0, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0xed, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x80, 0xed, 0xff, 0xff, + 0x9c, 0x12, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6e, 0xed, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x68, 0xed, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0xa8, 0xed, 0xff, 0xff, 0x3c, 0x12, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc4, 0xed, 0xff, 0xff, 0xc0, 0xed, 0xff, 0xff, + 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, + 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x34, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0xed, 0xff, 0xff, 0xf4, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xed, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x14, 0xee, 0xff, 0xff, 0xd0, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf4, 0xed, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0x3c, 0xee, 0xff, 0xff, 0x38, 0xee, 0xff, 0xff, 0xe4, 0x11, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14, 0xee, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x54, 0xee, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x3a, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xee, 0xff, 0xff, 0xc4, 0x0b, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6a, 0xee, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xa4, 0xee, 0xff, 0xff, 0xa0, 0xee, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x31, 0x00, - 0xa0, 0xef, 0xff, 0xff, 0x70, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x8e, 0xef, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x88, 0xef, 0xff, 0xff, 0xc4, 0xef, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, - 0xf0, 0xef, 0xff, 0xff, 0x20, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xef, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd8, 0xef, 0xff, 0xff, 0x14, 0xf0, 0xff, 0xff, 0xe0, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0x30, 0xf0, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, - 0x7a, 0x65, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x00, 0x58, 0xf0, 0xff, 0xff, 0xb8, 0x0f, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x00, 0x00, + 0xc4, 0xee, 0xff, 0xff, 0x90, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb2, 0xee, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xec, 0xee, 0xff, 0xff, 0xe8, 0xee, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x10, 0xef, 0xff, 0xff, + 0x6c, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xec, 0xee, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x2c, 0xef, 0xff, 0xff, 0x28, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x46, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x40, 0xf0, 0xff, 0xff, 0x7c, 0xf0, 0xff, 0xff, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x35, - 0x00, 0x00, 0x00, 0x00, 0xa4, 0xf0, 0xff, 0xff, 0xec, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x92, 0xf0, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x8c, 0xf0, 0xff, 0xff, 0xc8, 0xf0, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x36, 0x00, 0x00, 0x00, 0x00, - 0xf0, 0xf0, 0xff, 0xff, 0xa0, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd8, 0xf0, 0xff, 0xff, 0x14, 0xf1, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x3c, 0xf1, 0xff, 0xff, - 0xd4, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x2a, 0xf1, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x24, 0xf1, 0xff, 0xff, - 0x60, 0xf1, 0xff, 0xff, 0x94, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xb0, 0xf1, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x7c, 0xf1, 0xff, 0xff, + 0x1a, 0xef, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x54, 0xef, 0xff, 0xff, 0x50, 0xef, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x34, - 0x00, 0x00, 0x00, 0x00, 0xa4, 0xf1, 0xff, 0xff, 0xec, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x92, 0xf1, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x8c, 0xf1, 0xff, 0xff, 0xc8, 0xf1, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x78, 0xef, 0xff, 0xff, 0xdc, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x66, 0xef, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xa0, 0xef, 0xff, 0xff, 0x9c, 0xef, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, - 0xf0, 0xf1, 0xff, 0xff, 0xa0, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xf1, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd8, 0xf1, 0xff, 0xff, 0x14, 0xf2, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, - 0x3a, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x3c, 0xf2, 0xff, 0xff, - 0xa0, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x8c, 0xf2, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x58, 0xf2, 0xff, 0xff, 0xb8, 0x0d, 0x00, 0x00, + 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0x00, + 0xc4, 0xef, 0xff, 0xff, 0x90, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb2, 0xef, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xec, 0xef, 0xff, 0xff, 0xe8, 0xef, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, + 0x14, 0xf0, 0xff, 0xff, 0x68, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xf0, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x30, 0xf0, 0xff, 0xff, + 0x24, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x1e, 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x58, 0xf0, 0xff, 0xff, + 0x54, 0xf0, 0xff, 0xff, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0xd4, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x51, 0x4c, + 0x69, 0x6e, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x6e, 0x76, 0x3a, 0x31, 0x00, 0xa0, 0xf0, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x54, 0x34, 0x00, 0x00, 0x84, 0xf0, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, + 0xc4, 0xf0, 0xff, 0xff, 0x50, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xa0, 0xf0, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0xe0, 0xf0, 0xff, 0xff, + 0x6c, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xbc, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfc, 0xf0, 0xff, 0xff, 0xe8, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x46, 0xf2, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x40, 0xf2, 0xff, 0xff, 0x7c, 0xf2, 0xff, 0xff, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, - 0x31, 0x00, 0x00, 0x00, 0xa4, 0xf2, 0xff, 0xff, 0x6c, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x92, 0xf2, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x8c, 0xf2, 0xff, 0xff, 0xc8, 0xf2, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, - 0xf0, 0xf2, 0xff, 0xff, 0x20, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xde, 0xf2, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd8, 0xf2, 0xff, 0xff, 0x14, 0xf3, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xdc, 0xf0, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x24, 0xf1, 0xff, 0xff, 0x20, 0xf1, 0xff, 0xff, + 0xfc, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0xf1, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x08, 0xf1, 0xff, 0xff, + 0x03, 0x00, 0x00, 0x00, 0x48, 0xf1, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x77, 0x5f, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x00, 0x30, 0xf1, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x70, 0xf1, 0xff, 0xff, + 0x7c, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x5e, 0xf1, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x58, 0xf1, 0xff, 0xff, + 0x07, 0x00, 0x00, 0x00, 0x98, 0xf1, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x33, 0x00, 0x3c, 0xf3, 0xff, 0xff, - 0xb8, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x8c, 0xf3, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x58, 0xf3, 0xff, 0xff, 0xb8, 0x0c, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x35, 0x00, 0xc0, 0xf1, 0xff, 0xff, + 0xbc, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x9c, 0xf1, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xdc, 0xf1, 0xff, 0xff, 0x78, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x46, 0xf3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x40, 0xf3, 0xff, 0xff, 0x7c, 0xf3, 0xff, 0xff, - 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, - 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x33, - 0x00, 0x00, 0x00, 0x00, 0xb4, 0xf3, 0xff, 0xff, 0x58, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9e, 0xf3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xd0, 0xf3, 0xff, 0xff, 0x10, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0xf4, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, - 0xb8, 0xf3, 0xff, 0xff, 0xf4, 0xf3, 0xff, 0xff, 0xc8, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x44, 0xf4, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0x10, 0xf4, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x3a, 0x31, 0x39, 0x00, 0x00, 0x00, 0x00, 0x38, 0xf4, 0xff, 0xff, 0x58, 0x08, 0x00, 0x00, + 0xca, 0xf1, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x04, 0xf2, 0xff, 0xff, 0x00, 0xf2, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x28, 0xf2, 0xff, 0xff, 0x18, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16, 0xf2, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x50, 0xf2, 0xff, 0xff, 0x4c, 0xf2, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, + 0x74, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, + 0x61, 0x72, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xf2, 0xff, 0xff, 0x90, 0x0d, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x7a, 0xf2, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x74, 0xf2, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xb4, 0xf2, 0xff, 0xff, 0x30, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xf2, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0xdc, 0xf2, 0xff, 0xff, 0xd8, 0xf2, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, + 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x31, 0x00, 0x00, 0x00, 0x00, 0xf3, 0xff, 0xff, + 0x54, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xf2, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x28, 0xf3, 0xff, 0xff, + 0x24, 0xf3, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, + 0x73, 0x65, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xf3, 0xff, 0xff, 0x08, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x26, 0xf4, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x20, 0xf4, 0xff, 0xff, 0x5c, 0xf4, 0xff, 0xff, + 0x3a, 0xf3, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x74, 0xf3, 0xff, 0xff, 0x70, 0xf3, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, - 0x33, 0x00, 0x00, 0x00, 0x84, 0xf4, 0xff, 0xff, 0x8c, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x72, 0xf4, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x6c, 0xf4, 0xff, 0xff, 0xa8, 0xf4, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x98, 0xf3, 0xff, 0xff, 0xa8, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x86, 0xf3, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xc0, 0xf3, 0xff, 0xff, 0xbc, 0xf3, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0x00, - 0xe0, 0xf4, 0xff, 0xff, 0xb0, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xca, 0xf4, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xfc, 0xf4, 0xff, 0xff, - 0x14, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x50, 0xf5, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xe4, 0xf4, 0xff, 0xff, - 0x20, 0xf5, 0xff, 0xff, 0x48, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x70, 0xf5, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x3c, 0xf5, 0xff, 0xff, - 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, - 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x35, - 0x00, 0x00, 0x00, 0x00, 0x74, 0xf5, 0xff, 0xff, 0x6c, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc8, 0xf5, 0xff, 0xff, - 0x02, 0x00, 0x00, 0x00, 0x5c, 0xf5, 0xff, 0xff, 0x98, 0xf5, 0xff, 0xff, 0x74, 0x09, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x82, 0xf5, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0xb4, 0xf5, 0xff, 0xff, 0x08, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0xf6, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xd0, 0xf5, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3a, 0x47, 0x61, 0x74, - 0x68, 0x65, 0x72, 0x3a, 0x31, 0x00, 0x00, 0x00, 0xf8, 0xf5, 0xff, 0xff, 0xe4, 0x07, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0xf6, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x14, 0xf6, 0xff, 0xff, 0xfc, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0xf6, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0xfc, 0xf5, 0xff, 0xff, 0x38, 0xf6, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x34, 0x00, - 0x60, 0xf6, 0xff, 0xff, 0x94, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xb0, 0xf6, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x7c, 0xf6, 0xff, 0xff, - 0x94, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6a, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x64, 0xf6, 0xff, 0xff, - 0xa0, 0xf6, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, - 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x39, 0x00, 0x00, - 0xd0, 0xf6, 0xff, 0xff, 0x10, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x24, 0xf7, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xb8, 0xf6, 0xff, 0xff, 0xf4, 0xf6, 0xff, 0xff, 0xc8, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xf6, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x50, 0xf7, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x1c, 0xf7, 0xff, 0xff, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x44, 0xf7, 0xff, 0xff, 0x4c, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0xf7, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x2c, 0xf7, 0xff, 0xff, 0x68, 0xf7, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x00, 0x8c, 0xf7, 0xff, 0xff, - 0x84, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x7a, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x74, 0xf7, 0xff, 0xff, - 0xb0, 0xf7, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xf4, 0xf3, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0xe6, 0xf3, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x18, 0xf4, 0xff, 0xff, 0x3c, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xf3, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x40, 0xf4, 0xff, 0xff, 0x3c, 0xf4, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x78, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x24, 0xf4, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x64, 0xf4, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x47, 0x61, 0x74, + 0x68, 0x65, 0x72, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x8c, 0xf4, 0xff, 0xff, 0xd4, 0x08, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, 0xf4, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0xa8, 0xf4, 0xff, 0xff, 0xac, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x96, 0xf4, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xd0, 0xf4, 0xff, 0xff, 0xcc, 0xf4, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x4e, 0x68, + 0x77, 0x63, 0x4d, 0x61, 0x78, 0x50, 0x6f, 0x6f, 0x6c, 0x3a, 0x31, 0x00, 0x00, 0xf5, 0xff, 0xff, + 0x54, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xf4, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x28, 0xf5, 0xff, 0xff, + 0x24, 0xf5, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0x00, 0xd8, 0xf7, 0xff, 0xff, 0x38, 0x08, 0x00, 0x00, + 0x79, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xf5, 0xff, 0xff, 0xf4, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xc6, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xc0, 0xf7, 0xff, 0xff, 0xfc, 0xf7, 0xff, 0xff, + 0x3a, 0xf5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x74, 0xf5, 0xff, 0xff, 0x70, 0xf5, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x31, - 0x33, 0x00, 0x00, 0x00, 0x24, 0xf8, 0xff, 0xff, 0xec, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x12, 0xf8, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x0c, 0xf8, 0xff, 0xff, 0x48, 0xf8, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, - 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x30, 0x00, 0x00, 0x7c, 0xf8, 0xff, 0xff, - 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x79, 0x5f, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd8, 0xf8, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0xa4, 0xf8, 0xff, 0xff, 0x18, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x92, 0xf8, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x00, 0xf9, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xcc, 0xf8, 0xff, 0xff, - 0x14, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xa8, 0xf8, 0xff, 0xff, 0xe4, 0xf8, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, - 0x10, 0xf9, 0xff, 0xff, 0x00, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xfe, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0xf8, 0xf8, 0xff, 0xff, 0x34, 0xf9, 0xff, 0xff, 0xc0, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x84, 0xf9, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0x50, 0xf9, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, - 0x3a, 0x31, 0x00, 0x00, 0x74, 0xf9, 0xff, 0xff, 0x9c, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x62, 0xf9, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x5c, 0xf9, 0xff, 0xff, 0x98, 0xf9, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, - 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x35, 0x00, 0x00, 0xcc, 0xf9, 0xff, 0xff, - 0x14, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xa8, 0xf9, 0xff, 0xff, 0xe4, 0xf9, 0xff, 0xff, 0x28, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd2, 0xf9, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x40, 0xfa, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x0c, 0xfa, 0xff, 0xff, - 0xb0, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x5c, 0xfa, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x28, 0xfa, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, - 0x33, 0x00, 0x00, 0x00, 0x54, 0xfa, 0xff, 0xff, 0xbc, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x3c, 0xfa, 0xff, 0xff, 0x78, 0xfa, 0xff, 0xff, 0x7c, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc8, 0xfa, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x94, 0xfa, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, + 0x33, 0x00, 0x00, 0x00, 0x98, 0xf5, 0xff, 0xff, 0xbc, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x86, 0xf5, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xc0, 0xf5, 0xff, 0xff, 0xbc, 0xf5, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, - 0x65, 0x61, 0x72, 0x3a, 0x32, 0x33, 0x00, 0x00, 0xc8, 0xfa, 0xff, 0xff, 0xf4, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0xfb, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0xe4, 0xfa, 0xff, 0xff, 0x28, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd2, 0xfa, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x40, 0xfb, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x0c, 0xfb, 0xff, 0xff, - 0xd4, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xe8, 0xfa, 0xff, 0xff, 0x24, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, - 0x32, 0x31, 0x00, 0x00, 0x54, 0xfb, 0xff, 0xff, 0x68, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0xfb, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0xb0, 0xfb, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x7c, 0xfb, 0xff, 0xff, - 0x64, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0xd0, 0xfb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x64, 0xfb, 0xff, 0xff, - 0xa0, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, - 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x33, 0x00, 0xc8, 0xfb, 0xff, 0xff, 0x48, 0x04, 0x00, 0x00, + 0x65, 0x61, 0x72, 0x3a, 0x32, 0x31, 0x00, 0x00, 0xec, 0xf5, 0xff, 0xff, 0xf8, 0x09, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xcc, 0xf5, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x14, 0xf6, 0xff, 0xff, 0x10, 0xf6, 0xff, 0xff, + 0x0c, 0x0a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xfe, 0xf5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xf8, 0xf5, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x38, 0xf6, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, + 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x4e, 0x68, 0x77, 0x63, 0x46, 0x75, + 0x73, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x76, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x70, 0xf6, 0xff, 0xff, + 0xe4, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6a, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x64, 0xf6, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x6c, 0xf6, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x74, 0xf6, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xbc, 0xf6, 0xff, 0xff, 0xb8, 0xf6, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, + 0xe4, 0xf6, 0xff, 0xff, 0x98, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xc0, 0xf6, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf7, 0xff, 0xff, + 0x54, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xee, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x28, 0xf7, 0xff, 0xff, + 0x24, 0xf7, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, + 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x33, 0x00, 0x00, 0x00, 0x50, 0xf7, 0xff, 0xff, + 0x2c, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x2c, 0xf7, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x6c, 0xf7, 0xff, 0xff, 0xe8, 0x07, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x5a, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x94, 0xf7, 0xff, 0xff, 0x90, 0xf7, 0xff, 0xff, + 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, + 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x30, 0x00, 0x00, + 0xc4, 0xf7, 0xff, 0xff, 0x58, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb2, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xac, 0xf7, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xec, 0xf7, 0xff, 0xff, 0xf8, 0x07, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0xf8, 0xff, 0xff, + 0x04, 0xf8, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x79, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, + 0xec, 0xf7, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x2c, 0xf8, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, + 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x64, 0xf8, 0xff, 0xff, 0xb8, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0xf8, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x80, 0xf8, 0xff, 0xff, + 0x6c, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x6a, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x9c, 0xf8, 0xff, 0xff, 0x48, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xb6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xb0, 0xfb, 0xff, 0xff, 0xec, 0xfb, 0xff, 0xff, - 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x61, 0x78, 0x65, 0x73, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0xfc, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x14, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x7c, 0xf8, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xc4, 0xf8, 0xff, 0xff, 0xc0, 0xf8, 0xff, 0xff, + 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3a, 0x51, 0x75, 0x61, + 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x35, 0x00, 0x00, + 0xf4, 0xf8, 0xff, 0xff, 0xf8, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xdc, 0xf8, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x1c, 0xf9, 0xff, 0xff, 0xc8, 0x06, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x38, 0xf9, 0xff, 0xff, + 0x34, 0xf9, 0xff, 0xff, 0xe8, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x10, 0xf9, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x50, 0xf9, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, + 0x3a, 0x32, 0x34, 0x00, 0x78, 0xf9, 0xff, 0xff, 0x04, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0xf9, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x94, 0xf9, 0xff, 0xff, 0xc0, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x82, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xbc, 0xf9, 0xff, 0xff, 0xb8, 0xf9, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x32, 0x35, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf9, 0xff, 0xff, + 0x60, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xce, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x08, 0xfa, 0xff, 0xff, + 0x04, 0xfa, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x3a, 0x31, 0x36, 0x00, 0x00, 0x00, 0x00, 0x2c, 0xfa, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x22, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x5c, 0xfa, 0xff, 0xff, 0x58, 0xfa, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, 0x3c, 0xfc, 0xff, 0xff, + 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, 0x80, 0xfa, 0xff, 0xff, + 0xd4, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6e, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xa8, 0xfa, 0xff, 0xff, + 0xa4, 0xfa, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, + 0x72, 0x3a, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xfa, 0xff, 0xff, 0x10, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc6, 0xfa, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xf8, 0xfa, 0xff, 0xff, 0xec, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd8, 0xfa, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x20, 0xfb, 0xff, 0xff, 0x1c, 0xfb, 0xff, 0xff, 0x00, 0x05, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xfa, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x38, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, + 0x64, 0xfb, 0xff, 0xff, 0x18, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0xfb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x80, 0xfb, 0xff, 0xff, 0xd4, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x2a, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x24, 0xfc, 0xff, 0xff, - 0x60, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, 0x00, 0x88, 0xfc, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7e, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x78, 0xfc, 0xff, 0xff, 0xb4, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, - 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x51, 0x75, - 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x00, 0x00, - 0xf0, 0xfc, 0xff, 0xff, 0xf0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x44, 0xfd, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xd8, 0xfc, 0xff, 0xff, 0x14, 0xfd, 0xff, 0xff, 0xa8, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0xfd, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x70, 0xfd, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x3c, 0xfd, 0xff, 0xff, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x64, 0xfd, 0xff, 0xff, 0xac, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x52, 0xfd, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x4c, 0xfd, 0xff, 0xff, 0x88, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x3a, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x3a, 0x31, 0x31, 0x00, 0x00, - 0xb0, 0xfd, 0xff, 0xff, 0x60, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x9e, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x98, 0xfd, 0xff, 0xff, 0xd4, 0xfd, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x54, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x30, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfc, 0xfd, 0xff, 0xff, - 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, - 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, 0x00, - 0x30, 0xfe, 0xff, 0xff, 0x8c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x8c, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x58, 0xfe, 0xff, 0xff, 0x88, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6e, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xa8, 0xfb, 0xff, 0xff, + 0xa4, 0xfb, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, + 0x32, 0x33, 0x00, 0x00, 0xd8, 0xfb, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x54, 0x33, 0x00, 0x00, 0xce, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xc8, 0xfb, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x08, 0xfc, 0xff, 0xff, 0x14, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe4, 0xfb, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x24, 0xfc, 0xff, 0xff, 0xc0, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x40, 0xfc, 0xff, 0xff, 0x3c, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, + 0x33, 0x00, 0x00, 0x00, 0x68, 0xfc, 0xff, 0xff, 0x14, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x44, 0xfc, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x84, 0xfc, 0xff, 0xff, 0xd0, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x72, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xac, 0xfc, 0xff, 0xff, 0xa8, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, + 0x72, 0x3a, 0x32, 0x31, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xfc, 0xff, 0xff, 0x40, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xac, 0xfe, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x40, 0xfe, 0xff, 0xff, 0x7c, 0xfe, 0xff, 0xff, - 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, - 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x32, 0x34, - 0x00, 0x00, 0x00, 0x00, 0xb4, 0xfe, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x54, 0x32, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x0c, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xd8, 0xfe, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x31, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, - 0xc8, 0xfe, 0xff, 0xff, 0x04, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x54, 0x33, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xf6, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x28, 0xff, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, - 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x60, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x78, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xbc, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x88, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x7a, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xac, 0xff, 0xff, 0xff, - 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0xd8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x3a, 0x55, 0x6e, 0x73, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x31, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xca, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xc4, 0xfc, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x04, 0xfd, 0xff, 0xff, 0xe0, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe4, 0xfc, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0x2c, 0xfd, 0xff, 0xff, 0x28, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x3a, 0x47, 0x61, 0x74, 0x68, 0x65, 0x72, 0x3a, 0x31, 0x00, 0x00, 0x00, 0x50, 0xfd, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x54, 0x69, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x38, 0xfd, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x78, 0xfd, 0xff, 0xff, 0xdc, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x66, 0xfd, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0xa0, 0xfd, 0xff, 0xff, 0x9c, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x3a, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x3a, 0x32, 0x34, 0x00, 0x00, 0x00, + 0xc4, 0xfd, 0xff, 0xff, 0x90, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb2, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0xec, 0xfd, 0xff, 0xff, 0xe8, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x31, 0x33, 0x00, 0x10, 0xfe, 0xff, 0xff, + 0x44, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xfe, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x38, 0xfe, 0xff, 0xff, + 0x34, 0xfe, 0xff, 0xff, 0x48, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x10, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x50, 0xfe, 0xff, 0xff, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x3a, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x31, 0x00, + 0x74, 0xfe, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x62, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, + 0x9c, 0xfe, 0xff, 0xff, 0x98, 0xfe, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x3a, 0x44, 0x65, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, + 0x72, 0x3a, 0x31, 0x39, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xfe, 0xff, 0xff, 0x50, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xba, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0xb4, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xf4, 0xfe, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd4, 0xfe, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0xff, 0xff, 0xff, 0x18, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x3a, 0x53, 0x71, 0x75, 0x65, 0x65, 0x7a, 0x65, 0x3a, 0x32, 0x33, 0x00, 0x40, 0xff, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x36, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x01, 0x70, 0xff, 0xff, 0xff, 0x6c, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x61, 0x78, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x54, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x94, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x2e, + 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x3a, 0x51, 0x75, 0x61, 0x6e, 0x74, 0x69, + 0x7a, 0x65, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x72, 0x3a, 0x31, 0x00, 0x00, 0xd0, 0xff, 0xff, 0xff, + 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x31, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x54, 0x32, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, }; // clang-format on diff --git a/onnxruntime/core/session/inference_session.cc b/onnxruntime/core/session/inference_session.cc index c4b10a212450f..aaf227aec4ea9 100644 --- a/onnxruntime/core/session/inference_session.cc +++ b/onnxruntime/core/session/inference_session.cc @@ -1052,6 +1052,14 @@ common::Status InferenceSession::SaveToOrtFormat(const std::filesystem::path& fi ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); } +#if !defined(ORT_MINIMAL_BUILD) || defined(ORT_EXTENDED_MINIMAL_BUILD) + // Persist resolver entries for ops that layout transformation may add so newly saved ORT models do not + // rely on load-time aliasing to resolve their kernel type strings. + ORT_RETURN_IF_ERROR( + kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver( + kernel_type_str_resolver)); +#endif + ORT_RETURN_IF_ERROR( kernel_type_str_resolver.SaveToOrtFormat(builder, fbs_kernel_type_str_resolver)); diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index e9cd79f53870d..4c3ede49c6125 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -12,8 +12,13 @@ #include "core/graph/constants.h" #include "core/graph/model.h" #include "core/graph/schema_registry.h" +#include "core/session/onnxruntime_session_options_config_keys.h" #include "test/test_environment.h" #include "test/util/include/asserts.h" +#include "test/util/include/inference_session_wrapper.h" + +#include +#include namespace onnxruntime::test { @@ -56,15 +61,11 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv } #if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) -TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { - SchemaRegistryManager schema_registry; - const auto* fused_conv_schema = schema_registry.GetSchema("FusedConv", 1, kMSDomain); - ASSERT_NE(fused_conv_schema, nullptr); - +TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { KernelTypeStrResolver resolver; - ASSERT_STATUS_OK(resolver.RegisterOpSchema(*fused_conv_schema)); + ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); - Model model("nhwc_fused_conv_resolver_test", false, DefaultLoggingManager().DefaultLogger()); + Model model("nhwc_fused_conv_layout_transform_resolver_test", false, DefaultLoggingManager().DefaultLogger()); auto& graph = model.MainGraph(); ONNX_NAMESPACE::TypeProto float_tensor; @@ -85,11 +86,37 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromFusedConvSchema) { ASSERT_FALSE(resolved_args.empty()); } -TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { +TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) { + const auto ort_model_path = std::filesystem::temp_directory_path() / "nhwc_fused_conv_resolver_test.ort"; + std::error_code remove_ec; + std::filesystem::remove(ort_model_path, remove_ec); + + SessionOptions so; + so.optimized_model_filepath = ort_model_path.native(); + ASSERT_STATUS_OK(so.config_options.AddConfigEntry(kOrtSessionOptionsConfigSaveModelFormat, "ORT")); + + InferenceSessionWrapper session{so, GetEnvironment()}; + ASSERT_STATUS_OK(session.Load(ORT_TSTR("testdata/mnist.onnx"))); + ASSERT_STATUS_OK(session.Initialize()); + + std::ifstream ort_model_stream(ort_model_path, std::ios::in | std::ios::binary); + ASSERT_TRUE(ort_model_stream.good()); + const std::string ort_model_data((std::istreambuf_iterator(ort_model_stream)), + std::istreambuf_iterator()); + ort_model_stream.close(); + ASSERT_FALSE(ort_model_data.empty()); + + flatbuffers::Verifier verifier(reinterpret_cast(ort_model_data.data()), ort_model_data.size()); + ASSERT_TRUE(fbs::VerifyInferenceSessionBuffer(verifier)); + + const auto* fbs_session = fbs::GetInferenceSession(ort_model_data.data()); + ASSERT_NE(fbs_session, nullptr); + ASSERT_NE(fbs_session->kernel_type_str_resolver(), nullptr); + KernelTypeStrResolver resolver; - ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); + ASSERT_STATUS_OK(resolver.LoadFromOrtFormat(*fbs_session->kernel_type_str_resolver())); - Model model("nhwc_fused_conv_layout_transform_resolver_test", false, DefaultLoggingManager().DefaultLogger()); + Model model("nhwc_fused_conv_saved_ort_model_resolver_test", false, DefaultLoggingManager().DefaultLogger()); auto& graph = model.MainGraph(); ONNX_NAMESPACE::TypeProto float_tensor; @@ -108,6 +135,8 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio gsl::span resolved_args; ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); ASSERT_FALSE(resolved_args.empty()); + + std::filesystem::remove(ort_model_path, remove_ec); } #endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) From d076d5bd4eccd37a596fbef4ca6ce0179dd3e036 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 15:30:04 +0100 Subject: [PATCH 115/140] Remove unneeded comment Signed-off-by: Orlaith Monahan --- onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc index 5a57a58360ddf..beb020eab6848 100644 --- a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc +++ b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc @@ -388,7 +388,6 @@ ONNX_MS_OPERATOR_SET_SCHEMA(NhwcFusedConv, 1, OpSchema() .SetDoc(R"DOC( NhwcFusedConv is a Conv operator with optional activation and add operators fused in. -Only has fp16 implementation as of 2023/04/15. )DOC") .Attr("auto_pad", "", AttributeProto::STRING, std::string("NOTSET")) .Attr("kernel_shape", "", AttributeProto::INTS, OPTIONAL_VALUE) From 458a31f495ef7f36d7c327d4b1d6d0b119d8feab Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 15:38:49 +0100 Subject: [PATCH 116/140] Added descriptions to the nhwc_schema_defs for the nhwcfusedconv operator Signed-off-by: Orlaith Monahan --- .../graph/contrib_ops/nhwc_schema_defs.cc | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc index beb020eab6848..cb015a3a3c500 100644 --- a/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc +++ b/onnxruntime/core/graph/contrib_ops/nhwc_schema_defs.cc @@ -397,11 +397,26 @@ NhwcFusedConv is a Conv operator with optional activation and add operators fuse .Attr("group", "", AttributeProto::INT, static_cast(1)) .Attr("activation", "", AttributeProto::STRING, OPTIONAL_VALUE) .Attr("activation_params", "", AttributeProto::FLOATS, OPTIONAL_VALUE) - .Input(0, "X", "", "T") - .Input(1, "W", "", "T") - .Input(2, "B", "", "T", OpSchema::Optional) - .Input(3, "Z", "Tensor to be added to the output, must be the same shape and format as the output tensor.", "T", OpSchema::Optional) - .Output(0, "Y", "", "T") + .Input(0, "X", + "Input activation tensor in channels-last layout. For 2D convolution this is " + "[N, H, W, C], where N is batch size, H/W are spatial dimensions, and C is " + "the number of input channels.", + "T") + .Input(1, "W", + "Convolution weight tensor in the standard ONNX Conv filter layout " + "[M, C/group, kH, kW], where M is the number of output channels.", + "T") + .Input(2, "B", + "Optional 1D bias tensor of shape [M].", + "T", OpSchema::Optional) + .Input(3, "Z", + "Optional residual/add tensor in the same channels-last layout and shape as " + "the output tensor Y. For 2D convolution this is [N, out_H, out_W, M].", + "T", OpSchema::Optional) + .Output(0, "Y", + "Output tensor in channels-last layout. For 2D convolution this is " + "[N, out_H, out_W, M], where M is the number of output channels.", + "T") .TypeConstraint("T", {"tensor(float16)", "tensor(float)"}, "Constrain input and output types to float tensors") .TypeAndShapeInferenceFunction([](InferenceContext& ctx) { ONNX_NAMESPACE::propagateElemTypeFromInputToOutput(ctx, 0, 0); From 559c121980d625d6f0352ee4a61332c71942db7d Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 15:53:08 +0100 Subject: [PATCH 117/140] Fix for CoPilot issue to prevent dividing by 0 when strides or dilations are 0 Signed-off-by: Orlaith Monahan --- onnxruntime/core/optimizer/nhwc_transformer.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/onnxruntime/core/optimizer/nhwc_transformer.cc b/onnxruntime/core/optimizer/nhwc_transformer.cc index 61d951f3f0625..6c0717865b135 100644 --- a/onnxruntime/core/optimizer/nhwc_transformer.cc +++ b/onnxruntime/core/optimizer/nhwc_transformer.cc @@ -45,13 +45,13 @@ bool TryGetDimValueAsSizeT(const ONNX_NAMESPACE::TensorShapeProto& shape, int in return true; } -bool TryReadPositiveOrZeroInts(const std::vector& values, std::array& out) { +bool TryReadPositiveInts(const std::vector& values, std::array& out) { if (values.size() != out.size()) { return false; } for (size_t i = 0; i < out.size(); ++i) { - if (values[i] < 0) { + if (values[i] <= 0) { return false; } @@ -107,6 +107,12 @@ bool TryComputeFloatNhwcPads(const api::NodeRef& node, const std::array& strides, const std::array& dilations, std::array& pads) { + for (size_t i = 0; i < 2; ++i) { + if (kernel_shape[i] == 0 || strides[i] == 0 || dilations[i] == 0) { + return false; + } + } + const auto auto_pad_value = node.GetAttributeString("auto_pad"); AutoPadType auto_pad = AutoPadType::NOTSET; if (!TryParseAutoPadType(auto_pad_value.value_or("NOTSET"), auto_pad)) { @@ -207,12 +213,12 @@ bool FloatNhwcWrapperFilter(const onnx_transpose_optimization::api::GraphRef& gr } const auto dilations_opt = node.GetAttributeInts("dilations"); - if (dilations_opt.has_value() && !TryReadPositiveOrZeroInts(*dilations_opt, dilations)) { + if (dilations_opt.has_value() && !TryReadPositiveInts(*dilations_opt, dilations)) { return false; } const auto strides_opt = node.GetAttributeInts("strides"); - if (strides_opt.has_value() && !TryReadPositiveOrZeroInts(*strides_opt, strides)) { + if (strides_opt.has_value() && !TryReadPositiveInts(*strides_opt, strides)) { return false; } From a7233867bb8c6fdb4882757c439ccffc14f9abca Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Wed, 29 Apr 2026 15:59:54 +0100 Subject: [PATCH 118/140] Moving the channels last check earlier in conv.cc so it returns sooner if needed Signed-off-by: Orlaith Monahan --- onnxruntime/core/providers/cpu/nn/conv.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 4b6e4178f20c8..6cd01a5fb23fb 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -201,6 +201,11 @@ Status Conv::Compute(OpKernelContext* context) const { TensorShapeVector kernel_shape; ORT_RETURN_IF_ERROR(conv_attrs_.ComputeKernelShape(W->Shape(), kernel_shape)); + const size_t kernel_rank = kernel_shape.size(); + + if (channels_last_) { + ORT_RETURN_IF_NOT(kernel_rank == 2, "Conv with channels_last layout currently supports 2D kernels."); + } ConvPadVector pads(conv_attrs_.pads); if (pads.empty()) { @@ -234,13 +239,8 @@ Status Conv::Compute(OpKernelContext* context) const { auto Xdata = X->DataAsSpan(); const auto* Bdata = B != nullptr ? B->Data() : nullptr; auto Ydata = Y->MutableDataAsSpan(); - const size_t kernel_rank = kernel_shape.size(); concurrency::ThreadPool* thread_pool = context->GetOperatorThreadPool(); - if (channels_last_) { - ORT_RETURN_IF_NOT(kernel_rank == 2, "Conv with channels_last layout currently supports 2D kernels."); - } - const bool wants_channels_last = channels_last_; const bool sum_present = Sum != nullptr; std::array input_shape_size_t{}; From c5c181520989e9822d562bce77105ff4ee6471c4 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 09:40:13 +0100 Subject: [PATCH 119/140] Update kernel_type_str_resolver.cc whitespace change --- onnxruntime/core/framework/kernel_type_str_resolver.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index 69a482ae05bdb..043f145dadd82 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -36,6 +36,7 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } return op_it; + } Status KernelTypeStrResolver::ResolveKernelTypeStr(const Node& node, std::string_view kernel_type_str, From fd2b5793cdd46dee1c82b74c0d36a073918c2631 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 09:43:03 +0100 Subject: [PATCH 120/140] Update kernel_type_str_resolver.cc --- onnxruntime/core/framework/kernel_type_str_resolver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver.cc b/onnxruntime/core/framework/kernel_type_str_resolver.cc index 043f145dadd82..3142f94f289b3 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver.cc @@ -35,8 +35,8 @@ static OpKernelTypeStrMap::const_iterator LookUpOpId(const OpIdentifier& op_id, } } } - return op_it; + return op_it; } Status KernelTypeStrResolver::ResolveKernelTypeStr(const Node& node, std::string_view kernel_type_str, From a15a8da2a5e81414a599607ff1171a988d5466c9 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 10:03:51 +0100 Subject: [PATCH 121/140] Update fuse_initializers_transformer_test.cc --- .../test/optimizer/fuse_initializers_transformer_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc index 5b39c032353df..b1997701e132f 100644 --- a/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc +++ b/onnxruntime/test/optimizer/fuse_initializers_transformer_test.cc @@ -499,7 +499,7 @@ TEST(TransformerTest, FuseFp16InitializersWithGraphOutputs) { so.graph_optimization_level = TransformerLevel::MaxLevel; // Create session and check graph before / after initiation InferenceSessionWrapper session{so, GetEnvironment()}; - // Disabling ConstantFolding as it will remove the Cast node + // Disabling ConstantFolding optimizer as it will remove the Cast node // by folding it with Add node. This will not allow us to test the // scenario where Cast node is producing graph output and need to // kept untouched by FuseInitializersTransformer. From 3e498904a89831e1360c06a9664cbc584e32884e Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 10:08:03 +0100 Subject: [PATCH 122/140] Remove the conditional ifdef from kernel_type_str_resolver_utils.cc * NhwcFusedConv should now be available in minimal builds as the resolver bytes contain it Signed-off-by: Orlaith Monahan --- .../kernel_type_str_resolver_utils.cc | 19 ------------------- .../kernel_type_str_resolver_utils.h | 7 ------- .../kernel_type_str_resolver_utils_test.cc | 4 ++-- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc index 94920f7fa4b01..27312a2f0ed37 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc +++ b/onnxruntime/core/framework/kernel_type_str_resolver_utils.cc @@ -9,8 +9,6 @@ #include "core/common/common.h" #include "core/flatbuffers/schema/ort.fbs.h" -#include "core/graph/schema_registry.h" -#include "core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h" namespace onnxruntime::kernel_type_str_resolver_utils { @@ -18,10 +16,6 @@ static constexpr auto* kStandaloneKernelTypeStrResolverFileIdentifier = "ktsr"; #if !defined(ORT_MINIMAL_BUILD) -gsl::span GetLayoutTransformationRequiredOpIdentifiers() { - return kLayoutTransformationPotentiallyAddedOps; -} - Status SaveKernelTypeStrResolverToBuffer(const KernelTypeStrResolver& kernel_type_str_resolver, flatbuffers::DetachedBuffer& buffer, gsl::span& buffer_span) { flatbuffers::FlatBufferBuilder builder; @@ -46,18 +40,6 @@ Status LoadKernelTypeStrResolverFromBuffer(KernelTypeStrResolver& kernel_type_st } Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrResolver& kernel_type_str_resolver) { -#if !defined(ORT_MINIMAL_BUILD) - const auto required_op_ids = GetLayoutTransformationRequiredOpIdentifiers(); - const auto schema_registry = SchemaRegistryManager{}; - for (const auto& op_id : required_op_ids) { - const auto* op_schema = schema_registry.GetSchema(std::string{op_id.op_type}, op_id.since_version, - std::string{op_id.domain}); - ORT_RETURN_IF(op_schema == nullptr, "Failed to get op schema."); - ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); - } - - return Status::OK(); -#else KernelTypeStrResolver resolver_with_required_ops{}; // to generate kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes, run the test: @@ -444,7 +426,6 @@ Status AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(KernelTypeStrRe kLayoutTransformationRequiredOpsKernelTypeStrResolverBytes)); kernel_type_str_resolver.Merge(std::move(resolver_with_required_ops)); return Status::OK(); -#endif } } // namespace onnxruntime::kernel_type_str_resolver_utils diff --git a/onnxruntime/core/framework/kernel_type_str_resolver_utils.h b/onnxruntime/core/framework/kernel_type_str_resolver_utils.h index 5daab7c1159be..488970890a1a0 100644 --- a/onnxruntime/core/framework/kernel_type_str_resolver_utils.h +++ b/onnxruntime/core/framework/kernel_type_str_resolver_utils.h @@ -8,8 +8,6 @@ #include #include "core/common/status.h" #include "core/framework/kernel_type_str_resolver.h" -#include "core/graph/op_identifier.h" - namespace flatbuffers { class DetachedBuffer; } @@ -18,11 +16,6 @@ namespace onnxruntime::kernel_type_str_resolver_utils { #if !defined(ORT_MINIMAL_BUILD) -/** - * Gets the ops that the layout transformation may potentially add. - */ -gsl::span GetLayoutTransformationRequiredOpIdentifiers(); - /** * Saves `kernel_type_str_resolver` to a byte buffer owned by `buffer` and referenced by `buffer_span`. */ diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 4c3ede49c6125..97cec9288d3cb 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -12,6 +12,7 @@ #include "core/graph/constants.h" #include "core/graph/model.h" #include "core/graph/schema_registry.h" +#include "core/optimizer/layout_transformation/layout_transformation_potentially_added_ops.h" #include "core/session/onnxruntime_session_options_config_keys.h" #include "test/test_environment.h" #include "test/util/include/asserts.h" @@ -23,9 +24,8 @@ namespace onnxruntime::test { static Status LoadLayoutTransformationRequiredOpsFromOpSchemas(KernelTypeStrResolver& kernel_type_str_resolver) { - const auto required_op_ids = kernel_type_str_resolver_utils::GetLayoutTransformationRequiredOpIdentifiers(); const auto schema_registry = SchemaRegistryManager{}; - for (const auto& op_id : required_op_ids) { + for (const auto& op_id : kLayoutTransformationPotentiallyAddedOps) { const auto* op_schema = schema_registry.GetSchema(std::string{op_id.op_type}, op_id.since_version, std::string{op_id.domain}); ORT_RETURN_IF(op_schema == nullptr, From 74449ff22b5818500fc845f3cede4ec1a2025fe7 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 11:13:59 +0100 Subject: [PATCH 123/140] Update the hashing in convolve_kleidi to reduce the aliasing risk * RHS hashing now uses the full tensor to ensure uniqueness * LHS no longer uses hashing as it's unnecessary Signed-off-by: Orlaith Monahan --- .../mlas/lib/kleidiai/convolve_kleidiai.cpp | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp index b4dcc65cd7eb4..57e2c61a96682 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp +++ b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp @@ -48,34 +48,41 @@ struct LhsCacheKey { size_t padding, sh, sw; size_t kh, kw; size_t dilationh, dilationw; - size_t data_hash; bool operator==(const LhsCacheKey& other) const { return ci == other.ci && ih == other.ih && iw == other.iw && padding == other.padding && sh == other.sh && sw == other.sw && kh == other.kh && kw == other.kw && - dilationh == other.dilationh && dilationw == other.dilationw && - data_hash == other.data_hash; + dilationh == other.dilationh && dilationw == other.dilationw; } }; -// Derived from 2^32 * (sqrt(5) - 1) / 2 ≈ 0.6180339887 (reciprocal of the golden ratio) -// Based on Knuth's multiplicative hashing method -constexpr size_t HASH_GOLDEN_RATIO_CONST = 0x9e3779b9; +constexpr size_t kFnvOffsetBasis = sizeof(size_t) == 8 + ? 14695981039346656037ull + : 2166136261u; +constexpr size_t kFnvPrime = sizeof(size_t) == 8 + ? 1099511628211ull + : 16777619u; -size_t HashTensorPrefix(const float* data, size_t element_count, size_t prefix_count = 16) { - if (data == nullptr || element_count == 0 || prefix_count == 0) { +size_t HashBytes(const void* data, size_t byte_count) { + if (data == nullptr || byte_count == 0) { return 0; } - const size_t count = std::min(element_count, prefix_count); - size_t h = 0; - for (size_t i = 0; i < count; ++i) { - h ^= std::hash()(data[i]) + HASH_GOLDEN_RATIO_CONST + (h << 6) + (h >> 2); + const auto* bytes = static_cast(data); + size_t h = kFnvOffsetBasis; + for (size_t i = 0; i < byte_count; ++i) { + h ^= static_cast(bytes[i]); + h *= kFnvPrime; } + return h; } +size_t HashTensorContents(const float* data, size_t element_count) { + return HashBytes(data, element_count * sizeof(float)); +} + namespace std { // Specialize hash type for cache keys and do it within namespace std. // Doing this allows standard containers like std::unordered_map to find @@ -99,17 +106,16 @@ namespace std { template<> struct hash { size_t operator()(const LhsCacheKey& k) const { - return k.data_hash ^ - (std::hash()(k.ci) << 1) ^ - (std::hash()(k.ih) << 2) ^ - (std::hash()(k.iw) << 3) ^ - (std::hash()(k.padding) << 4) ^ - (std::hash()(k.sh) << 5) ^ - (std::hash()(k.sw) << 6) ^ - (std::hash()(k.kh) << 7) ^ - (std::hash()(k.kw) << 8) ^ - (std::hash()(k.dilationh) << 9) ^ - (std::hash()(k.dilationw) << 10); + return std::hash()(k.ci) ^ + (std::hash()(k.ih) << 1) ^ + (std::hash()(k.iw) << 2) ^ + (std::hash()(k.padding) << 3) ^ + (std::hash()(k.sh) << 4) ^ + (std::hash()(k.sw) << 5) ^ + (std::hash()(k.kh) << 6) ^ + (std::hash()(k.kw) << 7) ^ + (std::hash()(k.dilationh) << 8) ^ + (std::hash()(k.dilationw) << 9); } }; @@ -345,9 +351,9 @@ static std::shared_ptr RhsPackWeightsBiasSme(const size_t co, const thread_local std::unordered_map> rhs_cache; // The packed RHS buffer includes both weights and bias, so both must participate in the cache key. - const size_t weights_hash = HashTensorPrefix(weights, co * ci * kh * kw); + const size_t weights_hash = HashTensorContents(weights, co * ci * kh * kw); const bool has_bias = bias != nullptr; - const size_t bias_hash = has_bias ? HashTensorPrefix(bias, co) : 0; + const size_t bias_hash = has_bias ? HashTensorContents(bias, co) : 0; RhsCacheKey key = { co, ci, kh, kw, dilationh, dilationw, has_bias, weights_hash, bias_hash }; auto found = rhs_cache.find(key); @@ -521,8 +527,7 @@ static std::unique_ptr LhsPackImageDataSme(const size_t ci, const s ci, ih, iw, padding, sh, sw, kh, kw, - 1, 1, - HashTensorPrefix(in, ci * ih * iw) + 1, 1 }; auto& lhs_ptrs_cache = lhs_ptrs_cache_by_pad[cur_pad_ptr]; From 95a4ca35a309514b36377e66e4f3954731bb7aaf Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 17:31:16 +0100 Subject: [PATCH 124/140] Remove an unnecessary copy in conv.cc Signed-off-by: Orlaith Monahan --- onnxruntime/core/providers/cpu/nn/conv.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index 6cd01a5fb23fb..e628c719a8c15 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -278,7 +278,6 @@ Status Conv::Compute(OpKernelContext* context) const { pre_sum_activation.ActivationKind = MlasIdentityActivation; } - std::vector sum_manual_buffer; const float* sum_manual_data = nullptr; float Beta = 0.0f; @@ -286,9 +285,7 @@ Status Conv::Compute(OpKernelContext* context) const { const auto& sum_shape = Sum->Shape(); ORT_RETURN_IF_NOT(Y->Shape() == sum_shape, "output and sum shape must match"); if (manual_sum) { - auto sum_span = Sum->DataAsSpan(); - sum_manual_buffer.assign(sum_span.begin(), sum_span.end()); - sum_manual_data = sum_manual_buffer.data(); + sum_manual_data = Sum->Data(); } else { auto sum_span = Sum->DataAsSpan(); if (Ydata.data() != sum_span.data()) { From f378f883e2c750c5bb0628985cf6cccadd2a905b Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 30 Apr 2026 19:58:26 +0100 Subject: [PATCH 125/140] Generated Docs update Signed-off-by: Orlaith Monahan --- docs/ContribOperators.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/ContribOperators.md b/docs/ContribOperators.md index 74e4379a0cbba..034ca6ce5f13b 100644 --- a/docs/ContribOperators.md +++ b/docs/ContribOperators.md @@ -3569,7 +3569,6 @@ This version of the operator has been available since version 1 of the 'com.micr ###
**com.microsoft.NhwcFusedConv** NhwcFusedConv is a Conv operator with optional activation and add operators fused in. - Only has fp16 implementation as of 2023/04/15. #### Version @@ -3600,26 +3599,26 @@ This version of the operator has been available since version 1 of the 'com.micr
X : T
-
+
Input activation tensor in channels-last layout. For 2D convolution this is [N, H, W, C], where N is batch size, H/W are spatial dimensions, and C is the number of input channels.
W : T
-
+
Convolution weight tensor in the standard ONNX Conv filter layout [M, C/group, kH, kW], where M is the number of output channels.
B (optional) : T
-
+
Optional 1D bias tensor of shape [M].
Z (optional) : T
-
Tensor to be added to the output, must be the same shape and format as the output tensor.
+
Optional residual/add tensor in the same channels-last layout and shape as the output tensor Y. For 2D convolution this is [N, out_H, out_W, M].
#### Outputs
Y : T
-
+
Output tensor in channels-last layout. For 2D convolution this is [N, out_H, out_W, M], where M is the number of output channels.
#### Type Constraints
-
T : tensor(float16)
+
T : tensor(float16), tensor(float)
Constrain input and output types to float tensors
From 76707a89709d41d98bf8130d4ca017d03d8ad40d Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Fri, 1 May 2026 10:34:03 +0100 Subject: [PATCH 126/140] Removing unneeded nullptr chwck in nchwc_transformer.cc Signed-off-by: Orlaith Monahan --- onnxruntime/core/optimizer/nchwc_transformer.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/onnxruntime/core/optimizer/nchwc_transformer.cc b/onnxruntime/core/optimizer/nchwc_transformer.cc index a971a058f43b7..719cf57ef8d81 100644 --- a/onnxruntime/core/optimizer/nchwc_transformer.cc +++ b/onnxruntime/core/optimizer/nchwc_transformer.cc @@ -502,10 +502,8 @@ void NchwcTransformerImpl::TransformConv(Node& node) { nchwc_node.MutableInputDefs()[2] = nchwc_conv_B_arg; } - if (nchwc_sum_input != nullptr) { - nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; - nchwc_sum_input->remaining_original_uses_--; - } + nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; + nchwc_sum_input->remaining_original_uses_--; NchwcArgument::Shape output_shape(output_defs[0]); From a1c17392f1098b90acbcfd8583d7099599a57700 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Fri, 1 May 2026 11:20:15 +0100 Subject: [PATCH 127/140] =?UTF-8?q?Patch=20to=20improve=20hashing=20in=20c?= =?UTF-8?q?onvolve=5Fkleidi=20*=20The=20change=20replaces=20the=20unsafe?= =?UTF-8?q?=20long-lived=20thread=5Flocal=20RHS=20cache=20with=20a=20kerne?= =?UTF-8?q?l-owned=20packed-filter=20cache=20for=20NHWC=20float=20conv=20w?= =?UTF-8?q?hen=20the=20filter=20and=20optional=20bias=20are=20constant=20i?= =?UTF-8?q?nitializers.=20The=20packed=20RHS=20is=20built=20once=20per=20k?= =?UTF-8?q?ernel=20instance=20and=20then=20reused=20safely=20for=20that=20?= =?UTF-8?q?kernel=E2=80=99s=20lifetime,=20which=20avoids=20pointer/hash=20?= =?UTF-8?q?aliasing=20issues=20without=20pulling=20in=20the=20larger=20ORT?= =?UTF-8?q?=20prepack=20machinery.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Orlaith Monahan --- onnxruntime/core/mlas/inc/mlas.h | 3 + .../mlas/lib/kleidiai/convolve_kleidiai.cpp | 168 +++++++----------- .../core/mlas/lib/kleidiai/mlasi_kleidiai.h | 22 ++- onnxruntime/core/providers/cpu/nn/conv.cc | 77 +++++++- onnxruntime/core/providers/cpu/nn/conv.h | 33 ++++ 5 files changed, 198 insertions(+), 105 deletions(-) diff --git a/onnxruntime/core/mlas/inc/mlas.h b/onnxruntime/core/mlas/inc/mlas.h index bd7f45a4e0622..8f85da5e91800 100644 --- a/onnxruntime/core/mlas/inc/mlas.h +++ b/onnxruntime/core/mlas/inc/mlas.h @@ -907,6 +907,9 @@ struct MLAS_CONV_PARAMETERS { MLAS_CONV_ALGORITHM Algorithm; ptrdiff_t ThreadCount; const MLAS_BACKEND_KERNEL_SELECTOR_CONFIG* BackendKernelSelectorConfig = nullptr; + const void* PackedFilter = nullptr; + size_t PackedFilterGroupStride = 0; + bool FilterIsPacked = false; union { struct { CBLAS_TRANSPOSE TransB; diff --git a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp index 57e2c61a96682..cca4f5a19c417 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp +++ b/onnxruntime/core/mlas/lib/kleidiai/convolve_kleidiai.cpp @@ -24,24 +24,6 @@ const KaiF32IMatmulKernel& imatmul_conv = GetKleidiAIF32IMatmulUKernel(); -// Right-hand-side (weights) cache key -struct RhsCacheKey { - size_t co, ci, kh, kw, dilationh, dilationw; - bool has_bias; - size_t weights_hash; - size_t bias_hash; - - bool operator==(const RhsCacheKey& other) const { - return co == other.co && ci == other.ci && - kh == other.kh && kw == other.kw && - dilationh == other.dilationh && dilationw == other.dilationw && - has_bias == other.has_bias && - weights_hash == other.weights_hash && - bias_hash == other.bias_hash; - } -}; - - // Left-hand-side (input indirection) cache key struct LhsCacheKey { size_t ci, ih, iw; @@ -57,52 +39,11 @@ struct LhsCacheKey { } }; -constexpr size_t kFnvOffsetBasis = sizeof(size_t) == 8 - ? 14695981039346656037ull - : 2166136261u; -constexpr size_t kFnvPrime = sizeof(size_t) == 8 - ? 1099511628211ull - : 16777619u; - -size_t HashBytes(const void* data, size_t byte_count) { - if (data == nullptr || byte_count == 0) { - return 0; - } - - const auto* bytes = static_cast(data); - size_t h = kFnvOffsetBasis; - for (size_t i = 0; i < byte_count; ++i) { - h ^= static_cast(bytes[i]); - h *= kFnvPrime; - } - - return h; -} - -size_t HashTensorContents(const float* data, size_t element_count) { - return HashBytes(data, element_count * sizeof(float)); -} - namespace std { // Specialize hash type for cache keys and do it within namespace std. // Doing this allows standard containers like std::unordered_map to find // the appropriate hash function via template specialization, as ADL // (argument-dependent lookup) does not apply to std::hash. - template<> - struct hash { - size_t operator()(const RhsCacheKey& k) const { - return k.weights_hash ^ - (std::hash()(k.co) << 1) ^ - (std::hash()(k.ci) << 2) ^ - (std::hash()(k.kh) << 3) ^ - (std::hash()(k.kw) << 4) ^ - (std::hash()(k.dilationh) << 5) ^ - (std::hash()(k.dilationw) << 6) ^ - (std::hash()(k.has_bias) << 7) ^ - (std::hash()(k.bias_hash) << 8); - } - }; - template<> struct hash { size_t operator()(const LhsCacheKey& k) const { @@ -341,56 +282,65 @@ static void MultiThreadedLHSPackSme(MLAS_THREADPOOL* ThreadPool, const size_t ci }); } -static std::shared_ptr RhsPackWeightsBiasSme(const size_t co, const size_t ci, - const size_t kh, const size_t kw, - const size_t dilationh, const size_t dilationw, - const float* weights, const float* bias, - MLAS_THREADPOOL* ThreadPool) -{ - // Cache of prepacked kai rhs weights and biases. thread_local to prevent interference from parallel sessions. - thread_local std::unordered_map> rhs_cache; - - // The packed RHS buffer includes both weights and bias, so both must participate in the cache key. - const size_t weights_hash = HashTensorContents(weights, co * ci * kh * kw); - const bool has_bias = bias != nullptr; - const size_t bias_hash = has_bias ? HashTensorContents(bias, co) : 0; - RhsCacheKey key = { co, ci, kh, kw, dilationh, dilationw, has_bias, weights_hash, bias_hash }; - - auto found = rhs_cache.find(key); - if (found != rhs_cache.end()) { - return found->second; - } else { - // prepare mlas filter weights for kai rhs packing - // dilated nhwc format - auto nhwc = NChwToNhwc(co, ci, kh, kw, weights, dilationh, dilationw, true, ThreadPool); - +size_t +MLASCALL +ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackWSize( + size_t FilterCount, + size_t InputChannels, + const int64_t* KernelShape, + const int64_t* DilationShape) { + const auto d_kh = ComputeKernelSize(static_cast(DilationShape[0]), static_cast(KernelShape[0])); + const auto d_kw = ComputeKernelSize(static_cast(DilationShape[1]), static_cast(KernelShape[1])); + return kai_get_rhs_packed_size_rhs_imatmul_pack_kxn_x32p2vlx1b_x32_x32_sme(FilterCount, d_kh * d_kw, + InputChannels); +} - //dilation, axis swap (n x k -> k x n) where n == co, k == d_kh x d_kw x ci - const auto d_kh = ComputeKernelSize(dilationh,kh); - const auto d_kw = ComputeKernelSize(dilationw,kw); +void +MLASCALL +ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackW( + size_t FilterCount, + size_t InputChannels, + const int64_t* KernelShape, + const int64_t* DilationShape, + size_t GroupCount, + const float* Filter, + const float* Bias, + void* PackedFilter, + size_t PackedFilterGroupStride, + MLAS_THREADPOOL* ThreadPool) { + const size_t kh = static_cast(KernelShape[0]); + const size_t kw = static_cast(KernelShape[1]); + const size_t dilationh = static_cast(DilationShape[0]); + const size_t dilationw = static_cast(DilationShape[1]); + const auto d_kh = ComputeKernelSize(dilationh, kh); + const auto d_kw = ComputeKernelSize(dilationw, kw); - //t_weights[d_kh][d_kw][ci][co] = nhwc[co][d_kh][d_kw][ci] - auto t_weights = Transpose4D({co,d_kh,d_kw,ci},&nhwc[0],{1,2,3,0}); + for (size_t group_idx = 0; group_idx < GroupCount; ++group_idx) { + const float* weights = Filter + group_idx * FilterCount * InputChannels * kh * kw; + const float* bias = Bias ? Bias + group_idx * FilterCount : nullptr; + auto* packed_group = reinterpret_cast(PackedFilter) + group_idx * PackedFilterGroupStride; - const auto packed_size = kai_get_rhs_packed_size_rhs_imatmul_pack_kxn_x32p2vlx1b_x32_x32_sme(co,d_kh*d_kw,ci); - auto packed = std::shared_ptr(new std::byte[packed_size], std::default_delete()); + // prepare mlas filter weights for kai rhs packing + // dilated nhwc format + auto nhwc = NChwToNhwc(FilterCount, InputChannels, kh, kw, weights, dilationh, dilationw, true, ThreadPool); - rhs_cache[key] = packed; + //t_weights[d_kh][d_kw][ci][co] = nhwc[co][d_kh][d_kw][ci] + auto t_weights = Transpose4D({FilterCount, d_kh, d_kw, InputChannels}, &nhwc[0], {1, 2, 3, 0}); std::vector bias_copy; if (bias) { - bias_copy.assign(bias, bias + co); + bias_copy.assign(bias, bias + FilterCount); } else { - bias_copy.resize(co, 0.0f); + bias_copy.resize(FilterCount, 0.0f); } KLEIDIAI_KERNEL_LOG("kai_run_rhs_imatmul_pack_kxn_x32p2vlx1b_x32_x32_sme" - << " N=" << co << " k_chunk_count=" << (d_kh*d_kw) << " k_chunk_length=" << ci << " rhs_stride_row=" << (co * sizeof(float))); + << " N=" << FilterCount << " k_chunk_count=" << (d_kh * d_kw) + << " k_chunk_length=" << InputChannels + << " rhs_stride_row=" << (FilterCount * sizeof(float))); kai_run_rhs_imatmul_pack_kxn_x32p2vlx1b_x32_x32_sme( - co, d_kh*d_kw, ci, co * sizeof(float), &t_weights[0], bias_copy.data(), packed.get() - ); - - return packed; + FilterCount, d_kh * d_kw, InputChannels, FilterCount * sizeof(float), &t_weights[0], bias_copy.data(), + packed_group); } } @@ -559,6 +509,8 @@ static void ConvolveSme(const size_t co, //channels out const size_t groups, //number of filter groups const float* weights, //kernel weights [co,ci,ih,iw] const float* bias, //kernel biases + const std::byte* packed_rhs, + const size_t packed_rhs_group_stride, const float* in, //in image data float* out, //out image data float* tmp_mlas_aligned, //intermediate buffer if we need to perform a transpose @@ -607,7 +559,20 @@ static void ConvolveSme(const size_t co, //channels out } auto lhs = LhsPackImageDataSme(ci, ih, iw, d_kh, d_kw, sh, sw, padding, in, input_is_channels_last, ThreadPool); - auto rhs = RhsPackWeightsBiasSme(co, ci, kh, kw, dilationh, dilationw, weights, bias, ThreadPool); + const std::byte* rhs_data = packed_rhs ? packed_rhs + g * packed_rhs_group_stride : nullptr; + std::unique_ptr rhs_storage; + if (rhs_data == nullptr) { + const std::array kernel_shape{static_cast(kh), static_cast(kw)}; + const std::array dilation_shape{static_cast(dilationh), static_cast(dilationw)}; + const size_t packed_size = + ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackWSize(co, ci, kernel_shape.data(), + dilation_shape.data()); + rhs_storage = std::make_unique(packed_size); + ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackW(co, ci, kernel_shape.data(), dilation_shape.data(), 1, + weights, bias, rhs_storage.get(), packed_size, + ThreadPool); + rhs_data = rhs_storage.get(); + } MlasTrySimpleParallel(ThreadPool, static_cast(dim[0] * dim[1] * dim[2]), [&](ptrdiff_t tid) { //compute B,M,N index from iteration index @@ -620,7 +585,7 @@ static void ConvolveSme(const size_t co, //channels out imatmul_conv.ukernel.get_rhs_packed_offset(NIdx * n_step, d_kh * d_kw, ci); auto BTile = reinterpret_cast( - reinterpret_cast(rhs.get()) + rhs_packed_offset + rhs_data + rhs_packed_offset ); // Get lhs tile, A @@ -769,7 +734,10 @@ ArmKleidiAI::MlasConv( Parameters->DilationShape[0], Parameters->DilationShape[1], // kernel dilation Parameters->Padding[0], // image padding Parameters->GroupCount, // filter groups - Filter, Bias, Input, Output, WorkingBuffer, Parameters->ChannelsLast, ThreadPool); + Filter, Bias, + reinterpret_cast(Parameters->PackedFilter), + Parameters->PackedFilterGroupStride, + Input, Output, WorkingBuffer, Parameters->ChannelsLast, ThreadPool); MlasActivation(Parameters->Activation, Output, nullptr, Parameters->FilterCount, Parameters->OutputSize, Parameters->OutputSize); diff --git a/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h b/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h index cba812a64822d..3c9f398ece887 100644 --- a/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h +++ b/onnxruntime/core/mlas/lib/kleidiai/mlasi_kleidiai.h @@ -201,15 +201,29 @@ MlasConv( MLAS_THREADPOOL* ThreadPool ); -#if defined(MLAS_ENABLE_TEST_HOOKS) size_t MLASCALL -MlasConvLhsCacheEntryCountForTest(); +MlasConvSymmetricChannelsLast2DFloatPackWSize( + size_t FilterCount, + size_t InputChannels, + const int64_t* KernelShape, + const int64_t* DilationShape + ); void MLASCALL -MlasConvClearLhsCacheForTest(); -#endif +MlasConvSymmetricChannelsLast2DFloatPackW( + size_t FilterCount, + size_t InputChannels, + const int64_t* KernelShape, + const int64_t* DilationShape, + size_t GroupCount, + const float* Filter, + const float* Bias, + void* PackedFilter, + size_t PackedFilterGroupStride, + MLAS_THREADPOOL* ThreadPool + ); } /*++ diff --git a/onnxruntime/core/providers/cpu/nn/conv.cc b/onnxruntime/core/providers/cpu/nn/conv.cc index e628c719a8c15..87ce1b05caae2 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.cc +++ b/onnxruntime/core/providers/cpu/nn/conv.cc @@ -23,6 +23,10 @@ #include "core/common/safeint.h" #include "core/util/math_cpuonly.h" +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) +#include "core/mlas/lib/kleidiai/mlasi_kleidiai.h" +#endif + namespace onnxruntime { using ConvPadVector = ConvAttributes::ConvPadVector; @@ -187,6 +191,58 @@ Status Conv::Compute(OpKernelContext* context) const { return Status::OK(); } +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) +Status Conv::EnsurePackedChannelsLastFilter(concurrency::ThreadPool* thread_pool, + size_t filter_count_per_group, + size_t input_channels_per_group, + const TensorShapeVector& kernel_shape, + const TensorShapeVector& dilations) const { + if (!can_cache_packed_filter_) { + return Status::OK(); + } + + std::call_once(packed_filter_once_, [&] { + packed_filter_status_ = Status::OK(); + + auto alloc = Info().GetAllocator(OrtMemTypeDefault); + if (alloc == nullptr) { + packed_filter_status_ = ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, + "Failed to get allocator for cached KleidiAI packed filter."); + return; + } + + packed_filter_group_stride_ = + ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackWSize(filter_count_per_group, + input_channels_per_group, + kernel_shape.data(), + dilations.data()); + if (packed_filter_group_stride_ == 0) { + packed_filter_status_ = ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, + "Failed to get KleidiAI packed filter size."); + return; + } + + const size_t packed_filter_size = + packed_filter_group_stride_ * onnxruntime::narrow(conv_attrs_.group); + packed_filter_ = IAllocator::MakeUniquePtr(alloc, packed_filter_size, true); + memset(packed_filter_.get(), 0, packed_filter_size); + + ArmKleidiAI::MlasConvSymmetricChannelsLast2DFloatPackW(filter_count_per_group, + input_channels_per_group, + kernel_shape.data(), + dilations.data(), + onnxruntime::narrow(conv_attrs_.group), + constant_filter_tensor_->Data(), + constant_bias_tensor_ ? constant_bias_tensor_->Data() : nullptr, + packed_filter_.get(), + packed_filter_group_stride_, + thread_pool); + }); + + return packed_filter_status_; +} +#endif + Status Conv::Compute(OpKernelContext* context) const { size_t num_inputs = OpKernel::Node().InputDefs().size(); const Tensor* X = context->Input(0); @@ -272,6 +328,17 @@ Status Conv::Compute(OpKernelContext* context) const { strides_size_t.data(), narrow(M / conv_attrs_.group), /*Beta*/ 0.0f); + +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) + if (nhwc_fastpath && can_cache_packed_filter_) { + ORT_RETURN_IF_ERROR(EnsurePackedChannelsLastFilter(thread_pool, + narrow(M / conv_attrs_.group), + narrow(C / conv_attrs_.group), + kernel_shape, + dilations)); + } +#endif + const bool manual_sum = wants_channels_last && !nhwc_fastpath && sum_present; MLAS_ACTIVATION pre_sum_activation = activation_; if (manual_sum) { @@ -296,7 +363,7 @@ Status Conv::Compute(OpKernelContext* context) const { } if (kernel_rank >= 1 && kernel_rank <= 3) { - MLAS_CONV_PARAMETERS Parameters; + MLAS_CONV_PARAMETERS Parameters{}; Parameters.BackendKernelSelectorConfig = &mlas_backend_kernel_selector_config_; size_t WorkingBufferSize; @@ -318,6 +385,14 @@ Status Conv::Compute(OpKernelContext* context) const { nhwc_fastpath ? 0.0f : Beta, thread_pool); +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) + if (nhwc_fastpath && packed_filter_ != nullptr) { + Parameters.FilterIsPacked = true; + Parameters.PackedFilter = packed_filter_.get(); + Parameters.PackedFilterGroupStride = packed_filter_group_stride_; + } +#endif + float* working_data = nullptr; BufferUniquePtr working_buffer; if (WorkingBufferSize > 0) { diff --git a/onnxruntime/core/providers/cpu/nn/conv.h b/onnxruntime/core/providers/cpu/nn/conv.h index 5633739b43e4b..1cbe417cdbd96 100644 --- a/onnxruntime/core/providers/cpu/nn/conv.h +++ b/onnxruntime/core/providers/cpu/nn/conv.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "core/framework/op_kernel.h" #include "core/providers/cpu/nn/conv_attributes.h" #include "core/providers/cpu/mlas_backend_kernel_selector_config_utils.h" @@ -28,6 +30,20 @@ class Conv : public OpKernel { Conv(const OpKernelInfo& info) : OpKernel(info), conv_attrs_(info), channels_last_(info.GetKernelDef().OpName() == "NhwcFusedConv") { activation_.ActivationKind = MlasIdentityActivation; SetupMlasBackendKernelSelectorFromConfigOptions(mlas_backend_kernel_selector_config_, info.GetConfigOptions()); + +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) + if (channels_last_) { + const auto& input_defs = info.node().InputDefs(); + const bool has_bias_input = input_defs.size() >= 3 && input_defs[2] != nullptr; + info.TryGetConstantInput(1, &constant_filter_tensor_); + if (has_bias_input) { + info.TryGetConstantInput(2, &constant_bias_tensor_); + } + + can_cache_packed_filter_ = + constant_filter_tensor_ != nullptr && (!has_bias_input || constant_bias_tensor_ != nullptr); + } +#endif } Status Compute(OpKernelContext* context) const override; @@ -39,6 +55,23 @@ class Conv : public OpKernel { ConvAttributes conv_attrs_; bool channels_last_{false}; + +#if defined(USE_KLEIDIAI) && defined(__aarch64__) && defined(__linux__) + private: + Status EnsurePackedChannelsLastFilter(concurrency::ThreadPool* thread_pool, + size_t filter_count_per_group, + size_t input_channels_per_group, + const TensorShapeVector& kernel_shape, + const TensorShapeVector& dilations) const; + + const Tensor* constant_filter_tensor_{nullptr}; + const Tensor* constant_bias_tensor_{nullptr}; + bool can_cache_packed_filter_{false}; + mutable std::once_flag packed_filter_once_; + mutable Status packed_filter_status_; + mutable IAllocatorUniquePtr packed_filter_; + mutable size_t packed_filter_group_stride_{0}; +#endif }; } // namespace onnxruntime From 915f147678254f017b862c1bc8d2589d51db1571 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 09:04:36 +0100 Subject: [PATCH 128/140] Revert "Removing unneeded nullptr chwck in nchwc_transformer.cc" This reverts commit 39edc59af79d75ad4786df22dd4680ec9b60fca9. --- onnxruntime/core/optimizer/nchwc_transformer.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/onnxruntime/core/optimizer/nchwc_transformer.cc b/onnxruntime/core/optimizer/nchwc_transformer.cc index 719cf57ef8d81..a971a058f43b7 100644 --- a/onnxruntime/core/optimizer/nchwc_transformer.cc +++ b/onnxruntime/core/optimizer/nchwc_transformer.cc @@ -502,8 +502,10 @@ void NchwcTransformerImpl::TransformConv(Node& node) { nchwc_node.MutableInputDefs()[2] = nchwc_conv_B_arg; } - nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; - nchwc_sum_input->remaining_original_uses_--; + if (nchwc_sum_input != nullptr) { + nchwc_node.MutableInputDefs()[3] = nchwc_sum_input->nchwc_arg_; + nchwc_sum_input->remaining_original_uses_--; + } NchwcArgument::Shape output_shape(output_defs[0]); From e5780f65c62991f7e627ed7469dbb2e8b189ea4c Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 10:28:32 +0100 Subject: [PATCH 129/140] Removing unnecessary add ops from inference_session.cc Signed-off-by: Orlaith Monahan --- onnxruntime/core/session/inference_session.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/onnxruntime/core/session/inference_session.cc b/onnxruntime/core/session/inference_session.cc index aaf227aec4ea9..c4b10a212450f 100644 --- a/onnxruntime/core/session/inference_session.cc +++ b/onnxruntime/core/session/inference_session.cc @@ -1052,14 +1052,6 @@ common::Status InferenceSession::SaveToOrtFormat(const std::filesystem::path& fi ORT_RETURN_IF_ERROR(kernel_type_str_resolver.RegisterOpSchema(*op_schema)); } -#if !defined(ORT_MINIMAL_BUILD) || defined(ORT_EXTENDED_MINIMAL_BUILD) - // Persist resolver entries for ops that layout transformation may add so newly saved ORT models do not - // rely on load-time aliasing to resolve their kernel type strings. - ORT_RETURN_IF_ERROR( - kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver( - kernel_type_str_resolver)); -#endif - ORT_RETURN_IF_ERROR( kernel_type_str_resolver.SaveToOrtFormat(builder, fbs_kernel_type_str_resolver)); From a6a867ca88832f2567ab263a0920e113ba01e033 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 11:31:06 +0100 Subject: [PATCH 130/140] Removing USE_KLEIDIAI from the ResolveNhwcFusedConvFromLayoutTransformationRequiredOps test Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 97cec9288d3cb..f455bd1a5163d 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -60,7 +60,7 @@ TEST(KernelTypeStrResolverUtilsTest, VerifyLayoutTransformationRequiredOpsResolv #endif // !defined(DISABLE_CONTRIB_OPS) } -#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) +#if !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformationRequiredOps) { KernelTypeStrResolver resolver; ASSERT_STATUS_OK(kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); @@ -138,7 +138,7 @@ TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) std::filesystem::remove(ort_model_path, remove_ec); } -#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) && defined(USE_KLEIDIAI) +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in From 4ba96ac45b6dfe962a8cf520634adb177c5bac8e Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 12:04:59 +0100 Subject: [PATCH 131/140] Change SavedOrtModelResolverContainsNhwcFusedConv to LoadedOrtModelResolverCanResolveNhwcFusedConv and have it check load time instead of the saved model Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index f455bd1a5163d..96f0ff182f2f5 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -86,7 +86,7 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio ASSERT_FALSE(resolved_args.empty()); } -TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) { +TEST(KernelTypeStrResolverUtilsTest, LoadedOrtModelResolverCanResolveNhwcFusedConv) { const auto ort_model_path = std::filesystem::temp_directory_path() / "nhwc_fused_conv_resolver_test.ort"; std::error_code remove_ec; std::filesystem::remove(ort_model_path, remove_ec); @@ -99,6 +99,10 @@ TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) ASSERT_STATUS_OK(session.Load(ORT_TSTR("testdata/mnist.onnx"))); ASSERT_STATUS_OK(session.Initialize()); + InferenceSessionWrapper loaded_session{SessionOptions{}, GetEnvironment()}; + ASSERT_STATUS_OK(loaded_session.Load(ort_model_path.native())); + ASSERT_STATUS_OK(loaded_session.Initialize()); + std::ifstream ort_model_stream(ort_model_path, std::ios::in | std::ios::binary); ASSERT_TRUE(ort_model_stream.good()); const std::string ort_model_data((std::istreambuf_iterator(ort_model_stream)), @@ -115,6 +119,8 @@ TEST(KernelTypeStrResolverUtilsTest, SavedOrtModelResolverContainsNhwcFusedConv) KernelTypeStrResolver resolver; ASSERT_STATUS_OK(resolver.LoadFromOrtFormat(*fbs_session->kernel_type_str_resolver())); + ASSERT_STATUS_OK( + kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); Model model("nhwc_fused_conv_saved_ort_model_resolver_test", false, DefaultLoggingManager().DefaultLogger()); auto& graph = model.MainGraph(); From ff052e94b9a05b16ca2a562e6a72643d12f4f771 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 5 May 2026 20:36:27 +0100 Subject: [PATCH 132/140] Remove LoadedOrtModelResolverCanResolveNhwcFusedConv as it's no longer needed Signed-off-by: Orlaith Monahan --- .../kernel_type_str_resolver_utils_test.cc | 60 ------------------- 1 file changed, 60 deletions(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 96f0ff182f2f5..306b4494a7821 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -86,66 +86,6 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio ASSERT_FALSE(resolved_args.empty()); } -TEST(KernelTypeStrResolverUtilsTest, LoadedOrtModelResolverCanResolveNhwcFusedConv) { - const auto ort_model_path = std::filesystem::temp_directory_path() / "nhwc_fused_conv_resolver_test.ort"; - std::error_code remove_ec; - std::filesystem::remove(ort_model_path, remove_ec); - - SessionOptions so; - so.optimized_model_filepath = ort_model_path.native(); - ASSERT_STATUS_OK(so.config_options.AddConfigEntry(kOrtSessionOptionsConfigSaveModelFormat, "ORT")); - - InferenceSessionWrapper session{so, GetEnvironment()}; - ASSERT_STATUS_OK(session.Load(ORT_TSTR("testdata/mnist.onnx"))); - ASSERT_STATUS_OK(session.Initialize()); - - InferenceSessionWrapper loaded_session{SessionOptions{}, GetEnvironment()}; - ASSERT_STATUS_OK(loaded_session.Load(ort_model_path.native())); - ASSERT_STATUS_OK(loaded_session.Initialize()); - - std::ifstream ort_model_stream(ort_model_path, std::ios::in | std::ios::binary); - ASSERT_TRUE(ort_model_stream.good()); - const std::string ort_model_data((std::istreambuf_iterator(ort_model_stream)), - std::istreambuf_iterator()); - ort_model_stream.close(); - ASSERT_FALSE(ort_model_data.empty()); - - flatbuffers::Verifier verifier(reinterpret_cast(ort_model_data.data()), ort_model_data.size()); - ASSERT_TRUE(fbs::VerifyInferenceSessionBuffer(verifier)); - - const auto* fbs_session = fbs::GetInferenceSession(ort_model_data.data()); - ASSERT_NE(fbs_session, nullptr); - ASSERT_NE(fbs_session->kernel_type_str_resolver(), nullptr); - - KernelTypeStrResolver resolver; - ASSERT_STATUS_OK(resolver.LoadFromOrtFormat(*fbs_session->kernel_type_str_resolver())); - ASSERT_STATUS_OK( - kernel_type_str_resolver_utils::AddLayoutTransformationRequiredOpsToKernelTypeStrResolver(resolver)); - - Model model("nhwc_fused_conv_saved_ort_model_resolver_test", false, DefaultLoggingManager().DefaultLogger()); - auto& graph = model.MainGraph(); - - ONNX_NAMESPACE::TypeProto float_tensor; - auto* tensor_type = float_tensor.mutable_tensor_type(); - tensor_type->set_elem_type(ONNX_NAMESPACE::TensorProto_DataType_FLOAT); - tensor_type->mutable_shape()->add_dim()->set_dim_value(1); - - auto& x = graph.GetOrCreateNodeArg("x", &float_tensor); - auto& w = graph.GetOrCreateNodeArg("w", &float_tensor); - auto& y = graph.GetOrCreateNodeArg("y", &float_tensor); - - auto& nhwc_fused_conv = graph.AddNode( - "nhwc_fused_conv", "NhwcFusedConv", "test node", {&x, &w}, {&y}, nullptr, kMSDomain); - nhwc_fused_conv.SetSinceVersion(1); - - gsl::span resolved_args; - ASSERT_STATUS_OK(resolver.ResolveKernelTypeStr(nhwc_fused_conv, "T", resolved_args)); - ASSERT_FALSE(resolved_args.empty()); - - std::filesystem::remove(ort_model_path, remove_ec); -} -#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) - // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in // onnxruntime/core/framework/kernel_type_str_resolver_utils.cc From ad66735d60b4dcc81cf1c2e749a61a73925d05a9 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 7 May 2026 11:00:23 +0100 Subject: [PATCH 133/140] Add a mising trailing endif to kernel_type_str_resolver.cc Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 306b4494a7821..92efdbd9fb972 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -117,4 +117,6 @@ TEST(KernelTypeStrResolverUtilsTest, DISABLED_PrintExpectedLayoutTransformationR #endif // defined(DISABLE_CONTRIB_OPS) } +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) + } // namespace onnxruntime::test From fe9dccb923d06544ac51cea83479d5ea6e73f09f Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 7 May 2026 16:40:38 +0100 Subject: [PATCH 134/140] Move endif in kernel_type_str_resolver_tests.cc to only exclude needed tests Signed-off-by: Orlaith Monahan --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 92efdbd9fb972..9c1f8dbfcb2a3 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -86,6 +86,8 @@ TEST(KernelTypeStrResolverUtilsTest, ResolveNhwcFusedConvFromLayoutTransformatio ASSERT_FALSE(resolved_args.empty()); } +#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) + // run this test manually to output a hard-coded byte array. // update AddLayoutTransformationRequiredOpsToKernelTypeStrResolver in // onnxruntime/core/framework/kernel_type_str_resolver_utils.cc @@ -117,6 +119,5 @@ TEST(KernelTypeStrResolverUtilsTest, DISABLED_PrintExpectedLayoutTransformationR #endif // defined(DISABLE_CONTRIB_OPS) } -#endif // !defined(ORT_MINIMAL_BUILD) && !defined(DISABLE_CONTRIB_OPS) } // namespace onnxruntime::test From f34f9e02355347b296259532ca832f4d29277812 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 7 May 2026 18:40:10 +0100 Subject: [PATCH 135/140] Update kernel_type_str_resolver_utils_test.cc Remove extraneous whitespace --- .../test/framework/kernel_type_str_resolver_utils_test.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc index 9c1f8dbfcb2a3..32720e42988f6 100644 --- a/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc +++ b/onnxruntime/test/framework/kernel_type_str_resolver_utils_test.cc @@ -119,5 +119,4 @@ TEST(KernelTypeStrResolverUtilsTest, DISABLED_PrintExpectedLayoutTransformationR #endif // defined(DISABLE_CONTRIB_OPS) } - } // namespace onnxruntime::test From f1b0a859fdc7d089d0eb87bf3954873cd9db8bb3 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Mon, 11 May 2026 10:57:59 +0100 Subject: [PATCH 136/140] Regen OperatorKernels.md Signed-off-by: Orlaith Monahan --- docs/OperatorKernels.md | 962 ---------------------------------------- 1 file changed, 962 deletions(-) diff --git a/docs/OperatorKernels.md b/docs/OperatorKernels.md index d90280a5a3357..8b05b23a1e1c1 100644 --- a/docs/OperatorKernels.md +++ b/docs/OperatorKernels.md @@ -13,8 +13,6 @@ The **OpSet Version** column uses the following notation: ## Execution Providers - [CPUExecutionProvider](#cpuexecutionprovider) -- [CUDAExecutionProvider](#cudaexecutionprovider) -- [DmlExecutionProvider](#dmlexecutionprovider) --------------- @@ -636,963 +634,3 @@ The **OpSet Version** column uses the following notation: |WordConvEmbedding|*in* Sequence:**T**
*in* W:**T1**
*in* B:**T1**
*in* C:**T1**
*out* Y:**T1**|1+|**T** = tensor(int32)
**T1** = tensor(float)| | | | | -|**Operator Domain:** *com.microsoft.nchwc*|||| -|AveragePool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|Conv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*in* Sum:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|GlobalAveragePool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|GlobalMaxPool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|MaxPool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|ReorderInput|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|ReorderOutput|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|Upsample|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -| | -| | - - - - -## Operators implemented by CUDAExecutionProvider - -| Op Name | Parameters | OpSet Version | Types Supported | -|---------|------------|---------------|-----------------| -|**Operator Domain:** *ai.onnx*|||| -|Abs|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Add|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Affine|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|And|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)
**T1** = tensor(bool)| -|ArgMax|*in* data:**T**
*out* reduced:**tensor(int64)**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||12|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 11]|**T** = tensor(double), tensor(float), tensor(float16)| -|ArgMin|*in* data:**T**
*out* reduced:**tensor(int64)**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||12|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 11]|**T** = tensor(double), tensor(float), tensor(float16)| -|Attention|*in* Q:**T1**
*in* K:**T1**
*in* V:**T2**
*in* attn_mask:**U**
*in* past_key:**T1**
*in* past_value:**T2**
*in* nonpad_kv_seqlen:**tensor(int64)**
*out* Y:**T1**
*out* present_key:**T1**
*out* present_value:**T2**
*out* qk_matmul_output:**T1**

or

*in* Q:**T1**
*in* K:**T1**
*in* V:**T2**
*in* attn_mask:**U**
*in* past_key:**T1**
*in* past_value:**T2**
*out* Y:**T1**
*out* present_key:**T1**
*out* present_value:**T2**
*out* qk_matmul_output:**T1**|24+|**T1** = tensor(bfloat16), tensor(float), tensor(float16)
**T2** = tensor(bfloat16), tensor(float), tensor(float16)
**U** = tensor(bfloat16), tensor(bool), tensor(float), tensor(float16)| -|||23|**T1** = tensor(bfloat16), tensor(float), tensor(float16)
**T2** = tensor(bfloat16), tensor(float), tensor(float16)
**U** = tensor(bfloat16), tensor(bool), tensor(float), tensor(float16)| -|AveragePool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[19, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 18]|**T** = tensor(double), tensor(float), tensor(float16)| -|||10|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 9]|**T** = tensor(double), tensor(float), tensor(float16)| -|BatchNormalization|*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* input_mean:**U**
*in* input_var:**U**
*out* Y:**T**
*out* running_mean:**U**
*out* running_var:**U**

or

*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* mean:**T**
*in* var:**T**
*out* Y:**T**
*out* mean:**T**
*out* var:**T**
*out* saved_mean:**T**
*out* saved_var:**T**

or

*in* X:**T**
*in* scale:**T1**
*in* B:**T1**
*in* input_mean:**T2**
*in* input_var:**T2**
*out* Y:**T**
*out* running_mean:**T2**
*out* running_var:**T2**|15+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(double), tensor(float), tensor(float16)| -|||14|**T** = tensor(double), tensor(float), tensor(float16)
**U** = tensor(double), tensor(float), tensor(float16)| -|||[9, 13]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|Cast|*in* input:**T1**
*out* output:**T2**|25+|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 18]|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[9, 12]|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[6, 8]|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float4e2m1), tensor(float8e4m3fn), tensor(float8e5m2), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Ceil|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Clip|*in* input:**T**
*in* min:**T**
*in* max:**T**
*out* output:**T**

or

*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int64), tensor(int8), tensor(uint64), tensor(uint8)| -|||12|**T** = tensor(double), tensor(float), tensor(float16), tensor(int64), tensor(int8), tensor(uint64), tensor(uint8)| -|||11|**T** = tensor(float)| -|||[6, 10]|**T** = tensor(float)| -|Compress|*in* input:**T**
*in* condition:**T1**
*out* output:**T**|11+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||[9, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|Concat|*in* inputs:**T**
*out* concat_result:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[4, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ConcatFromSequence|*in* input_sequence:**S**
*out* concat_result:**T**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|ConstantOfShape|*in* input:**T1**
*out* output:**T2**|25+|**T1** = tensor(int64)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**T1** = tensor(int64)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T1** = tensor(int64)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[9, 20]|**T1** = tensor(int64)
**T2** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Conv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[11, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|ConvTranspose|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|11+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|Cos|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(double), tensor(float), tensor(float16)| -|Crop|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|CumSum|*in* x:**T**
*in* axis:**T2**
*out* y:**T**|14+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T2** = tensor(int32), tensor(int64)| -|||[11, 13]|**T** = tensor(double), tensor(float), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T2** = tensor(int32), tensor(int64)| -|DeformConv|*in* X:**T**
*in* W:**T**
*in* offset:**T**
*in* B:**T**
*in* mask:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[19, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|DepthToSpace|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|DequantizeLinear|*in* x:**T**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T**
*out* y:**tensor(float)**

or

*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T2**

or

*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T3**|25+|**T1** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)
**T2** = tensor(float), tensor(float16)
**T3** = tensor(float), tensor(float16)| -|||[23, 24]|**T1** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)
**T2** = tensor(float), tensor(float16)
**T3** = tensor(float), tensor(float16)| -|||[21, 22]|**T1** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||[19, 20]|**T1** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||[13, 18]|**T** = tensor(int8), tensor(uint8)| -|||[10, 12]|**T** = tensor(int8), tensor(uint8)| -|Div|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Dropout|*in* data:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T2**

or

*in* data:**T**
*out* output:**T**
*out* mask:**T**

or

*in* data:**T**
*out* output:**T**
*out* mask:**T1**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||12|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||[10, 11]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bool)| -|||[7, 9]|**T** = tensor(double), tensor(float), tensor(float16)| -|DynamicSlice|*in* data:**T**
*in* starts:**Tind**
*in* ends:**Tind**
*in* axes:**Tind**
*out* output:**T**|1+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|Einsum|*in* Inputs:**T**
*out* Output:**T**|12+|**T** = tensor(double), tensor(float), tensor(float16)| -|Elu|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(double), tensor(float), tensor(float16)| -|Equal|*in* A:**T**
*in* B:**T**
*out* C:**T1**|19+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[13, 18]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[11, 12]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 10]|**T** = tensor(bool), tensor(int32), tensor(int64)| -|Erf|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[9, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Exp|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Expand|*in* input:**T**
*in* shape:**tensor(int64)**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[8, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|EyeLike|*in* input:**T1**
*out* output:**T2**|9+|**T1** = tensor(double), tensor(float), tensor(int32), tensor(int64), tensor(uint64)
**T2** = tensor(double), tensor(float), tensor(int32), tensor(int64), tensor(uint64)| -|Flatten|*in* input:**T**
*out* output:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[9, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 8]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Floor|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|GRU|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*out* Y:**T**
*out* Y_h:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[14, 21]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[7, 13]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|Gather|*in* data:**T**
*in* indices:**Tind**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[1, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|GatherElements|*in* data:**T**
*in* indices:**Tind**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|GatherND|*in* data:**T**
*in* indices:**tensor(int64)**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int64)
**indices** = tensor(int64)| -|||12|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int64)
**indices** = tensor(int64)| -|||11|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int64)
**indices** = tensor(int64)| -|Gelu|*in* X:**T**
*out* Y:**T**|20+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Gemm|*in* A:**T**
*in* B:**T**
*in* C:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[9, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|GlobalAveragePool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|GlobalMaxPool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|Greater|*in* A:**T**
*in* B:**T**
*out* C:**T1**|13+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[9, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|GreaterOrEqual|*in* A:**T**
*in* B:**T**
*out* C:**T1**|16+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[12, 15]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|GridSample|*in* X:**T1**
*in* grid:**T2**
*out* Y:**T1**|22+|**T1** = tensor(float)
**T2** = tensor(float)| -|||[20, 21]|**T1** = tensor(float)
**T2** = tensor(float)| -|||[16, 19]|**T1** = tensor(float)
**T2** = tensor(float)| -|HardSigmoid|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|HardSwish|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[14, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|Identity|*in* input:**T**
*out* output:**T**

or

*in* input:**V**
*out* output:**V**|25+|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[14, 18]|**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|If|*in* cond:**B**
*out* outputs:**V**|25+|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 18]|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**B** = tensor(bool)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 10]|**B** = tensor(bool)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ImageScaler|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|InstanceNormalization|*in* input:**T**
*in* scale:**T**
*in* B:**T**
*out* output:**T**|6+|**T** = tensor(double), tensor(float), tensor(float16)| -|IsInf|*in* X:**T1**
*out* Y:**T2**|20+|**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz)
**T2** = tensor(bool)| -|||[10, 19]|**T1** = tensor(double), tensor(float)
**T2** = tensor(bool)| -|IsNaN|*in* X:**T1**
*out* Y:**T2**|20+|**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz)
**T2** = tensor(bool)| -|||[13, 19]|**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||[9, 12]|**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|LRN|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|LSTM|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*in* initial_c:**T**
*in* P:**T**
*out* Y:**T**
*out* Y_h:**T**
*out* Y_c:**T**|14+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[7, 13]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|LayerNormalization|*in* X:**T**
*in* Scale:**T**
*in* B:**T**
*out* Y:**T**
*out* Mean:**U**
*out* InvStdDev:**U**

or

*in* X:**T**
*in* Scale:**V**
*in* B:**V**
*out* Y:**V**
*out* Mean:**U**
*out* InvStdDev:**U**|17+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**U** = tensor(float)| -|||[1, 16]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**U** = tensor(double), tensor(float)
**V** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|LeakyRelu|*in* X:**T**
*out* Y:**T**|16+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 15]|**T** = tensor(double), tensor(float), tensor(float16)| -|Less|*in* A:**T**
*in* B:**T**
*out* C:**T1**|13+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[9, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|LessOrEqual|*in* A:**T**
*in* B:**T**
*out* C:**T1**|16+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|||[12, 15]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T1** = tensor(bool)| -|Log|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|LogSoftmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|Loop|*in* M:**I**
*in* cond:**B**
*in* v_initial:**V**
*out* v_final_and_scan_outputs:**V**|25+|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 18]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 10]|**B** = tensor(bool)
**I** = tensor(int64)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|MatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[9, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|MatMulInteger|*in* A:**T1**
*in* B:**T2**
*in* a_zero_point:**T1**
*in* b_zero_point:**T2**
*out* Y:**T3**|10+|**T1** = tensor(int8)
**T2** = tensor(int8)
**T3** = tensor(int32)| -|Max|*in* data_0:**T**
*out* max:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||12|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[6, 11]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|MaxPool|*in* X:**T**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**
*out* Indices:**I**|12+|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||11|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16)| -|||10|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16)| -|||[8, 9]|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 7]|**T** = tensor(double), tensor(float), tensor(float16)| -|MemcpyFromHost|*in* X:**T**
*out* Y:**T**|1+|**T** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|MemcpyToHost|*in* X:**T**
*out* Y:**T**|1+|**T** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Min|*in* data_0:**T**
*out* min:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||12|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[6, 11]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Mod|*in* A:**T**
*in* B:**T**
*out* C:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[10, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Mul|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Neg|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|NonZero|*in* X:**T**
*out* Y:**tensor(int64)**|13+|**T** = tensor(bool), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint8)| -|||[9, 12]|**T** = tensor(bool), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint8)| -|Not|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(bool)| -|OneHot|*in* indices:**T1**
*in* depth:**T2**
*in* values:**T3**
*out* output:**T3**|11+|**T1** = tensor(int32), tensor(int64)
**T2** = tensor(int32), tensor(int64)
**T3** = tensor(float), tensor(float16), tensor(int64)| -|Or|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)
**T1** = tensor(bool)| -|PRelu|*in* X:**T**
*in* slope:**T**
*out* Y:**T**|16+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[9, 15]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|Pad|*in* data:**T**
*in* pads:**tensor(int64)**
*in* constant_value:**T**
*in* axes:**Tind**
*out* output:**T**

or

*in* data:**T**
*in* pads:**tensor(int64)**
*in* constant_value:**T**
*out* output:**T**

or

*in* data:**T**
*out* output:**T**|25+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||24|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||23|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||[21, 22]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||[19, 20]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||18|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||[13, 17]|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[2, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|ParametricSoftplus|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Pow|*in* X:**T**
*in* Y:**T**
*out* Z:**T**

or

*in* X:**T**
*in* Y:**T1**
*out* Z:**T**|15+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||[13, 14]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||12|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||[7, 11]|**T** = tensor(double), tensor(float), tensor(float16)| -|QuantizeLinear|*in* x:**T1**
*in* y_scale:**T1**
*in* y_zero_point:**T2**
*out* y:**T2**

or

*in* x:**T1**
*in* y_scale:**T2**
*in* y_zero_point:**T3**
*out* y:**T3**

or

*in* x:**T1**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T2**
*out* y:**T2**|25+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(float), tensor(float16)
**T3** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)| -|||[23, 24]|**T1** = tensor(float), tensor(float16)
**T2** = tensor(float), tensor(float16)
**T3** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)| -|||[21, 22]|**T1** = tensor(float), tensor(float16)
**T2** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)| -|||[19, 20]|**T1** = tensor(float), tensor(float16)
**T2** = tensor(float8e4m3fn), tensor(float8e5m2), tensor(int8), tensor(uint8)| -|||[13, 18]|**T1** = tensor(float)
**T2** = tensor(int8), tensor(uint8)| -|||[10, 12]|**T1** = tensor(float)
**T2** = tensor(int8), tensor(uint8)| -|RMSNormalization|*in* X:**T**
*in* scale:**V**
*out* Y:**V**|23+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**V** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|RNN|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*out* Y:**T**
*out* Y_h:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[14, 21]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|||[7, 13]|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(int32)| -|RandomNormal|*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|RandomNormalLike|*in* input:**T1**
*out* output:**T2**|1+|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(double), tensor(float), tensor(float16)| -|RandomUniform|*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|RandomUniformLike|*in* input:**T1**
*out* output:**T2**|1+|**T1** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(double), tensor(float), tensor(float16)| -|Range|*in* start:**T**
*in* limit:**T**
*in* delta:**T**
*out* output:**T**|11+|**T** = tensor(double), tensor(float), tensor(int16), tensor(int32), tensor(int64)| -|Reciprocal|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|ReduceL1|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|ReduceL2|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|ReduceLogSum|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16)| -|ReduceLogSumExp|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16)| -|ReduceMax|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|20+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[18, 19]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|ReduceMean|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|ReduceMin|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|20+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[18, 19]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|ReduceProd|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32)| -|ReduceSum|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|ReduceSumSquare|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 17]|**T** = tensor(double), tensor(float), tensor(float16)| -|Relu|*in* X:**T**
*out* Y:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Reshape|*in* data:**T**
*in* shape:**tensor(int64)**
*out* reshaped:**T**

or

*in* data:**T**
*out* reshaped:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[19, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[14, 18]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||13|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[5, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**shape** = tensor(int64)| -|||[1, 4]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Resize|*in* X:**T**
*in* scales:**tensor(float)**
*out* Y:**T**

or

*in* X:**T1**
*in* roi:**T2**
*in* scales:**tensor(float)**
*in* sizes:**tensor(int64)**
*out* Y:**T1**|19+|**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||18|**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||[13, 17]|**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||[11, 12]|**T1** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||10|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|ReverseSequence|*in* input:**T**
*in* sequence_lens:**tensor(int64)**
*out* Y:**T**|10+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|RoiAlign|*in* X:**T1**
*in* rois:**T1**
*in* batch_indices:**T2**
*out* Y:**T1**|22+|**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(int64)| -|||[16, 21]|**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(int64)| -|||[10, 15]|**T1** = tensor(double), tensor(float)
**T2** = tensor(int64)| -|RotaryEmbedding|*in* X:**T**
*in* cos_cache:**T**
*in* sin_cache:**T**
*in* position_ids:**M**
*out* Y:**T**|23+|**M** = tensor(int64)
**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|Round|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 21]|**T** = tensor(double), tensor(float), tensor(float16)| -|ScaledTanh|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Scan|*in* initial_state_and_scan_inputs:**V**
*out* final_state_and_scan_outputs:**V**

or

*in* sequence_lens:**I**
*in* initial_state_and_scan_inputs:**V**
*out* final_state_and_scan_outputs:**V**|25+|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[19, 20]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[16, 18]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 15]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[9, 10]|**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||8|**I** = tensor(int64)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Scatter|*in* data:**T**
*in* indices:**Tind**
*in* updates:**T**
*out* output:**T**|[9, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|ScatterElements|*in* data:**T**
*in* indices:**Tind**
*in* updates:**T**
*out* output:**T**|18+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[16, 17]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[13, 15]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|ScatterND|*in* data:**T**
*in* indices:**tensor(int64)**
*in* updates:**T**
*out* output:**T**|18+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[16, 17]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 15]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Selu|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(double), tensor(float), tensor(float16)| -|SequenceAt|*in* input_sequence:**S**
*in* position:**I**
*out* tensor:**T**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))
**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SequenceConstruct|*in* inputs:**T**
*out* output_sequence:**S**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))
**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SequenceEmpty|*out* output:**S**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceErase|*in* input_sequence:**S**
*in* position:**I**
*out* output_sequence:**S**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceInsert|*in* input_sequence:**S**
*in* tensor:**T**
*in* position:**I**
*out* output_sequence:**S**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceLength|*in* input_sequence:**S**
*out* length:**I**|11+|**I** = tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|Shape|*in* data:**T**
*out* shape:**T1**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[19, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[15, 18]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[13, 14]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[1, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|Shrink|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sigmoid|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Sign|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SimplifiedLayerNormalization|*in* X:**T**
*in* scale:**V**
*out* Y:**V**
*out* inv_std_var:**U**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**U** = tensor(double), tensor(float)
**V** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Sin|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(double), tensor(float), tensor(float16)| -|Size|*in* data:**T**
*out* size:**T1**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||[1, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|Slice|*in* data:**T**
*in* starts:**Tind**
*in* ends:**Tind**
*in* axes:**Tind**
*in* steps:**Tind**
*out* output:**T**

or

*in* data:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||10|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||[1, 9]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Softmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|Softplus|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Softsign|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|SpaceToDepth|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Split|*in* input:**T**
*in* split:**T**
*out* outputs...:**T**

or

*in* input:**T**
*in* split:**tensor(int64)**
*out* outputs:**T**

or

*in* input:**T**
*out* outputs:**T**|18+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 17]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[2, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sqrt|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|Squeeze|*in* data:**T**
*in* axes:**tensor(int64)**
*out* squeezed:**T**

or

*in* data:**T**
*out* squeezed:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||24|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||23|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sub|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||[7, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Sum|*in* data_0:**T**
*out* sum:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[8, 12]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 7]|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Tanh|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|TensorScatter|*in* past_cache:**T**
*in* update:**T**
*in* write_indices:**tensor(int64)**
*out* present_cache:**T**|24+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ThresholdedRelu|*in* X:**T**
*out* Y:**T**|10+|**T** = tensor(double), tensor(float), tensor(float16)| -|||1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Tile|*in* input:**T**
*in* repeats:**T1**
*out* output:**T**

or

*in* input:**T**
*in* tiles:**T**
*in* axis:**T**
*out* output:**T**|13+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(int64)| -|||[6, 12]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)
**T1** = tensor(int64)| -|TopK|*in* X:**T**
*in* K:**tensor(int64)**
*out* Values:**T**
*out* Indices:**I**

or

*in* X:**T**
*out* Values:**T**
*out* Indices:**I**|24+|**I** = tensor(int64)
**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||[11, 23]|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint8)| -|||10|**I** = tensor(int64)
**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|||[1, 9]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64)| -|Transpose|*in* data:**T**
*out* transposed:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[23, 24]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Trilu|*in* input:**T**
*in* k:**tensor(int64)**
*out* output:**T**|14+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Unsqueeze|*in* data:**T**
*in* axes:**tensor(int64)**
*out* expanded:**T**

or

*in* data:**T**
*out* expanded:**T**|25+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||24|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||23|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[21, 22]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[13, 20]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[11, 12]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||[1, 10]|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Upsample|*in* X:**T**
*in* scales:**tensor(float)**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**|9|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(uint8)| -|Where|*in* condition:**B**
*in* X:**T**
*in* Y:**T**
*out* output:**T**|16+|**B** = tensor(bool)
**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint8)| -|||[9, 15]|**B** = tensor(bool)
**T** = tensor(double), tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint8)| -|Xor|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)
**T1** = tensor(bool)| -| | -| | -|**Operator Domain:** *ai.onnx.ml*|||| -|LabelEncoder|*in* X:**T1**
*out* Y:**T2**|4+|**T1** = tensor(double), tensor(float), tensor(int64)
**T2** = tensor(double), tensor(float), tensor(int64)| -|||[2, 3]|**T1** = tensor(float), tensor(int64)
**T2** = tensor(float), tensor(int64)| -| | -| | -|**Operator Domain:** *com.microsoft*|||| -|Attention|*in* input:**T**
*in* weights:**T**
*in* bias:**T**
*in* mask_index:**M**
*in* past:**T**
*in* attention_bias:**T**
*in* past_sequence_length:**M**
*out* output:**T**
*out* present:**T**|1+|**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|BeamSearch|*in* input_ids:**F**
*in* max_length:**I**
*in* min_length:**I**
*in* num_beams:**I**
*in* num_return_sequences:**I**
*in* length_penalty:**T**
*in* repetition_penalty:**T**
*in* vocab_mask:**M**
*in* prefix_vocab_mask:**M**
*in* attention_mask:**I**
*in* decoder_input_ids:**I**
*in* logits_processor:**I**
*out* sequences:**I**
*out* sequences_scores:**T**
*out* scores:**T**|1+|**T** = tensor(float), tensor(float16)| -|BiasAdd|*in* X:**T**
*in* bias:**T**
*in* skip:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|BiasDropout|*in* data:**T**
*in* bias:**T**
*in* residual:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T2**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)| -|BiasGelu|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|BiasSoftmax|*in* data:**T**
*in* bias:**T**
*out* output:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|BiasSplitGelu|*in* X:**T**
*in* bias:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|BitmaskBiasDropout|*in* data:**T**
*in* bias:**T**
*in* residual:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T3**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)
**T3** = tensor(uint32)| -|BitmaskDropout|*in* data:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T3**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T1** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)
**T2** = tensor(bool)
**T3** = tensor(uint32)| -|CausalConvWithState|*in* input:**T**
*in* weight:**T**
*in* bias:**T**
*in* past_state:**T**
*out* output:**T**
*out* present_state:**T**|1+|**T** = tensor(float), tensor(float16)| -|ComplexMul|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(float), tensor(float16)| -|ComplexMulConj|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(float), tensor(float16)| -|ConvTransposeWithDynamicPads|*in* X:**T**
*in* W:**T**
*in* Pads:**tensor(int64)**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|DecoderAttention|*in* query:**T**
*in* key:**T**
*in* q_weight:**T**
*in* kv_weight:**T**
*in* bias:**T**
*in* key_padding_mask:**B**
*in* key_cache:**T**
*in* value_cache:**T**
*in* static_kv:**B**
*in* use_past:**B**
*in* has_layer_state:**B**
*in* has_key_padding_mask:**B**
*out* output:**T**
*out* new_key_cache:**T**
*out* new_value_cache:**T**|1+|**T** = tensor(float), tensor(float16)| -|DecoderMaskedMultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* mask_index:**M**
*in* attention_bias:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* past_sequence_length:**M**
*in* beam_width:**M**
*in* cache_indirection:**M**
*in* bias:**T**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**
*out* qk:**QK**|1+|**QK** = tensor(float), tensor(float16)
**T** = tensor(float), tensor(float16)| -|DecoderMaskedSelfAttention|*in* input:**T**
*in* weights:**T**
*in* bias:**T**
*in* mask_index:**M**
*in* past:**T**
*in* attention_bias:**T**
*in* past_sequence_length:**M**
*in* beam_width:**M**
*in* cache_indirection:**M**
*out* output:**T**
*out* present:**T**|1+|**T** = tensor(float), tensor(float16)| -|DequantizeLinear|*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T2**|1+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(float16)| -|DequantizeWithOrder|*in* input:**Q**
*in* scale_input:**S**
*out* output:**F**|1+|**F** = tensor(float), tensor(float16)
**Q** = tensor(int8)
**S** = tensor(float)| -|DynamicTimeWarping|*in* input:**F**
*out* output:**I**|1+|**F** = tensor(float)
**I** = tensor(int32)| -|EmbedLayerNormalization|*in* input_ids:**T1**
*in* segment_ids:**T1**
*in* word_embedding:**T**
*in* position_embedding:**T**
*in* segment_embedding:**T**
*in* gamma:**T**
*in* beta:**T**
*in* mask:**T1**
*in* position_ids:**T1**
*out* output:**T**
*out* mask_index:**T1**
*out* embedding_sum:**T**|1+|**T** = tensor(float), tensor(float16)| -|FastGelu|*in* X:**T**
*in* bias:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|FusedConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*in* Z:**T**
*out* Y:**T**|1+|**T** = tensor(float)| -|FusedMatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|GatedRelativePositionBias|*in* query_layer:**T**
*in* query_bias:**T**
*in* rel_pos:**T**
*in* weight:**T**
*in* bias:**T**
*in* eco_a:**T**
*in* token_offset:**M**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|GatherBlockQuantized|*in* data:**T1**
*in* indices:**Tind**
*in* scales:**T2**
*in* zero_points:**T1**
*out* output:**T2**|1+|**T1** = tensor(int4), tensor(uint4), tensor(uint8)
**T2** = tensor(bfloat16), tensor(float), tensor(float16)
**Tind** = tensor(int32), tensor(int64)| -|Gelu|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|GemmFloat8|*in* A:**TA**
*in* B:**TB**
*in* C:**TC**
*in* scaleA:**TS**
*in* scaleB:**TS**
*in* scaleY:**TS**
*out* Y:**TR**|1+|**TA** = tensor(bfloat16), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2)
**TB** = tensor(bfloat16), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2)
**TR** = tensor(bfloat16), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e5m2)
**TS** = tensor(float)| -|GemmaRotaryEmbedding|*in* emb:**U**
*in* q:**T**
*in* q_rot:**T**
*in* k:**T**
*in* k_rot:**T**
*out* output1:**T**
*out* output2:**T**|1+|**T** = tensor(float16)
**U** = tensor(float)| -|GreedySearch|*in* input_ids:**I**
*in* max_length:**I**
*in* min_length:**I**
*in* repetition_penalty:**T**
*in* vocab_mask:**I**
*in* prefix_vocab_mask:**I**
*in* attention_mask:**I**
*out* sequences:**I**|1+|**T** = tensor(float), tensor(float16)| -|GridSample|*in* X:**T1**
*in* Grid:**T1**
*out* Y:**T2**|1+|**T1** = tensor(float)
**T2** = tensor(float)| -|GroupNorm|*in* X:**T**
*in* gamma:**M**
*in* beta:**M**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|GroupQueryAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* past_key:**T_CACHE**
*in* past_value:**T_CACHE**
*in* seqlens_k:**M**
*in* total_sequence_length:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*in* position_ids:**tensor(int64)**
*in* attention_bias:**T**
*in* head_sink:**T**
*in* k_scale:**T_KV_SCALE**
*in* v_scale:**T_KV_SCALE**
*out* output:**T**
*out* present_key:**T_CACHE**
*out* present_value:**T_CACHE**
*out* output_qk:**T**|1+|**M** = tensor(int32)
**T** = tensor(bfloat16), tensor(float16)
**T_CACHE** = tensor(bfloat16), tensor(float16), tensor(float8e4m3fn), tensor(int8)
**T_KV_SCALE** = tensor(float)| -|Inverse|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|Irfft|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|LinearAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* past_state:**S**
*in* decay:**T**
*in* beta:**T**
*out* output:**T**
*out* present_state:**S**|1+|**T** = tensor(float), tensor(float16)| -|LongformerAttention|*in* input:**T**
*in* weight:**T**
*in* bias:**T**
*in* mask:**T**
*in* global_weight:**T**
*in* global_bias:**T**
*in* global:**G**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|MatMulBnb4|*in* A:**T1**
*in* B:**T2**
*in* absmax:**T1**
*out* Y:**T1**|1+|**T1** = tensor(bfloat16), tensor(float), tensor(float16)
**T2** = tensor(uint8)| -|MatMulNBits|*in* A:**T1**
*in* B:**T2**
*in* scales:**T1**
*in* zero_points:**T3**
*in* g_idx:**T4**
*in* bias:**T1**
*out* Y:**T1**|1+|**T1** = tensor(bfloat16), tensor(float), tensor(float16)
**T2** = tensor(uint8)
**T3** = tensor(bfloat16), tensor(float), tensor(float16), tensor(uint8)| -|MoE|*in* input:**T**
*in* router_probs:**T**
*in* fc1_experts_weights:**T**
*in* fc1_experts_bias:**T**
*in* fc2_experts_weights:**T**
*in* fc2_experts_bias:**T**
*in* fc3_experts_weights:**T**
*in* fc3_experts_bias:**T**
*out* output:**T**|1+|**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|MultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* bias:**T**
*in* key_padding_mask:**M**
*in* attention_bias:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* past_sequence_length:**M**
*in* cache_indirection:**M**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**
*out* qk:**QK**|1+|**QK** = tensor(bfloat16), tensor(float), tensor(float16)
**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|NGramRepeatBlock|*in* input_ids:**Tid**
*in* scores:**T**
*out* scores_out:**T**|1+|**T** = tensor(float)
**Tid** = tensor(int64)| -|NhwcConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|PackedAttention|*in* input:**T**
*in* weights:**T**
*in* bias:**T**
*in* token_offset:**M**
*in* cumulative_sequence_length:**M**
*in* attention_bias:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|PackedMultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* bias:**T**
*in* token_offset:**M**
*in* cumulative_sequence_length:**M**
*in* attention_bias:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|PagedAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* key_cache:**T**
*in* value_cache:**T**
*in* cumulative_sequence_length:**S**
*in* past_seqlens:**S**
*in* block_table:**S**
*in* cos_cache:**T**
*in* sin_cache:**T**
*out* output:**T**
*out* key_cache_out:**T**
*out* value_cache_out:**T**|1+|**S** = tensor(int32)
**T** = tensor(bfloat16), tensor(float16)| -|QAttention|*in* input:**T1**
*in* weight:**T2**
*in* bias:**T3**
*in* input_scale:**T3**
*in* weight_scale:**T3**
*in* mask_index:**T4**
*in* input_zero_point:**T1**
*in* weight_zero_point:**T2**
*in* past:**T3**
*out* output:**T3**
*out* present:**T3**|1+|**T1** = tensor(int8)
**T2** = tensor(int8)
**T3** = tensor(float), tensor(float16)
**T4** = tensor(int32)| -|QMoE|*in* input:**T**
*in* router_probs:**T**
*in* fc1_experts_weights:**T1**
*in* fc1_scales:**T2**
*in* fc1_experts_bias:**T**
*in* fc2_experts_weights:**T1**
*in* fc2_scales:**T2**
*in* fc2_experts_bias:**T**
*in* fc3_experts_weights:**T1**
*in* fc3_scales:**T2**
*in* fc3_experts_bias:**T**
*in* fc1_zero_points:**T1**
*in* fc2_zero_points:**T1**
*in* fc3_zero_points:**T1**
*in* router_weights:**T**
*out* output:**T**|1+|**T** = tensor(bfloat16), tensor(float16)
**T1** = tensor(uint8)
**T2** = tensor(bfloat16), tensor(float16)| -|QOrderedAttention|*in* input:**Q**
*in* scale_input:**S**
*in* scale_Q_gemm:**S**
*in* scale_K_gemm:**S**
*in* scale_V_gemm:**S**
*in* Q_weight:**Q**
*in* K_weight:**Q**
*in* V_weight:**Q**
*in* scale_Q_weight:**S**
*in* scale_K_weight:**S**
*in* scale_V_weight:**S**
*in* Q_bias:**S**
*in* K_bias:**S**
*in* V_bias:**S**
*in* scale_QKT_gemm:**S**
*in* scale_QKT_softmax:**S**
*in* scale_values_gemm:**S**
*in* mask_index:**G**
*in* past:**Q**
*in* attention_bias:**S**
*out* output:**Q**|1+|**G** = tensor(int32)
**Q** = tensor(int8)
**S** = tensor(float)| -|QOrderedGelu|*in* X:**Q**
*in* scale_X:**S**
*in* scale_Y:**S**
*out* Y:**Q**|1+|**Q** = tensor(int8)
**S** = tensor(float)| -|QOrderedLayerNormalization|*in* X:**Q**
*in* scale_X:**S**
*in* scale:**F**
*in* B:**F**
*in* scale_Y:**S**
*out* Y:**Q**|1+|**F** = tensor(float), tensor(float16)
**Q** = tensor(int8)
**S** = tensor(float)| -|QOrderedLongformerAttention|*in* input:**Q**
*in* scale_input:**S**
*in* weight:**Q**
*in* scale_weight:**S**
*in* bias:**S**
*in* scale_bias:**S**
*in* scale_qkv_gemm:**S**
*in* mask:**F**
*in* global_weight:**Q**
*in* scale_global_weight:**S**
*in* global_bias:**S**
*in* scale_global_gemm:**S**
*in* global:**G**
*in* scale_output:**S**
*out* output:**Q**|1+|**F** = tensor(float16)
**G** = tensor(int32)
**Q** = tensor(int8)
**S** = tensor(float)| -|QOrderedMatMul|*in* A:**Q**
*in* scale_A:**S**
*in* B:**Q**
*in* scale_B:**S**
*in* scale_Y:**S**
*in* bias:**S**
*in* C:**Q**
*in* scale_C:**S**
*out* Y:**Q**|1+|**Q** = tensor(int8)
**S** = tensor(float)| -|QuantizeLinear|*in* x:**T1**
*in* y_scale:**T1**
*in* y_zero_point:**T2**
*out* y:**T2**|1+|**T1** = tensor(float16)
**T2** = tensor(int8), tensor(uint8)| -|QuantizeWithOrder|*in* input:**F**
*in* scale_input:**S**
*out* output:**Q**|1+|**F** = tensor(float), tensor(float16)
**Q** = tensor(int8)
**S** = tensor(float)| -|QuickGelu|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|RelativePositionBias|*in* bias_table:**T**
*in* query_length:**U**
*in* key_length:**U**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|RemovePadding|*in* input:**T**
*in* sequence_token_count:**M**
*out* output:**T**
*out* token_offset:**M**
*out* cumulated_seq_len:**M**
*out* max_seq_len:**M**|1+|**T** = tensor(float), tensor(float16)| -|RestorePadding|*in* input:**T**
*in* token_offset:**M**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|Rfft|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(double), tensor(float), tensor(float16)| -|RotaryEmbedding|*in* input:**T**
*in* position_ids:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*out* output:**T**|1+|**M** = tensor(int64)
**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|Sampling|*in* input_ids:**I**
*in* max_length:**I**
*in* min_length:**I**
*in* repetition_penalty:**T**
*in* vocab_mask:**I**
*in* prefix_vocab_mask:**I**
*in* attention_mask:**I**
*in* presence_mask:**I**
*in* seed:**I**
*out* sequences:**I**
*out* filtered_logits:**T**|1+|**T** = tensor(float), tensor(float16)| -|SkipGroupNorm|*in* X:**T**
*in* gamma:**M**
*in* beta:**M**
*in* skip:**T**
*in* bias:**T**
*out* Y:**T**
*out* S:**T**|1+|**T** = tensor(float), tensor(float16)| -|SkipLayerNormalization|*in* input:**T**
*in* skip:**T**
*in* gamma:**T**
*in* beta:**T**
*in* bias:**T**
*out* output:**T**
*out* mean:**U**
*out* inv_std_var:**U**
*out* input_skip_bias_sum:**T**|1+|**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|SkipSimplifiedLayerNormalization|*in* input:**T**
*in* skip:**T**
*in* gamma:**T**
*in* bias:**T**
*out* output:**T**
*out* mean:**U**
*out* inv_std_var:**U**
*out* input_skip_bias_sum:**T**|1+|**T** = tensor(bfloat16), tensor(float), tensor(float16)| -|SparseAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* block_row_indices:**M**
*in* block_col_indices:**M**
*in* total_sequence_length:**M**
*in* key_total_sequence_lengths:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**|1+|**M** = tensor(int32)
**T** = tensor(bfloat16), tensor(float16)| -|TransposeMatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(double), tensor(float), tensor(float16)| -|Trilu|*in* X:**T**
*in* k:**tensor(int64)**
*out* Y:**T**|1+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|UnfoldTensor|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|WhisperBeamSearch|*in* input_ids:**F**
*in* max_length:**I**
*in* min_length:**I**
*in* num_beams:**I**
*in* num_return_sequences:**I**
*in* length_penalty:**T**
*in* repetition_penalty:**T**
*in* vocab_mask:**M**
*in* prefix_vocab_mask:**M**
*in* attention_mask:**I**
*in* decoder_input_ids:**I**
*in* logits_processor:**I**
*in* cross_qk_layer_head:**I**
*in* extra_decoding_ids:**I**
*in* temperature:**T**
*out* sequences:**I**
*out* sequences_scores:**T**
*out* scores:**T**
*out* cross_qk:**V**
*out* non_speech_probs:**T**|1+|**T** = tensor(float), tensor(float16)| -| | -| | -|**Operator Domain:** *com.ms.internal.nhwc*|||| -|AveragePool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(float), tensor(float16)| -|||[19, 21]|**T** = tensor(float), tensor(float16)| -|||[11, 18]|**T** = tensor(float), tensor(float16)| -|||10|**T** = tensor(float), tensor(float16)| -|||[7, 9]|**T** = tensor(float), tensor(float16)| -|BatchNormalization|*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* input_mean:**U**
*in* input_var:**U**
*out* Y:**T**
*out* running_mean:**U**
*out* running_var:**U**

or

*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* mean:**T**
*in* var:**T**
*out* Y:**T**
*out* mean:**T**
*out* var:**T**
*out* saved_mean:**T**
*out* saved_var:**T**

or

*in* X:**T**
*in* scale:**T1**
*in* B:**T1**
*in* input_mean:**T2**
*in* input_var:**T2**
*out* Y:**T**
*out* running_mean:**T2**
*out* running_var:**T2**|15+|**T** = tensor(double), tensor(float), tensor(float16)
**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(double), tensor(float), tensor(float16)| -|||14|**T** = tensor(double), tensor(float), tensor(float16)
**U** = tensor(double), tensor(float), tensor(float16)| -|||[9, 13]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[7, 8]|**T** = tensor(double), tensor(float), tensor(float16)| -|Conv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|22+|**T** = tensor(float), tensor(float16)| -|||[11, 21]|**T** = tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(float), tensor(float16)| -|ConvTranspose|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|11+|**T** = tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(float), tensor(float16)| -|DepthToSpace|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[11, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 10]|**T** = tensor(double), tensor(float), tensor(float16)| -|GlobalAveragePool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(float), tensor(float16)| -|||[1, 21]|**T** = tensor(float), tensor(float16)| -|GlobalMaxPool|*in* X:**T**
*out* Y:**T**|22+|**T** = tensor(float), tensor(float16)| -|||[1, 21]|**T** = tensor(float), tensor(float16)| -|GridSample|*in* X:**T1**
*in* grid:**T2**
*out* Y:**T1**|22+|**T1** = tensor(float)
**T2** = tensor(float)| -|||[20, 21]|**T1** = tensor(float)
**T2** = tensor(float)| -|||[16, 19]|**T1** = tensor(float)
**T2** = tensor(float)| -|LRN|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -|MaxPool|*in* X:**T**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**
*out* Indices:**I**|12+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||11|**I** = tensor(int64)
**T** = tensor(float), tensor(float16)| -|||10|**I** = tensor(int64)
**T** = tensor(float), tensor(float16)| -|||[8, 9]|**I** = tensor(int64)
**T** = tensor(float), tensor(float16)| -|||[1, 7]|**T** = tensor(float), tensor(float16)| -|SpaceToDepth|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(double), tensor(float), tensor(float16)| -|||[1, 12]|**T** = tensor(double), tensor(float), tensor(float16)| -| | -| | - - -
- -## Operators implemented by DmlExecutionProvider - -| Op Name | Parameters | OpSet Version | Types Supported | -|---------|------------|---------------|-----------------| -|**Operator Domain:** *ai.onnx*|||| -|Abs|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|||6+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|Acos|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Acosh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|Add|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Affine|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|And|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)| -|ArgMax|*in* data:**T**
*out* reduced:**tensor(int64)**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ArgMin|*in* data:**T**
*out* reduced:**tensor(int64)**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Asin|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Asinh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|Atan|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Atanh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|AveragePool|*in* X:**T**
*out* Y:**T**|19+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||10+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|BatchNormalization|*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* input_mean:**U**
*in* input_var:**U**
*out* Y:**T**
*out* running_mean:**U**
*out* running_var:**U**

or

*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* mean:**T**
*in* var:**T**
*out* Y:**T**
*out* mean:**T**
*out* var:**T**
*out* saved_mean:**T**
*out* saved_var:**T**

or

*in* X:**T**
*in* scale:**T1**
*in* B:**T1**
*in* input_mean:**T2**
*in* input_var:**T2**
*out* Y:**T**
*out* running_mean:**T2**
*out* running_var:**T2**|15+|**T** = tensor(float), tensor(float16)| -|||14+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|BitShift|*in* X:**T**
*in* Y:**T**
*out* Z:**T**|11+|**T** = tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|BitwiseAnd|*in* A:**T**
*in* B:**T**
*out* C:**T**|18+|**T** = tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|BitwiseNot|*in* X:**T**
*out* Y:**T**|18+|**T** = tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|BitwiseOr|*in* A:**T**
*in* B:**T**
*out* C:**T**|18+|**T** = tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|BitwiseXor|*in* A:**T**
*in* B:**T**
*out* C:**T**|18+|**T** = tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Cast|*in* input:**T1**
*out* output:**T2**|21+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||6+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|CastLike|*in* input:**T1**
*in* target_type:**T2**
*out* output:**T2**|21+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||15+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Ceil|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Celu|*in* X:**T**
*out* Y:**T**|12+|**T** = tensor(float), tensor(float16)| -|Clip|*in* input:**T**
*in* min:**T**
*in* max:**T**
*out* output:**T**

or

*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Col2Im|*in* input:**T**
*in* image_shape:**tensor(int64)**
*in* block_shape:**tensor(int64)**
*out* output:**T**|18+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Concat|*in* inputs:**T**
*out* concat_result:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||4+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ConcatFromSequence|*in* input_sequence:**S**
*out* concat_result:**T**|11+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ConstantOfShape|*in* input:**T1**
*out* output:**T2**|21+|**T1** = tensor(int64)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T1** = tensor(int64)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Conv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ConvInteger|*in* x:**T1**
*in* w:**T2**
*in* x_zero_point:**T1**
*in* w_zero_point:**T2**
*out* y:**T3**|10+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int32)| -|ConvTranspose|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|Cos|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Cosh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|Crop|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|CumSum|*in* x:**T**
*in* axis:**T2**
*out* y:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|DFT|*in* input:**T1**
*in* dft_length:**T2**
*in* axis:**tensor(int64)**
*out* output:**T1**

or

*in* input:**T1**
*in* dft_length:**T2**
*out* output:**T1**|20+|**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|||17+|**T1** = tensor(double), tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|DepthToSpace|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|DequantizeLinear|*in* x:**T**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T**
*out* y:**tensor(float)**

or

*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T2**

or

*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T3**|21+|**T1** = tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||19+|**T1** = tensor(int32), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||13+|**T** = tensor(int32), tensor(int8), tensor(uint8)| -|||10+|**T** = tensor(int32), tensor(int8), tensor(uint8)| -|Div|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Dropout|*in* data:**T**
*in* ratio:**T1**
*in* training_mode:**T2**
*out* output:**T**
*out* mask:**T2**

or

*in* data:**T**
*out* output:**T**
*out* mask:**T**

or

*in* data:**T**
*out* output:**T**
*out* mask:**T1**|7+|**T** = tensor(float), tensor(float16)| -|DynamicQuantizeLinear|*in* x:**T1**
*out* y:**T2**
*out* y_scale:**tensor(float)**
*out* y_zero_point:**T2**|11+|**T1** = tensor(float)
**T2** = tensor(int8), tensor(uint8)| -|Einsum|*in* Inputs:**T**
*out* Output:**T**|12+|**T** = tensor(float), tensor(float16)| -|Elu|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(float), tensor(float16)| -|Equal|*in* A:**T**
*in* B:**T**
*out* C:**T1**|19+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||7+|**T** = tensor(float), tensor(float16)
**T1** = tensor(bool)| -|Erf|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|Exp|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Expand|*in* input:**T**
*in* shape:**tensor(int64)**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||8+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|EyeLike|*in* input:**T1**
*out* output:**T2**|9+|**T1** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Flatten|*in* input:**T**
*out* output:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Floor|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|GRU|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*out* Y:**T**
*out* Y_h:**T**|14+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|Gather|*in* data:**T**
*in* indices:**Tind**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|GatherElements|*in* data:**T**
*in* indices:**Tind**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|GatherND|*in* data:**T**
*in* indices:**tensor(int64)**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Gemm|*in* A:**T**
*in* B:**T**
*in* C:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|GlobalAveragePool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|GlobalLpPool|*in* X:**T**
*out* Y:**T**|2+|**T** = tensor(float), tensor(float16)| -|GlobalMaxPool|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|Greater|*in* A:**T**
*in* B:**T**
*out* C:**T1**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||7+|**T** = tensor(float), tensor(float16)
**T1** = tensor(bool)| -|GreaterOrEqual|*in* A:**T**
*in* B:**T**
*out* C:**T1**|16+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|GridSample|*in* X:**T1**
*in* grid:**T2**
*out* Y:**T1**|16+|**T1** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|GroupNorm||21+|**M** = tensor(float), tensor(float16)
**T** = tensor(float), tensor(float16)| -|HardSigmoid|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(float), tensor(float16)| -|Hardmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|Identity|*in* input:**T**
*out* output:**T**

or

*in* input:**V**
*out* output:**V**|21+|**V** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**V** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||16+|**V** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||14+|**V** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|If|*in* cond:**B**
*out* outputs:**V**|19+|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(float8e4m3fn)), seq(tensor(float8e4m3fnuz)), seq(tensor(float8e5m2)), seq(tensor(float8e5m2fnuz)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(float8e4m3fn), tensor(float8e4m3fnuz), tensor(float8e5m2), tensor(float8e5m2fnuz), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||16+|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**B** = tensor(bool)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**B** = tensor(bool)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**B** = tensor(bool)
**V** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ImageScaler|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|InstanceNormalization|*in* input:**T**
*in* scale:**T**
*in* B:**T**
*out* output:**T**|6+|**T** = tensor(float), tensor(float16)| -|IsInf|*in* X:**T1**
*out* Y:**T2**|20+|**T1** = tensor(float)
**T2** = tensor(bool)| -|||10+|**T1** = tensor(float)
**T2** = tensor(bool)| -|IsNaN|*in* X:**T1**
*out* Y:**T2**|20+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||13+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(bool)| -|||9+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(bool)| -|LRN|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|LSTM|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*in* initial_c:**T**
*in* P:**T**
*out* Y:**T**
*out* Y_h:**T**
*out* Y_c:**T**|14+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|LayerNormalization|*in* X:**T**
*in* Scale:**T**
*in* B:**T**
*out* Y:**T**
*out* Mean:**U**
*out* InvStdDev:**U**

or

*in* X:**T**
*in* Scale:**V**
*in* B:**V**
*out* Y:**V**
*out* Mean:**U**
*out* InvStdDev:**U**|17+|**T** = tensor(float), tensor(float16)
**U** = tensor(float)| -|||1+|**T** = tensor(float), tensor(float16)
**U** = tensor(float), tensor(float16)
**V** = tensor(float), tensor(float16)| -|LeakyRelu|*in* X:**T**
*out* Y:**T**|16+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Less|*in* A:**T**
*in* B:**T**
*out* C:**T1**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||7+|**T** = tensor(float), tensor(float16)
**T1** = tensor(bool)| -|LessOrEqual|*in* A:**T**
*in* B:**T**
*out* C:**T1**|16+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T1** = tensor(bool)| -|Log|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|LogSoftmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|LpNormalization|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|LpPool|*in* X:**T**
*out* Y:**T**|18+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||2+|**T** = tensor(float), tensor(float16)| -|MatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|MatMulInteger|*in* A:**T1**
*in* B:**T2**
*in* a_zero_point:**T1**
*in* b_zero_point:**T2**
*out* Y:**T3**|10+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int32)| -|Max|*in* data_0:**T**
*out* max:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||8+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|MaxPool|*in* X:**T**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**
*out* Indices:**I**|12+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||11+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||10+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||8+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)| -|||1+|**T** = tensor(float), tensor(float16)| -|MaxRoiPool|*in* X:**T**
*in* rois:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|MaxUnpool|*in* X:**T1**
*in* I:**T2**
*in* output_shape:**T2**
*out* output:**T1**|11+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int64)| -|||9+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int64)| -|Mean|*in* data_0:**T**
*out* mean:**T**|13+|**T** = tensor(float), tensor(float16)| -|||8+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|MeanVarianceNormalization|*in* X:**T**
*out* Y:**T**

or

*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|MemcpyFromHost|*in* X:**T**
*out* Y:**T**|1+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)| -|MemcpyToHost|*in* X:**T**
*out* Y:**T**|1+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)| -|Min|*in* data_0:**T**
*out* min:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||8+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Mod|*in* A:**T**
*in* B:**T**
*out* C:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||10+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|Mul|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Neg|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|||6+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8)| -|NonZero|*in* X:**T**
*out* Y:**tensor(int64)**|13+|**T** = tensor(bool), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||9+|**T** = tensor(bool), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|Not|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(bool)| -|OneHot|*in* indices:**T1**
*in* depth:**T2**
*in* values:**T3**
*out* output:**T3**|11+|**T1** = tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T3** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T1** = tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)
**T2** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**T3** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|OptionalGetElement|*in* input:**O**
*out* output:**V**|18+|**O** = optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint64)), optional(tensor(uint8)), seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||15+|**O** = optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint64)), optional(tensor(uint8))
**V** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|OptionalHasElement|*in* input:**O**
*out* output:**B**|18+|**B** = tensor(bool)
**O** = optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint64)), optional(tensor(uint8)), seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(string)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||15+|**B** = tensor(bool)
**O** = optional(seq(tensor(bfloat16))), optional(seq(tensor(bool))), optional(seq(tensor(double))), optional(seq(tensor(float))), optional(seq(tensor(float16))), optional(seq(tensor(int16))), optional(seq(tensor(int32))), optional(seq(tensor(int64))), optional(seq(tensor(int8))), optional(seq(tensor(string))), optional(seq(tensor(uint16))), optional(seq(tensor(uint32))), optional(seq(tensor(uint64))), optional(seq(tensor(uint8))), optional(tensor(bfloat16)), optional(tensor(bool)), optional(tensor(double)), optional(tensor(float)), optional(tensor(float16)), optional(tensor(int16)), optional(tensor(int32)), optional(tensor(int64)), optional(tensor(int8)), optional(tensor(string)), optional(tensor(uint16)), optional(tensor(uint32)), optional(tensor(uint64)), optional(tensor(uint8))| -|Or|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)| -|PRelu|*in* X:**T**
*in* slope:**T**
*out* Y:**T**|16+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8)| -|||9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8)| -|||7+|**T** = tensor(float), tensor(float16)| -|Pad|*in* data:**T**
*in* pads:**tensor(int64)**
*in* constant_value:**T**
*in* axes:**Tind**
*out* output:**T**

or

*in* data:**T**
*in* pads:**tensor(int64)**
*in* constant_value:**T**
*out* output:**T**

or

*in* data:**T**
*out* output:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||18+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||2+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|ParametricSoftplus|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|Pow|*in* X:**T**
*in* Y:**T**
*out* Z:**T**

or

*in* X:**T**
*in* Y:**T1**
*out* Z:**T**|15+|**T** = tensor(float), tensor(float16), tensor(int32)
**T1** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int32)
**T1** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int32)
**T1** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16)| -|QLinearConv|*in* x:**T1**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T1**
*in* w:**T2**
*in* w_scale:**tensor(float)**
*in* w_zero_point:**T2**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T3**
*in* B:**T4**
*out* y:**T3**|10+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int8), tensor(uint8)
**T4** = tensor(int32)| -|QLinearMatMul|*in* a:**T1**
*in* a_scale:**TS**
*in* a_zero_point:**T1**
*in* b:**T2**
*in* b_scale:**TS**
*in* b_zero_point:**T2**
*in* y_scale:**TS**
*in* y_zero_point:**T3**
*out* y:**T3**

or

*in* a:**T1**
*in* a_scale:**tensor(float)**
*in* a_zero_point:**T1**
*in* b:**T2**
*in* b_scale:**tensor(float)**
*in* b_zero_point:**T2**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T3**
*out* y:**T3**|21+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int8), tensor(uint8)| -|||10+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(int8), tensor(uint8)| -|QuantizeLinear|*in* x:**T1**
*in* y_scale:**T1**
*in* y_zero_point:**T2**
*out* y:**T2**

or

*in* x:**T1**
*in* y_scale:**T2**
*in* y_zero_point:**T3**
*out* y:**T3**

or

*in* x:**T1**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T2**
*out* y:**T2**|21+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int4), tensor(int8), tensor(uint4), tensor(uint8)| -|||19+|**T1** = tensor(float), tensor(float16), tensor(int32)
**T2** = tensor(int8), tensor(uint8)| -|||13+|**T1** = tensor(float), tensor(int32)
**T2** = tensor(int8), tensor(uint8)| -|||10+|**T1** = tensor(float), tensor(int32)
**T2** = tensor(int8), tensor(uint8)| -|RNN|*in* X:**T**
*in* W:**T**
*in* R:**T**
*in* B:**T**
*in* sequence_lens:**T1**
*in* initial_h:**T**
*out* Y:**T**
*out* Y_h:**T**|14+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|Range|*in* start:**T**
*in* limit:**T**
*in* delta:**T**
*out* output:**T**|11+|**T** = tensor(float), tensor(int16), tensor(int32), tensor(int64)| -|Reciprocal|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|ReduceL1|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|ReduceL2|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceLogSum|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceLogSumExp|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceMax|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|20+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceMean|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceMin|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|20+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||12+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|ReduceProd|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|ReduceSum|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|ReduceSumSquare|*in* data:**T**
*in* axes:**tensor(int64)**
*out* reduced:**T**

or

*in* data:**T**
*out* reduced:**T**|18+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||11+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|||1+|**T** = tensor(float), tensor(float16), tensor(int32), tensor(int64), tensor(uint32), tensor(uint64)| -|Relu|*in* X:**T**
*out* Y:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8)| -|||13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Reshape|*in* data:**T**
*in* shape:**tensor(int64)**
*out* reshaped:**T**

or

*in* data:**T**
*out* reshaped:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||19+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||14+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||5+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Resize|*in* X:**T**
*in* scales:**tensor(float)**
*out* Y:**T**

or

*in* X:**T1**
*in* roi:**T2**
*in* scales:**tensor(float)**
*in* sizes:**tensor(int64)**
*out* Y:**T1**|19+|**T1** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||18+|**T1** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||13+|**T1** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||11+|**T1** = tensor(float), tensor(float16), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|||10+|**T** = tensor(float), tensor(float16)| -|ReverseSequence|*in* input:**T**
*in* sequence_lens:**tensor(int64)**
*out* Y:**T**|10+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|RoiAlign|*in* X:**T1**
*in* rois:**T1**
*in* batch_indices:**T2**
*out* Y:**T1**|16+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|||10+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|Round|*in* X:**T**
*out* Y:**T**|11+|**T** = tensor(float), tensor(float16)| -|STFT|*in* signal:**T1**
*in* frame_step:**T2**
*in* window:**T1**
*in* frame_length:**T2**
*out* output:**T1**|17+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(int32), tensor(int64)| -|ScaledTanh|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|Scatter|*in* data:**T**
*in* indices:**Tind**
*in* updates:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||9+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|ScatterElements|*in* data:**T**
*in* indices:**Tind**
*in* updates:**T**
*out* output:**T**|16+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|ScatterND|*in* data:**T**
*in* indices:**tensor(int64)**
*in* updates:**T**
*out* output:**T**|16+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Selu|*in* X:**T**
*out* Y:**T**|6+|**T** = tensor(float), tensor(float16)| -|SequenceAt|*in* input_sequence:**S**
*in* position:**I**
*out* tensor:**T**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))
**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SequenceConstruct|*in* inputs:**T**
*out* output_sequence:**S**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))
**T** = tensor(bfloat16), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SequenceEmpty|*out* output:**S**|11+|**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceErase|*in* input_sequence:**S**
*in* position:**I**
*out* output_sequence:**S**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceInsert|*in* input_sequence:**S**
*in* tensor:**T**
*in* position:**I**
*out* output_sequence:**S**|11+|**I** = tensor(int32), tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|SequenceLength|*in* input_sequence:**S**
*out* length:**I**|11+|**I** = tensor(int64)
**S** = seq(tensor(bfloat16)), seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8))| -|Shape|*in* data:**T**
*out* shape:**T1**|21+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||19+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||15+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||13+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||1+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|Shrink|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint8)| -|Sigmoid|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Sign|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|SimplifiedLayerNormalization|*in* X:**T**
*in* scale:**V**
*out* Y:**V**
*out* inv_std_var:**U**|1+|**T** = tensor(float), tensor(float16)
**U** = tensor(float), tensor(float16)
**V** = tensor(float), tensor(float16)| -|Sin|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Sinh|*in* input:**T**
*out* output:**T**|9+|**T** = tensor(float), tensor(float16)| -|Size|*in* data:**T**
*out* size:**T1**|21+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||19+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||13+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|||1+|**T** = seq(tensor(bool)), seq(tensor(double)), seq(tensor(float)), seq(tensor(float16)), seq(tensor(int16)), seq(tensor(int32)), seq(tensor(int64)), seq(tensor(int8)), seq(tensor(uint16)), seq(tensor(uint32)), seq(tensor(uint64)), seq(tensor(uint8)), tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int4), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint4), tensor(uint64), tensor(uint8)
**T1** = tensor(int64)| -|Slice|*in* data:**T**
*in* starts:**Tind**
*in* ends:**Tind**
*in* axes:**Tind**
*in* steps:**Tind**
*out* output:**T**

or

*in* data:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||10+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)
**Tind** = tensor(int32), tensor(int64)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Softmax|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||11+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|Softplus|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|Softsign|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|SpaceToDepth|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Split|*in* input:**T**
*in* split:**T**
*out* outputs...:**T**

or

*in* input:**T**
*in* split:**tensor(int64)**
*out* outputs:**T**

or

*in* input:**T**
*out* outputs:**T**|18+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||2+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sqrt|*in* X:**T**
*out* Y:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Squeeze|*in* data:**T**
*in* axes:**tensor(int64)**
*out* squeezed:**T**

or

*in* data:**T**
*out* squeezed:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sub|*in* A:**T**
*in* B:**T**
*out* C:**T**|14+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||7+|**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Sum|*in* data_0:**T**
*out* sum:**T**|13+|**T** = tensor(float), tensor(float16)| -|||8+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|Tan|*in* input:**T**
*out* output:**T**|7+|**T** = tensor(float), tensor(float16)| -|Tanh|*in* input:**T**
*out* output:**T**|13+|**T** = tensor(float), tensor(float16)| -|||6+|**T** = tensor(float), tensor(float16)| -|ThresholdedRelu|*in* X:**T**
*out* Y:**T**|10+|**T** = tensor(float), tensor(float16)| -|||1+|**T** = tensor(float), tensor(float16)| -|Tile|*in* input:**T**
*in* repeats:**T1**
*out* output:**T**

or

*in* input:**T**
*in* tiles:**T**
*in* axis:**T**
*out* output:**T**|13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||6+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|TopK|*in* X:**T**
*in* K:**tensor(int64)**
*out* Values:**T**
*out* Indices:**I**

or

*in* X:**T**
*out* Values:**T**
*out* Indices:**I**|11+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||10+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**I** = tensor(int64)
**T** = tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Transpose|*in* data:**T**
*out* transposed:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Trilu|*in* input:**T**
*in* k:**tensor(int64)**
*out* output:**T**|14+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Unsqueeze|*in* data:**T**
*in* axes:**tensor(int64)**
*out* expanded:**T**

or

*in* data:**T**
*out* expanded:**T**|21+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||13+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||11+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||1+|**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Upsample|*in* X:**T**
*in* scales:**tensor(float)**
*out* Y:**T**

or

*in* X:**T**
*out* Y:**T**|10+|**T** = tensor(float), tensor(float16)| -|||9+|**T** = tensor(float), tensor(float16)| -|||7+|**T** = tensor(float), tensor(float16)| -|Where|*in* condition:**B**
*in* X:**T**
*in* Y:**T**
*out* output:**T**|16+|**B** = tensor(bool)
**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|||9+|**B** = tensor(bool)
**T** = tensor(bool), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8)| -|Xor|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)| -| | -| | -|**Operator Domain:** *com.microsoft*|||| -|Attention|*in* input:**T**
*in* weights:**T**
*in* bias:**T**
*in* mask_index:**M**
*in* past:**T**
*in* attention_bias:**T**
*in* past_sequence_length:**M**
*out* output:**T**
*out* present:**T**|1+|**M** = tensor(int32)
**T** = tensor(float), tensor(float16)| -|BiasAdd|*in* X:**T**
*in* bias:**T**
*in* skip:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|BiasGelu|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(float), tensor(float16)| -|BiasSplitGelu|*in* X:**T**
*in* bias:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|ConvTransposeWithDynamicPads|*in* X:**T**
*in* W:**T**
*in* Pads:**tensor(int64)**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DequantizeLinear|*in* x:**T1**
*in* x_scale:**T2**
*in* x_zero_point:**T1**
*out* y:**T2**|1+|**T1** = tensor(int32), tensor(int8), tensor(uint8)
**T2** = tensor(float), tensor(float16)| -|DynamicQuantizeMatMul|*in* A:**T1**
*in* B:**T2**
*in* b_scale:**T1**
*in* b_zero_point:**T2**
*in* bias:**T1**
*out* Y:**T1**|1+|**T1** = tensor(float)
**T2** = tensor(int8), tensor(uint8)| -|EmbedLayerNormalization|*in* input_ids:**T1**
*in* segment_ids:**T1**
*in* word_embedding:**T**
*in* position_embedding:**T**
*in* segment_embedding:**T**
*in* gamma:**T**
*in* beta:**T**
*in* mask:**T1**
*in* position_ids:**T1**
*out* output:**T**
*out* mask_index:**T1**
*out* embedding_sum:**T**|1+|**T** = tensor(float), tensor(float16)| -|FastGelu|*in* X:**T**
*in* bias:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|FusedMatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|FusedMatMulActivation|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|Gelu|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|GroupNorm|*in* X:**T**
*in* gamma:**M**
*in* beta:**M**
*out* Y:**T**|1+|**M** = tensor(float), tensor(float16)
**T** = tensor(float), tensor(float16)| -|GroupQueryAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* past_key:**T_CACHE**
*in* past_value:**T_CACHE**
*in* seqlens_k:**M**
*in* total_sequence_length:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*in* position_ids:**tensor(int64)**
*in* attention_bias:**T**
*in* head_sink:**T**
*in* k_scale:**T_KV_SCALE**
*in* v_scale:**T_KV_SCALE**
*out* output:**T**
*out* present_key:**T_CACHE**
*out* present_value:**T_CACHE**
*out* output_qk:**T**|1+|**M** = tensor(int32)
**T** = tensor(float), tensor(float16)| -|MatMulIntegerToFloat|*in* A:**T1**
*in* B:**T2**
*in* a_scale:**T3**
*in* b_scale:**T3**
*in* a_zero_point:**T1**
*in* b_zero_point:**T2**
*in* bias:**T3**
*out* Y:**T3**|1+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(float), tensor(float16)| -|MatMulNBits|*in* A:**T1**
*in* B:**T2**
*in* scales:**T1**
*in* zero_points:**T3**
*in* g_idx:**T4**
*in* bias:**T1**
*out* Y:**T1**|1+|**T1** = tensor(float), tensor(float16)
**T2** = tensor(uint8)| -|MultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* bias:**T**
*in* key_padding_mask:**M**
*in* attention_bias:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* past_sequence_length:**M**
*in* cache_indirection:**M**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**
*out* qk:**QK**|1+|**M** = tensor(int32)
**T** = tensor(float), tensor(float16)| -|NhwcConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|QAttention|*in* input:**T1**
*in* weight:**T2**
*in* bias:**T3**
*in* input_scale:**T3**
*in* weight_scale:**T3**
*in* mask_index:**T4**
*in* input_zero_point:**T1**
*in* weight_zero_point:**T2**
*in* past:**T3**
*out* output:**T3**
*out* present:**T3**|1+|**T1** = tensor(int8), tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(float), tensor(float16)
**T4** = tensor(int32)| -|QLinearAdd|*in* A:**T**
*in* A_scale:**tensor(float)**
*in* A_zero_point:**T**
*in* B:**T**
*in* B_scale:**tensor(float)**
*in* B_zero_point:**T**
*in* C_scale:**tensor(float)**
*in* C_zero_point:**T**
*out* C:**T**|1+|**T** = tensor(int8), tensor(uint8)| -|QLinearAveragePool|*in* X:**T**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T**
*out* Y:**T**|1+|**T** = tensor(int8), tensor(uint8)| -|QLinearConcat|*in* Y_scale:**TF**
*in* Y_zero_point:**T8**
*in* inputs:**TV**
*out* Y:**T8**|1+|**T8** = tensor(int8), tensor(uint8)
**TF** = tensor(float)
**TV** = tensor(float), tensor(int8), tensor(uint8)| -|QLinearGlobalAveragePool|*in* X:**T**
*in* x_scale:**tensor(float)**
*in* x_zero_point:**T**
*in* y_scale:**tensor(float)**
*in* y_zero_point:**T**
*out* Y:**T**|1+|**T** = tensor(int8), tensor(uint8)| -|QLinearSigmoid|*in* X:**T**
*in* X_scale:**tensor(float)**
*in* X_zero_point:**T**
*in* Y_scale:**tensor(float)**
*in* Y_zero_point:**T**
*out* Y:**T**|1+|**T** = tensor(int8), tensor(uint8)| -|QuantizeLinear|*in* x:**T1**
*in* y_scale:**T1**
*in* y_zero_point:**T2**
*out* y:**T2**|1+|**T1** = tensor(float), tensor(float16), tensor(int32)
**T2** = tensor(int8), tensor(uint8)| -|QuickGelu|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|RotaryEmbedding|*in* input:**T**
*in* position_ids:**M**
*in* cos_cache:**T**
*in* sin_cache:**T**
*out* output:**T**|1+|**M** = tensor(int64)
**T** = tensor(float), tensor(float16)| -|SkipLayerNormalization|*in* input:**T**
*in* skip:**T**
*in* gamma:**T**
*in* beta:**T**
*in* bias:**T**
*out* output:**T**
*out* mean:**U**
*out* inv_std_var:**U**
*out* input_skip_bias_sum:**T**|1+|**T** = tensor(float), tensor(float16)| -|SkipSimplifiedLayerNormalization|*in* input:**T**
*in* skip:**T**
*in* gamma:**T**
*in* bias:**T**
*out* output:**T**
*out* mean:**U**
*out* inv_std_var:**U**
*out* input_skip_bias_sum:**T**|1+|**T** = tensor(float), tensor(float16)| -| | -| | -|**Operator Domain:** *com.microsoft.dml*|||| -|DmlFusedAdd|*in* A:**T**
*in* B:**T**
*out* C:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedBatchNormalization|*in* X:**T**
*in* scale:**T**
*in* B:**T**
*in* mean:**T**
*in* var:**T**
*out* Y:**T**
*out* mean:**T**
*out* var:**T**
*out* saved_mean:**T**
*out* saved_var:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedConvTranspose|*in* X:**T**
*in* W:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedGemm|*in* A:**T**
*in* B:**T**
*in* C:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedInstanceNormalization|*in* input:**T**
*in* scale:**T**
*in* B:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedMatMul|*in* A:**T**
*in* B:**T**
*out* Y:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedMeanVarianceNormalization|*in* input:**T**
*out* output:**T**|1+|**T** = tensor(float), tensor(float16)| -|DmlFusedSum|*in* data_0:**T**
*out* sum:**T**|1+|**T** = tensor(float), tensor(float16)| -| | -| | From 831524b6505857d70600af57206bf3a5fffa5cac Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 12 May 2026 10:42:15 +0100 Subject: [PATCH 137/140] Add an implementation an NHWC implementation of convolution to avoid transposes * Modification to the CPU EP to specify channels_last when data format is NWHC * Added a FusedNhwcConv kernel * Implementation of the kernel in mlas * Added compiler guards so it is inly used with KleidiAi (for now, can be removed if needed) * Added unittests * Rebased * Docs regenerated Signed-off-by: Orlaith Monahan --- docs/OperatorKernels.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/OperatorKernels.md b/docs/OperatorKernels.md index 8b05b23a1e1c1..c576d758f68d9 100644 --- a/docs/OperatorKernels.md +++ b/docs/OperatorKernels.md @@ -602,6 +602,7 @@ The **OpSet Version** column uses the following notation: |MultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* bias:**T**
*in* key_padding_mask:**M**
*in* attention_bias:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* past_sequence_length:**M**
*in* cache_indirection:**M**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**
*out* qk:**QK**|1+|**T** = tensor(float)| |MurmurHash3|*in* X:**T1**
*out* Y:**T2**|1+|**T1** = tensor(double), tensor(float), tensor(int32), tensor(int64), tensor(string), tensor(uint32), tensor(uint64)
**T2** = tensor(int32), tensor(uint32)| |NGramRepeatBlock|*in* input_ids:**Tid**
*in* scores:**T**
*out* scores_out:**T**|1+|**T** = tensor(float)
**Tid** = tensor(int64)| +|NhwcFusedConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*in* Z:**T**
*out* Y:**T**|1+|**T** = tensor(float)| |NhwcMaxPool|*in* x:**T**
*out* y:**T**|1+|**T** = tensor(int8), tensor(uint8)| |Pad|*in* data:**T**
*in* pads:**tensor(int64)**
*in* value:**T**
*out* output:**T**|1+|**T** = tensor(float)| |QAttention|*in* input:**T1**
*in* weight:**T2**
*in* bias:**T3**
*in* input_scale:**T3**
*in* weight_scale:**T3**
*in* mask_index:**T4**
*in* input_zero_point:**T1**
*in* weight_zero_point:**T2**
*in* past:**T3**
*out* output:**T3**
*out* present:**T3**|1+|**T1** = tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(float)
**T4** = tensor(int32)| From 32b968571df9ade68fdc4ea266c5b52567369c65 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Tue, 12 May 2026 21:50:34 +0100 Subject: [PATCH 138/140] docs regen Signed-off-by: Orlaith Monahan --- docs/OperatorKernels.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/OperatorKernels.md b/docs/OperatorKernels.md index a773713f52431..9c633ca645099 100644 --- a/docs/OperatorKernels.md +++ b/docs/OperatorKernels.md @@ -13,6 +13,8 @@ The **OpSet Version** column uses the following notation: ## Execution Providers - [CPUExecutionProvider](#cpuexecutionprovider) +- [CUDAExecutionProvider](#cudaexecutionprovider) +- [DmlExecutionProvider](#dmlexecutionprovider) --------------- @@ -602,7 +604,6 @@ The **OpSet Version** column uses the following notation: |MultiHeadAttention|*in* query:**T**
*in* key:**T**
*in* value:**T**
*in* bias:**T**
*in* key_padding_mask:**M**
*in* attention_bias:**T**
*in* past_key:**T**
*in* past_value:**T**
*in* past_sequence_length:**M**
*in* cache_indirection:**M**
*out* output:**T**
*out* present_key:**T**
*out* present_value:**T**
*out* qk:**QK**|1+|**T** = tensor(float)| |MurmurHash3|*in* X:**T1**
*out* Y:**T2**|1+|**T1** = tensor(double), tensor(float), tensor(int32), tensor(int64), tensor(string), tensor(uint32), tensor(uint64)
**T2** = tensor(int32), tensor(uint32)| |NGramRepeatBlock|*in* input_ids:**Tid**
*in* scores:**T**
*out* scores_out:**T**|1+|**T** = tensor(float)
**Tid** = tensor(int64)| -|NhwcFusedConv|*in* X:**T**
*in* W:**T**
*in* B:**T**
*in* Z:**T**
*out* Y:**T**|1+|**T** = tensor(float)| |NhwcMaxPool|*in* x:**T**
*out* y:**T**|1+|**T** = tensor(int8), tensor(uint8)| |Pad|*in* data:**T**
*in* pads:**tensor(int64)**
*in* value:**T**
*out* output:**T**|1+|**T** = tensor(float)| |QAttention|*in* input:**T1**
*in* weight:**T2**
*in* bias:**T3**
*in* input_scale:**T3**
*in* weight_scale:**T3**
*in* mask_index:**T4**
*in* input_zero_point:**T1**
*in* weight_zero_point:**T2**
*in* past:**T3**
*out* output:**T3**
*out* present:**T3**|1+|**T1** = tensor(uint8)
**T2** = tensor(int8), tensor(uint8)
**T3** = tensor(float)
**T4** = tensor(int32)| From 7257c60e1b432c7bedab1ab6809dbc4ef405bb0f Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 14 May 2026 00:04:15 +0100 Subject: [PATCH 139/140] Fix for the CUDA CI errors Signed-off-by: Orlaith Monahan --- onnxruntime/core/providers/cuda/ml/label_encoder.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/onnxruntime/core/providers/cuda/ml/label_encoder.cc b/onnxruntime/core/providers/cuda/ml/label_encoder.cc index 60f43e0cb6c47..2021e8f53717d 100644 --- a/onnxruntime/core/providers/cuda/ml/label_encoder.cc +++ b/onnxruntime/core/providers/cuda/ml/label_encoder.cc @@ -62,7 +62,7 @@ static bool TryGetScalarTensorAttribute(const OpKernelInfo& info, const std::str auto* attr_tensor_proto = GetTensorProto(attr_tensor_holder); auto result = info.GetAttr(tensor_name, attr_tensor_proto); if (result.IsOK() && utils::HasDataType(*attr_tensor_proto)) { - result = utils::UnpackTensor(*attr_tensor_proto, nullptr, 0, &value, 1); + result = utils::UnpackTensor(*attr_tensor_proto, std::filesystem::path(), &value, 1); ORT_ENFORCE(result.IsOK(), "LabelEncoder could not unpack tensor attribute ", attr_name); return true; } @@ -113,7 +113,7 @@ static std::vector GetAttrOrTensor(const OpKernelInfo& info, const std::strin } const SafeInt tensor_size(element_count); std::vector out(tensor_size); - result = utils::UnpackTensor(*attr_tensor_proto, nullptr, 0, out.data(), tensor_size); + result = utils::UnpackTensor(*attr_tensor_proto, std::filesystem::path(), out.data(), tensor_size); ORT_ENFORCE(result.IsOK(), "LabelEncoder could not unpack tensor attribute ", name); return out; #endif // BUILD_CUDA_EP_AS_PLUGIN From d59f03625e1e04afeaede6507f9a905534a10314 Mon Sep 17 00:00:00 2001 From: Orlaith Monahan Date: Thu, 14 May 2026 09:53:49 +0100 Subject: [PATCH 140/140] Revert "Fix for the CUDA CI errors" This reverts commit 7257c60e1b432c7bedab1ab6809dbc4ef405bb0f. --- onnxruntime/core/providers/cuda/ml/label_encoder.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/onnxruntime/core/providers/cuda/ml/label_encoder.cc b/onnxruntime/core/providers/cuda/ml/label_encoder.cc index 2021e8f53717d..60f43e0cb6c47 100644 --- a/onnxruntime/core/providers/cuda/ml/label_encoder.cc +++ b/onnxruntime/core/providers/cuda/ml/label_encoder.cc @@ -62,7 +62,7 @@ static bool TryGetScalarTensorAttribute(const OpKernelInfo& info, const std::str auto* attr_tensor_proto = GetTensorProto(attr_tensor_holder); auto result = info.GetAttr(tensor_name, attr_tensor_proto); if (result.IsOK() && utils::HasDataType(*attr_tensor_proto)) { - result = utils::UnpackTensor(*attr_tensor_proto, std::filesystem::path(), &value, 1); + result = utils::UnpackTensor(*attr_tensor_proto, nullptr, 0, &value, 1); ORT_ENFORCE(result.IsOK(), "LabelEncoder could not unpack tensor attribute ", attr_name); return true; } @@ -113,7 +113,7 @@ static std::vector GetAttrOrTensor(const OpKernelInfo& info, const std::strin } const SafeInt tensor_size(element_count); std::vector out(tensor_size); - result = utils::UnpackTensor(*attr_tensor_proto, std::filesystem::path(), out.data(), tensor_size); + result = utils::UnpackTensor(*attr_tensor_proto, nullptr, 0, out.data(), tensor_size); ORT_ENFORCE(result.IsOK(), "LabelEncoder could not unpack tensor attribute ", name); return out; #endif // BUILD_CUDA_EP_AS_PLUGIN