init v0.23.0

Signed-off-by: Sun Ruoxi <sunruoxi@4paradigm.com>
This commit is contained in:
2026-08-27 15:11:51 +08:00
parent b582a8e7d1
commit 7f8a1b1f7a
2849 changed files with 712887 additions and 22001 deletions

View File

@@ -0,0 +1,534 @@
#!/usr/bin/env python3
# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------
import argparse
import copy
import hashlib
import json
import os
import sys
from collections import defaultdict
from typing import NamedTuple
import const_var
import opdesc_parser
import regex as re
PYF_PATH = os.path.dirname(os.path.realpath(__file__))
class ParamInfo(NamedTuple):
dtype_list: list
format_list: list
dtype_for_bin_list: dict
format_for_bin_list: dict
class BinParamBuilder(opdesc_parser.OpDesc):
def __init__(self: any, op_type: str):
super().__init__(op_type)
self.soc = ""
self.out_path = ""
self.tiling_keys = set()
self.op_debug_config = ""
self.op_super_config = []
def set_soc_version(self: any, soc: str):
self.soc = soc
def set_out_path(self: any, out_path: str):
self.out_path = out_path
def set_tiling_key(self: any, tiling_key_info: set):
if tiling_key_info:
self.tiling_keys.update(tiling_key_info)
def set_op_debug_config(self: any, op_debug_config: str):
if op_debug_config:
self.op_debug_config = op_debug_config
def set_op_super_config(self: any, op_super_config: str):
if op_super_config:
self.op_super_config = op_super_config
def get_full_list(self: any):
dtype_list = []
for dtype_in in self.input_dtype:
dtype_list.append(dtype_in.split(","))
for dtype_out in self.output_dtype:
dtype_list.append(dtype_out.split(","))
format_list = []
for fmt_in in self.input_fmt:
format_list.append(fmt_in.split(","))
for fmt_out in self.output_fmt:
format_list.append(fmt_out.split(","))
dtype_for_bin_list = [[] for _ in range(len(self.input_dtype) + len(self.output_dtype))]
format_for_bin_list = copy.deepcopy(dtype_for_bin_list)
for key, value in self.input_dtype_for_bin.items():
dtype_for_bin_list[key] = value.split(",")
for key, value in self.output_dtype_for_bin.items():
dtype_for_bin_list[key + len(self.input_dtype)] = value.split(",")
for key, value in self.input_fmt_for_bin.items():
format_for_bin_list[key] = value.split(",")
for key, value in self.output_fmt_for_bin.items():
format_for_bin_list[key + len(self.input_dtype)] = value.split(",")
return ParamInfo(dtype_list, format_list, dtype_for_bin_list, format_for_bin_list)
def gen_bin_cprs_list(self: any, param_info: ParamInfo):
combine_dict = {}
origin_combine_dict = {}
for cob_idx in range(0, len(self.input_dtype[0].split(","))):
origin_combine = ""
combine = ""
for param_idx in range(0, len(self.input_dtype) + len(self.output_dtype)):
if param_info.dtype_for_bin_list[param_idx]:
combine += param_info.dtype_for_bin_list[param_idx][cob_idx]
else:
combine += param_info.dtype_list[param_idx][cob_idx]
origin_combine += param_info.dtype_list[param_idx][cob_idx]
if param_info.format_for_bin_list[param_idx]:
combine += param_info.format_for_bin_list[param_idx][cob_idx]
else:
combine += param_info.format_list[param_idx][cob_idx]
origin_combine += param_info.format_list[param_idx][cob_idx]
if combine not in combine_dict:
combine_dict[combine] = []
combine_dict[combine].append(cob_idx)
origin_combine_dict[origin_combine] = cob_idx
for key, value in combine_dict.items():
if key not in origin_combine_dict:
print(f"WARNING: ForBinQuery {key} not in origin combine")
self.bin_save_list += value
continue
if len(value) == 1 and value[0] == origin_combine_dict[key]:
self.bin_save_list += value
continue
self.bin_cprs_head.append(origin_combine_dict[key])
self.bin_cprs_list.append(value)
for index, sub_list in enumerate(self.bin_cprs_list):
if self.bin_cprs_head[index] not in self.bin_save_list:
continue
sub_list.append(self.bin_cprs_head[index])
self.bin_save_list += self.bin_cprs_head
def gen_for_bin_list(self: any, param_info: ParamInfo):
combine_size = len(self.input_dtype[0].split(","))
input_size = len(self.input_dtype)
output_size = len(self.output_dtype)
self.input_dtype_for_bin_list = [[] for _ in range(input_size)]
self.output_dtype_for_bin_list = [[] for _ in range(output_size)]
for i in range(0, input_size):
self.input_dtype_for_bin_list[i] = [[] for _ in range(combine_size)]
for i in range(0, output_size):
self.output_dtype_for_bin_list[i] = [[] for _ in range(combine_size)]
self.input_fmt_for_bin_list = copy.deepcopy(self.input_dtype_for_bin_list)
self.output_fmt_for_bin_list = copy.deepcopy(self.output_dtype_for_bin_list)
for index, sub_list in enumerate(self.bin_cprs_list):
head_idx = self.bin_cprs_head[index]
for cmb_idx in sub_list:
for i in range(0, input_size):
self.input_dtype_for_bin_list[i][head_idx].append(param_info.dtype_list[i][cmb_idx])
self.input_fmt_for_bin_list[i][head_idx].append(param_info.format_list[i][cmb_idx])
for i in range(0, output_size):
self.output_dtype_for_bin_list[i][head_idx].append(param_info.dtype_list[i + input_size][cmb_idx])
self.output_fmt_for_bin_list[i][head_idx].append(param_info.format_list[i + input_size][cmb_idx])
def rm_cprs_cmb(self: any, dtype_list, format_list, input_size, output_size):
for i in range(0, input_size):
self.input_dtype_for_bin_list[i] = [
element for index, element in enumerate(self.input_dtype_for_bin_list[i]) if index in self.bin_save_list
]
self.input_fmt_for_bin_list[i] = [
element for index, element in enumerate(self.input_fmt_for_bin_list[i]) if index in self.bin_save_list
]
new_dtype_list = [element for index, element in enumerate(dtype_list[i]) if index in self.bin_save_list]
new_dtype_str = ""
for dtype in new_dtype_list:
new_dtype_str += f"{dtype},"
self.input_dtype[i] = new_dtype_str[:-1]
new_format_list = [element for index, element in enumerate(format_list[i]) if index in self.bin_save_list]
new_format_str = ""
for fmt in new_format_list:
new_format_str += f"{fmt},"
self.input_fmt[i] = new_format_str[:-1]
for i in range(0, output_size):
self.output_dtype_for_bin_list[i] = [
element
for index, element in enumerate(self.output_dtype_for_bin_list[i])
if index in self.bin_save_list
]
self.output_fmt_for_bin_list[i] = [
element for index, element in enumerate(self.output_fmt_for_bin_list[i]) if index in self.bin_save_list
]
new_dtype_list = [
element for index, element in enumerate(dtype_list[i + input_size]) if index in self.bin_save_list
]
new_dtype_str = ""
for dtype in new_dtype_list:
new_dtype_str += f"{dtype},"
self.output_dtype[i] = new_dtype_str[:-1]
new_format_list = [
element for index, element in enumerate(format_list[i + input_size]) if index in self.bin_save_list
]
new_format_str = ""
for fmt in new_format_list:
new_format_str += f"{fmt},"
self.output_fmt[i] = new_format_str[:-1]
def is_set_for_bin_query(self: any):
return any(
[
self.input_dtype_for_bin,
self.output_dtype_for_bin,
self.input_fmt_for_bin,
self.output_fmt_for_bin,
]
)
def for_bin_list_match(self: any):
if not self.is_set_for_bin_query():
return
input_size = len(self.input_dtype)
output_size = len(self.output_dtype)
param_info = self.get_full_list()
self.gen_bin_cprs_list(param_info)
self.gen_for_bin_list(param_info)
if len(self.bin_save_list) == len(self.input_dtype[0].split(",")):
print("WARNING: ForBinQuery can not compress number of bin file with this set, please check!!.")
return
self.rm_cprs_cmb(param_info.dtype_list, param_info.format_list, input_size, output_size)
def gen_input_json(self: any, auto_gen_path: str):
key_map = {}
self.for_bin_list_match()
if len(self.input_dtype) == 0:
count = len(self.output_dtype[0].split(","))
else:
count = len(self.input_dtype[0].split(","))
if count == 0:
raise RuntimeError(f"Op {self.op_type} must have at least one input or output")
required_parameters = set()
index_value = -1
for i in range(0, count):
inputs = []
outputs = []
attrs = []
required_parameter = []
op_node = {}
for idx in range(0, len(self.input_name)):
idtypes = self.input_dtype[idx].split(",")
ifmts = self.input_fmt[idx].split(",")
itype = self.input_type[idx]
para = {}
para["name"] = self.input_name[idx][:-5]
para["index"] = idx
para["dtype"] = idtypes[i]
if self.is_set_for_bin_query() and self.input_dtype_for_bin_list[idx][i]:
para["dtypeForBinQuery"] = self.input_dtype_for_bin_list[idx][i]
para["format"] = ifmts[i]
if self.is_set_for_bin_query() and self.input_fmt_for_bin_list[idx][i]:
para["formatForBinQuery"] = self.input_fmt_for_bin_list[idx][i]
para["paramType"] = itype
para["shape"] = [-2]
para["format_match_mode"] = "FormatAgnostic"
input_parameter_key = (idtypes[i], ifmts[i])
if itype == "dynamic":
inputs.append([para])
required_parameter.append(input_parameter_key)
elif itype == "required":
inputs.append(para)
required_parameter.append(input_parameter_key)
else:
inputs.append(para)
for idx in range(0, len(self.output_name)):
odtypes = self.output_dtype[idx].split(",")
ofmts = self.output_fmt[idx].split(",")
otype = self.output_type[idx]
para = {}
para["name"] = self.output_name[idx][:-5]
para["index"] = idx
para["dtype"] = odtypes[i]
if self.is_set_for_bin_query() and self.output_dtype_for_bin_list[idx][i]:
para["dtypeForBinQuery"] = self.output_dtype_for_bin_list[idx][i]
para["format"] = ofmts[i]
if self.is_set_for_bin_query() and self.output_fmt_for_bin_list[idx][i]:
para["formatForBinQuery"] = self.output_fmt_for_bin_list[idx][i]
para["paramType"] = otype
para["shape"] = [-2]
para["format_match_mode"] = "FormatAgnostic"
output_parameter_key = (odtypes[i], ofmts[i])
if otype == "dynamic":
outputs.append([para])
required_parameter.append(output_parameter_key)
elif otype == "required":
outputs.append(para)
required_parameter.append(output_parameter_key)
else:
outputs.append(para)
for attr in self.attr_list:
att = {}
att["name"] = attr
atype = self.attr_val.get(attr).get("type").lower()
att["dtype"] = atype
att["value"] = const_var.ATTR_DEF_VAL.get(atype)
attrs.append(att)
required_parameter_tuple = tuple(required_parameter)
if required_parameter_tuple in required_parameters:
continue
else:
required_parameters.add(required_parameter_tuple)
index_value += 1
op_node["bin_filename"] = ""
op_node["inputs"] = inputs
op_node["outputs"] = outputs
if len(attrs) > 0:
op_node["attrs"] = attrs
param = {}
param["op_type"] = self.op_type
param["op_list"] = [op_node]
objstr = json.dumps(param, indent=" ")
md5sum = hashlib.md5(objstr.encode("utf-8")).hexdigest()
while key_map.get(md5sum) is not None:
objstr += "1"
md5sum = hashlib.md5(objstr.encode("utf-8")).hexdigest()
key_map[md5sum] = md5sum
bin_file = self.op_type + "_" + md5sum
op_node["bin_filename"] = bin_file
param_file = os.path.join(self.out_path, bin_file + "_param.json")
param_file = os.path.realpath(param_file)
self._write_build_json(param_file, param)
self._write_build_cmd(param_file, bin_file, index_value, auto_gen_path)
if self.op_super_config:
bin_file += "_relocatable"
op_node["bin_filename"] = bin_file
param_file = os.path.join(self.out_path, bin_file + "_param.json")
param_file = os.path.realpath(param_file)
self._write_build_json(param_file, param)
index_value += 1
self._write_build_cmd(param_file, bin_file, index_value, auto_gen_path, True)
def _write_build_json(self: any, param_file: str, param):
with os.fdopen(os.open(param_file, const_var.WFLAGS, const_var.WMODES), "w") as fd:
json.dump(param, fd, indent=" ")
def _generate_check_result(self: any, enable_tiling_keys: bool, bin_file: str):
check_result = ""
if enable_tiling_keys is False:
check_result += 'echo "${res}"\n'
check_result += const_var.CHK_CMD.format(res_file=bin_file + ".json")
check_result += const_var.CHK_CMD.format(res_file=bin_file + ".o")
else:
check_result += "if [ $? -eq 1 ]; then\n"
check_result += ' if echo "${res}" | \
grep -q "None of the given tiling keys are in the supported list"; then\n'
check_result += ' echo "${res}"\n'
check_result += " else\n"
check_result += ' echo "${res}"\n'
check_result += " exit 1\n"
check_result += " fi\n"
check_result += "else\n"
check_result += 'echo "${res}"\n'
check_result += const_var.CHK_CMD.format(res_file=bin_file + ".json")
check_result += const_var.CHK_CMD.format(res_file=bin_file + ".o")
check_result += "fi\n"
return check_result
def _write_build_cmd(self: any, param_file: str, bin_file: str, index: int, auto_gen_path: str, super_mode=False):
hard_soc = const_var.conv_soc_ver(self.soc)
if not hard_soc:
hard_soc = self.soc.capitalize()
name_com = [self.op_type, self.op_file, str(index)]
compile_file = os.path.join(self.out_path, "-".join(name_com) + ".sh")
compile_file = os.path.realpath(compile_file)
bin_cmd_str = "res=$(opc $1 --main_func={fun} --input_param={param} --soc_version={soc} \
--output=$2 --impl_mode={impl} --simplified_key_mode=0 --op_mode=dynamic "
build_cmd_var = "#!/bin/bash\n"
build_cmd_var += f'echo "[{self.soc}] Generating {bin_file} ..."\n'
plog_level = os.environ.get("ASCEND_GLOBAL_LOG_LEVEL")
plog_stdout = os.environ.get("ASCEND_SLOG_PRINT_TO_STDOUT")
if plog_level is None:
build_cmd_var += const_var.SET_PLOG_LEVEL_ERROR
if plog_stdout is None:
build_cmd_var += const_var.SET_PLOG_STDOUT
build_cmd_var += const_var.SRC_ENV
if hard_soc == "Ascend610Lite":
build_cmd_var += f"export ASCEND_CUSTOM_OPP_PATH={auto_gen_path}:$ASCEND_CUSTOM_OPP_PATH \n"
build_cmd_var += bin_cmd_str.format(
fun=self.op_intf, soc=hard_soc, param=param_file, impl="high_performance,optional"
)
enable_tiling_keys = False
if self.tiling_keys:
tiling_keys_list = sorted(list(self.tiling_keys))
tiling_key_str = ",".join([str(_key) for _key in tiling_keys_list])
build_cmd_var += f' --tiling_key="{tiling_key_str}"'
enable_tiling_keys = True
if self.op_debug_config:
op_debug_str = ",".join([str(_key) for _key in list(self.op_debug_config)])
build_cmd_var += f" --op_debug_config={op_debug_str}"
if super_mode and self.op_super_config:
op_super_config_str = " ".join([str(_key) for _key in list(self.op_super_config)])
build_cmd_var += f" {op_super_config_str}"
build_cmd_var += ")\n"
build_cmd_var += "\n"
check_result = self._generate_check_result(enable_tiling_keys, bin_file)
build_cmd_var += check_result
build_cmd_var += f'echo "[{self.soc}] Generating {bin_file} Done"\n'
with os.fdopen(os.open(compile_file, const_var.WFLAGS, const_var.WMODES), "w") as fd:
fd.write(build_cmd_var)
def get_tiling_keys(tiling_keys: str) -> set:
all_tiling_keys = set()
if not tiling_keys:
return all_tiling_keys
tiling_key_list = tiling_keys.split(";")
for tiling_key_value in tiling_key_list:
pattern = r"(?<![^\s])(\d+)-(\d+)(?![^\s])"
results = re.findall(pattern, tiling_key_value)
if results:
start, end = results[0]
if int(start) > int(end):
continue
for i in range(int(start), int(end) + 1):
all_tiling_keys.add(i)
elif tiling_key_value.isdigit():
all_tiling_keys.add(int(tiling_key_value))
return all_tiling_keys
def trans_soc_verion(soc_ver: str):
low_soc_ver = soc_ver.lower()
if low_soc_ver not in opdesc_parser.SOC_TO_SHORT_SOC_MAP:
return low_soc_ver
return opdesc_parser.SOC_TO_SHORT_SOC_MAP[low_soc_ver]
def parse_op_debug_confg(opc_config_file: str, soc: str) -> dict:
tiling_key_info = defaultdict(set)
op_debug_config = defaultdict(set)
if not opc_config_file:
return tiling_key_info, op_debug_config
if not os.path.exists(opc_config_file):
return tiling_key_info, op_debug_config
with open(opc_config_file) as file:
contents = file.readlines()
for _content in contents:
content = _content.strip()
opc_configs = content.split("@")
if len(opc_configs) < 3:
continue
op_type = opc_configs[0]
if not op_type:
continue
compute_unit = opc_configs[1]
if compute_unit:
compute_unit_list = compute_unit.split(";")
soc_lists = []
for soc_ver in compute_unit_list:
short_soc_ver = trans_soc_verion(soc_ver)
soc_lists.append(short_soc_ver)
if soc not in soc_lists:
continue
for options in opc_configs[2:]:
if "--tiling_key" in options:
format_tiling_keys = get_tiling_keys(options.split("=")[1])
if format_tiling_keys:
tiling_key_info[op_type].update(format_tiling_keys)
if "--op_debug_config" in options:
first_index = options.find("=")
if first_index != -1:
debug_config = options[first_index + 1 :]
else:
debug_config = ""
format_debug_config = set(debug_config.split(";"))
for _config in format_debug_config:
op_debug_config[op_type].add(_config)
return tiling_key_info, op_debug_config
def gen_bin_param_file(cfgfile: str, out_dir: str, soc: str, opc_config_file: str = "", ops: list = None):
if not os.path.exists(cfgfile):
print(f"INFO: {cfgfile} does not exists in this project, skip generating compile commands.")
return
debug_config = defaultdict(set)
super_config = defaultdict(set)
op_descs = opdesc_parser.get_op_desc(cfgfile, [], [], BinParamBuilder, ops)
tiling_key_info, op_debug_config = parse_op_debug_confg(opc_config_file, soc)
for _op_type, _op_option in op_debug_config.items():
for _option in _op_option:
if _option.startswith("--op_relocatable_kernel_binary") or _option.startswith("--op_super_kernel_options"):
super_config[_op_type].add(_option)
else:
debug_config[_op_type].add(_option)
auto_gen_path_dir = os.path.dirname(cfgfile)
all_soc_key = "ALL"
for op_desc in op_descs:
op_desc.set_soc_version(soc)
op_desc.set_out_path(out_dir)
if op_desc.op_type in debug_config:
op_desc.set_op_debug_config(debug_config[op_desc.op_type])
if all_soc_key in debug_config:
op_desc.set_op_debug_config(debug_config[all_soc_key])
if op_desc.op_type in super_config:
op_desc.set_op_super_config(super_config[op_desc.op_type])
if op_desc.op_type in tiling_key_info:
op_desc.set_tiling_key(tiling_key_info[op_desc.op_type])
if all_soc_key in tiling_key_info:
op_desc.set_tiling_key(tiling_key_info[all_soc_key])
op_desc.gen_input_json(auto_gen_path_dir)
def parse_args(argv):
"""Command line parameter parsing"""
parser = argparse.ArgumentParser()
parser.add_argument("argv", nargs="+")
parser.add_argument("--opc-config-file", nargs="?", const="", default="")
return parser.parse_args(argv)
if __name__ == "__main__":
args = parse_args(sys.argv)
if len(args.argv) <= 3:
raise RuntimeError("arguments must greater than 3")
gen_bin_param_file(args.argv[1], args.argv[2], args.argv[3], opc_config_file=args.opc_config_file)

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python3
# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------
import os
import stat
import sys
def write_options_to_file(file_name: str, options_str: str, op_type: str, compute_unit: str, split_char: str):
flags = os.O_WRONLY | os.O_CREAT
modes = stat.S_IWUSR | stat.S_IRUSR
try:
with os.fdopen(os.open(file_name, flags, modes), "a") as fd:
fd.write(op_type + split_char + compute_unit + split_char + options_str + "\n")
except Exception as err:
print("write compile options config file failed")
raise (err)
def gen_compile_options(compile_options_file: str, op_type: str, compute_unit: str, compile_options: list):
base_dir = os.path.dirname(compile_options_file)
opc_config_file = os.path.join(base_dir, "custom_opc_options.ini")
compile_opt = []
opc_debug_config = []
opc_tiling_keys = ""
for opts in compile_options:
if "oom" in opts:
if opts == "--oom":
opc_debug_config.append("oom")
else:
raise RuntimeError(f"Unknown oom option format {opts}")
elif "--save-temp-files" in opts:
opc_debug_config.append("dump_cce")
elif opts.startswith("--op_relocatable_kernel_binary") or opts.startswith("--op_super_kernel_options"):
opc_debug_config.append(opts)
elif "--tiling_key" in opts:
keys = opts.strip().split("=")[1].split(",")
keys_str = ";".join([key for key in keys])
opc_tiling_keys = keys_str
else:
compile_opt.append(opts)
if len(compile_opt) > 0:
options_str = ";".join([opt for opt in compile_opt])
write_options_to_file(compile_options_file, options_str, op_type, compute_unit, ",")
opc_config_str = ""
if opc_debug_config:
opc_config_str = "--op_debug_config=" + ";".join([opt for opt in opc_debug_config])
if len(opc_tiling_keys) > 0:
if opc_config_str != "":
opc_config_str += "@"
opc_config_str += "--tiling_key=" + opc_tiling_keys
if opc_config_str != "":
write_options_to_file(opc_config_file, opc_config_str, op_type, compute_unit, "@")
if __name__ == "__main__":
if len(sys.argv) < 4:
raise RuntimeError("arguments must greater than 4")
compute_soc = ""
comp_options = []
for i in range(len(sys.argv) - 3):
if sys.argv[i + 3].upper().startswith("ASCEND"):
compute_soc += sys.argv[i + 3] + ";"
else:
comp_options.append(sys.argv[i + 3])
if compute_soc != "":
compute_soc = compute_soc[0:-1]
gen_compile_options(sys.argv[1], sys.argv[2], compute_soc, comp_options)

View File

@@ -0,0 +1,781 @@
#!/usr/bin/env python3
# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------
import argparse
import datetime
import glob
import json
import os
import sys
import const_var
import opdesc_parser
import regex as re
PYF_PATH = os.path.dirname(os.path.realpath(__file__))
IMPL_HEAD = '''#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
Copyright (c) Huawei Technologies Co., Ltd. {}-{}. All rights reserved.
"""
import regex as re
import os, sys
import ctypes
import json
import shutil
from tbe.common.platform import get_soc_spec
from tbe.common.utils import para_check
from tbe.tikcpp import compile_op, replay_op, check_op_cap, generalize_op_params, get_code_channel, OpInfo
from tbe.tikcpp.compile_op import CommonUtility, AscendCLogLevel
from tbe.common.buildcfg import get_default_build_config
from tbe.common.buildcfg import get_current_build_config
import tbe.common.register as tbe_register
PYF_PATH = os.path.dirname(os.path.realpath(__file__))
DTYPE_MAP = {{"float32": ["DT_FLOAT", "float"],
"float16": ["DT_FLOAT16", "half"],
"int8": ["DT_INT8", "int8_t"],
"int16": ["DT_INT16", "int16_t"],
"int32": ["DT_INT32", "int32_t"],
"int64": ["DT_INT64", "int64_t"],
"uint1": ["DT_UINT1", "uint1b_t"],
"uint8": ["DT_UINT8", "uint8_t"],
"uint16": ["DT_UINT16", "uint16_t"],
"uint32": ["DT_UINT32", "uint32_t"],
"uint64": ["DT_UINT64", "uint64_t"],
"bool": ["DT_BOOL", "bool"],
"double": ["DT_DOUBLE", "double"],
"dual": ["DT_DUAL", "unknown"],
"dual_sub_int8": ["DT_DUAL_SUB_INT8", "unknown"],
"dual_sub_uint8": ["DT_DUAL_SUB_UINT8", "unknown"],
"string": ["DT_STRING", "unknown"],
"complex32": ["DT_COMPLEX32", "complex32"],
"complex64": ["DT_COMPLEX64", "complex64"],
"complex128": ["DT_COMPLEX128", "unknown"],
"qint8": ["DT_QINT8", "unknown"],
"qint16": ["DT_QINT16", "unknown"],
"qint32": ["DT_QINT32", "unknown"],
"quint8": ["DT_QUINT8", "unknown"],
"quint16": ["DT_QUINT16", "unknown"],
"resource": ["DT_RESOURCE", "unknown"],
"string_ref": ["DT_STRING_REF", "unknown"],
"int4": ["DT_INT4", "int4b_t"],
"bfloat16": ["DT_BF16", "bfloat16_t"],
"float8_e5m2": ["DT_FLOAT8_E5M2", "fp8_e5m2_t"],
"float8_e4m3fn": ["DT_FLOAT8_E4M3FN", "fp8_e4m3fn_t"],
"hifloat8":["DT_HIFLOAT8", "hifloat8_t"],
"float8_e8m0":["DT_FLOAT8_E8M0", "fp8_e8m0_t"],
"float4_e2m1":["DT_FLOAT4_E2M1", "fp4x2_e2m1_t"],
"float4_e1m2":["DT_FLOAT4_E1M2", "fp4x2_e1m2_t"],
"int2": ["DT_INT2", "int2b_t"]}}
def add_dtype_fmt_option_single(x, x_n, is_ref: bool = False):
options = []
x_fmt = x.get("format")
x_dtype = x.get("dtype")
x_n_in_kernel = x_n + '_REF' if is_ref else x_n
options.append("-DDTYPE_{{n}}={{t}}".format(n=x_n_in_kernel, t=DTYPE_MAP.get(x_dtype)[1]))
options.append("-DORIG_DTYPE_{{n}}={{orig_t}}".format(n=x_n_in_kernel, orig_t=DTYPE_MAP.get(x_dtype)[0]))
options.append("-DFORMAT_{{n}}=FORMAT_{{f}}".format(n=x_n_in_kernel, f=x_fmt))
return options
def get_dtype_fmt_options(__inputs__, __outputs__):
options = []
input_names = {}
output_names = {}
unique_param_name_set = set()
for idx, x in enumerate(__inputs__):
if x is None:
continue
x_n = input_names[idx].upper()
unique_param_name_set.add(x_n)
options += add_dtype_fmt_option_single(x, x_n)
for idx, x in enumerate(__outputs__):
if x is None:
continue
x_n = output_names[idx].upper()
if x_n in unique_param_name_set:
options += add_dtype_fmt_option_single(x, x_n, True)
else:
options += add_dtype_fmt_option_single(x, x_n)
return options
def load_dso(so_path):
try:
ctypes.CDLL(so_path)
except OSError as error :
CommonUtility.print_compile_log("", error, AscendCLogLevel.LOG_ERROR)
raise RuntimeError("cannot open %s" %(so_path))
else:
msg = "load so succ " + so_path
CommonUtility.print_compile_log("", msg, AscendCLogLevel.LOG_INFO)
def get_shortsoc_compile_option(compile_option_list: list, shortsoc:str):
compile_options = []
if shortsoc in compile_option_list:
compile_options.extend(compile_option_list[shortsoc])
if '__ALLSOC__' in compile_option_list:
compile_options.extend(compile_option_list['__ALLSOC__'])
return compile_options
def get_kernel_source(src_file, dir_snake, dir_ex):
src = os.path.join(PYF_PATH, "op_kernel", src_file)
if os.path.exists(src):
return src
src = os.path.join(PYF_PATH, "..", "ascendc", dir_snake, "op_kernel", src_file)
if os.path.exists(src):
return src
src_ex = os.path.join(PYF_PATH, "..", "ascendc", dir_ex, "op_kernel", src_file)
if os.path.exists(src_ex):
return src_ex
src_ex = os.path.join(PYF_PATH, "..", "ascendc", dir_ex, src_file)
if os.path.exists(src_ex):
return src_ex
src = os.environ.get('BUILD_KERNEL_SRC')
if src and os.path.exists(src):
return src
src = os.path.join(PYF_PATH, "..", "ascendc", dir_snake, src_file)
if os.path.exists(src):
return src
src = os.path.join(PYF_PATH, src_file)
if os.path.exists(src):
return src
src = os.path.join(PYF_PATH, "..", "ascendc", dir_snake, dir_snake + ".cpp")
if os.path.exists(src):
return src
src = os.path.join(PYF_PATH, "..", "ascendc", dir_ex, dir_ex + ".cpp")
if os.path.exists(src):
return src
src = os.path.join(PYF_PATH, "..", "ascendc", os.path.splitext(src_file)[0], src_file)
if os.path.exists(src):
return src
return src_ex
'''
IMPL_API = """
@tbe_register.register_operator("{}", trans_bool_to_s8=False)
@para_check.check_op_params({})
def {}({}, kernel_name="{}"{}):
{}
if get_current_build_config("enable_op_prebuild"):
return
__inputs__, __outputs__, __attrs__ = _build_args({})
options = get_dtype_fmt_options(__inputs__, __outputs__)
options += ["-x", "cce"]
bisheng = os.environ.get('BISHENG_REAL_PATH')
if bisheng is None:
bisheng = shutil.which("bisheng")
if bisheng != None:
bisheng_path = os.path.dirname(bisheng)
tikcpp_path = os.path.realpath(os.path.join(bisheng_path, "..", "..", "tikcpp"))
else:
tikcpp_path = os.path.realpath("/usr/local/Ascend/latest/compiler/tikcpp")
options.append("-I" + tikcpp_path)
options.append("-I" + os.path.join(tikcpp_path, "..", "..", "include"))
options.append("-I" + os.path.join(tikcpp_path, "tikcfw"))
options.append("-I" + os.path.join(tikcpp_path, "tikcfw", "impl"))
options.append("-I" + os.path.join(tikcpp_path, "tikcfw", "interface"))
options.append("-I" + os.path.join(tikcpp_path, "..", "ascendc", "act"))
options.append("-I" + os.path.join(PYF_PATH, "..", "ascendc", "common"))
toolkit_path = os.environ.get('ASCEND_HOME_PATH')
if toolkit_path is None:
toolkit_path = os.path.realpath("/usr/local/Ascend/latest/")
options.append("-I" + toolkit_path + os.path.join("/", os.uname().machine +"-linux", "asc", "atcos"))
op_common_path = os.path.realpath(toolkit_path + "/pkg_inc/op_common/")
options.append("-I" + op_common_path)
if "impl_mode" in locals():
if impl_mode == "high_performance":
options.append("-DHIGH_PERFORMANCE=1")
elif impl_mode == "high_precision":
options.append("-DHIGH_PRECISION=1")
elif "high_precision" in impl_mode and "high_performance" in impl_mode:
options.append("-DHIGH_PRECISION=1 -DHIGH_PERFORMANCE=1")
if get_current_build_config("enable_deterministic_mode") == 1:
options.append("-DDETERMINISTIC_MODE=1")
else:
options.append("-DDETERMINISTIC_MODE=0")
ascendc_api_version_header_path = os.path.join(tikcpp_path, "tikcfw/lib/ascendc_api_version.h")
if os.path.exists(ascendc_api_version_header_path):
with open(ascendc_api_version_header_path, "r") as ascendc_api_version_file:
ascendc_api_version = re.findall(r"#define ASCENDC_API_VERSION (\d+)", ascendc_api_version_file.read())
if ascendc_api_version:
options.append(f"-DASCENDC_API_VERSION={{ascendc_api_version[0]}}")
custom_compile_options = {},
custom_all_compile_options = {},
soc_version = get_soc_spec("SOC_VERSION")
soc_short = get_soc_spec("SHORT_SOC_VERSION").lower()
custom_compile_options_soc = get_shortsoc_compile_option(custom_compile_options[0], soc_short)
custom_all_compile_options_soc = get_shortsoc_compile_option(custom_all_compile_options[0], soc_short)
options += custom_all_compile_options_soc
options += custom_compile_options_soc
origin_func_name = "{}"
ascendc_src_dir_ex = "{}"
ascendc_src_dir = "{}"
ascendc_src_file = "{}"
src = get_kernel_source(ascendc_src_file, ascendc_src_dir, ascendc_src_dir_ex)
"""
REPLAY_OP_API = """
msg = "start replay Ascend C Operator {}, kernel name is {}"
CommonUtility.print_compile_log("", msg, AscendCLogLevel.LOG_INFO)
tikreplay_codegen_path = tikcpp_path + "/tikreplaylib/lib"
tikreplay_stub_path = tikcpp_path + "/tikreplaylib/lib/" + soc_version
msg = "start load libtikreplaylib_codegen.so and libtikreplaylib_stub.so"
CommonUtility.print_compile_log("", msg, AscendCLogLevel.LOG_INFO)
codegen_so_path = tikreplay_codegen_path + "/libtikreplaylib_codegen.so"
replaystub_so_path = tikreplay_stub_path + "/libtikreplaylib_stub.so"
if PYF_PATH.endswith("dynamic"):
op_replay_path = os.path.join(PYF_PATH, "..", "..", "op_replay")
else:
op_replay_path = os.path.join(PYF_PATH, "..", "op_replay")
replayapi_so_path = os.path.join(op_replay_path, "libreplay_{}_" + soc_short + ".so")
load_dso(codegen_so_path)
load_dso(replaystub_so_path)
load_dso(replayapi_so_path)
op_type = "{}"
entry_obj = os.path.join(op_replay_path, "{}_entry_" + soc_short + ".o")
code_channel = get_code_channel(src, kernel_name, op_type, options)
op_info = OpInfo(kernel_name = kernel_name, op_type = op_type, inputs = __inputs__, outputs = __outputs__,\\
attrs = __attrs__, impl_mode = impl_mode, param_type_dynamic = {})
res, msg = replay_op(op_info, entry_obj, code_channel, src, options)
if not res:
print("call replay op failed for %s and get into call compile op" %(msg))
compile_op(src, origin_func_name, op_info, options, code_channel, '{}')
"""
COMPILE_OP_API = """
msg = "start compile Ascend C Operator {}, kernel name is " + kernel_name
CommonUtility.print_compile_log("", msg, AscendCLogLevel.LOG_INFO)
op_type = "{}"
code_channel = get_code_channel(src, kernel_name, op_type, options)
op_info = OpInfo(kernel_name = kernel_name, op_type = op_type, inputs = __inputs__, outputs = __outputs__,\\
attrs = __attrs__ {}, origin_inputs=[{}], origin_outputs = [{}],\\
param_type_dynamic = {}, mc2_ctx = {}, param_type_list = {}, init_value_list = {},\\
output_shape_depend_on_compute = {})
compile_op(src, origin_func_name, op_info, options, code_channel, '{}', {})
"""
COMPILE_OP_API_BUILT_IN = """
msg = "start compile Ascend C Operator {}, kernel name is " + kernel_name
CommonUtility.print_compile_log("", msg, AscendCLogLevel.LOG_INFO)
op_type = "{}"
code_channel = get_code_channel(src, kernel_name, op_type, options)
op_info = OpInfo(kernel_name = kernel_name, op_type = op_type, inputs = __inputs__, outputs = __outputs__,\\
attrs = __attrs__ {}, origin_inputs=[{}], origin_outputs = [{}],\\
param_type_dynamic = {}, mc2_ctx = {}, param_type_list = {}, init_value_list = {},\\
output_shape_depend_on_compute = {})
op_compile_option = '{}'
opp_path = os.environ.get('ASCEND_OPP_PATH')
dat_path = os.path.realpath(os.path.join(opp_path, "built-in", "op_impl", "ai_core", "tbe", "ascendc_impl.dat"))
if opp_path and os.path.exists(dat_path):
# dat file exists: built in hidden src file online compiling process. append vfs compile option in compile_op
abs_rel_kernel_src_path = "{}"
extend_options = {}
extend_options['opp_kernel_hidden_dat_path'] = dat_path
compile_op(abs_rel_kernel_src_path, origin_func_name, op_info, options, code_channel, op_compile_option,\\
extend_options)
else:
raise RuntimeError("built-in opp compile, ascendc_impl.dat file path does not exist: %s" %(dat_path))
"""
SUP_API = """
def {}({}{}):
__inputs__, __outputs__, __attrs__ = _build_args({})
ret_str = check_op_cap("{}", "{}", __inputs__, __outputs__, __attrs__)
ret_dict = json.loads(ret_str)
err_code = ret_dict.get("ret_code")
sup = "Unknown"
reason = "Unknown reason"
if err_code is not None:
if err_code == 0:
sup = "True"
reason = ""
elif err_code == 1:
sup = "False"
reason = ret_dict.get("reason")
else:
sup = "Unknown"
reason = ret_dict.get("reason")
return sup, reason
"""
CAP_API = """
def {}({}{}):
__inputs__, __outputs__, __attrs__ = _build_args({})
result = check_op_cap("{}", "{}", __inputs__, __outputs__, __attrs__)
return result.decode("utf-8")
"""
GLZ_API = """
@tbe_register.register_param_generalization("{}")
def {}_generalization({}, generalize_config=None):
__inputs__, __outputs__, __attrs__ = _build_args({})
ret_str = generalize_op_params("{}", __inputs__, __outputs__, __attrs__, generalize_config)
return [json.loads(ret_str)]
"""
ATTR_DEFAULT = {
"bool": "False",
"int": "0",
"float": "0.0",
"list_int": "[]",
"list_float": "[]",
"list_bool": "[]",
"list_list_int": "[[]]",
"str": "",
}
def optype_snake(origin_str):
temp_str = origin_str[0].lower() + origin_str[1:]
new_str = re.sub(r"([A-Z])", r"_\1", temp_str).lower()
return new_str
def optype_snake_ex(s):
snake_case = ""
for i, c in enumerate(s):
if i == 0:
snake_case += c.lower()
elif c.isupper():
if s[i - 1] != "_":
if not s[i - 1].isupper() or s[i - 1].isupper() and (i + 1) < len(s) and s[i + 1].islower():
snake_case += "_"
snake_case += c.lower()
else:
snake_case += c
return snake_case
class AdpBuilder(opdesc_parser.OpDesc):
def __init__(self: any, op_type: str):
self.argsdefv = []
self.op_compile_option: str = "{}"
super().__init__(op_type)
def write_adapt(self: any, impl_path, path: str, op_compile_option_all: list = None):
self._build_paradefault()
if os.environ.get("BUILD_BUILTIN_OPP") != "1" and impl_path != "":
src_file = os.path.join(impl_path, self.op_file + ".cpp")
if not os.path.exists(src_file):
print(f"[ERROR]: operator: {self.op_file} source file: {src_file} does not found, please check.")
return
out_path = os.path.abspath(path)
if self.dynamic_shape and not out_path.endswith("dynamic"):
out_path = os.path.join(path, "dynamic")
os.makedirs(out_path, exist_ok=True)
adpfile = os.path.join(out_path, self.op_file + ".py")
self._gen_op_compile_option(op_compile_option_all)
with os.fdopen(os.open(adpfile, const_var.WFLAGS, const_var.WMODES), "w") as fd:
self._write_head(fd)
self._write_argparse(fd)
self._get_impl_mode()
self._write_impl(fd, impl_path)
if self.op_chk_support:
self._write_cap("check_supported", fd)
self._write_cap("get_op_support_info", fd)
if self.op_fmt_sel:
self._write_cap("op_select_format", fd)
self._write_cap("get_op_specific_info", fd)
if self.op_range_limit == "limited" or self.op_range_limit == "dynamic":
self._write_glz(fd)
def _gen_op_compile_option(self: any, op_compile_option_all: list = None):
if op_compile_option_all is not None:
if self.op_type in op_compile_option_all:
self.op_compile_option = op_compile_option_all[self.op_type]
elif "__all__" in op_compile_option_all:
self.op_compile_option = op_compile_option_all["__all__"]
def _ip_argpack(self: any, default: bool = True) -> list:
args = []
for i in range(len(self.input_name)):
arg = self.input_name[i]
if default and self.argsdefv[i] is not None:
arg += "=" + self.argsdefv[i]
args.append(arg)
return args
def _op_argpack(self: any, default: bool = True) -> list:
args = []
argidx = len(self.input_name)
for i in range(len(self.output_name)):
arg = self.output_name[i]
if default and self.argsdefv[i + argidx] is not None:
arg += "=" + self.argsdefv[i + argidx]
args.append(arg)
return args
def _attr_argpack(self: any, default: bool = True) -> list:
args = []
argidx = len(self.input_name) + len(self.output_name)
for i in range(len(self.attr_list)):
att = self.attr_list[i]
arg = att
if default and self.argsdefv[i + argidx] is not None:
if self.attr_val.get(att).get("type") == "str":
arg += '="' + self.argsdefv[i + argidx] + '"'
elif self.attr_val.get(att).get("type") == "bool":
arg += "=" + self.argsdefv[i + argidx].capitalize()
elif self.attr_val.get(att).get("type") == "list_bool":
arg += (
"="
+ "["
+ ", ".join(
word.strip().capitalize() for word in self.argsdefv[i + argidx].strip("[]").split(",")
)
+ "]"
)
else:
arg += "=" + self.argsdefv[i + argidx]
args.append(arg)
return args
def _build_paralist(self: any, default: bool = True) -> str:
args = []
args.extend(self._ip_argpack(default))
args.extend(self._op_argpack(default))
args.extend(self._attr_argpack(default))
return ", ".join(args)
def _io_parachk(self: any, types: list, type_name: str) -> list:
chk = []
for iot in types:
if iot == "optional":
ptype = "OPTION"
else:
ptype = iot.upper()
chk.append("para_check.{}_{}".format(ptype, type_name))
return chk
def _attr_parachk(self: any) -> list:
chk = []
for att in self.attr_list:
att_type = self.attr_val.get(att).get("type").upper()
chk.append("para_check.{}_ATTR_{}".format("OPTION", att_type))
return chk
def _build_parachk(self: any) -> str:
chk = []
chk.extend(self._io_parachk(self.input_type, "INPUT"))
chk.extend(self._io_parachk(self.output_type, "OUTPUT"))
chk.extend(self._attr_parachk())
chk.append("para_check.KERNEL_NAME")
return ", ".join(chk)
def _build_virtual(self: any) -> str:
virt_exp = []
for index in range(len(self.input_name)):
if self.input_virt.get(index) is None:
continue
val = []
val.append('"param_name":"{}"'.format(self.input_name[index]))
val.append('"index":{}'.format(index))
val.append('"dtype":"{}"'.format(self.input_dtype[index].split(",")[0]))
val.append('"format":"{}"'.format(self.input_fmt[index].split(",")[0]))
val.append('"ori_format":"{}"'.format(self.input_fmt[index].split(",")[0]))
val.append('"paramType":"optional"')
val.append('"shape":[1]')
val.append('"ori_shape":[1]')
virt_exp.append(" " + self.input_name[index] + " = {" + ",".join(val) + "}")
if len(virt_exp) > 0:
return "\n".join(virt_exp)
else:
return " # do ascendc build step"
def _build_mc2_ctx(self: any):
if len(self.mc2_ctx) != 0:
return '["' + '", "'.join(self.mc2_ctx) + '"]'
return "[]"
def _build_paradefault(self: any):
optional = False
argtypes = []
argtypes.extend(self.input_type)
argtypes.extend(self.output_type)
for atype in argtypes:
if atype == "optional":
optional = True
if optional:
self.argsdefv.append("None")
else:
self.argsdefv.append(None)
for attr in self.attr_list:
atype = self.attr_val.get(attr).get("paramType")
if atype == "optional":
optional = True
attrval = self.attr_val.get(attr).get("defaultValue")
if attrval is not None:
optional = True
if atype == "bool":
attrval = attrval.capitalize()
elif atype == "str":
attrval = '"' + attrval + '"'
self.argsdefv.append(attrval)
continue
if optional:
self.argsdefv.append(ATTR_DEFAULT.get(self.attr_val.get(attr).get("type")))
else:
self.argsdefv.append(None)
def _write_head(self: any, fd: object):
now = datetime.datetime.now()
curr_year = now.year
former_year = curr_year - 1
fd.write(IMPL_HEAD.format(former_year, curr_year, self.input_ori_name, self.output_ori_name))
def _write_argparse(self: any, fd: object):
args = self._build_paralist(False)
fd.write("def _build_args({}):\n".format(args))
fd.write(" __inputs__ = []\n")
fd.write(" for arg in [{}]:\n".format(", ".join(self.input_name)))
fd.write(" if arg != None:\n")
fd.write(" if isinstance(arg, (list, tuple)):\n")
fd.write(" if len(arg) == 0:\n")
fd.write(" continue\n")
fd.write(" __inputs__.append(arg[0])\n")
fd.write(" else:\n")
fd.write(" __inputs__.append(arg)\n")
fd.write(" else:\n")
fd.write(" __inputs__.append(arg)\n")
fd.write(" __outputs__ = []\n")
fd.write(" for arg in [{}]:\n".format(", ".join(self.output_name)))
fd.write(" if arg != None:\n")
fd.write(" if isinstance(arg, (list, tuple)):\n")
fd.write(" if len(arg) == 0:\n")
fd.write(" continue\n")
fd.write(" __outputs__.append(arg[0])\n")
fd.write(" else:\n")
fd.write(" __outputs__.append(arg)\n")
fd.write(" else:\n")
fd.write(" __outputs__.append(arg)\n")
fd.write(" __attrs__ = []\n")
for attr in self.attr_list:
fd.write(" if {} != None:\n".format(attr))
fd.write(" attr = {}\n")
fd.write(' attr["name"] = "{}"\n'.format(attr))
fd.write(' attr["dtype"] = "{}"\n'.format(self.attr_val.get(attr).get("type")))
fd.write(' attr["value"] = {}\n'.format(attr))
fd.write(" __attrs__.append(attr)\n")
fd.write(" return __inputs__, __outputs__, __attrs__\n")
def _get_kernel_source(self: any, kernel_src_dir, src_file, dir_snake, dir_ex):
src = os.path.join(kernel_src_dir, "op_kernel", src_file)
if os.path.exists(src):
return src
src = os.path.join(kernel_src_dir, "..", "ascendc", dir_snake, "op_kernel", src_file)
if os.path.exists(src):
return src
src_ex = os.path.join(kernel_src_dir, "..", "ascendc", dir_ex, "op_kernel", src_file)
if os.path.exists(src_ex):
return src_ex
src_ex = os.path.join(kernel_src_dir, dir_ex, src_file)
if os.path.exists(src_ex):
return src_ex
src = os.environ.get("BUILD_KERNEL_SRC")
if src and os.path.exists(src):
return src
src = os.path.join(kernel_src_dir, dir_snake, src_file)
if os.path.exists(src):
return src
src = os.path.join(kernel_src_dir, src_file)
if os.path.exists(src):
return src
src = os.path.join(kernel_src_dir, dir_snake, dir_snake + ".cpp")
if os.path.exists(src):
return src
src = os.path.join(kernel_src_dir, dir_ex, dir_ex + ".cpp")
if os.path.exists(src):
return src
src = os.path.join(kernel_src_dir, os.path.splitext(src_file)[0], src_file)
if os.path.exists(src):
return src
return src_ex
def _get_impl_mode(self: any):
op_compile_options = json.loads(self.op_compile_option)
if "impl_mode" in op_compile_options:
if op_compile_options["impl_mode"] == "":
self.impl_mode = ""
self.impl_mode_op_info = ""
del op_compile_options["impl_mode"]
self.op_compile_option = json.dumps(op_compile_options)
else:
self.impl_mode = ", impl_mode ='" + op_compile_options["impl_mode"] + "'"
self.impl_mode_op_info = ", impl_mode ='" + op_compile_options["impl_mode"] + "'"
else:
self.impl_mode = ', impl_mode = ""'
self.impl_mode_op_info = ", impl_mode = impl_mode"
def _write_impl(self: any, fd: object, impl_path: str = ""):
argsdef = self._build_paralist()
argsval = self._build_paralist(False)
pchk = self._build_parachk()
if len(self.kern_name) > 0:
kern_name = self.kern_name
else:
kern_name = self.op_intf
src = self.op_file + ".cpp"
virt_exprs = self._build_virtual()
fd.write(
IMPL_API.format(
self.op_type,
pchk,
self.op_intf,
argsdef,
kern_name,
self.impl_mode,
virt_exprs,
argsval,
self.custom_compile_options,
self.custom_all_compile_options,
self.op_intf,
optype_snake_ex(self.op_type),
optype_snake(self.op_type),
src,
)
)
if self.op_replay_flag:
fd.write(
REPLAY_OP_API.format(
self.op_type,
kern_name,
self.op_file,
self.op_type,
self.op_file,
self.param_type_dynamic,
self.op_compile_option,
)
)
else:
value_depend_obj = {key: value for key, value in self.input_value_depend.items()}
extend_opt = {"valueDepend": value_depend_obj}
if os.environ.get("BUILD_BUILTIN_OPP") == "1":
relative_kernel_src_path = os.path.realpath(
self._get_kernel_source(impl_path, src, optype_snake(self.op_type), optype_snake_ex(self.op_type))
)
# to match src path in .dat file system, turn relative path into absolute path
abs_rel_kernel_src_path = os.path.join("/", os.path.relpath(relative_kernel_src_path, impl_path))
# compiling hidden src file requires src path before packaging .dat file,
# hard code such src path to <op_type>.py
fd.write(
COMPILE_OP_API_BUILT_IN.format(
self.op_type,
self.op_type,
self.impl_mode_op_info,
", ".join(self.input_name),
", ".join(self.output_name),
self.param_type_dynamic,
self._build_mc2_ctx(),
self.input_type + self.output_type,
self.output_init_value,
self.output_shape_depend_on_compute,
self.op_compile_option,
abs_rel_kernel_src_path,
repr(extend_opt),
)
)
else:
fd.write(
COMPILE_OP_API.format(
self.op_type,
self.op_type,
self.impl_mode_op_info,
", ".join(self.input_name),
", ".join(self.output_name),
self.param_type_dynamic,
self._build_mc2_ctx(),
self.input_type + self.output_type,
self.output_init_value,
self.output_shape_depend_on_compute,
self.op_compile_option,
repr(extend_opt),
)
)
def _write_cap(self: any, cap_name: str, fd: object):
argsdef = self._build_paralist()
argsval = self._build_paralist(False)
if cap_name == "check_supported":
fd.write(SUP_API.format(cap_name, argsdef, self.impl_mode, argsval, cap_name, self.op_type))
else:
fd.write(CAP_API.format(cap_name, argsdef, self.impl_mode, argsval, cap_name, self.op_type))
def _write_glz(self: any, fd: object):
argsdef = self._build_paralist()
argsval = self._build_paralist(False)
fd.write(GLZ_API.format(self.op_type, self.op_intf, argsdef, argsval, self.op_type))
def write_scripts(cfgfile: str, cfgs: dict, dirs: dict, ops: list = None, op_compile_option: list = None):
batch_lists = cfgs.get(const_var.REPLAY_BATCH).split(";")
iterator_lists = cfgs.get(const_var.REPLAY_ITERATE).split(";")
file_map = {}
op_descs = opdesc_parser.get_op_desc(
cfgfile, batch_lists, iterator_lists, AdpBuilder, ops, dirs.get(const_var.AUTO_GEN_DIR)
)
for op_desc in op_descs:
op_desc.write_adapt(dirs.get(const_var.CFG_IMPL_DIR), dirs.get(const_var.CFG_OUT_DIR), op_compile_option)
file_map[op_desc.op_type] = op_desc.op_file
return file_map
class OpFileNotExistsError(Exception):
"""File does not exist error."""
def __str__(self) -> str:
return f"File aic-*-ops-info.ini does not exist in directory {super().__str__()}"
def get_ops_info_files(opsinfo_dir: list[str]) -> list[str]:
"""Get all ops info files."""
ops_info_files = []
for _dir in opsinfo_dir:
ops_info_files.extend(glob.glob(f"{_dir}/aic-*-ops-info.ini"))
return sorted(ops_info_files)
def parse_args(argv):
"""Command line parameter parsing"""
parser = argparse.ArgumentParser()
parser.add_argument("argv", nargs="+")
parser.add_argument("--opsinfo-dir", nargs="*", default=None)
return parser.parse_args(argv)
if __name__ == "__main__":
args = parse_args(sys.argv)
if len(args.argv) <= 6:
raise RuntimeError("arguments must greater equal than 6")
rep_cfg = {}
rep_cfg[const_var.REPLAY_BATCH] = args.argv[2]
rep_cfg[const_var.REPLAY_ITERATE] = args.argv[3]
cfg_dir = {}
cfg_dir[const_var.CFG_IMPL_DIR] = args.argv[4]
cfg_dir[const_var.CFG_OUT_DIR] = args.argv[5]
cfg_dir[const_var.AUTO_GEN_DIR] = args.argv[6]
ops_infos = []
if args.opsinfo_dir:
ops_infos.extend(get_ops_info_files(args.opsinfo_dir))
if not ops_infos:
raise OpFileNotExistsError(args.opsinfo_dir)
else:
ops_infos.append(args.argv[1])
for ops_info in ops_infos:
write_scripts(cfgfile=ops_info, cfgs=rep_cfg, dirs=cfg_dir)

View File

@@ -0,0 +1,360 @@
#!/usr/bin/env python3
# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------
import argparse
import glob
import json
import os
import sys
from typing import NamedTuple
import const_var
class OpConfig(NamedTuple):
op_type: str
support_info: dict
core_type: str
task_ration: str
obj_file: str
def load_json(json_file: str):
with open(json_file, encoding="utf-8") as file:
json_content = json.load(file)
return json_content
def get_specified_suffix_file(root_dir, suffix):
specified_suffix = os.path.join(root_dir, "**/*{}".format(suffix))
all_suffix_files = glob.glob(specified_suffix, recursive=True)
return sorted(all_suffix_files)
def add_dict_key(dict_to_add, key, value):
if value is None:
return
dict_to_add[key] = value
def correct_format_mode(format_mode):
if format_mode == "FormatDefault":
return "nd_agnostic"
if format_mode == "FormatAgnostic":
return "static_nd_agnostic"
if format_mode == "FormatFixed":
return "normal"
return format_mode
def get_input_or_output_config(in_or_out):
param_dict = {}
name = in_or_out.get("name")
index = in_or_out.get("index")
param_type = in_or_out.get("paramType")
format_match_mode = in_or_out.get("format_match_mode")
format_mode = correct_format_mode(format_match_mode)
dtype_mode = in_or_out.get("dtype_match_mode")
if dtype_mode == "DtypeByte":
dtype_mode = "bit"
add_dict_key(param_dict, "name", name)
add_dict_key(param_dict, "index", index)
add_dict_key(param_dict, "paramType", param_type)
add_dict_key(param_dict, "dtypeMode", dtype_mode)
add_dict_key(param_dict, "formatMode", format_mode)
return param_dict
def get_inputs_or_outputs_config(inputs_or_outputs):
if inputs_or_outputs is None:
return None
inputs_or_outputs_list = []
for in_or_out in inputs_or_outputs:
if isinstance(in_or_out, dict):
dict_param_config = get_input_or_output_config(in_or_out)
inputs_or_outputs_list.append(dict_param_config)
elif isinstance(in_or_out, list):
param_info = in_or_out[0]
list_param_config = get_input_or_output_config(param_info)
tmp_list = [list_param_config]
inputs_or_outputs_list.append(tmp_list)
return inputs_or_outputs_list
def gen_attrs_config(attrs):
attrs_list = []
for attr in attrs:
attrs_dict = {}
name = attr.get("name")
mode = attr.get("mode")
add_dict_key(attrs_dict, "name", name)
add_dict_key(attrs_dict, "mode", mode)
attrs_list.append(attrs_dict)
return attrs_list
def get_params_config(support_info):
params_dict = {}
inputs = support_info.get("inputs")
inputs_list = get_inputs_or_outputs_config(inputs)
params_dict["inputs"] = inputs_list
outputs = support_info.get("outputs")
outputs_list = get_inputs_or_outputs_config(outputs)
params_dict["outputs"] = outputs_list
attrs = support_info.get("attrs")
if attrs is not None:
attrs_list = gen_attrs_config(attrs)
params_dict["attrs"] = attrs_list
return params_dict
def add_simplified_config(op_info, binary_info_config, config):
simplified_key = op_info.support_info.get("simplifiedKey")
json_path = op_info.obj_file.split(".")[0] + ".json"
simple_cfg = config.get(binary_info_config)
op_cfg = simple_cfg.get(op_info.op_type)
if not op_cfg:
op_cfg = {"dynamicRankSupport": True}
simplified_key_mode = op_info.support_info.get("simplifiedKeyMode")
add_dict_key(op_cfg, "simplifiedKeyMode", simplified_key_mode)
optional_input_mode = op_info.support_info.get("optionalInputMode")
optional_output_mode = op_info.support_info.get("optionalOutputMode")
add_dict_key(op_cfg, "optionalInputMode", optional_input_mode)
if optional_output_mode is not None:
add_dict_key(op_cfg, "optionalOutputMode", optional_output_mode)
params_info = get_params_config(op_info.support_info)
op_cfg["params"] = params_info
op_cfg["binaryList"] = []
simple_cfg[op_info.op_type] = op_cfg
bin_list = op_cfg.get("binaryList")
if op_info.core_type == 0 and op_info.task_ration == "tilingKey":
bin_list.append(
{
"coreType": op_info.core_type,
"simplifiedKey": simplified_key,
"multiKernelType": 1,
"binPath": op_info.obj_file,
"jsonPath": json_path,
}
)
else:
bin_list.append(
{
"coreType": op_info.core_type,
"simplifiedKey": simplified_key,
"binPath": op_info.obj_file,
"jsonPath": json_path,
}
)
def add_op_config(op_file, bin_info, config):
op_cfg = config.get(op_file)
if not op_cfg:
op_cfg = {"binList": []}
config[op_file] = op_cfg
op_cfg.get("binList").append(bin_info)
def gen_ops_config(json_file, soc, binary_info_config, config):
core_type_map = {"MIX": 0, "AiCore": 1, "VectorCore": 2, "MIX_AICORE": 3, "MIX_VECTOR_CORE": 4, "MIX_AIV": 4}
contents = load_json(json_file)
if ("binFileName" not in contents) or ("supportInfo" not in contents):
return
json_base_name = os.path.basename(json_file)
op_dir = os.path.basename(os.path.dirname(json_file))
support_info = contents.get("supportInfo")
bin_name = contents.get("binFileName")
bin_suffix = contents.get("binFileSuffix")
core_type = contents.get("coreType")
task_ration = contents.get("taskRation")
core_type = core_type_map.get(core_type, -1)
if core_type == -1 and soc != "ascend310b":
raise Exception("[ERROR]: must set coreType in json when soc version is {soc}.")
bin_file_name = bin_name + bin_suffix
op_type = bin_name.split("_")[0]
op_file = op_dir + ".json"
bin_info = {}
add_dict_key(bin_info, "implMode", support_info.get("implMode"))
add_dict_key(bin_info, "int64Mode", support_info.get("int64Mode"))
add_dict_key(bin_info, "simplifiedKeyMode", support_info.get("simplifiedKeyMode"))
simplified_key = support_info.get("simplifiedKey")
if simplified_key is not None:
bin_info["simplifiedKey"] = simplified_key
obj_file = os.path.join(soc, op_dir, bin_file_name)
op_info = OpConfig(
op_type=op_type,
support_info=support_info,
core_type=core_type,
task_ration=task_ration,
obj_file=obj_file,
)
add_simplified_config(op_info, binary_info_config, config)
add_dict_key(bin_info, "dynamicParamMode", support_info.get("dynamicParamMode"))
bin_info["staticKey"] = support_info.get("staticKey")
bin_info["inputs"] = support_info.get("inputs")
bin_info["outputs"] = support_info.get("outputs")
if support_info.get("attrs"):
bin_info["attrs"] = support_info.get("attrs")
add_dict_key(bin_info, "opMode", support_info.get("opMode"))
add_dict_key(bin_info, "optionalInputMode", support_info.get("optionalInputMode"))
add_dict_key(bin_info, "deterministic", support_info.get("deterministic"))
if support_info.get("optionalOutputMode") is not None:
add_dict_key(bin_info, "optionalOutputMode", support_info.get("optionalOutputMode"))
bin_info["binInfo"] = {"jsonFilePath": os.path.join(soc, op_dir, json_base_name)}
add_op_config(op_file, bin_info, config)
def check_single_op_is_void(root_dir):
for root, dirs, _ in os.walk(root_dir):
for sub_dir in dirs:
dir_path = os.path.join(root, sub_dir)
if len(os.listdir(dir_path)) == 0:
print(f"[ERROR] op {sub_dir}: not any obj compile success")
sys.exit(1)
def write_jsons(out_dir, file_list, config):
for json_name in file_list:
json_file = os.path.join(out_dir, json_name)
with os.fdopen(os.open(json_file, const_var.WFLAGS, const_var.WMODES), "w") as fd:
json.dump(config.get(json_name), fd, indent=" ")
def generate_operator_cfg_file(json_files, binary_info_config, soc, out_dir, gen_json_status):
if not json_files:
return
if gen_json_status == "not_generated":
return
json_files.sort()
config = {binary_info_config: {}}
for _json in json_files:
gen_ops_config(_json, soc, binary_info_config, config)
if gen_json_status == "single_json":
file_list = [json_file for json_file in config if json_file != binary_info_config]
elif gen_json_status == "summary_json":
file_list = [binary_info_config]
else:
file_list = config.keys()
write_jsons(out_dir, file_list, config)
def gen_all_config(root_dir, soc, out_dir, skip_binary_info_config, op_range="all"):
if op_range != "relocatable":
check_single_op_is_void(root_dir)
all_json_files = get_specified_suffix_file(root_dir, ".json")
relocatable_json_files = get_specified_suffix_file(root_dir, "_relocatable.json")
normal_json_files = list(set(all_json_files) - set(relocatable_json_files))
os.makedirs(out_dir, exist_ok=True)
if op_range != "relocatable":
for _json in all_json_files:
file_path = soc + _json.split(soc, maxsplit=1)[1]
with open(_json, "r+") as f:
data = json.load(f)
data["filePath"] = file_path
f.seek(0)
json.dump(data, f, indent=" ")
f.truncate()
if skip_binary_info_config:
gen_normale_json = "single_json"
gen_relocatable_json = "not_generated"
else:
gen_normale_json = "all_json"
gen_relocatable_json = "summary_json"
# normal kernel
if op_range == "all" or op_range == "normal":
binary_info_config = "binary_info_config.json"
generate_operator_cfg_file(normal_json_files, binary_info_config, soc, out_dir, gen_normale_json)
# relocatable kernel
if op_range == "all" or op_range == "relocatable":
binary_info_config = "relocatable_kernel_info_config.json"
generate_operator_cfg_file(relocatable_json_files, binary_info_config, soc, out_dir, gen_relocatable_json)
# Parse multiple soc_versions ops in single path.
def gen_all_soc_config(all_path):
soc_roots = glob.glob(os.path.join(all_path, "ascend*"))
for soc_root in soc_roots:
soc = os.path.basename(soc_root)
gen_all_config(soc_root, soc, soc_root, True)
cfg_files = glob.glob(os.path.join(soc_root, "*.json"))
cfg_path = os.path.join(all_path, "config", soc)
os.makedirs(cfg_path, exist_ok=True)
for cfg_file in cfg_files:
new_file = os.path.join(cfg_path, os.path.basename(cfg_file))
os.rename(cfg_file, new_file)
def args_prase():
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--path", nargs="?", required=True, help="Parse the path of the json file.")
parser.add_argument("-s", "--soc", nargs="?", required=True, help="Parse the soc_version of ops.")
parser.add_argument("-o", "--out", nargs="?", help="Output directory.")
parser.add_argument(
"--skip-binary-info-config", action="store_true", help="binary_info_config.json file is not parsed."
)
parser.add_argument(
"--op-range",
type=str,
choices=["all", "normal", "relocatable"],
default="all",
help="all operators/normal operators/relocatable operators.",
)
return parser.parse_args()
def main():
args = args_prase()
if args.out is None:
out_dir = args.path
else:
out_dir = args.out
gen_all_config(args.path, args.soc, out_dir, args.skip_binary_info_config, args.op_range)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,71 @@
#!/usr/bin/env python3
# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------
import os
import stat
REPLAY_BATCH = "batch"
REPLAY_ITERATE = "iterate"
CFG_IMPL_DIR = "impl_dir"
CFG_OUT_DIR = "out_dir"
AUTO_GEN_DIR = "auto_gen_dir"
WFLAGS = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
WMODES = stat.S_IWUSR | stat.S_IRUSR
SOC_MAP_EXT = {
"ascend310p": "Ascend310P3",
"ascend310b": "Ascend310B1",
"ascend910": "Ascend910A",
"ascend910b": "Ascend910B1",
"ascend910_93": "Ascend910_9391",
"ascend610lite": "Ascend610Lite",
"ascend950": "Ascend950PR_9599",
"kirinx90": "KirinX90",
}
BIN_CMD = "opc $1 --main_func={fun} --input_param={param} --soc_version={soc} \
--output=$2 --impl_mode={impl} --simplified_key_mode=0 --op_mode=dynamic\n"
SET_PLOG_LEVEL_ERROR = "export ASCEND_GLOBAL_LOG_LEVEL=3\n"
SET_PLOG_STDOUT = "export ASCEND_SLOG_PRINT_TO_STDOUT=1\n"
SRC_ENV = """
while true; do
case "$1" in
--kernel-src=*)
export BUILD_KERNEL_SRC=$(echo "$1" | cut -d"=" -f2-)
shift
;;
-*)
shift
;;
*)
break
;;
esac
done
"""
CHK_CMD = """
if ! test -f $2/{res_file} ; then
echo "$2/{res_file} not generated!"
exit 1
fi
"""
ATTR_DEF_VAL = {
"str": "",
"int": 0,
"float": 0.0,
"bool": False,
"list_bool": [],
"list_int": [],
"list_float": [],
"list_list_int": [[]],
}
def conv_soc_ver(ver: str):
return SOC_MAP_EXT.get(ver)

View File

@@ -0,0 +1,18 @@
#!/bin/bash\n"
# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------
ascend_install_dir=$1
gen_file_dir=$2
# create version.info
compiler_version=$(grep "Version" -w ${ascend_install_dir}/compiler/version.info | awk -F = '{print $2}')
echo "custom_opp_compiler_version=${compiler_version}" > ${gen_file_dir}/version.info

View File

@@ -0,0 +1,409 @@
#!/usr/bin/env python3
# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------
import os
OP_ALL = "__ALLOP__"
SOC_ALL = "__ALLSOC__"
SOC_TO_SHORT_SOC_MAP = {
"ascend910a": "ascend910",
"ascend910proa": "ascend910",
"ascend910b": "ascend910",
"ascend910prob": "ascend910",
"ascend910premiuma": "ascend910",
"ascend910b1": "ascend910b",
"ascend910b2": "ascend910b",
"ascend910b2c": "ascend910b",
"ascend910b3": "ascend910b",
"ascend910b4": "ascend910b",
"ascend910b4-1": "ascend910b",
"ascend910_9391": "ascend910_93",
"ascend910_9381": "ascend910_93",
"ascend910_9372": "ascend910_93",
"ascend910_9392": "ascend910_93",
"ascend910_9382": "ascend910_93",
"ascend910_9362": "ascend910_93",
"ascend310p1": "ascend310p",
"ascend310p3": "ascend310p",
"ascend310p5": "ascend310p",
"ascend310p7": "ascend310p",
"ascend310p3vir01": "ascend310p",
"ascend310p3vir02": "ascend310p",
"ascend310p3vir04": "ascend310p",
"ascend310p3vir08": "ascend310p",
"ascend310b1": "ascend310b",
"bs9sx1aa": "bs9sx1a",
"ascend610lite": "ascend610lite",
"ascend950": "ascend950",
}
CONFLICT_KEYWORDS = {
"and",
"as",
"assert",
"break",
"class",
"continue",
"def",
"del",
"elif",
"else",
"except",
"finally",
"for",
"from",
"global",
"if",
"import",
"in",
"is",
"lambda",
"not",
"or",
"pass",
"raise",
"return",
"try",
"while",
"with",
"yield",
"False",
"None",
"True",
"nonlocal",
"arg",
"__inputs__",
"__outputs__",
"options",
"bisheng",
"bisheng_path",
"tikcpp_path",
"impl_mode",
"custom_compile_options",
"custom_all_compile_options",
"soc_version",
"soc_short",
"custom_compile_options_soc",
"custom_all_compile_options_soc",
"origin_func_name",
"ascendc_src_dir_ex",
"ascendc_src_dir",
"ascendc_src_file",
"src",
"op_type",
"code_channel",
"op_info",
"compile_op",
"get_code_channel",
"result",
"__attrs__",
"isinstance",
"attr",
"get_current_build_config",
"_build_args",
"get_dtype_fmt_options",
"shutil",
"os",
"get_kernel_source",
}
class OpDesc:
def __init__(self: any, op_type: str):
self.op_type = op_type
self.attr_list = []
self.attr_val = {}
self.input_name = []
self.input_ori_name = []
self.input_type = []
self.input_dtype = []
self.input_dtype_for_bin_list = []
self.input_dtype_for_bin = {}
self.input_fmt = []
self.input_fmt_for_bin_list = []
self.input_fmt_for_bin = {}
self.input_virt = {}
self.input_value_depend = {}
self.output_name = []
self.output_ori_name = []
self.output_type = []
self.output_dtype = []
self.output_dtype_for_bin_list = []
self.output_dtype_for_bin = {}
self.output_fmt = []
self.output_fmt_for_bin_list = []
self.output_fmt_for_bin = {}
self.output_init_value = []
self.output_shape_depend_on_compute = []
self.op_fmt_sel = False
self.op_chk_support = False
self.op_intf = ""
self.kern_name = ""
self.op_file = ""
self.op_replay_flag = False
self.op_replay_batch = False
self.input_idx = -1
self.output_idx = -1
self.max_block_dim = 32
self.max_shape_size = 268435456
self.dynamic_shape = False
self.op_range_limit = ""
self.custom_compile_options = {}
self.custom_all_compile_options = {}
self.param_type_dynamic = False
self.mc2_ctx = []
self.bin_cprs_list = []
self.bin_cprs_head = []
self.bin_save_list = []
@staticmethod
def _parse_digit(conf: str) -> int:
return int(conf.split("=")[1])
@staticmethod
def _parse_flag(conf: str) -> bool:
return conf.split("=")[1] == "true"
@staticmethod
def _parse_str(conf: str) -> str:
return conf.split("=")[1]
@staticmethod
def _parse_list(conf: str) -> list:
return conf.split("=")[1].split(",")
def parse_input(self: any, conf: str):
if conf.startswith("input{}.name".format(int(self.input_idx) + 1)):
self.input_idx += 1
self.input_ori_name.append(self._parse_str(conf))
self.input_name.append(self.input_ori_name[-1] + "_in__")
elif conf.startswith("input{}.paramType".format(int(self.input_idx))):
param_type = self._parse_str(conf)
self.input_type.append(param_type)
if param_type == "dynamic":
self.param_type_dynamic = True
elif conf.startswith("input{}.dtype".format(int(self.input_idx))):
self.input_dtype.append(self._parse_str(conf))
elif conf.startswith("input{}.for_bin_dtype".format(int(self.input_idx))):
self.input_dtype_for_bin.update({self.input_idx: self._parse_str(conf)})
elif conf.startswith("input{}.format".format(int(self.input_idx))):
self.input_fmt.append(self._parse_str(conf))
elif conf.startswith("input{}.for_bin_format".format(int(self.input_idx))):
self.input_fmt_for_bin.update({self.input_idx: self._parse_str(conf)})
elif conf.startswith("input{}.virtual".format(int(self.input_idx))):
self.input_virt[self.input_idx] = self._parse_str(conf)
elif conf.startswith("input{}.valueDepend".format(int(self.input_idx))):
self.input_value_depend[self.input_idx] = self._parse_str(conf)
elif conf.startswith("input{}.initValue".format(int(self.input_idx))):
raise Exception(
f"[ERROR]: Op: {{'{self.op_type}'}} input {self.input_ori_name[int(self.input_idx)]}\
has InitValue, which is not support!"
)
else:
return
def parse_output(self: any, conf: str):
if conf.startswith("output{}.name".format(int(self.output_idx) + 1)):
self.output_idx += 1
self.output_ori_name.append(self._parse_str(conf))
self.output_name.append(self.output_ori_name[-1] + "_out_")
self.output_init_value.append(None)
elif conf.startswith("output{}.paramType".format(int(self.output_idx))):
param_type = self._parse_str(conf)
self.output_type.append(param_type)
if param_type == "dynamic":
self.param_type_dynamic = True
elif conf.startswith("output{}.dtype".format(int(self.output_idx))):
self.output_dtype.append(self._parse_str(conf))
elif conf.startswith("output{}.for_bin_dtype".format(int(self.output_idx))):
self.output_dtype_for_bin.update({self.output_idx: self._parse_str(conf)})
elif conf.startswith("output{}.format".format(int(self.output_idx))):
self.output_fmt.append(self._parse_str(conf))
elif conf.startswith("output{}.for_bin_format".format(int(self.output_idx))):
self.output_fmt_for_bin.update({self.output_idx: self._parse_str(conf)})
elif conf.startswith("output{}.initValue".format(int(self.output_idx))):
self.output_init_value[int(self.output_idx)] = self._parse_str(conf)
elif conf.startswith("output{}.outputShapeDependOnCompute=true".format(int(self.output_idx))):
self.output_shape_depend_on_compute.append(int(self.output_idx))
else:
return
def parse_op_format(self: any, conf: str):
self.op_fmt_sel = self._parse_flag(conf)
def parse_check_support(self: any, conf: str):
self.op_chk_support = self._parse_flag(conf)
def parse_range_limit(self: any, conf: str):
self.op_range_limit = self._parse_str(conf)
def parse_kern_name(self: any, conf: str):
self.kern_name = self._parse_str(conf)
def parse_op_intf(self: any, conf: str):
self.op_intf = self._parse_str(conf)
def parse_op_file(self: any, conf: str):
self.op_file = self._parse_str(conf)
def parse_dynamic_shape(self: any, conf: str):
self.dynamic_shape = self._parse_flag(conf)
def parse_attr_list(self: any, conf: str):
self.attr_list = self._parse_list(conf)
intersection_element = set(self.attr_list) & CONFLICT_KEYWORDS
if intersection_element:
raise Exception(
f"[ERROR]: The attribute name: {intersection_element} in op: {{'{self.op_type}'}} \
conflicts with the built-in variable name. Use a complex name or prefix the operator name."
)
def parse_mc2_ctx(self: any, conf: str):
self.mc2_ctx = self._parse_list(conf)
@staticmethod
def _camel_to_snake(camel_case_str: str):
snake_case_str = ""
for i, c in enumerate(camel_case_str):
if i == 0:
snake_case_str += c.lower()
elif c.isupper():
snake_case_str += "_" + c.lower()
else:
snake_case_str += c
return snake_case_str
def parse_attr_val(self: any, conf: str):
for attr in self.attr_list:
if self.attr_val.get(attr) is None:
self.attr_val[attr] = {}
if conf.startswith("attr_{}.type".format(attr)):
self.attr_val.get(attr)["type"] = self._camel_to_snake(self._parse_str(conf))
elif conf.startswith("attr_{}.paramType".format(attr)):
self.attr_val.get(attr)["paramType"] = self._parse_str(conf)
elif conf.startswith("attr_{}.defaultValue".format(attr)):
self.attr_val.get(attr)["defaultValue"] = self._parse_str(conf)
def parse_replay_val(self: any, batch_list: list, iterator_list: list):
if self.op_type in batch_list:
self.op_replay_flag = True
self.op_replay_batch = True
elif self.op_type in iterator_list:
self.op_replay_flag = True
self.op_replay_batch = False
def _is_op_type_in_opdesc(op_descs: list, op_type: str):
return any(op_type == op.op_type for op in op_descs)
def _set_all_options_to_opdescs(op_descs, soc_ver_compile_options):
for op in op_descs:
op.custom_all_compile_options = soc_ver_compile_options
def _set_options_to_opdesc(op_descs, op_type, soc_ver_compile_options):
for op in op_descs:
if op.op_type != op_type:
continue
op.custom_compile_options.update(soc_ver_compile_options)
def _trans_soc_ver_to_short(soc_ver: str):
low_soc_ver = soc_ver.lower()
if low_soc_ver not in SOC_TO_SHORT_SOC_MAP:
print(f"WARNING: caution: {soc_ver} will trans into ascend910, if not your intention,use ascend910b1~4 instead")
return SOC_TO_SHORT_SOC_MAP[low_soc_ver]
def _get_op_custom_options(op_descs: list, auto_gen_dir: str):
if auto_gen_dir is None:
return {}
file = os.path.join(auto_gen_dir, "custom_compile_options.ini")
if not os.path.exists(file):
print(f"WARNING: cannot find {auto_gen_dir}/custom_compile_options.ini")
return {}
with open(file) as fd:
lines = fd.readlines()
for line in lines:
param_list = str.split(line.rstrip("\n"), ",")
if len(param_list) != 3:
raise Exception(f"ERROR: custom compile option {param_list} len is not 3")
op_type = param_list[0]
if op_type.upper() == "ALL":
op_type = OP_ALL
if op_type != OP_ALL and not _is_op_type_in_opdesc(op_descs, op_type):
continue
soc_ver_compile_options = {}
soc_ver = param_list[1]
options_str = param_list[2]
options = str.split(options_str, ";")
if soc_ver == "":
soc_ver_compile_options[SOC_ALL] = options
else:
soc_ver_list = str.split(soc_ver, ";")
for ver in soc_ver_list:
short_ver = _trans_soc_ver_to_short(ver)
soc_ver_compile_options[short_ver] = options
if op_type == OP_ALL:
_set_all_options_to_opdescs(op_descs, soc_ver_compile_options)
else:
_set_options_to_opdesc(op_descs, op_type, soc_ver_compile_options)
def get_op_desc(
file: str, batch_list: list, iterator_list: list, builder: any, op_type: list, auto_gen_dir: str = None
) -> list:
op_descs = []
op_match = False
with open(file) as fd:
lines = fd.readlines()
for line in lines:
line = line.strip()
if line.startswith("["):
name = line[1:-1]
if op_type is None or name in op_type:
op_match = True
op_desc = builder(name)
op_desc.parse_replay_val(batch_list, iterator_list)
op_descs.append(op_desc)
else:
op_match = False
if op_type is not None and len(op_descs) == len(op_type):
break
continue
if not op_match:
continue
if line.startswith("input"):
op_desc.parse_input(line)
elif line.startswith("output"):
op_desc.parse_output(line)
elif line.startswith("dynamicFormat.flag"):
op_desc.parse_op_format(line)
elif line.startswith("needCheckSupport.flag"):
op_desc.parse_check_support(line)
elif line.startswith("rangeLimit.value"):
op_desc.parse_range_limit(line)
elif line.startswith("opInterface.value"):
op_desc.parse_op_intf(line)
elif line.startswith("kernel.name"):
op_desc.parse_kern_name(line)
elif line.startswith("opFile.value"):
op_desc.parse_op_file(line)
elif line.startswith("dynamicShapeSupport.flag"):
op_desc.parse_dynamic_shape(line)
elif line.startswith("mc2.ctx"):
op_desc.parse_mc2_ctx(line)
elif line.startswith("attr.list"):
op_desc.parse_attr_list(line)
elif line.startswith("attr_"):
op_desc.parse_attr_val(line)
_get_op_custom_options(op_descs, auto_gen_dir)
return op_descs

View File

@@ -0,0 +1,429 @@
#!/usr/bin/env python3
# -----------------------------------------------------------------------------------------------------------
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
# CANN Open Software License Agreement Version 2.0 (the "License").
# Please refer to the License for details. You may not use this file except in compliance with the License.
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
# See LICENSE in the root of the software repository for the full text of the License.
# -----------------------------------------------------------------------------------------------------------
import json
import os
import stat
import sys
ATTR_TYPE_LIST = [
"int",
"float",
"bool",
"str",
"listInt",
"listFloat",
"listBool",
"listStr",
"listListInt",
"type",
"listType",
"tensor",
"listTensor",
]
ATTR_PARAMTYPE_LIST = ["optional", "required"]
BOOL_FLAG_KEY = [
"dynamicFormat",
"dynamicShapeSupport",
"dynamicRankSupport",
"precision_reduce",
"heavyOp",
"needCheckSupport",
"enableVectorCore",
]
BOOL_LIST = ["true", "false"]
DTYPE_LIST = [
"float16",
"float",
"float32",
"int8",
"int16",
"int32",
"uint8",
"uint16",
"uint32",
"bool",
"int64",
"uint64",
"qint8",
"qint16",
"qint32",
"quint8",
"quint16",
"double",
"complex32",
"complex64",
"complex128",
"string",
"resource",
"dual",
"dual_sub_int8",
"dual_sub_uint8",
"string_ref",
"int4",
"bfloat16",
"uint1",
"hifloat8",
"float8_e4m3fn",
"float8_e5m2",
"float8_e8m0",
"float4_e2m1",
"float4_e1m2",
"int2",
]
FORMAT_LIST = [
"NCHW",
"NHWC",
"ND",
"NC1HWC0",
"FRACTAL_Z",
"NC1C0HWPAD",
"NHWC1C0",
"FSR_NCHW",
"FRACTAL_DECONV",
"C1HWNC0",
"FRACTAL_DECONV_TRANSPOSE",
"FRACTAL_DECONV_SP_STRIDE_TRANS",
"NC1HWC0_C04",
"FRACTAL_Z_C04",
"CHWN",
"FRACTAL_DECONV_SP_STRIDE8_TRANS",
"HWCN",
"NC1KHKWHWC0",
"BN_WEIGHT",
"FILTER_HWCK",
"HASHTABLE_LOOKUP_LOOKUPS",
"HASHTABLE_LOOKUP_KEYS",
"HASHTABLE_LOOKUP_VALUE",
"HASHTABLE_LOOKUP_OUTPUT",
"HASHTABLE_LOOKUP_HITS",
"C1HWNCoC0",
"MD",
"NDHWC",
"FRACTAL_ZZ",
"FRACTAL_NZ",
"NCDHW",
"DHWCN",
"NDC1HWC0",
"FRACTAL_Z_3D",
"CN",
"NC",
"DHWNC",
"FRACTAL_Z_3D_TRANSPOSE",
"FRACTAL_ZN_LSTM",
"FRACTAL_ZN_RNN",
"FRACTAL_Z_G",
"NULL",
"FRACTAL_NZ_C0_2",
"FRACTAL_NZ_C0_4",
"FRACTAL_NZ_C0_16",
"FRACTAL_NZ_C0_32",
]
def parse_ini_files(ini_files):
"""
parse ini files to json
Parameters:
----------------
ini_files:input file list
return:ops_info
----------------
"""
tbe_ops_info = {}
for ini_file in ini_files:
check_file_size(ini_file)
parse_ini_to_obj(ini_file, tbe_ops_info)
return tbe_ops_info
def check_file_size(input_file):
try:
file_size = os.path.getsize(input_file)
except OSError as os_error:
print(f'[ERROR] Failed to open "{input_file}". {os_error}')
raise OSError from os_error
if file_size > 10 * 1024 * 1024:
print(f"[WARN] The size of {input_file} exceeds 10MB, it may take more time to run, please wait.")
def parse_ini_to_obj(ini_file, tbe_ops_info):
"""
parse ini file to json obj
Parameters:
----------------
ini_file:ini file path
tbe_ops_info:ops_info
----------------
"""
with open(ini_file) as ini_file:
lines = ini_file.readlines()
op_dict = {}
op_name = ""
find_op_type = False
for line in lines:
line = line.rstrip()
if line == "":
continue
if line.startswith("["):
if line.endswith("]"):
op_name = line[1:-1]
op_dict = {}
tbe_ops_info[op_name] = op_dict
find_op_type = True
elif "=" in line:
key1 = line[: line.index("=")]
key2 = line[line.index("=") + 1 :]
key1_0, key1_1 = key1.split(".")
if key1_0 not in op_dict:
op_dict[key1_0] = {}
if key1_1 in op_dict.get(key1_0):
raise RuntimeError("Op:" + op_name + " " + key1_0 + " " + key1_1 + " is repeated!")
dic_key = op_dict.get(key1_0)
dic_key[key1_1] = key2
else:
continue
if not find_op_type:
raise RuntimeError("Not find OpType in .ini file.")
def check_output_exist(op_dict, is_valid):
"""
Function Description:
Check output is exist
Parameter: op_dict
Parameter: is_valid
"""
if "output0" in op_dict:
output0_dict = op_dict.get("output0")
if output0_dict.get("name", None) is None:
is_valid = False
print("output0.name is required in .ini file!")
else:
is_valid = False
print("output0 is required in .ini file!")
return is_valid
def check_attr_dict(attr_dict, is_valid, attr):
"""
Function Description:
Check attr_dict
Parameter: attr_dict
Parameter: is_valid
Parameter: attr
"""
attr_type = attr_dict.get("type")
value = attr_dict.get("value")
param_type = attr_dict.get("paramType")
if attr_type is None or value is None:
is_valid = False
print(f"If attr.list is exist, {attr}.type and {attr}.value is required")
if param_type and param_type not in ATTR_PARAMTYPE_LIST:
is_valid = False
print(f"{attr}.paramType only support {ATTR_PARAMTYPE_LIST}.")
if attr_type and attr_type not in ATTR_TYPE_LIST:
is_valid = False
print(f"{attr}.type only support {ATTR_TYPE_LIST}.")
return is_valid
def check_attr(op_dict, is_valid):
"""
Function Description:
Check attr
Parameter: op_dict
Parameter: is_valid
"""
if "attr" in op_dict:
attr_dict = op_dict.get("attr")
attr_list_str = attr_dict.get("list", None)
if attr_list_str is None:
is_valid = False
print("attr.list is required in .ini file!")
else:
attr_list = attr_list_str.split(",")
for attr_name in attr_list:
attr = "attr_" + attr_name.strip()
attr_dict = op_dict.get(attr)
if attr_dict:
is_valid = check_attr_dict(attr_dict, is_valid, attr)
else:
is_valid = False
print(f"{attr} is required in .ini file, when attr.list is {attr_list_str}!")
return is_valid
def check_bool_flag(op_dict, is_valid):
"""
Function Description:
check_bool_flag
Parameter: op_dict
Parameter: is_valid
"""
for key in BOOL_FLAG_KEY:
if key in op_dict:
op_bool_key = op_dict.get(key)
if op_bool_key.get("flag").strip() not in BOOL_LIST:
is_valid = False
print(f"{key}.flag only support {BOOL_LIST}.")
return is_valid
def check_type_format(op_info, is_valid, op_info_key):
"""
Function Description:
Check type and format
Parameter: op_info
Parameter: is_valid
Parameter: op_info_key
"""
op_info_dtype_str = op_info.get("dtype")
op_info_dtype_num = 0
op_info_format_num = 0
if op_info_dtype_str:
op_info_dtype = op_info_dtype_str.split(",")
op_info_dtype_num = len(op_info_dtype)
for dtype in op_info_dtype:
if dtype.strip() not in DTYPE_LIST:
is_valid = False
print(f"{op_info_key}.dtype not support {dtype}.")
op_info_format_str = op_info.get("format")
if op_info_format_str:
op_info_format = op_info_format_str.split(",")
op_info_format_num = len(op_info_format)
for op_format in op_info_format:
if op_format.strip() not in FORMAT_LIST:
is_valid = False
print(f"{op_info_key}.format not support {op_format}.")
if op_info_dtype_num > 0 and op_info_format_num > 0:
if op_info_dtype_num != op_info_format_num:
is_valid = False
print("The number of {0}.dtype not match the number of {0}.format.".format(op_info_key))
return is_valid
def check_op_info(tbe_ops):
"""
Function Description:
Check info.
Parameter: tbe_ops
Return Value: is_valid
"""
print("\n\n==============check valid for ops info start==============")
required_op_input_info_keys = ["paramType", "name"]
required_op_output_info_keys = ["paramType", "name"]
param_type_valid_value = ["dynamic", "optional", "required"]
is_valid = True
for op_key in tbe_ops:
op_dict = tbe_ops[op_key]
for op_info_key in op_dict:
if op_info_key.startswith("input"):
op_input_info = op_dict[op_info_key]
missing_keys = []
for required_op_input_info_key in required_op_input_info_keys:
if required_op_input_info_key not in op_input_info:
missing_keys.append(required_op_input_info_key)
if len(missing_keys) > 0:
print("op: " + op_key + " " + op_info_key + " missing: " + ",".join(missing_keys))
is_valid = False
else:
if op_input_info["paramType"] not in param_type_valid_value:
print(
"op: " + op_key + " " + op_info_key + " paramType not valid, valid key:[dynamic, "
"optional, required]"
)
is_valid = False
is_valid = check_type_format(op_input_info, is_valid, op_info_key)
if op_info_key.startswith("output"):
op_input_info = op_dict[op_info_key]
missing_keys = []
for required_op_input_info_key in required_op_output_info_keys:
if required_op_input_info_key not in op_input_info:
missing_keys.append(required_op_input_info_key)
if len(missing_keys) > 0:
print("op: " + op_key + " " + op_info_key + " missing: " + ",".join(missing_keys))
is_valid = False
else:
if op_input_info["paramType"] not in param_type_valid_value:
print(
"op: " + op_key + " " + op_info_key + " paramType not valid, valid key:[dynamic, "
"optional, required]"
)
is_valid = False
is_valid = check_type_format(op_input_info, is_valid, op_info_key)
is_valid = check_attr(op_dict, is_valid)
is_valid = check_bool_flag(op_dict, is_valid)
print("==============check valid for ops info end================\n\n")
return is_valid
def write_json_file(tbe_ops_info, json_file_path):
"""
Save info to json file
Parameters:
----------------
tbe_ops_info: ops_info
json_file_path: json file path
----------------
"""
json_file_real_path = os.path.realpath(json_file_path)
wr_flag = os.O_WRONLY | os.O_CREAT
wr_mode = stat.S_IWUSR | stat.S_IRUSR
with os.fdopen(os.open(json_file_real_path, wr_flag, wr_mode), "w") as file_path:
# The owner have all rights£¬group only have read rights
os.chmod(json_file_real_path, stat.S_IWUSR + stat.S_IRGRP + stat.S_IRUSR)
json.dump(tbe_ops_info, file_path, sort_keys=True, indent=4, separators=(",", ":"))
print("Compile op info cfg successfully.")
def parse_ini_to_json(ini_file_paths, outfile_path):
"""
parse ini files to json file
Parameters:
----------------
ini_file_paths: list of ini file path
outfile_path: output file path
----------------
"""
tbe_ops_info = parse_ini_files(ini_file_paths)
if not check_op_info(tbe_ops_info):
print("Compile op info cfg failed.")
return False
write_json_file(tbe_ops_info, outfile_path)
return True
if __name__ == "__main__":
args = sys.argv
OUTPUT_FILE_PATH = "tbe_ops_info.json"
ini_file_path_list = []
parse_ini_list = []
for arg in args:
if arg.endswith("ini"):
ini_file_path_list.append(arg)
OUTPUT_FILE_PATH = arg.replace(".ini", ".json")
if arg.endswith("json"):
OUTPUT_FILE_PATH = arg
if not ini_file_path_list:
ini_file_path_list.append("tbe_ops_info.ini")
for ini_file in ini_file_path_list:
if os.path.exists(ini_file):
parse_ini_list.append(ini_file)
if parse_ini_list:
if not parse_ini_to_json(parse_ini_list, OUTPUT_FILE_PATH):
sys.exit(1)
sys.exit(0)