diff --git a/docs/2026.html b/docs/2026.html
index cf7c826325..d8ff90d7d7 100644
--- a/docs/2026.html
+++ b/docs/2026.html
@@ -63,6 +63,9 @@
New features
NEON, SVE2 optimizations of classes SynetMergedConvolution16bCdc.
NEON, SVE2 optimizations of classes SynetMergedConvolution16bCd.
NEON, SVE2 optimizations of classes SynetMergedConvolution16bDc.
+ NEON optimizations of classes SynetQuantizedMergedConvolutionCdc.
+ NEON optimizations of classes SynetQuantizedMergedConvolutionCd.
+ NEON optimizations of classes SynetQuantizedMergedConvolutionDc.
Improving
diff --git a/prj/vs2022/Neon.vcxproj b/prj/vs2022/Neon.vcxproj
index 84ae49bd5b..12016499e9 100644
--- a/prj/vs2022/Neon.vcxproj
+++ b/prj/vs2022/Neon.vcxproj
@@ -146,6 +146,10 @@
+
+
+
+
@@ -250,6 +254,7 @@
+
diff --git a/prj/vs2022/Neon.vcxproj.filters b/prj/vs2022/Neon.vcxproj.filters
index 827437b671..16a8c45c33 100644
--- a/prj/vs2022/Neon.vcxproj.filters
+++ b/prj/vs2022/Neon.vcxproj.filters
@@ -364,6 +364,18 @@
Neon\Synet\MergedConvolution
+
+ Neon\Synet\Quantized
+
+
+ Neon\Synet\Quantized
+
+
+ Neon\Synet\Quantized
+
+
+ Neon\Synet\Quantized
+
Neon\Synet\Other
@@ -779,6 +791,9 @@
Inc
+
+ Inc
+
Inc
diff --git a/src/Simd/SimdLib.cpp b/src/Simd/SimdLib.cpp
index edf19b79df..1d1f97e8c1 100644
--- a/src/Simd/SimdLib.cpp
+++ b/src/Simd/SimdLib.cpp
@@ -7480,7 +7480,7 @@ SIMD_API void* SimdSynetQuantizedMergedConvolutionInit(size_t batch, const SimdC
SIMD_EMPTY();
#if defined(SIMD_SYNET_ENABLE)
typedef void* (*SimdSynetQuantizedMergedConvolutionInitPtr) (size_t batch, const SimdConvolutionParameters* convs, size_t count, int add);
- const static SimdSynetQuantizedMergedConvolutionInitPtr simdSynetQuantizedMergedConvolutionInit = SIMD_FUNC5(SynetQuantizedMergedConvolutionInit, SIMD_AMXBF16_FUNC, SIMD_AVX512VNNI_FUNC, SIMD_AVX512BW_FUNC, SIMD_AVX2_FUNC, SIMD_SSE41_FUNC);
+ const static SimdSynetQuantizedMergedConvolutionInitPtr simdSynetQuantizedMergedConvolutionInit = SIMD_FUNC6(SynetQuantizedMergedConvolutionInit, SIMD_AMXBF16_FUNC, SIMD_AVX512VNNI_FUNC, SIMD_AVX512BW_FUNC, SIMD_AVX2_FUNC, SIMD_SSE41_FUNC, SIMD_NEON_FUNC);
return simdSynetQuantizedMergedConvolutionInit(batch, convs, count, add);
#else
diff --git a/src/Simd/SimdMath.h b/src/Simd/SimdMath.h
index 91cb350466..5a4dc94c25 100644
--- a/src/Simd/SimdMath.h
+++ b/src/Simd/SimdMath.h
@@ -1515,6 +1515,15 @@ namespace Simd
return vcvtq_s32_f32(vaddq_f32(value, round));
}
+ SIMD_INLINE int32x4_t NearbyInt(float32x4_t value)
+ {
+#if defined(__aarch64__)
+ return vcvtnq_s32_f32(value);
+#else
+ return Round(value);
+#endif
+ }
+
SIMD_INLINE uint32x4_t RoundPositive(float32x4_t value)
{
return vcvtq_u32_f32(vaddq_f32(value, vdupq_n_f32(0.5f)));
diff --git a/src/Simd/SimdNeonSynetQuantizedMergedConvolution.cpp b/src/Simd/SimdNeonSynetQuantizedMergedConvolution.cpp
new file mode 100644
index 0000000000..70dd87f18a
--- /dev/null
+++ b/src/Simd/SimdNeonSynetQuantizedMergedConvolution.cpp
@@ -0,0 +1,93 @@
+/*
+* Simd Library (http://ermig1979.github.io/Simd).
+*
+* Copyright (c) 2011-2026 Yermalayeu Ihar.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+#include "Simd/SimdSynetQuantizedMergedConvolution.h"
+#include "Simd/SimdSynetQuantizeLinear.h"
+#include "Simd/SimdSynetConvolution8iCommon.h"
+#include "Simd/SimdSynet.h"
+#include "Simd/SimdMath.h"
+#include "Simd/SimdBase.h"
+#include "Simd/SimdCpu.h"
+#include "Simd/SimdLog.h"
+
+namespace Simd
+{
+#if defined(SIMD_NEON_ENABLE) && defined(SIMD_SYNET_ENABLE)
+ namespace Neon
+ {
+ typedef Base::SynetQuantizedMergedConvolution::AlgParam AlgParam;
+
+ //------------------------------------------------------------------------------------------------
+
+ SynetQuantizedMergedConvolutionCdc::SynetQuantizedMergedConvolutionCdc(const MergConvParam& p)
+ : Base::SynetQuantizedMergedConvolutionCdc(p)
+ {
+ SetSize(F, 4, 2);
+ SetInputConvolution(p.conv[0], _alg, _inputConvolution);
+ SetDepthwisePreprocess(p.conv[1], _alg, _depthwisePreprocess);
+ SetDepthwiseConvolution(p.conv[1], _alg, _depthwiseConvolution);
+ SetOutputConvolution(p.conv[2], _alg, _outputConvolution);
+ SetAddInputToOutput(p.conv[2], _alg, _addInputToOutput);
+ }
+
+ //------------------------------------------------------------------------------------------------
+
+ SynetQuantizedMergedConvolutionCd::SynetQuantizedMergedConvolutionCd(const MergConvParam& p)
+ : Base::SynetQuantizedMergedConvolutionCd(p)
+ {
+ SetSize(F, 4, 2);
+ SetInputConvolution(p.conv[0], _alg, _inputConvolution);
+ SetDepthwisePreprocess(p.conv[1], _alg, _depthwisePreprocess);
+ SetDepthwiseConvolution(p.conv[1], _alg, _depthwiseConvolution);
+ }
+
+ //------------------------------------------------------------------------------------------------
+
+ SynetQuantizedMergedConvolutionDc::SynetQuantizedMergedConvolutionDc(const MergConvParam& p)
+ : Base::SynetQuantizedMergedConvolutionDc(p)
+ {
+ SetSize(F, 4, 2);
+ SetDepthwisePreprocess(p.conv[0], _alg, _depthwisePreprocess);
+ SetDepthwiseConvolution(p.conv[0], _alg, _depthwiseConvolution);
+ SetOutputConvolution(p.conv[1], _alg, _outputConvolution);
+ SetAddInputToOutput(p.conv[1], _alg, _addInputToOutput);
+ }
+
+ //------------------------------------------------------------------------------------------------
+
+ void* SynetQuantizedMergedConvolutionInit(size_t batch, const SimdConvolutionParameters* convs, size_t count, int add)
+ {
+ MergConvParam param(batch, convs, count, add);
+ if (!param.Valid(SimdTensorData8u, SimdTensorData8u))
+ return NULL;
+ else if (SynetQuantizedMergedConvolutionCdc::Preferable(param))
+ return new SynetQuantizedMergedConvolutionCdc(param);
+ else if (SynetQuantizedMergedConvolutionCd::Preferable(param))
+ return new SynetQuantizedMergedConvolutionCd(param);
+ else if (SynetQuantizedMergedConvolutionDc::Preferable(param))
+ return new SynetQuantizedMergedConvolutionDc(param);
+ return new Base::SynetQuantizedMergedConvolutionRef(param);
+ }
+ }
+#endif
+}
diff --git a/src/Simd/SimdNeonSynetQuantizedMergedConvolutionDepthwise.cpp b/src/Simd/SimdNeonSynetQuantizedMergedConvolutionDepthwise.cpp
new file mode 100644
index 0000000000..75c5b7f5a6
--- /dev/null
+++ b/src/Simd/SimdNeonSynetQuantizedMergedConvolutionDepthwise.cpp
@@ -0,0 +1,645 @@
+/*
+* Simd Library (http://ermig1979.github.io/Simd).
+*
+* Copyright (c) 2011-2026 Yermalayeu Ihar.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+#include "Simd/SimdSynetQuantizedMergedConvolution.h"
+#include "Simd/SimdSynetQuantizeLinear.h"
+#include "Simd/SimdSynetQuantizedActivation.h"
+#include "Simd/SimdSynetConvolution8iCommon.h"
+#include "Simd/SimdSynet.h"
+#include "Simd/SimdMath.h"
+#include "Simd/SimdBase.h"
+#include "Simd/SimdCpu.h"
+#include "Simd/SimdLog.h"
+
+namespace Simd
+{
+#if defined(SIMD_NEON_ENABLE) && defined(SIMD_SYNET_ENABLE)
+ namespace Neon
+ {
+ typedef Base::SynetQuantizedMergedConvolution::AlgParam AlgParam;
+
+ //-------------------------------------------------------------------------------------------------
+
+ SIMD_INLINE int32x4_t UnpackU8x4(const uint8_t* src)
+ {
+ uint8x8_t u8 = vreinterpret_u8_u32(vdup_n_u32(*(const uint32_t*)src));
+ return vreinterpretq_s32_u32(vmovl_u16(vget_low_u16(vmovl_u8(u8))));
+ }
+
+ SIMD_INLINE int32x4_t PackRows(int32x4_t s0, int32x4_t s1)
+ {
+ return vorrq_s32(s0, vshlq_n_s32(s1, 16));
+ }
+
+ SIMD_INLINE int32x4_t ShiftLeft16(int32x4_t value)
+ {
+ return vshlq_n_s32(value, 16);
+ }
+
+ SIMD_INLINE int32x4_t ShiftRight16(int32x4_t value)
+ {
+ return vreinterpretq_s32_u32(vshrq_n_u32(vreinterpretq_u32_s32(value), 16));
+ }
+
+ void QuantizedMergedConvolutionDepthwisePreprocess(const uint8_t* src, const uint8_t* zero, const ConvParam& p, const AlgParam& a, size_t maC, size_t dyBeg, size_t dyEnd, uint8_t* dst)
+ {
+ int16x8_t _zero = vdupq_n_s16(zero[0]);
+ size_t byMask = a.dbH - 1, byPad = p.kernelY - 1, byBeg = dyBeg ? dyBeg * p.strideY + byPad : 0, byEnd = dyEnd * p.strideY + byPad;
+ if (a.dsB)
+ {
+ size_t syMask = a.dsH - 1, sC = a.dsH * p.srcW, sR = p.srcW * F;
+ size_t bW = a.dbW * 2, bR = a.dbW * a.maC, xPad = p.padX * 2, wPad = p.padW * 2;
+ for (size_t c = 0; c < maC; c += F)
+ {
+ for (size_t by = byBeg; by < byEnd; by += 2)
+ {
+ int16_t* pd = (int16_t*)dst + (by & byMask) * bR;
+ size_t sy = by - p.padY;
+ const uint8_t* ps0 = (sy + 0) < p.srcH ? src + ((sy + 0) & syMask) * sR : zero;
+ const uint8_t* ps1 = (sy + 1) < p.srcH ? src + ((sy + 1) & syMask) * sR : zero;
+ if (xPad)
+ {
+ for (size_t x = 0; x < xPad; x += 2, pd += DF)
+ vst1q_s16(pd, _zero);
+ }
+ for (size_t sx = 0; sx < sR; sx += F, pd += DF)
+ {
+ int32x4_t s0 = UnpackU8x4(ps0 + sx);
+ int32x4_t s1 = UnpackU8x4(ps1 + sx);
+ vst1q_s32((int32_t*)pd, PackRows(s0, s1));
+ }
+ if (wPad)
+ {
+ for (size_t x = 0; x < wPad; x += 2, pd += DF)
+ vst1q_s16(pd, _zero);
+ }
+ }
+ src += sC * F;
+ dst += bW * DF;
+ }
+ }
+ else
+ {
+ size_t sR = p.srcW * p.srcC, sC = p.srcC;
+ size_t bW = a.dbW * 2, bC = a.maC, xPad = p.padX * 2, wPad = p.padW * 2, bR = a.dbW * a.maC;
+ for (size_t by = byBeg; by < byEnd; by += 2)
+ {
+ int16_t* pd = (int16_t*)dst + (by & byMask) * bR;
+ size_t sy = by - p.padY;
+ const uint8_t* ps0 = (sy + 0) < p.srcH ? src + (sy + 0) * sR : zero;
+ const uint8_t* ps1 = (sy + 1) < p.srcH ? src + (sy + 1) * sR : zero;
+ if (xPad)
+ {
+ for (size_t x = 0; x < xPad; x += 2, pd += DF)
+ for (size_t c = 0; c < bC; c += F)
+ vst1q_s16(pd + c * bW, _zero);
+ }
+ for (size_t sx = 0; sx < p.srcW; sx++, pd += DF)
+ {
+ for (size_t sc = 0; sc < maC; sc += F)
+ {
+ int32x4_t s0 = UnpackU8x4(ps0 + sc);
+ int32x4_t s1 = UnpackU8x4(ps1 + sc);
+ vst1q_s32((int32_t*)(pd + sc * bW), PackRows(s0, s1));
+ }
+ ps0 += sC;
+ ps1 += sC;
+ }
+ if (wPad)
+ {
+ for (size_t x = 0; x < wPad; x += 2, pd += DF)
+ for (size_t c = 0; c < bC; c += F)
+ vst1q_s16(pd + c * bW, _zero);
+ }
+ }
+ }
+ }
+
+ //-------------------------------------------------------------------------------------------------
+
+ SIMD_INLINE void Madd2(int32x4_t& i32, int32x4_t u8, int32x4_t i8)
+ {
+ int16x8_t a = vreinterpretq_s16_s32(u8);
+ int16x8_t b = vreinterpretq_s16_s32(i8);
+ int32x4_t lo = vmull_s16(vget_low_s16(a), vget_low_s16(b));
+ int32x4_t hi = vmull_s16(vget_high_s16(a), vget_high_s16(b));
+#if defined(__aarch64__)
+ i32 = vaddq_s32(i32, vpaddq_s32(lo, hi));
+#else
+ i32 = vaddq_s32(i32, vcombine_s32(
+ vpadd_s32(vget_low_s32(lo), vget_high_s32(lo)),
+ vpadd_s32(vget_low_s32(hi), vget_high_s32(hi))));
+#endif
+ }
+
+ SIMD_INLINE void Save1(uint8_t* dst, int32x4_t sum, const int32x4_t& bias, const float32x4_t& norm, const int32x4_t& zero)
+ {
+ QuntizedTerm8i::template Save<0>(dst, (int32_t*)NULL, sum, &bias, &norm, zero);
+ }
+
+ SIMD_INLINE void Save1(uint8_t* dst, int32x4_t sum, const int32x4_t& bias, const float32x4_t& norm, const int32x4_t& zero, size_t tail)
+ {
+ QuntizedTerm8i::template Save<0>(dst, (int32_t*)NULL, sum, &bias, &norm, zero, tail);
+ }
+
+ SIMD_INLINE int32x4_t LoadI16(const int16_t* src)
+ {
+ return vld1q_s32((const int32_t*)src);
+ }
+
+ //------------------------------------------------------------------------------------------------
+
+ void QuantizedMergedConvolutionDepthwiseConvolutionAny(const uint8_t* src8, const ConvParam& p, const AlgParam& a, size_t maC, size_t dyBeg, size_t dyEnd,
+ const int8_t* weight8, const int32_t* bias, const float* norm, int32_t zero, uint8_t* dst)
+ {
+ const int16_t* src = (int16_t*)src8, *weight = (int16_t*)weight8;
+ float32x4_t _norm;
+ int32x4_t _zero = vdupq_n_s32(zero), _bias;
+ int32x4_t d00, d10, d20, d30, d01, d11, d21, d31, w0, w1, s0;
+ size_t sC = maC, sCF = AlignLo(sC, F), kY = p.kernelY, kX = p.kernelX, sY = p.strideY, sX = p.strideX, dX = sX * DF, dW = a.dwStep;
+ size_t byMask = a.dbH - 1, bW = a.dbW * 2, bR = a.dbW * a.maC, dstW2 = AlignLo(p.dstW, 2), dstW4 = AlignLo(p.dstW, 4), dD = a.ddB ? a.maC : p.dstC;
+ size_t dyEnd2 = dyBeg + (sY == 1 ? AlignLo(dyEnd - dyBeg, 2) : 0), sizeW = a.dwSize, dyD = p.dstW * dD;
+ if(a.ddB)
+ dst += (dyBeg % a.ddStep) * p.dstW * dD;
+ else
+ dst += dyBeg * p.dstW * dD;
+ size_t dy = dyBeg;
+ for (; dy < dyEnd2; dy += 2)
+ {
+ size_t sc = 0, sy = dy * sY;
+ for (; sc < sCF; sc += F)
+ {
+ uint8_t* pd0 = dst + sc, * pd1 = pd0 + dyD;
+ const int16_t* ps0 = src + sc * bW;
+ _bias = vld1q_s32(bias + sc);
+ _norm = vld1q_f32(norm + sc);
+ size_t dx = 0;
+ for (; dx < dstW4; dx += 4, ps0 += 4 * dX)
+ {
+ d00 = vdupq_n_s32(0);
+ d10 = vdupq_n_s32(0);
+ d20 = vdupq_n_s32(0);
+ d30 = vdupq_n_s32(0);
+ d01 = vdupq_n_s32(0);
+ d11 = vdupq_n_s32(0);
+ d21 = vdupq_n_s32(0);
+ d31 = vdupq_n_s32(0);
+ const int16_t* pw0 = weight + sc * dW, * pw1 = pw0 + sizeW;
+ for (size_t ky = 0; ky < kY; ky += 2)
+ {
+ const int16_t* ps = ps0 + ((sy + ky) & byMask) * bR;
+ for (size_t kx = 0; kx < kX; ++kx, ps += DF, pw0 += DF, pw1 += DF)
+ {
+ w0 = LoadI16(pw0);
+ w1 = LoadI16(pw1);
+ s0 = LoadI16(ps + 0 * dX);
+ Madd2(d00, s0, w0);
+ Madd2(d01, s0, w1);
+ s0 = LoadI16(ps + 1 * dX);
+ Madd2(d10, s0, w0);
+ Madd2(d11, s0, w1);
+ s0 = LoadI16(ps + 2 * dX);
+ Madd2(d20, s0, w0);
+ Madd2(d21, s0, w1);
+ s0 = LoadI16(ps + 3 * dX);
+ Madd2(d30, s0, w0);
+ Madd2(d31, s0, w1);
+ }
+ }
+ Save1(pd0 + 0 * dD, d00, _bias, _norm, _zero);
+ Save1(pd0 + 1 * dD, d10, _bias, _norm, _zero);
+ Save1(pd0 + 2 * dD, d20, _bias, _norm, _zero);
+ Save1(pd0 + 3 * dD, d30, _bias, _norm, _zero);
+ Save1(pd1 + 0 * dD, d01, _bias, _norm, _zero);
+ Save1(pd1 + 1 * dD, d11, _bias, _norm, _zero);
+ Save1(pd1 + 2 * dD, d21, _bias, _norm, _zero);
+ Save1(pd1 + 3 * dD, d31, _bias, _norm, _zero);
+ pd0 += 4 * dD;
+ pd1 += 4 * dD;
+ }
+ for (; dx < dstW2; dx += 2, ps0 += 2 * dX)
+ {
+ d00 = vdupq_n_s32(0);
+ d10 = vdupq_n_s32(0);
+ d01 = vdupq_n_s32(0);
+ d11 = vdupq_n_s32(0);
+ const int16_t* pw0 = weight + sc * dW, * pw1 = pw0 + sizeW;
+ for (size_t ky = 0; ky < kY; ky += 2)
+ {
+ const int16_t* ps = ps0 + ((sy + ky) & byMask) * bR;
+ for (size_t kx = 0; kx < kX; ++kx, ps += DF, pw0 += DF, pw1 += DF)
+ {
+ w0 = LoadI16(pw0);
+ w1 = LoadI16(pw1);
+ s0 = LoadI16(ps + 0 * dX);
+ Madd2(d00, s0, w0);
+ Madd2(d01, s0, w1);
+ s0 = LoadI16(ps + 1 * dX);
+ Madd2(d10, s0, w0);
+ Madd2(d11, s0, w1);
+ }
+ }
+ Save1(pd0 + 0 * dD, d00, _bias, _norm, _zero);
+ Save1(pd0 + 1 * dD, d10, _bias, _norm, _zero);
+ Save1(pd1 + 0 * dD, d01, _bias, _norm, _zero);
+ Save1(pd1 + 1 * dD, d11, _bias, _norm, _zero);
+ pd0 += 2 * dD;
+ pd1 += 2 * dD;
+ }
+ for (; dx < p.dstW; ++dx, ps0 += dX)
+ {
+ d00 = vdupq_n_s32(0);
+ d01 = vdupq_n_s32(0);
+ const int16_t* pw0 = weight + sc * dW, * pw1 = pw0 + sizeW;
+ for (size_t ky = 0; ky < kY; ky += 2)
+ {
+ const int16_t* ps = ps0 + ((sy + ky) & byMask) * bR;
+ for (size_t kx = 0; kx < kX; ++kx, ps += DF, pw0 += DF, pw1 += DF)
+ {
+ w0 = LoadI16(pw0);
+ w1 = LoadI16(pw1);
+ s0 = LoadI16(ps + 0 * dX);
+ Madd2(d00, s0, w0);
+ Madd2(d01, s0, w1);
+ }
+ }
+ Save1(pd0 + 0 * dD, d00, _bias, _norm, _zero);
+ Save1(pd1 + 0 * dD, d01, _bias, _norm, _zero);
+ pd0 += dD;
+ pd1 += dD;
+ }
+ }
+ for (; sc < sC; sc += F)
+ {
+ uint8_t* pd0 = dst + sc, * pd1 = pd0 + dyD;
+ const int16_t* ps0 = src + sc * bW;
+ _bias = vld1q_s32(bias + sc);
+ _norm = vld1q_f32(norm + sc);
+ size_t dx = 0, tail = sC - sCF;
+ for (; dx < p.dstW; ++dx, ps0 += dX)
+ {
+ d00 = vdupq_n_s32(0);
+ d01 = vdupq_n_s32(0);
+ const int16_t* pw0 = weight + sc * dW, * pw1 = pw0 + sizeW;
+ for (size_t ky = 0; ky < kY; ky += 2)
+ {
+ const int16_t* ps = ps0 + ((sy + ky) & byMask) * bR;
+ for (size_t kx = 0; kx < kX; ++kx, ps += DF, pw0 += DF, pw1 += DF)
+ {
+ w0 = LoadI16(pw0);
+ w1 = LoadI16(pw1);
+ s0 = LoadI16(ps + 0 * dX);
+ Madd2(d00, s0, w0);
+ Madd2(d01, s0, w1);
+ }
+ }
+ Save1(pd0 + 0 * dD, d00, _bias, _norm, _zero, tail);
+ Save1(pd1 + 0 * dD, d01, _bias, _norm, _zero, tail);
+ pd0 += dD;
+ pd1 += dD;
+ }
+ }
+ dst += p.dstW * 2 * dD;
+ }
+ for (; dy < dyEnd; ++dy)
+ {
+ size_t sc = 0, sy = dy * sY;
+ for (; sc < sCF; sc += F)
+ {
+ uint8_t* pd = dst + sc;
+ const int16_t* ps0 = src + sc * bW;
+ _bias = vld1q_s32(bias + sc);
+ _norm = vld1q_f32(norm + sc);
+ size_t dx = 0;
+ for (; dx < dstW4; dx += 4, ps0 += 4 * dX)
+ {
+ d00 = vdupq_n_s32(0);
+ d10 = vdupq_n_s32(0);
+ d20 = vdupq_n_s32(0);
+ d30 = vdupq_n_s32(0);
+ const int16_t* pw = weight + sc * dW;
+ for (size_t ky = 0; ky < kY; ky += 2)
+ {
+ const int16_t* ps = ps0 + ((sy + ky) & byMask) * bR;
+ for (size_t kx = 0; kx < kX; ++kx, ps += DF, pw += DF)
+ {
+ w0 = LoadI16(pw);
+ Madd2(d00, LoadI16(ps + 0 * dX), w0);
+ Madd2(d10, LoadI16(ps + 1 * dX), w0);
+ Madd2(d20, LoadI16(ps + 2 * dX), w0);
+ Madd2(d30, LoadI16(ps + 3 * dX), w0);
+ }
+ }
+ Save1(pd + 0 * dD, d00, _bias, _norm, _zero);
+ Save1(pd + 1 * dD, d10, _bias, _norm, _zero);
+ Save1(pd + 2 * dD, d20, _bias, _norm, _zero);
+ Save1(pd + 3 * dD, d30, _bias, _norm, _zero);
+ pd += 4 * dD;
+ }
+ for (; dx < dstW2; dx += 2, ps0 += 2 * dX)
+ {
+ d00 = vdupq_n_s32(0);
+ d10 = vdupq_n_s32(0);
+ const int16_t* pw = weight + sc * dW;
+ for (size_t ky = 0; ky < kY; ky += 2)
+ {
+ const int16_t* ps = ps0 + ((sy + ky) & byMask) * bR;
+ for (size_t kx = 0; kx < kX; ++kx, ps += DF, pw += DF)
+ {
+ w0 = LoadI16(pw);
+ Madd2(d00, LoadI16(ps + 0 * dX), w0);
+ Madd2(d10, LoadI16(ps + 1 * dX), w0);
+ }
+ }
+ Save1(pd + 0 * dD, d00, _bias, _norm, _zero);
+ Save1(pd + 1 * dD, d10, _bias, _norm, _zero);
+ pd += 2 * dD;
+ }
+ for (; dx < p.dstW; ++dx, ps0 += dX)
+ {
+ d00 = vdupq_n_s32(0);
+ const int16_t* pw = weight + sc * dW;
+ for (size_t ky = 0; ky < kY; ky += 2)
+ {
+ const int16_t* ps = ps0 + ((sy + ky) & byMask) * bR;
+ for (size_t kx = 0; kx < kX; ++kx, ps += DF, pw += DF)
+ {
+ w0 = LoadI16(pw);
+ Madd2(d00, LoadI16(ps), w0);
+ }
+ }
+ Save1(pd, d00, _bias, _norm, _zero);
+ pd += dD;
+ }
+ }
+ for (; sc < sC; sc += F)
+ {
+ uint8_t* pd = dst + sc;
+ const int16_t* ps0 = src + sc * bW;
+ _bias = vld1q_s32(bias + sc);
+ _norm = vld1q_f32(norm + sc);
+ size_t dx = 0, tail = sC - sCF;
+ for (; dx < p.dstW; ++dx, ps0 += dX)
+ {
+ d00 = vdupq_n_s32(0);
+ const int16_t* pw = weight + sc * dW;
+ for (size_t ky = 0; ky < kY; ky += 2)
+ {
+ const int16_t* ps = ps0 + ((sy + ky) & byMask) * bR;
+ for (size_t kx = 0; kx < kX; ++kx, ps += DF, pw += DF)
+ {
+ w0 = LoadI16(pw);
+ Madd2(d00, LoadI16(ps), w0);
+ }
+ }
+ Save1(pd, d00, _bias, _norm, _zero, tail);
+ pd += dD;
+ }
+ }
+ dst += p.dstW * dD;
+ }
+ }
+
+ //-------------------------------------------------------------------------------------------------
+
+ void QuantizedMergedConvolutionDepthwiseConvolution3x3(const uint8_t* src8, const ConvParam& p, const AlgParam& a, size_t maC, size_t dyBeg, size_t dyEnd,
+ const int8_t* weight8, const int32_t* bias, const float* norm, int32_t zero, uint8_t* dst)
+ {
+ const int16_t* src = (int16_t*)src8, * weight = (int16_t*)weight8;
+ float32x4_t _norm;
+ int32x4_t _zero = vdupq_n_s32(zero), _bias;
+ int32x4_t d00, d10, w03, w14, w25, s0;
+ size_t sC = maC, sCF = AlignLo(sC, F), kY = p.kernelY, kX = p.kernelX, sY = p.strideY, sX = p.strideX, dX = sX * DF, dW = a.dwStep;
+ size_t byMask = a.dbH - 1, bW = a.dbW * 2, bR = a.dbW * a.maC, dstW2 = (sX == 1 ? AlignLo(p.dstW, 2) : 0), dD = a.ddB ? a.maC : p.dstC;
+ size_t dyEnd2 = dyBeg + (sY == 1 ? AlignLo(dyEnd - dyBeg, 2) : 0), sizeW = a.dwSize, dyD = p.dstW * dD;
+ if (a.ddB)
+ dst += (dyBeg % a.ddStep) * p.dstW * dD;
+ else
+ dst += dyBeg * p.dstW * dD;
+ size_t dy = dyBeg;
+ for (; dy < dyEnd2; dy += 2)
+ {
+ int32x4_t d01, w36, w47, w58;
+ size_t sc = 0, sy = dy * sY;
+ for (; sc < sC; sc += F)
+ {
+ uint8_t* pd0 = dst + sc, * pd1 = pd0 + dyD;
+ const int16_t* ps0 = src + ((sy + 0) & byMask) * bR + sc * bW;
+ const int16_t* ps2 = src + ((sy + 2) & byMask) * bR + sc * bW;
+ const int16_t* pw0 = weight + sc * dW, * pw1 = pw0 + sizeW;
+ _bias = vld1q_s32(bias + sc);
+ _norm = vld1q_f32(norm + sc);
+ w03 = LoadI16(pw0 + 0 * 8);
+ w14 = LoadI16(pw0 + 1 * 8);
+ w25 = LoadI16(pw0 + 2 * 8);
+ w36 = LoadI16(pw1 + 3 * 8);
+ w47 = LoadI16(pw1 + 4 * 8);
+ w58 = LoadI16(pw1 + 5 * 8);
+ if (sc < sCF)
+ {
+ size_t dx = 0;
+ for (; dx < p.dstW; ++dx, ps0 += dX, ps2 += dX)
+ {
+ d00 = vdupq_n_s32(0);
+ d01 = vdupq_n_s32(0);
+
+ s0 = LoadI16(ps0 + 0 * 8);
+ Madd2(d00, s0, w03);
+ Madd2(d01, s0, ShiftLeft16(w03));
+ s0 = LoadI16(ps0 + 1 * 8);
+ Madd2(d00, s0, w14);
+ Madd2(d01, s0, ShiftLeft16(w14));
+ s0 = LoadI16(ps0 + 2 * 8);
+ Madd2(d00, s0, w25);
+ Madd2(d01, s0, ShiftLeft16(w25));
+ s0 = LoadI16(ps2 + 0 * 8);
+ Madd2(d00, s0, ShiftRight16(w36));
+ Madd2(d01, s0, w36);
+ s0 = LoadI16(ps2 + 1 * 8);
+ Madd2(d00, s0, ShiftRight16(w47));
+ Madd2(d01, s0, w47);
+ s0 = LoadI16(ps2 + 2 * 8);
+ Madd2(d00, s0, ShiftRight16(w58));
+ Madd2(d01, s0, w58);
+
+ Save1(pd0, d00, _bias, _norm, _zero);
+ Save1(pd1, d01, _bias, _norm, _zero);
+ pd0 += dD;
+ pd1 += dD;
+ }
+ }
+ else
+ {
+ size_t tail = sC - sCF;
+ for (size_t dx = 0; dx < p.dstW; ++dx, ps0 += dX, ps2 += dX)
+ {
+ d00 = vdupq_n_s32(0);
+ d01 = vdupq_n_s32(0);
+
+ s0 = LoadI16(ps0 + 0 * 8);
+ Madd2(d00, s0, w03);
+ Madd2(d01, s0, ShiftLeft16(w03));
+ s0 = LoadI16(ps0 + 1 * 8);
+ Madd2(d00, s0, w14);
+ Madd2(d01, s0, ShiftLeft16(w14));
+ s0 = LoadI16(ps0 + 2 * 8);
+ Madd2(d00, s0, w25);
+ Madd2(d01, s0, ShiftLeft16(w25));
+ s0 = LoadI16(ps2 + 0 * 8);
+ Madd2(d00, s0, ShiftRight16(w36));
+ Madd2(d01, s0, w36);
+ s0 = LoadI16(ps2 + 1 * 8);
+ Madd2(d00, s0, ShiftRight16(w47));
+ Madd2(d01, s0, w47);
+ s0 = LoadI16(ps2 + 2 * 8);
+ Madd2(d00, s0, ShiftRight16(w58));
+ Madd2(d01, s0, w58);
+
+ Save1(pd0, d00, _bias, _norm, _zero, tail);
+ Save1(pd1, d01, _bias, _norm, _zero, tail);
+ pd0 += dD;
+ pd1 += dD;
+ }
+ }
+ }
+ dst += p.dstW * dD * 2;
+ }
+ for (; dy < dyEnd; ++dy)
+ {
+ int32x4_t w6, w7, w8;
+ size_t sc = 0, sy = dy * sY;
+ for (; sc < sC; sc += F)
+ {
+ uint8_t* pd = dst + sc;
+ const int16_t* ps0 = src + ((sy + 0) & byMask) * bR + sc * bW;
+ const int16_t* ps2 = src + ((sy + 2) & byMask) * bR + sc * bW;
+ const int16_t* pw = weight + sc * dW;
+ _bias = vld1q_s32(bias + sc);
+ _norm = vld1q_f32(norm + sc);
+ w03 = LoadI16(pw + 0 * 8);
+ w14 = LoadI16(pw + 1 * 8);
+ w25 = LoadI16(pw + 2 * 8);
+ w6 = LoadI16(pw + 3 * 8);
+ w7 = LoadI16(pw + 4 * 8);
+ w8 = LoadI16(pw + 5 * 8);
+ if (sc < sCF)
+ {
+ size_t dx = 0;
+ for (; dx < dstW2; dx += 2, ps0 += QF, ps2 += QF)
+ {
+ d00 = vdupq_n_s32(0);
+ d10 = vdupq_n_s32(0);
+
+ s0 = LoadI16(ps0 + 0 * 8);
+ Madd2(d00, s0, w03);
+ s0 = LoadI16(ps0 + 1 * 8);
+ Madd2(d00, s0, w14);
+ Madd2(d10, s0, w03);
+ s0 = LoadI16(ps0 + 2 * 8);
+ Madd2(d00, s0, w25);
+ Madd2(d10, s0, w14);
+ s0 = LoadI16(ps0 + 3 * 8);
+ Madd2(d10, s0, w25);
+
+ s0 = LoadI16(ps2 + 0 * 8);
+ Madd2(d00, s0, w6);
+ s0 = LoadI16(ps2 + 1 * 8);
+ Madd2(d00, s0, w7);
+ Madd2(d10, s0, w6);
+ s0 = LoadI16(ps2 + 2 * 8);
+ Madd2(d00, s0, w8);
+ Madd2(d10, s0, w7);
+ s0 = LoadI16(ps2 + 3 * 8);
+ Madd2(d10, s0, w8);
+
+ Save1(pd + 0 * dD, d00, _bias, _norm, _zero);
+ Save1(pd + 1 * dD, d10, _bias, _norm, _zero);
+ pd += 2 * dD;
+ }
+ for (; dx < p.dstW; ++dx, ps0 += dX, ps2 += dX)
+ {
+ d00 = vdupq_n_s32(0);
+
+ s0 = LoadI16(ps0 + 0 * 8);
+ Madd2(d00, s0, w03);
+ s0 = LoadI16(ps0 + 1 * 8);
+ Madd2(d00, s0, w14);
+ s0 = LoadI16(ps0 + 2 * 8);
+ Madd2(d00, s0, w25);
+ s0 = LoadI16(ps2 + 0 * 8);
+ Madd2(d00, s0, w6);
+ s0 = LoadI16(ps2 + 1 * 8);
+ Madd2(d00, s0, w7);
+ s0 = LoadI16(ps2 + 2 * 8);
+ Madd2(d00, s0, w8);
+
+ Save1(pd, d00, _bias, _norm, _zero);
+ pd += dD;
+ }
+ }
+ else
+ {
+ size_t tail = sC - sCF;
+ for (size_t dx = 0; dx < p.dstW; ++dx, ps0 += dX, ps2 += dX)
+ {
+ d00 = vdupq_n_s32(0);
+
+ s0 = LoadI16(ps0 + 0 * 8);
+ Madd2(d00, s0, w03);
+ s0 = LoadI16(ps0 + 1 * 8);
+ Madd2(d00, s0, w14);
+ s0 = LoadI16(ps0 + 2 * 8);
+ Madd2(d00, s0, w25);
+ s0 = LoadI16(ps2 + 0 * 8);
+ Madd2(d00, s0, w6);
+ s0 = LoadI16(ps2 + 1 * 8);
+ Madd2(d00, s0, w7);
+ s0 = LoadI16(ps2 + 2 * 8);
+ Madd2(d00, s0, w8);
+
+ Save1(pd, d00, _bias, _norm, _zero, tail);
+ pd += dD;
+ }
+ }
+ }
+ dst += p.dstW * dD;
+ }
+ }
+
+ //-------------------------------------------------------------------------------------------------
+
+ void SetDepthwisePreprocess(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::DepthwisePreprocessPtr& func)
+ {
+ func = QuantizedMergedConvolutionDepthwisePreprocess;
+ }
+
+ void SetDepthwiseConvolution(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::DepthwiseConvolutionPtr& func)
+ {
+ if(p.IsKernel(3))
+ func = QuantizedMergedConvolutionDepthwiseConvolution3x3;
+ else
+ func = QuantizedMergedConvolutionDepthwiseConvolutionAny;
+ }
+ }
+#endif
+}
diff --git a/src/Simd/SimdNeonSynetQuantizedMergedConvolutionInput.cpp b/src/Simd/SimdNeonSynetQuantizedMergedConvolutionInput.cpp
new file mode 100644
index 0000000000..68640a3d92
--- /dev/null
+++ b/src/Simd/SimdNeonSynetQuantizedMergedConvolutionInput.cpp
@@ -0,0 +1,185 @@
+/*
+* Simd Library (http://ermig1979.github.io/Simd).
+*
+* Copyright (c) 2011-2026 Yermalayeu Ihar.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+#include "Simd/SimdSynetQuantizedMergedConvolution.h"
+#include "Simd/SimdSynetQuantizeLinear.h"
+#include "Simd/SimdSynetQuantizedActivation.h"
+#include "Simd/SimdSynetConvolution8iCommon.h"
+#include "Simd/SimdSynet.h"
+#include "Simd/SimdMath.h"
+#include "Simd/SimdBase.h"
+#include "Simd/SimdCpu.h"
+#include "Simd/SimdLog.h"
+
+namespace Simd
+{
+#if defined(SIMD_NEON_ENABLE) && defined(SIMD_SYNET_ENABLE)
+ namespace Neon
+ {
+ typedef Base::SynetQuantizedMergedConvolution::AlgParam AlgParam;
+
+ //-------------------------------------------------------------------------------------------------
+
+ SIMD_INLINE void SaveInput1(uint8_t* dst, int32x4_t sum, const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero)
+ {
+ QuntizedTerm8i::template Save<0>(dst, NULL, sum, bias, norm, zero);
+ }
+
+ SIMD_INLINE void SaveInput2(uint8_t* dst0, uint8_t* dst1, int32x4_t sum0, int32x4_t sum1, const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero)
+ {
+ QuntizedTerm8i::template Save<0>(dst0, NULL, sum0, bias + 0, norm + 0, zero);
+ QuntizedTerm8i::template Save<0>(dst1, NULL, sum1, bias + 1, norm + 1, zero);
+ }
+
+ //------------------------------------------------------------------------------------------------
+
+ template void QuantizedMergedConvolutionInput_2xM(const uint8_t* src0, const ConvParam& p, const AlgParam& a,
+ size_t dstC, const int8_t* weight0, const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero, uint8_t* dst0, uint8_t* dst1)
+ {
+ int32x4_t d00, d01, d10, d11, d20, d21, d30, d31, d40, d41;
+ uint8x16_t s0;
+ int8x16_t w0, w1;
+ size_t srcC = a.isB ? a.iwStep : p.srcC;
+ const int8_t* weight1 = weight0 + a.iwStep * F;
+ const uint8_t* src1 = src0 + 1 * srcC;
+ const uint8_t* src2 = src0 + 2 * srcC;
+ const uint8_t* src3 = src0 + 3 * srcC;
+ const uint8_t* src4 = src0 + 4 * srcC;
+ if (dstC > F)
+ {
+ if (M > 0) d00 = vdupq_n_s32(0), d01 = vdupq_n_s32(0);
+ if (M > 1) d10 = vdupq_n_s32(0), d11 = vdupq_n_s32(0);
+ if (M > 2) d20 = vdupq_n_s32(0), d21 = vdupq_n_s32(0);
+ if (M > 3) d30 = vdupq_n_s32(0), d31 = vdupq_n_s32(0);
+ if (M > 4) d40 = vdupq_n_s32(0), d41 = vdupq_n_s32(0);
+ for (size_t offs = 0; offs < srcC; offs += 4)
+ {
+ w0 = vld1q_s8(weight0);
+ w1 = vld1q_s8(weight1);
+ if (M > 0) s0 = Set4(src0 + offs), Madd4(d00, s0, w0), Madd4(d01, s0, w1);
+ if (M > 1) s0 = Set4(src1 + offs), Madd4(d10, s0, w0), Madd4(d11, s0, w1);
+ if (M > 2) s0 = Set4(src2 + offs), Madd4(d20, s0, w0), Madd4(d21, s0, w1);
+ if (M > 3) s0 = Set4(src3 + offs), Madd4(d30, s0, w0), Madd4(d31, s0, w1);
+ if (M > 4) s0 = Set4(src4 + offs), Madd4(d40, s0, w0), Madd4(d41, s0, w1);
+ weight0 += A, weight1 += A;
+ }
+ if (M > 0) SaveInput2(dst0 + 0 * F, dst1 + 0 * F, d00, d01, bias, norm, zero);
+ if (M > 1) SaveInput2(dst0 + 1 * F, dst1 + 1 * F, d10, d11, bias, norm, zero);
+ if (M > 2) SaveInput2(dst0 + 2 * F, dst1 + 2 * F, d20, d21, bias, norm, zero);
+ if (M > 3) SaveInput2(dst0 + 3 * F, dst1 + 3 * F, d30, d31, bias, norm, zero);
+ if (M > 4) SaveInput2(dst0 + 4 * F, dst1 + 4 * F, d40, d41, bias, norm, zero);
+ }
+ else
+ {
+ if (M > 0) d00 = vdupq_n_s32(0);
+ if (M > 1) d10 = vdupq_n_s32(0);
+ if (M > 2) d20 = vdupq_n_s32(0);
+ if (M > 3) d30 = vdupq_n_s32(0);
+ if (M > 4) d40 = vdupq_n_s32(0);
+ for (size_t offs = 0; offs < srcC; offs += 4)
+ {
+ w0 = vld1q_s8(weight0);
+ if (M > 0) s0 = Set4(src0 + offs), Madd4(d00, s0, w0);
+ if (M > 1) s0 = Set4(src1 + offs), Madd4(d10, s0, w0);
+ if (M > 2) s0 = Set4(src2 + offs), Madd4(d20, s0, w0);
+ if (M > 3) s0 = Set4(src3 + offs), Madd4(d30, s0, w0);
+ if (M > 4) s0 = Set4(src4 + offs), Madd4(d40, s0, w0);
+ weight0 += A;
+ }
+ if (M > 0) SaveInput1(dst0 + 0 * F, d00, bias, norm, zero);
+ if (M > 1) SaveInput1(dst0 + 1 * F, d10, bias, norm, zero);
+ if (M > 2) SaveInput1(dst0 + 2 * F, d20, bias, norm, zero);
+ if (M > 3) SaveInput1(dst0 + 3 * F, d30, bias, norm, zero);
+ if (M > 4) SaveInput1(dst0 + 4 * F, d40, bias, norm, zero);
+ }
+ }
+
+ typedef void(*QuantizedMergedConvolutionInput_2xM_Ptr)(const uint8_t* src0, const ConvParam& p, const AlgParam& a,
+ size_t dstC, const int8_t* weight0, const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero, uint8_t* dst0, uint8_t* dst1);
+
+ QuantizedMergedConvolutionInput_2xM_Ptr GetQuantizedMergedConvolutionInput_2xM(size_t M)
+ {
+ switch (M)
+ {
+ case 0: return NULL;
+ case 1: return QuantizedMergedConvolutionInput_2xM<1>;
+ case 2: return QuantizedMergedConvolutionInput_2xM<2>;
+ case 3: return QuantizedMergedConvolutionInput_2xM<3>;
+ case 4: return QuantizedMergedConvolutionInput_2xM<4>;
+ case 5: return QuantizedMergedConvolutionInput_2xM<5>;
+ }
+ assert(0);
+ return NULL;
+ }
+
+ void QuantizedMergedConvolutionInput_2(const uint8_t* src, const ConvParam& p, const AlgParam& a, size_t maC, size_t yBeg, size_t yEnd,
+ const int8_t* weight, const int32_t* bias, const float* norm, int32_t zero, int32_t* sum, uint8_t* dst)
+ {
+ size_t dstM = a.dsH - 1, dstS = a.dsH * p.dstW * F, srcC = a.isB ? a.iwStep : p.srcC, y0 = a.isB ? yBeg : 0;
+ float32x4_t _norm[2];
+ int32x4_t _bias[2], _zero = vdupq_n_s32(zero);
+ size_t yInt = Simd::Max(yBeg, AlignLo(yEnd, a.dsH)), n = 5;
+ size_t i1 = (yInt - yBeg) * p.dstW, in = AlignLoAny(i1, n), i = i1 - in;
+ size_t e1 = (yEnd - yInt) * p.dstW, en = AlignLoAny(e1, n), e = e1 - en;
+ QuantizedMergedConvolutionInput_2xM_Ptr quantizedMergedConvolutionInput_2xN = GetQuantizedMergedConvolutionInput_2xM(n);
+ QuantizedMergedConvolutionInput_2xM_Ptr quantizedMergedConvolutionInput_2xI = GetQuantizedMergedConvolutionInput_2xM(i);
+ QuantizedMergedConvolutionInput_2xM_Ptr quantizedMergedConvolutionInput_2xE = GetQuantizedMergedConvolutionInput_2xM(e);
+ for (size_t dc = 0; dc < maC; dc += DF)
+ {
+ size_t dC = Simd::Min(DF, maC - dc);
+ _bias[0] = vld1q_s32(bias + dc + 0);
+ _bias[1] = vld1q_s32(bias + dc + F);
+ _norm[0] = vld1q_f32(norm + dc + 0);
+ _norm[1] = vld1q_f32(norm + dc + F);
+ if (yInt > yBeg)
+ {
+ const uint8_t* src0 = src + (yBeg - y0) * p.srcW * srcC;
+ uint8_t* dst0 = dst + (yBeg & dstM) * p.dstW * F, * dst1 = dst0 + dstS;
+ for (size_t j = 0; j < in; j += n, src0 += srcC * n, dst0 += F * n, dst1 += F * n)
+ quantizedMergedConvolutionInput_2xN(src0, p, a, dC, weight, _bias, _norm, _zero, dst0, dst1);
+ if (in < i1)
+ quantizedMergedConvolutionInput_2xI(src0, p, a, dC, weight, _bias, _norm, _zero, dst0, dst1);
+ }
+ if (yEnd > yInt)
+ {
+ const uint8_t* src0 = src + (yInt - y0) * p.srcW * srcC;
+ uint8_t* dst0 = dst + (yInt & dstM) * p.dstW * F, * dst1 = dst0 + dstS;
+ for (size_t j = 0; j < en; j += n, src0 += srcC * n, dst0 += F * n, dst1 += F * n)
+ quantizedMergedConvolutionInput_2xN(src0, p, a, dC, weight, _bias, _norm, _zero, dst0, dst1);
+ if (en < e1)
+ quantizedMergedConvolutionInput_2xE(src0, p, a, dC, weight, _bias, _norm, _zero, dst0, dst1);
+ }
+ dst += a.dsH * p.dstW * DF;
+ weight += a.iwStep * DF;
+ }
+ }
+
+ //------------------------------------------------------------------------------------------------
+
+ void SetInputConvolution(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::InputConvolutionPtr& func)
+ {
+ func = QuantizedMergedConvolutionInput_2;
+ }
+ }
+#endif
+}
diff --git a/src/Simd/SimdNeonSynetQuantizedMergedConvolutionOutput.cpp b/src/Simd/SimdNeonSynetQuantizedMergedConvolutionOutput.cpp
new file mode 100644
index 0000000000..3a13f29333
--- /dev/null
+++ b/src/Simd/SimdNeonSynetQuantizedMergedConvolutionOutput.cpp
@@ -0,0 +1,224 @@
+/*
+* Simd Library (http://ermig1979.github.io/Simd).
+*
+* Copyright (c) 2011-2026 Yermalayeu Ihar.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+#include "Simd/SimdSynetQuantizedMergedConvolution.h"
+#include "Simd/SimdSynetQuantizeLinear.h"
+#include "Simd/SimdSynetQuantizedActivation.h"
+#include "Simd/SimdSynetConvolution8iCommon.h"
+#include "Simd/SimdSynetQuantizedAddCommon.h"
+#include "Simd/SimdSynet.h"
+#include "Simd/SimdMath.h"
+#include "Simd/SimdBase.h"
+#include "Simd/SimdCpu.h"
+#include "Simd/SimdLog.h"
+
+namespace Simd
+{
+#if defined(SIMD_NEON_ENABLE) && defined(SIMD_SYNET_ENABLE)
+ namespace Neon
+ {
+ typedef Base::SynetQuantizedMergedConvolution::AlgParam AlgParam;
+
+ //------------------------------------------------------------------------------------------------
+
+ template void QuantizedMergedConvolutionOutputConvolution_2xM(const uint8_t* src0, const ConvParam& p, const AlgParam& a,
+ size_t srcC, size_t dstC, int update, const int8_t* weight0, const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero, int32_t* buf, uint8_t* dst)
+ {
+ int32x4_t d00, d01, d10, d11, d20, d21, d30, d31, d40, d41;
+ uint8x16_t s0;
+ int8x16_t w0, w1;
+ size_t dS = a.maC * p.strideX, dB = a.owStep, dD = p.dstC;
+ const int8_t* weight1 = weight0 + AlignHi(srcC, 4) * F;
+ const uint8_t* src1 = src0 + 1 * dS;
+ const uint8_t* src2 = src0 + 2 * dS;
+ const uint8_t* src3 = src0 + 3 * dS;
+ const uint8_t* src4 = src0 + 4 * dS;
+ if (dstC > F)
+ {
+ if (update)
+ {
+ if (M > 0) d00 = vld1q_s32(buf + 0 * dB + 0), d01 = vld1q_s32(buf + 0 * dB + F);
+ if (M > 1) d10 = vld1q_s32(buf + 1 * dB + 0), d11 = vld1q_s32(buf + 1 * dB + F);
+ if (M > 2) d20 = vld1q_s32(buf + 2 * dB + 0), d21 = vld1q_s32(buf + 2 * dB + F);
+ if (M > 3) d30 = vld1q_s32(buf + 3 * dB + 0), d31 = vld1q_s32(buf + 3 * dB + F);
+ if (M > 4) d40 = vld1q_s32(buf + 4 * dB + 0), d41 = vld1q_s32(buf + 4 * dB + F);
+ }
+ else
+ {
+ if (M > 0) d00 = vdupq_n_s32(0), d01 = vdupq_n_s32(0);
+ if (M > 1) d10 = vdupq_n_s32(0), d11 = vdupq_n_s32(0);
+ if (M > 2) d20 = vdupq_n_s32(0), d21 = vdupq_n_s32(0);
+ if (M > 3) d30 = vdupq_n_s32(0), d31 = vdupq_n_s32(0);
+ if (M > 4) d40 = vdupq_n_s32(0), d41 = vdupq_n_s32(0);
+ }
+ for (size_t offs = 0; offs < srcC; offs += 4)
+ {
+ w0 = vld1q_s8(weight0);
+ w1 = vld1q_s8(weight1);
+ if (M > 0) s0 = Set4(src0 + offs), Madd4(d00, s0, w0), Madd4(d01, s0, w1);
+ if (M > 1) s0 = Set4(src1 + offs), Madd4(d10, s0, w0), Madd4(d11, s0, w1);
+ if (M > 2) s0 = Set4(src2 + offs), Madd4(d20, s0, w0), Madd4(d21, s0, w1);
+ if (M > 3) s0 = Set4(src3 + offs), Madd4(d30, s0, w0), Madd4(d31, s0, w1);
+ if (M > 4) s0 = Set4(src4 + offs), Madd4(d40, s0, w0), Madd4(d41, s0, w1);
+ weight0 += A;
+ weight1 += A;
+ }
+ if (dstC == DF)
+ {
+ if (M > 0) Save2(dst, buf, d00, d01, bias, norm, zero), buf += dB, dst += dD;
+ if (M > 1) Save2(dst, buf, d10, d11, bias, norm, zero), buf += dB, dst += dD;
+ if (M > 2) Save2(dst, buf, d20, d21, bias, norm, zero), buf += dB, dst += dD;
+ if (M > 3) Save2(dst, buf, d30, d31, bias, norm, zero), buf += dB, dst += dD;
+ if (M > 4) Save2(dst, buf, d40, d41, bias, norm, zero), buf += dB, dst += dD;
+ }
+ else
+ {
+ if (M > 0) Save2(dst, buf, d00, d01, bias, norm, zero, dstC - F), buf += dB, dst += dD;
+ if (M > 1) Save2(dst, buf, d10, d11, bias, norm, zero, dstC - F), buf += dB, dst += dD;
+ if (M > 2) Save2(dst, buf, d20, d21, bias, norm, zero, dstC - F), buf += dB, dst += dD;
+ if (M > 3) Save2(dst, buf, d30, d31, bias, norm, zero, dstC - F), buf += dB, dst += dD;
+ if (M > 4) Save2(dst, buf, d40, d41, bias, norm, zero, dstC - F), buf += dB, dst += dD;
+ }
+ }
+ else
+ {
+ if (update)
+ {
+ if (M > 0) d00 = vld1q_s32(buf + 0 * dB);
+ if (M > 1) d10 = vld1q_s32(buf + 1 * dB);
+ if (M > 2) d20 = vld1q_s32(buf + 2 * dB);
+ if (M > 3) d30 = vld1q_s32(buf + 3 * dB);
+ if (M > 4) d40 = vld1q_s32(buf + 4 * dB);
+ }
+ else
+ {
+ if (M > 0) d00 = vdupq_n_s32(0);
+ if (M > 1) d10 = vdupq_n_s32(0);
+ if (M > 2) d20 = vdupq_n_s32(0);
+ if (M > 3) d30 = vdupq_n_s32(0);
+ if (M > 4) d40 = vdupq_n_s32(0);
+ }
+ for (size_t offs = 0; offs < srcC; offs += 4)
+ {
+ w0 = vld1q_s8(weight0);
+ if (M > 0) s0 = Set4(src0 + offs), Madd4(d00, s0, w0);
+ if (M > 1) s0 = Set4(src1 + offs), Madd4(d10, s0, w0);
+ if (M > 2) s0 = Set4(src2 + offs), Madd4(d20, s0, w0);
+ if (M > 3) s0 = Set4(src3 + offs), Madd4(d30, s0, w0);
+ if (M > 4) s0 = Set4(src4 + offs), Madd4(d40, s0, w0);
+ weight0 += A;
+ }
+ if (dstC == F)
+ {
+ if (M > 0) Save1(dst, buf, d00, bias, norm, zero), buf += dB, dst += dD;
+ if (M > 1) Save1(dst, buf, d10, bias, norm, zero), buf += dB, dst += dD;
+ if (M > 2) Save1(dst, buf, d20, bias, norm, zero), buf += dB, dst += dD;
+ if (M > 3) Save1(dst, buf, d30, bias, norm, zero), buf += dB, dst += dD;
+ if (M > 4) Save1(dst, buf, d40, bias, norm, zero), buf += dB, dst += dD;
+ }
+ else
+ {
+ if (M > 0) Save1(dst, buf, d00, bias, norm, zero, dstC), buf += dB, dst += dD;
+ if (M > 1) Save1(dst, buf, d10, bias, norm, zero, dstC), buf += dB, dst += dD;
+ if (M > 2) Save1(dst, buf, d20, bias, norm, zero, dstC), buf += dB, dst += dD;
+ if (M > 3) Save1(dst, buf, d30, bias, norm, zero, dstC), buf += dB, dst += dD;
+ if (M > 4) Save1(dst, buf, d40, bias, norm, zero, dstC), buf += dB, dst += dD;
+ }
+ }
+ }
+
+ typedef void(*QuantizedMergedConvolutionOutputConvolution_2xM_Ptr)(const uint8_t* src0, const ConvParam& p, const AlgParam& a,
+ size_t srcC, size_t dstC, int update, const int8_t* weight0, const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero, int32_t* buf, uint8_t* dst);
+
+ template QuantizedMergedConvolutionOutputConvolution_2xM_Ptr GetQuantizedMergedConvolutionOutputConvolution_2xM(size_t M)
+ {
+ switch (M)
+ {
+ case 0: return NULL;
+ case 1: return QuantizedMergedConvolutionOutputConvolution_2xM;
+ case 2: return QuantizedMergedConvolutionOutputConvolution_2xM;
+ case 3: return QuantizedMergedConvolutionOutputConvolution_2xM;
+ case 4: return QuantizedMergedConvolutionOutputConvolution_2xM;
+ case 5: return QuantizedMergedConvolutionOutputConvolution_2xM;
+ }
+ assert(0);
+ return NULL;
+ }
+
+ template void QuantizedMergedConvolutionOutputConvolution_2(const uint8_t* src, const ConvParam& p, const AlgParam& a, size_t maC, size_t yBeg, size_t yEnd,
+ int update, const int8_t* weight, const int32_t* bias, const float* norm, int32_t zero, int32_t* buf, uint8_t* dst)
+ {
+ size_t n = 5, n1 = (yEnd - yBeg) * p.dstW, nn = AlignLoAny(n1, n), m = n1 - nn;
+ QuantizedMergedConvolutionOutputConvolution_2xM_Ptr outputConvolution1x1_2xN = GetQuantizedMergedConvolutionOutputConvolution_2xM(n);
+ QuantizedMergedConvolutionOutputConvolution_2xM_Ptr outputConvolution1x1_2xM = GetQuantizedMergedConvolutionOutputConvolution_2xM(m);
+ float32x4_t _norm[2];
+ int32x4_t _bias[2], _zero = vdupq_n_s32(zero);
+ for (size_t dc = 0; dc < p.dstC; dc += DF)
+ {
+ size_t dC = Simd::Min(DF, p.dstC - dc);
+ _bias[0] = vld1q_s32(bias + dc + 0);
+ _bias[1] = vld1q_s32(bias + dc + F);
+ _norm[0] = vld1q_f32(norm + dc + 0);
+ _norm[1] = vld1q_f32(norm + dc + F);
+ const uint8_t* s = src;
+ int32_t* b = buf + dc + yBeg * p.dstW * a.owStep;
+ uint8_t* d = dst + dc + yBeg * p.dstW * p.dstC;
+ size_t i = 0;
+ for (; i < nn; i += n, s += a.maC * n, b += a.owStep * n, d += p.dstC * n)
+ outputConvolution1x1_2xN(s, p, a, maC, dC, update, weight, _bias, _norm, _zero, b, d);
+ for (; i < n1; i += m, s += a.maC * m, b += a.owStep * m, d += p.dstC * m)
+ outputConvolution1x1_2xM(s, p, a, maC, dC, update, weight, _bias, _norm, _zero, b, d);
+ weight += AlignHi(maC, 4) * DF;
+ }
+ }
+
+ //------------------------------------------------------------------------------------------------
+
+ void QuantizedMergedConvolutionAddInputToOutput(const uint8_t* a, float aNorm, const uint8_t* b, float bNorm, const ConvParam& p, size_t yBeg, size_t yEnd, float dBias, uint8_t* dst)
+ {
+ float32x4_t _aNorm = vdupq_n_f32(aNorm), _bNorm = vdupq_n_f32(bNorm), _dBias = vdupq_n_f32(dBias);
+ size_t beg = yBeg * p.dstW * p.dstC, end = yEnd * p.dstW * p.dstC;
+ size_t i = beg, end4 = beg + AlignLo(end - beg, 4), end16 = beg + AlignLo(end - beg, 16);
+ for (; i < end16; i += 16)
+ QuantizedAdd8u8u8u16(a + i, _aNorm, b + i, _bNorm, _dBias, dst + i);
+ for (; i < end4; i += 4)
+ QuantizedAdd8u8u8u4(a + i, _aNorm, b + i, _bNorm, _dBias, dst + i);
+ for (; i < end; i += 1)
+ QuantizedAdd8u8u8u1(a + i, _aNorm, b + i, _bNorm, _dBias, dst + i);
+ }
+
+ //------------------------------------------------------------------------------------------------
+
+ void SetOutputConvolution(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::OutputConvolutionPtr* funcs)
+ {
+ funcs[0] = QuantizedMergedConvolutionOutputConvolution_2;
+ funcs[1] = QuantizedMergedConvolutionOutputConvolution_2;
+ }
+
+ void SetAddInputToOutput(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::AddInputToOutputPtr& func)
+ {
+ func = QuantizedMergedConvolutionAddInputToOutput;
+ }
+ }
+#endif
+}
diff --git a/src/Simd/SimdSynetQuantizedActivation.h b/src/Simd/SimdSynetQuantizedActivation.h
index 5a6f5cb98f..6cff488276 100644
--- a/src/Simd/SimdSynetQuantizedActivation.h
+++ b/src/Simd/SimdSynetQuantizedActivation.h
@@ -684,6 +684,105 @@ namespace Simd
}
}
#endif
+
+#ifdef SIMD_NEON_ENABLE
+ namespace Neon
+ {
+ template struct QuntizedTerm8i
+ {
+ template static SIMD_INLINE void Save(uint8_t* dst, int32_t* buf, int32x4_t sum,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero);
+ template static SIMD_INLINE void Save(uint8_t* dst, int32_t* buf, int32x4_t sum,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero, size_t tail);
+
+ static SIMD_INLINE void Save(uint8_t* dst, int32_t* buf, int32x4_t sum0, int32x4_t sum1,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero);
+ };
+
+ template <> struct QuntizedTerm8i
+ {
+ template static SIMD_INLINE void Save(uint8_t* dst, int32_t* buf, int32x4_t sum,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero)
+ {
+ int32x4_t i32 = vaddq_s32(NearbyInt(vmulq_f32(vcvtq_f32_s32(vaddq_s32(sum, bias[index])), norm[index])), zero);
+ uint8x8_t u8 = vqmovun_s16(vcombine_s16(vqmovn_s32(i32), vdup_n_s16(0)));
+ ((int32_t*)dst)[index] = vget_lane_s32(vreinterpret_s32_u8(u8), 0);
+ }
+
+ template static SIMD_INLINE void Save(uint8_t* dst, int32_t* buf, int32x4_t sum,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero, size_t tail)
+ {
+ uint8_t tmp[F];
+ QuntizedTerm8i::Save(tmp - index * F, buf, sum, bias, norm, zero);
+ for (size_t i = 0; i < tail; ++i)
+ dst[index * F + i] = tmp[i];
+ }
+
+ static SIMD_INLINE void Save(uint8_t* dst, int32_t* buf, int32x4_t sum0, int32x4_t sum1,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero)
+ {
+ int32x4_t d0 = vaddq_s32(NearbyInt(vmulq_f32(vcvtq_f32_s32(vaddq_s32(sum0, bias[0])), norm[0])), zero);
+ int32x4_t d1 = vaddq_s32(NearbyInt(vmulq_f32(vcvtq_f32_s32(vaddq_s32(sum1, bias[1])), norm[1])), zero);
+ uint8x8_t u8 = vqmovun_s16(vcombine_s16(vqmovn_s32(d0), vqmovn_s32(d1)));
+ vst1_u8(dst, u8);
+ }
+ };
+
+ template <> struct QuntizedTerm8i
+ {
+ template static SIMD_INLINE void Save(uint8_t* dst, int32_t* buf, int32x4_t sum,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero)
+ {
+ vst1q_s32(buf + index * F, sum);
+ }
+
+ template static SIMD_INLINE void Save(uint8_t* dst, int32_t* buf, int32x4_t sum,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero, size_t tail)
+ {
+ int32_t tmp[F];
+ vst1q_s32(tmp, sum);
+ for (size_t i = 0; i < tail; ++i)
+ buf[index * F + i] = tmp[i];
+ }
+
+ static SIMD_INLINE void Save(uint8_t* dst, int32_t* buf, int32x4_t sum0, int32x4_t sum1,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero)
+ {
+ vst1q_s32(buf + 0 * F, sum0);
+ vst1q_s32(buf + 1 * F, sum1);
+ }
+ };
+
+ template
+ SIMD_INLINE void Save1(uint8_t* dst, int32_t* buf, int32x4_t sum,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero)
+ {
+ QuntizedTerm8i::template Save<0>(dst, buf, sum, bias, norm, zero);
+ }
+
+ template
+ SIMD_INLINE void Save1(uint8_t* dst, int32_t* buf, int32x4_t sum,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero, size_t tail)
+ {
+ QuntizedTerm8i::template Save<0>(dst, buf, sum, bias, norm, zero, tail);
+ }
+
+ template
+ SIMD_INLINE void Save2(uint8_t* dst, int32_t* buf, int32x4_t sum0, int32x4_t sum1,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero)
+ {
+ QuntizedTerm8i::Save(dst, buf, sum0, sum1, bias, norm, zero);
+ }
+
+ template
+ SIMD_INLINE void Save2(uint8_t* dst, int32_t* buf, int32x4_t sum0, int32x4_t sum1,
+ const int32x4_t* bias, const float32x4_t* norm, const int32x4_t& zero, size_t tail)
+ {
+ QuntizedTerm8i::template Save<0>(dst, buf, sum0, bias, norm, zero);
+ QuntizedTerm8i::template Save<1>(dst, buf, sum1, bias, norm, zero, tail);
+ }
+ }
+#endif
}
#endif
diff --git a/src/Simd/SimdSynetQuantizedAddCommon.h b/src/Simd/SimdSynetQuantizedAddCommon.h
index 1ca45a4ab2..8897f68112 100644
--- a/src/Simd/SimdSynetQuantizedAddCommon.h
+++ b/src/Simd/SimdSynetQuantizedAddCommon.h
@@ -148,7 +148,7 @@ namespace Simd
SIMD_INLINE int32x4_t QuantizedAdd(int32x4_t a, float32x4_t adScale, int32x4_t b, float32x4_t bdScale, float32x4_t term)
{
float32x4_t fa = vmlaq_f32(term, vcvtq_f32_s32(a), adScale);
- return Round(vmlaq_f32(fa, vcvtq_f32_s32(b), bdScale));
+ return NearbyInt(vmlaq_f32(fa, vcvtq_f32_s32(b), bdScale));
}
SIMD_INLINE void QuantizedAdd8u8u8u1(const uint8_t* a, float32x4_t adScale, const uint8_t* b, float32x4_t bdScale, float32x4_t term, uint8_t* dst)
diff --git a/src/Simd/SimdSynetQuantizedMergedConvolution.h b/src/Simd/SimdSynetQuantizedMergedConvolution.h
index c6f8b6a093..e0b6c4a868 100644
--- a/src/Simd/SimdSynetQuantizedMergedConvolution.h
+++ b/src/Simd/SimdSynetQuantizedMergedConvolution.h
@@ -1,7 +1,7 @@
/*
* Simd Library (http://ermig1979.github.io/Simd).
*
-* Copyright (c) 2011-2025 Yermalayeu Ihar.
+* Copyright (c) 2011-2026 Yermalayeu Ihar.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -445,6 +445,49 @@ namespace Simd
#ifdef SIMD_NEON_ENABLE
namespace Neon
{
+ void SetInputConvolution(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::InputConvolutionPtr& func);
+
+ void SetDepthwisePreprocess(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::DepthwisePreprocessPtr& func);
+
+ void SetDepthwiseConvolution(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::DepthwiseConvolutionPtr& func);
+
+ void SetOutputConvolution(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::OutputConvolutionPtr* funcs);
+
+ void SetAddInputToOutput(const ConvParam& p, const Base::SynetQuantizedMergedConvolution::AlgParam& a, Base::SynetQuantizedMergedConvolution::AddInputToOutputPtr& func);
+
+ //------------------------------------------------------------------------------------------------
+
+ class SynetQuantizedMergedConvolutionCdc : public Base::SynetQuantizedMergedConvolutionCdc
+ {
+ public:
+ SynetQuantizedMergedConvolutionCdc(const MergConvParam& p);
+
+ virtual String Ext() const { return "Neon"; }
+ };
+
+ //------------------------------------------------------------------------------------------------
+
+ class SynetQuantizedMergedConvolutionCd : public Base::SynetQuantizedMergedConvolutionCd
+ {
+ public:
+ SynetQuantizedMergedConvolutionCd(const MergConvParam& p);
+
+ virtual String Ext() const { return "Neon"; }
+ };
+
+ //------------------------------------------------------------------------------------------------
+
+ class SynetQuantizedMergedConvolutionDc : public Base::SynetQuantizedMergedConvolutionDc
+ {
+ public:
+ SynetQuantizedMergedConvolutionDc(const MergConvParam& p);
+
+ virtual String Ext() const { return "Neon"; }
+ };
+
+ //------------------------------------------------------------------------------------------------
+
+ void* SynetQuantizedMergedConvolutionInit(size_t batch, const SimdConvolutionParameters* convs, size_t count, int add);
}
#endif
}
diff --git a/src/Test/TestSynetQuantizedMergedConvolution.cpp b/src/Test/TestSynetQuantizedMergedConvolution.cpp
index ff74385219..ca88143128 100644
--- a/src/Test/TestSynetQuantizedMergedConvolution.cpp
+++ b/src/Test/TestSynetQuantizedMergedConvolution.cpp
@@ -384,6 +384,11 @@ namespace Test
result = result && SynetQuantizedMergedConvolutionForwardAutoTest(f, FUNC_QMC(Simd::AmxBf16::SynetQuantizedMergedConvolutionInit), FUNC_QMC(SimdSynetQuantizedMergedConvolutionInit));
#endif
+#ifdef SIMD_NEON_ENABLE
+ if (Simd::Neon::Enable && TestNeon(options))
+ result = result && SynetQuantizedMergedConvolutionForwardAutoTest(t, FUNC_QMC(Simd::Neon::SynetQuantizedMergedConvolutionInit), FUNC_QMC(SimdSynetQuantizedMergedConvolutionInit));
+#endif
+
return result;
}
#endif