chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from . import ap as ap, fuse as fuse
from .compiler import compile
__all__ = ['fuse', 'compile']
+15
View File
@@ -0,0 +1,15 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .facade_op import FacadeOp as FacadeOp
@@ -0,0 +1,559 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import ast
import functools
import itertools
import json
import operator
import typing as t
from dataclasses import dataclass
def convert_python_stmts_to_axpr_json(python_code_stmts_str):
tree = ast.parse(python_code_stmts_str)
parser = PyToAnfParser()
return parser(tree).ConvertToAnfExpr().JsonDump()
@dataclass
class AnfExpr:
def DumpToFileAsJson(self, file_name):
with open(file_name, "w") as f:
json.dump(self.value, f, indent=2)
def JsonDump(self):
return json.dumps(self.value)
@dataclass
class AtomicAnfExpr(AnfExpr):
value: t.Any
@dataclass
class CombinedAnfExpr(AnfExpr):
value: t.Any
@dataclass
class AnfParseResult:
bindings: list[str]
body_atomic_anf_expr: AtomicAnfExpr
def __add__(self, other):
return AnfParseResult(
bindings=[*self.bindings, *other.bindings],
body_atomic_anf_expr=other.body_atomic_anf_expr,
)
def ConvertToAnfExpr(self):
ret = self.body_atomic_anf_expr
if len(self.bindings) == 0:
return ret
assert isinstance(ret, AtomicAnfExpr)
ret = CombinedAnfExpr(
["__builtin_identity__", self.body_atomic_anf_expr.value]
)
return CombinedAnfExpr(["__builtin_let__", self.bindings, ret.value])
class PyToAnfParser:
def __init__(self, seq_no_counter=None, return_count_constraint=None):
self.bindings = []
self.seq_no_counter = (
seq_no_counter if seq_no_counter is not None else itertools.count()
)
self.return_count_constraint = (
return_count_constraint
if return_count_constraint is not None
else ReturnCounterConstraint(limits=1)
)
def __call__(self, tree):
ret = self.Parse(tree)
return AnfParseResult(bindings=self.bindings, body_atomic_anf_expr=ret)
def Parse(self, tree):
method_name = f"Parse{type(tree).__name__}"
return getattr(self, method_name)(tree)
def ParseImport(self, tree):
for alias in tree.names:
assert isinstance(alias, ast.alias)
name = alias.name
asname = alias.asname if alias.asname is not None else name
self.Bind(asname, ["import", {"str": name}])
return AtomicAnfExpr(None)
def ParseClassDef(self, tree: ast.ClassDef):
assert len(tree.keywords) == 0
class_name = tree.name
def GetBases():
bases = [self.Parse(base) for base in tree.bases]
return self.BindToTmpVar(
['__builtin_list__', *[x.value for x in bases]]
)
def GetFunctions():
body_name_and_method_pair = []
for func_def in tree.body:
if isinstance(func_def, ast.Pass):
continue
assert isinstance(func_def, ast.FunctionDef), (
f"only method supported in class definition, {type(func_def)} were given."
)
func_code = self.BindToTmpVar(
[
'__builtin_getattr__',
self.Parse(func_def).value,
{"str": '__function__'},
]
)
pair = self.BindToTmpVar(
[
"__builtin_list__",
{"str": func_def.name},
func_code.value,
]
)
body_name_and_method_pair.append(pair)
positional_args = self.BindToTmpVar(['__builtin_list__'])
keyword_args = self.BindToTmpVar(
[
'__builtin_list__',
*[x.value for x in body_name_and_method_pair],
]
)
packed_args = self.BindToTmpVar(
[
'__builtin_PackedArgs__',
positional_args.value,
keyword_args.value,
]
)
return self.BindToTmpVar(
['BuiltinSerializableAttrMap', packed_args.value]
)
class_anf_expr = self.BindToTmpVar(
[
'type',
{"str": class_name},
GetBases().value,
GetFunctions().value,
]
)
for elt in reversed(tree.decorator_list):
decorator = self.Parse(elt)
class_anf_expr = self.BindToTmpVar(
[decorator.value, class_anf_expr.value]
)
self.Bind(class_name, class_anf_expr)
return class_anf_expr
def Parsekeyword(self, tree):
value = self.Parse(tree.value)
return self.BindToTmpVar(
["__builtin_list__", {"str": tree.arg}, value.value]
)
def ParseBinOp(self, tree):
left = self.Parse(tree.left)
op = self.Parse(tree.op)
right = self.Parse(tree.right)
return self.BindToTmpVar([op.value, left.value, right.value])
def ParseUnaryOp(self, tree):
op = self.Parse(tree.op)
operand = self.Parse(tree.operand)
return self.BindToTmpVar([op.value, operand.value])
def ParseCompare(self, tree):
assert len(tree.ops) == 1
op = self.Parse(tree.ops[0])
left = self.Parse(tree.left)
assert len(tree.comparators) == 1
right = self.Parse(tree.comparators[0])
return self.BindToTmpVar([op.value, left.value, right.value])
def ParseAdd(self, tree):
return AtomicAnfExpr("__builtin_Add__")
def ParseSub(self, tree):
return AtomicAnfExpr("__builtin_Sub__")
def ParseMult(self, tree):
return AtomicAnfExpr("__builtin_Mul__")
def ParseDiv(self, tree):
return AtomicAnfExpr("__builtin_Div__")
def ParseFloorDiv(self, tree):
return AtomicAnfExpr("__builtin_FloorDiv__")
def ParseMod(self, tree):
return AtomicAnfExpr("__builtin_Mod__")
def ParseUSub(self, tree):
return AtomicAnfExpr("__builtin_Neg__")
def ParseEq(self, tree):
return AtomicAnfExpr("__builtin_EQ__")
def ParseNotEq(self, tree):
return AtomicAnfExpr("__builtin_NE__")
def ParseGt(self, tree):
return AtomicAnfExpr("__builtin_GT__")
def ParseGtE(self, tree):
return AtomicAnfExpr("__builtin_GE__")
def ParseLt(self, tree):
return AtomicAnfExpr("__builtin_LT__")
def ParseLtE(self, tree):
return AtomicAnfExpr("__builtin_LE__")
def ParseModule(self, module: ast.Module):
parse_result = AnfParseResult(
bindings=[], body_atomic_anf_expr=AtomicAnfExpr(None)
)
if len(module.body) > 0:
seq_no_counter = itertools.count()
return_count_constraint = ReturnCounterConstraint(limits=0)
parse_result = functools.reduce(
operator.add,
(
PyToAnfParser(seq_no_counter, return_count_constraint)(tree)
for tree in module.body
),
)
return parse_result.ConvertToAnfExpr()
def ParseFunctionDef(self, function_def: ast.FunctionDef):
if len(function_def.body) > 0:
return_count_constraint = ReturnCounterConstraint(limits=1)
return_stmt_idx = self.GetStmtSizeUntilReturn(function_def.body)
parse_result = functools.reduce(
operator.add,
[
PyToAnfParser(self.seq_no_counter, return_count_constraint)(
tree
)
for tree in function_def.body[0:return_stmt_idx]
if not isinstance(tree, ast.Pass)
]
+ [
AnfParseResult(
bindings=[], body_atomic_anf_expr=AtomicAnfExpr(None)
)
],
)
else:
parse_result = AnfParseResult(
bindings=[], body_atomic_anf_expr=AtomicAnfExpr(None)
)
args = [arg.arg for arg in function_def.args.args]
lmbd = AtomicAnfExpr(
['lambda', args, parse_result.ConvertToAnfExpr().value]
)
for elt in reversed(function_def.decorator_list):
decorator = self.Parse(elt)
lmbd = self.BindToTmpVar([decorator.value, lmbd.value])
func_name = function_def.name
self.Bind(func_name, lmbd)
return AtomicAnfExpr(func_name)
def ParseLambda(self, function_def: ast.Lambda):
return_count_constraint = ReturnCounterConstraint(limits=0)
parser = PyToAnfParser(self.seq_no_counter, return_count_constraint)
parse_result = parser(function_def.body)
args = [arg.arg for arg in function_def.args.args]
return AtomicAnfExpr(
['lambda', args, parse_result.ConvertToAnfExpr().value]
)
def ParseIfExp(self, if_expr: ast.IfExp):
test_value = self.Parse(if_expr.test)
true_value = self.ParseExprTo0ArgLambda(if_expr.body)
false_value = self.ParseExprTo0ArgLambda(if_expr.orelse)
ret = self.BindToTmpVar(
[
'__builtin_if__',
test_value.value,
true_value.value,
false_value.value,
]
)
return ret
def ParseBoolOp(self, bool_op: ast.BoolOp):
name = type(bool_op.op).__name__
method = f"Parse{name}"
return getattr(self, method)(bool_op)
def ParseOr(self, bool_op: ast.BoolOp):
assert len(bool_op.values) == 2
test_value = self.Parse(bool_op.values[0])
true_value = AtomicAnfExpr(['lambda', [], AtomicAnfExpr(True).value])
false_value = self.ParseExprTo0ArgLambda(bool_op.values[1])
ret = self.BindToTmpVar(
[
'__builtin_if__',
test_value.value,
true_value.value,
false_value.value,
]
)
return ret
def ParseAnd(self, bool_op: ast.BoolOp):
assert len(bool_op.values) == 2
test_value = self.Parse(bool_op.values[0])
true_value = self.ParseExprTo0ArgLambda(bool_op.values[1])
false_value = AtomicAnfExpr(['lambda', [], AtomicAnfExpr(False).value])
ret = self.BindToTmpVar(
[
'__builtin_if__',
test_value.value,
true_value.value,
false_value.value,
]
)
return ret
def ParseNot(self, unary_op: ast.UnaryOp):
return AtomicAnfExpr('__builtin_not__')
def ParseExprTo0ArgLambda(self, expr):
return_count_constraint = ReturnCounterConstraint(limits=0)
parser = PyToAnfParser(self.seq_no_counter, return_count_constraint)
parse_result = parser(expr)
return AtomicAnfExpr(
['lambda', [], parse_result.ConvertToAnfExpr().value]
)
def ParseAssert(self, expr: ast.Assert):
test_value = self.Parse(expr.test)
true_value = AtomicAnfExpr(['lambda', [], AtomicAnfExpr(None).value])
# handle lambda: rase(msg)
return_count_constraint = ReturnCounterConstraint(limits=0)
parser = PyToAnfParser(self.seq_no_counter, return_count_constraint)
if expr.msg is None:
msg = parser.BindToTmpVar(AtomicAnfExpr({"str": ""}))
else:
msg = parser.Parse(expr.msg)
exception = parser.BindToTmpVar(['AssertionError', msg.value])
raise_ret = parser.BindToTmpVar(['raise', exception.value])
false_value = AtomicAnfExpr(
[
'lambda',
[],
AnfParseResult(
bindings=parser.bindings, body_atomic_anf_expr=raise_ret
)
.ConvertToAnfExpr()
.value,
]
)
ret = self.BindToTmpVar(
[
'__builtin_if__',
test_value.value,
true_value.value,
false_value.value,
]
)
return ret
def ParseAssign(self, tree):
assert len(tree.targets) == 1
if isinstance(tree.targets[0], ast.Name):
val = self.Parse(tree.value)
var = tree.targets[0].id
self.Bind(var, val)
return AtomicAnfExpr(var)
elif isinstance(tree.targets[0], ast.Attribute):
val = self.Parse(tree.value)
attr = tree.targets[0]
f = self.BindToTmpVar(
[
'__builtin_setattr__',
self.Parse(attr.value).value,
{"str": attr.attr},
]
)
return self.BindToTmpVar([f.value, {"str": attr.attr}, val.value])
elif isinstance(tree.targets[0], ast.Subscript):
val = self.Parse(tree.value)
subscript = tree.targets[0]
slice_val = self.Parse(subscript.slice).value
f = self.BindToTmpVar(
[
'__builtin_setitem__',
self.Parse(subscript.value).value,
slice_val,
]
)
return self.BindToTmpVar([f.value, slice_val, val.value])
else:
raise NotImplementedError(tree.targets)
def ParseSubscript(self, tree):
val = self.Parse(tree.value)
slc = self.Parse(tree.slice)
return self.BindToTmpVar(["__builtin_getitem__", val.value, slc.value])
def ParseExpr(self, tree):
return self.BindToTmpVar(self.Parse(tree.value))
def BindToTmpVar(self, value):
tmp_var = self.get_tmp_var()
self.Bind(tmp_var, value)
return AtomicAnfExpr(tmp_var)
def GetStmtSizeUntilReturn(self, stmts):
for idx, stmt in enumerate(stmts):
if isinstance(stmt, ast.Return):
return idx + 1
return len(stmts)
def ParseReturn(self, tree: ast.Return):
self.return_count_constraint.CountAndCheck()
value = self.Parse(tree.value)
return self.BindToTmpVar(["__builtin_return__", value.value])
def ParseStarred(self, tree: ast.Starred):
value = self.Parse(tree.value)
return self.BindToTmpVar(["__builtin_starred__", value.value])
def ParseCall(self, tree: ast.Call):
func = self.Parse(tree.func)
assert isinstance(func, AtomicAnfExpr)
def ParseArg(arg):
parsed_arg = self.Parse(arg)
assert isinstance(parsed_arg, AtomicAnfExpr)
return parsed_arg
args = [ParseArg(arg).value for arg in tree.args]
kwargs = None
if len(tree.keywords) > 0:
keywords = [ParseArg(arg).value for arg in tree.keywords]
kwargs = self.BindToTmpVar(["__builtin_list__", *keywords])
if kwargs is None:
if any(isinstance(arg, ast.Starred) for arg in tree.args):
l = self.BindToTmpVar(["__builtin_list__", *args])
return self.BindToTmpVar(
["__builtin_apply__", func.value, l.value]
)
else:
return self.BindToTmpVar([func.value, *args])
else:
args = self.BindToTmpVar(["__builtin_list__", *args])
packed_args = self.BindToTmpVar(
["__builtin_PackedArgs__", args.value, kwargs.value]
)
return self.BindToTmpVar([func.value, packed_args.value])
def ParseList(self, lst: ast.List):
return self._ParseCall('__builtin_list__', lst.elts)
def _ParseCall(self, func, ast_args):
def ParseArg(arg):
parsed_arg = self.Parse(arg)
assert isinstance(parsed_arg, AtomicAnfExpr)
return parsed_arg
args = [ParseArg(arg).value for arg in ast_args]
ret_var = self.get_tmp_var()
self.Bind(ret_var, [func, *args])
return AtomicAnfExpr(ret_var)
def ParseAttribute(self, attr: ast.Attribute):
ret_var = self.get_tmp_var()
self.Bind(
ret_var,
[
'__builtin_getattr__',
self.Parse(attr.value).value,
{"str": attr.attr},
],
)
return AtomicAnfExpr(ret_var)
def ParseName(self, name: ast.Name):
return AtomicAnfExpr(name.id)
def ParseConstant(self, constant: ast.Constant):
if isinstance(constant.value, str):
return AtomicAnfExpr({"str": constant.value})
if isinstance(constant.value, (bool, int, float)):
return AtomicAnfExpr(constant.value)
if constant.value is None:
return AtomicAnfExpr(None)
raise NotImplementedError(f"{constant} not supported by anf_expr")
def ParseJoinedStr(self, tree: ast.JoinedStr):
if len(tree.values) == 0:
return AtomicAnfExpr({"str": ""})
def ToString(elt):
parsed_elt = self.Parse(elt)
parsed_elt = self.BindToTmpVar(
['__builtin_ToString__', parsed_elt.value]
)
return parsed_elt
ret = ToString(tree.values[0])
for elt in tree.values[1:]:
parsed_elt = ToString(elt)
ret = self.BindToTmpVar(
['__builtin_Add__', ret.value, parsed_elt.value]
)
return ret
def ParseFormattedValue(self, tree: ast.FormattedValue):
return self.Parse(tree.value)
def Bind(self, var_name, anf_expr):
return getattr(self, f"Bind{type(anf_expr).__name__}")(
var_name, anf_expr
)
def BindAtomicAnfExpr(self, var_name, anf_expr):
self.bindings.append(
[var_name, ["__builtin_identity__", anf_expr.value]]
)
def Bindlist(self, var_name, anf_expr):
self.bindings.append([var_name, anf_expr])
def get_tmp_var(self):
return f"___{next(self.seq_no_counter)}"
class ReturnCounterConstraint:
def __init__(self, limits):
self.counter = itertools.count()
self.limits = limits
def CountAndCheck(self):
return_stmt_id = next(self.counter)
assert return_stmt_id < self.limits
+93
View File
@@ -0,0 +1,93 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import warnings
import paddle
from .pir_attrs_serializer import PirAttrsSerializer
class FacadeOp:
def __init__(self):
self.custom_op_name_ = self.custom_op_name()
self.infer_meta_ = self._check_to_str_pair(self.infer_meta())
self.infer_symbolic_ = self._check_to_str_pair(self.infer_symbolic())
self.num_inputs_ = self.num_inputs()
self.attrs_serializer_ = PirAttrsSerializer(self.attributes_schema)
def custom_op_name(self) -> str:
raise NotImplementedError(
"static method custom_op_name() is not overwritten"
)
def infer_meta(self) -> str:
raise NotImplementedError(
"static method infer_meta() is not overwritten"
)
def infer_symbolic(self) -> str:
raise NotImplementedError(
"static method infer_symbolic() is not overwritten"
)
def num_inputs(self) -> int:
raise NotImplementedError(
"static method num_inputs() is not overwritten"
)
def num_outputs(self, args) -> int:
raise NotImplementedError(
"static method num_outputs() is not overwritten"
)
def attributes_schema(self):
# annotations matter.
raise NotImplementedError(
"static method attributes_schema() is not overwritten"
)
def __call__(self, args, **kwargs):
if paddle.in_dynamic_mode():
warnings.warn("ap FacadeOp should not run in dynamic mode")
assert isinstance(args, (tuple, list))
self._check_num_inputs(len(args))
serialized_attrs = self.attrs_serializer_(**kwargs)
ret = paddle._C_ops.ap_facade(
args if len(args) > 0 else None,
self.num_outputs(args),
self.custom_op_name_,
self.infer_meta_,
self.infer_symbolic_,
serialized_attrs,
)
self._check_num_outputs(args, len(ret))
return ret
def _check_num_inputs(self, num_args):
if self.num_inputs_ >= 0:
assert self.num_inputs_ == num_args
def _check_num_outputs(self, args, num_rets):
num_outputs = self.num_outputs(args)
if num_outputs >= 0:
assert num_outputs == num_rets
def _check_to_str_pair(self, pair_str):
assert isinstance(pair_str, str)
pair = pair_str.split(".")
assert len(pair) == 2
assert pair[0] not in (None, "")
assert pair[1] not in (None, "")
return pair_str
@@ -0,0 +1,234 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import inspect
import paddle
from ..data_type_util import get_dtype_lower_case_name
from ..typing import DType
from .apy_to_axpr_json import convert_python_stmts_to_axpr_json
class PirAttrsSerializer:
def __init__(self, func):
self.attributes_schema = self._get_attributes_schema(func)
self._check_attributes_schema(self.attributes_schema)
self.attr_name2serializer = {
attr_name: serializer
for attr_name, schema_item in self.attributes_schema
for serializer in [self._get_serializer(attr_name, schema_item)]
}
def __call__(self, **attributes):
print(attributes)
attributes_names = {name for name, _ in attributes.items()}
attr_names = {name for name, _ in self.attributes_schema}
assert attributes_names == attr_names, (
f"expected attr_names: {attr_names}, but actual attr_names are {attributes_names}"
)
py_assigns = "\n".join(
py_stmt
for attr_name, attr_val in attributes.items()
for py_stmt in self.attr_name2serializer[attr_name](attr_val)
)
py_stmts_str = f"{py_assigns}\n{self._get_attr_map_ctor_str(self.attributes_schema)}"
return convert_python_stmts_to_axpr_json(py_stmts_str)
def _get_attr_map_ctor_str(self, attributes_schema):
kwargs = ", ".join(f"{name}={name}" for name, _ in attributes_schema)
return f"__builtin__AttrMap({kwargs})"
def _get_attributes_schema(self, obj):
if isinstance(obj, (list, tuple)):
return obj
func = obj
assert inspect.isfunction(func) or inspect.ismethod(func)
full_arg_spec = inspect.getfullargspec(func)
args = (
full_arg_spec.args[1:]
if inspect.ismethod(func)
else full_arg_spec.args
)
return [
(arg_name, annotation)
for arg_name in args
for annotation in [full_arg_spec.annotations[arg_name]]
]
def _check_attributes_schema(self, attributes_schema):
for _, attr_type in attributes_schema:
self._check_attributes_schema_item_is_valid(attr_type)
def _check_attributes_schema_item_is_valid(self, attr_type):
if attr_type in self._supported_basic_types():
return
assert isinstance(attr_type, list), (
f"attribute type {attr_type} is not supported."
)
assert len(attr_type) == 1, (
"only syntax like [bool], [int], [float], [str] supported."
)
assert attr_type[0] in self._supported_basic_types(), (
f"supported list element types are bool/int/float/str, not include {attr_type[0]}."
)
def _supported_basic_types(self):
return (bool, int, float, str, DType)
def _get_serializer(self, attr_name, schema_item):
assert attr_name not in (
"custom_op_name",
"infer_meta_func_name",
"infer_symbolic_func_name",
)
schema_item_as_key = self._get_schema_item_as_key(schema_item)
return _get_serializer_factory[schema_item_as_key](attr_name)
def _get_schema_item_as_key(self, schema_item):
if schema_item in self._supported_basic_types():
return schema_item
assert isinstance(schema_item, list)
return tuple(schema_item)
class PirAttributeSerializer:
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
yield from []
raise NotImplementedError
class BoolAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, bool)
yield f"{self.attr_name} = {value}"
class IntAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, int)
yield f"{self.attr_name} = {value}"
class FloatAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, float)
yield f"{self.attr_name} = {value}"
class StrAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, str)
yield f"{self.attr_name} = {value}"
class DTypeAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, paddle.dtype)
name = get_dtype_lower_case_name(value)
yield f"{self.attr_name} = __builtin__DataType.{name}"
class BoolArrayAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, list)
for elt in value:
assert isinstance(elt, bool)
yield f"{self.attr_name} = {value}"
class IntArrayAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, list)
for elt in value:
assert isinstance(elt, int)
yield f"{self.attr_name} = {value}"
class FloatArrayAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, list)
for elt in value:
assert isinstance(elt, float)
yield f"{self.attr_name} = {value}"
class StrArrayAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, list)
for elt in value:
assert isinstance(elt, str)
yield f"{self.attr_name} = {value}"
class DTypeArrayAttributeSerializer(PirAttributeSerializer):
def __init__(self, attr_name):
self.attr_name = attr_name
def __call__(self, value):
assert isinstance(value, list)
for elt in value:
assert isinstance(elt, paddle.dtype)
value_str = ", ".join(
f"__builtin__DataType.{name}"
for dtype in value
for name in [get_dtype_lower_case_name(dtype)]
)
yield f"{self.attr_name} = [{value_str}]"
_get_serializer_factory = {
bool: BoolAttributeSerializer,
int: IntAttributeSerializer,
float: FloatAttributeSerializer,
str: StrAttributeSerializer,
DType: DTypeAttributeSerializer,
(bool,): BoolArrayAttributeSerializer,
(int,): IntArrayAttributeSerializer,
(float,): FloatArrayAttributeSerializer,
(str,): StrArrayAttributeSerializer,
(DType,): DTypeArrayAttributeSerializer,
}
+292
View File
@@ -0,0 +1,292 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import inspect
import os
from collections import OrderedDict
from contextlib import contextmanager
from dataclasses import dataclass
from typing import TYPE_CHECKING
import paddle
from paddle.incubate.cc.tools import apy_to_axpr_json
from paddle.static import InputSpec
from . import typing as pct
if TYPE_CHECKING:
from collections.abc import Callable
__all__ = ['compile']
# Usage:
# import paddle.incubate.cc.typing as pct
# import paddle.incubate.cc as pcc
# import paddle.nn.functional as F
#
# N = pct.DimVar('N', min=2)
# K = pct.DimVar("K", min=2)
# M = pct.DimVar("M", 7168)
# DType = pct.DTypeVar("T", "bfloat16", "float32")
#
# def foo(
# x: pct.Tensor([N, K], DType),
# y: pct.Tensor([K, M], DType),
# b: pct.Tensor([M], DType)
# ):
# @pcc.force_register_fusion
# def activate(out):
# return F.relu(out + b)
# return activate(x @ y)
#
# fused_foo = pcc.compile(
# foo
# )
def compile(func, *args, **kwargs):
annotations = _get_input_annotations(func)
dtypes2func = {}
for input_specs in _get_input_spec_lists(annotations):
dtypes = tuple(input_spec.dtype for input_spec in input_specs)
dtypes2func[dtypes] = _compile(func, input_specs, *args, **kwargs)
return OverloadedFunc(FuncOverloadCtx(dtypes2func))
def _compile(
func,
input_specs,
train=False,
ap_path="",
ap_workspace_dir='/tmp/paddle/ap',
backend_device='cuda',
target_framework='paddle',
compile_engine='PCC',
):
assert ap_path is not None
assert not train, "only support inference now"
assert backend_device in ["cuda", "dcu", "custom_device"]
os.makedirs(ap_workspace_dir, exist_ok=True)
build_strategy = paddle.static.BuildStrategy()
assert compile_engine in ('CINN', 'PCC')
with _ap_envs(ap_path, ap_workspace_dir, backend_device):
static_fn = paddle.jit.to_static(
func,
input_spec=input_specs,
build_strategy=build_strategy,
full_graph=True,
backend=compile_engine,
)
if not train:
static_fn.eval()
else:
static_fn.train()
concrete_program, partial_program_layer = (
static_fn.get_concrete_program(
*input_specs, is_train=static_fn._is_train_mode()
)
)
partial_program_layer.training = static_fn._is_train_mode()
# Force to generate the program immediately.
if train:
_ = partial_program_layer.train_program.forward_program
else:
_ = partial_program_layer.infer_program.forward_program
return partial_program_layer
@dataclass
class FuncOverloadCtx:
dtypes2func: dict[list[paddle.dtype], Callable]
class OverloadedFunc:
def __init__(self, func_overload_ctx: FuncOverloadCtx):
self.func_overload_ctx = func_overload_ctx
def __call__(self, *args):
dtypes = tuple(tensor.dtype for tensor in args)
func = self.func_overload_ctx.dtypes2func.get(dtypes, None)
assert func is not None, self.mismatched_debug_info(dtypes)
return func(inputs=[*args])
def mismatched_debug_info(self, dtypes):
valid_signatures = "; ".join(
f"[{idx + 1}] {dtypes}"
for idx, pair in enumerate(
self.func_overload_ctx.dtypes2func.items()
)
for dtypes in [pair[0]]
)
return f"input signature {dtypes} mismatched, valid signatures are: {valid_signatures}"
@dataclass
class InputSpecMakeCtx:
name2dtype_num_candidates: dict[str, int]
name2dtype_candidate_idx: dict[str, int]
@contextmanager
def _ap_envs(ap_path, ap_workspace_dir, backend_device):
old_ap_workspace_dir = os.environ.get('AP_WORKSPACE_DIR')
new_ap_path, old_ap_path = _get_ap_path(ap_path, backend_device)
_convert_apy_to_axpr(new_ap_path)
os.environ['AP_PATH'] = new_ap_path
os.environ['AP_WORKSPACE_DIR'] = ap_workspace_dir
new_flags, old_flags = _get_ap_flags()
paddle.set_flags(new_flags)
old_prim_all = paddle.base.core._is_all_prim_enabled()
paddle.base.core._set_prim_all_enabled(True)
try:
yield
finally:
os.environ['AP_PATH'] = old_ap_path
if old_ap_workspace_dir is not None:
os.environ['AP_WORKSPACE_DIR'] = old_ap_workspace_dir
paddle.set_flags(old_flags)
paddle.base.core._set_prim_all_enabled(old_prim_all)
def _get_ap_path(ap_path, backend_device):
ap_sys_path = f"{os.path.dirname(paddle.__file__)}/apy/sys"
matmul_path = f"{os.path.dirname(paddle.__file__)}/apy/matmul_pass"
if backend_device in ["cuda", "dcu"]:
device_path = (
f"{os.path.dirname(paddle.__file__)}/apy/device/{backend_device}"
)
else:
device_path = ""
old_ap_path = os.environ.get('AP_PATH')
new_ap_path = f"{ap_sys_path}:{ap_path}:{device_path}:{matmul_path}:{old_ap_path if old_ap_path is not None else ''}"
if old_ap_path is None:
# Always add sys_path to AP_PATH, as it is required at runtime.
old_ap_path = ap_sys_path
return new_ap_path, old_ap_path
def _get_ap_flags():
old_flags = paddle.get_flags(
['FLAGS_enable_ap', 'FLAGS_prim_enable_dynamic']
)
new_flags = dict(old_flags)
new_flags['FLAGS_enable_ap'] = True
new_flags['FLAGS_prim_enable_dynamic'] = True
return new_flags, old_flags
def _convert_apy_to_axpr(ap_path):
all_ap_paths = {p for p in ap_path.split(":") if p and os.path.isdir(p)}
for path in all_ap_paths:
apy_to_axpr_json.PyToAxpr(path)(path)
def _get_input_annotations(func):
full_arg_spec = inspect.getfullargspec(func)
return [
pct_type
for arg_name in full_arg_spec.args
for pct_type in [full_arg_spec.annotations[arg_name]]
]
def _get_input_spec_lists(annotations):
ctx = _create_empty_input_spec_make_ctx(annotations)
assert len(ctx.name2dtype_num_candidates) > 0
dtype_var_names = [
pair[0] for pair in ctx.name2dtype_num_candidates.items()
]
dtype_num_candidates = [
pair[1] for pair in ctx.name2dtype_num_candidates.items()
]
dtype_candidate_idx_compositions = _cartesian_product(
[range(num_candidates) for num_candidates in dtype_num_candidates]
)
for idx_composition in dtype_candidate_idx_compositions:
for arg_idx, candidate_idx in enumerate(idx_composition):
ctx.name2dtype_candidate_idx[dtype_var_names[arg_idx]] = (
candidate_idx
)
yield _get_input_specs(annotations, ctx)
def _create_empty_input_spec_make_ctx(annotations):
ctx = InputSpecMakeCtx(OrderedDict(), OrderedDict())
_init_empty_input_spec_make_ctx(annotations, ctx)
return ctx
def _init_empty_input_spec_make_ctx(annotations, mut_ctx: InputSpecMakeCtx):
for pct_type in annotations:
_init_input_spec_make_ctx_name2dtype_num_candidates(pct_type, mut_ctx)
def _init_input_spec_make_ctx_name2dtype_num_candidates(
pct_type, mut_ctx: InputSpecMakeCtx
):
assert isinstance(pct_type.dtype, pct.DTypeVar), (
f"pct_type.dtype should be a DTypeVar, but {type(pct_type.dtype)} were given."
)
name = pct_type.dtype.name
if name in mut_ctx.name2dtype_num_candidates:
assert mut_ctx.name2dtype_num_candidates[name] == len(
pct_type.dtype.candidates
)
else:
mut_ctx.name2dtype_num_candidates[name] = len(pct_type.dtype.candidates)
def _get_input_specs(annotations, ctx: InputSpecMakeCtx):
return [_get_input_spec(pct_type, ctx) for pct_type in annotations]
def _get_input_spec(pct_type, ctx: InputSpecMakeCtx):
assert isinstance(pct_type, pct.Tensor)
return InputSpec(
shape=_get_input_spec_shape(pct_type, ctx),
dtype=_get_input_spec_dtype(pct_type, ctx),
)
def _get_input_spec_shape(pct_type, ctx: InputSpecMakeCtx):
return [_get_input_spec_shape_dim(dim_var) for dim_var in pct_type.shape]
def _get_input_spec_shape_dim(dim_var: pct.DimVar):
if isinstance(dim_var, int):
return dim_var
assert isinstance(dim_var, pct.DimVar)
if isinstance(dim_var.name_or_value, int):
return dim_var.name_or_value
return None
def _get_input_spec_dtype(pct_type, ctx: InputSpecMakeCtx):
assert isinstance(pct_type.dtype, pct.DTypeVar)
name = pct_type.dtype.name
candidate_idx = ctx.name2dtype_candidate_idx[name]
return pct_type.dtype.candidates[candidate_idx]
def _cartesian_product(lst_of_lst):
assert len(lst_of_lst) > 0
return _cartesian_product_impl([()], lst_of_lst)
def _cartesian_product_impl(collect_lst, lst_of_lst):
if len(lst_of_lst) == 0:
return collect_lst
collect_lst = [(*x, y) for x in collect_lst for y in lst_of_lst[0]]
return _cartesian_product_impl(collect_lst, lst_of_lst[1:])
@@ -0,0 +1,40 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
def get_dtype_lower_case_name(dtype):
return _up_case_name2lower_case_name[dtype.name]
_up_case_name2lower_case_name = {
"UNDEFINED": "void",
"BOOL": "bool",
"INT8": "int8",
"UINT8": "uint8",
"INT16": "int16",
"UINT16": "uint16",
"INT32": "int32",
"UINT32": "uint32",
"INT64": "int64",
"UINT64": "uint64",
"FLOAT8_E4M3FN": "float8_e4m3fn",
"FLOAT8_E5M2": "float8_e5m2",
"BFLOAT16": "bfloat16",
"FLOAT16": "float16",
"FLOAT32": "float32",
"FLOAT64": "float64",
"COMPLEX64": "complex64",
"COMPLEX128": "complex128",
"PSTRING": "pstring",
}
+32
View File
@@ -0,0 +1,32 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from contextlib import contextmanager
import paddle
__all__ = ['matmul', 'by_register']
@contextmanager
def by_register():
paddle._C_ops.ap_trivial_fusion_begin(None)
yield
paddle._C_ops.ap_trivial_fusion_end(None)
def matmul(x, w, epilogue, **kwargs):
x = paddle.matmul(x, w, **kwargs)
with by_register():
return epilogue(x)
@@ -0,0 +1,108 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import ast
import fnmatch
import glob
import os
import sys
from pathlib import Path
from paddle.incubate.cc.ap.apy_to_axpr_json import PyToAnfParser
FLAGS_FIRST_CYCLE = True
APY_ROOT = "apy"
def CollectParentIgnoreFile(start_path):
apy_ignores = []
path = Path(start_path).parent
while True:
ignore_path = os.path.join(path, '.apy_ignore')
if os.path.isfile(ignore_path):
apy_ignores.append(ignore_path)
parent_path = os.path.dirname(path)
if parent_path.split(os.sep)[-1] == APY_ROOT or parent_path == path:
break
path = parent_path
for root, dirs, files in os.walk(start_path):
if '.apy_ignore' in files:
apy_ignores.append(os.path.join(root, '.apy_ignore'))
return apy_ignores
def ReadIgnoreRules(ignore_paths):
rules = []
for ignore_path in ignore_paths:
base_dir = os.path.dirname(ignore_path)
with open(ignore_path, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
is_negation = line.startswith('!')
if is_negation:
pattern = line[1:]
else:
pattern = line
if pattern.startswith(os.sep):
pattern = pattern[1:]
rules.append((pattern, is_negation, base_dir))
return rules
def IsAllowed(file_path, ignore_rules):
if os.path.isfile(file_path) and not file_path.endswith(".py"):
return False
file_path = os.path.normpath(file_path)
result = True
for pattern, is_negation, base_dir in ignore_rules:
full_pattern = os.path.join(base_dir, pattern).rstrip(os.sep)
if os.path.isdir(full_pattern):
full_pattern += '*'
match = fnmatch.fnmatch(file_path, full_pattern)
if match:
if is_negation:
result = True
else:
result = False
return result
class PyToAxpr:
def __init__(self, file_path, ignore_paths=None):
ignore_files = CollectParentIgnoreFile(file_path)
self.ignore_rules = ReadIgnoreRules(ignore_files)
if ignore_paths:
self.ignore_rules += [(name, False, "") for name in ignore_paths]
def __call__(self, file_path):
if not IsAllowed(file_path, self.ignore_rules):
pass
elif os.path.isdir(file_path):
for file in glob.glob(f"{file_path}{os.sep}*"):
self.__call__(file)
else:
print(f"apy_to_axpr_json {file_path}")
with open(file_path) as f:
tree = ast.parse(f.read())
parser = PyToAnfParser()
parser(tree).ConvertToAnfExpr().DumpToFileAsJson(
f"{file_path}.json"
)
if __name__ == "__main__":
for file_path in sys.argv[1:]:
PyToAxpr(file_path)(file_path)
+71
View File
@@ -0,0 +1,71 @@
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
__all__ = [
'DimVar',
'DTypeVar',
'Tensor',
]
# Usage:
# N = paddle.incubate.cc.typing.DimVar("N")
# M = paddle.incubate.cc.typing.DimVar(4096)
class DimVar:
def __init__(
self,
name_or_value: str | int,
min: int | None = None,
max: int | None = None,
):
self.name_or_value = name_or_value
self.min = min
self.max = max
# alias
Dim = DimVar
# Usage:
# T = paddle.incubate.cc.typing.DTypeVar("T", "bfloat16", "float32")
class DTypeVar:
def __init__(self, name: str, *candidates):
assert len(candidates) > 0
assert len(candidates) == len(set(candidates))
for candidate in candidates:
assert isinstance(candidate, str)
self.name = str
self.candidates = candidates
# alias
DType = DTypeVar
# Usage:
#
# import paddle.incubate.cc.typing as pct
# N = pct.DimVar("N")
# M = pct.DimVar("M")
# DType = pct.DTypeVar("T")
# def foo(x: paddle.cc.typing.Tensor([N, M], DType)):
# ...
#
class Tensor:
def __init__(self, shape, dtype):
self.shape = shape
self.dtype = dtype