1675 lines
70 KiB
Python
1675 lines
70 KiB
Python
# 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.
|
|
|
|
from __future__ import annotations
|
|
|
|
import collections
|
|
import re
|
|
|
|
PREFIX_TENSOR_NAME = 'input_'
|
|
PREFIX_META_TENSOR_NAME = 'meta_'
|
|
ORIGIN_PREFIX_TENSOR_NAME = 'origin_input_'
|
|
|
|
|
|
def parse_plain_list(s: str, sep=",") -> list[str]:
|
|
"""Copy from `paddle/fluid/operators/generator/parse_utils.py`"""
|
|
if sep == ",":
|
|
pattern = re.compile(r',(?![^{]*\})') # support "int[] a={1,2}"
|
|
items = re.split(pattern, s.strip())
|
|
items = [x.strip() for x in items]
|
|
return items
|
|
else:
|
|
return [item.strip() for item in s.strip().split(sep)]
|
|
|
|
|
|
def IsUsePredefinedOut(position_list: list) -> bool:
|
|
"""
|
|
Determine whether all forwards are Tensors, including outputs and positions, And the length is between [1,7].
|
|
The number 7 represents that the multi out mechanism currently supports a maximum of 7 output tensors.
|
|
"""
|
|
if not position_list:
|
|
return False
|
|
|
|
is_all_tensor = all(pos == "Tensor" for pos in position_list)
|
|
length = len(position_list)
|
|
|
|
return is_all_tensor and 1 <= length <= 7
|
|
|
|
|
|
class BaseAPI:
|
|
def __init__(self, api_item_yaml):
|
|
self.api = self.get_api_name(api_item_yaml)
|
|
|
|
# inputs:
|
|
# names : [], list of input names
|
|
# input_info : {input_name : type}
|
|
# attrs:
|
|
# names : [], list of attribute names
|
|
# attr_info : { attr_name : (type, default_values)}
|
|
# outputs:
|
|
# names : [], list of output names
|
|
# types : [], list of output types
|
|
# out_size_expr : [], expression for getting size of vector<Tensor>
|
|
(
|
|
self.inputs,
|
|
self.attrs,
|
|
self.outputs,
|
|
self.optional_vars,
|
|
) = self.parse_args(self.api, api_item_yaml)
|
|
|
|
self.is_base_api = True
|
|
self.is_only_composite_api = False
|
|
# Whether to generate code for inplace API
|
|
self.is_inplace_context = False
|
|
if 'invoke' in api_item_yaml:
|
|
self.is_base_api = False
|
|
self.invoke = api_item_yaml['invoke']
|
|
else:
|
|
if 'infer_meta' in api_item_yaml:
|
|
self.infer_meta = self.parse_infer_meta(
|
|
api_item_yaml['infer_meta']
|
|
)
|
|
if 'composite' in api_item_yaml and 'kernel' not in api_item_yaml:
|
|
self.is_base_api = False
|
|
self.is_only_composite_api = True
|
|
self.kernel = None
|
|
else:
|
|
self.kernel = self.parse_kernel(api_item_yaml['kernel'])
|
|
self.data_transform = self.parse_data_transform(api_item_yaml)
|
|
self.inplace_map, self.view_map = {}, {}
|
|
|
|
self.gene_input_func = {
|
|
"const Tensor&": {
|
|
"dense": self.gene_dense_input,
|
|
"selected_rows": self.gene_selected_rows_input,
|
|
},
|
|
"const paddle::optional<Tensor>&": {
|
|
"dense": self.gene_dense_input,
|
|
"selected_rows": self.gene_selected_rows_input,
|
|
},
|
|
"const std::vector<Tensor>&": {"dense": self.gene_vec_dense_input},
|
|
"const paddle::optional<std::vector<Tensor>>&": {
|
|
"dense": self.gene_optional_vec_dense_input
|
|
},
|
|
}
|
|
|
|
def get_api_name(self, api_item_yaml):
|
|
if 'op' in api_item_yaml:
|
|
return api_item_yaml['op']
|
|
elif 'backward_op' in api_item_yaml:
|
|
return api_item_yaml['backward_op']
|
|
else:
|
|
raise ValueError("op or backward_op are not in api_yaml.")
|
|
|
|
def get_api_func_name(self):
|
|
return self.api
|
|
|
|
def is_inplace_input(self, input_name):
|
|
is_inplace_api = (
|
|
self.get_api_func_name()[-1] == "_" or self.is_inplace_context
|
|
)
|
|
return is_inplace_api and input_name in self.inplace_map.values()
|
|
|
|
def get_input_tensor_args(self, inplace_flag=False):
|
|
input_args = []
|
|
inplace_type_map = {
|
|
"const Tensor&": "Tensor&",
|
|
"const paddle::optional<Tensor>&": "paddle::optional<Tensor>&",
|
|
"const std::vector<Tensor>&": "std::vector<Tensor>&",
|
|
"const paddle::optional<std::vector<Tensor>>&": "paddle::optional<std::vector<Tensor>>&",
|
|
}
|
|
for name in self.inputs['names']:
|
|
name = name.split('@')[0]
|
|
if inplace_flag and name in self.inplace_map.values():
|
|
input_args.append(
|
|
inplace_type_map[self.inputs['input_info'][name]]
|
|
+ ' '
|
|
+ name
|
|
)
|
|
else:
|
|
input_args.append(self.inputs['input_info'][name] + ' ' + name)
|
|
return input_args
|
|
|
|
# funcs backward_api.h will use
|
|
def get_grad_outputs_define(self, inplace_flag=False):
|
|
define_string = ""
|
|
for i, out_type in enumerate(self.outputs['types']):
|
|
out_name = self.outputs['names'][i].split('@')[0]
|
|
if out_type == "std::vector<Tensor>":
|
|
if inplace_flag and out_name in self.inplace_map:
|
|
out_name = self.inplace_map[out_name]
|
|
define_string = " "
|
|
else:
|
|
define_string += " " + out_type + " " + out_name + ";\n"
|
|
|
|
vec_tensor_string = f""" std::vector<Tensor*> {out_name}_x;
|
|
for (size_t i = 0; i < {out_name}.size(); i++){{
|
|
{out_name}_x.push_back(&({out_name}[i]));
|
|
}}"""
|
|
define_string += vec_tensor_string
|
|
else:
|
|
if inplace_flag and out_name in self.inplace_map:
|
|
continue
|
|
define_string += " " + out_type + " " + out_name + ";\n"
|
|
define_string += (
|
|
" "
|
|
+ out_type
|
|
+ "* "
|
|
+ out_name
|
|
+ "_ptr = &"
|
|
+ out_name
|
|
+ ";\n"
|
|
)
|
|
return define_string
|
|
|
|
def get_optional_inputs_change(self, inplace_flag=False):
|
|
branch_string = ""
|
|
input_name = []
|
|
output_type = []
|
|
output_name = []
|
|
|
|
if len(self.optional_vars) == 0 or inplace_flag:
|
|
return branch_string
|
|
|
|
for name, type in zip(self.outputs['names'], self.outputs['types']):
|
|
name = name.split('@')[0]
|
|
output_name.append(name)
|
|
output_type.append(type)
|
|
|
|
for name in self.inputs['names']:
|
|
name = name.split('@')[0]
|
|
input_name.append(name)
|
|
|
|
for out_name, type in zip(output_name, output_type):
|
|
if out_name.endswith("_grad"):
|
|
name = out_name[:-5]
|
|
else:
|
|
continue
|
|
|
|
if (
|
|
name in input_name
|
|
and name in self.optional_vars
|
|
and type != "std::vector<Tensor>"
|
|
):
|
|
branch_string += (
|
|
f" if (!{name}) {{ {name}_grad_ptr = nullptr; }} \n"
|
|
)
|
|
|
|
return branch_string
|
|
|
|
def get_grad_api_call_args(self, inplace_flag):
|
|
args = []
|
|
for name in self.inputs['names']:
|
|
name = name.split('@')[0]
|
|
args.append(name)
|
|
|
|
for name in self.attrs['names']:
|
|
args.append(name)
|
|
|
|
for i, name in enumerate(self.outputs['names']):
|
|
name = name.split('@')[0]
|
|
out_type = self.outputs['types'][i]
|
|
if out_type == "std::vector<Tensor>":
|
|
if inplace_flag and name in self.inplace_map:
|
|
name = self.inplace_map[name]
|
|
|
|
out_string = name + "_x"
|
|
else:
|
|
if inplace_flag and name in self.inplace_map:
|
|
name = self.inplace_map[name]
|
|
if name in self.optional_vars:
|
|
out_string = name + ".get_ptr()"
|
|
else:
|
|
out_string = "&" + name
|
|
else:
|
|
out_string = name + "_ptr"
|
|
|
|
args.append(out_string)
|
|
return ", ".join(args)
|
|
|
|
def get_grad_output(self, inplace_flag):
|
|
args = []
|
|
for i, name in enumerate(self.outputs['names']):
|
|
name = name.split('@')[0]
|
|
if inplace_flag and name in self.inplace_map:
|
|
args.append("std::ref(" + self.inplace_map[name] + ")")
|
|
else:
|
|
args.append(name)
|
|
|
|
if len(args) == 1:
|
|
return args[0]
|
|
else:
|
|
return f"""std::make_tuple({", ".join(args)})"""
|
|
|
|
def get_declare_args(
|
|
self, inplace_flag=False, grad_flag=False, append_predefined_out=False
|
|
):
|
|
declare_args = self.get_input_tensor_args(inplace_flag)
|
|
for name in self.attrs['names']:
|
|
default_value = ''
|
|
if self.attrs['attr_info'][name][1] is not None:
|
|
default_value = ' = ' + self.attrs['attr_info'][name][1]
|
|
declare_args.append(
|
|
self.attrs['attr_info'][name][0] + ' ' + name + default_value
|
|
)
|
|
|
|
if (
|
|
not grad_flag
|
|
and not inplace_flag
|
|
and append_predefined_out
|
|
and self.api != "empty_like"
|
|
):
|
|
if IsUsePredefinedOut(self.outputs['types']):
|
|
length = len(self.outputs['names'])
|
|
if length == 1:
|
|
type_str = "paddle::Tensor*"
|
|
else:
|
|
type_str = (
|
|
f"std::tuple<{', '.join(['paddle::Tensor*'] * length)}>"
|
|
)
|
|
declare_args.append(
|
|
f"paddle::optional<{type_str}> predefined_out = paddle::none"
|
|
)
|
|
|
|
return ", ".join(declare_args)
|
|
|
|
def get_define_args(
|
|
self, inplace_flag=False, grad_flag=False, append_predefined_out=True
|
|
):
|
|
define_args = self.get_input_tensor_args(inplace_flag)
|
|
for name in self.attrs['names']:
|
|
define_args.append(self.attrs['attr_info'][name][0] + ' ' + name)
|
|
|
|
if (
|
|
not grad_flag
|
|
and not inplace_flag
|
|
and append_predefined_out
|
|
and self.api != "empty_like"
|
|
):
|
|
if IsUsePredefinedOut(self.outputs['types']):
|
|
length = len(self.outputs['names'])
|
|
if length == 1:
|
|
type_str = "paddle::Tensor*"
|
|
else:
|
|
type_str = (
|
|
f"std::tuple<{', '.join(['paddle::Tensor*'] * length)}>"
|
|
)
|
|
define_args.append(
|
|
f"paddle::optional<{type_str}> predefined_out"
|
|
)
|
|
|
|
return ", ".join(define_args)
|
|
|
|
def parse_args(self, api_name, api_item_yaml):
|
|
optional_vars = []
|
|
if 'optional' in api_item_yaml:
|
|
optional_vars = [
|
|
item.strip() for item in api_item_yaml['optional'].split(',')
|
|
]
|
|
inputs, attrs = self.parse_input_and_attr(
|
|
api_name, api_item_yaml['args'], optional_vars
|
|
)
|
|
output_type_list, output_names, out_size_expr = self.parse_output(
|
|
api_name, api_item_yaml['output']
|
|
)
|
|
return (
|
|
inputs,
|
|
attrs,
|
|
{
|
|
'names': output_names,
|
|
'types': output_type_list,
|
|
'out_size_expr': out_size_expr,
|
|
},
|
|
optional_vars,
|
|
)
|
|
|
|
def parse_input_and_attr(self, api_name, args_config, optional_vars=[]):
|
|
inputs = {'names': [], 'input_info': {}}
|
|
attrs = {'names': [], 'attr_info': {}}
|
|
args_str = args_config.strip()
|
|
assert args_str.startswith('(') and args_str.endswith(')'), (
|
|
f"Args declaration should start with '(' and end with ')', please check the args of {api_name} in yaml."
|
|
)
|
|
args_str = args_str[1:-1]
|
|
pattern = re.compile(r',(?![^{]*\})') # support int[] a={1,3}
|
|
args_list = re.split(pattern, args_str.strip())
|
|
args_list = [x.strip() for x in args_list]
|
|
input_types_map = {
|
|
'Tensor': 'const Tensor&',
|
|
'Tensor[]': 'const std::vector<Tensor>&',
|
|
}
|
|
attr_types_map = {
|
|
'IntArray': 'const IntArray&',
|
|
'Scalar': 'const Scalar&',
|
|
'Scalar(int)': 'const Scalar&',
|
|
'Scalar(int64_t)': 'const Scalar&',
|
|
'Scalar(float)': 'const Scalar&',
|
|
'Scalar(double)': 'const Scalar&',
|
|
'Scalar[]': 'const std::vector<phi::Scalar>&',
|
|
'int': 'int',
|
|
'int32_t': 'int32_t',
|
|
'int64_t': 'int64_t',
|
|
'long': 'long',
|
|
'size_t': 'size_t',
|
|
'float': 'float',
|
|
'float[]': 'const std::vector<float>&',
|
|
'double': 'double',
|
|
'double[]': 'const std::vector<double>&',
|
|
'bool': 'bool',
|
|
'bool[]': 'const std::vector<bool>&',
|
|
'str': 'const std::string&',
|
|
'str[]': 'const std::vector<std::string>&',
|
|
'Place': 'const Place&',
|
|
'DataLayout': 'DataLayout',
|
|
'DataType': 'DataType',
|
|
'int64_t[]': 'const std::vector<int64_t>&',
|
|
'int[]': 'const std::vector<int>&',
|
|
}
|
|
optional_types_trans = {
|
|
'Tensor': 'const paddle::optional<Tensor>&',
|
|
'Tensor[]': 'const paddle::optional<std::vector<Tensor>>&',
|
|
'int': 'paddle::optional<int>',
|
|
'int32_t': 'paddle::optional<int32_t>',
|
|
'int64_t': 'paddle::optional<int64_t>',
|
|
'float': 'paddle::optional<float>',
|
|
'double': 'paddle::optional<double>',
|
|
'bool': 'paddle::optional<bool>',
|
|
'Place': 'paddle::optional<const Place&>',
|
|
'DataLayout': 'paddle::optional<DataLayout>',
|
|
'DataType': 'paddle::optional<DataType>',
|
|
}
|
|
|
|
for item in args_list:
|
|
item = item.strip()
|
|
type_and_name = item.split(' ')
|
|
# match the input tensor
|
|
has_input = False
|
|
for in_type_symbol, in_type in input_types_map.items():
|
|
if type_and_name[0] == in_type_symbol:
|
|
input_name = type_and_name[1].strip()
|
|
assert len(input_name) > 0, (
|
|
f"The input tensor name should not be empty. Please check the args of {api_name} in yaml."
|
|
)
|
|
assert len(attrs['names']) == 0, (
|
|
f"The input Tensor should appear before attributes. please check the position of {api_name}:input({input_name}) in yaml"
|
|
)
|
|
|
|
if input_name in optional_vars:
|
|
in_type = optional_types_trans[in_type_symbol]
|
|
|
|
inputs['names'].append(input_name)
|
|
inputs['input_info'][input_name] = in_type
|
|
has_input = True
|
|
break
|
|
if has_input:
|
|
continue
|
|
|
|
# match the attribute
|
|
for attr_type_symbol, attr_type in attr_types_map.items():
|
|
if type_and_name[0] == attr_type_symbol:
|
|
attr_name = item[len(attr_type_symbol) :].strip()
|
|
assert len(attr_name) > 0, (
|
|
f"The attribute name should not be empty. Please check the args of {api_name} in yaml."
|
|
)
|
|
default_value = None
|
|
if '=' in attr_name:
|
|
attr_infos = attr_name.split('=')
|
|
attr_name = attr_infos[0].strip()
|
|
default_value = attr_infos[1].strip()
|
|
|
|
if attr_name in optional_vars:
|
|
attr_type = optional_types_trans[attr_type_symbol]
|
|
|
|
default_value_str = (
|
|
"" if default_value is None else '=' + default_value
|
|
)
|
|
attrs['names'].append(attr_name)
|
|
attrs['attr_info'][attr_name] = (attr_type, default_value)
|
|
break
|
|
|
|
return inputs, attrs
|
|
|
|
def parse_output(self, api_name, output_config):
|
|
def parse_output_item(output_item):
|
|
output_type_map = {
|
|
'Tensor': 'Tensor',
|
|
'Tensor[]': 'std::vector<Tensor>',
|
|
}
|
|
result = re.search(
|
|
r"(?P<out_type>[a-zA-Z0-9_[\]]+)\s*(?P<name>\([a-zA-Z0-9_@]+\))?\s*(?P<expr>\{[^\}]+\})?",
|
|
output_item,
|
|
)
|
|
assert result is not None, (
|
|
f"{api_name} : the output config parse error."
|
|
)
|
|
out_type = result.group('out_type')
|
|
assert out_type in output_type_map, (
|
|
f"{api_name} : Output type error: the output type only support Tensor and Tensor[], \
|
|
but now is {out_type}."
|
|
)
|
|
|
|
out_name = (
|
|
'out'
|
|
if result.group('name') is None
|
|
else result.group('name')[1:-1]
|
|
)
|
|
out_size_expr = (
|
|
None
|
|
if result.group('expr') is None
|
|
else result.group('expr')[1:-1]
|
|
)
|
|
return output_type_map[out_type], out_name, out_size_expr
|
|
|
|
temp_list = output_config.split(',')
|
|
|
|
if len(temp_list) == 1:
|
|
out_type, out_name, size_expr = parse_output_item(temp_list[0])
|
|
return [out_type], [out_name], [size_expr]
|
|
else:
|
|
out_type_list = []
|
|
out_name_list = []
|
|
out_size_expr_list = []
|
|
for output_item in temp_list:
|
|
out_type, out_name, size_expr = parse_output_item(output_item)
|
|
out_type_list.append(out_type)
|
|
out_name_list.append(out_name)
|
|
out_size_expr_list.append(size_expr)
|
|
|
|
return out_type_list, out_name_list, out_size_expr_list
|
|
|
|
def parse_infer_meta(self, infer_meta_config):
|
|
infer_meta = infer_meta_config
|
|
if 'param' not in infer_meta_config:
|
|
infer_meta['param'] = None
|
|
|
|
return infer_meta
|
|
|
|
def parse_kernel(self, kernel_config):
|
|
# kernel :
|
|
# func : [], Kernel functions (example: scale, scale_sr)
|
|
# param : [], Input params of kernel
|
|
# backend : str, the names of param to choose the kernel backend, default is None
|
|
# layout : str, the names of param to choose the kernel layout, default is None
|
|
# data_type : str, the names of param to choose the kernel data_type, default is None
|
|
# dispatch : {}, the key is kernel_func, the value is type of inputs and outputs for kernel (example: {kernel_name : (['dense','sparse_coo']#input,['sparse_coo']#output)})
|
|
kernel = {
|
|
'func': [],
|
|
'param': None,
|
|
'backend': None,
|
|
'layout': None,
|
|
'data_type': None,
|
|
'dispatch': {},
|
|
}
|
|
if 'backend' in kernel_config and len(kernel_config['backend']) > 0:
|
|
kernel['backend'] = kernel_config['backend']
|
|
if 'layout' in kernel_config and len(kernel_config['layout']) > 0:
|
|
kernel['layout'] = kernel_config['layout']
|
|
if 'data_type' in kernel_config and len(kernel_config['data_type']) > 0:
|
|
kernel['data_type'] = kernel_config['data_type']
|
|
if 'param' in kernel_config:
|
|
kernel['param'] = kernel_config['param']
|
|
kernel_funcs = re.compile(r'([a-zA-Z0-9_]+)\s*({[^}]+})?').findall(
|
|
kernel_config['func']
|
|
)
|
|
|
|
def parse_kernel_in_out_type(in_out_str):
|
|
if len(in_out_str) == 0:
|
|
return None
|
|
tmp_in_out_list = in_out_str[1:-1].split('->')
|
|
inputs = [item.strip() for item in tmp_in_out_list[0].split(',')]
|
|
outputs = [item.strip() for item in tmp_in_out_list[1].split(',')]
|
|
|
|
# check the tensor type
|
|
for item in inputs:
|
|
assert item in [
|
|
'dense',
|
|
'selected_rows',
|
|
'sparse_coo',
|
|
'sparse_csr',
|
|
], (
|
|
f"{self.api} : Invalid input tensor type ('{item}'), here we only support 'dense', 'selected_rows', 'sparse_coo' and 'sparse_csr'."
|
|
)
|
|
for item in outputs:
|
|
assert item in [
|
|
'dense',
|
|
'selected_rows',
|
|
'sparse_coo',
|
|
'sparse_csr',
|
|
], (
|
|
f"{self.api} : Invalid output tensor type ('{item}'), here we only support 'dense', 'selected_rows', 'sparse_coo' and 'sparse_csr'."
|
|
)
|
|
|
|
return (inputs, outputs)
|
|
|
|
for func_item in kernel_funcs:
|
|
kernel['func'].append(func_item[0])
|
|
kernel['dispatch'][func_item[0]] = parse_kernel_in_out_type(
|
|
func_item[1]
|
|
)
|
|
|
|
return kernel
|
|
|
|
def parse_data_transform(self, api_item_yaml):
|
|
data_transform = {'skip_transform': [], 'support_trans_dtype': []}
|
|
if 'data_transform' in api_item_yaml:
|
|
if 'skip_transform' in api_item_yaml['data_transform']:
|
|
data_transform['skip_transform'] = parse_plain_list(
|
|
api_item_yaml['data_transform']['skip_transform']
|
|
)
|
|
if 'support_trans_dtype' in api_item_yaml['data_transform']:
|
|
data_transform['support_trans_dtype'] = parse_plain_list(
|
|
api_item_yaml['data_transform']['support_trans_dtype']
|
|
)
|
|
return data_transform
|
|
|
|
# Override by child class
|
|
def get_return_type(self, inplace_flag=False):
|
|
return None
|
|
|
|
def gene_api_declaration(self, grad_flag=False, append_predefined_out=True):
|
|
api_declaration = ""
|
|
api_func_name = self.get_api_func_name()
|
|
if api_func_name[-1] != '_':
|
|
api_declaration = f"""
|
|
PADDLE_API {self.get_return_type()} {api_func_name}({self.get_declare_args(grad_flag=grad_flag, append_predefined_out=append_predefined_out)});
|
|
"""
|
|
|
|
if self.is_base_api and len(self.inplace_map) > 0:
|
|
if api_func_name[-1] != '_':
|
|
api_func_name += '_'
|
|
api_declaration = (
|
|
api_declaration
|
|
+ f"""
|
|
PADDLE_API {self.get_return_type(inplace_flag=True)} {api_func_name}({self.get_declare_args(inplace_flag=True, grad_flag=grad_flag, append_predefined_out=append_predefined_out)});
|
|
"""
|
|
)
|
|
|
|
return api_declaration
|
|
|
|
# Backward API Override this method
|
|
def gene_kernel_backend_select(self):
|
|
backend_select_code = ""
|
|
if self.kernel['backend'] is not None:
|
|
if '>' in self.kernel['backend']:
|
|
vars_list = self.kernel['backend'].split('>')
|
|
assert len(vars_list) == 2, (
|
|
f"{self.api} api: The number of params to set backend with '>' only allows 2, but received {len(vars_list)}."
|
|
)
|
|
assert (vars_list[0].strip() in self.attrs['names']) and (
|
|
self.attrs['attr_info'][vars_list[0].strip()][0]
|
|
== 'const Place&'
|
|
), (
|
|
f"{self.api} api: When use '>' to set kernel backend, the first param should be an attribute with Place type."
|
|
)
|
|
backend_select_code = f"""
|
|
kernel_backend = ParseBackendWithInputOrder({vars_list[0].strip()}, {vars_list[1].strip()});
|
|
"""
|
|
|
|
else:
|
|
backend_args = [
|
|
ele.strip() for ele in self.kernel['backend'].split(',')
|
|
]
|
|
backend_select_code = f"""
|
|
kernel_backend = ParseBackend({", ".join(backend_args)});
|
|
"""
|
|
|
|
return backend_select_code
|
|
|
|
def gene_kernel_select(self) -> str:
|
|
api = self.api
|
|
input_names = self.inputs['names']
|
|
attrs = self.attrs
|
|
kernel = self.kernel
|
|
|
|
kernel_key_item_init = """
|
|
Backend kernel_backend = Backend::UNDEFINED;
|
|
DataLayout kernel_layout = DataLayout::UNDEFINED;
|
|
DataType kernel_data_type = DataType::UNDEFINED;
|
|
"""
|
|
# Check the tensor options
|
|
attr_backend_count = 0
|
|
attr_layout_count = 0
|
|
attr_data_type_count = 0
|
|
for attr_name in attrs['names']:
|
|
if attrs['attr_info'][attr_name][0] == 'const Place&':
|
|
assert kernel['backend'] is not None, (
|
|
f"{api} api: When there is a parameter with 'Place' type in attributes, you must set backend of kernel manually."
|
|
)
|
|
attr_backend_count = attr_backend_count + 1
|
|
if attrs['attr_info'][attr_name][0] == 'DataLayout':
|
|
assert kernel['layout'] is not None, (
|
|
f"{api} api: When there is a parameter with 'DataLayout' type in attributes, you must set layout of kernel manually."
|
|
)
|
|
attr_layout_count = attr_layout_count + 1
|
|
if attrs['attr_info'][attr_name][0] == 'DataType':
|
|
assert kernel['data_type'] is not None, (
|
|
f"{api} api: When there is a parameter with 'DataType' type in attributes, you must set data_type of kernel manually."
|
|
)
|
|
attr_data_type_count = attr_data_type_count + 1
|
|
|
|
# preprocess kernel configures
|
|
kernel_select_code = self.gene_kernel_backend_select()
|
|
|
|
if kernel['layout'] is not None:
|
|
if '>' in kernel['layout']:
|
|
vars_list = kernel['layout'].split('>')
|
|
assert len(vars_list) == 2, (
|
|
f"{api} api: The number of params to set layout with '>' only allows 2, but received {len(vars_list)}."
|
|
)
|
|
assert (
|
|
vars_list[0].strip() in attrs['names']
|
|
and attrs['attr_info'][vars_list[0].strip()][0]
|
|
== 'DataLayout'
|
|
), (
|
|
f"{api} api: When use '>' to set kernel layout, the first param should be an attribute with DataLayout type."
|
|
)
|
|
kernel_select_code = (
|
|
kernel_select_code
|
|
+ f"""
|
|
kernel_layout = ParseLayoutWithInputOrder({vars_list[0].strip()}, {vars_list[1].strip()});
|
|
"""
|
|
)
|
|
|
|
else:
|
|
vars_list = kernel['layout'].split(',')
|
|
assert len(vars_list) == 1, (
|
|
f"{api} api: The number of params to set layout must be 1, but received {len(vars_list)}."
|
|
)
|
|
kernel_select_code = (
|
|
kernel_select_code
|
|
+ f"""
|
|
kernel_layout = ParseLayout({vars_list[0].strip()});
|
|
"""
|
|
)
|
|
|
|
if kernel['data_type'] is not None:
|
|
|
|
def process_data_type_args(args_item):
|
|
args_item = args_item.strip()
|
|
complex_match_result = re.match(
|
|
r"complex\((?P<param_name>\w+)\)", args_item
|
|
)
|
|
if complex_match_result:
|
|
return f"phi::dtype::ToComplex(ParseDataType({complex_match_result.group('param_name')}))"
|
|
else:
|
|
return f"ParseDataType({args_item})"
|
|
|
|
if '>' in kernel['data_type']:
|
|
vars_list = kernel['data_type'].split('>')
|
|
assert len(vars_list) == 2, (
|
|
f"{api} api: The number of params to set data_type with '>' only allows 2, but received {len(vars_list)}."
|
|
)
|
|
assert (
|
|
vars_list[0].strip() in attrs['names']
|
|
and attrs['attr_info'][vars_list[0].strip()][0]
|
|
== 'DataType'
|
|
), (
|
|
f"{api} api: When use '>' to set kernel data_type, the first param should be an attribute with DataType type."
|
|
)
|
|
kernel_select_code = (
|
|
kernel_select_code
|
|
+ f"""
|
|
kernel_data_type = ParseDataTypeWithInputOrder({vars_list[0].strip()}, {vars_list[1].strip()});
|
|
"""
|
|
)
|
|
|
|
else:
|
|
vars_list = kernel['data_type'].split(',')
|
|
assert len(vars_list) == 1, (
|
|
f"{api} api: The number of params to set data_type only allows 1, but received {len(vars_list)}."
|
|
)
|
|
kernel_select_code = (
|
|
kernel_select_code
|
|
+ f"""
|
|
kernel_data_type = {process_data_type_args(vars_list[0])};
|
|
"""
|
|
)
|
|
|
|
if len(input_names) == 0:
|
|
assert attr_backend_count > 0 and attr_data_type_count > 0, (
|
|
f"{api} api: When there is no input tensor, the args must have 'Place' and 'DataType'."
|
|
)
|
|
|
|
kernel_select_args = ""
|
|
for input_name in input_names:
|
|
kernel_select_args = kernel_select_args + input_name + ", "
|
|
|
|
if len(kernel_select_args) > 2:
|
|
kernel_select_args = kernel_select_args[:-2]
|
|
|
|
kernel_select_code = kernel_key_item_init + kernel_select_code
|
|
|
|
if len(input_names) > 0:
|
|
kernel_select_code = (
|
|
kernel_select_code
|
|
+ f"""
|
|
if (kernel_backend == Backend::UNDEFINED
|
|
|| kernel_layout == DataLayout::UNDEFINED
|
|
|| kernel_data_type == DataType::UNDEFINED ) {{
|
|
auto kernel_key_set = ParseKernelKeyByInputArgs({kernel_select_args});
|
|
auto kernel_key = kernel_key_set.GetHighestPriorityKernelKey();
|
|
if (kernel_backend == Backend::UNDEFINED) {{
|
|
kernel_backend = kernel_key.backend();
|
|
}}
|
|
if (kernel_layout == DataLayout::UNDEFINED) {{
|
|
kernel_layout = kernel_key.layout();
|
|
}}
|
|
if (kernel_data_type == DataType::UNDEFINED) {{
|
|
kernel_data_type = kernel_key.dtype();
|
|
}}
|
|
}}"""
|
|
)
|
|
|
|
return kernel_select_code
|
|
|
|
def gene_infer_meta(self, kernel_output_names, code_indent) -> str:
|
|
input_names = self.inputs['names']
|
|
attr_names = self.attrs['names']
|
|
infer_meta = self.infer_meta
|
|
|
|
infer_meta_params = (
|
|
infer_meta['param']
|
|
if infer_meta['param'] is not None
|
|
else input_names + attr_names
|
|
)
|
|
# generate meta tensors
|
|
meta_tensor_code = ""
|
|
param_code = ""
|
|
for param in infer_meta_params:
|
|
if param in input_names:
|
|
if self.inputs['input_info'][param] == "const Tensor&":
|
|
if self.is_inplace_input(param):
|
|
meta_tensor_code += f"""
|
|
{code_indent} auto {ORIGIN_PREFIX_TENSOR_NAME}{param} = *{PREFIX_TENSOR_NAME}{param};
|
|
"""
|
|
param_code = (
|
|
param_code
|
|
+ "MakeMetaTensor("
|
|
+ ORIGIN_PREFIX_TENSOR_NAME
|
|
+ param
|
|
+ "), "
|
|
)
|
|
else:
|
|
param_code = (
|
|
param_code
|
|
+ "MakeMetaTensor(*"
|
|
+ PREFIX_TENSOR_NAME
|
|
+ param
|
|
+ "), "
|
|
)
|
|
elif (
|
|
self.inputs['input_info'][param]
|
|
== "const std::vector<Tensor>&"
|
|
):
|
|
meta_tensor_code = (
|
|
meta_tensor_code
|
|
+ f"""
|
|
{code_indent} auto {param}_meta_vec = MakeMetaTensor({PREFIX_TENSOR_NAME}{param});
|
|
{code_indent} std::vector<const phi::MetaTensor*> {param}_metas({param}_meta_vec.size());
|
|
{code_indent} for (size_t i = 0; i < {param}_meta_vec.size(); ++i) {{
|
|
{code_indent} {param}_metas[i] = &{param}_meta_vec[i];
|
|
{code_indent} }}
|
|
"""
|
|
)
|
|
param_code = param_code + param + "_metas, "
|
|
elif (
|
|
self.inputs['input_info'][param]
|
|
== "const paddle::optional<std::vector<Tensor>>&"
|
|
):
|
|
meta_tensor_code = (
|
|
meta_tensor_code
|
|
+ f"""
|
|
{code_indent} auto {param}_meta_vec = MakeMetaTensor({PREFIX_TENSOR_NAME}{param});
|
|
{code_indent} paddle::optional<std::vector<const phi::MetaTensor*>> {param}_metas({param}_meta_vec.size());
|
|
{code_indent} for (size_t i = 0; i < {param}_meta_vec.size(); ++i) {{
|
|
{code_indent} {param}_metas->at(i) = &{param}_meta_vec[i];
|
|
{code_indent} }}
|
|
"""
|
|
)
|
|
param_code = param_code + param + "_metas, "
|
|
elif param in self.optional_vars:
|
|
param_code = (
|
|
param_code
|
|
+ "MakeMetaTensor("
|
|
+ PREFIX_TENSOR_NAME
|
|
+ param
|
|
+ "), "
|
|
)
|
|
else:
|
|
raise ValueError(
|
|
f"{self.api} : Param of infer_meta error : {self.inputs['input_info'][param]} type is not supported."
|
|
)
|
|
elif param in attr_names:
|
|
param_code = param_code + param + ", "
|
|
elif isinstance(param, str):
|
|
param_code = f'{param_code}"{param}", '
|
|
elif isinstance(param, bool):
|
|
param_code = param_code + str(param).lower() + ", "
|
|
else:
|
|
param_code = param_code + str(param) + ", "
|
|
|
|
# --- New logic for collecting all output MetaTensors for 'compact' ---
|
|
|
|
# C++ variable to hold the list of all output MetaTensors for compact()
|
|
compact_meta_tensor_list = f"{code_indent} std::vector<phi::MetaTensor*> output_metas_for_compact;"
|
|
|
|
for i, out_name in enumerate(kernel_output_names):
|
|
if self.outputs['types'][i] == 'std::vector<Tensor>':
|
|
# Case 1: Output is std::vector<Tensor>
|
|
meta_tensor_code = (
|
|
meta_tensor_code
|
|
+ f"""
|
|
{code_indent} auto {out_name}_{PREFIX_META_TENSOR_NAME}vec = MakeMetaTensor({out_name});
|
|
{code_indent} std::vector<phi::MetaTensor*> {out_name}_metas({out_name}_{PREFIX_META_TENSOR_NAME}vec.size());
|
|
{code_indent} for (size_t i = 0; i < {out_name}_{PREFIX_META_TENSOR_NAME}vec.size(); ++i) {{
|
|
{code_indent} {out_name}_metas[i] = {out_name}[i] ? &{out_name}_{PREFIX_META_TENSOR_NAME}vec[i] : nullptr;
|
|
{code_indent} }}"""
|
|
)
|
|
# Add all elements of the vector output to the compact list
|
|
compact_meta_tensor_list += f"""
|
|
{code_indent} output_metas_for_compact.insert(output_metas_for_compact.end(), {out_name}_metas.begin(), {out_name}_metas.end());"""
|
|
|
|
param_code = param_code + out_name + '_metas, '
|
|
else:
|
|
# Case 2: Output is a single Tensor
|
|
out_meta_var_name = out_name.replace(
|
|
'kernel_', PREFIX_META_TENSOR_NAME
|
|
)
|
|
meta_tensor_code = (
|
|
meta_tensor_code
|
|
+ code_indent
|
|
+ " phi::MetaTensor "
|
|
+ out_meta_var_name
|
|
+ "("
|
|
+ out_name
|
|
+ ", kernel_result.is_stride_kernel"
|
|
+ ");\n"
|
|
)
|
|
|
|
# Add the single output pointer to the compact list if it's not nullptr
|
|
compact_meta_tensor_list += f"""
|
|
{code_indent} if ({out_name}) {{ output_metas_for_compact.push_back(&{out_meta_var_name}); }}"""
|
|
|
|
if len(kernel_output_names) == 1:
|
|
param_code = param_code + f"&{out_meta_var_name}, "
|
|
else:
|
|
param_code = (
|
|
param_code
|
|
+ f"{out_name} ? &{out_meta_var_name} : nullptr, "
|
|
)
|
|
|
|
param_code = param_code[:-2]
|
|
|
|
return f"""{meta_tensor_code}
|
|
{compact_meta_tensor_list}
|
|
{code_indent} phi::{infer_meta['func']}({param_code});
|
|
{code_indent} CheckAndDoCompact(output_metas_for_compact, "{self.api}");
|
|
"""
|
|
|
|
def gene_trans_flag(self, input_name):
|
|
trans_flag = "{}"
|
|
if input_name in self.data_transform['skip_transform']:
|
|
trans_flag = "{true}"
|
|
elif input_name in self.data_transform['support_trans_dtype']:
|
|
trans_flag = "{false, true}"
|
|
return trans_flag
|
|
|
|
def gene_dense_input(
|
|
self, input_name, input_name_tensor_map, code_indent=''
|
|
):
|
|
input_tensor_code = ""
|
|
trans_flag = self.gene_trans_flag(input_name)
|
|
input_names = self.inputs['names']
|
|
attr_names = self.attrs['names']
|
|
kernel_param = self.kernel['param']
|
|
if kernel_param is None:
|
|
kernel_param = input_names + attr_names
|
|
|
|
input_name_tensor_map[input_name].append(
|
|
(f"{PREFIX_TENSOR_NAME}{input_name}", False)
|
|
)
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ f"""
|
|
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name} = PrepareData({input_name}, GetKernelInputArgDef(kernel.InputAt({kernel_param.index(input_name)}), actual_kernel_backend), {trans_flag}, kernel_result.is_stride_kernel);"""
|
|
)
|
|
return input_tensor_code
|
|
|
|
def gene_selected_rows_input(
|
|
self, input_name, input_name_tensor_map, code_indent=''
|
|
):
|
|
input_tensor_code = ""
|
|
trans_flag = self.gene_trans_flag(input_name)
|
|
input_names = self.inputs['names']
|
|
attr_names = self.attrs['names']
|
|
kernel_param = self.kernel['param']
|
|
if kernel_param is None:
|
|
kernel_param = input_names + attr_names
|
|
|
|
input_name_tensor_map[input_name].append(
|
|
(f"{PREFIX_TENSOR_NAME}{input_name}", False)
|
|
)
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ f"""
|
|
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name} = PrepareDataForSelectedRows({input_name}, GetKernelInputArgDef(kernel.InputAt({kernel_param.index(input_name)}), actual_kernel_backend), {trans_flag});
|
|
"""
|
|
)
|
|
return input_tensor_code
|
|
|
|
def gene_optional_vec_dense_input(
|
|
self, input_name, input_name_tensor_map, code_indent=''
|
|
):
|
|
input_tensor_code = ""
|
|
trans_flag = self.gene_trans_flag(input_name)
|
|
input_names = self.inputs['names']
|
|
attr_names = self.attrs['names']
|
|
kernel_param = self.kernel['param']
|
|
if kernel_param is None:
|
|
kernel_param = input_names + attr_names
|
|
if input_name in self.inplace_map.values():
|
|
input_name_tensor_map[input_name].append(
|
|
(f"{PREFIX_TENSOR_NAME}{input_name}", True)
|
|
)
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ f"""
|
|
{code_indent} // inplace vector of tensors should also be transferred to CPU when kernel has fallen back
|
|
{code_indent} paddle::optional<std::vector<const phi::DenseTensor*>> {PREFIX_TENSOR_NAME}{input_name};
|
|
{code_indent} paddle::optional<std::vector<phi::DenseTensor>> {PREFIX_TENSOR_NAME}{input_name}_vec;
|
|
{code_indent} if (kernel_result.has_fallback_cpu) {{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name}_vec = PrepareData({input_name}, GetKernelInputArgDef(kernel.InputAt({kernel_param.index(input_name)}), actual_kernel_backend), {trans_flag}, kernel_result.is_stride_kernel);
|
|
{code_indent} if ({PREFIX_TENSOR_NAME}{input_name}_vec){{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name} = paddle::optional<std::vector<const phi::DenseTensor*>>({PREFIX_TENSOR_NAME}{input_name}_vec->size());
|
|
{code_indent} for (size_t i = 0; i < {PREFIX_TENSOR_NAME}{input_name}_vec->size(); ++i) {{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name}->at(i) = &{PREFIX_TENSOR_NAME}{input_name}_vec->at(i);
|
|
{code_indent} }}
|
|
{code_indent} }}
|
|
{code_indent} }}
|
|
{code_indent} else {{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name} = TensorToConstDenseTensorPtr({input_name});
|
|
{code_indent} }}"""
|
|
)
|
|
else:
|
|
input_name_tensor_map[input_name].append(
|
|
(f"{PREFIX_TENSOR_NAME}{input_name}_vec", True)
|
|
)
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ f"""
|
|
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name}_vec = PrepareData({input_name}, GetKernelInputArgDef(kernel.InputAt({kernel_param.index(input_name)}), actual_kernel_backend), {trans_flag}, kernel_result.is_stride_kernel);
|
|
{code_indent} paddle::optional<std::vector<const phi::DenseTensor*>> {PREFIX_TENSOR_NAME}{input_name};
|
|
{code_indent} if ({PREFIX_TENSOR_NAME}{input_name}_vec){{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name} = paddle::optional<std::vector<const phi::DenseTensor*>>({PREFIX_TENSOR_NAME}{input_name}_vec->size());
|
|
{code_indent} for (size_t i = 0; i < {PREFIX_TENSOR_NAME}{input_name}_vec->size(); ++i) {{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name}->at(i) = &{PREFIX_TENSOR_NAME}{input_name}_vec->at(i);
|
|
{code_indent} }}
|
|
{code_indent} }}"""
|
|
)
|
|
return input_tensor_code
|
|
|
|
def gene_vec_dense_input(
|
|
self, input_name, input_name_tensor_map, code_indent=''
|
|
):
|
|
input_tensor_code = ""
|
|
trans_flag = self.gene_trans_flag(input_name)
|
|
input_names = self.inputs['names']
|
|
attr_names = self.attrs['names']
|
|
kernel_param = self.kernel['param']
|
|
if kernel_param is None:
|
|
kernel_param = input_names + attr_names
|
|
|
|
if input_name in self.inplace_map.values():
|
|
input_name_tensor_map[input_name].append(
|
|
(f"{PREFIX_TENSOR_NAME}{input_name}", True)
|
|
)
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ f"""
|
|
{code_indent} // inplace vector of tensors should also be transferred to CPU when kernel has fallen back
|
|
{code_indent} std::vector<const phi::DenseTensor*> {PREFIX_TENSOR_NAME}{input_name};
|
|
{code_indent} std::unique_ptr<std::vector<phi::DenseTensor>> {PREFIX_TENSOR_NAME}{input_name}_vec;
|
|
{code_indent} if (kernel_result.has_fallback_cpu) {{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name}_vec = PrepareData({input_name}, GetKernelInputArgDef(kernel.InputAt({kernel_param.index(input_name)}), actual_kernel_backend), {trans_flag}, kernel_result.is_stride_kernel);
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name}.resize({PREFIX_TENSOR_NAME}{input_name}_vec->size());
|
|
{code_indent} for (size_t i = 0; i < {PREFIX_TENSOR_NAME}{input_name}.size(); ++i) {{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name}[i] = &{PREFIX_TENSOR_NAME}{input_name}_vec->at(i);
|
|
{code_indent} }}
|
|
{code_indent} }}
|
|
{code_indent} else {{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name} = TensorToConstDenseTensorPtr({input_name});
|
|
{code_indent} }}"""
|
|
)
|
|
else:
|
|
input_name_tensor_map[input_name].append(
|
|
(f"{PREFIX_TENSOR_NAME}{input_name}_vec", True)
|
|
)
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ f"""
|
|
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name}_vec = PrepareData({input_name}, GetKernelInputArgDef(kernel.InputAt({kernel_param.index(input_name)}), actual_kernel_backend), {trans_flag}, kernel_result.is_stride_kernel);
|
|
{code_indent} std::vector<const phi::DenseTensor*> {PREFIX_TENSOR_NAME}{input_name}({PREFIX_TENSOR_NAME}{input_name}_vec->size());
|
|
{code_indent} for (size_t i = 0; i < {PREFIX_TENSOR_NAME}{input_name}.size(); ++i) {{
|
|
{code_indent} {PREFIX_TENSOR_NAME}{input_name}[i] = &{PREFIX_TENSOR_NAME}{input_name}_vec->at(i);
|
|
{code_indent} }}"""
|
|
)
|
|
return input_tensor_code
|
|
|
|
def gene_input(self, kernel_tensor_type=None, code_indent=''):
|
|
input_names = self.inputs['names']
|
|
attr_names = self.attrs['names']
|
|
kernel_param = self.kernel['param']
|
|
if kernel_param is None:
|
|
kernel_param = input_names + attr_names
|
|
input_name_tensor_map = collections.defaultdict(list)
|
|
input_tensor_code = ""
|
|
for i, input_name in enumerate(input_names):
|
|
# set input code
|
|
if input_name in kernel_param:
|
|
# input is dense tensor
|
|
api_tensor_type = self.inputs['input_info'][input_name]
|
|
phi_tensor_type = (
|
|
'dense'
|
|
if kernel_tensor_type is None
|
|
else kernel_tensor_type[0][kernel_param.index(input_name)]
|
|
)
|
|
if api_tensor_type in self.gene_input_func.keys():
|
|
input_tensor_code += self.gene_input_func[api_tensor_type][
|
|
phi_tensor_type
|
|
](input_name, input_name_tensor_map, code_indent)
|
|
else:
|
|
# do nothing
|
|
pass
|
|
else:
|
|
if input_name in self.infer_meta['param']:
|
|
if input_name in self.optional_vars:
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ f"""
|
|
{code_indent} paddle::optional<phi::TensorBase> {PREFIX_TENSOR_NAME}{input_name} = {input_name} ? paddle::optional<phi::TensorBase>(*{input_name}->impl()) : paddle::none;"""
|
|
)
|
|
|
|
else:
|
|
if (
|
|
self.inputs['input_info'][input_name]
|
|
== "const std::vector<Tensor>&"
|
|
):
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ f"""
|
|
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name}_uq_ptr = TensorToDenseTensor({input_name});
|
|
{code_indent} const auto& {PREFIX_TENSOR_NAME}{input_name} = *{PREFIX_TENSOR_NAME}{input_name}_uq_ptr;"""
|
|
)
|
|
else:
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ f"""
|
|
{code_indent} auto {PREFIX_TENSOR_NAME}{input_name} = {input_name}.impl();"""
|
|
)
|
|
|
|
return input_name_tensor_map, input_tensor_code
|
|
|
|
def generate_record_op_info_supplement(
|
|
self, input_name_tensor_map, code_indent='', in_auto_parallel=False
|
|
):
|
|
record_op_info_supplement_str = f"""
|
|
{code_indent} if(phi::RecordOpInfoSupplement::IsEnabled()){{"""
|
|
|
|
single_tensor_names = []
|
|
list_tensor_names = []
|
|
for input_name, input_tensors in input_name_tensor_map.items():
|
|
has_vector_tensor = False
|
|
for input_tensor, is_vector in input_tensors:
|
|
if is_vector is True:
|
|
has_vector_tensor = True
|
|
if has_vector_tensor is False:
|
|
single_tensor_names.append(input_name)
|
|
else:
|
|
list_tensor_names.append(input_name)
|
|
if not single_tensor_names:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} std::vector<std::pair<const char*, std::vector<phi::DDim>>> input_shapes;"""
|
|
)
|
|
else:
|
|
for input_name in single_tensor_names:
|
|
if input_name in self.optional_vars:
|
|
input_tensors = input_name_tensor_map[input_name]
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} std::vector<phi::DDim> {input_name}_record_shapes;"""
|
|
)
|
|
for input_tensor, _ in input_tensors:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} if({input_tensor}){{
|
|
{code_indent} {input_name}_record_shapes.push_back((*{input_tensor}).dims());
|
|
{code_indent} }}"""
|
|
)
|
|
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} std::vector<std::pair<const char*, std::vector<phi::DDim>>> input_shapes{{"""
|
|
)
|
|
for input_name in single_tensor_names[:-1]:
|
|
if input_name in self.optional_vars:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} {{"{input_name}", {input_name}_record_shapes}},"""
|
|
)
|
|
else:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} {{"{input_name}", {{"""
|
|
)
|
|
input_tensors = input_name_tensor_map[input_name]
|
|
for input_tensor, _ in input_tensors[:-1]:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} (*{input_tensor}).dims(),"""
|
|
)
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} (*{input_tensors[-1][0]}).dims()}}}},"""
|
|
)
|
|
if single_tensor_names[-1] in self.optional_vars:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} {{"{single_tensor_names[-1]}",
|
|
{code_indent} {single_tensor_names[-1]}_record_shapes}}}};"""
|
|
)
|
|
else:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} {{"{single_tensor_names[-1]}", {{"""
|
|
)
|
|
input_tensors = input_name_tensor_map[single_tensor_names[-1]]
|
|
for input_tensor, _ in input_tensors[:-1]:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} (*{input_tensor}).dims(),"""
|
|
)
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} (*{input_tensors[-1][0]}).dims()}}}}}};"""
|
|
)
|
|
if list_tensor_names:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} std::vector<phi::DDim> ddims_vec;"""
|
|
)
|
|
for input_name in list_tensor_names:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} ddims_vec.clear();"""
|
|
)
|
|
for input_tensor, is_vector in input_name_tensor_map[input_name]:
|
|
if is_vector:
|
|
input_tensor_truncate = input_tensor[:-4]
|
|
if (
|
|
input_name in self.inplace_map.values()
|
|
or in_auto_parallel
|
|
):
|
|
input_tensor_truncate = input_tensor
|
|
|
|
if input_name in self.optional_vars:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} if ({input_tensor_truncate}){{
|
|
{code_indent} ddims_vec.reserve({input_tensor_truncate}->size());
|
|
{code_indent} for (size_t i = 0; i < {input_tensor_truncate}->size(); ++i) {{
|
|
{code_indent} ddims_vec.emplace_back((*{input_tensor_truncate}->at(i)).dims());
|
|
{code_indent} }}
|
|
{code_indent} }}"""
|
|
)
|
|
else:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} ddims_vec.reserve({input_tensor_truncate}.size());
|
|
{code_indent} for (size_t i = 0; i < {input_tensor_truncate}.size(); ++i) {{
|
|
{code_indent} ddims_vec.emplace_back((*{input_tensor_truncate}[i]).dims());
|
|
{code_indent} }}"""
|
|
)
|
|
else:
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
ddims_vec.emplace_back((*{input_tensor}).dims());
|
|
{code_indent} """
|
|
)
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} input_shapes.emplace_back("{input_name}", ddims_vec);"""
|
|
)
|
|
|
|
record_op_info_supplement_str += f"""
|
|
{code_indent} phi::AttributeMap attrs;"""
|
|
|
|
for attr_name in self.attrs['names']:
|
|
if 'IntArray' in self.attrs['attr_info'][attr_name][0]:
|
|
record_op_info_supplement_str += f"""
|
|
{code_indent} attrs["{attr_name}"] = {attr_name}.GetData();"""
|
|
elif 'vector<phi::Scalar>' in self.attrs['attr_info'][attr_name][0]:
|
|
record_op_info_supplement_str += f"""
|
|
{code_indent} attrs["{attr_name}"] = "";""" # TODO(kuizhiqing)
|
|
elif 'Scalar' in self.attrs['attr_info'][attr_name][0]:
|
|
record_op_info_supplement_str += f"""
|
|
{code_indent} switch ({attr_name}.dtype()) {{
|
|
{code_indent} case DataType::FLOAT32:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<float>({attr_name}.to<float>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::FLOAT64:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<double>({attr_name}.to<double>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::FLOAT16:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<float>({attr_name}.to<float16>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::BFLOAT16:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<float>({attr_name}.to<bfloat16>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::INT32:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<int32_t>({attr_name}.to<int32_t>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::INT64:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<int64_t>({attr_name}.to<int64_t>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::INT16:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<int16_t>({attr_name}.to<int16_t>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::INT8:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<int8_t>({attr_name}.to<int8_t>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::UINT16:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<uint16_t>({attr_name}.to<uint16_t>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::UINT8:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<uint8_t>({attr_name}.to<uint8_t>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::BOOL:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<bool>({attr_name}.to<bool>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::COMPLEX64:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<float>({attr_name}.to<complex64>());
|
|
{code_indent} break;
|
|
{code_indent} case DataType::COMPLEX128:
|
|
{code_indent} attrs["{attr_name}"] = static_cast<double>({attr_name}.to<complex128>());
|
|
{code_indent} break;
|
|
{code_indent} default:
|
|
{code_indent} attrs["{attr_name}"] = "";
|
|
{code_indent} break;
|
|
{code_indent} }}"""
|
|
elif 'DataType' in self.attrs['attr_info'][attr_name][0]:
|
|
pass # no need
|
|
elif 'Place' in self.attrs['attr_info'][attr_name][0]:
|
|
pass # no need
|
|
else:
|
|
record_op_info_supplement_str += f"""
|
|
{code_indent} attrs["{attr_name}"] = {attr_name};"""
|
|
|
|
record_op_info_supplement_str = (
|
|
record_op_info_supplement_str
|
|
+ f"""
|
|
{code_indent} phi::RecordOpInfoSupplement("{self.api}", input_shapes, attrs);
|
|
{code_indent} }}"""
|
|
)
|
|
return record_op_info_supplement_str
|
|
|
|
def get_kernel_args(self, kernel_tensor_type=None, code_indent=''):
|
|
dense_input_trans_map = {
|
|
'const Tensor&': 'const phi::DenseTensor&',
|
|
'const std::vector<Tensor>&': 'const std::vector<const phi::DenseTensor*>&',
|
|
'const paddle::optional<Tensor&>': 'paddle::optional<const phi::DenseTensor&>',
|
|
'const paddle::optional<Tensor>&': 'const paddle::optional<phi::DenseTensor>&',
|
|
'const paddle::optional<std::vector<Tensor>>&': 'const paddle::optional<std::vector<const phi::DenseTensor*>>&',
|
|
}
|
|
dense_out_trans_map = {
|
|
'Tensor': 'phi::DenseTensor*',
|
|
'std::vector<Tensor>': 'std::vector<phi::DenseTensor*>',
|
|
}
|
|
sr_input_trans_map = {
|
|
'const Tensor&': 'const phi::SelectedRows&',
|
|
'const paddle::optional<Tensor>&': 'const paddle::optional<phi::SelectedRows>&',
|
|
}
|
|
sr_out_trans_map = {'Tensor': 'phi::SelectedRows*'}
|
|
input_names = self.inputs['names']
|
|
input_infos = self.inputs['input_info']
|
|
kernel_args_type_list = ['const phi::DeviceContext&']
|
|
|
|
attr_names = self.attrs['names']
|
|
kernel_param = self.kernel['param']
|
|
if kernel_param is None:
|
|
kernel_param = input_names + attr_names
|
|
|
|
input_name_tensor_map, input_tensor_code = self.gene_input(
|
|
kernel_tensor_type, code_indent
|
|
)
|
|
|
|
input_tensor_code = (
|
|
input_tensor_code
|
|
+ self.generate_record_op_info_supplement(
|
|
input_name_tensor_map, code_indent
|
|
)
|
|
)
|
|
|
|
infer_meta_params = (
|
|
self.infer_meta['param']
|
|
if self.infer_meta['param'] is not None
|
|
else self.inputs['names'] + self.attrs['names']
|
|
)
|
|
|
|
kernel_args = ["*dev_ctx"]
|
|
for param in kernel_param:
|
|
if param in input_names:
|
|
if param in self.optional_vars:
|
|
kernel_args.append(PREFIX_TENSOR_NAME + param)
|
|
else:
|
|
if self.inputs['input_info'][param] == "const Tensor&":
|
|
if self.is_inplace_input(param):
|
|
if param not in infer_meta_params:
|
|
input_tensor_code += f"""
|
|
{code_indent} auto {ORIGIN_PREFIX_TENSOR_NAME}{param} = *{PREFIX_TENSOR_NAME}{param};
|
|
"""
|
|
kernel_args.append(
|
|
ORIGIN_PREFIX_TENSOR_NAME + param
|
|
)
|
|
else:
|
|
kernel_args.append("*" + PREFIX_TENSOR_NAME + param)
|
|
elif (
|
|
self.inputs['input_info'][param]
|
|
== "const std::vector<Tensor>&"
|
|
):
|
|
kernel_args.append(PREFIX_TENSOR_NAME + param)
|
|
else:
|
|
# do nothing
|
|
pass
|
|
# input is dense tensor
|
|
if (
|
|
kernel_tensor_type is None
|
|
or kernel_tensor_type[0][kernel_param.index(param)]
|
|
== 'dense'
|
|
):
|
|
kernel_args_type_list.append(
|
|
dense_input_trans_map[input_infos[param]]
|
|
)
|
|
else: # input is selected_rows
|
|
kernel_args_type_list.append(
|
|
sr_input_trans_map[input_infos[param]]
|
|
)
|
|
elif param in attr_names:
|
|
# set attr for kernel_context
|
|
if 'IntArray' in self.attrs['attr_info'][param][0]:
|
|
kernel_args_type_list.append('const phi::IntArray&')
|
|
param = 'phi::IntArray(' + param + ')'
|
|
elif 'vector<phi::Scalar>' in self.attrs['attr_info'][param][0]:
|
|
kernel_args_type_list.append(
|
|
'const std::vector<phi::Scalar>&'
|
|
)
|
|
param = param
|
|
elif 'Scalar' in self.attrs['attr_info'][param][0]:
|
|
kernel_args_type_list.append('const phi::Scalar&')
|
|
param = 'phi::Scalar(' + param + ')'
|
|
else:
|
|
kernel_args_type_list.append(
|
|
self.attrs['attr_info'][param][0]
|
|
)
|
|
kernel_args.append(param)
|
|
elif isinstance(param, bool):
|
|
kernel_args.append(str(param).lower())
|
|
else:
|
|
kernel_args.append(str(param))
|
|
|
|
for i, out_type in enumerate(self.outputs['types']):
|
|
# output is dense tensor
|
|
if (
|
|
kernel_tensor_type is None
|
|
or kernel_tensor_type[1][i] == 'dense'
|
|
):
|
|
kernel_args_type_list.append(dense_out_trans_map[out_type])
|
|
else: # output is selected_rows
|
|
kernel_args_type_list.append(sr_out_trans_map[out_type])
|
|
|
|
kernel_signature = "void(*)(" + ", ".join(kernel_args_type_list) + ")"
|
|
|
|
return input_tensor_code, ", ".join(kernel_args), kernel_signature
|
|
|
|
# Override by child class
|
|
def gene_return_code(self):
|
|
return "return api_output;"
|
|
|
|
# Override by child class
|
|
def gene_output(
|
|
self,
|
|
out_dtype_list,
|
|
out_tensor_type_list=None,
|
|
code_indent='',
|
|
inplace_flag=False,
|
|
):
|
|
return None, None, None
|
|
|
|
def reset_view_after_fallback(
|
|
self, out_dtype_list, code_indent='', inplace_flag=False
|
|
):
|
|
return ''
|
|
|
|
def gen_kernel_code(self, kernel_name, code_indent, inplace_flag=False):
|
|
kernel_dispatch = self.kernel['dispatch'][kernel_name]
|
|
input_tensors, kernel_args, kernel_signature = self.get_kernel_args(
|
|
kernel_dispatch, code_indent
|
|
)
|
|
out_tensor_type_list = kernel_dispatch[1] if kernel_dispatch else None
|
|
outputs_args, kernel_output_names, output_create = self.gene_output(
|
|
self.outputs['types'],
|
|
out_tensor_type_list,
|
|
code_indent,
|
|
inplace_flag,
|
|
)
|
|
pre_save_stride = ""
|
|
transdata2strided = ""
|
|
if inplace_flag and kernel_name not in [
|
|
"squeeze",
|
|
"unsqueeze",
|
|
"reshape",
|
|
"flatten",
|
|
"transpose",
|
|
]:
|
|
i = 0
|
|
for kernel_out in outputs_args:
|
|
pre_save_stride += f"""{code_indent} auto backup{i} = ProcessStrideBackup(&{kernel_out});\n"""
|
|
transdata2strided += f"""{code_indent} TransStride(dev_ctx, {kernel_out}, backup{i});\n"""
|
|
i = i + 1
|
|
fallback_kernel_output_trans = ""
|
|
for idx, kernel_out in enumerate(outputs_args):
|
|
fallback_kernel_output_trans += f"""
|
|
{code_indent} TransDataBackend({kernel_out}, kernel_backend, {kernel_out});"""
|
|
if (
|
|
self.outputs['types'][idx] == 'std::vector<Tensor>'
|
|
and self.outputs['names'][idx] in self.inplace_map
|
|
):
|
|
target_input = self.inplace_map[self.outputs['names'][idx]]
|
|
if (
|
|
self.inplace_map[self.outputs['names'][idx]]
|
|
in self.optional_vars
|
|
):
|
|
fallback_kernel_output_trans += f"""
|
|
{code_indent} if ({target_input}) {{
|
|
{code_indent} for (size_t i = 0; i < {target_input}->size(); ++i) {{
|
|
{code_indent} auto target_ptr = static_cast<phi::DenseTensor*>({target_input}->at(i).impl().get());
|
|
{code_indent} *target_ptr = *{kernel_out}.at(i);
|
|
{code_indent} }}
|
|
{code_indent} }}"""
|
|
else:
|
|
fallback_kernel_output_trans += f"""
|
|
{code_indent} for (size_t i = 0; i < {target_input}.size(); ++i) {{
|
|
{code_indent} auto target_ptr = static_cast<phi::DenseTensor*>({target_input}.at(i).impl().get());
|
|
{code_indent} *target_ptr = *{kernel_out}.at(i);
|
|
{code_indent} }}"""
|
|
return f"""
|
|
{code_indent} VLOG(4) << "{self.api} API kernel key: [" << kernel_backend << ", " << kernel_layout << ", "<< kernel_data_type << "]";
|
|
{code_indent} auto kernel_result = phi::KernelFactory::Instance().SelectKernelOrThrowError(
|
|
{code_indent} "{kernel_name}", {{kernel_backend, kernel_layout, kernel_data_type}}, true);
|
|
{code_indent} const auto& kernel = kernel_result.kernel;
|
|
{code_indent} if (FLAGS_low_precision_op_list) {{
|
|
{code_indent} phi::KernelFactory::Instance().AddToLowPrecisionKernelList("{self.api}", kernel_data_type);
|
|
{code_indent} }}
|
|
{code_indent} VLOG(4) << "{kernel_name} kernel: " << kernel;
|
|
{code_indent} // add actual_kernel_backend to select actual kernel backend after a potential falling-back to CPU
|
|
{code_indent} Backend actual_kernel_backend = kernel_result.has_fallback_cpu ? Backend::CPU : kernel_backend;
|
|
{code_indent} auto* dev_ctx = GetDeviceContextByBackend(actual_kernel_backend);
|
|
{input_tensors}
|
|
{output_create}
|
|
{pre_save_stride}
|
|
{code_indent} phi::RecordEvent *infer_shape_record_event = nullptr;
|
|
{code_indent} if(phi::RecordEvent::IsEnabled()){{
|
|
{code_indent} infer_shape_record_event = new phi::RecordEvent(\"{self.api} infer_meta\", phi::TracerEventType::OperatorInner, 1);
|
|
{code_indent} }}
|
|
{self.gene_infer_meta(kernel_output_names, code_indent)}
|
|
{code_indent} if(infer_shape_record_event != nullptr){{
|
|
{code_indent} delete infer_shape_record_event;
|
|
{code_indent} }}
|
|
{code_indent} using kernel_signature = {kernel_signature};
|
|
{code_indent} auto* kernel_fn = kernel.GetVariadicKernelFn<kernel_signature>();
|
|
{code_indent} phi::RecordEvent* kernel_record_event = nullptr;
|
|
{code_indent} if(phi::RecordEvent::IsEnabled()){{
|
|
{code_indent} kernel_record_event = new phi::RecordEvent(\"{kernel_name} kernel launch\", phi::TracerEventType::DygraphKernelLaunch, 1);
|
|
{code_indent} }}
|
|
{code_indent} (*kernel_fn)({kernel_args}, {", ".join(outputs_args)});
|
|
{code_indent} if (FLAGS_benchmark) {{
|
|
{code_indent} dev_ctx->Wait();
|
|
{code_indent} std::cout << \"{kernel_name} kernel run finish.\" << std::endl;
|
|
{code_indent} }}
|
|
{code_indent} if(kernel_record_event != nullptr){{
|
|
{code_indent} delete kernel_record_event;
|
|
{code_indent} }}
|
|
{code_indent} if (kernel_result.has_fallback_cpu) {{
|
|
{fallback_kernel_output_trans}
|
|
{self.reset_view_after_fallback(self.outputs['types'], code_indent, inplace_flag)}
|
|
{code_indent} }}
|
|
{code_indent}{' dev_ctx = GetDeviceContextByBackend(kernel_backend);' if transdata2strided != '' else ''}
|
|
{transdata2strided}
|
|
{code_indent} {self.gene_return_code()}"""
|
|
|
|
def get_condition_code(self, kernel_name):
|
|
assert self.kernel['dispatch'][kernel_name], (
|
|
f"{self.api} api: the tensor type of inputs and outputs for kernel isn't set, see also 'kernel:func' of 'scale' in ops.yaml."
|
|
)
|
|
input_types = self.kernel['dispatch'][kernel_name][0]
|
|
condition_list = []
|
|
for i, in_type in enumerate(input_types):
|
|
if in_type == "dense":
|
|
if self.inputs['names'][i] in self.optional_vars:
|
|
condition_list.append(
|
|
f"(!{self.inputs['names'][i]} || {self.inputs['names'][i]}->is_dense_tensor())"
|
|
)
|
|
else:
|
|
condition_list.append(
|
|
f"{self.inputs['names'][i]}.is_dense_tensor()"
|
|
)
|
|
else:
|
|
if self.inputs['names'][i] in self.optional_vars:
|
|
condition_list.append(
|
|
f"(!{self.inputs['names'][i]} || {self.inputs['names'][i]}->is_selected_rows())"
|
|
)
|
|
else:
|
|
condition_list.append(
|
|
f"{self.inputs['names'][i]}.is_selected_rows()"
|
|
)
|
|
return " && ".join(condition_list)
|
|
|
|
def gene_dispatch_code(self, kernel_name, inplace_flag=False):
|
|
return f"""
|
|
if ({self.get_condition_code(kernel_name)}) {{
|
|
{self.gen_kernel_code(kernel_name, ' ', inplace_flag)}
|
|
}}
|
|
"""
|
|
|
|
def gene_base_api_code_for_inplace(self):
|
|
self.is_inplace_context = True
|
|
code = self.gene_base_api_code(inplace_flag=True)
|
|
self.is_inplace_context = False
|
|
return code
|
|
|
|
def gene_base_api_code(self, inplace_flag=False):
|
|
api_func_name = self.get_api_func_name()
|
|
if inplace_flag and api_func_name[-1] != '_':
|
|
api_func_name += '_'
|
|
api_code = f"""
|
|
PADDLE_API {self.get_return_type(inplace_flag)} {api_func_name}({self.get_define_args(inplace_flag)}) {{
|
|
{self.gene_kernel_select()}
|
|
"""
|
|
|
|
if len(self.kernel['func']) > 1:
|
|
kernel_dispatch_code = ''
|
|
for kernel_name in self.kernel['func']:
|
|
kernel_dispatch_code += self.gene_dispatch_code(
|
|
kernel_name, inplace_flag
|
|
)
|
|
return (
|
|
api_code
|
|
+ f"""
|
|
{kernel_dispatch_code}
|
|
PADDLE_THROW(common::errors::Unimplemented(
|
|
"The kernel of ({self.api}) for input tensors is unimplemented, please check the type of input tensors."));
|
|
}}
|
|
"""
|
|
)
|
|
else:
|
|
return (
|
|
api_code
|
|
+ self.gen_kernel_code(self.kernel['func'][0], '', inplace_flag)
|
|
+ """
|
|
}
|
|
"""
|
|
)
|
|
|
|
def gene_invoke_code(self, invoke_code, params_code):
|
|
return f"""
|
|
PADDLE_API {self.get_return_type()} {self.api}({params_code}) {{
|
|
return {invoke_code};
|
|
}}"""
|
|
|
|
def gene_api_code(self, grad_flag=False, append_predefined_out=True):
|
|
if self.is_base_api:
|
|
api_code = self.gene_base_api_code()
|
|
if len(self.inplace_map) > 0:
|
|
if self.api[-1] == '_':
|
|
api_code = ""
|
|
api_code = api_code + self.gene_base_api_code_for_inplace()
|
|
return api_code
|
|
elif self.is_only_composite_api:
|
|
# for composite and invoke api, dygraph use prim::xxx_grad method
|
|
return ''
|
|
else:
|
|
invoke_code = self.invoke
|
|
params_code = self.get_define_args(
|
|
grad_flag=grad_flag, append_predefined_out=append_predefined_out
|
|
)
|
|
return self.gene_invoke_code(invoke_code, params_code)
|