diff --git a/.github/workflows/runtime-build.yml b/.github/workflows/runtime-build.yml
index 3939046ec3..b54f4a5bfe 100644
--- a/.github/workflows/runtime-build.yml
+++ b/.github/workflows/runtime-build.yml
@@ -95,7 +95,7 @@ jobs:
strategy:
matrix:
config:
- - { name: riscv64-linux, shell: bash, arch: riscv64, toolchain: riscv64-unknown-linux, toolchain_env: RISCV_ROOT_PATH, toolchain_file: riscv64-unknown-linux-gnu-12.0.1, qemu: qemu-riscv64, loader_args: '-cpu;rv64,v=true,Zfh=true,vlen=128,elen=64,vext_spec=v1.0;-L', cmakeArgs: '', buildType: Release }
+ - {name: riscv64-linux, shell: bash, arch: riscv64, toolchain: riscv64-unknown-linux, toolchain_env: RISCV_ROOT_PATH, toolchain_file: riscv64-unknown-linux-gnu-13.2.0, qemu: qemu-riscv64, loader_args: '-cpu;rv64,v=true,Zfh=true,vlen=128,elen=64,vext_spec=v1.0;-L', cmakeArgs: '', buildType: Release}
steps:
- uses: actions/checkout@v3
@@ -151,4 +151,4 @@ jobs:
with:
name: nncaseruntime-${{matrix.config.name}}
path: ${{github.workspace}}/install
- if-no-files-found: error
\ No newline at end of file
+ if-no-files-found: error
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 636db14888..ce82d0c916 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,6 +57,10 @@ if(ENABLE_DUMP_MEM)
add_definitions(-DDUMP_MEM)
endif()
+if(ENABLE_OPENMP)
+ add_definitions(-DENABLE_OPENMP)
+endif()
+
if(NOT CMAKE_TOOLCHAIN_FILE)
include(toolchains/native.toolchain.cmake)
endif()
@@ -283,5 +287,7 @@ if(BUILD_TESTING)
endif()
# Modules
+add_subdirectory(modules/ncnn)
+#add_subdirectory(modules/k210)
#add_subdirectory(modules/vulkan)
diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake
index 2827003ab2..ab7bc5b2ed 100644
--- a/cmake/dependencies.cmake
+++ b/cmake/dependencies.cmake
@@ -1,3 +1,6 @@
+find_package(gsl-lite REQUIRED)
+find_package(ncnn REQUIRED)
+
if (ENABLE_OPENMP)
find_package(OpenMP COMPONENTS CXX REQUIRED)
endif ()
diff --git a/conanfile.py b/conanfile.py
index 6ddcfc9ce3..a8cf6991d0 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -35,13 +35,14 @@ class nncaseConan(ConanFile):
"python": True,
"vulkan_runtime": False
}
-
+
def imports(self):
if self.settings.os == 'Windows':
self.copy("nethost.dll", "bin", "bin")
self.copy("ortki.dll", "bin", "bin")
def requirements(self):
+ self.requires('ncnn/20240102')
if self.options.tests:
self.requires('gtest/1.10.0')
self.requires('ortki/0.0.3')
@@ -64,7 +65,6 @@ def configure(self):
if self.settings.os == 'Windows':
self.settings.compiler.toolset = 'ClangCL'
-
if not self.options.runtime:
if self.settings.os == 'Windows':
self.options["nethost"].shared = True
diff --git a/modules/Nncase.Modules.CPU/Targets/CPUTarget.cs b/modules/Nncase.Modules.CPU/Targets/CPUTarget.cs
index ad654b9b54..4994fad0b6 100644
--- a/modules/Nncase.Modules.CPU/Targets/CPUTarget.cs
+++ b/modules/Nncase.Modules.CPU/Targets/CPUTarget.cs
@@ -61,6 +61,73 @@ public void RegisterTargetInDependentPass(IPassManager passManager, CompileOptio
///
public void RegisterTargetDependentPass(IPassManager passManager, CompileOptions options)
{
+ passManager.AddWithName("LowerNcnnIR").Configure(p =>
+ {
+ p.Add();
+
+ // Fold reduce
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+
+ // single op
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+ p.Add();
+
+ p.Add();
+ p.Add();
+ p.Add();
+
+ // p.Add(); // ncnn dequantize int to float.
+ });
+
+ passManager.AddWithName("RemoveGlueOp").Configure(p =>
+ {
+ p.Add();
+ p.Add();
+ p.Add();
+ });
+
+ passManager.AddWithName("RemoveSingleSqueezeAndUnsqueeze").Configure(p =>
+ {
+ p.Add();
+ p.Add();
+ });
}
///
diff --git a/modules/Nncase.Modules.CPU/packages.lock.json b/modules/Nncase.Modules.CPU/packages.lock.json
index dded23c243..08721aae0d 100644
--- a/modules/Nncase.Modules.CPU/packages.lock.json
+++ b/modules/Nncase.Modules.CPU/packages.lock.json
@@ -190,10 +190,18 @@
"nncase.io": {
"type": "Project"
},
+ "nncase.modules.ncnn": {
+ "type": "Project",
+ "dependencies": {
+ "Nncase.CodeGen": "[1.0.0, )",
+ "Nncase.Passes": "[1.0.0, )"
+ }
+ },
"nncase.modules.stackvm": {
"type": "Project",
"dependencies": {
"Nncase.CodeGen": "[1.0.0, )",
+ "Nncase.Modules.Ncnn": "[1.0.0, )",
"Nncase.Passes": "[1.0.0, )"
}
},
diff --git a/modules/Nncase.Modules.Ncnn/ArgsStruct/args.cs b/modules/Nncase.Modules.Ncnn/ArgsStruct/args.cs
new file mode 100644
index 0000000000..0bbbaf459d
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/ArgsStruct/args.cs
@@ -0,0 +1,362 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+namespace Nncase.ArgsStruct;
+
+///
+/// Ncnn Pooling arguments.
+///
+public record PoolingArgs
+{
+ public PoolingArgs(int poolingType, int kernelW, int kernelH, int strideW, int strideH, int padLeft, int padRight, int padTop, int padBottom, bool globalPooling, int padMode, bool avgPoolCountIncludePad, bool adaptivePooling, int outW, int outH, bool ceilMode)
+ {
+ PoolingType = poolingType;
+ KernelH = kernelH;
+ KernelW = kernelW;
+ StrideH = strideH;
+ StrideW = strideW;
+ PadLeft = padLeft;
+ PadRight = padRight;
+ PadTop = padTop;
+ PadBottom = padBottom;
+ GlobalPooling = globalPooling;
+ PadMode = padMode;
+ AvgPoolCountIncludePad = avgPoolCountIncludePad;
+ AdaptivePooling = adaptivePooling;
+ OutW = outW;
+ OutH = outH;
+ CeilMode = ceilMode;
+ }
+
+ public int PoolingType { get; }
+
+ public int KernelW { get; }
+
+ public int KernelH { get; }
+
+ public int StrideW { get; }
+
+ public int StrideH { get; }
+
+ public int PadLeft { get; }
+
+ public int PadRight { get; }
+
+ public int PadTop { get; }
+
+ public int PadBottom { get; }
+
+ public bool GlobalPooling { get; }
+
+ public int PadMode { get; }
+
+ public bool AvgPoolCountIncludePad { get; }
+
+ public bool AdaptivePooling { get; }
+
+ public int OutW { get; }
+
+ public int OutH { get; }
+
+ public bool CeilMode { get; }
+}
+
+public record ReductionArgs
+{
+ public ReductionArgs(int opType, int reduceAll, float coeff, long[] axes, int keepdims)
+ {
+ OpType = opType;
+ ReduceAll = reduceAll;
+ Coeff = coeff;
+ Axes = axes;
+ Keepdims = keepdims;
+ }
+
+ public int OpType { get; }
+
+ public int ReduceAll { get; }
+
+ public float Coeff { get; }
+
+ public long[] Axes { get; }
+
+ public int Keepdims { get; }
+}
+
+public record CropArgs
+{
+ public CropArgs(int[] starts, int[] ends, int[] axes)
+ {
+ Woffset = 0;
+ Hoffset = 0;
+ Doffset = 0;
+ Coffset = 0;
+ Outw = 0;
+ Outh = 0;
+ Outd = 0;
+ Outc = 0;
+ Woffset2 = 0;
+ Hoffset2 = 0;
+ Doffset2 = 0;
+ Coffset2 = 0;
+ Starts = starts;
+ Ends = ends;
+ Axes = axes;
+ }
+
+ public CropArgs(int woffset, int hoffset, int doffset, int coffset, int outw, int outh, int outd, int outc, int woffset2, int hoffset2, int doffset2, int coffset2)
+ {
+ Woffset = woffset;
+ Hoffset = hoffset;
+ Doffset = doffset;
+ Coffset = coffset;
+ Outw = outw;
+ Outh = outh;
+ Outd = outd;
+ Outc = outc;
+ Woffset2 = woffset2;
+ Hoffset2 = hoffset2;
+ Doffset2 = doffset2;
+ Coffset2 = coffset2;
+ Starts = null;
+ Ends = null;
+ Axes = null;
+ }
+
+ public int Woffset { get; }
+
+ public int Hoffset { get; }
+
+ public int Doffset { get; }
+
+ public int Coffset { get; }
+
+ public int Outw { get; }
+
+ public int Outh { get; }
+
+ public int Outd { get; }
+
+ public int Outc { get; }
+
+ public int Woffset2 { get; }
+
+ public int Hoffset2 { get; }
+
+ public int Doffset2 { get; }
+
+ public int Coffset2 { get; }
+
+ public int[]? Starts { get; }
+
+ public int[]? Ends { get; }
+
+ public int[]? Axes { get; }
+}
+
+public record ConvTransposeArgs
+{
+ public ConvTransposeArgs(Tensor? weightData = null, float[]? biasData = null, int numOutput = default, int kernelW = default, int kernelH = default, int dilationW = default, int dilationH = default, int strideW = default, int strideH = default, int padLeft = default, int padRight = default, int padTop = default, int padBottom = default, int biasTerm = default, int weightDataSize = default, int activationType = default, float[]? activationParams = null, int outputPadRight = default, int outputPadBottom = default, int outputW = default, int outputH = default)
+ {
+ WeightData = weightData!;
+ BiasData = biasData!;
+ NumOutput = numOutput;
+ KernelW = kernelW;
+ KernelH = kernelH;
+ DilationW = dilationW;
+ DilationH = dilationH;
+ StrideW = strideW;
+ StrideH = strideH;
+ PadLeft = padLeft;
+ PadRight = padRight;
+ PadTop = padTop;
+ PadBottom = padBottom;
+ BiasTerm = biasTerm;
+ WeightDataSize = weightDataSize;
+ ActivationType = activationType;
+ ActivationParams = activationParams!;
+ OutputPadRight = outputPadRight;
+ OutputPadBottom = outputPadBottom;
+ OutputW = outputW;
+ OutputH = outputH;
+ }
+
+ public Tensor WeightData { get; }
+
+ public float[] BiasData { get; }
+
+ public int NumOutput { get; }
+
+ public int KernelW { get; }
+
+ public int KernelH { get; }
+
+ public int DilationW { get; }
+
+ public int DilationH { get; }
+
+ public int StrideW { get; }
+
+ public int StrideH { get; }
+
+ public int PadLeft { get; }
+
+ public int PadRight { get; }
+
+ public int PadTop { get; }
+
+ public int PadBottom { get; }
+
+ public int BiasTerm { get; }
+
+ public int WeightDataSize { get; }
+
+ public int ActivationType { get; }
+
+ public float[] ActivationParams { get; }
+
+ public int OutputPadRight { get; }
+
+ public int OutputPadBottom { get; }
+
+ public int OutputW { get; }
+
+ public int OutputH { get; }
+
+ public override string ToString() => $"{nameof(WeightData)}: {string.Join("_", WeightData.Shape.ToValueArray())}, {nameof(NumOutput)}: {NumOutput}, {nameof(KernelW)}: {KernelW}, {nameof(KernelH)}: {KernelH}, {nameof(DilationW)}: {DilationW}, {nameof(DilationH)}: {DilationH}, {nameof(StrideW)}: {StrideW}, {nameof(StrideH)}: {StrideH}, {nameof(PadLeft)}: {PadLeft}, {nameof(PadRight)}: {PadRight}, {nameof(PadTop)}: {PadTop}, {nameof(PadBottom)}: {PadBottom}, {nameof(BiasTerm)}: {BiasTerm}, {nameof(WeightDataSize)}: {WeightDataSize}, {nameof(OutputPadRight)}: {OutputPadRight}, {nameof(OutputPadBottom)}: {OutputPadBottom}, {nameof(OutputW)}: {OutputW}, {nameof(OutputH)}: {OutputH}";
+}
+
+public record ConvArgs
+{
+ public ConvArgs(float[] weightData, float[] biasData, int numOutput, int kernelW, int kernelH, int dilationW, int dilationH, int strideW, int strideH, int padLeft, int padRight, int padTop, int padBottom, float padValue, int biasTerm, int weightDataSize, int int8ScaleTerm, int activationType, float[] activationParams, int dynamicWeight, int groups)
+ {
+ WeightData = weightData;
+ BiasData = biasData;
+ NumOutput = numOutput;
+ KernelW = kernelW;
+ KernelH = kernelH;
+ DilationW = dilationW;
+ DilationH = dilationH;
+ StrideW = strideW;
+ StrideH = strideH;
+ PadLeft = padLeft;
+ PadRight = padRight;
+ PadTop = padTop;
+ PadBottom = padBottom;
+ PadValue = padValue;
+ BiasTerm = biasTerm;
+ WeightDataSize = weightDataSize;
+ Int8ScaleTerm = int8ScaleTerm;
+ ActivationType = activationType;
+ ActivationParams = activationParams;
+ DynamicWeight = dynamicWeight;
+ Groups = groups;
+ }
+
+ ///
+ /// Gets input.
+ ///
+ public float[] WeightData { get; }
+
+ ///
+ /// Gets BiasData.
+ ///
+ public float[] BiasData { get; }
+
+ ///
+ /// Gets NumOutput.
+ ///
+ public int NumOutput { get; }
+
+ ///
+ /// Gets KernelW.
+ ///
+ public int KernelW { get; }
+
+ ///
+ /// Gets KernelH.
+ ///
+ public int KernelH { get; }
+
+ ///
+ /// Gets DilationW.
+ ///
+ public int DilationW { get; }
+
+ ///
+ /// Gets DilationH.
+ ///
+ public int DilationH { get; }
+
+ ///
+ /// Gets StrideW.
+ ///
+ public int StrideW { get; }
+
+ ///
+ /// Gets StrideH.
+ ///
+ public int StrideH { get; }
+
+ ///
+ /// Gets PadLeft.
+ ///
+ public int PadLeft { get; }
+
+ ///
+ /// Gets PadRight.
+ ///
+ public int PadRight { get; }
+
+ ///
+ /// Gets PadTop.
+ ///
+ public int PadTop { get; }
+
+ ///
+ /// Gets PadBottom.
+ ///
+ public int PadBottom { get; }
+
+ ///
+ /// Gets PadValue.
+ ///
+ public float PadValue { get; }
+
+ ///
+ /// Gets BiasTerm.
+ ///
+ public int BiasTerm { get; }
+
+ ///
+ /// Gets WeightDataSize.
+ ///
+ public int WeightDataSize { get; }
+
+ ///
+ /// Gets Int8ScaleTerm.
+ ///
+ public int Int8ScaleTerm { get; }
+
+ ///
+ /// Gets ActivationType.
+ ///
+ public int ActivationType { get; }
+
+ ///
+ /// Gets ActivationParams.
+ ///
+ public float[] ActivationParams { get; }
+
+ ///
+ /// Gets DynamicWeight.
+ ///
+ public int DynamicWeight { get; }
+
+ ///
+ /// Gets Groups.
+ ///
+ public int Groups { get; }
+
+ public override string ToString() => $"{nameof(WeightData)}: {WeightData}, {nameof(BiasData)}: {BiasData}, {nameof(NumOutput)}: {NumOutput}, {nameof(KernelW)}: {KernelW}, {nameof(KernelH)}: {KernelH}, {nameof(DilationW)}: {DilationW}, {nameof(DilationH)}: {DilationH}, {nameof(StrideW)}: {StrideW}, {nameof(StrideH)}: {StrideH}, {nameof(PadLeft)}: {PadLeft}, {nameof(PadRight)}: {PadRight}, {nameof(PadTop)}: {PadTop}, {nameof(PadBottom)}: {PadBottom}, {nameof(PadValue)}: {PadValue}, {nameof(BiasTerm)}: {BiasTerm}, {nameof(WeightDataSize)}: {WeightDataSize}, {nameof(Int8ScaleTerm)}: {Int8ScaleTerm}, {nameof(ActivationType)}: {ActivationType}, {nameof(ActivationParams)}: {ActivationParams}, {nameof(DynamicWeight)}: {DynamicWeight}, {nameof(Groups)}: {Groups}";
+}
diff --git a/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnEmitter.cs b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnEmitter.cs
new file mode 100644
index 0000000000..47f24950a5
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnEmitter.cs
@@ -0,0 +1,612 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using DryIoc.ImTools;
+using Microsoft.Toolkit.HighPerformance;
+using Nncase.ArgsStruct;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using Nncase.Runtime.Ncnn;
+
+namespace Nncase.CodeGen.Ncnn;
+
+internal class NcnnEmitter
+{
+ private static long _s_pos;
+ private readonly NcnnModel _model;
+ private readonly BinaryWriter _binWriter;
+ private readonly List? _rData;
+
+ public NcnnEmitter(BinaryWriter binWriter)
+ {
+ _model = new NcnnModel();
+ _binWriter = binWriter;
+ _rData = new List();
+ }
+
+ public List? GetRData()
+ {
+ return _rData;
+ }
+
+ public void SaveParam(Stream paramStream)
+ {
+ using var sw = new StreamWriter(paramStream, Encoding.ASCII, leaveOpen: true);
+ _model.Serialize(sw);
+ }
+
+ public void SaveBin(string dumpPath, uint id)
+ {
+ if (id == 0)
+ {
+ _s_pos = 0;
+ }
+
+ using (var fileStream = File.Create(Path.Join(dumpPath, $"/ncnn_{id}.bin")))
+ {
+ long now = _binWriter.BaseStream.Position;
+ if (now != 0 && now != _s_pos)
+ {
+ using (var tempStream = new MemoryStream())
+ {
+ _binWriter.BaseStream.Position = _s_pos; // 重置位置到新内容的开始
+ _binWriter.BaseStream.CopyTo(tempStream, (int)(now - _s_pos));
+ tempStream.Position = 0;
+ tempStream.CopyTo(fileStream);
+ }
+
+ _s_pos = _binWriter.BaseStream.Position;
+ }
+ }
+ }
+
+ public void Input(string name) =>
+ AddLayer("Input", name, Array.Empty(), new[] { name }, null);
+
+ public void Softmax(string name, string input, int axis) =>
+ AddLayer("Softmax", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = axis }, // axis
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = 1 }, // fixbug0
+ });
+
+ public void Unary(string name, string input, UnaryOperationType opTypes) =>
+ AddLayer("UnaryOp", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = (int)opTypes },
+ });
+
+ public void BatchNorm(string name, string input, int channels, float eps, float[] slopeData, float[] meanData, float[] varData, float[] biasData/*, float[] aData, float[] bData*/)
+ {
+ AddLayer("BatchNorm", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = channels },
+ [1] = new ParamValue { Kind = ParamKind.Float, FloatValue = eps },
+ });
+
+ WriteFloatArray(slopeData);
+ WriteFloatArray(meanData);
+ WriteFloatArray(varData);
+ WriteFloatArray(biasData);
+ }
+
+ public void Binary(string name, string inputA, string inputB, BinaryOperationType opTypes, int lOrR, float[] constInput, int[] constShape)
+ {
+ var inputList = new[] { inputA, inputB };
+
+ if (constInput != null && constShape != null)
+ {
+ if (lOrR == 1)
+ {
+ inputList[0] = name + "_memorydata";
+ }
+ else
+ {
+ inputList[1] = name + "_memorydata";
+ }
+
+ var paramDict = new ParamDict();
+ for (int i = 0; i < constShape.Length; i++)
+ {
+ paramDict[i] = new ParamValue { Kind = ParamKind.Int, IntValue = constShape[constShape.Length - 1 - i] };
+ }
+
+ AddLayer("MemoryData", name + "_memorydata", Array.Empty(), new[] { name + "_memorydata" }, paramDict);
+
+ WriteFloatArray(constInput);
+ }
+
+ AddLayer("BinaryOp", name, inputList, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = (int)opTypes },
+ });
+ }
+
+ public void Celu(string name, string input, float alpha) =>
+ AddLayer("CELU", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Float, FloatValue = alpha }, // alpha
+ });
+
+ public void Clip(string name, string input, float min, float max) =>
+ AddLayer("Clip", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Float, FloatValue = min }, // min
+ [1] = new ParamValue { Kind = ParamKind.Float, FloatValue = max }, // max
+ });
+
+ public void Concat(string name, string[] input, int axis)
+ {
+ AddLayer("Concat", name, input, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = axis }, // axis
+ });
+ }
+
+ public void Conv(string name, string input, ConvArgs args)
+ {
+ var actData = new List { args.ActivationParams.Length };
+ actData.AddRange(args.ActivationParams);
+
+ var param = new ParamDict();
+ param.Add(0, new ParamValue { Kind = ParamKind.Int, IntValue = args.NumOutput });
+ param.Add(1, new ParamValue { Kind = ParamKind.Int, IntValue = args.KernelW });
+ param.Add(11, new ParamValue { Kind = ParamKind.Int, IntValue = args.KernelH });
+ param.Add(2, new ParamValue { Kind = ParamKind.Int, IntValue = args.DilationW });
+ param.Add(12, new ParamValue { Kind = ParamKind.Int, IntValue = args.DilationH });
+ param.Add(3, new ParamValue { Kind = ParamKind.Int, IntValue = args.StrideW });
+ param.Add(13, new ParamValue { Kind = ParamKind.Int, IntValue = args.StrideH });
+ param.Add(4, new ParamValue { Kind = ParamKind.Int, IntValue = args.PadLeft });
+ param.Add(15, new ParamValue { Kind = ParamKind.Int, IntValue = args.PadRight });
+ param.Add(14, new ParamValue { Kind = ParamKind.Int, IntValue = args.PadTop });
+ param.Add(16, new ParamValue { Kind = ParamKind.Int, IntValue = args.PadBottom });
+ param.Add(18, new ParamValue { Kind = ParamKind.Float, FloatValue = args.PadValue });
+ param.Add(5, new ParamValue { Kind = ParamKind.Int, IntValue = args.BiasTerm });
+ param.Add(6, new ParamValue { Kind = ParamKind.Int, IntValue = args.WeightDataSize });
+ param.Add(7, new ParamValue { Kind = ParamKind.Int, IntValue = args.Groups });
+ param.Add(8, new ParamValue { Kind = ParamKind.Int, IntValue = args.Int8ScaleTerm });
+ param.Add(9, new ParamValue { Kind = ParamKind.Int, IntValue = args.ActivationType });
+ param.Add(-10, new ParamValue { Kind = ParamKind.ArrayOfIntOrFloat, TensorValue = actData.ToArray() });
+ param.Add(19, new ParamValue { Kind = ParamKind.Int, IntValue = args.DynamicWeight });
+
+ if (args.Groups > 1)
+ {
+ AddLayer("ConvolutionDepthWise", name, new[] { input }, new[] { name }, param);
+ }
+ else
+ {
+ AddLayer("Convolution", name, new[] { input }, new[] { name }, param);
+ }
+
+ WriteFloatArray(new float[] { 0 }); // quantize flag [Not exist in ncnn op.md]
+ WriteFloatArray(args.WeightData);
+ WriteFloatArray(args.BiasData);
+ }
+
+ public void Cumsum(string name, string input, int axis)
+ {
+ AddLayer("CumulativeSum", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = axis }, // axis
+ });
+ }
+
+ public void Elu(string name, string input, float alpha) =>
+ AddLayer("ELU", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Float, FloatValue = alpha }, // alpha
+ });
+
+ public void Erf(string name, string input) => AddLayer("Erf", name, new[] { input }, new[] { name }, new ParamDict { });
+
+ public void HardSigmoid(string name, string input, float alpha, float beta) =>
+ AddLayer("HardSigmoid", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Float, FloatValue = alpha }, // alpha
+ [1] = new ParamValue { Kind = ParamKind.Float, FloatValue = beta }, // beta
+ });
+
+ public void HardSwish(string name, string input, float alpha, float beta) =>
+ AddLayer("HardSwish", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Float, FloatValue = alpha }, // alpha
+ [1] = new ParamValue { Kind = ParamKind.Float, FloatValue = beta }, // beta
+ });
+
+ public void InstanceNorm(string name, string input, int channels, float eps, int affine, float[] gammaData, float[] betaData)
+ {
+ AddLayer("InstanceNorm", name, new[] { input }, new[] { name }, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = channels }, // channels
+ [1] = new ParamValue { Kind = ParamKind.Float, FloatValue = eps }, // eps
+ [2] = new ParamValue { Kind = ParamKind.Int, IntValue = affine }, // affine
+ });
+ WriteFloatArray(gammaData);
+ WriteFloatArray(betaData);
+ }
+
+ public void LayerNorm(string[] name, string input, int affineSize, float eps, int affine, float[] gammaData, float[] betaData)
+ {
+ AddLayer("LayerNorm", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = affineSize }, // affineSize
+ [1] = new ParamValue { Kind = ParamKind.Float, FloatValue = eps }, // eps
+ [2] = new ParamValue { Kind = ParamKind.Int, IntValue = affine }, // affine
+ });
+ WriteFloatArray(gammaData);
+ WriteFloatArray(betaData);
+ }
+
+ public void LRN(string[] name, string input, float alpha, float beta, float bias, int size) =>
+ AddLayer("LRN", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = 0 }, // region_type
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = size }, // size
+ [2] = new ParamValue { Kind = ParamKind.Float, FloatValue = alpha }, // alpha
+ [3] = new ParamValue { Kind = ParamKind.Float, FloatValue = beta }, // beta
+ [4] = new ParamValue { Kind = ParamKind.Float, FloatValue = bias }, // bias
+ });
+
+ public void LSTM(string[] name, string input, int hiddenSize, int weightDataSize, int direction, float[] w, float[] r, float[] b)
+ {
+ AddLayer("LSTM", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = hiddenSize },
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = weightDataSize },
+ [2] = new ParamValue { Kind = ParamKind.Int, IntValue = direction },
+ });
+ WriteFloatArray(new float[] { 0 });
+ WriteFloatArray(w);
+ WriteFloatArray(new float[] { 0 });
+ WriteFloatArray(b);
+ WriteFloatArray(new float[] { 0 });
+ WriteFloatArray(r);
+ }
+
+ public void Padding(string[] name, string input, int top, int bottom, int left, int right, int type, float value, int front, int behind)
+ {
+ AddLayer("Padding", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = top },
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = bottom },
+ [2] = new ParamValue { Kind = ParamKind.Int, IntValue = left },
+ [3] = new ParamValue { Kind = ParamKind.Int, IntValue = right },
+ [4] = new ParamValue { Kind = ParamKind.Int, IntValue = type },
+ [5] = new ParamValue { Kind = ParamKind.Float, FloatValue = value },
+
+ // [6] for perChannelPadDataSize.
+ [7] = new ParamValue { Kind = ParamKind.Int, IntValue = front },
+ [8] = new ParamValue { Kind = ParamKind.Int, IntValue = behind },
+ });
+
+ // TODO: confirm padValue is a tensor.
+ WriteFloatArray(new float[] { value });
+ }
+
+ public void Pooling(string[] name, string input, PoolingArgs poolingArgs)
+ {
+ AddLayer("Pooling", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.PoolingType },
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.KernelW },
+ [11] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.KernelH },
+ [2] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.StrideW },
+ [12] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.StrideH },
+ [3] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.PadLeft },
+ [14] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.PadRight },
+ [13] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.PadTop },
+ [15] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.PadBottom },
+ [4] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.GlobalPooling ? 1 : 0 },
+ [5] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.PadMode },
+ [6] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.AvgPoolCountIncludePad ? 1 : 0 },
+ [7] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.AdaptivePooling ? 1 : 0 },
+
+ // [8] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.OutH },
+ // [18] = new ParamValue { Kind = ParamKind.Int, IntValue = poolingArgs.OutH },
+ });
+ }
+
+ public void PReLU(string[] name, string input, float[] slope)
+ {
+ AddLayer("PReLU", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = slope.Length },
+ });
+
+ WriteFloatArray(slope);
+ }
+
+ public void Reduction(string[] name, string input, ReductionArgs reductionArgs)
+ {
+ var args = new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = reductionArgs.OpType },
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = reductionArgs.ReduceAll },
+ };
+
+ if (reductionArgs.Axes.Length > 0)
+ {
+ var axesSizeAndData = new List { reductionArgs.Axes.Length };
+ foreach (var item in reductionArgs.Axes)
+ {
+ axesSizeAndData.Add(item);
+ }
+
+ args.Add(-3, new ParamValue { Kind = ParamKind.ArrayOfInt, TensorValue = axesSizeAndData.ToArray() });
+ }
+
+ args.Add(4, new ParamValue { Kind = ParamKind.Int, IntValue = reductionArgs.Keepdims });
+ args.Add(5, new ParamValue { Kind = ParamKind.Int, IntValue = 1 });
+ AddLayer("Reduction", name[0], new[] { input }, name, args);
+ }
+
+ public void Reshape(string[] name, string input, int[] newshape)
+ {
+ var args = new ParamDict();
+ List index;
+ if (newshape.Length < 4)
+ {
+ index = Enumerable.Range(0, newshape.Length).ToList();
+ }
+ else
+ {
+ index = new List { 0, 1, 11, 2 };
+ }
+
+ int i = 0;
+ foreach (int item in newshape.Reverse())
+ {
+ args.Add(index[i], new ParamValue { Kind = ParamKind.Int, IntValue = item });
+ i += 1;
+ }
+
+ AddLayer("Reshape", name[0], new[] { input }, name, args);
+ }
+
+ public void SELU(string[] name, string input, float alpha, float gamma)
+ {
+ AddLayer("SELU", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Float, FloatValue = alpha },
+ [1] = new ParamValue { Kind = ParamKind.Float, FloatValue = gamma },
+ });
+ }
+
+ public void Crop(string[] name, string input, CropArgs cropArgs)
+ {
+ var args = new ParamDict();
+
+ // TODO: if need to fit torch crop, add other args into paramDict.
+ if (cropArgs.Axes!.Length > 0)
+ {
+ var startData = new List { cropArgs.Axes.Length };
+ var endData = new List { cropArgs.Axes.Length };
+ var axisData = new List { cropArgs.Axes.Length };
+ for (int i = 0; i < cropArgs.Axes.Length; i++)
+ {
+ startData.Add(cropArgs.Starts![i]);
+ endData.Add(cropArgs.Ends![i]);
+ axisData.Add(cropArgs.Axes![i]);
+ }
+
+ args.Add(-9, new ParamValue { Kind = ParamKind.ArrayOfInt, TensorValue = startData.ToArray() });
+ args.Add(-10, new ParamValue { Kind = ParamKind.ArrayOfInt, TensorValue = endData.ToArray() });
+ args.Add(-11, new ParamValue { Kind = ParamKind.ArrayOfInt, TensorValue = axisData.ToArray() });
+ }
+
+ AddLayer("Crop", name[0], new[] { input }, name, args);
+ }
+
+ public void Sigmoid(string[] name, string input)
+ {
+ AddLayer("Sigmoid", name[0], new[] { input }, name);
+ }
+
+ public void Softplus(string[] name, string input)
+ {
+ AddLayer("Softplus", name[0], new[] { input }, name);
+ }
+
+ public void Slice(string[] name, string input, int[] slices, int axis)
+ {
+ var sliceData = new List { slices.Length };
+ sliceData.AddRange(slices);
+ AddLayer("Slice", name[0], new[] { input }, name, new ParamDict
+ {
+ [-0] = new ParamValue { Kind = ParamKind.ArrayOfInt, TensorValue = sliceData.ToArray() },
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = axis },
+ });
+ }
+
+ public void Tile(string[] name, string input, int[] repeats)
+ {
+ var repeatsData = new List { repeats.Length };
+ repeatsData.AddRange(repeats);
+ AddLayer("Tile", name[0], new[] { input }, name, new ParamDict
+ {
+ [-2] = new ParamValue { Kind = ParamKind.ArrayOfInt, TensorValue = repeatsData.ToArray() },
+ });
+ }
+
+ public void Permute(string[] name, string input, int orderType)
+ {
+ AddLayer("Permute", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = orderType },
+ });
+ }
+
+ public void Matmul(string[] name, string inputA, string inputB, int lOrR, float[] constInput, int[] constShape)
+ {
+ var inputList = new[] { inputA, inputB };
+
+ if (constInput != null && constShape != null)
+ {
+ if (lOrR == 1)
+ {
+ inputList[0] = name[0] + "_memorydata";
+ }
+ else
+ {
+ inputList[1] = name[0] + "_memorydata";
+ }
+
+ var paramDict = new ParamDict();
+ for (int i = 0; i < constShape.Length; i++)
+ {
+ int index = i switch
+ {
+ 0 => 0,
+ 1 => 1,
+ 2 when constShape.Length == 3 => 2,
+ 2 when constShape.Length == 4 => 11,
+ 3 when constShape.Length == 4 => 2,
+ _ => throw new NotSupportedException("Only support less than 5D"),
+ };
+ paramDict[index] = new ParamValue { Kind = ParamKind.Int, IntValue = constShape[constShape.Length - 1 - i] };
+ }
+
+ AddLayer("MemoryData", name[0] + "_memorydata", Array.Empty(), new[] { name[0] + "_memorydata" }, paramDict);
+
+ WriteFloatArray(constInput);
+ }
+
+ AddLayer("MatMul", name[0], inputList, name, null);
+ }
+
+ public void ConvTranspose(string[] name, string input, ConvTransposeArgs args)
+ {
+ var actData = new List { args.ActivationParams.Length };
+ actData.AddRange(args.ActivationParams);
+
+ AddLayer("Deconvolution", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = args.NumOutput },
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = args.KernelW },
+ [11] = new ParamValue { Kind = ParamKind.Int, IntValue = args.KernelH },
+ [2] = new ParamValue { Kind = ParamKind.Int, IntValue = args.DilationW },
+ [12] = new ParamValue { Kind = ParamKind.Int, IntValue = args.DilationH },
+ [3] = new ParamValue { Kind = ParamKind.Int, IntValue = args.StrideW },
+ [13] = new ParamValue { Kind = ParamKind.Int, IntValue = args.StrideH },
+ [4] = new ParamValue { Kind = ParamKind.Int, IntValue = args.PadLeft },
+ [14] = new ParamValue { Kind = ParamKind.Int, IntValue = args.PadTop },
+ [15] = new ParamValue { Kind = ParamKind.Int, IntValue = args.PadRight },
+ [16] = new ParamValue { Kind = ParamKind.Int, IntValue = args.PadBottom },
+
+ [5] = new ParamValue { Kind = ParamKind.Int, IntValue = args.BiasTerm },
+ [6] = new ParamValue { Kind = ParamKind.Int, IntValue = args.WeightDataSize },
+
+ // [9] = new ParamValue { Kind = ParamKind.Int, IntValue = args.ActivationType },
+ // [-10] = new ParamValue { Kind = ParamKind.ArrayOfIntOrFloat, TensorValue = actData.ToArray() },
+ //
+ // [18] = new ParamValue { Kind = ParamKind.Int, IntValue = args.OutputPadRight },
+ // [19] = new ParamValue { Kind = ParamKind.Int, IntValue = args.OutputPadBottom },
+ // [20] = new ParamValue { Kind = ParamKind.Int, IntValue = args.OutputW },
+ // [21] = new ParamValue { Kind = ParamKind.Int, IntValue = args.OutputH },
+ });
+ WriteFloatArray(new float[] { 0 }); // quantize flag [Not exist in ncnn op.md]
+ WriteFloatArray(args.WeightData.ToArray());
+ WriteFloatArray(args.BiasData);
+ }
+
+ public void Cast(string[] name, string input, int fromType, int toType)
+ {
+ AddLayer("Cast", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = fromType },
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = toType },
+ });
+ }
+
+ public void GELU(string[] name, string input)
+ {
+ AddLayer("GELU", name[0], new[] { input }, name, null);
+ }
+
+ public void Dequantize(string[] name, string input, float[] scale, float[] bias)
+ {
+ AddLayer("Dequantize", name[0], new[] { input }, name, new ParamDict
+ {
+ [0] = new ParamValue { Kind = ParamKind.Int, IntValue = scale.Length },
+ [1] = new ParamValue { Kind = ParamKind.Int, IntValue = bias.Length },
+ });
+
+ WriteFloatArray(scale);
+ WriteFloatArray(bias);
+ }
+
+ public void Squeeze(string[] name, string input, int[] dims)
+ {
+ var repeatsData = new List { dims.Length };
+ repeatsData.AddRange(dims);
+ var args = new ParamDict();
+ if (dims.Length == 0)
+ {
+ args.Add(0, new ParamValue { Kind = ParamKind.Int, IntValue = 1 });
+ args.Add(1, new ParamValue { Kind = ParamKind.Int, IntValue = 1 });
+ args.Add(2, new ParamValue { Kind = ParamKind.Int, IntValue = 1 });
+ }
+ else
+ {
+ args.Add(-3, new ParamValue { Kind = ParamKind.ArrayOfInt, TensorValue = repeatsData.ToArray() });
+ }
+
+ AddLayer("Squeeze", name[0], new[] { input }, name, args);
+ }
+
+ public void Unsqueeze(string[] name, string input, int[] dims)
+ {
+ var repeatsData = new List { dims.Length };
+ repeatsData.AddRange(dims);
+ AddLayer("ExpandDims", name[0], new[] { input }, name, new ParamDict
+ {
+ [-3] = new ParamValue { Kind = ParamKind.ArrayOfInt, TensorValue = repeatsData.ToArray() },
+ });
+ }
+
+ private void AddLayer(string type, string name, string[] bottoms, string[] tops, ParamDict? paramDict = null, int layerType = 1)
+ {
+ var layer = new NcnnLayer(type, name, bottoms.Length, tops.Length);
+ if (paramDict != null)
+ {
+ layer.ParamDict = paramDict;
+ }
+
+ for (int i = 0; i < bottoms.Length; i++)
+ {
+ layer.Bottoms[i] = new NcnnTensor { Name = bottoms[i] };
+ }
+
+ for (int i = 0; i < tops.Length; i++)
+ {
+ layer.Tops[i] = new NcnnTensor { Name = tops[i] };
+ }
+
+ switch (type)
+ {
+ case "Input":
+ _model.ModelInputs.Add(layer);
+ break;
+ case "MemoryData":
+ _model.MemoryDatas.Add(layer);
+ break;
+ default:
+ _model.Layers.Add(layer);
+ break;
+ }
+ }
+
+ private void WriteFloatArray(float[] data)
+ {
+ _rData!.AddRange(data);
+ foreach (float value in data)
+ {
+ _binWriter.Write(value);
+ }
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnFunctionBuilder.cs b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnFunctionBuilder.cs
new file mode 100644
index 0000000000..a697a077eb
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnFunctionBuilder.cs
@@ -0,0 +1,265 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reactive;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Toolkit.HighPerformance;
+using NetFabric.Hyperlinq;
+using Nncase.Diagnostics;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using Nncase.IR.Tensors;
+using Nncase.Utilities;
+using static Nncase.CodeGen.CodeGenDumper;
+
+namespace Nncase.CodeGen.Ncnn;
+
+///
+/// Ncnn function builder.
+///
+internal class NcnnFunctionBuilder : FunctionBuilder
+{
+ private readonly NcnnEmitter _emitter;
+ private string[]? _inputs;
+ private string[]? _outputs;
+
+ public NcnnFunctionBuilder(uint id, SectionManager sectionManager)
+ : base(id, sectionManager)
+ {
+ _emitter = new NcnnEmitter(sectionManager.GetWriter(WellknownSectionNames.Rdata));
+ }
+
+ protected override ILinkableFunction CreateLinkableFunction(uint id, BaseFunction callable, IReadOnlyList functionRefs, Stream text)
+ {
+ return new NcnnLinkableFunction(id, callable, functionRefs, text, _inputs!, _outputs!, _emitter.GetRData()!.ToArray()!);
+ }
+
+ protected override void Compile(BaseFunction callable)
+ {
+ var visitor = new CodeGenVisitor(_emitter);
+ _outputs = visitor.Visit(callable).Split(',');
+ _inputs = visitor.Inputs.ToArray();
+ }
+
+ protected override void WriteText()
+ {
+ _emitter.SaveParam(TextWriter.BaseStream);
+
+ // dump param and bin.
+ string dumpPath = Path.Join(DumpScope.Current.Directory, "ncnn_param_dir");
+ if (!Directory.Exists(dumpPath))
+ {
+ Directory.CreateDirectory(dumpPath);
+ }
+ else if (Id == 0)
+ {
+ // clear dir before single case.
+ foreach (string filePath in Directory.GetFiles(dumpPath, "*", SearchOption.AllDirectories))
+ {
+ File.SetAttributes(filePath, FileAttributes.Normal); // 移除所有特殊属性以便删除
+ File.Delete(filePath);
+ }
+ }
+
+ if (DumpScope.Current.IsEnabled(DumpFlags.CodeGen))
+ {
+ // dump param.
+ using (var fileStream = File.Create(Path.Join(dumpPath, $"ncnn_{Id}.param")))
+ {
+ TextWriter.BaseStream.Seek(0, SeekOrigin.Begin);
+ TextWriter.BaseStream.CopyTo(fileStream);
+ }
+
+ // dump bin.
+ _emitter.SaveBin(dumpPath, Id);
+ }
+ }
+
+ private class CodeGenVisitor : ExprVisitor
+ {
+ private readonly NcnnEmitter _emitter;
+
+ private int _layerId;
+
+ public CodeGenVisitor(NcnnEmitter emitter)
+ {
+ _emitter = emitter;
+ }
+
+ public List Inputs { get; } = new();
+
+ public List Outputs { get; } = new();
+
+ protected override string VisitLeafVar(Var expr)
+ {
+ var name = GetNextName();
+ _emitter.Input(name);
+ Inputs.Add(name);
+ return name;
+ }
+
+ protected override string VisitLeafFunction(Function expr) => ExprMemo[expr.Body];
+
+ protected override string VisitLeafOp(Op expr) => string.Empty;
+
+ protected override string VisitLeafTuple(IR.Tuple expr) => StringUtility.Join(",", expr.Fields.AsValueEnumerable().Select(x => ExprMemo[x]));
+
+ protected override string VisitLeafCall(Call expr)
+ {
+ var names = new List { GetNextName() };
+ string[]? inString;
+ switch (expr.Target)
+ {
+ case NcnnSoftmax op:
+ _emitter.Softmax(names[0], ExprMemo[expr.Arguments[0]], op.Axis);
+ break;
+ case NcnnUnary op:
+ _emitter.Unary(names[0], ExprMemo[expr.Arguments[0]], op.OpType);
+ break;
+ case NcnnBatchNorm op:
+ _emitter.BatchNorm(names[0], ExprMemo[expr.Arguments[0]], op.Channels, op.Eps, op.SlopeData, op.MeanData, op.VarData, op.BiasData);
+ break;
+ case NcnnBinary op:
+ inString = op.LorR switch
+ {
+ 0 => new string[] { ExprMemo[expr.Arguments[0]], ExprMemo[expr.Arguments[1]] },
+ 1 => new string[] { string.Empty, ExprMemo[expr.Arguments[0]] },
+ 2 => new string[] { ExprMemo[expr.Arguments[0]], string.Empty },
+ _ => throw new NotImplementedException("Never reach here."),
+ };
+ _emitter.Binary(names[0], inString[0], inString[1], op.OpType, op.LorR, op.ConstInput!, op.ConstShape!);
+ break;
+ case NcnnCelu op:
+ _emitter.Celu(names[0], ExprMemo[expr.Arguments[0]], op.Alpha);
+ break;
+ case NcnnClip op:
+ _emitter.Clip(names[0], ExprMemo[expr.Arguments[0]], op.Min, op.Max);
+ break;
+ case NcnnConcat op:
+ List in_ = new();
+ var t = (IR.Tuple)expr.Arguments[0];
+ for (int i = 0; i < t.Fields.Length; i++)
+ {
+ in_.Add(ExprMemo[t.Fields[i]]);
+ }
+
+ _emitter.Concat(names[0], in_.ToArray(), op.Axis);
+ break;
+ case NcnnConv op:
+ _emitter.Conv(names[0], ExprMemo[expr.Arguments[0]], op.Args);
+ break;
+ case NcnnCumsum op:
+ _emitter.Cumsum(names[0], ExprMemo[expr.Arguments[0]], op.Axis);
+ break;
+ case NcnnElu op:
+ _emitter.Elu(names[0], ExprMemo[expr.Arguments[0]], op.Alpha);
+ break;
+ case NcnnErf:
+ _emitter.Erf(names[0], ExprMemo[expr.Arguments[0]]);
+ break;
+ case NcnnHardSigmoid op:
+ _emitter.HardSigmoid(names[0], ExprMemo[expr.Arguments[0]], op.Alpha, op.Beta);
+ break;
+ case NcnnHardSwish op:
+ _emitter.HardSwish(names[0], ExprMemo[expr.Arguments[0]], op.Alpha, op.Beta);
+ break;
+ case NcnnInstanceNorm op:
+ _emitter.InstanceNorm(names[0], ExprMemo[expr.Arguments[0]], op.Channels, op.Eps, op.Affine, op.GammaData, op.BetaData);
+ break;
+ case NcnnLRN op:
+ _emitter.LRN(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Alpha, op.Beta, op.Bias, op.Size);
+ break;
+ case NcnnLSTM op:
+ for (int i = 1; i < op.OutputSize; i++)
+ {
+ var a = GetNextName();
+ names.Add(a);
+ }
+
+ _emitter.LSTM(names.ToArray(), ExprMemo[expr.Arguments[0]], op.HiddenSize, op.WeightDataSize, op.Direction, op.W, op.R, op.B);
+ break;
+ case NcnnPadding op:
+ _emitter.Padding(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Top, op.Bottom, op.Left, op.Right, op.Type, op.Value, op.Front, op.Behind);
+ break;
+ case NcnnPooling op:
+ _emitter.Pooling(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Args);
+ break;
+ case NcnnPReLU op:
+ _emitter.PReLU(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Slope);
+ break;
+ case NcnnReduction op:
+ _emitter.Reduction(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Args);
+ break;
+ case NcnnReshape op:
+ _emitter.Reshape(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Shape);
+ break;
+ case NcnnSELU op:
+ _emitter.SELU(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Alpha, op.Gamma);
+ break;
+ case NcnnSigmoid:
+ _emitter.Sigmoid(names.ToArray(), ExprMemo[expr.Arguments[0]]);
+ break;
+ case NcnnCrop op:
+ _emitter.Crop(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Args);
+ break;
+ case NcnnSoftplus:
+ _emitter.Softplus(names.ToArray(), ExprMemo[expr.Arguments[0]]);
+ break;
+ case NcnnSlice op:
+ names.AddRange(op.Slices.Select(i => GetNextName()));
+ _emitter.Slice(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Slices, op.Axis);
+ break;
+ case NcnnTile op:
+ _emitter.Tile(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Repeats);
+ break;
+ case NcnnPermute op:
+ _emitter.Permute(names.ToArray(), ExprMemo[expr.Arguments[0]], op.OrderType);
+ break;
+ case NcnnMatMul op:
+ inString = op.LorR switch
+ {
+ 0 => new string[] { ExprMemo[expr.Arguments[0]], ExprMemo[expr.Arguments[1]] },
+ 1 => new string[] { string.Empty, ExprMemo[expr.Arguments[0]] },
+ 2 => new string[] { ExprMemo[expr.Arguments[0]], string.Empty },
+ _ => throw new NotImplementedException("Never reach here."),
+ };
+ _emitter.Matmul(names.ToArray(), inString[0], inString[1], op.LorR, op.ConstInput, op.ConstShape);
+ break;
+ case NcnnConvTranspose op:
+ _emitter.ConvTranspose(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Args);
+ break;
+ case NcnnLayerNorm op:
+ _emitter.LayerNorm(names.ToArray(), ExprMemo[expr.Arguments[0]], op.AffineSize, op.Eps, op.Affine, op.GammaData, op.BetaData);
+ break;
+ case NcnnCast op:
+ _emitter.Cast(names.ToArray(), ExprMemo[expr.Arguments[0]], op.FromType, op.ToType);
+ break;
+ case NcnnGELU:
+ _emitter.GELU(names.ToArray(), ExprMemo[expr.Arguments[0]]);
+ break;
+ case NcnnDequantize op:
+ _emitter.Dequantize(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Scale, op.Bias);
+ break;
+ case NcnnSqueeze op:
+ _emitter.Squeeze(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Dims);
+ break;
+ case NcnnUnsqueeze op:
+ _emitter.Unsqueeze(names.ToArray(), ExprMemo[expr.Arguments[0]], op.Dims);
+ break;
+ default:
+ throw new NotSupportedException($"Not support {nameof(expr.Target)} in Ncnn ops emitter");
+ }
+
+ // serialize outputs to string.
+ string output = string.Join(",", names.Select(x => x.ToString()));
+ return output;
+ }
+
+ private string GetNextName() => $"layer{_layerId++}";
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnLinkableFunction.cs b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnLinkableFunction.cs
new file mode 100644
index 0000000000..28fe673c69
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnLinkableFunction.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.IR;
+
+namespace Nncase.CodeGen.Ncnn;
+
+internal class NcnnLinkableFunction : ILinkableFunction
+{
+ public NcnnLinkableFunction(uint id, BaseFunction sourceFunction, IEnumerable functionRefs, Stream text, string[] inputs, string[] outputs, float[] rData)
+ {
+ Id = id;
+ SourceFunction = sourceFunction;
+ FunctionRefs = functionRefs;
+ Text = text;
+ Inputs = inputs;
+ Outputs = outputs;
+ Sections = new[]
+ {
+ LinkedSection.FromStrings(inputs, ".inputs"),
+ LinkedSection.FromStrings(outputs, ".outputs"),
+ LinkedSection.FromData(rData, ".rdata"),
+ };
+ }
+
+ public uint Id { get; }
+
+ public BaseFunction SourceFunction { get; }
+
+ public IEnumerable FunctionRefs { get; }
+
+ public Stream Text { get; }
+
+ public string[] Inputs { get; }
+
+ public string[] Outputs { get; }
+
+ public IReadOnlyList Sections { get; }
+}
diff --git a/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnLinkableModule.cs b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnLinkableModule.cs
new file mode 100644
index 0000000000..5ea4a751ba
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnLinkableModule.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Nncase.CodeGen.Ncnn;
+
+internal class NcnnLinkableModule : LinkableModule
+{
+ public NcnnLinkableModule(IReadOnlyList functions, SectionManager sectionManager)
+ : base(functions, sectionManager)
+ {
+ }
+
+ protected override ILinkedModule CreateLinkedModule(IReadOnlyList linkedFunctions, Stream text)
+ {
+ return new NcnnLinkedModule(
+ linkedFunctions,
+ text,
+ SectionManager.GetContent(WellknownSectionNames.Rdata));
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnLinkedModule.cs b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnLinkedModule.cs
new file mode 100644
index 0000000000..078f9c903c
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnLinkedModule.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Nncase.CodeGen.Ncnn;
+
+internal class NcnnLinkedModule : ILinkedModule
+{
+ public NcnnLinkedModule(IReadOnlyList functions, Stream text, Stream? rdata)
+ {
+ Functions = functions;
+ Sections = new[]
+ {
+ new LinkedSection(text, ".text", 0, 8, (uint)text.Length),
+ new LinkedSection(rdata, ".rdata", 0, 8, (uint)(rdata?.Length ?? 0)),
+ };
+ }
+
+ public string ModuleKind => "ncnn";
+
+ public uint Version => 1;
+
+ public IReadOnlyList Functions { get; }
+
+ public IReadOnlyList Sections { get; }
+}
diff --git a/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnModuleBuilder.cs b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnModuleBuilder.cs
new file mode 100644
index 0000000000..3234727e69
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/CodeGen/Ncnn/NcnnModuleBuilder.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.IR;
+
+namespace Nncase.CodeGen.Ncnn;
+
+///
+/// Ncnn module builder.
+///
+public class NcnnModuleBuilder : ModuleBuilder
+{
+ ///
+ public override string ModuleKind => "ncnn";
+
+ ///
+ protected override ILinkableModule CreateLinkableModule(IReadOnlyList linkableFunctions)
+ {
+ return new NcnnLinkableModule(linkableFunctions, SectionManager);
+ }
+
+ ///
+ protected override FunctionBuilder CreateFunctionBuilder(uint id)
+ {
+ return new NcnnFunctionBuilder(id, SectionManager);
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnBatchNorm.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnBatchNorm.cs
new file mode 100644
index 0000000000..e843def456
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnBatchNorm.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnBatchNormEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnBatchNorm batchnorm)
+ {
+ var input = context.GetOrtArgumentValue(batchnorm, NcnnBatchNorm.Input);
+ var eps = batchnorm.Eps;
+ var slopeData = batchnorm.SlopeData.ToArray();
+ var meanData = batchnorm.MeanData.ToArray();
+ var varData = batchnorm.VarData.ToArray();
+ var biasData = batchnorm.BiasData.ToArray();
+ return OrtKI.BatchNormalization(input, slopeData, biasData, meanData, varData, eps, 0.0f).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnBatchNorm target)
+ {
+ var input = context.CheckArgumentType(target, NcnnBatchNorm.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnBatchNorm target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnBatchNorm target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnBatchNorm.Input);
+ var returnType = context.GetReturnType();
+ var returnF = MetricUtility.GetFLOPs(returnType);
+ var inputF = MetricUtility.GetFLOPs(inputType);
+ var inner = inputF / returnF;
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.FLOPs] = (inner * 2) + (inputF * (MetricUtility.SubFLOPs + MetricUtility.DivFLOPs + MetricUtility.MulFLOPs + MetricUtility.AddFLOPs)),
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnBatchNorm target) => context.GetArgumentShape(target, NcnnBatchNorm.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnBinary.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnBinary.cs
new file mode 100644
index 0000000000..a3cc410e3c
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnBinary.cs
@@ -0,0 +1,137 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using DryIoc;
+using NetFabric.Hyperlinq;
+using Nncase.CostModel;
+using Nncase.Evaluator;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using Nncase.Utilities;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnBinaryEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnBinary binary)
+ {
+ var inputA = context.GetOrtArgumentValue(binary, NcnnBinary.InputA);
+ var inputB = context.GetOrtArgumentValue(binary, NcnnBinary.InputB);
+ var opType = binary.OpType;
+
+ // return OrtKI. (input, dim).ToValue();
+ switch (opType)
+ {
+ case BinaryOperationType.ADD:
+ return OrtKI.Add(inputA, inputB).ToValue();
+ case BinaryOperationType.SUB:
+ return OrtKI.Sub(inputA, inputB).ToValue();
+ case BinaryOperationType.MUL:
+ return OrtKI.Mul(inputA, inputB).ToValue();
+ case BinaryOperationType.DIV:
+ return OrtKI.Div(inputA, inputB).ToValue();
+
+ // case BinaryOperationType.MAX:
+ // return System.Math.Min(inputA, inputB).ToValue();
+ // case BinaryOperationType.MIN:
+ // return OrtKI.Min(inputA, inputB).ToValue();
+
+ // TODO: trunc
+ default:
+ throw new NotSupportedException("Ncnn unary ops");
+ }
+ }
+
+ public IRType Visit(NcnnBinary target, TensorType lhs, TensorType rhs)
+ {
+ return TypeInference.BroadcastType(lhs, rhs);
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnBinary target)
+ {
+ return target.LorR switch
+ {
+ 1 => Visit(context.CheckArgumentType(target, NcnnBinary.InputA), new TensorType(context.CheckArgumentType(target, NcnnBinary.InputA).DType, target.ConstShape)),
+ 2 => Visit(new TensorType(context.CheckArgumentType(target, NcnnBinary.InputA).DType, target.ConstShape), context.CheckArgumentType(target, NcnnBinary.InputA)),
+ 0 => Visit(context.CheckArgumentType(target, NcnnBinary.InputA), context.CheckArgumentType(target, NcnnBinary.InputB)),
+ _ => throw new NotSupportedException("Never reach here, LorR without fourth situation."),
+ };
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnBinary target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnBinary target)
+ {
+ var lhsType = context.GetArgumentType(target, NcnnBinary.InputA);
+ var rhsType = context.GetArgumentType(target, NcnnBinary.InputB);
+ var outputType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(lhsType) + CostUtility.GetMemoryAccess(rhsType) + CostUtility.GetMemoryAccess(outputType),
+ [MetricFactorNames.FLOPs] = MetricUtility.GetFLOPs(outputType, (int)MetricUtility.GetBinaryFLOPs(MapBinaryOp(target.OpType))),
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnBinary target)
+ {
+ var lhs = context.GetArgumentShape(target, NcnnBinary.InputA);
+ var rhs = context.GetArgumentShape(target, NcnnBinary.InputB);
+ return ShapeExprUtility.BroadcastShape(lhs, rhs);
+ }
+
+ private static BinaryOp MapBinaryOp(BinaryOperationType binaryOp) =>
+ binaryOp switch
+ {
+ BinaryOperationType.ADD => BinaryOp.Add,
+ BinaryOperationType.SUB => BinaryOp.Sub,
+ BinaryOperationType.MUL => BinaryOp.Mul,
+ BinaryOperationType.DIV => BinaryOp.Div,
+ BinaryOperationType.MAX => BinaryOp.Max,
+ BinaryOperationType.MIN => BinaryOp.Min,
+ BinaryOperationType.POW => BinaryOp.Pow,
+ _ => throw new ArgumentException("Invalid binary op type"),
+
+ // unsupported Binary ops
+ // BinaryOp.Mod =>
+ // BitwiseAnd
+ // BitwiseOr
+ // BitwiseXor
+ // LogicalAnd
+ // LogicalOr
+ // LogicalXor
+ // LeftShift
+ // RightShift
+ // => BinaryOperationType.RSUB,
+ // => BinaryOperationType.RDIV,
+ // => BinaryOperationType.RPOW,
+ // => BinaryOperationType.ATAN2,
+ // => BinaryOperationType.RATAN2,
+ };
+
+ private IRType Visit(TensorType inputA, TensorType inputB)
+ {
+ return TypeInference.BroadcastType(inputA, inputB);
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCast.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCast.cs
new file mode 100644
index 0000000000..e8e2ebdbb1
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCast.cs
@@ -0,0 +1,77 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnCastEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnCast cast)
+ {
+ var input = context.GetOrtArgumentValue(cast, NcnnCast.Input);
+ return OrtKI.Cast(input, cast.ToType).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnCast target)
+ {
+ var input = context.CheckArgumentType(target, NcnnCast.Input);
+ return Visit(input, target.ToType);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnCast target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnCast target)
+ {
+ _ = context.GetArgumentType(target, NcnnCast.Input);
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnCast target) => context.GetArgumentShape(target, NcnnCast.Input);
+
+ public DataType RecoverDataType(int num)
+ {
+ return num switch
+ {
+ 1 => DataTypes.Float32,
+ 2 => DataTypes.Float16,
+ 4 => DataTypes.BFloat16,
+ _ => throw new DataException($"not support DataTypeNum :{num}"),
+ };
+ }
+
+ private IRType Visit(TensorType input, int dT)
+ {
+ return new TensorType(RecoverDataType(dT), input.Shape);
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCelu.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCelu.cs
new file mode 100644
index 0000000000..be3d722b15
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCelu.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnCeluEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnCelu celu)
+ {
+ var input = context.GetOrtArgumentValue(celu, NcnnCelu.Input);
+ var alpha = celu.Alpha;
+ return OrtKI.Celu(input, alpha).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnCelu target)
+ {
+ var input = context.CheckArgumentType(target, NcnnCelu.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnCelu target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnCelu target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnCelu.Input);
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.FLOPs] = MetricUtility.GetFLOPs(inputType) * (MetricUtility.DivFLOPs + MetricUtility.ExpFLOPs + MetricUtility.MulFLOPs + MetricUtility.SubFLOPs + MetricUtility.AddFLOPs + (MetricUtility.CmpFLOPs * 2)),
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnCelu target) => context.GetArgumentShape(target, NcnnCelu.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnClip.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnClip.cs
new file mode 100644
index 0000000000..538d1ed07b
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnClip.cs
@@ -0,0 +1,67 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnClipEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnClip clip)
+ {
+ var input = context.GetOrtArgumentValue(clip, NcnnClip.Input);
+ var min = clip.Min;
+ var max = clip.Max;
+ return OrtKI.Clip(input, min, max).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnClip target)
+ {
+ var input = context.CheckArgumentType(target, NcnnClip.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnClip target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnClip target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnClip.Input);
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.FLOPs] = MetricUtility.GetFLOPs(inputType) * 2,
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnClip target) => context.GetArgumentShape(target, NcnnClip.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnConcat.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnConcat.cs
new file mode 100644
index 0000000000..f0f758fcea
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnConcat.cs
@@ -0,0 +1,113 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using Nncase.Utilities;
+using OrtKISharp;
+using static Nncase.IR.F.Tensors;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnConcatEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnConcat concat)
+ {
+ var inputs = context.GetArgumentValueAsTensors(concat, NcnnConcat.Input);
+ var axis = concat.Axis;
+ return OrtKI.Concat(inputs.Select(t => t.ToOrtTensor()).ToArray(), axis).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnConcat target)
+ {
+ var inputs = context.CheckArgumentType(target, NcnnConcat.Input);
+ return Visit(inputs.Fields, target.Axis);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnConcat target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnConcat target) => Metric.Zero;
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnConcat target) => context.GetArgumentShape(target, NcnnConcat.Input);
+
+ private IRType? CheckType(TupleType inputs)
+ {
+ bool? allScalar = null;
+ DataType? allDType = null;
+ foreach (var (i, input) in Enumerable.Range(0, inputs.Count).Select(i => (i, inputs[i])))
+ {
+ TensorType type;
+ if (input is TensorType a)
+ {
+ type = a;
+ }
+ else if (input is DistributedType { TensorType: TensorType b })
+ {
+ type = b;
+ }
+ else
+ {
+ return new InvalidType($"The ConCat Item[{i}] Must Have TensorType But Get {input}");
+ }
+
+ if (type.Shape.IsUnranked)
+ {
+ return new TensorType(type.DType, Shape.Unranked);
+ }
+
+ allScalar = (allScalar ?? type.IsScalar) & type.IsScalar;
+ allDType ??= type.DType;
+ if (allDType != type.DType)
+ {
+ return new InvalidType(
+ $"The ConCat Item[{i}] Must Be {allDType} But Get {type.DType.GetDisplayName()}");
+ }
+ }
+
+ if (allScalar == true && allDType is not null)
+ {
+ return new TensorType(allDType, new[] { inputs.Count });
+ }
+
+ return null;
+ }
+
+ private TensorType GetTensorType(IRType input) => input switch
+ {
+ TensorType t => t,
+ DistributedType d => d.TensorType,
+ _ => throw new InvalidCastException(),
+ };
+
+ private IRType Visit(IRArray inputs, int axisValue)
+ {
+ var outputShape = GetTensorType(inputs[0]).Shape.ToArray();
+
+ foreach (var item in inputs[1..])
+ {
+ outputShape[axisValue] += GetTensorType(item).Shape.ToArray()[axisValue];
+ }
+
+ return new TensorType(GetTensorType(inputs[0]).DType, outputShape);
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnConv.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnConv.cs
new file mode 100644
index 0000000000..362fdfc601
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnConv.cs
@@ -0,0 +1,88 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+using static Nncase.IR.F.Tensors;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnConvEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnConv conv)
+ {
+ var inputs = context.GetOrtArgumentValue(conv, NcnnConv.Input);
+ var weights = new Tensor(conv.Args.WeightData, new[] { conv.Args.NumOutput, conv.Args.WeightDataSize / (conv.Args.KernelH * conv.Args.KernelW), conv.Args.KernelH, conv.Args.KernelW }).ToOrtTensor();
+ var bias = new Tensor(conv.Args.BiasData, new[] { conv.Args.NumOutput }).ToOrtTensor();
+ var result = OrtKI.Conv(inputs, weights, bias, "NOTSET", new long[] { conv.Args.DilationH, conv.Args.DilationW }, 1, new long[] { conv.Args.KernelH, conv.Args.KernelW }, new long[] { conv.Args.PadLeft, conv.Args.PadTop, conv.Args.PadRight, conv.Args.PadBottom }, new long[] { conv.Args.StrideH, conv.Args.StrideW });
+ return OrtKI.Clip(result, conv.Args.ActivationParams[0], conv.Args.ActivationParams[1]).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnConv target)
+ {
+ var inputs = context.CheckArgumentType(target, NcnnConv.Input);
+ return Visit(inputs, target);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnConv target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnConv.Input);
+ var weightsType = new TensorType(DataTypes.Float32, new[] { target.Args.WeightDataSize });
+ var biasType = new TensorType(DataTypes.Float32, new[] { target.Args.NumOutput });
+ var macPerElement = (2 * target.Args.WeightDataSize / target.Args.NumOutput) - 1;
+
+ var outputType = context.GetReturnType();
+
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(inputType) + CostUtility.GetMemoryAccess(weightsType) + CostUtility.GetMemoryAccess(biasType),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(outputType),
+ [CostFactorNames.CPUCycles] = CostUtility.GetCPUCycles(outputType, (uint)macPerElement),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnConv target) => Metric.Zero;
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnConv target) => context.GetArgumentShape(target, NcnnConv.Input);
+
+ private TensorType GetTensorType(IRType input) => input switch
+ {
+ TensorType t => t,
+ DistributedType d => d.TensorType,
+ _ => throw new InvalidCastException(),
+ };
+
+ private IRType Visit(TensorType input, NcnnConv conv)
+ {
+ var outputShape = input.Shape.ToList();
+ var kernelShape = new[] { conv.Args.NumOutput, conv.Args.WeightDataSize / (conv.Args.NumOutput * conv.Args.KernelH * conv.Args.KernelW), conv.Args.KernelH, conv.Args.KernelW };
+ outputShape[0] = conv.Args.NumOutput;
+
+ outputShape[1] = IR.TypePatternUtility.GetWindowedOutputSize(
+ input.Shape[1].FixedValue + conv.Args.PadTop + conv.Args.PadBottom,
+ kernelShape[2],
+ conv.Args.StrideH,
+ conv.Args.DilationH,
+ false);
+ outputShape[2] = IR.TypePatternUtility.GetWindowedOutputSize(
+ input.Shape[2].FixedValue + conv.Args.PadLeft + conv.Args.PadRight,
+ kernelShape[3],
+ conv.Args.StrideW,
+ conv.Args.DilationW,
+ false);
+ return new TensorType(GetTensorType(input).DType, outputShape.ToArray());
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnConvTranspose.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnConvTranspose.cs
new file mode 100644
index 0000000000..6182197696
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnConvTranspose.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+using static Nncase.IR.F.Tensors;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnConvTransposeEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnConvTranspose convTranspose)
+ {
+ var inputs = context.GetOrtArgumentValue(convTranspose, NcnnConvTranspose.Input);
+ var weights = Transpose(convTranspose.Args.WeightData, new int[] { 1, 0, 2, 3 }).Evaluate().AsTensor().ToOrtTensor();
+ var bias = new Tensor(convTranspose.Args.BiasData, new[] { convTranspose.Args.NumOutput }).ToOrtTensor();
+ var result = OrtKI.ConvTranspose(inputs, weights, bias, "NOTSET", new long[] { convTranspose.Args.DilationH, convTranspose.Args.DilationW }, 1, new long[] { convTranspose.Args.KernelH, convTranspose.Args.KernelW }, new long[] { convTranspose.Args.OutputPadBottom, convTranspose.Args.OutputPadRight, }, new long[] { convTranspose.Args.OutputH, convTranspose.Args.OutputW }, new long[] { convTranspose.Args.PadRight, convTranspose.Args.PadBottom, convTranspose.Args.PadRight, convTranspose.Args.PadBottom }, new long[] { convTranspose.Args.StrideH, convTranspose.Args.StrideW });
+ return OrtKI.Clip(result, convTranspose.Args.ActivationParams[0], convTranspose.Args.ActivationParams[1]).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnConvTranspose target)
+ {
+ var inputs = context.CheckArgumentType(target, NcnnConvTranspose.Input);
+ return Visit(inputs, target);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnConvTranspose target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnConvTranspose.Input);
+ _ = inputType.Shape.ToValueArray();
+ var weightsType = new TensorType(target.Args.WeightData.ElementType, target.Args.WeightData.Shape);
+ var weightsShape = weightsType.Shape.ToValueArray();
+ var biasType = new TensorType(target.Args.WeightData.ElementType, new[] { target.Args.BiasData.Length });
+
+ var macPerElement = weightsShape[1] * weightsShape[2] * weightsShape[3];
+ var outputType = context.GetReturnType();
+
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(inputType) + CostUtility.GetMemoryAccess(weightsType) + CostUtility.GetMemoryAccess(biasType),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(outputType),
+ [CostFactorNames.CPUCycles] = CostUtility.GetCPUCycles(outputType, (uint)macPerElement * 2),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnConvTranspose target)
+ {
+ var returnType = context.GetReturnType();
+ _ = returnType.Shape.ToValueArray();
+
+ var inputType = context.GetArgumentType(target, NcnnConvTranspose.Input);
+ var inputShape = inputType.Shape.ToValueArray();
+ var weightType = new TensorType(target.Args.WeightData.ElementType, target.Args.WeightData.Shape);
+ var weightShape = weightType.Shape.ToValueArray();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(inputType) + CostUtility.GetMemoryAccess(weightType) + CostUtility.GetMemoryAccess(returnType),
+ [MetricFactorNames.FLOPs] = (UInt128)(inputShape[0] * weightShape[0] * weightShape[1] * inputShape[2] * inputShape[3] * weightShape[2] * weightShape[3]),
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnConvTranspose target) => context.GetArgumentShape(target, NcnnConvTranspose.Input);
+
+ private TensorType GetTensorType(IRType input) => input switch
+ {
+ TensorType t => t,
+ DistributedType d => d.TensorType,
+ _ => throw new InvalidCastException(),
+ };
+
+ private IRType Visit(TensorType input, NcnnConvTranspose convTranspose)
+ {
+ var outputShape = input.Shape.ToList();
+ outputShape[0] = convTranspose.Args.NumOutput;
+ outputShape[1] = convTranspose.Args.OutputH;
+ outputShape[2] = convTranspose.Args.OutputW;
+
+ return new TensorType(GetTensorType(input).DType, outputShape.ToArray());
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCrop.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCrop.cs
new file mode 100644
index 0000000000..02956bc523
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCrop.cs
@@ -0,0 +1,75 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.ArgsStruct;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnCropEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnCrop crop)
+ {
+ var input = context.GetOrtArgumentValue(crop, NcnnCrop.Input);
+ var starts = crop.Args.Starts;
+ var ends = crop.Args.Ends;
+ var axes = crop.Args.Axes;
+ var steps = Enumerable.Repeat(1, (starts ?? Array.Empty()).Length).ToArray();
+ return OrtKI.Slice(input, starts ?? Array.Empty(), ends ?? Array.Empty(), axes ?? Array.Empty(), steps).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnCrop target)
+ {
+ var input = context.CheckArgumentType(target, NcnnCrop.Input);
+ return Visit(input, target.Args);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnCrop target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnCrop target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnCrop.Input);
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(inputType) * 2,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnCrop target) => context.GetArgumentShape(target, NcnnCrop.Input);
+
+ private IRType Visit(TensorType input, CropArgs args)
+ {
+ var outputShape = input.Shape.ToArray();
+ for (int i = 0; i < args.Axes!.Length; i++)
+ {
+ int tStart = args.Starts![i] >= 0 ? args.Starts[i] : args.Starts[i] + outputShape[args.Axes[i]].FixedValue;
+ int tEnd = args.Ends![i] >= 0 ? args.Ends[i] : args.Ends[i] + outputShape[args.Axes[i]].FixedValue;
+ outputShape[args.Axes[i] < 0 ? args.Axes[i] + outputShape.Length : args.Axes[i]] = System.Math.Abs(tEnd - tStart);
+ }
+
+ return new TensorType(input.DType, outputShape);
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCumsum.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCumsum.cs
new file mode 100644
index 0000000000..c4c83ede68
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnCumsum.cs
@@ -0,0 +1,67 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnCumsumEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnCumsum cumsum)
+ {
+ var input = context.GetOrtArgumentValue(cumsum, NcnnCumsum.Input);
+ var axis = cumsum.Axis;
+ var axisTensor = new Tensor(new[] { axis }, new Shape(1));
+ return OrtKI.CumSum(input, axisTensor.ToOrtTensor(), 0L, 0L).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnCumsum target)
+ {
+ var input = context.CheckArgumentType(target, NcnnCumsum.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnCumsum target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnCumsum target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnCumsum.Input);
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.FLOPs] = MetricUtility.GetFLOPs(inputType) * MetricUtility.AddFLOPs,
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnCumsum target) => context.GetArgumentShape(target, NcnnCumsum.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnDequantize.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnDequantize.cs
new file mode 100644
index 0000000000..60a1006b36
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnDequantize.cs
@@ -0,0 +1,67 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnDequantizeEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnDequantize dequantize)
+ {
+ var input = context.GetOrtArgumentValue(dequantize, NcnnDequantize.Input);
+ return OrtKI.DequantizeLinear(input, dequantize.Scale, dequantize.Bias, 0).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnDequantize target)
+ {
+ var input = context.CheckArgumentType(target, NcnnDequantize.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnDequantize target)
+ {
+ var input = context.GetArgumentType(target, NcnnDequantize.Input);
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(input) +
+ (UInt128)((target.Scale.Length + target.Bias.Length) * sizeof(float)),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnDequantize target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnDequantize.Input);
+ var outputType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(inputType) + CostUtility.GetMemoryAccess(outputType),
+ [MetricFactorNames.FLOPs] = MetricUtility.GetFLOPs(outputType, 2),
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnDequantize target) => context.GetArgumentShape(target, NcnnDequantize.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ // ncnn only support dequantize to float32.
+ return new TensorType(DataTypes.Float32, input.Shape);
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnElu.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnElu.cs
new file mode 100644
index 0000000000..e450a140b5
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnElu.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnEluEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnElu elu)
+ {
+ var input = context.GetOrtArgumentValue(elu, NcnnElu.Input);
+ var alpha = elu.Alpha;
+ return OrtKI.Elu(input, alpha).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnElu target)
+ {
+ var input = context.CheckArgumentType(target, NcnnElu.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnElu target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnElu target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnElu.Input);
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.FLOPs] = MetricUtility.GetFLOPs(inputType) * (MetricUtility.ExpFLOPs + MetricUtility.MulFLOPs + MetricUtility.SubFLOPs + MetricUtility.CmpFLOPs),
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnElu target) => context.GetArgumentShape(target, NcnnElu.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnErf.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnErf.cs
new file mode 100644
index 0000000000..bc890a5ace
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnErf.cs
@@ -0,0 +1,65 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnErfEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnErf erf)
+ {
+ var input = context.GetOrtArgumentValue(erf, NcnnErf.Input);
+ return OrtKI.Erf(input).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnErf target)
+ {
+ var input = context.CheckArgumentType(target, NcnnErf.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnErf target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnErf target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnErf.Input);
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.FLOPs] = MetricUtility.GetFLOPs(inputType) * (MetricUtility.ExpFLOPs + MetricUtility.MulFLOPs + MetricUtility.SubFLOPs + MetricUtility.CmpFLOPs),
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnErf target) => context.GetArgumentShape(target, NcnnErf.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnGELU.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnGELU.cs
new file mode 100644
index 0000000000..5e5b0aacdf
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnGELU.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NetFabric.Hyperlinq;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Math;
+using Nncase.IR.Ncnn;
+using Nncase.IR.NN;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnGELUEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnGELU celu)
+ {
+ var input = context.GetOrtArgumentValue(celu, NcnnGELU.Input);
+ var res = IR.F.Math.Mul(IR.F.NN.Erf(IR.F.Math.Div(input.ToValue().AsTensor(), 1.4142135381698608)) + 1, input.ToValue().AsTensor()) * 0.5;
+ return res.Evaluate();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnGELU target)
+ {
+ var input = context.CheckArgumentType(target, NcnnGELU.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnGELU target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnGELU target)
+ {
+ _ = context.GetArgumentType(target, NcnnGELU.Input);
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnGELU target) => context.GetArgumentShape(target, NcnnGELU.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnHardSigmoid.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnHardSigmoid.cs
new file mode 100644
index 0000000000..2a3f4b9848
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnHardSigmoid.cs
@@ -0,0 +1,67 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnHardSigmoidEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnHardSigmoid hardSigmoid)
+ {
+ var input = context.GetOrtArgumentValue(hardSigmoid, NcnnHardSigmoid.Input);
+ var alpha = hardSigmoid.Alpha;
+ var beta = hardSigmoid.Beta;
+ return OrtKI.HardSigmoid(input, alpha, beta).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnHardSigmoid target)
+ {
+ var input = context.CheckArgumentType(target, NcnnHardSigmoid.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnHardSigmoid target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnHardSigmoid target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnHardSigmoid.Input);
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.FLOPs] = MetricUtility.GetFLOPs(inputType) * (MetricUtility.MulFLOPs + MetricUtility.AddFLOPs + (MetricUtility.CmpFLOPs * 2)),
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnHardSigmoid target) => context.GetArgumentShape(target, NcnnHardSigmoid.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnHardSwish.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnHardSwish.cs
new file mode 100644
index 0000000000..a372b289b9
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnHardSwish.cs
@@ -0,0 +1,65 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnHardSwishEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnHardSwish hardSwish)
+ {
+ var input = context.GetOrtArgumentValue(hardSwish, NcnnHardSwish.Input);
+ return OrtKI.HardSwish(input).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnHardSwish target)
+ {
+ var input = context.CheckArgumentType(target, NcnnHardSwish.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnHardSwish target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnHardSwish target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnHardSwish.Input);
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = CostUtility.GetMemoryAccess(returnType) * 2,
+ [MetricFactorNames.FLOPs] = MetricUtility.GetFLOPs(inputType) * (MetricUtility.PowFLOPs + MetricUtility.MulFLOPs + MetricUtility.AddFLOPs + (MetricUtility.CmpFLOPs * 2)),
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnHardSwish target) => context.GetArgumentShape(target, NcnnHardSwish.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnInstanceNorm.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnInstanceNorm.cs
new file mode 100644
index 0000000000..ee54186dba
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnInstanceNorm.cs
@@ -0,0 +1,74 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using OrtKISharp;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnInstanceNormEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator
+{
+ ///
+ public IValue Visit(IEvaluateContext context, NcnnInstanceNorm instanceNorm)
+ {
+ var input = context.GetOrtArgumentValue(instanceNorm, NcnnInstanceNorm.Input);
+
+ return OrtKI.InstanceNormalization(input, instanceNorm.GammaData, instanceNorm.BetaData, instanceNorm.Eps).ToValue();
+ }
+
+ ///
+ public IRType Visit(ITypeInferenceContext context, NcnnInstanceNorm target)
+ {
+ var input = context.CheckArgumentType(target, NcnnInstanceNorm.Input);
+ return Visit(input);
+ }
+
+ ///
+ public Cost Visit(ICostEvaluateContext context, NcnnInstanceNorm target)
+ {
+ var ret = context.GetReturnType();
+ return new()
+ {
+ [CostFactorNames.MemoryLoad] = CostUtility.GetMemoryAccess(ret),
+ [CostFactorNames.MemoryStore] = CostUtility.GetMemoryAccess(ret),
+ };
+ }
+
+ public Metric Visit(IMetricEvaluateContext context, NcnnInstanceNorm target)
+ {
+ var inputType = context.GetArgumentType(target, NcnnInstanceNorm.Input);
+ var gamma = new TensorType(DataTypes.Float32, new[] { target.Channels });
+ var returnType = context.GetReturnType();
+
+ return new()
+ {
+ [MetricFactorNames.OffChipMemoryTraffic] = (CostUtility.GetMemoryAccess(returnType) * 2) + (CostUtility.GetMemoryAccess(gamma) * 2),
+
+ // x = (x - mean)/(standard_deviation) * gamma + beta;
+ // mean = sum(x)/N;
+ // standard-deviation = sqrt(sum(square(x-mean))/N + eps);
+ [MetricFactorNames.FLOPs] =
+ (MetricUtility.GetFLOPs(inputType) * (MetricUtility.AddFLOPs + MetricUtility.SubFLOPs)) + MetricUtility.DivFLOPs + // x = x-mean
+ (MetricUtility.GetFLOPs(inputType) * (MetricUtility.PowFLOPs + MetricUtility.AddFLOPs)) + MetricUtility.SqrtFLOPs + MetricUtility.DivFLOPs + MetricUtility.AddFLOPs +
+ (MetricUtility.GetFLOPs(inputType) * (MetricUtility.DivFLOPs + MetricUtility.MulFLOPs + MetricUtility.AddFLOPs)),
+ [MetricFactorNames.Parallel] = 4,
+ };
+ }
+
+ public Expr Visit(IShapeEvaluateContext context, NcnnInstanceNorm target) => context.GetArgumentShape(target, NcnnInstanceNorm.Input);
+
+ private IRType Visit(TensorType input)
+ {
+ return input;
+ }
+}
diff --git a/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnLSTM.cs b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnLSTM.cs
new file mode 100644
index 0000000000..7f2f7576b1
--- /dev/null
+++ b/modules/Nncase.Modules.Ncnn/Evaluator/Ncnn/NcnnLSTM.cs
@@ -0,0 +1,135 @@
+// Copyright (c) Canaan Inc. All rights reserved.
+// Licensed under the Apache license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Nncase.CostModel;
+using Nncase.IR;
+using Nncase.IR.Ncnn;
+using Nncase.IR.RNN;
+using OrtKISharp;
+using static Nncase.LSTMHelper;
+
+namespace Nncase.Evaluator.Ncnn;
+
+///
+/// Evaluator for .
+///
+public class NcnnLSTMEvaluator : IEvaluator, ITypeInferencer, ICostEvaluator, IShapeEvaluator, IMetricEvaluator