/*
 * SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **************************************************************************
 * Modified from Deformable DETR
 * Copyright (c) 2020-2023 SenseTime. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 [see LICENSE for details]
 * https://github.com/fundamentalvision/Deformable-DETR/blob/main/LICENSE
 **************************************************************************
 * Modified from DCN (https://github.com/msracver/Deformable-ConvNets)
 * Copyright (c) 2018-2023 Microsoft
 **************************************************************************
*/

#ifndef TRT_MULTISCALE_DEFORMABLE_IM2COL_CUDA_H
#define TRT_MULTISCALE_DEFORMABLE_IM2COL_CUDA_H

#include <algorithm>
#include <cstdio>
#include <cstring>

#include <cuda_fp16.h>
#include <cuda_runtime.h>

#include "common/checkMacrosPlugin.h"

#define CUDA_KERNEL_LOOP(i, n) for (int32_t i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); i += blockDim.x * gridDim.x)

constexpr int32_t kCUDA_NUM_THREADS{768};
inline int32_t GET_BLOCKS(int32_t const N, int32_t const numThreads)
{
    return (N + numThreads - 1) / numThreads;
}

template <typename scalar_t>
__device__ scalar_t ms_deform_attn_im2col_bilinear(scalar_t const*& bottomData, int32_t const& height, int32_t const& width,
    int32_t const& nHeads, int32_t const& channels, scalar_t const& h, scalar_t const& w, int32_t const& m, int32_t const& c)
{
    int32_t const hLow = floor(h);
    int32_t const wLow = floor(w);
    int32_t const hHigh = hLow + 1;
    int32_t const wHigh = wLow + 1;

    scalar_t const lh = h - hLow;
    scalar_t const lw = w - wLow;
    scalar_t const hh = 1 - lh, hw = 1 - lw;

    int32_t const wStride = nHeads * channels;
    int32_t const hStride = width * wStride;
    int32_t const hLowPtrOffset = hLow * hStride;
    int32_t const hHighPtrOffset = hLowPtrOffset + hStride;
    int32_t const wLowPtrOffset = wLow * wStride;
    int32_t const wHighPtrOffset = wLowPtrOffset + wStride;
    int32_t const basePtr = m * channels + c;

    scalar_t v1 = 0;
    if (hLow >= 0 && wLow >= 0)
    {
        int32_t const ptr1 = hLowPtrOffset + wLowPtrOffset + basePtr;
        v1 = bottomData[ptr1];
    }
    scalar_t v2 = 0;
    if (hLow >= 0 && wHigh <= width - 1)
    {
        int32_t const ptr2 = hLowPtrOffset + wHighPtrOffset + basePtr;
        v2 = bottomData[ptr2];
    }
    scalar_t v3 = 0;
    if (hHigh <= height - 1 && wLow >= 0)
    {
        int32_t const ptr3 = hHighPtrOffset + wLowPtrOffset + basePtr;
        v3 = bottomData[ptr3];
    }
    scalar_t v4 = 0;
    if (hHigh <= height - 1 && wHigh <= width - 1)
    {
        int32_t const ptr4 = hHighPtrOffset + wHighPtrOffset + basePtr;
        v4 = bottomData[ptr4];
    }

    scalar_t const w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw;

    scalar_t const val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4);
    return val;
}

template <>
__device__ __half ms_deform_attn_im2col_bilinear<__half>(__half const*& bottomData, int32_t const& height, int32_t const& width,
    int32_t const& nHeads, int32_t const& channels, __half const& h, __half const& w, int32_t const& m, int32_t const& c)
{
    int32_t const hLow = __half2int_rd(h);
    int32_t const wLow = __half2int_rd(w);
    int32_t const hHigh = hLow + 1;
    int32_t const wHigh = wLow + 1;

    __half const kZERO = __int2half_rz(0);
    __half const one = __int2half_rz(1);

#if __CUDA_ARCH__>=530
    __half const lh = __hsub(h, __int2half_rd(hLow));
    __half const lw = __hsub(w, __int2half_rd(wLow));
    __half const hh = __hsub(one, lh), hw = __hsub(one, lw);
#else
    __half const lh = __float2half(__half2float(h) - hLow);
    __half const lw = __float2half(__half2float(w) - wLow);
    __half const hh = __float2half(__half2float(one) - __half2float(lh));
    __half const hw = __float2half(__half2float(one) - __half2float(lw));
#endif
    int32_t const wStride = nHeads * channels;
    int32_t const hStride = width * wStride;
    int32_t const hLowPtrOffset = hLow * hStride;
    int32_t const hHighPtrOffset = hLowPtrOffset + hStride;
    int32_t const wLowPtrOffset = wLow * wStride;
    int32_t const wHighPtrOffset = wLowPtrOffset + wStride;
    int32_t const basePtr = m * channels + c;

    __half v1 = kZERO;
    if (hLow >= 0 && wLow >= 0)
    {
        int32_t const ptr1 = hLowPtrOffset + wLowPtrOffset + basePtr;
        v1 = bottomData[ptr1];
    }
    __half v2 = kZERO;
    if (hLow >= 0 && wHigh <= width - 1)
    {
        int32_t const ptr2 = hLowPtrOffset + wHighPtrOffset + basePtr;
        v2 = bottomData[ptr2];
    }
    __half v3 = kZERO;
    if (hHigh <= height - 1 && wLow >= 0)
    {
        int32_t const ptr3 = hHighPtrOffset + wLowPtrOffset + basePtr;
        v3 = bottomData[ptr3];
    }
    __half v4 = kZERO;
    if (hHigh <= height - 1 && wHigh <= width - 1)
    {
        int32_t const ptr4 = hHighPtrOffset + wHighPtrOffset + basePtr;
        v4 = bottomData[ptr4];
    }

#if __CUDA_ARCH__>=530
    __half w1 = __hmul(__hmul(hh, hw), v1);
    __half w2 = __hmul(__hmul(hh, lw), v2);
    __half w3 = __hmul(__hmul(lh, hw), v3);
    __half w4 = __hmul(__hmul(lh, lw), v4);

    w1 = __hadd(w1, w2);
    w3 = __hadd(w3, w4);

    __half const val = __hadd(w1, w3);
#else
    __half w1 = __float2half((__half2float(hh) * __half2float(hw)) * __half2float(v1));
    __half w2 = __float2half((__half2float(hh) * __half2float(lw)) * __half2float(v2));
    __half w3 = __float2half((__half2float(lh) * __half2float(hw)) * __half2float(v3));
    __half w4 = __float2half((__half2float(lh) * __half2float(lw)) * __half2float(v4));
    
    w1 = __float2half(__half2float(w1) + __half2float(w2));
    w3 = __float2half(__half2float(w3) + __half2float(w4));

    __half const val = __float2half(__half2float(w1) + __half2float(w3));
#endif 
    return val;
}

template <typename scalar_t>
__device__ void ms_deform_attn_col2im_bilinear(scalar_t const*& bottomData, int32_t const& height, int32_t const& width,
    int32_t const& nHeads, int32_t const& channels, scalar_t const& h, scalar_t const& w, int32_t const& m, int32_t const& c,
    scalar_t const& topGrad, scalar_t const& attnWeight, scalar_t*& gradValue, scalar_t* gradSamplingLoc,
    scalar_t* gradAttnWeight)
{
    int32_t const hLow = floor(h);
    int32_t const wLow = floor(w);
    int32_t const hHigh = hLow + 1;
    int32_t const wHigh = wLow + 1;

    scalar_t const lh = h - hLow;
    scalar_t const lw = w - wLow;
    scalar_t const hh = 1 - lh, hw = 1 - lw;

    int32_t const wStride = nHeads * channels;
    int32_t const hStride = width * wStride;
    int32_t const hLowPtrOffset = hLow * hStride;
    int32_t const hHighPtrOffset = hLowPtrOffset + hStride;
    int32_t const wLowPtrOffset = wLow * wStride;
    int32_t const wHighPtrOffset = wLowPtrOffset + wStride;
    int32_t const basePtr = m * channels + c;

    scalar_t const w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw;
    scalar_t const topGradvalue = topGrad * attnWeight;
    scalar_t gradHWeight = 0, gradWWeight = 0;

    scalar_t v1 = 0;
    if (hLow >= 0 && wLow >= 0)
    {
        int32_t const ptr1 = hLowPtrOffset + wLowPtrOffset + basePtr;
        v1 = bottomData[ptr1];
        gradHWeight -= hw * v1;
        gradWWeight -= hh * v1;
        atomicAdd(gradValue + ptr1, w1 * topGradvalue);
    }
    scalar_t v2 = 0;
    if (hLow >= 0 && wHigh <= width - 1)
    {
        int32_t const ptr2 = hLowPtrOffset + wHighPtrOffset + basePtr;
        v2 = bottomData[ptr2];
        gradHWeight -= lw * v2;
        gradWWeight += hh * v2;
        atomicAdd(gradValue + ptr2, w2 * topGradvalue);
    }
    scalar_t v3 = 0;
    if (hHigh <= height - 1 && wLow >= 0)
    {
        int32_t const ptr3 = hHighPtrOffset + wLowPtrOffset + basePtr;
        v3 = bottomData[ptr3];
        gradHWeight += hw * v3;
        gradWWeight -= lh * v3;
        atomicAdd(gradValue + ptr3, w3 * topGradvalue);
    }
    scalar_t v4 = 0;
    if (hHigh <= height - 1 && wHigh <= width - 1)
    {
        int32_t const ptr4 = hHighPtrOffset + wHighPtrOffset + basePtr;
        v4 = bottomData[ptr4];
        gradHWeight += lw * v4;
        gradWWeight += lh * v4;
        atomicAdd(gradValue + ptr4, w4 * topGradvalue);
    }

    scalar_t const val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4);
    *gradAttnWeight = topGrad * val;
    *gradSamplingLoc = width * gradWWeight * topGradvalue;
    *(gradSamplingLoc + 1) = height * gradHWeight * topGradvalue;
}

template <typename scalar_t>
__device__ void ms_deform_attn_col2im_bilinear_gm(scalar_t const*& bottomData, int32_t const& height, int32_t const& width,
    int32_t const& nHeads, int32_t const& channels, scalar_t const& h, scalar_t const& w, int32_t const& m, int32_t const& c,
    scalar_t const& topGrad, scalar_t const& attnWeight, scalar_t*& gradValue, scalar_t* gradSamplingLoc,
    scalar_t* gradAttnWeight)
{
    int32_t const hLow = floor(h);
    int32_t const wLow = floor(w);
    int32_t const hHigh = hLow + 1;
    int32_t const wHigh = wLow + 1;

    scalar_t const lh = h - hLow;
    scalar_t const lw = w - wLow;
    scalar_t const hh = 1 - lh, hw = 1 - lw;

    int32_t const wStride = nHeads * channels;
    int32_t const hStride = width * wStride;
    int32_t const hLowPtrOffset = hLow * hStride;
    int32_t const hHighPtrOffset = hLowPtrOffset + hStride;
    int32_t const wLowPtrOffset = wLow * wStride;
    int32_t const wHighPtrOffset = wLowPtrOffset + wStride;
    int32_t const basePtr = m * channels + c;

    scalar_t const w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw;
    scalar_t const topGradvalue = topGrad * attnWeight;
    scalar_t gradHWeight = 0, gradWWeight = 0;

    scalar_t v1 = 0;
    if (hLow >= 0 && wLow >= 0)
    {
        int32_t const ptr1 = hLowPtrOffset + wLowPtrOffset + basePtr;
        v1 = bottomData[ptr1];
        gradHWeight -= hw * v1;
        gradWWeight -= hh * v1;
        atomicAdd(gradValue + ptr1, w1 * topGradvalue);
    }
    scalar_t v2 = 0;
    if (hLow >= 0 && wHigh <= width - 1)
    {
        int32_t const ptr2 = hLowPtrOffset + wHighPtrOffset + basePtr;
        v2 = bottomData[ptr2];
        gradHWeight -= lw * v2;
        gradWWeight += hh * v2;
        atomicAdd(gradValue + ptr2, w2 * topGradvalue);
    }
    scalar_t v3 = 0;
    if (hHigh <= height - 1 && wLow >= 0)
    {
        int32_t const ptr3 = hHighPtrOffset + wLowPtrOffset + basePtr;
        v3 = bottomData[ptr3];
        gradHWeight += hw * v3;
        gradWWeight -= lh * v3;
        atomicAdd(gradValue + ptr3, w3 * topGradvalue);
    }
    scalar_t v4 = 0;
    if (hHigh <= height - 1 && wHigh <= width - 1)
    {
        int32_t const ptr4 = hHighPtrOffset + wHighPtrOffset + basePtr;
        v4 = bottomData[ptr4];
        gradHWeight += lw * v4;
        gradWWeight += lh * v4;
        atomicAdd(gradValue + ptr4, w4 * topGradvalue);
    }

    scalar_t const val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4);
    atomicAdd(gradAttnWeight, topGrad * val);
    atomicAdd(gradSamplingLoc, width * gradWWeight * topGradvalue);
    atomicAdd(gradSamplingLoc + 1, height * gradHWeight * topGradvalue);
}

#if 1
template <typename scalar_t>
__global__ void ms_deformable_im2col_gpu_kernel(int32_t const n, scalar_t const* dataValue,
    int32_t const* dataSpatialShapes, int32_t const* dataLevelStartIndex, scalar_t const* dataSamplingLoc,
    scalar_t const* dataAttnWeight, int32_t const batchSize, int32_t const spatialSize, int32_t const numHeads, int32_t const channels,
    int32_t const numLevels, int32_t const numQuery, int32_t const numPoint, scalar_t* dataCol)
{
    CUDA_KERNEL_LOOP(index, n)
    {
        int32_t _temp = index;
        int32_t const cCol = _temp % channels;
        _temp /= channels;
        int32_t const samplingIndex = _temp;
        int32_t const mCol = _temp % numHeads;
        _temp /= numHeads;
        _temp /= numQuery;
        int32_t const bCol = _temp;

        scalar_t* dataColPtr = dataCol + index;
        int32_t dataWeightPtr = samplingIndex * numLevels * numPoint;
        int32_t dataLocWPtr = dataWeightPtr << 1;
        int32_t const qidStride = numHeads * channels;
        int32_t const dataValuePtrInitOffset = bCol * spatialSize * qidStride;
        scalar_t col = 0;

        for (int32_t lCol = 0; lCol < numLevels; ++lCol)
        {
            int32_t const levelStartId = dataLevelStartIndex[lCol];
            int32_t const spatialHPtr = lCol << 1;
            int32_t const spatialH = dataSpatialShapes[spatialHPtr];
            int32_t const spatialW = dataSpatialShapes[spatialHPtr + 1];
            scalar_t const* dataValuePtr = dataValue + (dataValuePtrInitOffset + levelStartId * qidStride);
            for (int32_t pCol = 0; pCol < numPoint; ++pCol)
            {
                scalar_t const locW = dataSamplingLoc[dataLocWPtr];
                scalar_t const locH = dataSamplingLoc[dataLocWPtr + 1];
                scalar_t const weight = dataAttnWeight[dataWeightPtr];

                scalar_t const hIm = locH * spatialH - 0.5;
                scalar_t const wIm = locW * spatialW - 0.5;

                if (hIm > -1 && wIm > -1 && hIm < spatialH && wIm < spatialW)
                {
                    col += ms_deform_attn_im2col_bilinear(
                               dataValuePtr, spatialH, spatialW, numHeads, channels, hIm, wIm, mCol, cCol)
                        * weight;
                }

                dataWeightPtr += 1;
                dataLocWPtr += 2;
            }
        }
        *dataColPtr = col;
    }
}

template <>
__global__ void ms_deformable_im2col_gpu_kernel<__half>(int32_t const n, __half const* dataValue,
    int32_t const* dataSpatialShapes, int32_t const* dataLevelStartIndex, __half const* dataSamplingLoc,
    __half const* dataAttnWeight, int32_t const batchSize, int32_t const spatialSize, int32_t const numHeads, int32_t const channels,
    int32_t const numLevels, int32_t const numQuery, int32_t const numPoint, __half* dataCol)
{
    CUDA_KERNEL_LOOP(index, n)
    {
        int32_t _temp = index;
        int32_t const cCol = _temp % channels;
        _temp /= channels;
        int32_t const samplingIndex = _temp;
        int32_t const mCol = _temp % numHeads;
        _temp /= numHeads;
        _temp /= numQuery;
        int32_t const bCol = _temp;

        __half* dataColPtr = dataCol + index;
        int32_t dataWeightPtr = samplingIndex * numLevels * numPoint;
        int32_t dataLocWPtr = dataWeightPtr << 1;
        int32_t const qidStride = numHeads * channels;
        int32_t const dataValuePtrInitOffset = bCol * spatialSize * qidStride;
        __half const kZERO_POINT_FIVE = __float2half(0.5f);
        __half const kMINUS_ONE = __float2half(-1.0f);
        __half const kZERO = __int2half_rz(0);
        __half tpVal = kZERO;
        __half col = kZERO;

        for (int32_t lCol = 0; lCol < numLevels; ++lCol)
        {
            int32_t const levelStartId = dataLevelStartIndex[lCol];
            int32_t const spatialHPtr = lCol << 1;
            int32_t const spatialH = dataSpatialShapes[spatialHPtr];
            int32_t const spatialW = dataSpatialShapes[spatialHPtr + 1];
            __half const spatialHHalf = __int2half_rd(spatialH);
            __half const spatialWHalf = __int2half_rd(spatialW);
            __half const* dataValuePtr = dataValue + (dataValuePtrInitOffset + levelStartId * qidStride);
            for (int32_t pCol = 0; pCol < numPoint; ++pCol)
            {
                __half const locW = dataSamplingLoc[dataLocWPtr];
                __half const locH = dataSamplingLoc[dataLocWPtr + 1];
                __half const weight = dataAttnWeight[dataWeightPtr];
#if __CUDA_ARCH__ >= 530
                __half const hIm = __hsub(__hmul(locH, spatialHHalf), kZERO_POINT_FIVE);
                __half const wIm = __hsub(__hmul(locW, spatialWHalf), kZERO_POINT_FIVE);

                if (__hgt(hIm, kMINUS_ONE) && __hgt(wIm, kMINUS_ONE) && __hlt(hIm, spatialHHalf)
                    && __hlt(wIm, spatialWHalf))
                {
                    tpVal = ms_deform_attn_im2col_bilinear(
                        dataValuePtr, spatialH, spatialW, numHeads, channels, hIm, wIm, mCol, cCol);
                    col = __hadd(col, __hmul(tpVal, weight));
                }
#else
                __half const hIm = __float2half(__half2float(locH) * __half2float(spatialHHalf) - __half2float(kZERO_POINT_FIVE));
                __half const wIm = __float2half(__half2float(locW) * __half2float(spatialWHalf) - __half2float(kZERO_POINT_FIVE));

                if((__half2float(hIm)>__half2float(kMINUS_ONE)) && (__half2float(wIm)>__half2float(kMINUS_ONE))
                    && (__half2float(hIm)<__half2float(spatialHHalf)) && (__half2float(wIm)<__half2float(spatialWHalf)))
                {
                    tpVal = ms_deform_attn_im2col_bilinear(
                        dataValuePtr, spatialH, spatialW, numHeads, channels, hIm, wIm, mCol, cCol);
                    col = __float2half(__half2float(col) + (__half2float(tpVal) * __half2float(weight)));
                }
#endif 
                dataWeightPtr += 1;
                dataLocWPtr += 2;
            }
        }
        *dataColPtr = col;
    }
}
#endif

template <typename scalar_t, uint32_t blockSize>
__global__ void ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1(int32_t const n, scalar_t const* grad_col,
    scalar_t const* dataValue, int32_t const* dataSpatialShapes, int32_t const* dataLevelStartIndex,
    scalar_t const* dataSamplingLoc, scalar_t const* dataAttnWeight, int32_t const batchSize, int32_t const spatialSize,
    int32_t const numHeads, int32_t const channels, int32_t const numLevels, int32_t const numQuery, int32_t const numPoint,
    scalar_t* gradValue, scalar_t* gradSamplingLoc, scalar_t* gradAttnWeight)
{
    CUDA_KERNEL_LOOP(index, n)
    {
        __shared__ scalar_t cacheGradSamplingLoc[blockSize * 2];
        __shared__ scalar_t cacheGradAttnWeight[blockSize];
        uint32_t tid = threadIdx.x;
        int32_t _temp = index;
        int32_t const cCol = _temp % channels;
        _temp /= channels;
        int32_t const samplingIndex = _temp;
        int32_t const mCol = _temp % numHeads;
        _temp /= numHeads;
        int32_t const qCol = _temp % numQuery;
        _temp /= numQuery;
        int32_t const bCol = _temp;

        scalar_t const topGrad = grad_col[index];

        int32_t dataWeightPtr = samplingIndex * numLevels * numPoint;
        int32_t dataLocWPtr = dataWeightPtr << 1;
        int32_t const gradSamplingPtr = dataWeightPtr;
        gradSamplingLoc += gradSamplingPtr << 1;
        gradAttnWeight += gradSamplingPtr;
        int32_t const gradWeightStride = 1;
        int32_t const gradLocStride = 2;
        int32_t const qidStride = numHeads * channels;
        int32_t const dataValuePtrInitOffset = bCol * spatialSize * qidStride;

        for (int32_t lCol = 0; lCol < numLevels; ++lCol)
        {
            int32_t const levelStartId = dataLevelStartIndex[lCol];
            int32_t const spatialHPtr = lCol << 1;
            int32_t const spatialH = dataSpatialShapes[spatialHPtr];
            int32_t const spatialW = dataSpatialShapes[spatialHPtr + 1];
            int32_t const valuePtrOffset = dataValuePtrInitOffset + levelStartId * qidStride;
            scalar_t const* dataValuePtr = dataValue + valuePtrOffset;
            scalar_t* gradValuePtr = gradValue + valuePtrOffset;

            for (int32_t pCol = 0; pCol < numPoint; ++pCol)
            {
                scalar_t const locW = dataSamplingLoc[dataLocWPtr];
                scalar_t const locH = dataSamplingLoc[dataLocWPtr + 1];
                scalar_t const weight = dataAttnWeight[dataWeightPtr];

                scalar_t const hIm = locH * spatialH - 0.5;
                scalar_t const wIm = locW * spatialW - 0.5;
                *(cacheGradSamplingLoc + (threadIdx.x << 1)) = 0;
                *(cacheGradSamplingLoc + ((threadIdx.x << 1) + 1)) = 0;
                *(cacheGradAttnWeight + threadIdx.x) = 0;
                if (hIm > -1 && wIm > -1 && hIm < spatialH && wIm < spatialW)
                {
                    ms_deform_attn_col2im_bilinear(dataValuePtr, spatialH, spatialW, numHeads, channels, hIm, wIm, mCol,
                        cCol, topGrad, weight, gradValuePtr, cacheGradSamplingLoc + (threadIdx.x << 1),
                        cacheGradAttnWeight + threadIdx.x);
                }

                __syncthreads();
                if (tid == 0)
                {
                    scalar_t _gradW = cacheGradSamplingLoc[0], _gradH = cacheGradSamplingLoc[1],
                             _gradA = cacheGradAttnWeight[0];
                    int32_t sid = 2;
                    for (uint32_t tid = 1; tid < blockSize; ++tid)
                    {
                        _gradW += cacheGradSamplingLoc[sid];
                        _gradH += cacheGradSamplingLoc[sid + 1];
                        _gradA += cacheGradAttnWeight[tid];
                        sid += 2;
                    }

                    *gradSamplingLoc = _gradW;
                    *(gradSamplingLoc + 1) = _gradH;
                    *gradAttnWeight = _gradA;
                }
                __syncthreads();

                dataWeightPtr += 1;
                dataLocWPtr += 2;
                gradAttnWeight += gradWeightStride;
                gradSamplingLoc += gradLocStride;
            }
        }
    }
}

template <typename scalar_t, uint32_t blockSize>
__global__ void ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2(int32_t const n, scalar_t const* grad_col,
    scalar_t const* dataValue, int32_t const* dataSpatialShapes, int32_t const* dataLevelStartIndex,
    scalar_t const* dataSamplingLoc, scalar_t const* dataAttnWeight, int32_t const batchSize, int32_t const spatialSize,
    int32_t const numHeads, int32_t const channels, int32_t const numLevels, int32_t const numQuery, int32_t const numPoint,
    scalar_t* gradValue, scalar_t* gradSamplingLoc, scalar_t* gradAttnWeight)
{
    CUDA_KERNEL_LOOP(index, n)
    {
        __shared__ scalar_t cacheGradSamplingLoc[blockSize * 2];
        __shared__ scalar_t cacheGradAttnWeight[blockSize];
        uint32_t tid = threadIdx.x;
        int32_t _temp = index;
        int32_t const cCol = _temp % channels;
        _temp /= channels;
        int32_t const samplingIndex = _temp;
        int32_t const mCol = _temp % numHeads;
        _temp /= numHeads;
        int32_t const qCol = _temp % numQuery;
        _temp /= numQuery;
        int32_t const bCol = _temp;

        scalar_t const topGrad = grad_col[index];

        int32_t dataWeightPtr = samplingIndex * numLevels * numPoint;
        int32_t dataLocWPtr = dataWeightPtr << 1;
        int32_t const gradSamplingPtr = dataWeightPtr;
        gradSamplingLoc += gradSamplingPtr << 1;
        gradAttnWeight += gradSamplingPtr;
        int32_t const gradWeightStride = 1;
        int32_t const gradLocStride = 2;
        int32_t const qidStride = numHeads * channels;
        int32_t const dataValuePtrInitOffset = bCol * spatialSize * qidStride;

        for (int32_t lCol = 0; lCol < numLevels; ++lCol)
        {
            int32_t const levelStartId = dataLevelStartIndex[lCol];
            int32_t const spatialHPtr = lCol << 1;
            int32_t const spatialH = dataSpatialShapes[spatialHPtr];
            int32_t const spatialW = dataSpatialShapes[spatialHPtr + 1];
            int32_t const valuePtrOffset = dataValuePtrInitOffset + levelStartId * qidStride;
            scalar_t const* dataValuePtr = dataValue + valuePtrOffset;
            scalar_t* gradValuePtr = gradValue + valuePtrOffset;

            for (int32_t pCol = 0; pCol < numPoint; ++pCol)
            {
                scalar_t const locW = dataSamplingLoc[dataLocWPtr];
                scalar_t const locH = dataSamplingLoc[dataLocWPtr + 1];
                scalar_t const weight = dataAttnWeight[dataWeightPtr];

                scalar_t const hIm = locH * spatialH - 0.5;
                scalar_t const wIm = locW * spatialW - 0.5;
                *(cacheGradSamplingLoc + (threadIdx.x << 1)) = 0;
                *(cacheGradSamplingLoc + ((threadIdx.x << 1) + 1)) = 0;
                *(cacheGradAttnWeight + threadIdx.x) = 0;
                if (hIm > -1 && wIm > -1 && hIm < spatialH && wIm < spatialW)
                {
                    ms_deform_attn_col2im_bilinear(dataValuePtr, spatialH, spatialW, numHeads, channels, hIm, wIm, mCol,
                        cCol, topGrad, weight, gradValuePtr, cacheGradSamplingLoc + (threadIdx.x << 1),
                        cacheGradAttnWeight + threadIdx.x);
                }

                __syncthreads();

                for (uint32_t s = blockSize / 2; s > 0; s >>= 1)
                {
                    if (tid < s)
                    {
                        uint32_t const xid1 = tid << 1;
                        uint32_t const xid2 = (tid + s) << 1;
                        cacheGradAttnWeight[tid] += cacheGradAttnWeight[tid + s];
                        cacheGradSamplingLoc[xid1] += cacheGradSamplingLoc[xid2];
                        cacheGradSamplingLoc[xid1 + 1] += cacheGradSamplingLoc[xid2 + 1];
                    }
                    __syncthreads();
                }

                if (tid == 0)
                {
                    *gradSamplingLoc = cacheGradSamplingLoc[0];
                    *(gradSamplingLoc + 1) = cacheGradSamplingLoc[1];
                    *gradAttnWeight = cacheGradAttnWeight[0];
                }
                __syncthreads();

                dataWeightPtr += 1;
                dataLocWPtr += 2;
                gradAttnWeight += gradWeightStride;
                gradSamplingLoc += gradLocStride;
            }
        }
    }
}

template <typename scalar_t>
__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v1(int32_t const n, scalar_t const* grad_col,
    scalar_t const* dataValue, int32_t const* dataSpatialShapes, int32_t const* dataLevelStartIndex,
    scalar_t const* dataSamplingLoc, scalar_t const* dataAttnWeight, int32_t const batchSize, int32_t const spatialSize,
    int32_t const numHeads, int32_t const channels, int32_t const numLevels, int32_t const numQuery, int32_t const numPoint,
    scalar_t* gradValue, scalar_t* gradSamplingLoc, scalar_t* gradAttnWeight)
{
    CUDA_KERNEL_LOOP(index, n)
    {
        extern __shared__ int32_t _s[];
        scalar_t* cacheGradSamplingLoc = (scalar_t*) _s;
        scalar_t* cacheGradAttnWeight = cacheGradSamplingLoc + 2 * blockDim.x;
        uint32_t tid = threadIdx.x;
        int32_t _temp = index;
        int32_t const cCol = _temp % channels;
        _temp /= channels;
        int32_t const samplingIndex = _temp;
        int32_t const mCol = _temp % numHeads;
        _temp /= numHeads;
        int32_t const qCol = _temp % numQuery;
        _temp /= numQuery;
        int32_t const bCol = _temp;

        scalar_t const topGrad = grad_col[index];

        int32_t dataWeightPtr = samplingIndex * numLevels * numPoint;
        int32_t dataLocWPtr = dataWeightPtr << 1;
        int32_t const gradSamplingPtr = dataWeightPtr;
        gradSamplingLoc += gradSamplingPtr << 1;
        gradAttnWeight += gradSamplingPtr;
        int32_t const gradWeightStride = 1;
        int32_t const gradLocStride = 2;
        int32_t const qidStride = numHeads * channels;
        int32_t const dataValuePtrInitOffset = bCol * spatialSize * qidStride;

        for (int32_t lCol = 0; lCol < numLevels; ++lCol)
        {
            int32_t const levelStartId = dataLevelStartIndex[lCol];
            int32_t const spatialHPtr = lCol << 1;
            int32_t const spatialH = dataSpatialShapes[spatialHPtr];
            int32_t const spatialW = dataSpatialShapes[spatialHPtr + 1];
            int32_t const valuePtrOffset = dataValuePtrInitOffset + levelStartId * qidStride;
            scalar_t const* dataValuePtr = dataValue + valuePtrOffset;
            scalar_t* gradValuePtr = gradValue + valuePtrOffset;

            for (int32_t pCol = 0; pCol < numPoint; ++pCol)
            {
                scalar_t const locW = dataSamplingLoc[dataLocWPtr];
                scalar_t const locH = dataSamplingLoc[dataLocWPtr + 1];
                scalar_t const weight = dataAttnWeight[dataWeightPtr];

                scalar_t const hIm = locH * spatialH - 0.5;
                scalar_t const wIm = locW * spatialW - 0.5;
                *(cacheGradSamplingLoc + (threadIdx.x << 1)) = 0;
                *(cacheGradSamplingLoc + ((threadIdx.x << 1) + 1)) = 0;
                *(cacheGradAttnWeight + threadIdx.x) = 0;
                if (hIm > -1 && wIm > -1 && hIm < spatialH && wIm < spatialW)
                {
                    ms_deform_attn_col2im_bilinear(dataValuePtr, spatialH, spatialW, numHeads, channels, hIm, wIm, mCol,
                        cCol, topGrad, weight, gradValuePtr, cacheGradSamplingLoc + (threadIdx.x << 1),
                        cacheGradAttnWeight + threadIdx.x);
                }

                __syncthreads();
                if (tid == 0)
                {
                    scalar_t _gradW = cacheGradSamplingLoc[0], _gradH = cacheGradSamplingLoc[1],
                             _gradA = cacheGradAttnWeight[0];
                    int32_t sid = 2;
                    for (uint32_t tid = 1; tid < blockDim.x; ++tid)
                    {
                        _gradW += cacheGradSamplingLoc[sid];
                        _gradH += cacheGradSamplingLoc[sid + 1];
                        _gradA += cacheGradAttnWeight[tid];
                        sid += 2;
                    }

                    *gradSamplingLoc = _gradW;
                    *(gradSamplingLoc + 1) = _gradH;
                    *gradAttnWeight = _gradA;
                }
                __syncthreads();

                dataWeightPtr += 1;
                dataLocWPtr += 2;
                gradAttnWeight += gradWeightStride;
                gradSamplingLoc += gradLocStride;
            }
        }
    }
}

template <typename scalar_t>
__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v2(int32_t const n, scalar_t const* grad_col,
    scalar_t const* dataValue, int32_t const* dataSpatialShapes, int32_t const* dataLevelStartIndex,
    scalar_t const* dataSamplingLoc, scalar_t const* dataAttnWeight, int32_t const batchSize, int32_t const spatialSize,
    int32_t const numHeads, int32_t const channels, int32_t const numLevels, int32_t const numQuery, int32_t const numPoint,
    scalar_t* gradValue, scalar_t* gradSamplingLoc, scalar_t* gradAttnWeight)
{
    CUDA_KERNEL_LOOP(index, n)
    {
        extern __shared__ int32_t _s[];
        scalar_t* cacheGradSamplingLoc = (scalar_t*) _s;
        scalar_t* cacheGradAttnWeight = cacheGradSamplingLoc + 2 * blockDim.x;
        uint32_t tid = threadIdx.x;
        int32_t _temp = index;
        int32_t const cCol = _temp % channels;
        _temp /= channels;
        int32_t const samplingIndex = _temp;
        int32_t const mCol = _temp % numHeads;
        _temp /= numHeads;
        int32_t const qCol = _temp % numQuery;
        _temp /= numQuery;
        int32_t const bCol = _temp;

        scalar_t const topGrad = grad_col[index];

        int32_t dataWeightPtr = samplingIndex * numLevels * numPoint;
        int32_t dataLocWPtr = dataWeightPtr << 1;
        int32_t const gradSamplingPtr = dataWeightPtr;
        gradSamplingLoc += gradSamplingPtr << 1;
        gradAttnWeight += gradSamplingPtr;
        int32_t const gradWeightStride = 1;
        int32_t const gradLocStride = 2;
        int32_t const qidStride = numHeads * channels;
        int32_t const dataValuePtrInitOffset = bCol * spatialSize * qidStride;

        for (int32_t lCol = 0; lCol < numLevels; ++lCol)
        {
            int32_t const levelStartId = dataLevelStartIndex[lCol];
            int32_t const spatialHPtr = lCol << 1;
            int32_t const spatialH = dataSpatialShapes[spatialHPtr];
            int32_t const spatialW = dataSpatialShapes[spatialHPtr + 1];
            int32_t const valuePtrOffset = dataValuePtrInitOffset + levelStartId * qidStride;
            scalar_t const* dataValuePtr = dataValue + valuePtrOffset;
            scalar_t* gradValuePtr = gradValue + valuePtrOffset;

            for (int32_t pCol = 0; pCol < numPoint; ++pCol)
            {
                scalar_t const locW = dataSamplingLoc[dataLocWPtr];
                scalar_t const locH = dataSamplingLoc[dataLocWPtr + 1];
                scalar_t const weight = dataAttnWeight[dataWeightPtr];

                scalar_t const hIm = locH * spatialH - 0.5;
                scalar_t const wIm = locW * spatialW - 0.5;
                *(cacheGradSamplingLoc + (threadIdx.x << 1)) = 0;
                *(cacheGradSamplingLoc + ((threadIdx.x << 1) + 1)) = 0;
                *(cacheGradAttnWeight + threadIdx.x) = 0;
                if (hIm > -1 && wIm > -1 && hIm < spatialH && wIm < spatialW)
                {
                    ms_deform_attn_col2im_bilinear(dataValuePtr, spatialH, spatialW, numHeads, channels, hIm, wIm, mCol,
                        cCol, topGrad, weight, gradValuePtr, cacheGradSamplingLoc + (threadIdx.x << 1),
                        cacheGradAttnWeight + threadIdx.x);
                }

                __syncthreads();

                for (uint32_t s = blockDim.x / 2, spre = blockDim.x; s > 0; s >>= 1, spre >>= 1)
                {
                    if (tid < s)
                    {
                        uint32_t const xid1 = tid << 1;
                        uint32_t const xid2 = (tid + s) << 1;
                        cacheGradAttnWeight[tid] += cacheGradAttnWeight[tid + s];
                        cacheGradSamplingLoc[xid1] += cacheGradSamplingLoc[xid2];
                        cacheGradSamplingLoc[xid1 + 1] += cacheGradSamplingLoc[xid2 + 1];
                        if (tid + (s << 1) < spre)
                        {
                            cacheGradAttnWeight[tid] += cacheGradAttnWeight[tid + (s << 1)];
                            cacheGradSamplingLoc[xid1] += cacheGradSamplingLoc[xid2 + (s << 1)];
                            cacheGradSamplingLoc[xid1 + 1] += cacheGradSamplingLoc[xid2 + 1 + (s << 1)];
                        }
                    }
                    __syncthreads();
                }

                if (tid == 0)
                {
                    *gradSamplingLoc = cacheGradSamplingLoc[0];
                    *(gradSamplingLoc + 1) = cacheGradSamplingLoc[1];
                    *gradAttnWeight = cacheGradAttnWeight[0];
                }
                __syncthreads();

                dataWeightPtr += 1;
                dataLocWPtr += 2;
                gradAttnWeight += gradWeightStride;
                gradSamplingLoc += gradLocStride;
            }
        }
    }
}

template <typename scalar_t>
__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v2_multi_blocks(int32_t const n, scalar_t const* grad_col,
    scalar_t const* dataValue, int32_t const* dataSpatialShapes, int32_t const* dataLevelStartIndex,
    scalar_t const* dataSamplingLoc, scalar_t const* dataAttnWeight, int32_t const batchSize, int32_t const spatialSize,
    int32_t const numHeads, int32_t const channels, int32_t const numLevels, int32_t const numQuery, int32_t const numPoint,
    scalar_t* gradValue, scalar_t* gradSamplingLoc, scalar_t* gradAttnWeight)
{
    CUDA_KERNEL_LOOP(index, n)
    {
        extern __shared__ int32_t _s[];
        scalar_t* cacheGradSamplingLoc = (scalar_t*) _s;
        scalar_t* cacheGradAttnWeight = cacheGradSamplingLoc + 2 * blockDim.x;
        uint32_t tid = threadIdx.x;
        int32_t _temp = index;
        int32_t const cCol = _temp % channels;
        _temp /= channels;
        int32_t const samplingIndex = _temp;
        int32_t const mCol = _temp % numHeads;
        _temp /= numHeads;
        int32_t const qCol = _temp % numQuery;
        _temp /= numQuery;
        int32_t const bCol = _temp;

        scalar_t const topGrad = grad_col[index];

        int32_t dataWeightPtr = samplingIndex * numLevels * numPoint;
        int32_t dataLocWPtr = dataWeightPtr << 1;
        int32_t const gradSamplingPtr = dataWeightPtr;
        gradSamplingLoc += gradSamplingPtr << 1;
        gradAttnWeight += gradSamplingPtr;
        int32_t const gradWeightStride = 1;
        int32_t const gradLocStride = 2;
        int32_t const qidStride = numHeads * channels;
        int32_t const dataValuePtrInitOffset = bCol * spatialSize * qidStride;

        for (int32_t lCol = 0; lCol < numLevels; ++lCol)
        {
            int32_t const levelStartId = dataLevelStartIndex[lCol];
            int32_t const spatialHPtr = lCol << 1;
            int32_t const spatialH = dataSpatialShapes[spatialHPtr];
            int32_t const spatialW = dataSpatialShapes[spatialHPtr + 1];
            int32_t const valuePtrOffset = dataValuePtrInitOffset + levelStartId * qidStride;
            scalar_t const* dataValuePtr = dataValue + valuePtrOffset;
            scalar_t* gradValuePtr = gradValue + valuePtrOffset;

            for (int32_t pCol = 0; pCol < numPoint; ++pCol)
            {
                scalar_t const locW = dataSamplingLoc[dataLocWPtr];
                scalar_t const locH = dataSamplingLoc[dataLocWPtr + 1];
                scalar_t const weight = dataAttnWeight[dataWeightPtr];

                scalar_t const hIm = locH * spatialH - 0.5;
                scalar_t const wIm = locW * spatialW - 0.5;
                *(cacheGradSamplingLoc + (threadIdx.x << 1)) = 0;
                *(cacheGradSamplingLoc + ((threadIdx.x << 1) + 1)) = 0;
                *(cacheGradAttnWeight + threadIdx.x) = 0;
                if (hIm > -1 && wIm > -1 && hIm < spatialH && wIm < spatialW)
                {
                    ms_deform_attn_col2im_bilinear(dataValuePtr, spatialH, spatialW, numHeads, channels, hIm, wIm, mCol,
                        cCol, topGrad, weight, gradValuePtr, cacheGradSamplingLoc + (threadIdx.x << 1),
                        cacheGradAttnWeight + threadIdx.x);
                }

                __syncthreads();

                for (uint32_t s = blockDim.x / 2, spre = blockDim.x; s > 0; s >>= 1, spre >>= 1)
                {
                    if (tid < s)
                    {
                        uint32_t const xid1 = tid << 1;
                        uint32_t const xid2 = (tid + s) << 1;
                        cacheGradAttnWeight[tid] += cacheGradAttnWeight[tid + s];
                        cacheGradSamplingLoc[xid1] += cacheGradSamplingLoc[xid2];
                        cacheGradSamplingLoc[xid1 + 1] += cacheGradSamplingLoc[xid2 + 1];
                        if (tid + (s << 1) < spre)
                        {
                            cacheGradAttnWeight[tid] += cacheGradAttnWeight[tid + (s << 1)];
                            cacheGradSamplingLoc[xid1] += cacheGradSamplingLoc[xid2 + (s << 1)];
                            cacheGradSamplingLoc[xid1 + 1] += cacheGradSamplingLoc[xid2 + 1 + (s << 1)];
                        }
                    }
                    __syncthreads();
                }

                if (tid == 0)
                {
                    atomicAdd(gradSamplingLoc, cacheGradSamplingLoc[0]);
                    atomicAdd(gradSamplingLoc + 1, cacheGradSamplingLoc[1]);
                    atomicAdd(gradAttnWeight, cacheGradAttnWeight[0]);
                }
                __syncthreads();

                dataWeightPtr += 1;
                dataLocWPtr += 2;
                gradAttnWeight += gradWeightStride;
                gradSamplingLoc += gradLocStride;
            }
        }
    }
}

template <typename scalar_t>
__global__ void ms_deformable_col2im_gpu_kernel_gm(int32_t const n, scalar_t const* grad_col, scalar_t const* dataValue,
    int32_t const* dataSpatialShapes, int32_t const* dataLevelStartIndex, scalar_t const* dataSamplingLoc,
    scalar_t const* dataAttnWeight, int32_t const batchSize, int32_t const spatialSize, int32_t const numHeads, int32_t const channels,
    int32_t const numLevels, int32_t const numQuery, int32_t const numPoint, scalar_t* gradValue, scalar_t* gradSamplingLoc,
    scalar_t* gradAttnWeight)
{
    CUDA_KERNEL_LOOP(index, n)
    {
        int32_t _temp = index;
        int32_t const cCol = _temp % channels;
        _temp /= channels;
        int32_t const samplingIndex = _temp;
        int32_t const mCol = _temp % numHeads;
        _temp /= numHeads;
        int32_t const qCol = _temp % numQuery;
        _temp /= numQuery;
        int32_t const bCol = _temp;

        scalar_t const topGrad = grad_col[index];

        int32_t dataWeightPtr = samplingIndex * numLevels * numPoint;
        int32_t dataLocWPtr = dataWeightPtr << 1;
        int32_t const gradSamplingPtr = dataWeightPtr;
        gradSamplingLoc += gradSamplingPtr << 1;
        gradAttnWeight += gradSamplingPtr;
        int32_t const gradWeightStride = 1;
        int32_t const gradLocStride = 2;
        int32_t const qidStride = numHeads * channels;
        int32_t const dataValuePtrInitOffset = bCol * spatialSize * qidStride;

        for (int32_t lCol = 0; lCol < numLevels; ++lCol)
        {
            int32_t const levelStartId = dataLevelStartIndex[lCol];
            int32_t const spatialHPtr = lCol << 1;
            int32_t const spatialH = dataSpatialShapes[spatialHPtr];
            int32_t const spatialW = dataSpatialShapes[spatialHPtr + 1];
            int32_t const valuePtrOffset = dataValuePtrInitOffset + levelStartId * qidStride;
            scalar_t const* dataValuePtr = dataValue + valuePtrOffset;
            scalar_t* gradValuePtr = gradValue + valuePtrOffset;

            for (int32_t pCol = 0; pCol < numPoint; ++pCol)
            {
                scalar_t const locW = dataSamplingLoc[dataLocWPtr];
                scalar_t const locH = dataSamplingLoc[dataLocWPtr + 1];
                scalar_t const weight = dataAttnWeight[dataWeightPtr];

                scalar_t const hIm = locH * spatialH - 0.5;
                scalar_t const wIm = locW * spatialW - 0.5;
                if (hIm > -1 && wIm > -1 && hIm < spatialH && wIm < spatialW)
                {
                    ms_deform_attn_col2im_bilinear_gm(dataValuePtr, spatialH, spatialW, numHeads, channels, hIm, wIm,
                        mCol, cCol, topGrad, weight, gradValuePtr, gradSamplingLoc, gradAttnWeight);
                }
                dataWeightPtr += 1;
                dataLocWPtr += 2;
                gradAttnWeight += gradWeightStride;
                gradSamplingLoc += gradLocStride;
            }
        }
    }
}

template <typename scalar_t>
void ms_deformable_im2col_cuda(cudaStream_t stream, scalar_t const* dataValue, int32_t const* dataSpatialShapes,
    int32_t const* dataLevelStartIndex, scalar_t const* dataSamplingLoc, scalar_t const* dataAttnWeight,
    int32_t const batchSize, int32_t const spatialSize, int32_t const numHeads, int32_t const channels, int32_t const numLevels,
    int32_t const numQuery, int32_t const numPoint, scalar_t* dataCol)
{
    int32_t const numKernels = batchSize * numQuery * numHeads * channels;
    int32_t const numActualKernels = batchSize * numQuery * numHeads * channels;
    int32_t const numThreads = kCUDA_NUM_THREADS;
    cudaError_t err = cudaSuccess;

    ms_deformable_im2col_gpu_kernel<scalar_t><<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(
        numKernels, dataValue, dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize,
        spatialSize, numHeads, channels, numLevels, numQuery, numPoint, dataCol);
    err = cudaGetLastError();
    if (err != cudaSuccess)
    {
        nvinfer1::plugin::gLogError << "error in ms_deformable_im2col_cuda: " << cudaGetErrorString(err) << std::endl;
    }
}

template <typename scalar_t>
void ms_deformable_col2im_cuda(cudaStream_t stream, scalar_t const* grad_col, scalar_t const* dataValue,
    int32_t const* dataSpatialShapes, int32_t const* dataLevelStartIndex, scalar_t const* dataSamplingLoc,
    scalar_t const* dataAttnWeight, int32_t const batchSize, int32_t const spatialSize, int32_t const numHeads, int32_t const channels,
    int32_t const numLevels, int32_t const numQuery, int32_t const numPoint, scalar_t* gradValue, scalar_t* gradSamplingLoc,
    scalar_t* gradAttnWeight)
{
    int32_t const numThreads = (channels > kCUDA_NUM_THREADS) ? kCUDA_NUM_THREADS : channels;
    int32_t const numKernels = batchSize * numQuery * numHeads * channels;
    int32_t const numActualKernels = batchSize * numQuery * numHeads * channels;
    if (channels > 1024)
    {
        if ((channels & 1023) == 0)
        {
            ms_deformable_col2im_gpu_kernel_shm_reduce_v2_multi_blocks<scalar_t>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, numThreads * 3 * sizeof(scalar_t), stream>>>(
                    numKernels, grad_col, dataValue, dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc,
                    dataAttnWeight, batchSize, spatialSize, numHeads, channels, numLevels, numQuery, numPoint,
                    gradValue, gradSamplingLoc, gradAttnWeight);
        }
        else
        {
            ms_deformable_col2im_gpu_kernel_gm<scalar_t>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
        }
    }
    else
    {
        switch (channels)
        {
        case 1:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 1>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 2:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 2>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 4:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 4>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 8:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 8>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 16:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 16>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 32:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1<scalar_t, 32>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 64:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 64>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 128:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 128>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 256:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 256>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 512:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 512>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        case 1024:
            ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2<scalar_t, 1024>
                <<<GET_BLOCKS(numActualKernels, numThreads), numThreads, 0, stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            break;
        default:
            if (channels < 64)
            {
                ms_deformable_col2im_gpu_kernel_shm_reduce_v1<scalar_t><<<GET_BLOCKS(numActualKernels, numThreads),
                    numThreads, numThreads * 3 * sizeof(scalar_t), stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            }
            else
            {
                ms_deformable_col2im_gpu_kernel_shm_reduce_v2<scalar_t><<<GET_BLOCKS(numActualKernels, numThreads),
                    numThreads, numThreads * 3 * sizeof(scalar_t), stream>>>(numKernels, grad_col, dataValue,
                    dataSpatialShapes, dataLevelStartIndex, dataSamplingLoc, dataAttnWeight, batchSize, spatialSize,
                    numHeads, channels, numLevels, numQuery, numPoint, gradValue, gradSamplingLoc, gradAttnWeight);
            }
        }
    }
    cudaError_t err = cudaGetLastError();
    if (err != cudaSuccess)
    {
        nvinfer1::plugin::gLogError << "error in ms_deformable_col2im_cuda: " << cudaGetErrorString(err) << std::endl;
    }
}

#define CUDA_KERNEL_LOOP_RANGE(tid, nDataMin, nDataMax)                                                                \
    for (int32_t tid = blockIdx.x * blockDim.x + threadIdx.x; ((tid >= (nDataMin)) && (tid < (nDataMax)));                 \
         tid += blockDim.x * gridDim.x)

__global__ void float2half_input(int32_t const nData1, int32_t const nData2, int32_t const nData3, float const* data1Float,
    float const* data2Float, float const* data3Float, __half* data1Half, __half* data2Half, __half* data3Half)
{
    CUDA_KERNEL_LOOP(index, nData1)
    {
        data1Half[index] = __float2half(data1Float[index]);
        data2Half[index] = __float2half(data2Float[index]);
        data3Half[index] = __float2half(data3Float[index]);
    }

    CUDA_KERNEL_LOOP_RANGE(index, nData1, nData2)
    {
        data2Half[index] = __float2half(data2Float[index]);
        data3Half[index] = __float2half(data3Float[index]);
    }

    CUDA_KERNEL_LOOP_RANGE(index, nData2, nData3)
    {
        data3Half[index] = __float2half(data3Float[index]);
    }
}

__global__ void half2float_output(int32_t const n_data, __half const* data_half, float* data_float)
{
    CUDA_KERNEL_LOOP(index, n_data)
    {
        data_float[index] = __half2float(data_half[index]);
    }
}

#endif
