// 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 #include "paddle/common/exception.h" #include "paddle/phi/core/dense_tensor.h" #include "paddle/phi/kernels/empty_kernel.h" #include "paddle/phi/backends/xpu/enforce_xpu.h" #include "paddle/phi/backends/xpu/xpu_context.h" #include "paddle/phi/core/kernel_registry.h" namespace phi { static void GetRowsCols(const std::vector &shape, int64_t *p_rows, int64_t *p_cols) { int64_t rows = 1; for (size_t i = 0; i + 1 < shape.size(); ++i) { rows *= shape[i]; } int64_t cols = shape[shape.size() - 1]; *p_rows = rows; *p_cols = cols; } template void RMSNormFwdKernel(const Context &dev_ctx, const DenseTensor &x, const optional &scale_opt, const std::vector &normalized_shape, double epsilon, DenseTensor *y, DenseTensor *invvar) { int begin_norm_axis = x.dims().size() - normalized_shape.size(); PADDLE_ENFORCE_EQ( begin_norm_axis, x.dims().size() - 1, common::errors::InvalidArgument( "XPU RMSNorm only supports begin_norm_axis=%d, but got %d", x.dims().size() - 1, begin_norm_axis)); auto *scale_ptr = scale_opt.get_ptr(); if (scale_ptr == nullptr) { PADDLE_THROW(common::errors::InvalidArgument( "Scale must be provided for RMSNorm backward")); } const DenseTensor &scale = *scale_ptr; int64_t rows, cols; GetRowsCols(vectorize(x.dims()), &rows, &cols); if (scale.dtype() == DataType::BFLOAT16) { dev_ctx.template Alloc(y); } else if (scale.dtype() == DataType::FLOAT16) { dev_ctx.template Alloc(y); } else if (scale.dtype() == DataType::FLOAT32) { dev_ctx.template Alloc(y); } else { PADDLE_THROW(common::errors::InvalidArgument( "The dtype of scale must be FLOAT32, FLOAT16 or BFLOAT16, but got [%s]", scale.dtype())); } invvar->Resize({rows}); dev_ctx.template Alloc(invvar); /* refer to: - https://github.com/NVIDIA/apex/blob/bfb500c8/csrc/layer_norm_cuda_kernel.cu#L1018 - https://github.com/PaddlePaddle/PaddleNLP/blob/5b9e0b33/ops/csrc/fused_ln/layer_norm_cuda.h#L1087 Supported Type combinations: input compute scale output ======================================= fp32 fp32 fp32 fp32 fp16 fp32 fp16 fp16 bf16 fp32 bf16 bf16 Not supported yet: input compute scale output ======================================= fp32 fp32 fp16 fp16 fp32 fp32 bf16 bf16 Remarks: Output type = Scale type Compute always in FP32 */ #define DISPATCH_FWD_CASE(scalar_t_out) \ using XPUType = typename XPUTypeTrait::Type; \ auto ret = xpu::rms_layer_norm( \ dev_ctx.x_context(), \ reinterpret_cast(x.data()), \ reinterpret_cast(y->data()), \ rows, \ cols, \ epsilon, \ reinterpret_cast(scale.data()), \ /*bias=*/nullptr, \ invvar->data(), \ /*is_rstd=*/true); \ PADDLE_ENFORCE_XDNN_SUCCESS(ret, "rms_layer_norm"); // scale.dtype() same as y->dtype() if (x.dtype() == DataType::FLOAT32 && scale.dtype() == DataType::FLOAT32) { DISPATCH_FWD_CASE(float); } else if (x.dtype() == DataType::FLOAT16 && scale.dtype() == DataType::FLOAT16) { DISPATCH_FWD_CASE(phi::float16); } else if (x.dtype() == DataType::BFLOAT16 && scale.dtype() == DataType::BFLOAT16) { DISPATCH_FWD_CASE(phi::bfloat16); } else { PADDLE_THROW(common::errors::InvalidArgument( "Unsupported dtype combination: x [%s], scale [%s]. " "Expected both to be float32, float16, or bfloat16.", DataTypeToString(x.dtype()), DataTypeToString(scale.dtype()))); } #undef DISPATCH_FWD_CASE } template void RMSNormBwdKernel(const Context &dev_ctx, const DenseTensor &x, const optional &scale_opt, const DenseTensor &invvar, const DenseTensor &y_grad, const std::vector &normalized_shape, double epsilon, DenseTensor *x_grad, DenseTensor *scale_grad) { int begin_norm_axis = x.dims().size() - normalized_shape.size(); PADDLE_ENFORCE_EQ( begin_norm_axis, x.dims().size() - 1, common::errors::InvalidArgument( "XPU RMSNorm only supports begin_norm_axis=%d, but got %d", x.dims().size() - 1, begin_norm_axis)); auto *scale_ptr = scale_opt.get_ptr(); if (scale_ptr == nullptr) { PADDLE_THROW(common::errors::InvalidArgument( "Scale must be provided for RMSNorm backward")); } const DenseTensor &scale = *scale_ptr; int64_t rows, cols; GetRowsCols(vectorize(x.dims()), &rows, &cols); dev_ctx.template Alloc(x_grad); DenseTensor actual_scale_grad; if (scale_grad) { if (scale.dtype() == DataType::BFLOAT16) { dev_ctx.template Alloc(scale_grad); } else if (scale.dtype() == DataType::FLOAT16) { dev_ctx.template Alloc(scale_grad); } else if (scale.dtype() == DataType::FLOAT32) { dev_ctx.template Alloc(scale_grad); } else { PADDLE_THROW( common::errors::InvalidArgument("The dtype of scale must be FLOAT32, " "FLOAT16 or BFLOAT16, but got [%s]", scale.dtype())); } actual_scale_grad = *scale_grad; } else { // lora specific, scale_grad is nullptr if (scale.dtype() == DataType::BFLOAT16) { actual_scale_grad = EmptyLike(dev_ctx, scale); } else if (scale.dtype() == DataType::FLOAT16) { actual_scale_grad = EmptyLike(dev_ctx, scale); } else if (scale.dtype() == DataType::FLOAT32) { actual_scale_grad = EmptyLike(dev_ctx, scale); } else { PADDLE_THROW( common::errors::InvalidArgument("The dtype of scale must be FLOAT32, " "FLOAT16 or BFLOAT16, but got [%s]", scale.dtype())); } } #define DISPATCH_BWD_CASE(scalar_t_out) \ using XPUType = typename XPUTypeTrait::Type; \ auto ret = xpu::rms_layer_norm_grad( \ dev_ctx.x_context(), \ reinterpret_cast(x.data()), \ reinterpret_cast(y_grad.data()), \ reinterpret_cast(x_grad->data()), \ rows, \ cols, \ epsilon, \ reinterpret_cast(scale.data()), \ invvar.data(), \ reinterpret_cast(actual_scale_grad.data()), \ /*bias=*/nullptr, \ /*is_rstd=*/true); \ PADDLE_ENFORCE_XDNN_SUCCESS(ret, "rms_layer_norm_grad"); // scale.dtype() same as y->dtype() if (x.dtype() == DataType::FLOAT32 && scale.dtype() == DataType::FLOAT32) { DISPATCH_BWD_CASE(float); } else if (x.dtype() == DataType::FLOAT16 && scale.dtype() == DataType::FLOAT16) { DISPATCH_BWD_CASE(phi::float16); } else if (x.dtype() == DataType::BFLOAT16 && scale.dtype() == DataType::BFLOAT16) { DISPATCH_BWD_CASE(phi::bfloat16); } else { PADDLE_THROW(common::errors::InvalidArgument( "Unsupported dtype combination: x [%s], scale [%s]. " "Expected both to be float32, float16, or bfloat16.", DataTypeToString(x.dtype()), DataTypeToString(scale.dtype()))); } #undef DISPATCH_BWD_CASE } } // namespace phi PD_REGISTER_KERNEL(rms_norm, XPU, ALL_LAYOUT, phi::RMSNormFwdKernel, float, phi::float16, phi::bfloat16) {} PD_REGISTER_KERNEL(rms_norm_grad, XPU, ALL_LAYOUT, phi::RMSNormBwdKernel, float, phi::float16, phi::bfloat16) {}