Files
2026-07-13 12:40:42 +08:00

1445 lines
62 KiB
C++

/* Copyright (c) 2021 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/fluid/framework/custom_operator.h"
#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "paddle/fluid/eager/api/utils/global_utils.h"
#include "paddle/fluid/framework/attribute.h"
#include "paddle/fluid/framework/convert_utils.h"
#include "paddle/fluid/framework/phi_utils.h"
#include "paddle/fluid/framework/tensor.h"
#include "paddle/phi/api/all.h"
#include "paddle/phi/backends/dynload/dynamic_loader.h"
#include "paddle/phi/core/compat/convert_utils.h"
#include "paddle/phi/core/platform/device/gpu/gpu_info.h"
#include "paddle/phi/core/tensor_utils.h"
#include "paddle/utils/any.h"
#include "paddle/utils/string/string_helper.h"
#ifdef PADDLE_WITH_CUSTOM_DEVICE
#include "paddle/fluid/framework/infershape_utils.h"
#include "paddle/phi/backends/device_manager.h"
#include "paddle/phi/capi/include/c_infer_meta_context.h"
#include "paddle/phi/capi/include/c_kernel_registry.h"
#include "paddle/phi/capi/include/c_meta_tensor.h"
#endif
#include "paddle/common/flags.h"
#include "paddle/phi/api/include/operants_manager.h"
#include "paddle/phi/api/include/tensor_operants.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
COMMON_DECLARE_string(tensor_operants_mode);
COMMON_DECLARE_bool(enable_pir_in_executor);
namespace paddle::framework {
// custom op kernel call function define
static void RunKernelFunc(
const framework::ExecutionContext& ctx,
const paddle::KernelFunc& func,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::vector<std::string>& attrs,
const std::unordered_map<std::string, std::string>& inplace_map) {
VLOG(3) << "Custom Operator: Start run KernelFunc.";
// prepare CustomOpKernelContext
paddle::CustomOpKernelContext kernel_ctx;
for (auto& in_name : inputs) {
VLOG(3) << "Custom Operator: input name - " << in_name;
if (detail::IsDuplicableVar(in_name)) { // inputs vector<Tensor>
std::vector<paddle::Tensor> custom_vec_in;
if (ctx.HasInputs(in_name)) { // general vector<Tensor> inputs
// return const std::vector<const DenseTensor*>
auto vec_x = ctx.MultiInput<DenseTensor>(in_name);
PADDLE_ENFORCE_NE(vec_x.empty(),
true,
common::errors::NotFound(
"Input vector<tensor> (%s) is empty.", in_name));
for (size_t i = 0; i < vec_x.size(); ++i) {
auto* x = vec_x[i];
PADDLE_ENFORCE_NOT_NULL(
x,
common::errors::NotFound(
"The %d-th tensor in input vector<tensor> (%s) is nullptr.",
i,
in_name));
PADDLE_ENFORCE_EQ(x->IsInitialized(),
true,
common::errors::InvalidArgument(
"The %d-th tensor in input vector<tensor> (%s) "
"is not initialized.",
i,
in_name));
paddle::Tensor custom_t;
custom_t.set_impl(std::make_shared<DenseTensor>(*x));
custom_vec_in.emplace_back(custom_t);
}
} else { // optional vector<Tensor> inputs.
PADDLE_ENFORCE(
detail::IsOptionalVar(in_name),
common::errors::NotFound("Your custom operator's KernelFunc cannot "
"find input parameter `%s`",
in_name));
VLOG(3) << "Custom Operator: KernelFunc's vector input " << in_name
<< " is optional dtype with None input";
// NOTE(HongyuJia): In dygraph mode, we can not distinguish Tensor and
// vector<Tensor> when user inputs None, so dygraph mode appends one
// un-initialized Tensor to CustomOpKernelContext. To be compatible with
// dygraph mode, `custom_vec_in` also emplace_back one un-initialized
// tensor here.
custom_vec_in.emplace_back(paddle::Tensor());
}
kernel_ctx.EmplaceBackInputs(custom_vec_in);
} else { // inputs Tensor
if (ctx.HasInput(in_name)) { // general Tensor inputs
auto* x = ctx.Input<DenseTensor>(in_name);
PADDLE_ENFORCE_NOT_NULL(
x,
common::errors::NotFound("Input tensor (%s) is nullptr.", in_name));
PADDLE_ENFORCE_EQ(
x->IsInitialized(),
true,
common::errors::InvalidArgument(
"Input tensor (%s) is not initialized.", in_name));
paddle::Tensor custom_in;
custom_in.set_impl(std::make_shared<DenseTensor>(*x));
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (custom_in.is_gpu_pinned()) {
VLOG(3) << "Custom Operator: custom input is gpu pinned tensor";
auto gpu_place = GPUPlace(platform::GetCurrentDeviceId());
auto custom_gpu_in = custom_in.copy_to(gpu_place, true);
kernel_ctx.EmplaceBackInput(std::move(custom_gpu_in));
} else {
kernel_ctx.EmplaceBackInput(std::move(custom_in));
}
#else
kernel_ctx.EmplaceBackInput(std::move(custom_in));
#endif
} else { // optional Tensor inputs
PADDLE_ENFORCE(
detail::IsOptionalVar(in_name),
common::errors::NotFound("Your custom operator's KernelFunc cannot "
"find input parameter `%s`",
in_name));
VLOG(3) << "Custom Operator: KernelFunc's input " << in_name
<< " is optional dtype with None input";
kernel_ctx.EmplaceBackInput(paddle::Tensor());
}
}
}
for (auto& attr_str : attrs) {
auto attr_name_and_type = paddle::ParseAttrStr(attr_str);
auto attr_name = attr_name_and_type[0];
auto attr_type_str = attr_name_and_type[1];
if (attr_type_str == "bool") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<bool>(attr_name));
} else if (attr_type_str == "int") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<int>(attr_name));
} else if (attr_type_str == "float") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<float>(attr_name));
} else if (attr_type_str == "double") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<double>(attr_name));
} else if (attr_type_str == "int64_t") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<int64_t>(attr_name));
} else if (attr_type_str == "std::string") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<std::string>(attr_name));
} else if (attr_type_str == "std::vector<int>") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<std::vector<int>>(attr_name));
} else if (attr_type_str == "std::vector<float>") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<std::vector<float>>(attr_name));
} else if (attr_type_str == "std::vector<double>") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<std::vector<double>>(attr_name));
} else if (attr_type_str == "std::vector<int64_t>") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<std::vector<int64_t>>(attr_name));
} else if (attr_type_str == "std::vector<std::string>") {
kernel_ctx.EmplaceBackAttr(ctx.Attr<std::vector<std::string>>(attr_name));
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported `%s` type value as custom attribute now. "
"Supported data types include `bool`, `int`, `float`, `double`, "
"`int64_t`, `std::string`, `std::vector<int>`, "
"`std::vector<float>`, `std::vector<double>`, "
"`std::vector<int64_t>`,`std::vector<std::string>`, Please check "
"whether "
"the attribute data type and data type string are matched.",
attr_type_str));
}
}
VLOG(3) << "Custom Operator: push outputs into CustomOpKernelContext.";
// cache the target tensor pointers
std::vector<DenseTensor*> true_out_ptrs;
for (size_t i = 0; i < outputs.size(); ++i) {
auto out_name = outputs[i];
if (detail::IsDuplicableVar(
out_name)) { // general/inplace vector<Tensor> outputs
PADDLE_ENFORCE(
!inplace_map.empty() || (i == 0UL && outputs.size() == 1UL),
common::errors::PreconditionNotMet(
"If custom operator's outputs contains `paddle::Vec()` type "
"without setting InplaceMap, it only can hold one output."));
auto vec_out = ctx.MultiOutput<DenseTensor>(out_name);
// handle inplace optional outputs = None case
if (vec_out.empty()) {
PADDLE_ENFORCE(
detail::IsOptionalVar(out_name) && !inplace_map.empty(),
common::errors::InvalidArgument(
"Custom operator couldn't find custom output for name %s. If "
"you "
"are using inplace optional inputs & outputs, please check "
"your "
"InplaceMap and `Outputs` again and make sure %s is wrapped by "
"`paddle::Optional`",
out_name,
out_name));
VLOG(3) << "Custom Operator: InferDtype - inplace optional outputs : "
<< out_name << " is None.";
true_out_ptrs.emplace_back(nullptr);
kernel_ctx.EmplaceBackOutput(paddle::Tensor());
continue;
}
// general/inplace vector<Tensor> outputs
std::vector<paddle::Tensor> custom_vec_out;
for (size_t j = 0; j < vec_out.size(); ++j) {
auto* out = vec_out[j];
PADDLE_ENFORCE_NOT_NULL(
out,
common::errors::NotFound(
"The %d-th tensor in output vector<tensor> (%s) is nullptr.",
j,
out_name));
true_out_ptrs.emplace_back(out);
paddle::Tensor custom_t;
// here only can copy the output tensor into context
custom_t.set_impl(std::make_shared<DenseTensor>(*out));
custom_vec_out.emplace_back(custom_t);
}
kernel_ctx.EmplaceBackOutputs(custom_vec_out);
} else {
// handle inplace optional outputs = None case
if (!ctx.HasOutput(out_name)) {
PADDLE_ENFORCE(
detail::IsOptionalVar(out_name) && !inplace_map.empty(),
common::errors::InvalidArgument(
"Custom operator couldn't find custom output for name %s. If "
"you "
"are using inplace optional inputs & outputs, please check "
"your "
"InplaceMap and `Outputs` again and make sure %s is wrapped by "
"`paddle::Optional`",
out_name,
out_name));
VLOG(3) << "Custom Operator: InferDtype - inplace optional outputs : "
<< out_name << " is None.";
true_out_ptrs.emplace_back(nullptr);
kernel_ctx.EmplaceBackOutput(paddle::Tensor());
continue;
}
// general/inplace Tensor outputs
auto* out = ctx.Output<DenseTensor>(out_name);
PADDLE_ENFORCE_NOT_NULL(
out,
common::errors::NotFound("Output tensor (%s) is nullptr.", out_name));
true_out_ptrs.emplace_back(out);
paddle::Tensor custom_out;
// here only can copy the output tensor into context
custom_out.set_impl(std::make_shared<DenseTensor>(*out));
kernel_ctx.EmplaceBackOutput(std::move(custom_out));
}
}
try {
VLOG(3) << "Custom Operator: Run ComputeFunc.";
FLAGS_tensor_operants_mode = "phi";
if (paddle::OperantsManager::Instance().phi_operants.get() == nullptr) {
paddle::OperantsManager::Instance().phi_operants =
std::make_unique<paddle::operants::PhiTensorOperants>();
VLOG(4) << "Initialize phi tensor operants successfully";
}
// handle inplace map
kernel_ctx.UpdatePlainOutputs(inputs, outputs, inplace_map);
func(&kernel_ctx);
kernel_ctx.AssignInplaceOutputs();
// sync output tensor data into original output
auto* calc_outs = kernel_ctx.AllMutableOutput();
PADDLE_ENFORCE_EQ(
true_out_ptrs.size(),
calc_outs->size(),
common::errors::InvalidArgument(
"The number of element in custom operator outputs is wrong, "
"expected contains %d Tensors, but actually contains %d "
"Tensors.",
true_out_ptrs.size(),
calc_outs->size()));
for (size_t i = 0; i < true_out_ptrs.size(); ++i) {
auto* true_out = true_out_ptrs.at(i);
// handle optional inplace outputs = None case
if (true_out == nullptr && !calc_outs->at(i).defined()) {
continue;
}
PADDLE_ENFORCE(
true_out != nullptr && calc_outs->at(i).defined(),
common::errors::InvalidArgument(
"The returned Tensor is not defined in the KernelFn or custom "
"operator passes wrong output in static mode."));
auto calc_out =
std::dynamic_pointer_cast<DenseTensor>(calc_outs->at(i).impl());
// assign meta info
auto* true_out_meta = phi::DenseTensorUtils::GetMutableMeta(true_out);
true_out_meta->dims = calc_out->dims();
true_out_meta->dtype = calc_out->dtype();
true_out_meta->layout = calc_out->layout();
true_out_meta->offset = calc_out->offset();
true_out_meta->strides = true_out_meta->calc_strides(true_out_meta->dims);
// lod no need to be reset
// reset holder if needed
if (true_out->Holder() != calc_out->Holder()) {
true_out->ResetHolder(calc_out->Holder());
}
}
} catch (platform::EnforceNotMet& exception) {
throw exception;
} catch (std::exception& ex) {
PADDLE_THROW(common::errors::External("%s", ex.what()));
} catch (...) {
PADDLE_THROW(common::errors::Fatal(
"Custom operator raises an unknown exception in runtime."));
}
}
static void RunDefaultInferShapeFunc(
framework::InferShapeContext* ctx,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::unordered_map<std::string, std::string>& inplace_map) {
if (inplace_map.empty()) { // general case, assure single input and output
PADDLE_ENFORCE_EQ(
inputs.size(),
1UL,
common::errors::Unavailable(
"Your custom operator contains multiple inputs. "
"We only allow a custom operator that contains only one input "
"and only one output without setting the InferShapeFn. "
"At this time, the input shape will be directly set to "
"the output shape.\n"
"Please set the InferShapeFn of custom "
"operator by .SetInferShapeFn(PD_INFER_SHAPE(...))"));
PADDLE_ENFORCE_EQ(
outputs.size(),
1UL,
common::errors::Unavailable(
"Your custom operator contains multiple outputs. "
"We only allow a custom operator that contains only one input "
"and only one output without setting the InferShapeFn. "
"At this time, the input shape will be directly set to "
"the output shape.\n"
"Please set the InferShapeFn of custom "
"operator by .SetInferShapeFn(PD_INFER_SHAPE(...))"));
VLOG(3) << "Custom Operator: Default InferShape - share ddim.";
ctx->ShareDim(inputs[0], outputs[0]);
} else { // inplace case
PADDLE_ENFORCE_EQ(
inplace_map.size(),
outputs.size(),
common::errors::Unavailable(
"Your custom operator uses `SetInplaceMap` without setting the "
"InferShapeFn. However, `Outputs` size = %d does not match the "
"`InplaceMap` size = %d. Please check `SetInplaceMap` again or set "
"the InferShapeFn of custom operator by "
"`.SetInferShapeFn(PD_INFER_SHAPE(...)`)",
outputs.size(),
inplace_map.size()));
for (auto const& pair : inplace_map) {
if (detail::IsDuplicableVar(pair.first)) {
// make sure ctx has valid inplace optional outputs
if (!ctx->HasOutputs(pair.second)) {
PADDLE_ENFORCE(
detail::IsOptionalVar(pair.second),
common::errors::InvalidArgument(
"Custom operator couldn't find custom output name for %s. If "
"you are using inplace optional inputs & outputs, please "
"check "
"your InplaceMap and `Outputs` again and make sure %s is "
"wrapped by `paddle::Optional`",
pair.second,
pair.second));
VLOG(3) << "Custom Operator: InferDtype - inplace optional outputs : "
<< pair.second << " is None.";
} else {
ctx->SetOutputsDim(pair.second, ctx->GetInputsDim(pair.first));
}
} else {
// make sure ctx has valid inplace optional outputs
if (!ctx->HasOutput(pair.second)) {
PADDLE_ENFORCE(
detail::IsOptionalVar(pair.second),
common::errors::InvalidArgument(
"Custom operator couldn't find custom output name for %s. If "
"you are using inplace optional inputs & outputs, please "
"check "
"your InplaceMap and `Outputs` again and make sure %s is "
"wrapped by `paddle::Optional`",
pair.second,
pair.second));
VLOG(3) << "Custom Operator: InferDtype - inplace optional outputs : "
<< pair.second << " is None.";
} else {
ctx->ShareDim(pair.first, pair.second);
}
}
}
}
}
static void RunInferShapeFunc(
framework::InferShapeContext* ctx,
const paddle::InferShapeFunc& func,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::vector<std::string>& attrs,
const std::unordered_map<std::string, std::string>& inplace_map,
const std::unordered_map<std::string, std::string>& inplace_reverse_map) {
std::vector<std::vector<int64_t>> input_shapes;
std::vector<std::vector<std::vector<int64_t>>> vec_input_shapes;
VLOG(3) << "Custom Operator: InferShape - get input ddim.";
for (auto& in_name : inputs) {
if (detail::IsDuplicableVar(in_name)) {
std::vector<std::vector<int64_t>> vec_shape;
if (ctx->HasInputs(in_name)) { // general inputs
auto vec_ddim = ctx->GetInputsDim(in_name);
vec_shape.reserve(vec_ddim.size());
std::transform(vec_ddim.begin(),
vec_ddim.end(),
std::back_inserter(vec_shape),
[&](const DDim& ddim) -> std::vector<int64_t> {
return common::vectorize(ddim);
});
} else { // optional inputs, `vec_shape` is empty
PADDLE_ENFORCE(
detail::IsOptionalVar(in_name),
common::errors::NotFound("Your custom operator's InferShapeFunc "
"cannot find input parameter `%s`",
in_name));
VLOG(3) << "Custom Operator: InferShapeFunc's vector input " << in_name
<< " is optional dtype with None input";
}
vec_input_shapes.emplace_back(vec_shape);
} else {
if (ctx->HasInput(in_name)) { // general inputs
auto ddim = ctx->GetInputDim(in_name);
input_shapes.emplace_back(common::vectorize(ddim));
} else { // optional inputs
PADDLE_ENFORCE(
detail::IsOptionalVar(in_name),
common::errors::NotFound("Your custom operator's InferShapeFunc "
"cannot find input parameter `%s`",
in_name));
input_shapes.emplace_back(std::vector<int64_t>());
VLOG(3) << "Custom Operator: InferShapeFunc's input " << in_name
<< " is optional dtype with None input";
}
}
}
std::vector<paddle::any> custom_attrs;
for (auto& attr_str : attrs) {
auto attr_name_and_type = paddle::ParseAttrStr(attr_str);
auto attr_name = attr_name_and_type[0];
auto attr_type_str = attr_name_and_type[1];
if (attr_type_str == "bool") {
custom_attrs.emplace_back(ctx->Attrs().Get<bool>(attr_name));
} else if (attr_type_str == "int") {
custom_attrs.emplace_back(ctx->Attrs().Get<int>(attr_name));
} else if (attr_type_str == "float") {
custom_attrs.emplace_back(ctx->Attrs().Get<float>(attr_name));
} else if (attr_type_str == "int64_t") {
custom_attrs.emplace_back(ctx->Attrs().Get<int64_t>(attr_name));
} else if (attr_type_str == "std::string") {
custom_attrs.emplace_back(ctx->Attrs().Get<std::string>(attr_name));
} else if (attr_type_str == "std::vector<int>") {
custom_attrs.emplace_back(ctx->Attrs().Get<std::vector<int>>(attr_name));
} else if (attr_type_str == "std::vector<float>") {
custom_attrs.emplace_back(
ctx->Attrs().Get<std::vector<float>>(attr_name));
} else if (attr_type_str == "std::vector<int64_t>") {
// NOTE(chenweihang): InferShape can't support std::vector<int64_t>
// attr type, because the input type is std::vector<int64_t>, only
// can use one rule to parse std::vector<int64_t> parameter
continue;
} else if (attr_type_str == "std::vector<std::string>") {
custom_attrs.emplace_back(
ctx->Attrs().Get<std::vector<std::string>>(attr_name));
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported `%s` type value as custom attribute now. "
"Supported data types include `bool`, `int`, `float`, "
"`int64_t`, `std::string`, `std::vector<int>`, "
"`std::vector<float>`, `std::vector<int64_t>`, "
"`std::vector<std::string>`, Please check whether the attribute data "
"type and data type string are matched.",
attr_type_str));
}
}
VLOG(3) << "Custom Operator: InferShape - calc output ddim.";
auto output_shapes = func(input_shapes, vec_input_shapes, custom_attrs);
if (inplace_map.empty()) {
PADDLE_ENFORCE_EQ(outputs.size(),
output_shapes.size(),
common::errors::InvalidArgument(
"Your custom operator has set the InferShapeFn. "
"However, `Outputs` size = %d does not match the "
"returned vector size of InferShapeFn = %d. Please "
"check InferShapeFn again.",
outputs.size(),
output_shapes.size()));
} else {
PADDLE_ENFORCE_EQ(
outputs.size(),
output_shapes.size() + inplace_map.size(),
common::errors::InvalidArgument(
"Your custom operator uses `SetInplaceMap` and sets the "
"InferShapeFn. However, `Outputs` size = %d does not match the "
"`InplaceMap size + InferShapeFn output size` = %d. Please check "
"InplaceMap and InferShapeFn again",
outputs.size(),
output_shapes.size() + inplace_map.size()));
}
VLOG(3)
<< "Custom Operator: InferShape - set output ddim: inplace_map.size() = "
<< inplace_map.size()
<< ", output_shapes.size() = " << output_shapes.size();
size_t output_shape_idx = 0;
for (auto out_name : outputs) {
if (detail::IsDuplicableVar(out_name)) {
PADDLE_ENFORCE(
inplace_reverse_map.find(out_name) != inplace_reverse_map.end(),
common::errors::InvalidArgument(
"Custom operator only supports `paddle::Vec(...)` inputs and "
"cannot support `paddle::Vec(...)` output without setting "
"InplaceMap. If you have to use `paddle::Vec(...)` output, "
"please indicate it by setting InplaceMap manually."));
// make sure ctx has valid inplace optional outputs
if (ctx->HasOutputs(out_name)) {
auto in_name = inplace_reverse_map.at(out_name);
ctx->SetOutputsDim(out_name, ctx->GetInputsDim(in_name));
} else {
PADDLE_ENFORCE(
detail::IsOptionalVar(out_name),
common::errors::InvalidArgument(
"Custom operator couldn't find custom output name for %s. If "
"you are using inplace optional inputs & outputs, please check "
"your InplaceMap and `Outputs` again and make sure %s is "
"wrapped by `paddle::Optional`",
out_name,
out_name));
VLOG(3) << "Custom Operator: InferDtype - inplace optional outputs : "
<< out_name << " is None.";
}
} else {
if (inplace_reverse_map.find(out_name) != inplace_reverse_map.end()) {
// make sure ctx has valid inplace optional outputs
if (ctx->HasOutput(out_name)) {
// Share dims between inplace inputs and outputs
ctx->ShareDim(inplace_reverse_map.at(out_name), out_name);
} else {
PADDLE_ENFORCE(
detail::IsOptionalVar(out_name),
common::errors::InvalidArgument(
"Custom operator couldn't find custom output name for %s. If "
"you are using inplace optional inputs & outputs, please "
"check your InplaceMap and `Outputs` again and make sure %s "
"is wrapped by `paddle::Optional`",
out_name,
out_name));
VLOG(3) << "Custom Operator: InferDtype - inplace optional outputs : "
<< out_name << " is None.";
}
} else {
// Set output dims by the output of InferShapeFn
ctx->SetOutputDim(out_name,
common::make_ddim(output_shapes[output_shape_idx++]));
}
}
}
}
static void RunDefaultInferDtypeFunc(
framework::InferVarTypeContext* ctx,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::unordered_map<std::string, std::string>& inplace_map) {
if (inplace_map.empty()) { // general case, assure single input and output
PADDLE_ENFORCE_EQ(
inputs.size(),
1UL,
common::errors::Unavailable(
"Your custom operator contains multiple inputs. "
"We only allow a custom operator that contains only one input "
"and only one output without setting the InferDtypeFn. "
"At this time, the input dtype will be directly set to "
"the output dtype.\n"
"Please set the InferDtypeFn of custom "
"operator by `.SetInferDtypeFn(PD_INFER_DTYPE(...))`"));
PADDLE_ENFORCE_EQ(
outputs.size(),
1UL,
common::errors::Unavailable(
"Your custom operator contains multiple outputs. "
"We only allow a custom operator that contains only one input "
"and only one output without setting the InferDtypeFn. "
"At this time, the input dtype will be directly set to "
"the output dtype.\n"
"Please set the InferDtypeFn of custom "
"operator by `.SetInferDtypeFn(PD_INFER_DTYPE(...))`"));
VLOG(3) << "Custom Operator: InferDtype - share dtype.";
auto dtype = ctx->GetInputDataType(inputs[0]);
ctx->SetOutputDataType(outputs[0], dtype);
} else { // inplace case
PADDLE_ENFORCE_EQ(
inplace_map.size(),
outputs.size(),
common::errors::Unavailable(
"Your custom operator uses `SetInplaceMap` without setting the "
"InferDtypeFn. However, `Outputs` size = %d does not match the "
"`InplaceMap` size = %d. Please check `SetInplaceMap` again or set "
"the InferDtypeFn of custom operator by "
"`.SetInferDtypeFn(PD_INFER_DTYPE(...))`",
outputs.size(),
inplace_map.size()));
for (auto const& pair : inplace_map) {
VLOG(3) << "Custom Operator: InferDtype - inplace dtype: " << pair.first
<< "->" << pair.second;
// make sure ctx has valid inplace optional outputs
if (!ctx->HasOutput(pair.second)) {
PADDLE_ENFORCE(
detail::IsOptionalVar(pair.second),
common::errors::InvalidArgument(
"Custom operator couldn't find custom output name for %s. If "
"you are using inplace optional inputs & outputs, please check "
"your InplaceMap and `Outputs` again and make sure %s is "
"wrapped by `paddle::Optional`",
pair.second,
pair.second));
VLOG(3) << "Custom Operator: InferDtype - inplace optional outputs : "
<< pair.second << " is None.";
continue;
}
if (detail::IsDuplicableVar(pair.first)) {
size_t size = ctx->InputSize(pair.first);
for (size_t i = 0; i < size; ++i) {
auto dtype = ctx->GetInputDataType(pair.first, static_cast<int>(i));
ctx->SetOutputDataType(pair.second, dtype, static_cast<int>(i));
}
} else {
auto dtype = ctx->GetInputDataType(pair.first);
ctx->SetOutputDataType(pair.second, dtype);
}
}
}
}
static void RunInferDtypeFunc(
framework::InferVarTypeContext* ctx,
const paddle::InferDtypeFunc& func,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::vector<std::string>& attrs,
const std::unordered_map<std::string, std::string>& inplace_map,
const std::unordered_map<std::string, std::string>& inplace_reverse_map) {
std::vector<DataType> input_dtypes;
std::vector<std::vector<DataType>> vec_input_dtypes;
VLOG(3) << "Custom Operator: InferDtype - get input dtype.";
for (auto& in_name : inputs) {
if (detail::IsDuplicableVar(in_name)) {
std::vector<DataType> vec_custom_dtype;
if (ctx->HasInput(in_name)) { // general inputs
for (size_t i = 0; i < ctx->InputSize(in_name); ++i) {
auto dtype = ctx->GetInputDataType(in_name, static_cast<int>(i));
vec_custom_dtype.emplace_back(phi::TransToPhiDataType(dtype));
}
} else { // optional inputs, `vec_custom_dtype` is empty
PADDLE_ENFORCE(
detail::IsOptionalVar(in_name),
common::errors::NotFound("Your custom operator's InferDtypeFn "
"cannot find input parameter `%s`",
in_name));
VLOG(3) << "Custom Operator: InferDtypeFn's vector input " << in_name
<< " is optional dtype with None input";
}
vec_input_dtypes.emplace_back(vec_custom_dtype);
} else {
if (ctx->HasInput(in_name)) { // general inputs
auto dtype = ctx->GetInputDataType(in_name);
input_dtypes.emplace_back(phi::TransToPhiDataType(dtype));
} else { // optional inputs
PADDLE_ENFORCE(
detail::IsOptionalVar(in_name),
common::errors::NotFound("Your custom operator's InferDtypeFn "
"cannot find input parameter `%s`",
in_name));
input_dtypes.emplace_back(DataType::UNDEFINED);
VLOG(3) << "Custom Operator: InferDtypeFn's input " << in_name
<< " is optional dtype with None input";
}
}
}
std::vector<paddle::any> custom_attrs;
for (auto& attr_str : attrs) {
auto attr_name_and_type = paddle::ParseAttrStr(attr_str);
auto attr_name = attr_name_and_type[0];
auto attr_type_str = attr_name_and_type[1];
if (attr_type_str == "bool") {
custom_attrs.emplace_back(
PADDLE_GET_CONST(bool, ctx->GetAttr(attr_name)));
} else if (attr_type_str == "int") {
custom_attrs.emplace_back(PADDLE_GET_CONST(int, ctx->GetAttr(attr_name)));
} else if (attr_type_str == "float") {
custom_attrs.emplace_back(
PADDLE_GET_CONST(float, ctx->GetAttr(attr_name)));
} else if (attr_type_str == "int64_t") {
custom_attrs.emplace_back(
PADDLE_GET_CONST(int64_t, ctx->GetAttr(attr_name)));
} else if (attr_type_str == "std::string") {
custom_attrs.emplace_back(
PADDLE_GET_CONST(std::string, ctx->GetAttr(attr_name)));
} else if (attr_type_str == "std::vector<int>") {
custom_attrs.emplace_back(
PADDLE_GET_CONST(std::vector<int>, ctx->GetAttr(attr_name)));
} else if (attr_type_str == "std::vector<float>") {
custom_attrs.emplace_back(
PADDLE_GET_CONST(std::vector<float>, ctx->GetAttr(attr_name)));
} else if (attr_type_str == "std::vector<int64_t>") {
custom_attrs.emplace_back(
PADDLE_GET_CONST(std::vector<int64_t>, ctx->GetAttr(attr_name)));
} else if (attr_type_str == "std::vector<std::string>") {
custom_attrs.emplace_back(
PADDLE_GET_CONST(std::vector<std::string>, ctx->GetAttr(attr_name)));
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported `%s` type value as custom attribute now. "
"Supported data types include `bool`, `int`, `float`, "
"`int64_t`, `std::string`, `std::vector<int>`, "
"`std::vector<float>`, `std::vector<int64_t>`, "
"`std::vector<std::string>`, Please check whether the attribute data "
"type and data type string are matched.",
attr_type_str));
}
}
VLOG(3) << "Custom Operator: InferDtype - infer output dtype.";
auto output_dtypes = func(input_dtypes, vec_input_dtypes, custom_attrs);
if (inplace_map.empty()) {
PADDLE_ENFORCE_EQ(outputs.size(),
output_dtypes.size(),
common::errors::InvalidArgument(
"Your custom operator has set the InferDtypeFn. "
"However, `Outputs` size = %d does not match the "
"returned vector size of InferDtypeFn = %d. Please "
"check InferDtypeFn again.",
outputs.size(),
output_dtypes.size()));
} else {
PADDLE_ENFORCE_EQ(
outputs.size(),
output_dtypes.size() + inplace_map.size(),
common::errors::InvalidArgument(
"Your custom operator uses `SetInplaceMap` and sets the "
"InferDtypeFn. However, `Outputs` size = %d does not match the "
"`InplaceMap size + InferDtypeFn output size` = %d. Please check "
"InplaceMap and InferDtypeFn again",
outputs.size(),
output_dtypes.size() + inplace_map.size()));
}
VLOG(3)
<< "Custom Operator: InferDtype - set output dtype: inplace_map.size() = "
<< inplace_map.size()
<< ", output_dtypes.size() = " << output_dtypes.size();
size_t output_dtype_idx = 0;
for (auto out_name : outputs) {
if (detail::IsDuplicableVar(out_name)) {
PADDLE_ENFORCE(
inplace_reverse_map.find(out_name) != inplace_reverse_map.end(),
common::errors::InvalidArgument(
"Custom operator only supports `paddle::Vec(...)` inputs and "
"cannot support `paddle::Vec(...)` output without setting "
"InplaceMap. If you have to use `paddle::Vec(...)` output, "
"please indicate it by setting InplaceMap manually."));
auto in_name = inplace_reverse_map.at(out_name);
// make sure ctx has valid inplace optional outputs
if (ctx->HasOutput(out_name)) {
size_t size = ctx->InputSize(in_name);
for (size_t i = 0; i < size; ++i) {
auto dtype = ctx->GetInputDataType(in_name, static_cast<int>(i));
ctx->SetOutputDataType(out_name, dtype, static_cast<int>(i));
}
} else {
PADDLE_ENFORCE(
detail::IsOptionalVar(out_name),
common::errors::InvalidArgument(
"Custom operator couldn't find custom output name for %s. If "
"you are using inplace optional inputs & outputs, please check "
"your InplaceMap and `Outputs` again and make sure %s is "
"wrapped by `paddle::Optional`",
out_name,
out_name));
VLOG(3) << "Custom Operator: InferDtype - inplace optional outputs : "
<< out_name << " is None.";
}
} else {
if (inplace_reverse_map.find(out_name) != inplace_reverse_map.end()) {
// make sure ctx has valid inplace optional outputs
if (ctx->HasOutput(out_name)) {
auto in_name = inplace_reverse_map.at(out_name);
// Share dtype between inplace inputs and outputs
ctx->SetOutputDataType(out_name, ctx->GetInputDataType(in_name));
} else {
PADDLE_ENFORCE(
out_name.find(paddle::kOptionalSuffix) != std::string::npos,
common::errors::InvalidArgument(
"Custom operator couldn't find custom output name for %s. If "
"you are using inplace optional inputs & outputs, please "
"check your InplaceMap and `Outputs` again and make sure %s "
"is wrapped by `paddle::Optional`",
out_name,
out_name));
VLOG(3) << "Custom Operator: InferDtype - inplace optional outputs : "
<< out_name << " is None.";
}
} else {
// Set output dtype by the output of InferDtypeFn
ctx->SetOutputDataType(out_name,
paddle::framework::TransToProtoVarType(
output_dtypes[output_dtype_idx++]));
}
}
}
}
//////////////////// Operator Define /////////////////
class CustomOperator : public OperatorWithKernel {
public:
using OperatorWithKernel::OperatorWithKernel;
// Dummy infershape
// Because it is a pure virtual function, it must be implemented
void InferShape(framework::InferShapeContext* ctx) const override {
VLOG(3) << "Custom Operator: Dummy infer shape of custom operator.";
}
/**
* NOTE: [Skip the Kernel Selection]
* Custom Op only registers one Op kernel on each device, so that the
* data type selection and promotion that depends on GetExpectedKernelType,
* as well as the adaptation of various other special situations,
* need users to implement, to avoid users needs to implement
* GetExpectedKernelType function when expanding other cases.
* The RAW type is used here as the data type, indicating that
* it can only be determined at runtime.
*/
phi::KernelKey GetExpectedKernelType(
const framework::ExecutionContext& ctx) const override {
return phi::KernelKey(ctx.GetPlace());
}
/**
* NOTE: [Skip Input Variable Cast for DataType]
* Because the kernel data type is RAW, we should skip the cast for
* data type difference when PrepareData.
*/
phi::KernelKey GetKernelTypeForVar(
const std::string& var_name,
const DenseTensor& tensor,
const phi::KernelKey& expected_kernel_type) const override {
return phi::KernelKey(phi::Backend::ALL_BACKEND,
tensor.layout(),
expected_kernel_type.dtype());
}
};
//////////// Operator and Kernel Register //////////////
static void RegisterOperatorKernelWithPlace(
const std::string& name,
const OperatorWithKernel::OpKernelFunc& op_kernel_func,
const proto::VarType::Type type,
const Place& place) {
OpKernelType key(type, place);
VLOG(3) << "Custom Operator: op kernel key: " << key;
OperatorWithKernel::AllOpKernels()[name][key] = op_kernel_func;
}
static void RegisterOperatorKernel(
const std::string& name,
const paddle::KernelFunc& kernel_func,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::vector<std::string>& attrs,
const std::unordered_map<std::string, std::string>& inplace_map,
void* dso_handle) {
VLOG(3) << "Custom Operator: op name in kernel: " << name;
// NOTE [ Dummy Op Kernel Key ]
// TODO(chenweihang): Because execute engine need get device context based
// op_kernel_key.place_, so we should register kernel for each
// device. But this is not entirely correct, if user only give a cpu kernel,
// but call api in gpu device, it will cause error.
OperatorWithKernel::OpKernelFunc op_kernel_func;
if (kernel_func) {
VLOG(3) << "Register custom operator " << name << " with kernel func";
op_kernel_func =
[kernel_func, inputs, outputs, attrs, inplace_map]( // NOLINT
const framework::ExecutionContext& ctx) {
VLOG(3) << "Custom Operator: run custom kernel func in lambda.";
RunKernelFunc(ctx, kernel_func, inputs, outputs, attrs, inplace_map);
};
} else {
VLOG(3) << "Register custom operator " << name
<< " with raw op kernel func";
PADDLE_ENFORCE_NOT_NULL(
dso_handle,
common::errors::InvalidArgument(
"The dso handle must be provided if kernel_func is nullptr."));
using OpKernelFuncPtr = void(const framework::ExecutionContext&);
auto symbol_name = "PD_" + name + "_raw_op_kernel_func";
auto* func = detail::DynLoad<OpKernelFuncPtr>(dso_handle, symbol_name);
op_kernel_func = func;
}
RegisterOperatorKernelWithPlace(
name, op_kernel_func, proto::VarType::RAW, CPUPlace());
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
RegisterOperatorKernelWithPlace(
name, op_kernel_func, proto::VarType::RAW, GPUPlace());
#endif
#if defined(PADDLE_WITH_XPU)
RegisterOperatorKernelWithPlace(
name, op_kernel_func, proto::VarType::RAW, XPUPlace());
#endif
#ifdef PADDLE_WITH_CUSTOM_DEVICE
auto device_types = phi::DeviceManager::GetAllCustomDeviceTypes();
for (const auto& dev_type : device_types) {
RegisterOperatorKernelWithPlace(
name, op_kernel_func, proto::VarType::RAW, phi::CustomPlace(dev_type));
}
#endif
}
void RegisterOperatorWithMetaInfo(const std::vector<OpMetaInfo>& op_meta_infos,
void* dso_handle) {
/* Op register */
OpInfo info;
auto& base_op_meta = op_meta_infos.front();
auto op_name = OpMetaInfoHelper::GetOpName(base_op_meta);
if (OpInfoMap::Instance().Has(op_name)) {
LOG(WARNING) << "Operator (" << op_name
<< ") has been registered before as PIR op.";
LOG(WARNING) << "PIR Operator (" << op_name
<< ") has been overridden by Custom op!.";
}
auto& op_inputs = OpMetaInfoHelper::GetInputs(base_op_meta);
auto& op_outputs = OpMetaInfoHelper::GetOutputs(base_op_meta);
auto& op_attrs = OpMetaInfoHelper::GetAttrs(base_op_meta);
auto& op_inplace_map = OpMetaInfoHelper::GetInplaceMap(base_op_meta);
auto& op_inplace_reverse_map =
OpMetaInfoHelper::GetInplaceReverseMap(base_op_meta);
auto& kernel_fn = OpMetaInfoHelper::GetKernelFn(base_op_meta);
auto& infer_shape_func = OpMetaInfoHelper::GetInferShapeFn(base_op_meta);
auto& infer_dtype_func = OpMetaInfoHelper::GetInferDtypeFn(base_op_meta);
VLOG(3) << "Custom Operator: forward, op name: " << op_name;
VLOG(3) << "Custom Operator: forward, op inputs: "
<< string::join_strings(op_inputs, ',');
VLOG(3) << "Custom Operator: forward, op outputs: "
<< string::join_strings(op_outputs, ',');
VLOG(3) << "Custom Operator: forward, op attrs: "
<< string::join_strings(op_attrs, ',');
if (!op_inplace_map.empty()) {
VLOG(3) << "Custom Operator: forward, op inplace_map: "
<< string::join_strings(op_inplace_map, ',', [](auto& pair) {
return pair.first + ": " + pair.second;
});
}
// Op
info.creator_ = [](const std::string& op_name,
const VariableNameMap& inputs,
const VariableNameMap& outputs,
const AttributeMap& attrs) {
return new CustomOperator(op_name, inputs, outputs, attrs);
};
// OpMaker
info.proto_ = new proto::OpProto;
info.proto_->set_type(op_name);
info.checker_ = new OpAttrChecker();
CustomOpMaker custom_maker(op_inputs, op_outputs, op_attrs);
custom_maker(info.proto_, info.checker_);
PADDLE_ENFORCE_EQ(
info.proto_->IsInitialized(),
true,
common::errors::PreconditionNotMet(
"Fail to initialize %s's OpProto, because %s is not initialized.",
op_name,
info.proto_->InitializationErrorString()));
// Inplace
if (!op_inplace_map.empty()) {
info.infer_inplace_ = [op_inplace_map](bool use_cuda) {
return op_inplace_map;
};
}
// InferShape
if (infer_shape_func == nullptr) {
// use default InferShape
info.infer_shape_ = [op_inputs, op_outputs, op_inplace_map]( // NOLINT
InferShapeContext* ctx) {
RunDefaultInferShapeFunc(ctx, op_inputs, op_outputs, op_inplace_map);
};
} else {
info.infer_shape_ = [op_inputs, // NOLINT
op_outputs,
op_attrs,
op_inplace_map,
op_inplace_reverse_map,
infer_shape_func](InferShapeContext* ctx) {
RunInferShapeFunc(ctx,
infer_shape_func,
op_inputs,
op_outputs,
op_attrs,
op_inplace_map,
op_inplace_reverse_map);
};
}
// Infer Dtype
if (infer_dtype_func == nullptr) {
// use default InferDtype
info.infer_var_type_ = [op_inputs, op_outputs, op_inplace_map]( // NOLINT
InferVarTypeContext* ctx) {
RunDefaultInferDtypeFunc(ctx, op_inputs, op_outputs, op_inplace_map);
};
} else {
info.infer_var_type_ = [op_inputs, // NOLINT
op_outputs,
op_attrs,
op_inplace_map,
op_inplace_reverse_map,
infer_dtype_func](InferVarTypeContext* ctx) {
RunInferDtypeFunc(ctx,
infer_dtype_func,
op_inputs,
op_outputs,
op_attrs,
op_inplace_map,
op_inplace_reverse_map);
};
}
// Kernel func
RegisterOperatorKernel(op_name,
kernel_fn,
op_inputs,
op_outputs,
op_attrs,
op_inplace_map,
dso_handle);
// If grad op or double grad op exists
std::string cur_op_name = op_name;
for (size_t i = 1; i < op_meta_infos.size(); ++i) {
auto& cur_grad_op = op_meta_infos[i];
auto& grad_op_name = OpMetaInfoHelper::GetOpName(cur_grad_op);
auto& grad_op_inputs = OpMetaInfoHelper::GetInputs(cur_grad_op);
auto& grad_op_outputs = OpMetaInfoHelper::GetOutputs(cur_grad_op);
auto& grad_op_attrs = OpMetaInfoHelper::GetAttrs(cur_grad_op);
auto& grad_op_inplace_map = OpMetaInfoHelper::GetInplaceMap(cur_grad_op);
auto& grad_op_inplace_reverse_map =
OpMetaInfoHelper::GetInplaceReverseMap(cur_grad_op);
auto& grad_kernel_fn = OpMetaInfoHelper::GetKernelFn(cur_grad_op);
auto& grad_infer_shape_fn = OpMetaInfoHelper::GetInferShapeFn(cur_grad_op);
auto& grad_infer_dtype_fn = OpMetaInfoHelper::GetInferDtypeFn(cur_grad_op);
VLOG(3) << "Custom Operator: backward, op name: " << grad_op_name;
VLOG(3) << "Custom Operator: backward, op inputs: "
<< string::join_strings(grad_op_inputs, ',');
VLOG(3) << "Custom Operator: backward, op outputs: "
<< string::join_strings(grad_op_outputs, ',');
VLOG(3) << "Custom Operator: backward, op attrs: "
<< string::join_strings(grad_op_attrs, ',');
if (!op_inplace_map.empty()) {
VLOG(3) << "Custom Operator: backward, op inplace_map: "
<< string::join_strings(grad_op_inplace_map, ',', [](auto& pair) {
return pair.first + ": " + pair.second;
});
}
bool is_double_grad = (i == 2);
// GradOpDescMaker
info.grad_op_maker_ =
[grad_op_name, // NOLINT
grad_op_inputs,
grad_op_outputs,
is_double_grad](
const OpDesc& fwd_op,
const std::unordered_set<std::string>& no_grad_set,
std::unordered_map<std::string, std::string>* grad_to_var,
const std::vector<BlockDesc*>& grad_block) {
CustomGradOpMaker<paddle::framework::OpDesc> maker(fwd_op,
no_grad_set,
grad_to_var,
grad_block,
grad_op_name,
grad_op_inputs,
grad_op_outputs,
is_double_grad);
return maker();
};
// GradOpBaseMaker
info.dygraph_grad_op_maker_ =
[grad_op_name, // NOLINT
grad_op_inputs,
grad_op_outputs,
is_double_grad](
const std::string& type,
const imperative::NameVarBaseMap& var_base_map_in,
const imperative::NameVarBaseMap& var_base_map_out,
const framework::AttributeMap& attrs,
const framework::AttributeMap& default_attrs,
const std::map<std::string, std::string>& inplace_map) {
CustomGradOpMaker<paddle::imperative::OpBase> maker(type,
var_base_map_in,
var_base_map_out,
attrs,
inplace_map,
grad_op_name,
grad_op_inputs,
grad_op_outputs,
is_double_grad);
maker.SetDygraphDefaultAttrsMap(default_attrs);
return maker();
};
/* Grad op register */
OpInfo grad_info;
// Grad Op
grad_info.creator_ = [](const std::string& type,
const VariableNameMap& inputs,
const VariableNameMap& outputs,
const AttributeMap& attrs) {
return new CustomOperator(type, inputs, outputs, attrs);
};
// Inplace
if (!grad_op_inplace_map.empty()) {
grad_info.infer_inplace_ = [grad_op_inplace_map](bool use_cuda) {
return grad_op_inplace_map;
};
}
// Grad InferShape
if (grad_infer_shape_fn == nullptr) {
grad_info.infer_shape_ = [grad_op_inputs, // NOLINT
grad_op_outputs,
is_double_grad](InferShapeContext* ctx) {
// 1. if forward input exists, gradient's shape is same with forward
// input
// default
// [Suitable for most situations]
// 2. if forward input not exists, and only contains one grad input and
// output,
// use grad input shape as grad output shape
// [Suitable for the situation that forward input is not used as
// backward input]
for (auto& out_name : grad_op_outputs) {
auto fwd_name = detail::NoGrad(out_name, is_double_grad);
if (detail::IsDuplicableVar(fwd_name)) {
// Duplicable forward var must as backward input
ctx->ShareDim(fwd_name, out_name);
} else {
if (ctx->HasInput(fwd_name)) {
ctx->ShareDim(fwd_name, out_name);
} else {
PADDLE_ENFORCE_EQ(
grad_op_inputs.size() == 1UL && grad_op_outputs.size() == 1UL,
true,
common::errors::Unavailable(
"Custom grad operator infershape error. "
"If a custom grad operator contains only one input and "
"only one output, the input shape will be directly set "
"to the output shape. Otherwise, Please set the forward "
"input as the grad operator's input or set the "
"InferShapeFn of custom grad operator by "
".SetInferShapeFn(PD_INFER_SHAPE(...))"));
ctx->ShareDim(grad_op_inputs[0], out_name);
}
}
}
};
} else {
grad_info.infer_shape_ = [grad_op_inputs, // NOLINT
grad_op_outputs,
grad_op_attrs,
grad_op_inplace_map,
grad_op_inplace_reverse_map,
grad_infer_shape_fn](InferShapeContext* ctx) {
RunInferShapeFunc(ctx,
grad_infer_shape_fn,
grad_op_inputs,
grad_op_outputs,
grad_op_attrs,
grad_op_inplace_map,
grad_op_inplace_reverse_map);
};
}
// Grad InferDtype
if (grad_infer_dtype_fn != nullptr) {
grad_info.infer_var_type_ =
[grad_op_inputs, // NOLINT
grad_op_outputs,
grad_op_attrs,
grad_op_inplace_map,
grad_op_inplace_reverse_map,
grad_infer_dtype_fn](InferVarTypeContext* ctx) {
RunInferDtypeFunc(ctx,
grad_infer_dtype_fn,
grad_op_inputs,
grad_op_outputs,
grad_op_attrs,
grad_op_inplace_map,
grad_op_inplace_reverse_map);
};
}
// Kernel func
RegisterOperatorKernel(grad_op_name,
grad_kernel_fn,
grad_op_inputs,
grad_op_outputs,
grad_op_attrs,
grad_op_inplace_map,
dso_handle);
// update current info
OpInfoMap::Instance().Insert(cur_op_name, info);
cur_op_name = grad_op_name;
info = grad_info;
}
// insert last info
OpInfoMap::Instance().Insert(cur_op_name, info);
}
std::unordered_map<std::string, std::vector<OpMetaInfo>>
RegisterOperatorWithMetaInfoMap(const paddle::OpMetaInfoMap& op_meta_info_map,
void* dso_handle) {
auto& meta_info_map = op_meta_info_map.GetMap();
VLOG(3) << "Custom Operator: size of op meta info map - "
<< meta_info_map.size();
// pair: {op_type, OpMetaInfo}
pir::IrContext* ctx = pir::IrContext::Instance();
auto* custom_dialect =
ctx->GetOrRegisterDialect<paddle::dialect::CustomOpDialect>();
std::unordered_map<std::string, std::vector<OpMetaInfo>> diff_map;
for (auto& pair : meta_info_map) {
VLOG(3) << "Custom Operator: pair first -> op name: " << pair.first;
auto& inplace_map = OpMetaInfoHelper::GetInplaceMap(pair.second[0]);
auto postfix = inplace_map.empty() ? "" : "_";
// Custom dialect register
if (custom_dialect->HasRegistered(paddle::framework::kCustomDialectPrefix +
pair.first + postfix)) {
VLOG(3) << "The operator `" << pair.first
<< "` has been registered. "
"Therefore, we will not repeat the registration here.";
continue;
}
for (const auto& meta_info : pair.second) {
VLOG(3) << "register pir custom op :"
<< OpMetaInfoHelper::GetOpName(meta_info);
custom_dialect->RegisterCustomOp(meta_info);
}
diff_map[pair.first] = pair.second;
// Register Fluid op
RegisterOperatorWithMetaInfo(pair.second, dso_handle);
}
return diff_map;
}
////////////////////// User APIs ///////////////////////
// load op api
std::unordered_map<std::string, std::vector<OpMetaInfo>>
LoadOpMetaInfoAndRegisterOp(const std::string& dso_name) {
void* handle = phi::dynload::GetOpDsoHandle(dso_name);
VLOG(3) << "load custom_op lib: " << dso_name;
typedef OpMetaInfoMap& get_op_meta_info_map_t();
auto* get_op_meta_info_map =
detail::DynLoad<get_op_meta_info_map_t>(handle, "PD_GetOpMetaInfoMap");
auto& op_meta_info_map = get_op_meta_info_map();
auto diff_map = RegisterOperatorWithMetaInfoMap(op_meta_info_map, handle);
for (auto& pair : diff_map) {
VLOG(3) << "diff op name: " << pair.first;
}
// return op_meta_info_map.GetMap();
return diff_map;
}
} // namespace paddle::framework
#ifdef PADDLE_WITH_CUSTOM_DEVICE
void PD_RegisterOperator(const char* kernel_name_cstr,
size_t in_nargs,
PD_KernelArgumentType* in_args_type,
size_t attr_nargs,
PD_KernelArgumentType* attr_args_type,
size_t out_nargs,
PD_KernelArgumentType* out_args_type,
void (*infer_shape_fn)(PD_InferMetaContext*)) {
std::string kernel_name(kernel_name_cstr);
if (infer_shape_fn &&
!paddle::framework::OpInfoMap::Instance().Has(kernel_name)) {
VLOG(8) << "Registering a new operator: " << kernel_name;
std::vector<std::string> op_inputs, op_outputs, op_attrs;
for (size_t i = 0; i < in_nargs; ++i) {
if (in_args_type[i] == PD_KernelArgumentType::PD_ARG_TYPE_TENSOR) {
op_inputs.push_back("Input_" + std::to_string(i));
} else if (in_args_type[i] ==
PD_KernelArgumentType::PD_ARG_TYPE_LIST_TENSOR) {
op_inputs.push_back("Input_" + std::to_string(i) +
paddle::kTensorVectorSuffix);
} else if (in_args_type[i] ==
PD_KernelArgumentType::PD_ARG_TYPE_OPTIONAL_TENSOR) {
op_inputs.push_back("Input_" + std::to_string(i) +
paddle::kOptionalSuffix);
} else {
op_inputs.push_back("Input_unknown");
}
}
for (size_t i = 0; i < out_nargs; ++i) {
if (out_args_type[i] == PD_KernelArgumentType::PD_ARG_TYPE_TENSOR) {
op_outputs.push_back("Output_" + std::to_string(i));
} else if (out_args_type[i] ==
PD_KernelArgumentType::PD_ARG_TYPE_LIST_TENSOR) {
op_outputs.push_back("Output_" + std::to_string(i) +
paddle::kTensorVectorSuffix);
} else {
op_outputs.push_back("Output_unknown");
}
}
for (size_t i = 0; i < attr_nargs; ++i) {
auto attr_type = attr_args_type[i];
if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_BOOL) {
op_attrs.push_back("Attr_" + std::to_string(i) + ":bool");
} else if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_INT32) {
op_attrs.push_back("Attr_" + std::to_string(i) + ":int");
} else if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_FLOAT32) {
op_attrs.push_back("Attr_" + std::to_string(i) + ":float");
} else if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_FLOAT64) {
op_attrs.push_back("Attr_" + std::to_string(i) + ":double");
} else if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_INT64) {
op_attrs.push_back("Attr_" + std::to_string(i) + ":int64_t");
} else if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_STRING) {
op_attrs.push_back("Attr_" + std::to_string(i) + ":std::string");
} else if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_LIST_INT32) {
op_attrs.push_back("Attr_" + std::to_string(i) + ":std::vector<int>");
} else if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_LIST_FLOAT32) {
op_attrs.push_back("Attr_" + std::to_string(i) + ":std::vector<float>");
} else if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_LIST_INT64) {
op_attrs.push_back("Attr_" + std::to_string(i) +
":std::vector<int64_t>");
} else if (attr_type == PD_KernelArgumentType::PD_ARG_TYPE_LIST_STRING) {
op_attrs.push_back("Attr_" + std::to_string(i) +
":std::vector<std::string>");
} else {
op_attrs.push_back("Attr_unknown");
}
}
paddle::framework::OpInfo info;
// Op
info.creator_ = [](const std::string& op_name,
const paddle::framework::VariableNameMap& inputs,
const paddle::framework::VariableNameMap& outputs,
const paddle::framework::AttributeMap& attrs) {
return new paddle::framework::OperatorWithKernel(
op_name, inputs, outputs, attrs);
};
// OpMaker
info.proto_ = new paddle::framework::proto::OpProto;
info.proto_->set_type(kernel_name);
info.checker_ = new paddle::framework::OpAttrChecker();
paddle::framework::CustomOpMaker custom_maker(
op_inputs, op_outputs, op_attrs);
custom_maker(info.proto_, info.checker_);
PADDLE_ENFORCE_EQ(
info.proto_->IsInitialized(),
true,
common::errors::PreconditionNotMet(
"Fail to initialize %s's OpProto, because %s is not initialized.",
kernel_name,
info.proto_->InitializationErrorString()));
info.infer_shape_ = [infer_shape_fn, kernel_name](
paddle::framework::InferShapeContext* ctx) {
auto infer_meta_context =
paddle::framework::BuildInferMetaContext(ctx, kernel_name);
infer_shape_fn(
reinterpret_cast<PD_InferMetaContext*>(&infer_meta_context));
};
paddle::framework::OpInfoMap::Instance().Insert(kernel_name, info);
}
}
#endif