Files
paddlepaddle--paddle/paddle/fluid/pir/dialect/operator/utils/utils.cc
T
2026-07-13 12:40:42 +08:00

657 lines
23 KiB
C++

// Copyright (c) 2023 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 <glog/logging.h>
#include <sstream>
#include <unordered_set>
#include "paddle/common/errors.h"
#include "paddle/fluid/framework/phi_utils.h"
#include "paddle/fluid/pir/dialect/distributed/ir/dist_attribute.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/manual_op.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_attribute.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
#include "paddle/phi/core/kernel_factory.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/utils/string/string_helper.h"
#ifdef PADDLE_WITH_DNNL
#include "paddle/fluid/pir/dialect/operator/ir/onednn_op.h"
#endif
namespace paddle {
namespace dialect {
const std::unordered_set<std::string> LegacyOpList = {
SendV2Op::name(),
RecvV2Op::name(),
};
enum class AttrType {
UNDEFINED = 0,
BOOL,
INT32,
INT64,
FLOAT,
DOUBLE,
ARRAY,
STRING,
TENSOR_NAME,
DATA_TYPE,
INT_ARRAY,
PLACE,
TensorDist,
SCALAR,
DATA_LAYOUT,
NUM_ATTR_TYPES,
};
static inline AttrType GetAttributeType(const pir::Attribute& attr) {
if (attr.isa<pir::BoolAttribute>()) {
return AttrType::BOOL;
} else if (attr.isa<pir::Int32Attribute>()) {
return AttrType::INT32;
} else if (attr.isa<pir::Int64Attribute>()) {
return AttrType::INT64;
} else if (attr.isa<pir::FloatAttribute>()) {
return AttrType::FLOAT;
} else if (attr.isa<pir::DoubleAttribute>()) {
return AttrType::DOUBLE;
} else if (attr.isa<pir::ArrayAttribute>()) {
return AttrType::ARRAY;
} else if (attr.isa<pir::StrAttribute>()) {
return AttrType::STRING;
} else if (attr.isa<pir::TensorNameAttribute>()) {
return AttrType::TENSOR_NAME;
} else if (attr.isa<paddle::dialect::DataTypeAttribute>()) {
return AttrType::DATA_TYPE;
} else if (attr.isa<paddle::dialect::IntArrayAttribute>()) {
return AttrType::INT_ARRAY;
} else if (attr.isa<paddle::dialect::PlaceAttribute>()) {
return AttrType::PLACE;
} else if (attr.isa<paddle::dialect::TensorDistAttribute>()) {
return AttrType::TensorDist;
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported ir Attribute type when casting it into "
"AttrType."));
}
}
template <typename T>
static std::function<T(const pir::Attribute& attr)> GetAttrCast(
AttrType attr_type) {
std::unordered_map<AttrType, std::function<T(const pir::Attribute& attr)>>
kAttrCastMap = {
{AttrType::BOOL,
[](const pir::Attribute& attr) {
return T{attr.dyn_cast<pir::BoolAttribute>().data()};
}},
{AttrType::INT32,
[](const pir::Attribute& attr) {
return T{attr.dyn_cast<pir::Int32Attribute>().data()};
}},
{AttrType::INT64,
[](const pir::Attribute& attr) {
return T{attr.dyn_cast<pir::Int64Attribute>().data()};
}},
{AttrType::FLOAT,
[](const pir::Attribute& attr) {
return T{attr.dyn_cast<pir::FloatAttribute>().data()};
}},
{AttrType::DOUBLE,
[](const pir::Attribute& attr) {
return T{attr.dyn_cast<pir::DoubleAttribute>().data()};
}},
{AttrType::ARRAY,
[](const pir::Attribute& attr) {
auto attr_vec = attr.dyn_cast<pir::ArrayAttribute>().AsVector();
if (attr_vec.empty()) {
return T{std::vector<int>()};
}
AttrType element_type = GetAttributeType(attr_vec[0]);
if (element_type == AttrType::BOOL) {
std::vector<bool> vec_bools;
vec_bools.reserve(attr_vec.size());
for (auto vec_element : attr_vec) {
vec_bools.push_back(
vec_element.dyn_cast<pir::BoolAttribute>().data());
}
return T{vec_bools};
} else if (element_type == AttrType::INT32) {
std::vector<int> vec_int32;
vec_int32.reserve(attr_vec.size());
for (auto vec_element : attr_vec) {
vec_int32.push_back(
vec_element.dyn_cast<pir::Int32Attribute>().data());
}
return T{vec_int32};
} else if (element_type == AttrType::INT64) {
std::vector<int64_t> vec_int64;
vec_int64.reserve(attr_vec.size());
for (auto vec_element : attr_vec) {
vec_int64.push_back(
vec_element.dyn_cast<pir::Int64Attribute>().data());
}
return T{vec_int64};
} else if (element_type == AttrType::FLOAT) {
std::vector<float> vec_float;
vec_float.reserve(attr_vec.size());
for (auto vec_element : attr_vec) {
vec_float.push_back(
vec_element.dyn_cast<pir::FloatAttribute>().data());
}
return T{vec_float};
} else if (element_type == AttrType::DOUBLE) {
std::vector<double> vec_double;
vec_double.reserve(attr_vec.size());
for (auto vec_element : attr_vec) {
vec_double.push_back(
vec_element.dyn_cast<pir::DoubleAttribute>().data());
}
return T{vec_double};
} else if (element_type == AttrType::STRING) {
std::vector<std::string> vec_string;
vec_string.reserve(attr_vec.size());
for (auto vec_element : attr_vec) {
vec_string.push_back(
vec_element.dyn_cast<pir::StrAttribute>().AsString());
}
return T{vec_string};
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported ir Attribute type when casting it into "
"vector."));
}
}},
{AttrType::STRING,
[](const pir::Attribute& attr) {
return T{attr.dyn_cast<pir::StrAttribute>().AsString()};
}},
{AttrType::TENSOR_NAME,
[](const pir::Attribute& attr) {
return T{attr.dyn_cast<pir::TensorNameAttribute>().data()};
}},
{AttrType::DATA_TYPE,
[](const pir::Attribute& attr) {
return T{
attr.dyn_cast<paddle::dialect::DataTypeAttribute>().data()};
}},
{AttrType::INT_ARRAY,
[](const pir::Attribute& attr) {
return T{attr.dyn_cast<paddle::dialect::IntArrayAttribute>()
.data()
.GetData()};
}},
{AttrType::PLACE,
[](const pir::Attribute& attr) {
return T{attr.dyn_cast<paddle::dialect::PlaceAttribute>().data()};
}},
{AttrType::TensorDist, [](const pir::Attribute& attr) {
return T{attr.dyn_cast<paddle::dialect::TensorDistAttribute>()};
}}};
return kAttrCastMap[attr_type];
}
VariantType GetAttributeData(const pir::Attribute& attr) {
AttrType attr_type = GetAttributeType(attr);
return GetAttrCast<VariantType>(attr_type)(attr);
}
paddle::any TransAttrToAny(const pir::Attribute& attr) {
AttrType attr_type = GetAttributeType(attr);
return GetAttrCast<paddle::any>(attr_type)(attr);
}
bool IsLegacyOp(const std::string& name) { return LegacyOpList.count(name); }
bool IsEmptyValue(const pir::Value& value) {
return !value.impl() || !value.type();
}
std::vector<int64_t> GetInt64Vector(const pir::Attribute& attr) {
PADDLE_ENFORCE_EQ(attr.isa<pir::ArrayAttribute>(),
true,
common::errors::PreconditionNotMet(
"attribute MUST be a pir::ArrayAttribute"));
auto attr_vec = attr.dyn_cast<pir::ArrayAttribute>().AsVector();
std::vector<int64_t> vec_int64;
for (auto vec_element : attr_vec) {
PADDLE_ENFORCE_EQ(
vec_element.isa<pir::Int64Attribute>(),
true,
common::errors::PreconditionNotMet("element MUST be a Int64Attribute"));
vec_int64.push_back(vec_element.dyn_cast<pir::Int64Attribute>().data());
}
return vec_int64;
}
std::set<std::string> GetRegisterDataType(const std::string& op_name) {
std::string non_inplace_op_name;
if (paddle::string::ends_with(op_name, "_")) {
non_inplace_op_name = op_name.substr(0, op_name.size() - 1);
}
std::set<std::string> data_type;
auto& phi_kernels = phi::KernelFactory::Instance().kernels();
for (auto& kernel_pair : phi_kernels) {
auto fluid_op_name = phi::TransToFluidOpName(kernel_pair.first);
if (kernel_pair.first != op_name && fluid_op_name != op_name &&
kernel_pair.first != non_inplace_op_name &&
fluid_op_name != non_inplace_op_name) {
continue;
}
for (auto& info_pair : kernel_pair.second) {
data_type.insert(phi::DataTypeToString(info_pair.first.dtype()));
}
}
if (data_type.empty()) {
VLOG(6) << "No data type is registered for " << op_name;
}
return data_type;
}
phi::DataType GetValueDataType(const pir::Type& type) {
if (type.isa<pir::DenseTensorType>()) {
return dialect::TransToPhiDataType(
type.dyn_cast<pir::DenseTensorType>().dtype());
} else if (type.isa<paddle::dialect::SelectedRowsType>()) {
return dialect::TransToPhiDataType(
type.dyn_cast<paddle::dialect::SelectedRowsType>().dtype());
} else if (type.isa<paddle::dialect::SparseCooTensorType>()) {
return dialect::TransToPhiDataType(
type.dyn_cast<paddle::dialect::SparseCooTensorType>().dtype());
} else if (type.isa<paddle::dialect::SparseCsrTensorType>()) {
return dialect::TransToPhiDataType(
type.dyn_cast<paddle::dialect::SparseCsrTensorType>().dtype());
} else if (type.isa<DenseTensorArrayType>()) {
return dialect::TransToPhiDataType(
type.dyn_cast<DenseTensorArrayType>().dtype());
} else if (type.isa<pir::VectorType>()) {
auto vec_value = type.dyn_cast<pir::VectorType>();
if (vec_value.size() > 0) {
return GetValueDataType(vec_value[0]);
} else {
return phi::DataType::UNDEFINED;
}
} else {
PADDLE_THROW(common::errors::InvalidType(
"Not support op type %s in ConvertOpTypeToKernelType.", type));
PADDLE_THROW(
common::errors::InvalidType("Currently, we can only get dtype for "
"DenseTensorType and SelectedRowsType."));
}
}
phi::DataType GetValueDataType(const pir::Value& value) {
if (value.impl() == nullptr) {
return phi::DataType::UNDEFINED;
}
return GetValueDataType(value.type());
}
void DoValueCheck(const pir::Value& value,
const std::string& input_name,
const std::set<std::string>& expected_dtype,
const std::string& op_name) {
std::string value_type = phi::DataTypeToString(GetValueDataType(value));
if (expected_dtype.find(value_type) == expected_dtype.end()) {
std::ostringstream joined;
std::copy(expected_dtype.begin(),
expected_dtype.end(),
std::ostream_iterator<std::string>(joined, ", "));
PADDLE_THROW(common::errors::InvalidType(
"Check data type error for op: %s, input: %s, %s.dtype: %s, and "
"expected_dtype: %s",
op_name,
input_name,
input_name,
value_type,
joined.str()));
}
}
void CheckValueDataType(const pir::Value& value,
const std::string& input_name,
const std::string& op_name) {
VLOG(6) << "CheckValueDataType for " << op_name << ", input: " << input_name;
std::set<std::string> expected_dtype = GetRegisterDataType(op_name);
DoValueCheck(value, input_name, expected_dtype, op_name);
}
bool IsSameDataTypeForValues(const std::vector<pir::Value>& vector_value) {
if (vector_value.size() <= 1) {
return true;
}
auto dtype = GetValueDataType(vector_value[0]);
for (size_t i = 1; i < vector_value.size(); ++i) {
if (GetValueDataType(vector_value[i]) != dtype) {
return false;
}
}
return true;
}
void CheckVectorOfValueDataType(const std::vector<pir::Value>& vector_value,
const std::string& input_name,
const std::string& op_name) {
VLOG(6) << "CheckVectorOfValueDataType for " << op_name
<< ", input: " << input_name;
if (vector_value.size() == 0) {
return;
}
if (!IsSameDataTypeForValues(vector_value)) {
PADDLE_THROW(common::errors::InvalidType(
"All the Values in the input must have the same data type."));
}
std::set<std::string> expected_dtype = GetRegisterDataType(op_name);
DoValueCheck(vector_value[0], input_name, expected_dtype, op_name);
}
void CheckDataType(const phi::DataType& dtype,
const std::string& dtype_name,
const std::string& op_name) {
VLOG(6) << "CheckDataType for " << op_name << ", input dtype: " << dtype_name;
std::set<std::string> expected_dtype = GetRegisterDataType(op_name);
std::string str_dtype = phi::DataTypeToString(dtype);
if (expected_dtype.find(str_dtype) == expected_dtype.end()) {
std::ostringstream joined;
std::copy(expected_dtype.begin(),
expected_dtype.end(),
std::ostream_iterator<std::string>(joined, ", "));
PADDLE_THROW(common::errors::InvalidType(
"Check data type error for op: %s, dtype: %s, and "
"expected_dtype: %s",
op_name,
str_dtype,
joined.str()));
}
}
void CheckDataTypeOrValue(const phi::DataType& dtype,
const std::string& dtype_name,
const pir::Value& value,
const std::string& value_name,
const std::string& op_name) {
if (dtype == phi::DataType::UNDEFINED) {
CheckValueDataType(value, value_name, op_name);
} else {
CheckDataType(dtype, dtype_name, op_name);
}
}
std::vector<int64_t> ParseValueShape(const pir::Value& shape,
bool* is_from_tensor) {
std::vector<int64_t> vec_shape;
if (shape.isa<pir::OpResult>() &&
shape.defining_op()->isa<paddle::dialect::FullIntArrayOp>()) {
vec_shape = paddle::dialect::GetInt64Vector(
shape.defining_op()
->dyn_cast<paddle::dialect::FullIntArrayOp>()
.attribute("value"));
} else if (shape.isa<pir::OpResult>() &&
shape.defining_op()->isa<paddle::dialect::FullOp>()) {
auto shape_item = shape.defining_op()
->dyn_cast<paddle::dialect::FullOp>()
.attribute("value")
.dyn_cast<paddle::dialect::ScalarAttribute>()
.data()
.to<double>();
auto shape_vec = shape.defining_op()
->dyn_cast<paddle::dialect::FullOp>()
.attribute("shape")
.dyn_cast<paddle::dialect::IntArrayAttribute>()
.data()
.GetData();
// TODO(ooooo): If can make sure shape_value's size is less than or equal
// to 1, can add a check here rather than product.
int64_t items = 1;
for (const auto& item : shape_vec) {
items *= item;
}
vec_shape = std::vector<int64_t>(items, shape_item);
} else if (shape.isa<pir::OpResult>() &&
shape.defining_op()->isa<paddle::dialect::StackOp>()) {
std::vector<pir::Value> inputs =
shape.defining_op()->operand_source(0).defining_op()->operands_source();
for (auto item : inputs) {
auto tmp = ParseValueShape(item, is_from_tensor);
vec_shape.insert(vec_shape.end(), tmp.begin(), tmp.end());
}
} else if (shape.isa<pir::OpResult>() &&
(shape.defining_op()->isa<paddle::dialect::ShapeOp>() ||
shape.defining_op()->isa<paddle::dialect::Shape64Op>()) &&
shape.type().isa<paddle::dialect::DenseTensorType>()) {
// tensor_shape may come from shape op
// x0.shape = [-1,3]
// tensor_shape = shape(x0)
// y = reshape(x, tensor_shape)
pir::Value inputs = shape.defining_op()->operand_source(0);
vec_shape = common::vectorize(
inputs.type().dyn_cast<paddle::dialect::DenseTensorType>().dims());
*is_from_tensor = true;
} else if (shape.isa<pir::OpResult>() &&
shape.defining_op()->isa<paddle::dialect::ConcatOp>()) {
// tensor_shape may come from concat
// tensor_shape = concat([full(1), full(2)])
// y = reshape(x, tensor_shape)
std::vector<pir::Value> inputs =
shape.defining_op()->operand_source(0).defining_op()->operands_source();
for (auto item : inputs) {
auto tmp = ParseValueShape(item, is_from_tensor);
vec_shape.insert(vec_shape.end(), tmp.begin(), tmp.end());
}
} else if (shape.type().isa<pir::VectorType>()) {
size_t shape_size = shape.type().dyn_cast<pir::VectorType>().size();
vec_shape = std::vector<int64_t>(shape_size, -1);
*is_from_tensor = true;
} else if (shape.type().isa<paddle::dialect::DenseTensorType>()) {
DDim shape_dim =
shape.type().dyn_cast<paddle::dialect::DenseTensorType>().dims();
size_t shape_size = common::product(shape_dim);
if (common::contain_unknown_dim(shape_dim)) {
shape_size = 1;
}
vec_shape = std::vector<int64_t>(shape_size, -1);
*is_from_tensor = true;
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Only support VectorType or DenseTensorType "
"or AllocatedDenseTensorType"));
}
return vec_shape;
}
const std::unordered_map<std::string, std::string>& CppTypeToAttrTypeMap() {
static const std::unordered_map<std::string, std::string> attr_type_map = {
{"bool", "pir::BoolAttribute"},
{"int", "pir::Int32Attribute"},
{"float", "pir::FloatAttribute"},
{"int64_t", "pir::Int64Attribute"},
{"std::string", "pir::StrAttribute"},
{"std::vector<int>", "pir::ArrayAttribute<pir::Int32Attribute>"},
{"std::vector<float>", "pir::ArrayAttribute<pir::FloatAttribute>"},
{"std::vector<int64_t>", "pir::ArrayAttribute<pir::Int64Attribute>"},
{"std::vector<std::string>", "pir::ArrayAttribute<pir::StrAttribute>"},
{"void*", "pir::PointerAttribute"},
};
return attr_type_map;
}
const std::unordered_map<std::string, phi::DataType>& StringToDataTypeMap() {
static std::unordered_map<std::string, phi::DataType> data_type_map{
{"bool", phi::DataType::BOOL},
{"uint8", phi::DataType::UINT8},
{"int8", phi::DataType::INT8},
{"uint16", phi::DataType::UINT16},
{"int16", phi::DataType::INT16},
{"uint32", phi::DataType::UINT32},
{"int32", phi::DataType::INT32},
{"uint64", phi::DataType::UINT64},
{"int64", phi::DataType::INT64},
{"float32", phi::DataType::FLOAT32},
{"complex64", phi::DataType::COMPLEX64},
{"complex128", phi::DataType::COMPLEX128},
{"Undefined", phi::DataType::UNDEFINED},
{"pstring", phi::DataType::PSTRING},
{"float16", phi::DataType::FLOAT16},
{"bfloat16", phi::DataType::BFLOAT16},
{"float64", phi::DataType::FLOAT64}};
return data_type_map;
}
const std::unordered_map<std::string, phi::Place>& StringToPlaceMap() {
static std::unordered_map<std::string, phi::Place> place_map{
{"cpu", CPUPlace{}},
{"gpu", GPUPlace{}},
{"gpu_pinned", phi::GPUPinnedPlace{}},
{"xpu", phi::XPUPlace{}},
{"xpu_pinned", phi::XPUPinnedPlace{}},
{"ipu", phi::IPUPlace{}},
{":", phi::CustomPlace{}},
{"undefined", phi::Place{}}};
return place_map;
}
const std::unordered_map<std::string, DataLayout>& StringToDataLayoutMap() {
static std::unordered_map<std::string, DataLayout> data_layout_map{
{"NHWC", DataLayout::NHWC},
{"NCHW", DataLayout::NCHW},
{"Undefined", DataLayout::kAnyLayout},
{"ONEDNN", DataLayout::ONEDNN},
{"SPARSE_COO", DataLayout::SPARSE_COO},
{"SPARSE_CSR", DataLayout::SPARSE_CSR},
{"NDHWC", DataLayout::kNDHWC},
{"NCDHW", DataLayout::kNCDHW},
{"PSTRING_UNION", DataLayout::PSTRING_UNION},
{"STRIDED", DataLayout::STRIDED}};
return data_layout_map;
}
void SetStopGradient() {}
void SetStopGradient(pir::Value* value) {
value->set_attribute(
"stop_gradient",
pir::BoolAttribute::get(pir::IrContext::Instance(), true));
}
void SetStopGradient(std::vector<pir::Value>* values) {
for (auto& value : *values) {
SetStopGradient(&value);
}
}
void SetStopGradient(paddle::optional<pir::Value>* value) {
if (value->get_ptr() != nullptr) {
SetStopGradient(value->get_ptr());
}
}
void SetStopGradient(paddle::optional<std::vector<pir::Value>>* values) {
if (values->get_ptr() != nullptr) {
SetStopGradient(values->get_ptr());
}
}
void PushStopGradient(const pir::Value& value, std::vector<bool>* arr) {
if (!IsEmptyValue(value)) {
arr->push_back(false);
} else {
arr->push_back(true);
}
}
std::vector<std::vector<bool>> ConstructStopGradient(pir::Operation* op) {
std::vector<std::vector<bool>> stop_gradients(op->results().size());
for (size_t i = 0; i < op->results().size(); i++) {
PushStopGradient(op->result(i), &stop_gradients[i]);
}
return stop_gradients;
}
bool CanGroupOpRunCpuKernel(const std::vector<pir::Value>& vec_inputs,
const std::vector<pir::Value>& vec_output) {
for (size_t i = 0; i < vec_inputs.size(); ++i) {
auto tmp_in = vec_inputs[i];
if (!tmp_in || !tmp_in.type()) {
continue;
}
DDim in_dims;
if (auto type_info =
tmp_in.type()
.dyn_cast<paddle::dialect::AllocatedDenseTensorType>()) {
auto type = tmp_in.type().dyn_cast<AllocatedDenseTensorType>();
in_dims = type.dims();
if (type.place().GetType() != phi::AllocationType::CPU) {
return false;
}
} else if (auto type_info =
tmp_in.type().dyn_cast<paddle::dialect::DenseTensorType>()) {
in_dims = type_info.dims();
}
// 1. dynamic shape not need lower x86
if (::common::contain_unknown_dim(in_dims)) {
return false;
}
// 2. size < 4 not need lower x86
if (phi::product(in_dims) > 4) {
return false;
}
}
for (size_t i = 0; i < vec_output.size(); ++i) {
const auto& out = vec_output[i];
if (!out || !out.type()) {
continue;
}
if (out.type().isa<DenseTensorType>()) {
auto type = out.type().dyn_cast<DenseTensorType>();
if (type.dtype().isa<pir::BFloat16Type>()) {
return false;
}
if (::common::contain_unknown_dim(type.dims())) {
return false;
}
if (phi::product(type.dims()) > 4) {
return false;
}
}
}
return true;
}
} // namespace dialect
} // namespace paddle