diff --git a/BUILD.md b/BUILD.md index ddb9acd1db..5cf420c44c 100644 --- a/BUILD.md +++ b/BUILD.md @@ -403,6 +403,8 @@ alias cmake="/usr/bin/cmake -DCMAKE_TOOLCHAIN_FILE=$OECORE_NATIVE_SYSROOT/usr/sh ``` cmake ../onnxruntime-arm-upstream/cmake -DONNX_CUSTOM_PROTOC_EXECUTABLE=/usr/bin/protoc -Donnxruntime_RUN_ONNX_TESTS=OFF -Donnxruntime_GENERATE_TEST_REPORTS=ON -Donnxruntime_DEV_MODE=ON -DPYTHON_EXECUTABLE=/usr/bin/python3 -Donnxruntime_USE_CUDA=OFF -Donnxruntime_USE_NSYNC=OFF -Donnxruntime_CUDNN_HOME= -Donnxruntime_USE_JEMALLOC=OFF -Donnxruntime_ENABLE_PYTHON=OFF -Donnxruntime_BUILD_CSHARP=OFF -Donnxruntime_BUILD_SHARED_LIB=ON -Donnxruntime_USE_EIGEN_FOR_BLAS=ON -Donnxruntime_USE_OPENBLAS=OFF -Donnxruntime_USE_ACL=ON -Donnxruntime_USE_DNNL=OFF -Donnxruntime_USE_MKLML=OFF -Donnxruntime_USE_OPENMP=ON -Donnxruntime_USE_TVM=OFF -Donnxruntime_USE_LLVM=OFF -Donnxruntime_ENABLE_MICROSOFT_INTERNAL=OFF -Donnxruntime_USE_BRAINSLICE=OFF -Donnxruntime_USE_NUPHAR=OFF -Donnxruntime_USE_EIGEN_THREADPOOL=OFF -Donnxruntime_BUILD_UNIT_TESTS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo ``` +The ```-Donnxruntime_USE_ACL=ON``` option will use, by default, the 19.05 version of the Arm Compute Library. To set the right version you can use: +```-Donnxruntime_USE_ACL_1902=ON```, ```-Donnxruntime_USE_ACL_1905=ON``` or ```-Donnxruntime_USE_ACL_1908=ON```; 2. Build ONNX Runtime library, test and performance application: ``` diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 64e89e749c..b884f8fd40 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -91,6 +91,9 @@ option(onnxruntime_DEBUG_NODE_INPUTS_OUTPUTS "Dump node input shapes and output option(onnxruntime_USE_DML "Build with DirectML support" OFF) option(onnxruntime_USE_WINML "Build with WinML support" OFF) option(onnxruntime_USE_ACL "Build with ACL support" OFF) +option(onnxruntime_USE_ACL_1902 "Build with ACL version 1902 support" OFF) +option(onnxruntime_USE_ACL_1905 "Build with ACL version 1905 support" OFF) +option(onnxruntime_USE_ACL_1908 "Build with ACL version 1908 support" OFF) option(onnxruntime_ENABLE_INSTRUMENT "Enable Instrument with Event Tracing for Windows (ETW)" OFF) option(onnxruntime_USE_TELEMETRY "Build with Telemetry" OFF) #The onnxruntime_PREFER_SYSTEM_LIB is mainly designed for package managers like apt/yum/vcpkg. @@ -472,7 +475,18 @@ endfunction() set(onnxruntime_EXTERNAL_DEPENDENCIES onnx_proto) # ACL -if (onnxruntime_USE_ACL) +if (onnxruntime_USE_ACL OR onnxruntime_USE_ACL_1902 OR onnxruntime_USE_ACL_1905 OR onnxruntime_USE_ACL_1908) + set(onnxruntime_USE_ACL ON) + if(onnxruntime_USE_ACL_1902) + add_definitions(-DACL_1902=1) + else() + if(onnxruntime_USE_ACL_1908) + add_definitions(-DACL_1908=1) + else() + add_definitions(-DACL_1905=1) + endif() + endif() + list(APPEND onnxruntime_EXTERNAL_LIBRARIES arm_compute acl arm_compute_graph arm_compute_core) endif() diff --git a/onnxruntime/core/optimizer/graph_transformer_utils.cc b/onnxruntime/core/optimizer/graph_transformer_utils.cc index 14b686cbc9..823e15e0f1 100644 --- a/onnxruntime/core/optimizer/graph_transformer_utils.cc +++ b/onnxruntime/core/optimizer/graph_transformer_utils.cc @@ -122,7 +122,10 @@ std::vector> GenerateTransformers(TransformerL #ifndef DISABLE_CONTRIB_OPS transformers.emplace_back(onnxruntime::make_unique(cpu_execution_providers)); - transformers.emplace_back(onnxruntime::make_unique(cpu_execution_providers)); + + std::unordered_set cpu_acl_execution_providers = {onnxruntime::kCpuExecutionProvider, onnxruntime::kAclExecutionProvider}; + + transformers.emplace_back(onnxruntime::make_unique(cpu_acl_execution_providers)); std::unordered_set cpu_cuda_execution_providers = {onnxruntime::kCpuExecutionProvider, onnxruntime::kCudaExecutionProvider}; transformers.emplace_back(onnxruntime::make_unique(cpu_cuda_execution_providers)); diff --git a/onnxruntime/core/providers/acl/acl_execution_provider.cc b/onnxruntime/core/providers/acl/acl_execution_provider.cc index f1d0899543..8319121abf 100644 --- a/onnxruntime/core/providers/acl/acl_execution_provider.cc +++ b/onnxruntime/core/providers/acl/acl_execution_provider.cc @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved. +// Copyright (c) 2019-2020, NXP Semiconductor, Inc. All rights reserved. // Licensed under the MIT License. #include "acl_execution_provider.h" @@ -37,6 +37,8 @@ class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kAclExecutionProvider, kOn // Opset 10 class ONNX_OPERATOR_VERSIONED_TYPED_KERNEL_CLASS_NAME(kAclExecutionProvider, kOnnxDomain, 10, 10, float, AveragePool); +class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kAclExecutionProvider, kMSDomain, 1, float, FusedConv); + static void RegisterACLKernels(KernelRegistry& kernel_registry) { kernel_registry.Register(BuildKernelCreateInfo()); kernel_registry.Register(BuildKernelCreateInfo()); @@ -58,6 +60,8 @@ static void RegisterACLKernels(KernelRegistry& kernel_registry) { // Opset 10 kernel_registry.Register(BuildKernelCreateInfo()); + + kernel_registry.Register(BuildKernelCreateInfo()); } std::shared_ptr GetAclKernelRegistry() { diff --git a/onnxruntime/core/providers/acl/math/gemm.h b/onnxruntime/core/providers/acl/math/gemm.h index c171837c6d..7bbfbf0a91 100644 --- a/onnxruntime/core/providers/acl/math/gemm.h +++ b/onnxruntime/core/providers/acl/math/gemm.h @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved. +// Copyright (c) 2019-2020, NXP Semiconductor, Inc. All rights reserved. // Licensed under the MIT License. #pragma once @@ -19,12 +19,26 @@ #include "arm_compute/runtime/MemoryManagerOnDemand.h" //NEON -#include "arm_compute/runtime/NEON/functions/NEGEMM.h" -#include "arm_compute/runtime/NEON/functions/NETranspose.h" #include "arm_compute/runtime/NEON/functions/NEFullyConnectedLayer.h" -#undef GEMM_ACL -#define CACHE_TRANSPOSED_DATA +template +void importDataFromTensor(arm_compute::Tensor* tensor, T* data){ + + arm_compute::Window aclInpuWindow; + aclInpuWindow.use_tensor_dimensions(tensor->info()->tensor_shape()); + + arm_compute::Iterator aclInputIt(tensor, aclInpuWindow); + const unsigned int aclWidth = tensor->info()->dimension(0); + const unsigned int aclHeight = tensor->info()->dimension(1); + + // copy input tensor into the larger buffer + arm_compute::execute_window_loop( + aclInpuWindow, + [&](const arm_compute::Coordinates& co) { + data[co.z() * (aclWidth * aclHeight) + co.y() * aclWidth + co.x()] = *reinterpret_cast(aclInputIt.ptr()); + }, + aclInputIt); +} namespace onnxruntime { namespace acl { @@ -53,27 +67,51 @@ class Gemm : public onnxruntime::Gemm { } Status Compute(OpKernelContext* context) const override { - const auto X = context->Input(0); - const auto W = context->Input(1); - const auto B = context->Input(2); + const auto A = context->Input(0); + const auto B = context->Input(1); + const auto C = context->Input(2); - GemmHelper helper(X->Shape(), trans_A_ != CblasNoTrans, W->Shape(), trans_B_ != CblasNoTrans, B->Shape()); + GemmHelper helper(A->Shape(), trans_A_ != CblasNoTrans, B->Shape(), trans_B_ != CblasNoTrans, C->Shape()); if (!helper.State().IsOK()) return helper.State(); int64_t M = helper.M(); int64_t N = helper.N(); - auto Y = context->Output(0, TensorShape({M, N})); + auto D = context->Output(0, TensorShape({M, N})); - bool FC = ((alpha_ == 1 && beta_ == 1) || (alpha_ == 1 && beta_ == 0)); + bool FC = alpha_ == 1 && (beta_ == 1 || beta_ == 0); + bool useC = C != nullptr && beta_ != 0; + + if(trans_A_ == CblasTrans){ // transpose input + return onnxruntime::Gemm::Compute(context); + } + + arm_compute::TensorShape cShape = ACLTensorShape(C->Shape()); + if(useC && + (cShape.num_dimensions() > 2 || + (cShape.num_dimensions() == 2 && cShape[0] > 1 && cShape[1] > 1))) { // Multi-dimensional Bias + return onnxruntime::Gemm::Compute(context); + } + + if(useC && (cShape.num_dimensions() == 1 && cShape[0] != (long unsigned int) N)) { // Broadcast + return onnxruntime::Gemm::Compute(context); + } + + if(useC && cShape.num_dimensions() == 2){ + if((cShape[0] == 1 && cShape[1] != (long unsigned int) N) || + (cShape[1] == 1 && cShape[0] != (long unsigned int) N)) { + return onnxruntime::Gemm::Compute(context); + } + cShape = arm_compute::TensorShape(1, N); + } int64_t K = helper.K(); LOGS_DEFAULT(VERBOSE) << "Gemm ACL:" << std::endl; - if (X) LOGS_DEFAULT(VERBOSE) << "X " << X->Shape().ToString().c_str() << std::endl; - if (W) LOGS_DEFAULT(VERBOSE) << "W " << W->Shape().ToString().c_str() << std::endl; + if (A) LOGS_DEFAULT(VERBOSE) << "A " << A->Shape().ToString().c_str() << std::endl; if (B) LOGS_DEFAULT(VERBOSE) << "B " << B->Shape().ToString().c_str() << std::endl; - LOGS_DEFAULT(VERBOSE) << "Y " << Y->Shape().ToString().c_str() << std::endl; + if (C) LOGS_DEFAULT(VERBOSE) << "C " << C->Shape().ToString().c_str() << std::endl; + LOGS_DEFAULT(VERBOSE) << "D " << D->Shape().ToString().c_str() << std::endl; LOGS_DEFAULT(VERBOSE) << "M " << (int)M << ", N " << (int)N << ", K " << (int)K << std::endl; LOGS_DEFAULT(VERBOSE) << "Alfa " << alpha_ << ", Beta " << beta_ << std::endl; LOGS_DEFAULT(VERBOSE) << "trans_A_ " << (trans_A_ == CblasTrans) << std::endl; @@ -89,51 +127,22 @@ class Gemm : public onnxruntime::Gemm { tGEMM.c = std::make_shared(); tGEMM.d = std::make_shared(); - tGEMM.a->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(X->Shape()), arm_compute::Format::F32)); - tGEMM.c->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(B->Shape()), arm_compute::Format::F32)); + tGEMM.a->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(A->Shape()), arm_compute::Format::F32)); + tGEMM.b->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(B->Shape()), arm_compute::Format::F32)); + tGEMM.c->allocator()->init(arm_compute::TensorInfo(cShape, arm_compute::Format::F32)); // dimensions are stored in the opposite order to ACL's - tGEMM.d->allocator()->init(arm_compute::TensorInfo(arm_compute::TensorShape(N, M), tGEMM.a->info()->format())); - - // transpose - if (!FC && trans_B_ == CblasTrans) { - auto trans_layer = std::make_shared(); - tGEMM.b->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32)); - - arm_compute::Tensor tmp; - tmp.allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32)); - - trans_layer->configure(&tmp, tGEMM.b.get()); - - const T* b_data = W->template Data(); - ACLImportMemory(tmp.allocator(), (void*)b_data, W->Shape().Size() * 4); - - tGEMM.b->allocator()->allocate(); - - trans_layer->run(); - } else { - tGEMM.b->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32)); - } - + tGEMM.d->allocator()->init(arm_compute::TensorInfo(arm_compute::TensorShape(N, M), arm_compute::Format::F32)); + tGEMM.mm_layer = ACLCreateMemoryManager(); if(FC) { auto layer = std::make_shared(tGEMM.mm_layer); - layer->configure(tGEMM.a.get(), tGEMM.b.get(), (B != nullptr && beta_ != 0) ? tGEMM.c.get() : nullptr, tGEMM.d.get()); + arm_compute::FullyConnectedLayerInfo fc_info; + fc_info.transpose_weights = trans_B_ == CblasTrans; + layer->configure(tGEMM.a.get(), tGEMM.b.get(), useC ? tGEMM.c.get() : nullptr, tGEMM.d.get(), fc_info); tGEMM.layer = std::move(layer); } else { -#ifdef GEMM_ACL - auto layer = std::make_shared(tGEMM.mm_layer); - layer->configure(tGEMM.a.get(), tGEMM.b.get(), (B != nullptr && beta_ != 0) ? tGEMM.c.get() : nullptr, tGEMM.d.get(), alpha_, beta_, arm_compute::GEMMInfo()); - tGEMM.layer = std::move(layer); -#else return onnxruntime::Gemm::Compute(context); -#endif - } - - // non-transpose - if (FC || trans_B_ != CblasTrans) { - const T* b_data = W->template Data(); - ACLImportMemory(tGEMM.b->allocator(), (void*)b_data, W->Shape().Size() * 4); } std::pair ret; @@ -142,41 +151,24 @@ class Gemm : public onnxruntime::Gemm { } else { //TODO: valildate shapes pGEMM = &it->second; - - // transpose - if (!FC && trans_B_ == CblasTrans) { -#ifndef CACHE_TRANSPOSED_DATA - auto trans_layer = std::make_shared(); - pGEMM->b->allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32)); - - arm_compute::Tensor tmp; - tmp.allocator()->init(arm_compute::TensorInfo(ACLTensorShape(W->Shape()), arm_compute::Format::F32)); - - trans_layer->configure(&tmp, pGEMM->b.get()); - - const T* b_data = W->template Data(); - ACLImportMemory(tmp.allocator(), (void*)b_data, W->Shape().Size() * 4); - - // allocate memory for b - pGEMM->b->allocator()->allocate(); - - trans_layer->run(); -#else - LOGS_DEFAULT(VERBOSE) << "Reuse transposed data" << std::endl; -#endif - } else { - const T* b_data = W->template Data(); - ACLImportMemory(pGEMM->b->allocator(), (void*)b_data, W->Shape().Size() * 4); - } } - const T* a_data = X->template Data(); - const T* c_data = B->template Data(); - const T* d_data = Y->template Data(); + const T* a_data = A->template Data(); + const T* b_data = B->template Data(); + T* d_data = D->template MutableData(); - ACLImportMemory(pGEMM->a->allocator(), (void*)a_data, X->Shape().Size() * 4); - ACLImportMemory(pGEMM->c->allocator(), (void*)c_data, B->Shape().Size() * 4); - ACLImportMemory(pGEMM->d->allocator(), (void*)d_data, Y->Shape().Size() * 4); + ACLImportMemory(pGEMM->a->allocator(), (void*)a_data, A->Shape().Size() * 4); + ACLImportMemory(pGEMM->b->allocator(), (void*)b_data, B->Shape().Size() * 4); + if(useC){ + const T* c_data = C->template Data(); + ACLImportMemory(pGEMM->c->allocator(), (void*)c_data, C->Shape().Size() * 4); + } + + if(D->Shape().Size() != 0 && pGEMM->d->info()->has_padding() ){ + pGEMM->d.get()->allocator()->allocate(); + } else { + ACLImportMemory(pGEMM->d->allocator(), (void*)d_data, D->Shape().Size() * 4); + } ACLPrintTensorShape("a", *pGEMM->a); ACLPrintTensorShape("b", *pGEMM->b); @@ -188,13 +180,12 @@ class Gemm : public onnxruntime::Gemm { pGEMM->layer->run(); pGEMM->mm_layer->clear(); + if(D->Shape().Size() != 0 && pGEMM->d->info()->has_padding() ){ + importDataFromTensor(pGEMM->d.get(), d_data); + } + pGEMM->a->allocator()->free(); -#ifdef CACHE_TRANSPOSED_DATA - if (trans_B_ != CblasTrans) - pGEMM->b->allocator()->free(); -#else pGEMM->b->allocator()->free(); -#endif pGEMM->c->allocator()->free(); pGEMM->d->allocator()->free(); diff --git a/onnxruntime/core/providers/acl/nn/conv.cc b/onnxruntime/core/providers/acl/nn/conv.cc index a4a42c908d..c7ccaf5f04 100644 --- a/onnxruntime/core/providers/acl/nn/conv.cc +++ b/onnxruntime/core/providers/acl/nn/conv.cc @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved. +// Copyright (c) 2019-2020, NXP Semiconductor, Inc. All rights reserved. // Licensed under the MIT License. #ifdef _WIN32 @@ -26,7 +26,8 @@ #ifdef ACL_1902 #include "arm_compute/core/NEON/kernels/NEDepthwiseConvolutionLayer3x3Kernel.h" -#else +#endif +#if defined(ACL_1905) || defined(ACL_1908) #include "arm_compute/runtime/NEON/functions/assembly/NEDepthwiseConvolutionAssemblyDispatch.h" #endif @@ -109,20 +110,20 @@ Status Conv::Compute(OpKernelContext* context) const { arm_compute::ActivationLayerInfo::ActivationFunction acl_activ_func; bool acl_activ_enabled = false; - if (conv_attrs_.activation == "Relu") { + if (activation_type == "Relu") { acl_activ_func = arm_compute::ActivationLayerInfo::ActivationFunction::RELU; acl_activ_enabled = true; - } else if (conv_attrs_.activation == "LeakyRelu") { + } else if (activation_type == "LeakyRelu") { acl_activ_func = arm_compute::ActivationLayerInfo::ActivationFunction::LEAKY_RELU; acl_activ_enabled = true; - } else if (conv_attrs_.activation == "Tanh") { + } else if (activation_type == "Tanh") { acl_activ_func = arm_compute::ActivationLayerInfo::ActivationFunction::TANH; acl_activ_enabled = true; - } else if (conv_attrs_.activation == "Sigmoid") { + } else if (activation_type == "Sigmoid") { acl_activ_func = arm_compute::ActivationLayerInfo::ActivationFunction::LOGISTIC; acl_activ_enabled = true; - } else if (!conv_attrs_.activation.empty()) { - ORT_NOT_IMPLEMENTED("Not implemented fused activation: ", conv_attrs_.activation); + } else if (!activation_type.empty()) { + ORT_NOT_IMPLEMENTED("Not implemented fused activation: ", activation_type); } if (it == Conv::convLayers.end()) { @@ -195,7 +196,8 @@ Status Conv::Compute(OpKernelContext* context) const { tconv.in->info()->data_type(), 1 /* depth multiplier */, tconv.in->info()->data_layout()); -#else +#endif +#if defined(ACL_1905) || defined(ACL_1908) bool optimizable = arm_compute::NEDepthwiseConvolutionAssemblyDispatch::is_optimized_supported(tconv.in->info(), tconv.k->info(), @@ -205,12 +207,18 @@ Status Conv::Compute(OpKernelContext* context) const { #endif if(optimizable) { //optimized depthwise convolution +#if defined(ACL_1902) || defined(ACL_1905) auto layer = std::make_shared(); +#endif +#ifdef ACL_1908 + auto layer = std::make_shared(); +#endif #ifdef ACL_1902 layer->configure(tconv.in.get(), tconv.k.get(), (B != nullptr) ? tconv.b.get() : nullptr, tconv.out.get(), aclPadStride, 1 /* depth multiplier */, acl_activ_enabled ? arm_compute::ActivationLayerInfo(acl_activ_func, conv_attrs_.alpha) : arm_compute::ActivationLayerInfo()); -#else +#endif +#if defined(ACL_1905) || defined(ACL_1908) layer->configure(tconv.in.get(), tconv.k.get(), (B != nullptr) ? tconv.b.get() : nullptr, tconv.out.get(), aclPadStride, 1 /* depth multiplier */, acl_activ_enabled ? arm_compute::ActivationLayerInfo(acl_activ_func, conv_attrs_.alpha) : arm_compute::ActivationLayerInfo(), @@ -225,7 +233,7 @@ Status Conv::Compute(OpKernelContext* context) const { ret = Conv::convLayers.insert(std::pair((OpKernel*)this, tconv)); return s; } -#endif +#endif //DEPTHWISE_CPU } else { if(tconv.k->info()->tensor_shape()[0] == 1 && tconv.k->info()->tensor_shape()[1] == 1) { //pointwise convolution diff --git a/onnxruntime/core/providers/acl/nn/conv.h b/onnxruntime/core/providers/acl/nn/conv.h index e4e413e454..7d4fc2b170 100644 --- a/onnxruntime/core/providers/acl/nn/conv.h +++ b/onnxruntime/core/providers/acl/nn/conv.h @@ -1,5 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. -// Copyright (c) 2019, NXP Semiconductor, Inc. All rights reserved. +// Copyright (c) 2019-2020, NXP Semiconductor, Inc. All rights reserved. // Licensed under the MIT License. #pragma once @@ -36,7 +36,7 @@ typedef struct typedef std::map::iterator ConvLayersIterator; template -class Conv final : public onnxruntime::Conv { +class Conv : public onnxruntime::Conv { public: explicit Conv(const OpKernelInfo& info) : onnxruntime::Conv(info), conv_attrs_(info) { provider_ = (const_cast( @@ -49,10 +49,11 @@ class Conv final : public onnxruntime::Conv { Status Compute(OpKernelContext* context) const override; - private: + protected: static thread_local std::map convLayers; ConvAttributes conv_attrs_; ACLExecutionProvider* provider_; + std::string activation_type; arm_compute::TensorShape ACLReshapeWeightsDepthwise(arm_compute::Tensor* kernel) const; }; diff --git a/onnxruntime/core/providers/acl/nn/fused_conv.cc b/onnxruntime/core/providers/acl/nn/fused_conv.cc new file mode 100644 index 0000000000..740e130915 --- /dev/null +++ b/onnxruntime/core/providers/acl/nn/fused_conv.cc @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) 2020, NXP Semiconductor, Inc. All rights reserved. +// Licensed under the MIT License. + +#ifdef _WIN32 +#pragma warning(disable : 4244) +#endif +#include +#include + +#include "core/providers/acl/nn/conv.h" +#include "core/providers/acl/acl_common.h" +#include "core/providers/acl/acl_fwd.h" +#include "core/providers/acl/acl_execution_provider.h" + +namespace onnxruntime { +namespace acl{ + +template +class FusedConv final : public acl::Conv { + public: + explicit FusedConv(const OpKernelInfo& info) : acl::Conv(info) { + ORT_ENFORCE(info.GetAttr("activation", &(this->activation_type)).IsOK()); + // printf("fused\n"); + } + // Status Compute(OpKernelContext* context) const override; +}; + +ONNX_OPERATOR_TYPED_KERNEL_EX( + FusedConv, + kMSDomain, + 1, + float, + kAclExecutionProvider, + KernelDefBuilder().TypeConstraint("T", DataTypeImpl::GetTensorType()), + FusedConv); + +} // namespace acl +} // namespace onnxruntime