chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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']
|
||||
@@ -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
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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",
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user