// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. // // 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. #include "paddle/phi/kernels/masked_fill_grad_kernel.h" #include "paddle/phi/kernels/cast_kernel.h" #include "paddle/phi/kernels/funcs/masked_fill_utils.h" #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/backends/gpu/gpu_launch_config.h" #include "paddle/phi/backends/gpu/gpu_primitives.h" #include "paddle/phi/common/memory_utils.h" #include "paddle/phi/common/place.h" #include "paddle/phi/core/dense_tensor.h" #include "paddle/phi/kernels/empty_kernel.h" #include "paddle/phi/kernels/expand_grad_kernel.h" #include "paddle/phi/kernels/expand_kernel.h" #include "paddle/phi/kernels/full_kernel.h" #include "paddle/phi/kernels/funcs/aligned_vector.h" #include "paddle/phi/kernels/funcs/common_infer_shape_functions.h" #include "paddle/phi/kernels/primitive/kernel_primitives.h" #include "paddle/phi/kernels/reduce_sum_kernel.h" #include "paddle/phi/kernels/scale_kernel.h" #include "paddle/phi/kernels/where_kernel.h" namespace phi { template __global__ void GPUMaskedFillXGradKernel(const T* out_grad, const bool* mask, const int64_t input_len, const int64_t batch_size, T* x_grad) { int64_t idx = static_cast(blockIdx.x) * blockDim.x + threadIdx.x; if (idx >= (input_len / VecSize)) { return; } int64_t vec_idx = idx * VecSize; int64_t mask_idx = vec_idx / batch_size; using VecType = kps::details::VectorType; const VecType* src = reinterpret_cast(&out_grad[vec_idx]); VecType* x_grad_dst = reinterpret_cast(&x_grad[vec_idx]); T set_value[VecSize]; #pragma unroll for (int i = 0; i < VecSize; i++) { set_value[i] = 0; } const VecType* vec_value = reinterpret_cast(&set_value[0]); if (mask[mask_idx]) { *x_grad_dst = *vec_value; } else { *x_grad_dst = *src; } } template __global__ void GPUMaskedFillValueGradKernel(const T* out_grad, const bool* mask, const int64_t input_len, const int64_t batch_size, T* value_grad) { int64_t idx = static_cast(blockIdx.x) * blockDim.x + threadIdx.x; if (idx >= (input_len / VecSize)) { return; } int64_t vec_idx = idx * VecSize; int64_t mask_idx = vec_idx / batch_size; using VecType = kps::details::VectorType; const VecType* src = reinterpret_cast(&out_grad[vec_idx]); VecType* value_grad_dst = reinterpret_cast(&value_grad[vec_idx]); T set_value[VecSize]; #pragma unroll for (int i = 0; i < VecSize; i++) { set_value[i] = 0; } const VecType* vec_value = reinterpret_cast(&set_value[0]); if (mask[mask_idx]) { *value_grad_dst = *src; } else { *value_grad_dst = *vec_value; } } template __global__ void GPUMaskedFillGradKernel(const T* out_grad, const bool* mask, const int64_t input_len, const int64_t batch_size, T* x_grad, T* value_grad) { int64_t idx = static_cast(blockIdx.x) * blockDim.x + threadIdx.x; if (idx >= (input_len / VecSize)) { return; } int64_t vec_idx = idx * VecSize; int64_t mask_idx = vec_idx / batch_size; using VecType = kps::details::VectorType; const VecType* src = reinterpret_cast(&out_grad[vec_idx]); VecType* x_grad_dst = reinterpret_cast(&x_grad[vec_idx]); VecType* value_grad_dst = reinterpret_cast(&value_grad[vec_idx]); T set_value[VecSize]; #pragma unroll for (int i = 0; i < VecSize; i++) { set_value[i] = 0; } const VecType* vec_value = reinterpret_cast(&set_value[0]); if (mask[mask_idx]) { *x_grad_dst = *vec_value; *value_grad_dst = *src; } else { *x_grad_dst = *src; *value_grad_dst = *vec_value; } } template void DispatchMaskFillGradKernel(const GPUContext& dev_ctx, const T* input, const bool* mask, const int64_t input_len, const int64_t batch_size, T* x_grad, T* value_grad, int vec_size, const backends::gpu::GpuLaunchConfig& config) { auto stream = dev_ctx.stream(); if (x_grad && value_grad) { switch (vec_size) { #define CASE_VECSIZE(__Vs) \ case __Vs: \ GPUMaskedFillGradKernel \ <<>>( \ input, mask, input_len, batch_size, x_grad, value_grad); \ break; CASE_VECSIZE(1) CASE_VECSIZE(2) CASE_VECSIZE(4) CASE_VECSIZE(8) #undef CASE_VECSIZE default: PADDLE_THROW(common::errors::Unimplemented( "Unsupported vectorized size: %d", vec_size)); } } else if (x_grad) { switch (vec_size) { #define CASE_VECSIZE(__Vs) \ case __Vs: \ GPUMaskedFillXGradKernel \ <<>>( \ input, mask, input_len, batch_size, x_grad); \ break; CASE_VECSIZE(1) CASE_VECSIZE(2) CASE_VECSIZE(4) CASE_VECSIZE(8) #undef CASE_VECSIZE default: PADDLE_THROW(common::errors::Unimplemented( "Unsupported vectorized size: %d", vec_size)); } } else if (value_grad) { switch (vec_size) { #define CASE_VECSIZE(__Vs) \ case __Vs: \ GPUMaskedFillValueGradKernel \ <<>>( \ input, mask, input_len, batch_size, value_grad); \ break; CASE_VECSIZE(1) CASE_VECSIZE(2) CASE_VECSIZE(4) CASE_VECSIZE(8) #undef CASE_VECSIZE default: PADDLE_THROW(common::errors::Unimplemented( "Unsupported vectorized size: %d", vec_size)); } } } template void DispatchMaskFillOneValueGradKernel( const GPUContext& dev_ctx, const T* input, const bool* mask, const int64_t input_len, const int64_t batch_size, T* x_grad, int vec_size, const backends::gpu::GpuLaunchConfig& config) { auto stream = dev_ctx.stream(); if (x_grad) { switch (vec_size) { #define CASE_VECSIZE(__Vs) \ case __Vs: \ GPUMaskedFillXGradKernel \ <<>>( \ input, mask, input_len, batch_size, x_grad); \ break; CASE_VECSIZE(1) CASE_VECSIZE(2) CASE_VECSIZE(4) CASE_VECSIZE(8) #undef CASE_VECSIZE default: PADDLE_THROW(common::errors::Unimplemented( "Unsupported vectorized size: %d", vec_size)); } } } template void GPUMaskedFillGrad(const GPUContext& dev_ctx, const DenseTensor& out_grad, const DenseTensor& mask, DenseTensor* x_grad, DenseTensor* value_grad) { const T* out_grad_data = out_grad.data(); const bool* mask_data = mask.data(); T* x_grad_data = nullptr; T* value_grad_data = nullptr; int64_t input_len = out_grad.numel(); int64_t mask_len = mask.numel(); int64_t batch_size = input_len / mask_len; int vec_size = 8; vec_size = std::min(GetVectorizedSize(out_grad_data), vec_size); if (x_grad && x_grad->initialized()) { x_grad_data = x_grad->data(); vec_size = std::min(GetVectorizedSize(x_grad_data), vec_size); } if (value_grad && value_grad->initialized()) { value_grad_data = value_grad->data(); vec_size = std::min(GetVectorizedSize(value_grad_data), vec_size); } while (vec_size > 1 && batch_size % vec_size != 0) { vec_size /= 2; } auto config = backends::gpu::GetGpuLaunchConfig1D(dev_ctx, input_len, vec_size); if (value_grad && value_grad->numel() == 1) { DispatchMaskFillOneValueGradKernel(dev_ctx, out_grad_data, mask_data, input_len, batch_size, x_grad_data, vec_size, config); if (value_grad) { DenseTensor zero_tensor; Full(dev_ctx, out_grad.dims(), T(0.0), &zero_tensor); DenseTensor value_grad_tensor; value_grad_tensor.set_meta(out_grad.meta()); WhereKernel( dev_ctx, mask, out_grad, zero_tensor, &value_grad_tensor); std::vector v_dims(value_grad_tensor.dims().size()); std::iota(v_dims.begin(), v_dims.end(), 0); IntArray v_axis(v_dims); SumKernel(dev_ctx, value_grad_tensor, v_axis, value_grad->dtype(), false, value_grad); } } else { DispatchMaskFillGradKernel(dev_ctx, out_grad_data, mask_data, input_len, batch_size, x_grad_data, value_grad_data, vec_size, config); } } template void MaskedFillGradKernel(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& mask, const DenseTensor& value UNUSED, const DenseTensor& out_grad, DenseTensor* x_grad, DenseTensor* v_grad) { if (out_grad.numel() == 0 || mask.numel() == 0) { // x shape [2, 1, 3], mask shape [2, 0, 3], x_grad shape [2, 1, 3] if (x_grad) { Full(dev_ctx, x_grad->dims(), 0, x_grad); } if (v_grad) { Full(dev_ctx, v_grad->dims(), 0, v_grad); } return; } auto out_grad_dims = out_grad.dims(); auto x_dims = x.dims(); auto mask_dims = mask.dims(); DenseTensor mask_expand; DenseTensor x_grad_expand; DenseTensor v_grad_expand; bool expand_x = false; bool expand_v = false; auto expanded_size = vectorize(funcs::BroadcastTwoDims(x_dims, mask_dims, -1)); auto expanded_dims = make_ddim(expanded_size); bool flag = funcs::CanDispatchMaskFillShortcut(out_grad_dims, mask_dims); if (expanded_dims != x_dims) flag = false; if (v_grad && v_grad->dims() != expanded_dims && v_grad->numel() != 1) flag = false; if (x_grad) { dev_ctx.template Alloc(x_grad); } if (v_grad) { dev_ctx.template Alloc(v_grad); } if (flag) { GPUMaskedFillGrad(dev_ctx, out_grad, mask, x_grad, v_grad); return; } if (mask.dims() != expanded_dims) { ExpandKernel( dev_ctx, mask, IntArray(expanded_size), &mask_expand); } else { mask_expand = mask; } auto mask_size = mask_expand.numel(); if (mask_size <= 0) return; if (x_grad) { if (x_grad->dims() != expanded_dims) { x_grad_expand = Empty(dev_ctx, IntArray(expanded_size)); expand_x = true; } else { x_grad_expand = *x_grad; } } if (v_grad) { if (v_grad->dims() != expanded_dims && v_grad->numel() != 1) { v_grad_expand = Empty(dev_ctx, IntArray(expanded_size)); expand_v = true; } else { v_grad_expand = *v_grad; } } GPUMaskedFillGrad( dev_ctx, out_grad, mask_expand, &x_grad_expand, &v_grad_expand); if (expand_x) { ExpandGradKernel( dev_ctx, x, x_grad_expand, IntArray(expanded_size), x_grad); } if (expand_v) { ExpandGradKernel( dev_ctx, value, v_grad_expand, IntArray(expanded_size), v_grad); } } } // namespace phi PD_REGISTER_KERNEL(masked_fill_grad, GPU, ALL_LAYOUT, phi::MaskedFillGradKernel, bool, float, double, int, int8_t, int64_t, int16_t, uint8_t, phi::float16, phi::bfloat16, phi::complex64, phi::complex128) { kernel->InputAt(1).SetDataType(phi::DataType::BOOL); }