94
csrc/cmake/scripts/check_version_compatible.py
Normal file
94
csrc/cmake/scripts/check_version_compatible.py
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
"""
|
||||
版本兼容性检查
|
||||
|
||||
检查当前代码仓与基础 CANN 包间的兼容性.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import NoReturn
|
||||
|
||||
|
||||
class VersionChecker:
|
||||
@classmethod
|
||||
def main(cls) -> NoReturn:
|
||||
parser = argparse.ArgumentParser(description="Check Version Compatible", epilog="Best Regards!")
|
||||
sub_parser = parser.add_subparsers(help="Sub-Command")
|
||||
# 参数注册
|
||||
parser.add_argument("--cann_path", required=True, nargs=1, type=str, help="CANN install path")
|
||||
parser.add_argument("--cann_package_name", required=True, nargs=1, type=str, help="CANN package name")
|
||||
# 子命令行(Check)
|
||||
p_chk = sub_parser.add_parser("check_code_compatible", help="Check Version Compatible.")
|
||||
p_chk.add_argument(
|
||||
"--code_version_info_file", required=True, nargs=1, type=str, help="Code version info file path"
|
||||
)
|
||||
p_chk.set_defaults(func=VersionChecker._check_compatible)
|
||||
# 子命令行(Get)
|
||||
p_get = sub_parser.add_parser("get_package_version", help="Get Package Version")
|
||||
p_get.set_defaults(func=VersionChecker._get_package_version)
|
||||
# 参数处理
|
||||
args = parser.parse_args()
|
||||
# 基本合法性检查, 版本号获取
|
||||
cann_version_info_file = Path(args.cann_path[0], args.cann_package_name[0], "version.info").absolute()
|
||||
if not cann_version_info_file.exists():
|
||||
raise ValueError(f"CANN version info file({cann_version_info_file}) not exist.")
|
||||
ret, cann_version = cls._get_version_str(file=cann_version_info_file)
|
||||
if not ret:
|
||||
raise ValueError(f"Can't get version from CANN version info file({cann_version_info_file}).")
|
||||
rst = args.func(cann_version, args)
|
||||
return rst
|
||||
|
||||
@classmethod
|
||||
def _check_compatible(cls, cann_version: str, args) -> str:
|
||||
code_version_info_file = Path(args.code_version_info_file[0]).absolute()
|
||||
if not code_version_info_file.exists():
|
||||
raise ValueError(f"Code version info file({code_version_info_file}) not exist.")
|
||||
ret, code_version = cls._get_version_str(file=code_version_info_file)
|
||||
if not ret:
|
||||
raise ValueError(f"Can't get version from Code version info file({code_version_info_file}).")
|
||||
# 兼容性检查
|
||||
cann_sub_version = cann_version.rsplit(".", 1)[0]
|
||||
code_sub_version = code_version.rsplit(".", 1)[0]
|
||||
if cann_sub_version != code_sub_version:
|
||||
raise ValueError(
|
||||
f"The version number of the current code is {code_sub_version}, "
|
||||
f"and the version number of the cann package used is {cann_sub_version}. "
|
||||
f"Please install version {code_sub_version} of the cann package."
|
||||
)
|
||||
return cann_sub_version
|
||||
|
||||
@classmethod
|
||||
def _get_package_version(cls, cann_version: str, args) -> str:
|
||||
cann_sub_version = cann_version.rsplit(".", 1)[0]
|
||||
return cann_sub_version
|
||||
|
||||
@classmethod
|
||||
def _get_version_str(cls, file: Path):
|
||||
with open(file) as fh:
|
||||
lines = fh.readlines()
|
||||
for line in lines:
|
||||
if not line.startswith("Version="):
|
||||
continue
|
||||
version = line[8:].replace("\r", "").replace("\n", "")
|
||||
return True, version
|
||||
return False, ""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(format="%(filename)s:%(lineno)d [%(levelname)s] %(message)s", level=logging.INFO)
|
||||
try:
|
||||
print(VersionChecker.main())
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
exit(1)
|
||||
122
csrc/cmake/scripts/convert_yaml.py
Normal file
122
csrc/cmake/scripts/convert_yaml.py
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
"""
|
||||
test_config.yaml 格式转换
|
||||
|
||||
转换成 ops-nn 仓的格式, 方便ci读取
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def load_test_config(test_config_path: str):
|
||||
"""读取并解析test_config.yaml文件"""
|
||||
try:
|
||||
with open(test_config_path, encoding="utf-8") as file:
|
||||
return yaml.safe_load(file)
|
||||
|
||||
except Exception as e:
|
||||
logging.error("Failed to read test_config.yaml file: %s", e)
|
||||
return None
|
||||
|
||||
|
||||
def extract_src_and_exclude(data):
|
||||
"""从数据中提取所有有options的算子的src和exclude路径"""
|
||||
src_paths = set()
|
||||
exclude_paths = set()
|
||||
|
||||
def extract_from_dict(obj, current_key=None):
|
||||
if isinstance(obj, dict):
|
||||
if "src" in obj and isinstance(obj["src"], list):
|
||||
for path in obj["src"]:
|
||||
src_paths.add(path)
|
||||
if "exclude" in obj and isinstance(obj["exclude"], list):
|
||||
for path in obj["exclude"]:
|
||||
exclude_paths.add(path)
|
||||
if "ut_cov_exclude" in obj and isinstance(obj["ut_cov_exclude"], list):
|
||||
for path in obj["ut_cov_exclude"]:
|
||||
exclude_paths.add(f'"{path}"' if path.startswith("*") else path)
|
||||
|
||||
# 递归处理所有值
|
||||
for key, value in obj.items():
|
||||
extract_from_dict(value, key)
|
||||
elif isinstance(obj, list):
|
||||
for item in obj:
|
||||
extract_from_dict(item, current_key)
|
||||
|
||||
extract_from_dict(data)
|
||||
|
||||
return sorted(src_paths), sorted(exclude_paths)
|
||||
|
||||
|
||||
def write_new_format(new_file_path: str, src_paths: list, exclude_paths: list):
|
||||
"""以新格式写入文件"""
|
||||
try:
|
||||
with open(new_file_path, "w", encoding="utf-8") as file:
|
||||
file.write("ops-transformer:\n")
|
||||
file.write(" src:\n")
|
||||
|
||||
file.write(" release:\n")
|
||||
for path in src_paths:
|
||||
file.write(f" - {path}\n")
|
||||
|
||||
file.write(" unrelease:\n")
|
||||
for path in exclude_paths:
|
||||
file.write(f" - {path}\n")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error("Failed to write file: %s", e)
|
||||
return False
|
||||
|
||||
|
||||
def main(test_config_path: str, output_path: str):
|
||||
"""主函数"""
|
||||
# 检查文件是否存在
|
||||
if not os.path.exists(test_config_path):
|
||||
logging.error("File does not exist: %s", test_config_path)
|
||||
return
|
||||
|
||||
# 读取test_config文件
|
||||
data = load_test_config(test_config_path)
|
||||
if data is None:
|
||||
return
|
||||
|
||||
# 提取所有有options的算子的src和exclude路径
|
||||
src_paths, exclude_paths = extract_src_and_exclude(data)
|
||||
|
||||
logging.info("Found %s src paths", len(src_paths))
|
||||
logging.info("Found %s exclude paths", len(exclude_paths))
|
||||
|
||||
# 以新格式写回
|
||||
if write_new_format(output_path, src_paths, exclude_paths):
|
||||
logging.info("File conversion completed")
|
||||
else:
|
||||
logging.error("File conversion failed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(format="[%(asctime)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
main("test_config.yaml", "test_config.yaml")
|
||||
elif len(sys.argv) == 2:
|
||||
main(sys.argv[1], sys.argv[1])
|
||||
elif len(sys.argv) == 3:
|
||||
main(sys.argv[1], sys.argv[2])
|
||||
else:
|
||||
logging.error("usage: convert_yaml.py test_config_path [output_path]")
|
||||
exit(1)
|
||||
2
csrc/cmake/scripts/custom/help.info
Normal file
2
csrc/cmake/scripts/custom/help.info
Normal file
@@ -0,0 +1,2 @@
|
||||
--install-path Install operator package to specific dir path
|
||||
--install-for-all Allow other users to use the operator package
|
||||
344
csrc/cmake/scripts/custom/install.sh
Normal file
344
csrc/cmake/scripts/custom/install.sh
Normal file
@@ -0,0 +1,344 @@
|
||||
#!/bin/bash
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
# 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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
vendor_name=customize
|
||||
targetdir=/usr/local/Ascend/opp
|
||||
target_custom=0
|
||||
|
||||
sourcedir=$PWD/packages
|
||||
vendordir=vendors/$vendor_name
|
||||
|
||||
QUIET="y"
|
||||
INSTALL_FOR_ALL="n"
|
||||
|
||||
|
||||
while true
|
||||
do
|
||||
case $1 in
|
||||
--quiet)
|
||||
QUIET="y"
|
||||
shift
|
||||
;;
|
||||
--install-path=*)
|
||||
INSTALL_PATH=$(echo $1 | cut -d"=" -f2-)
|
||||
INSTALL_PATH=${INSTALL_PATH%*/}
|
||||
shift
|
||||
;;
|
||||
--install-for-all)
|
||||
INSTALL_FOR_ALL="y"
|
||||
shift
|
||||
;;
|
||||
--*)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log() {
|
||||
cur_date=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
echo "[ops_custom] [$cur_date] "$1
|
||||
}
|
||||
|
||||
if [ -n "${INSTALL_PATH}" ]; then
|
||||
if [[ ! "${INSTALL_PATH}" = /* ]]; then
|
||||
log "[ERROR] use absolute path for --install-path argument"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d ${INSTALL_PATH} ]; then
|
||||
mkdir ${INSTALL_PATH} >> /dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
log "[ERROR] create ${INSTALL_PATH} failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
targetdir=${INSTALL_PATH}
|
||||
elif [ -n "${ASCEND_CUSTOM_OPP_PATH}" ]; then
|
||||
if [[ "${ASCEND_CUSTOM_OPP_PATH}" == *:* ]]; then
|
||||
log "[ERROR] environment variable ASCEND_CUSTOM_OPP_PATH=${ASCEND_CUSTOM_OPP_PATH} is set and \
|
||||
has multiple path in it (colon inside), which will cause the custom op installed incorrectly. \
|
||||
Please use the --install-path option to specify an installation path instead."
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d ${ASCEND_CUSTOM_OPP_PATH} ]; then
|
||||
mkdir -p ${ASCEND_CUSTOM_OPP_PATH} >> /dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
log "[ERROR] create ${ASCEND_CUSTOM_OPP_PATH} failed"
|
||||
fi
|
||||
fi
|
||||
targetdir=${ASCEND_CUSTOM_OPP_PATH}
|
||||
else
|
||||
if [ "x${ASCEND_OPP_PATH}" == "x" ]; then
|
||||
log "[ERROR] env ASCEND_OPP_PATH no exist"
|
||||
exit 1
|
||||
fi
|
||||
targetdir="${ASCEND_OPP_PATH}"
|
||||
fi
|
||||
|
||||
if [ ! -d $targetdir ];then
|
||||
log "[ERROR] $targetdir no exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x $targetdir ] || [ ! -w $targetdir ] || [ ! -r $targetdir ];then
|
||||
log "[WARNING] The directory $targetdir does not have sufficient permissions. \
|
||||
Please check and modify the folder permissions (e.g., using chmod), \
|
||||
or use the --install-path option to specify an installation path and \
|
||||
change the environment variable ASCEND_CUSTOM_OPP_PATH to the specified path."
|
||||
fi
|
||||
|
||||
upgrade()
|
||||
{
|
||||
if [ ! -d ${sourcedir}/$vendordir/$1 ]; then
|
||||
log "[INFO] no need to upgrade ops $1 files"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ ! -d ${targetdir}/$vendordir/$1 ];then
|
||||
log "[INFO] create ${targetdir}/$vendordir/$1."
|
||||
mkdir -p ${targetdir}/$vendordir/$1
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] create ${targetdir}/$vendordir/$1 failed"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
has_same_file=-1
|
||||
for file_a in ${sourcedir}/$vendordir/$1/*; do
|
||||
file_b=${file_a##*/};
|
||||
if [ "ls ${targetdir}/$vendordir/$1" = "" ]; then
|
||||
log "[INFO] ${targetdir}/$vendordir/$1 is empty !!"
|
||||
return 1
|
||||
fi
|
||||
grep -q $file_b <<<`ls ${targetdir}/$vendordir/$1`;
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo -n "${file_b} "
|
||||
has_same_file=0
|
||||
fi
|
||||
done
|
||||
if [ 0 -eq $has_same_file ]; then
|
||||
echo
|
||||
if test $QUIET = "n"; then
|
||||
echo "[INFO]: has old version in ${targetdir}/$vendordir/$1, \
|
||||
you want to Overlay Installation , please enter:[o]; \
|
||||
or replace directory installation , please enter: [r]; \
|
||||
or not install , please enter:[n]."
|
||||
|
||||
while true
|
||||
do
|
||||
read orn
|
||||
if [ "$orn" = n ]; then
|
||||
return 0
|
||||
elif [ "$orn" = o ]; then
|
||||
break;
|
||||
elif [ "$orn" = r ]; then
|
||||
[ -d "${targetdir}/$vendordir/$1/" ] && rm -rf "${targetdir}/$vendordir/$1"/*
|
||||
break;
|
||||
else
|
||||
log "[ERROR] input error, please input again!"
|
||||
fi
|
||||
done
|
||||
else
|
||||
[ -d "${targetdir}/$vendordir/$1/" ] && rm -rf "${targetdir}/$vendordir/$1"/*
|
||||
fi
|
||||
fi
|
||||
log "[INFO] replace or merge old ops $1 files ......"
|
||||
fi
|
||||
|
||||
log "[INFO] copy new ops $1 files ......"
|
||||
if [ -d ${targetdir}/$vendordir/$1/ ]; then
|
||||
chmod -R +w "$targetdir/$vendordir/$1/" >/dev/null 2>&1
|
||||
fi
|
||||
cp -rf ${sourcedir}/$vendordir/$1/* $targetdir/$vendordir/$1/
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] copy new $1 files failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
upgrade_proto()
|
||||
{
|
||||
if [ ! -f ${sourcedir}/$vendordir/custom.proto ]; then
|
||||
log "[INFO] no need to upgrade custom.proto files"
|
||||
return 0
|
||||
fi
|
||||
if [ ! -d ${targetdir}/$vendordir/framework/caffe ];then
|
||||
log "[INFO] create ${targetdir}/$vendordir/framework/caffe."
|
||||
mkdir -p ${targetdir}/$vendordir/framework/caffe
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] create ${targetdir}/$vendordir/framework/caffe failed"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
if [ -f ${targetdir}/$vendordir/framework/caffe/custom.proto ]; then
|
||||
# 有老版本,判断是否要覆盖式安装
|
||||
if test $QUIET = "n"; then
|
||||
echo "[INFO] ${targetdir}/$vendordir/framework/caffe has old version"\
|
||||
"custom.proto file. Do you want to replace? [y/n] "
|
||||
|
||||
while true
|
||||
do
|
||||
read yn
|
||||
if [ "$yn" = n ]; then
|
||||
return 0
|
||||
elif [ "$yn" = y ]; then
|
||||
break;
|
||||
else
|
||||
log "[ERROR] input error, please input again!"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
log "[INFO] replace old caffe.proto files ......"
|
||||
fi
|
||||
chmod -R +w "$targetdir/$vendordir/framework/caffe/" >/dev/null 2>&1
|
||||
cp -rf ${sourcedir}/$vendordir/custom.proto ${targetdir}/$vendordir/framework/caffe/
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] copy new custom.proto failed"
|
||||
return 1
|
||||
fi
|
||||
log "[INFO] copy custom.proto success"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
upgrade_file()
|
||||
{
|
||||
if [ ! -e ${sourcedir}/$vendordir/$1 ]; then
|
||||
log "[INFO] no need to upgrade ops $1 file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "[INFO] copy new $1 files ......"
|
||||
cp -f ${sourcedir}/$vendordir/$1 $targetdir/$vendordir/$1
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] copy new $1 file failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
delete_optiling_file()
|
||||
{
|
||||
if [ ! -d ${targetdir}/vendors ];then
|
||||
log "[INFO] $1 not exist, no need to uninstall"
|
||||
return 0
|
||||
fi
|
||||
sys_info=$(uname -m)
|
||||
if [ ! -d ${sourcedir}/$vendordir/$1/ai_core/tbe/op_tiling/lib/linux/${sys_info} ];then
|
||||
rm -rf ${sourcedir}/$vendordir/$1/ai_core/tbe/op_tiling/liboptiling.so
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
log "[INFO] copy uninstall sh success"
|
||||
|
||||
if [ ! -d ${targetdir}/vendors ];then
|
||||
log "[INFO] create ${targetdir}/vendors."
|
||||
mkdir -p ${targetdir}/vendors
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] create ${targetdir}/vendors failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
chmod u+w ${targetdir}/vendors
|
||||
|
||||
log "[INFO] upgrade framework"
|
||||
upgrade framework
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "[INFO] upgrade op proto"
|
||||
upgrade op_proto
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "[INFO] upgrade op impl"
|
||||
delete_optiling_file op_impl
|
||||
upgrade op_impl
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "[INFO] upgrade op api"
|
||||
upgrade op_api
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "[INFO] upgrade version.info"
|
||||
upgrade_file version.info
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
upgrade_proto
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# set the set_env.bash
|
||||
if [ -n "${INSTALL_PATH}" ] && [ -d ${INSTALL_PATH} ]; then
|
||||
_ASCEND_CUSTOM_OPP_PATH=${targetdir}/${vendordir}
|
||||
bin_path="${_ASCEND_CUSTOM_OPP_PATH}/bin"
|
||||
set_env_variable="#!/bin/bash\nexport ASCEND_CUSTOM_OPP_PATH=${_ASCEND_CUSTOM_OPP_PATH}:\${ASCEND_CUSTOM_OPP_PATH}\nexport LD_LIBRARY_PATH=${_ASCEND_CUSTOM_OPP_PATH}/op_api/lib/:\${LD_LIBRARY_PATH}"
|
||||
if [ ! -d ${bin_path} ]; then
|
||||
mkdir -p ${bin_path} >> /dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
log "[ERROR] create ${bin_path} failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo -e ${set_env_variable} > ${bin_path}/set_env.bash
|
||||
if [ $? -ne 0 ]; then
|
||||
log "[ERROR] write ASCEND_CUSTOM_OPP_PATH to set_env.bash failed"
|
||||
exit 1
|
||||
else
|
||||
log "[INFO] using requirements: when custom module install finished or before you run the custom module, \
|
||||
execute the command [ source ${bin_path}/set_env.bash ] to set the environment path"
|
||||
fi
|
||||
else
|
||||
_ASCEND_CUSTOM_OPP_PATH=${targetdir}/${vendordir}
|
||||
config_file=${targetdir}/vendors/config.ini
|
||||
if [ ! -f ${config_file} ]; then
|
||||
touch ${config_file}
|
||||
chmod 640 ${config_file}
|
||||
echo "load_priority=$vendor_name" > ${config_file}
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] echo load_priority failed"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
found_vendors="$(grep -w "load_priority" "$config_file" | cut --only-delimited -d"=" -f2-)"
|
||||
found_vendor=$(echo $found_vendors | sed "s/\<$vendor_name\>//g" | tr ',' ' ')
|
||||
vendor=$(echo $found_vendor | tr -s ' ' ',')
|
||||
if [ "$vendor" != "" ]; then
|
||||
sed -i "/load_priority=$found_vendors/s@load_priority=$found_vendors@load_priority=$vendor_name,$vendor@g" "$config_file"
|
||||
fi
|
||||
fi
|
||||
if test $INSTALL_FOR_ALL = "y"; then
|
||||
chmod 755 ${config_file}
|
||||
fi
|
||||
log "[INFO] using requirements: when custom module install finished or before you run the custom module, \
|
||||
execute the command [ export LD_LIBRARY_PATH=${_ASCEND_CUSTOM_OPP_PATH}/op_api/lib/:\${LD_LIBRARY_PATH} ] to set the environment path"
|
||||
fi
|
||||
|
||||
if [ -d ${targetdir}/$vendordir/op_impl/cpu/aicpu_kernel/impl/ ]; then
|
||||
chmod -R 440 ${targetdir}/$vendordir/op_impl/cpu/aicpu_kernel/impl/* >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
echo "SUCCESS"
|
||||
exit 0
|
||||
153
csrc/cmake/scripts/custom/upgrade.sh
Normal file
153
csrc/cmake/scripts/custom/upgrade.sh
Normal file
@@ -0,0 +1,153 @@
|
||||
#!/bin/bash
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
# 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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
vendor_name=customize
|
||||
targetdir=/usr/local/Ascend/opp
|
||||
target_custom=0
|
||||
|
||||
sourcedir=$PWD/packages
|
||||
vendordir=vendors/$vendor_name
|
||||
|
||||
log() {
|
||||
cur_date=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
echo "[ops_custom] [$cur_date] "$1
|
||||
}
|
||||
|
||||
if [[ "x${ASCEND_OPP_PATH}" == "x" ]];then
|
||||
log "[ERROR] env ASCEND_OPP_PATH no exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
targetdir=${ASCEND_OPP_PATH}
|
||||
|
||||
if [ ! -d $targetdir ];then
|
||||
log "[ERROR] $targetdir no exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x $targetdir ] || [ ! -w $targetdir ] || [ ! -r $targetdir ];then
|
||||
log "[WARNING] The directory $targetdir does not have sufficient permissions. \
|
||||
Please check and modify the folder permissions (e.g., using chmod), \
|
||||
or use the --install-path option to specify an installation path and \
|
||||
change the environment variable ASCEND_CUSTOM_OPP_PATH to the specified path."
|
||||
fi
|
||||
|
||||
upgrade()
|
||||
{
|
||||
if [ ! -d ${sourcedir}/$vendordir/$1 ]; then
|
||||
log "[INFO] no need to upgrade ops $1 files"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ ! -d ${targetdir}/$vendordir/$1 ];then
|
||||
log "[INFO] create ${targetdir}/$vendordir/$1."
|
||||
mkdir -p ${targetdir}/$vendordir/$1
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] create ${targetdir}/$vendordir/$1 failed"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
vendor_installed_dir=$(ls "$targetdir/vendors" 2> /dev/null)
|
||||
for i in $vendor_installed_dir;do
|
||||
vendor_installed_file=$(ls "$vendor_installed_dir/$vendor_name/$i" 2> /dev/null)
|
||||
if [ "$i" = "$vendor_name" ] && [ "$vendor_installed_file" != "" ]; then
|
||||
echo "[INFO]: $vendor_name custom opp package has been installed on the path $vendor_installed_dir, \
|
||||
you want to Overlay Installation , please enter:[o]; \
|
||||
or replace directory installation , please enter: [r]; \
|
||||
or not install , please enter:[n]."
|
||||
fi
|
||||
while true
|
||||
do
|
||||
read mrn
|
||||
if [ "$mrn" = o ]; then
|
||||
break
|
||||
elif [ "$mrn" = r ]; then
|
||||
[ -n "$vendor_installed_file" ] && rm -rf "$vendor_installed_file"
|
||||
break
|
||||
elif [ "$mrn" = n ]; then
|
||||
return 0
|
||||
else
|
||||
log "[WARNING]: Input error, please input m or r or n to choose!"
|
||||
fi
|
||||
done
|
||||
done
|
||||
log "[INFO] replace old ops $1 files ......"
|
||||
fi
|
||||
|
||||
log "copy new ops $1 files ......"
|
||||
cp -rf ${sourcedir}/$vendordir/$1/* $targetdir/$vendordir/$1/
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] copy new $1 files failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
upgrade_file()
|
||||
{
|
||||
if [ ! -e ${sourcedir}/$vendordir/$1 ]; then
|
||||
log "[INFO] no need to upgrade ops $1 file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "copy new $1 files ......"
|
||||
cp -f ${sourcedir}/$vendordir/$1 $targetdir/$vendordir/$1
|
||||
if [ $? -ne 0 ];then
|
||||
log "[ERROR] copy new $1 file failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
log "[INFO] copy uninstall sh success"
|
||||
|
||||
log "[INFO] upgrade framework"
|
||||
upgrade framework
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "[INFO] upgrade op proto"
|
||||
upgrade op_proto
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "[INFO] upgrade op impl"
|
||||
upgrade op_impl
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "[INFO] upgrade op api"
|
||||
upgrade op_api
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "[INFO] upgrade version.info"
|
||||
upgrade_file version.info
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
config_file=${targetdir}/vendors/config.ini
|
||||
found_vendors="$(grep -w "load_priority" "$config_file" | cut --only-delimited -d"=" -f2-)"
|
||||
found_vendor=$(echo $found_vendors | sed "s/\<$vendor_name\>//g" | tr ',' ' ')
|
||||
vendor=$(echo $found_vendor | tr -s ' ' ',')
|
||||
if [ "$vendor" != "" ]; then
|
||||
sed -i "/load_priority=$found_vendors/s@load_priority=$found_vendors@load_priority=$vendor_name,$vendor@g" "$config_file"
|
||||
fi
|
||||
|
||||
echo "SUCCESS"
|
||||
exit 0
|
||||
137
csrc/cmake/scripts/examples/get_opapi_abs_path.py
Normal file
137
csrc/cmake/scripts/examples/get_opapi_abs_path.py
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
"""
|
||||
获取 opapi 二进制绝对路径
|
||||
|
||||
Examples 场景下, 用于 built-in 包与 custom 包共存场景下获取正确的 opapi 动态库绝对路径.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class OpApiMgr:
|
||||
@staticmethod
|
||||
def has_symbol(file_path: Path, sym: str) -> bool:
|
||||
cmd = f"nm -D {file_path}".split()
|
||||
ret = subprocess.run(cmd, capture_output=True, check=True, encoding="utf-8")
|
||||
ret.check_returncode()
|
||||
return sym in ret.stdout
|
||||
|
||||
@staticmethod
|
||||
def get_environ_custom_lib_paths() -> list[Path]:
|
||||
paths: list[Path] = []
|
||||
env = os.getenv("ASCEND_CUSTOM_OPP_PATH")
|
||||
if env is None:
|
||||
logging.debug("ASCEND_CUSTOM_OPP_PATH is none.")
|
||||
return paths
|
||||
str_paths = str(env).split(sep=":")
|
||||
if len(str_paths) == 0:
|
||||
return paths
|
||||
for s in str_paths:
|
||||
if len(s) == 0:
|
||||
continue
|
||||
p = Path(s, "op_api/lib/libcust_opapi.so").resolve(strict=False)
|
||||
if not p.exists():
|
||||
logging.warning("Skip not exist path(%s)", p)
|
||||
continue
|
||||
paths.append(p)
|
||||
return paths
|
||||
|
||||
@staticmethod
|
||||
def get_default_custom_lib_paths() -> list[Path]:
|
||||
paths: list[Path] = []
|
||||
env = os.getenv("ASCEND_OPP_PATH")
|
||||
if env is None:
|
||||
logging.warning("ASCEND_OPP_PATH is none.")
|
||||
return paths
|
||||
path_env = Path(env).resolve(strict=False)
|
||||
if not path_env.exists():
|
||||
logging.warning("ASCEND_CUSTOM_OPP_PATH(%s) not exist.", path_env)
|
||||
return paths
|
||||
cfg_file = Path(path_env, "vendors/config.ini").resolve(strict=False)
|
||||
if not cfg_file.exists():
|
||||
logging.debug("Config file(%s) not exist.", cfg_file)
|
||||
return paths
|
||||
# 手工解析 ini 文件
|
||||
with open(cfg_file) as fh:
|
||||
lines = fh.readlines()
|
||||
for line in lines:
|
||||
if not line.startswith("load_priority="):
|
||||
continue
|
||||
sub_str = line[14:]
|
||||
sub_str = sub_str.split(sep="#")[0]
|
||||
sub_str = sub_str.replace("\r", "").replace("\n", "").replace(" ", "")
|
||||
if len(sub_str) == 0:
|
||||
continue
|
||||
vendors = sub_str.split(sep=",")
|
||||
for v in vendors:
|
||||
if len(v) == 0:
|
||||
continue
|
||||
p = Path(path_env, "vendors", v, "op_api/lib/libcust_opapi.so")
|
||||
if not p.exists():
|
||||
logging.warning("Skip not exist path(%s)", p)
|
||||
continue
|
||||
paths.append(p)
|
||||
return paths
|
||||
|
||||
@staticmethod
|
||||
def get_default_builtin_lib_paths() -> list[Path]:
|
||||
paths: list[Path] = []
|
||||
env = os.getenv("ASCEND_OPP_PATH")
|
||||
if env is None:
|
||||
logging.warning("ASCEND_OPP_PATH is none.")
|
||||
return paths
|
||||
path_env = Path(env).resolve(strict=False)
|
||||
if not path_env.exists():
|
||||
logging.warning("ASCEND_CUSTOM_OPP_PATH(%s) not exist.", path_env)
|
||||
return paths
|
||||
shared = Path(path_env, "lib64/libopapi.so").resolve(strict=False)
|
||||
if not shared.exists():
|
||||
logging.error("Can't get built-in libopapi.so(%s)", shared)
|
||||
return paths
|
||||
paths.append(shared)
|
||||
return paths
|
||||
|
||||
@staticmethod
|
||||
def judge_lib_path(sym: str) -> Path | None:
|
||||
path = None
|
||||
environ_custom_lib_paths = OpApiMgr.get_environ_custom_lib_paths()
|
||||
default_custom_lib_paths = OpApiMgr.get_default_custom_lib_paths()
|
||||
default_builtin_lib_paths = OpApiMgr.get_default_builtin_lib_paths()
|
||||
path_list = environ_custom_lib_paths + default_custom_lib_paths + default_builtin_lib_paths
|
||||
for p in path_list:
|
||||
if OpApiMgr.has_symbol(file_path=p, sym=sym):
|
||||
path = p
|
||||
break
|
||||
return path
|
||||
|
||||
@staticmethod
|
||||
def main() -> str:
|
||||
ps = argparse.ArgumentParser(description="Get opapi path", epilog="Best Regards!")
|
||||
ps.add_argument("-f", "--func", required=True, nargs=1, type=str, help="Func name")
|
||||
args = ps.parse_args()
|
||||
sym = args.func[0]
|
||||
if sym is None or len(sym) == 0:
|
||||
return ""
|
||||
lib = OpApiMgr.judge_lib_path(sym=sym)
|
||||
if lib is None:
|
||||
return ""
|
||||
else:
|
||||
return str(lib)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(format="%(filename)s:%(lineno)d [%(levelname)s] %(message)s", level=logging.INFO)
|
||||
print(OpApiMgr.main(), end="")
|
||||
54
csrc/cmake/scripts/examples/get_soc_info.py
Normal file
54
csrc/cmake/scripts/examples/get_soc_info.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
"""
|
||||
获取 Soc 相关信息
|
||||
|
||||
Examples 场景下, 用于获取 Soc 相关信息.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import ctypes
|
||||
import logging
|
||||
|
||||
|
||||
class SocInfoMgr:
|
||||
@staticmethod
|
||||
def get_soc_name() -> str:
|
||||
acl_lib = ctypes.cdll.LoadLibrary("libascendcl.so")
|
||||
acl_lib.aclrtGetSocName.restype = ctypes.c_char_p
|
||||
rst = acl_lib.aclrtGetSocName()
|
||||
if rst:
|
||||
rst = str(rst, encoding="utf-8")
|
||||
else:
|
||||
rst = ""
|
||||
return rst
|
||||
|
||||
@staticmethod
|
||||
def main() -> str:
|
||||
ps = argparse.ArgumentParser(description="Get soc info", epilog="Best Regards!")
|
||||
ps.add_argument("-i", "--info", required=True, type=str, help="SocInfo")
|
||||
args = ps.parse_args()
|
||||
rst = ""
|
||||
if args.info == "soc_name":
|
||||
rst = SocInfoMgr.get_soc_name()
|
||||
else:
|
||||
logging.error("Unknown SocInfo name %s", args.info)
|
||||
return rst
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(format="%(filename)s:%(lineno)d [%(levelname)s] %(message)s", level=logging.INFO)
|
||||
g_rst = ""
|
||||
try:
|
||||
g_rst = SocInfoMgr.main()
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
print(g_rst, end="")
|
||||
35
csrc/cmake/scripts/fix_format.sh
Normal file
35
csrc/cmake/scripts/fix_format.sh
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
set -e
|
||||
if find $1 -type f -name "GroupedMatmul_*json" -print0 | grep -q .; then
|
||||
echo "Found GroupedMatmul_*json files, performing sed operation..."
|
||||
find $1 -type f -name "GroupedMatmul_*json" -print0 | xargs -0 sed -i 's/FormatAgnostic/FormatDefault/g'
|
||||
fi
|
||||
|
||||
if find $1 -type f -name "MlaProlog_*json" -print0 | grep -q .; then
|
||||
echo "Found MlaProlog_*json files, performing sed operation..."
|
||||
find $1 -type f -name "MlaProlog_*json" -print0 | xargs -0 sed -i 's/FormatAgnostic/FormatDefault/g'
|
||||
fi
|
||||
|
||||
if find $1 -type f -name "MlaPrologV2_*json" -print0 | grep -q .; then
|
||||
echo "Found MlaPrologV2_*json files, performing sed operation..."
|
||||
find $1 -type f -name "MlaPrologV2_*json" -print0 | xargs -0 sed -i 's/FormatAgnostic/FormatDefault/g'
|
||||
fi
|
||||
|
||||
if find $1 -type f -name "MlaPrologV3_*json" -print0 | grep -q .; then
|
||||
echo "Found MlaPrologV3_*json files, performing sed operation..."
|
||||
find $1 -type f -name "MlaPrologV3_*json" -print0 | xargs -0 sed -i 's/FormatAgnostic/FormatDefault/g'
|
||||
fi
|
||||
|
||||
if find $1 -type f -name "GroupedMatmulSwigluQuant_*json" -print0 | grep -q .; then
|
||||
echo "Found GroupedMatmulSwigluQuant_*json files, performing sed operation..."
|
||||
find $1 -type f -name "GroupedMatmulSwigluQuant_*json" -print0 | xargs -0 sed -i 's/FormatAgnostic/FormatDefault/g'
|
||||
fi
|
||||
282
csrc/cmake/scripts/parse_changed_files.py
Normal file
282
csrc/cmake/scripts/parse_changed_files.py
Normal file
@@ -0,0 +1,282 @@
|
||||
#!/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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
"""
|
||||
获取修改文件应触发的测试范围.
|
||||
|
||||
当前仅支持对应触发的 UTest 用例进行分析, 切仅支持 ops_test 这个 UTest 目标.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
class Module:
|
||||
def __init__(self, name):
|
||||
self.name: str = name
|
||||
self.src_files: list[Path] = []
|
||||
self.src_exclude_files: list[Path] = []
|
||||
self.tests_ut_ops_test_src_files: list[Path] = []
|
||||
self.tests_ut_ops_test_src_exclude_files: list[Path] = []
|
||||
self.tests_ut_ops_test_options: list[str] = []
|
||||
self.options: list[str] = []
|
||||
self.test_excludes: list[str] = []
|
||||
|
||||
@staticmethod
|
||||
def _add_str_cfg(src, dst: list[str]):
|
||||
if isinstance(src, str):
|
||||
src = [src]
|
||||
for s in src:
|
||||
if s not in dst:
|
||||
dst.append(s)
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _add_test_excludes(test_options, dst: list[str]):
|
||||
if isinstance(test_options, dict):
|
||||
if "examples" in test_options and not test_options["examples"]:
|
||||
dst.append("examples")
|
||||
if "ut" in test_options and not test_options["ut"]:
|
||||
dst.append("ut")
|
||||
return True
|
||||
|
||||
def update_classify_cfg(self, desc: dict[str, Any]) -> bool:
|
||||
if not self._update_src(desc=desc):
|
||||
return False
|
||||
if not self._update_exclude_src(desc=desc):
|
||||
return False
|
||||
if not self._update_test_excludes(desc=desc):
|
||||
return False
|
||||
return self._update_options(desc=desc)
|
||||
|
||||
def get_test_options(self, f: Path) -> list[str]:
|
||||
def is_excluded(e_f: Path):
|
||||
for e in self.src_exclude_files:
|
||||
try:
|
||||
e_f.relative_to(e)
|
||||
return True
|
||||
except ValueError:
|
||||
continue
|
||||
return False
|
||||
|
||||
related_options: list[str] = []
|
||||
for s in self.src_files:
|
||||
if is_excluded(e_f=f):
|
||||
continue
|
||||
try:
|
||||
if f.relative_to(s):
|
||||
# 当同一个修改文件需要触发多个 Options 时, 需要把这些 Options 全部添加
|
||||
related_options.extend(self.options)
|
||||
except ValueError:
|
||||
continue
|
||||
# 关联 Options 去重
|
||||
related_options = list(set(related_options))
|
||||
return related_options
|
||||
|
||||
def get_test_example_ops_test_options(self, f: Path) -> list[str]:
|
||||
return self.get_test_options(f)
|
||||
|
||||
def print_details(self):
|
||||
dbg_str = (
|
||||
f"Name={self.name} SrcLen={len(self.src_files)} "
|
||||
f"TestUtOpsTestSrcLen={len(self.tests_ut_ops_test_src_files)} "
|
||||
f"TestUtOpsTestOptions={self.options} "
|
||||
f"TestUtOpsTestOptions={self.tests_ut_ops_test_options}"
|
||||
)
|
||||
logging.debug(dbg_str)
|
||||
|
||||
def _add_rel_path(self, src, dst: list[Path]):
|
||||
if isinstance(src, (str, Path)):
|
||||
src = [src]
|
||||
for p in src:
|
||||
p = Path(p)
|
||||
if p.is_absolute():
|
||||
logging.error("[%s]'s Path[%s] is absolute path.", self.name, p)
|
||||
return False
|
||||
if p not in dst:
|
||||
dst.append(p)
|
||||
return True
|
||||
|
||||
def _update_src(self, desc: dict[str, Any]) -> bool:
|
||||
src_paths = desc.get("src", [])
|
||||
return self._add_rel_path(src=src_paths, dst=self.src_files)
|
||||
|
||||
def _update_exclude_src(self, desc: dict[str, Any]) -> bool:
|
||||
src_paths = desc.get("exclude", [])
|
||||
return self._add_rel_path(src=src_paths, dst=self.src_exclude_files)
|
||||
|
||||
def _update_test_excludes(self, desc: dict[str, Any]) -> bool:
|
||||
test_options = desc.get("test", [])
|
||||
return self._add_test_excludes(test_options=test_options, dst=self.test_excludes)
|
||||
|
||||
def _update_options(self, desc: dict[str, Any]) -> bool:
|
||||
options = desc.get("options", [])
|
||||
return self._add_str_cfg(src=options, dst=self.options)
|
||||
|
||||
|
||||
class Parser:
|
||||
"""
|
||||
规则文件、修改文件列表文件解析.
|
||||
"""
|
||||
|
||||
_Modules: list[Module] = [] # 保存规则文件(tests/test_config.yaml)内设置的模块列表
|
||||
_ChangedPaths: list[Path] = [] # 修改文件列表文件(changed_file)内设置的修改文件列表
|
||||
_UTExcludes: list[str] = []
|
||||
_ExamplesExcludes: list[str] = []
|
||||
|
||||
@classmethod
|
||||
def print_details(cls):
|
||||
for m in cls._Modules:
|
||||
m.print_details()
|
||||
for p in cls._ChangedPaths:
|
||||
logging.debug(p)
|
||||
|
||||
@classmethod
|
||||
def parse_classify_file(cls, file: Path) -> bool:
|
||||
file = Path(file).resolve()
|
||||
if not file.exists():
|
||||
logging.error("Classify file(%s) not exist.", file)
|
||||
return False
|
||||
with open(file, encoding="utf-8") as f:
|
||||
desc: dict[str, Any] = yaml.load(f, Loader=yaml.SafeLoader)
|
||||
|
||||
def extract_from_dict(obj, current_key="root") -> bool:
|
||||
# 只看 dict 类型
|
||||
if not isinstance(obj, dict):
|
||||
return True
|
||||
|
||||
# 递归到 module 时说明到达最后一层
|
||||
if "module" in obj:
|
||||
return cls._parse_classify_item(current_key, desc)
|
||||
|
||||
# 递归处理其他值
|
||||
return all(extract_from_dict(value, key) for key, value in obj.items())
|
||||
|
||||
return extract_from_dict(desc)
|
||||
|
||||
@classmethod
|
||||
def parse_changed_file(cls, file: Path) -> bool:
|
||||
file = Path(file).resolve()
|
||||
if not file.exists():
|
||||
logging.error("Change files desc file(%s) not exist.", file)
|
||||
return False
|
||||
with open(file) as fh:
|
||||
lines = fh.readlines()
|
||||
for cur_line in lines:
|
||||
cur_line = cur_line.strip()
|
||||
f = Path(cur_line)
|
||||
if f.is_absolute():
|
||||
logging.error("%s is absolute path.", f)
|
||||
return False
|
||||
cls._ChangedPaths.append(f)
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def get_related_ut(cls):
|
||||
ops_test_option_lst: list[str] = []
|
||||
for p in cls._ChangedPaths:
|
||||
for m in cls._Modules:
|
||||
new_options = m.get_test_options(f=p)
|
||||
for opt in new_options:
|
||||
if opt not in ops_test_option_lst:
|
||||
ops_test_option_lst.append(opt)
|
||||
if len(ops_test_option_lst) == 0:
|
||||
logging.info("Don't trigger any UT.")
|
||||
return ""
|
||||
ops_test_ut_str: str = ""
|
||||
if "all" in ops_test_option_lst:
|
||||
ops_test_ut_str = "all"
|
||||
else:
|
||||
for opt in ops_test_option_lst:
|
||||
if opt not in cls._UTExcludes:
|
||||
ops_test_ut_str += f"{opt};"
|
||||
ops_test_ut_str = f"{ops_test_ut_str}"
|
||||
logging.info("Trigger UT: %s", ops_test_ut_str)
|
||||
return ops_test_ut_str
|
||||
|
||||
@classmethod
|
||||
def get_ops_test_option_lst(cls) -> list[str]:
|
||||
ops_test_option_lst: list[str] = []
|
||||
for p in cls._ChangedPaths:
|
||||
for m in cls._Modules:
|
||||
new_options = m.get_test_example_ops_test_options(f=p)
|
||||
for opt in new_options:
|
||||
if opt not in ops_test_option_lst:
|
||||
ops_test_option_lst.append(opt)
|
||||
return ops_test_option_lst
|
||||
|
||||
@classmethod
|
||||
def get_related_examples(cls) -> str:
|
||||
ops_test_option_lst = cls.get_ops_test_option_lst()
|
||||
if len(ops_test_option_lst) == 0:
|
||||
logging.info("Don't trigger any examples.")
|
||||
return ""
|
||||
ops_test_examples_str: str = ""
|
||||
if "all" in ops_test_option_lst:
|
||||
ops_test_examples_str = "all"
|
||||
else:
|
||||
for opt in ops_test_option_lst:
|
||||
if opt not in cls._ExamplesExcludes:
|
||||
ops_test_examples_str += f"{opt};"
|
||||
ops_test_examples_str = f"{ops_test_examples_str}"
|
||||
logging.info("Trigger examples: %s", ops_test_examples_str)
|
||||
return ops_test_examples_str
|
||||
|
||||
@classmethod
|
||||
def _parse_classify_item(cls, name: str, desc: dict[str, Any] | None = None) -> bool:
|
||||
if desc is None:
|
||||
logging.error("[%s]'s desc is None.", name)
|
||||
return False
|
||||
if desc.get("module", False):
|
||||
mod = Module(name=name)
|
||||
rst = mod.update_classify_cfg(desc=desc)
|
||||
if rst:
|
||||
cls._Modules.append(mod)
|
||||
short_name = name.split("/")[-1]
|
||||
if "examples" in mod.test_excludes:
|
||||
cls._ExamplesExcludes.append(short_name)
|
||||
if "ut" in mod.test_excludes:
|
||||
cls._UTExcludes.append(short_name)
|
||||
return rst
|
||||
return all(cls._parse_classify_item(name=name + "/" + k, desc=sub_desc) for k, sub_desc in desc.items())
|
||||
|
||||
@staticmethod
|
||||
def main() -> str:
|
||||
# 参数注册
|
||||
ps = argparse.ArgumentParser(description="Parse changed files", epilog="Best Regards!")
|
||||
ps.add_argument("-c", "--classify", required=True, nargs=1, type=Path, help="tests/test_config.yaml")
|
||||
ps.add_argument("-f", "--file", required=True, nargs=1, type=Path, help="changed files desc file.")
|
||||
# 子命令行
|
||||
sub_ps = ps.add_subparsers(help="Sub-Command")
|
||||
p_ut = sub_ps.add_parser("get_related_ut", help="Get related ut.")
|
||||
p_ut.set_defaults(func=Parser.get_related_ut)
|
||||
p_examples = sub_ps.add_parser("get_related_examples", help="Get related examples.")
|
||||
p_examples.set_defaults(func=Parser.get_related_examples)
|
||||
# 处理
|
||||
args = ps.parse_args()
|
||||
logging.debug(args)
|
||||
if not Parser.parse_classify_file(file=Path(args.classify[0])):
|
||||
return ""
|
||||
if not Parser.parse_changed_file(file=Path(args.file[0])):
|
||||
return ""
|
||||
Parser.print_details()
|
||||
rst = args.func()
|
||||
return rst
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(
|
||||
format="[%(asctime)s][%(filename)s:%(lineno)d] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO
|
||||
)
|
||||
print(Parser.main())
|
||||
178
csrc/cmake/scripts/prepare.sh
Normal file
178
csrc/cmake/scripts/prepare.sh
Normal file
@@ -0,0 +1,178 @@
|
||||
#!/bin/bash
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
# 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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
CPU_NUM=$(($(cat /proc/cpuinfo | grep "^processor" | wc -l)*2))
|
||||
JOB_NUM="-j${CPU_NUM}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-s)
|
||||
PATH_TO_SOURCE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-b)
|
||||
PATH_TO_BUILD="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p)
|
||||
ASCEND_CANN_PACKAGE_PATH="$2"
|
||||
shift 2
|
||||
;;
|
||||
--autogen-dir)
|
||||
ASCEND_AUTOGEN_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--build-open-project)
|
||||
BUILD_OPEN_PROJECT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--binary-out-dir)
|
||||
ASCEND_BINARY_OUT_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--impl-out-dir)
|
||||
ASCEND_IMPL_OUT_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--op-build-tool)
|
||||
OP_BUILD_TOOL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ascend-cmake-dir)
|
||||
ASCEND_CMAKE_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--tiling-key)
|
||||
TILING_KEY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ops-compile-options)
|
||||
OPS_COMPILE_OPTIONS="$2"
|
||||
shift 2
|
||||
;;
|
||||
--check-compatible)
|
||||
CHECK_COMPATIBLE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ascend-compute_unit)
|
||||
ASCEND_COMPUTE_UNIT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ascend-op_name)
|
||||
ASCEND_OP_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--op_debug_config)
|
||||
OP_DEBUG_CONFIG="$2"
|
||||
shift 2
|
||||
;;
|
||||
--build_type)
|
||||
BUILD_TYPE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--version)
|
||||
VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--build_ops_rty_kernel)
|
||||
BUILD_OPS_RTY_KERNEL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--enable_built_in)
|
||||
ENABLE_BUILT_IN="$2"
|
||||
shift 2
|
||||
;;
|
||||
--enable_static)
|
||||
ENABLE_STATIC="$2"
|
||||
shift 2
|
||||
;;
|
||||
--enable_experimental)
|
||||
ENABLE_EXPERIMENTAL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--enable_ccache)
|
||||
ENABLE_CCACHE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--enable_oom)
|
||||
ENABLE_OOM="$2"
|
||||
shift 2
|
||||
;;
|
||||
--cann_3rd_lib_path)
|
||||
CANN_3RD_LIB_PATH="$(realpath $2)"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
function clean() {
|
||||
if [ -n "${PATH_TO_BUILD}" ];then
|
||||
rm -rf ${PATH_TO_BUILD}
|
||||
mkdir -p ${PATH_TO_BUILD}
|
||||
fi
|
||||
}
|
||||
|
||||
function convert_string() {
|
||||
local _input=$1
|
||||
_output=$(echo $_input | sed 's/::/;/g')
|
||||
echo "${_output}"
|
||||
}
|
||||
|
||||
function set_env() {
|
||||
CONVERT_TILING_KEY="$(convert_string ${TILING_KEY})"
|
||||
|
||||
CONVERT_OPS_COMPILE_OPTIONS="$(convert_string ${OPS_COMPILE_OPTIONS})"
|
||||
|
||||
CONVERT_ASCEND_COMPUTE_UNIT="$(convert_string ${ASCEND_COMPUTE_UNIT})"
|
||||
|
||||
CONVERT_ASCEND_OP_NAME="$(convert_string ${ASCEND_OP_NAME})"
|
||||
}
|
||||
|
||||
function build() {
|
||||
cd ${PATH_TO_BUILD}
|
||||
cmake ${PATH_TO_SOURCE} \
|
||||
-DBUILD_OPEN_PROJECT=${BUILD_OPEN_PROJECT} \
|
||||
-DPREPARE_BUILD=ON \
|
||||
-DCUSTOM_ASCEND_CANN_PACKAGE_PATH=${ASCEND_CANN_PACKAGE_PATH} \
|
||||
-DASCEND_AUTOGEN_DIR=${ASCEND_AUTOGEN_DIR} \
|
||||
-DASCEND_BINARY_OUT_DIR=${ASCEND_BINARY_OUT_DIR} \
|
||||
-DASCEND_IMPL_OUT_DIR=${ASCEND_IMPL_OUT_DIR} \
|
||||
-DOP_BUILD_TOOL=${OP_BUILD_TOOL} \
|
||||
-DASCEND_CMAKE_DIR=${ASCEND_CMAKE_DIR} \
|
||||
-DCHECK_COMPATIBLE=${CHECK_COMPATIBLE} \
|
||||
-DTILING_KEY="${CONVERT_TILING_KEY}" \
|
||||
-DOPS_COMPILE_OPTIONS="${CONVERT_OPS_COMPILE_OPTIONS}" \
|
||||
-DASCEND_COMPUTE_UNIT=${CONVERT_ASCEND_COMPUTE_UNIT} \
|
||||
-DASCEND_OP_NAME=${CONVERT_ASCEND_OP_NAME} \
|
||||
-DENABLE_CCACHE=${ENABLE_CCACHE} \
|
||||
-DBUILD_OPS_RTY_KERNEL=${BUILD_OPS_RTY_KERNEL} \
|
||||
-DENABLE_BUILT_IN=${ENABLE_BUILT_IN} \
|
||||
-DENABLE_STATIC=${ENABLE_STATIC} \
|
||||
-DENABLE_EXPERIMENTAL=${ENABLE_EXPERIMENTAL} \
|
||||
-DOP_DEBUG_CONFIG=${OP_DEBUG_CONFIG} \
|
||||
-DCANN_3RD_LIB_PATH=${CANN_3RD_LIB_PATH} \
|
||||
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
|
||||
-DVERSION=${VERSION} \
|
||||
-DENABLE_OOM=${ENABLE_OOM}
|
||||
|
||||
make ${JOB_NUM} prepare_build
|
||||
}
|
||||
|
||||
function main() {
|
||||
clean
|
||||
set_env
|
||||
build
|
||||
}
|
||||
|
||||
main
|
||||
222
csrc/cmake/scripts/utest/gen_coverage.py
Normal file
222
csrc/cmake/scripts/utest/gen_coverage.py
Normal file
@@ -0,0 +1,222 @@
|
||||
#!/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 dataclasses
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
class GenCoverage:
|
||||
@dataclasses.dataclass
|
||||
class Param:
|
||||
source_dir: Path | None = None
|
||||
data_dir: Path | None = None
|
||||
info_file: Path | None = None
|
||||
info_file_filtered: Path | None = None
|
||||
html_report_dir: Path | None = None
|
||||
filter_str: str = ""
|
||||
|
||||
@staticmethod
|
||||
def get_exclude_paths_from_yaml(yaml_path: Path):
|
||||
with open(yaml_path, encoding="utf-8") as file:
|
||||
data = yaml.safe_load(file)
|
||||
|
||||
exclude_paths = set()
|
||||
|
||||
def extract_from_dict(obj, current_key=None):
|
||||
if isinstance(obj, dict):
|
||||
if "exclude" in obj and isinstance(obj["exclude"], list):
|
||||
for path in obj["exclude"]:
|
||||
exclude_paths.add(path)
|
||||
if "ut_cov_exclude" in obj and isinstance(obj["ut_cov_exclude"], list):
|
||||
for path in obj["ut_cov_exclude"]:
|
||||
exclude_paths.add(path)
|
||||
|
||||
# 递归处理所有值
|
||||
for key, value in obj.items():
|
||||
extract_from_dict(value, key)
|
||||
elif isinstance(obj, list):
|
||||
for item in obj:
|
||||
extract_from_dict(item, current_key)
|
||||
|
||||
extract_from_dict(data)
|
||||
|
||||
return exclude_paths
|
||||
|
||||
def init_filter_str(self, fs: list[list[str]] | None):
|
||||
if not fs:
|
||||
return
|
||||
for fl in fs:
|
||||
self.filter_str += f"{fl[0]} "
|
||||
|
||||
def init_filter_str_from_yaml(self, source_dir: Path, yaml_path: Path):
|
||||
exclude_paths = self.get_exclude_paths_from_yaml(yaml_path)
|
||||
|
||||
for path in sorted(exclude_paths):
|
||||
if path.startswith("*"):
|
||||
lcov_path = path
|
||||
else:
|
||||
full_path = source_dir / Path(path)
|
||||
if full_path.is_dir():
|
||||
lcov_path = f"{full_path}/*"
|
||||
else:
|
||||
lcov_path = f"{full_path}"
|
||||
|
||||
self.filter_str += f"{lcov_path} "
|
||||
|
||||
@classmethod
|
||||
def main(cls):
|
||||
# 参数注册
|
||||
parser = argparse.ArgumentParser(description="Generate Coverage", epilog="Best Regards!")
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--source_base_dir",
|
||||
required=True,
|
||||
nargs=1,
|
||||
type=Path,
|
||||
help="Explicitly specify the source base directory.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--coverage_data_dir",
|
||||
required=True,
|
||||
nargs=1,
|
||||
type=Path,
|
||||
help="Explicitly specify the *.da's base directory.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i", "--info_file", required=False, nargs=1, type=Path, help="Explicitly specify coverage info file path."
|
||||
)
|
||||
# 考虑最低支持 Python 版本为 3.7, 此处用 append 而非 extend
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--filter",
|
||||
required=False,
|
||||
action="append",
|
||||
nargs="*",
|
||||
type=str,
|
||||
help="Explicitly specify filter file/dir in coverage info.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-y",
|
||||
"--yaml",
|
||||
required=False,
|
||||
nargs=1,
|
||||
type=Path,
|
||||
help="Explicitly specify filter file/dir from tests/test_config.yaml.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--html_report", required=False, nargs=1, type=Path, help="Explicitly specify coverage html report dir."
|
||||
)
|
||||
# 参数解析, 默认值处理
|
||||
p = cls.Param()
|
||||
args = parser.parse_args()
|
||||
p.source_dir = Path(args.source_base_dir[0]).absolute()
|
||||
p.data_dir = Path(args.coverage_data_dir[0]).absolute()
|
||||
if args.info_file:
|
||||
p.info_file = Path(args.info_file[0]).absolute()
|
||||
p.info_file_filtered = Path(p.info_file.parent, f"{p.info_file.stem}_filtered{p.info_file.suffix}")
|
||||
else:
|
||||
p.info_file = Path(p.data_dir, "cov_result/coverage.info")
|
||||
p.info_file_filtered = p.info_file
|
||||
p.html_report_dir = args.html_report[0] if args.html_report else Path(p.info_file.parent, "html_report")
|
||||
p.html_report_dir = Path(p.html_report_dir).absolute()
|
||||
p.init_filter_str(fs=args.filter)
|
||||
p.init_filter_str_from_yaml(source_dir=p.source_dir, yaml_path=args.yaml[0]) if args.yaml else None
|
||||
logging.debug("[DEBUG] filter_str=%s", p.filter_str)
|
||||
# 参数检查
|
||||
if not p.data_dir.exists():
|
||||
logging.error("[ERROR] The dir(%s) required to find the .da files not exist.", p.data_dir)
|
||||
exit(1)
|
||||
if not p.info_file.exists():
|
||||
p.info_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not p.html_report_dir.exists():
|
||||
p.html_report_dir.mkdir(parents=True, exist_ok=True)
|
||||
# 环境检查
|
||||
if not cls._chk_env():
|
||||
exit(1)
|
||||
# 生成覆盖率数据
|
||||
cls._gen_cov(param=p)
|
||||
|
||||
@classmethod
|
||||
def _chk_env(cls):
|
||||
try:
|
||||
ret = subprocess.run(["lcov", "--version"], capture_output=True, check=True, encoding="utf-8")
|
||||
ret.check_returncode()
|
||||
except FileNotFoundError:
|
||||
logging.error("[ERROR] lcov is required to generate coverage data, please install.")
|
||||
return False
|
||||
try:
|
||||
ret = subprocess.run(["genhtml", "--version"], capture_output=True, check=True, encoding="utf-8")
|
||||
ret.check_returncode()
|
||||
except FileNotFoundError:
|
||||
logging.error("[ERROR] genhtml is required to generate coverage html report, please install.")
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def _gen_cov(cls, param: Param):
|
||||
"""
|
||||
使用 lcov 生成覆盖率
|
||||
"""
|
||||
# 当 log 等级小于 INFO 时,lcov 不带 -q 标签
|
||||
lcov_log_tag = "" if logging.getLogger().level <= logging.INFO else "-q"
|
||||
logging.critical("================================================================================")
|
||||
logging.critical("Coverage Report")
|
||||
logging.critical("================================================================================")
|
||||
|
||||
# 生成覆盖率
|
||||
cmd = f"lcov -c -d {param.data_dir} -o {param.info_file} {lcov_log_tag}"
|
||||
logging.debug("[DEBUG] Generate origin coverage file, cmd=`%s`", cmd)
|
||||
ret = subprocess.run(cmd.split(), capture_output=False, check=True, encoding="utf-8")
|
||||
ret.check_returncode()
|
||||
if param.info_file.stat().st_size == 0:
|
||||
logging.critical("No file found in origin coverage file.")
|
||||
return
|
||||
logging.debug("[DEBUG] Generated origin coverage file %s", param.info_file)
|
||||
# 滤掉某些文件/路径的覆盖率信息
|
||||
cmd = f"lcov --remove {param.info_file} {param.filter_str} -o {param.info_file_filtered} {lcov_log_tag}"
|
||||
logging.debug("[DEBUG] Generate filtered coverage file, cmd=`%s`", cmd)
|
||||
ret = subprocess.run(cmd.split(), capture_output=False, check=True, encoding="utf-8")
|
||||
ret.check_returncode()
|
||||
logging.debug("[DEBUG] Generated filtered coverage file %s", param.info_file_filtered)
|
||||
logging.info("[INFO] Generated coverage result in %s", os.path.dirname(param.info_file))
|
||||
|
||||
if param.info_file_filtered.stat().st_size == 0:
|
||||
logging.critical("No file found in filtered coverage file.")
|
||||
return
|
||||
# 生成 html 报告
|
||||
sub_cmd_prefix = f"-p {param.source_dir}" if param.source_dir else ""
|
||||
cmd = f"genhtml {param.info_file_filtered} {sub_cmd_prefix} -o {param.html_report_dir} {lcov_log_tag}"
|
||||
logging.debug("[DEBUG] Generate filtered coverage html report, cmd=`%s`", cmd)
|
||||
ret = subprocess.run(cmd.split(), capture_output=False, check=True, encoding="utf-8")
|
||||
ret.check_returncode()
|
||||
logging.info("[INFO] Generated filtered coverage html report. %s", param.html_report_dir)
|
||||
# 输出覆盖率数据到终端
|
||||
cmd = f"lcov --list {param.info_file_filtered}"
|
||||
ret = subprocess.run(cmd.split(), capture_output=False, check=True, encoding="utf-8")
|
||||
logging.critical("================================================================================")
|
||||
ret.check_returncode()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 将环境变量中的 ASCEND_GLOBAL_LOG_LEVEL 换算成 python 的 log 等级
|
||||
log_level = (int(os.getenv("ASCEND_GLOBAL_LOG_LEVEL", "3")) + 1) * 10
|
||||
logging.basicConfig(format="[%(asctime)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=log_level)
|
||||
GenCoverage.main()
|
||||
432
csrc/cmake/scripts/utest/gen_tiling_data_stub.py
Normal file
432
csrc/cmake/scripts/utest/gen_tiling_data_stub.py
Normal file
@@ -0,0 +1,432 @@
|
||||
#!/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.
|
||||
# -----------------------------------------------------------------------------------------------------------
|
||||
"""
|
||||
生成 TilingData 桩
|
||||
|
||||
用于 UTest 场景下, 生成 Struct 表示的 TilingData 相关头文件.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import stat
|
||||
from pathlib import Path
|
||||
|
||||
import regex as re
|
||||
|
||||
|
||||
def check_if_new_tiling_file_path_existed(ori_file: Path) -> Path:
|
||||
current_path = ori_file
|
||||
target_dir = ori_file / "op_kernel"
|
||||
while True:
|
||||
if target_dir.is_dir():
|
||||
break
|
||||
parent_path = current_path.parent
|
||||
if parent_path == current_path:
|
||||
return ori_file, False
|
||||
current_path = parent_path
|
||||
target_dir = current_path / "op_kernel"
|
||||
tiling_files = list(target_dir.glob("*tiling_data.h"))
|
||||
if not tiling_files:
|
||||
return ori_file, False
|
||||
new_file = tiling_files[0]
|
||||
new_path = target_dir / new_file.name
|
||||
return new_path, True
|
||||
|
||||
|
||||
def process_class_fields(fields_str, class_name):
|
||||
result = []
|
||||
seen = set()
|
||||
arr_pattern = re.compile(
|
||||
r"^\s*"
|
||||
r"(?P<type>(?:[\w:<>]+\s+)*[\w:<>* &]+?)\s+" # 类型(含修饰符/指针/引用)
|
||||
r"(?P<name>\w+)\s*" # 变量名
|
||||
r"\[(?P<len>\d+)\]" # 数组长度
|
||||
r"(?:\s*=\s*\{\s*[^\}]*\s*})?" # 可选初始化,支持 {} 或 {0, ...}
|
||||
r"\s*;\s*" # 以 ; 结尾
|
||||
r"(?:[ \t]*(?://[^\n]*)?)?$", # 行尾可有 // 注释
|
||||
re.MULTILINE,
|
||||
)
|
||||
var_pattern = re.compile(r"^\s*(?P<type>[\w:<>]+)\s+(?P<name>\w+)\s*(?:=\s*[^;]*)?;", re.MULTILINE)
|
||||
# 逐行处理,保持顺序
|
||||
for line in fields_str.splitlines():
|
||||
# 跳空行或纯注释行
|
||||
if not line.strip() or line.strip().startswith("//"):
|
||||
continue
|
||||
|
||||
m = arr_pattern.match(line)
|
||||
if m:
|
||||
t, n, ln = m.group("type"), m.group("name"), m.group("len")
|
||||
result.append(("array", t, n, ln))
|
||||
seen.add(n)
|
||||
continue
|
||||
|
||||
m = var_pattern.match(line)
|
||||
if m:
|
||||
t, n = m.group("type"), m.group("name")
|
||||
if n not in seen:
|
||||
result.append(("normal", t, n))
|
||||
seen.add(n)
|
||||
continue
|
||||
return result
|
||||
|
||||
|
||||
def find_classes(content):
|
||||
out = []
|
||||
class_re = re.compile(r"\bclass\s+(\w+)\s*{")
|
||||
for m in class_re.finditer(content):
|
||||
class_name = m.group(1)
|
||||
start = m.end()
|
||||
idx = start
|
||||
braces = 1
|
||||
while idx < len(content):
|
||||
c = content[idx]
|
||||
if c == "{":
|
||||
braces += 1
|
||||
elif c == "}":
|
||||
braces -= 1
|
||||
if braces == 0:
|
||||
out.append((class_name, content[start:idx].strip()))
|
||||
break
|
||||
idx += 1
|
||||
return out
|
||||
|
||||
|
||||
def convert_template_tilingkey(ori_file: Path):
|
||||
with open(ori_file) as f:
|
||||
content = f.read()
|
||||
|
||||
classes = find_classes(content)
|
||||
output = []
|
||||
for class_name, fields_str in classes:
|
||||
fields = process_class_fields(fields_str, class_name)
|
||||
output.append(f"BEGIN_TILING_DATA_DEF({class_name})")
|
||||
for entry in fields:
|
||||
if entry[0] == "normal":
|
||||
_, field_type, field_name = entry
|
||||
if field_type in [
|
||||
"uint32_t",
|
||||
"int32_t",
|
||||
"uint8_t",
|
||||
"uint16_t",
|
||||
"float",
|
||||
"uint64_t",
|
||||
"int64_t",
|
||||
"double",
|
||||
]:
|
||||
output.append(f"TILING_DATA_FIELD_DEF({field_type}, {field_name});")
|
||||
else:
|
||||
output.append(f"TILING_DATA_FIELD_DEF_STRUCT({field_type}, {field_name});")
|
||||
elif entry[0] == "array":
|
||||
_, field_type, field_name, field_len = entry
|
||||
output.append(f"TILING_DATA_FIELD_DEF_ARR({field_type}, {field_len}, {field_name});")
|
||||
output.append("END_TILING_DATA_DEF;")
|
||||
output.append(f"REGISTER_TILING_DATA_CLASS({class_name}Op, {class_name})\n")
|
||||
result_code = "\n".join(output)
|
||||
|
||||
return result_code
|
||||
|
||||
|
||||
def process_fields(fields_str, struct_name):
|
||||
field_pattern = re.compile(r"(\w+)\s+(\w+)(?:\s*=\d+)?;")
|
||||
fields = field_pattern.findall(fields_str)
|
||||
return fields
|
||||
|
||||
|
||||
def convert_to_old_tiling_struct_style(redirected_file_path):
|
||||
with open(redirected_file_path) as f:
|
||||
content = f.read()
|
||||
struct_pattern = re.compile(r"struct (\w+) {([^}]*)}", re.DOTALL)
|
||||
structs = struct_pattern.findall(content)
|
||||
output = []
|
||||
for struct_name, fields_str in structs:
|
||||
fields = process_fields(fields_str, struct_name)
|
||||
output.append(f"BEGIN_TILING_DATA_DEF({struct_name})")
|
||||
for field_type, field_name in fields:
|
||||
if field_type in ["uint32_t", "uint8_t", "uint16_t"]:
|
||||
output.append(f"TILING_DATA_FIELD_DEF({field_type}, {field_name});")
|
||||
else:
|
||||
output.append(f"TILING_DATA_FIELD_DEF_STRUCT({field_type}, {field_name});")
|
||||
output.append("END_TILING_DATA_DEF;")
|
||||
output.append(f"REGISTER_TILING_DATA_CLASS({struct_name}Op, {struct_name})\n")
|
||||
result_code = "\n".join(output)
|
||||
return result_code
|
||||
|
||||
|
||||
class Process:
|
||||
_WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
|
||||
_WRITE_MODES = stat.S_IWUSR | stat.S_IRUSR
|
||||
|
||||
@classmethod
|
||||
def _write_file(cls, file: Path, src: str):
|
||||
with os.fdopen(os.open(file, cls._WRITE_FLAGS, cls._WRITE_MODES), "w") as fh:
|
||||
fh.write(src)
|
||||
|
||||
@classmethod
|
||||
def _get_begin_source(cls, ori_file: Path, gen_file: Path) -> str:
|
||||
bgn_src: str = (
|
||||
"/**\n"
|
||||
" * This program is free software, you can redistribute it and/or modify.\n"
|
||||
" * Copyright (c) {year} Huawei Technologies Co., Ltd.\n"
|
||||
" * This file is a part of the CANN Open Software.\n"
|
||||
' * Licensed under CANN Open Software License Agreement Version 2.0 (the "License").\n'
|
||||
" * Please refer to the License for details. "
|
||||
"You may not use this file except in compliance with the License.\n"
|
||||
' * 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.\n"
|
||||
" * See LICENSE in the root of the software repository for the full text of the License.\n"
|
||||
" */\n"
|
||||
).format(year=datetime.datetime.today().year)
|
||||
bgn_src += "\n"
|
||||
bgn_src += ("/*!\n * \\file {gen_file_name}\n * \\brief Generate {ori_file_name}\n */\n").format(
|
||||
gen_file_name=gen_file.name, ori_file_name=ori_file.name
|
||||
)
|
||||
bgn_src += "\n"
|
||||
bgn_src += "#pragma once\n"
|
||||
bgn_src += "\n"
|
||||
return bgn_src
|
||||
|
||||
@classmethod
|
||||
def _get_tiling_source(cls, ori_file: Path, isTemplateTilingKey: bool = False) -> str:
|
||||
"""
|
||||
获取 TilingData 定义源码
|
||||
|
||||
:param ori_file: 原始文件
|
||||
:return: 生成文件内容
|
||||
"""
|
||||
rst_source = (
|
||||
"#include <cstdint>\n#include <cstring>\n#include <securec.h>\n#include <kernel_tiling/kernel_tiling.h>\n\n"
|
||||
)
|
||||
pattern = re.compile(r"[(](.*)[)]", re.S)
|
||||
if isTemplateTilingKey:
|
||||
lines = convert_template_tilingkey(ori_file)
|
||||
lines = lines.splitlines()
|
||||
else:
|
||||
ori_file, existed_flag = check_if_new_tiling_file_path_existed(ori_file)
|
||||
if existed_flag:
|
||||
lines = convert_to_old_tiling_struct_style(ori_file)
|
||||
lines = lines.splitlines()
|
||||
else:
|
||||
with open(ori_file) as fd:
|
||||
lines = fd.readlines()
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
struct_src = ""
|
||||
if line.startswith("BEGIN_TILING_DATA_DEF"):
|
||||
struct_name = re.findall(pattern, line)[0]
|
||||
struct_src += ("#pragma pack(1)\nstruct {}\n").format(struct_name)
|
||||
struct_src += "{\n"
|
||||
struct_offset = 0
|
||||
elif line.startswith("TILING_DATA_FIELD_DEF_ARR"):
|
||||
field_params = re.findall(pattern, line)[0]
|
||||
fds = field_params.split(",")
|
||||
fds_dtype = fds[0].strip()
|
||||
fds_num = int(fds[1].strip())
|
||||
fds_name = fds[2].strip()
|
||||
tmp_src, tmp_offset = cls._get_tmp_src(
|
||||
offset=struct_offset, dtype=fds_dtype, name=fds_name, num=fds_num
|
||||
)
|
||||
struct_src += tmp_src
|
||||
struct_offset += tmp_offset
|
||||
elif line.startswith("TILING_DATA_FIELD_DEF_STRUCT"):
|
||||
field_params = re.findall(pattern, line)[0]
|
||||
fds = field_params.split(",")
|
||||
struct_src += " {} {};\n".format(fds[0].strip(), fds[1].strip())
|
||||
elif line.startswith("TILING_DATA_FIELD_DEF"):
|
||||
field_params = re.findall(pattern, line)[0]
|
||||
fds = field_params.split(",")
|
||||
fds_dtype = fds[0].strip()
|
||||
fds_num = 1
|
||||
fds_name = fds[1].strip()
|
||||
tmp_src, tmp_offset = cls._get_tmp_src(
|
||||
offset=struct_offset, dtype=fds_dtype, name=fds_name, num=fds_num
|
||||
)
|
||||
struct_src += tmp_src
|
||||
struct_offset += tmp_offset
|
||||
elif line.startswith("END_TILING_DATA_DEF"):
|
||||
# 要求结构体满足 8 字节对齐
|
||||
if struct_offset % 8 != 0:
|
||||
pad_num = 8 - (struct_offset % 8)
|
||||
struct_src += " uint8_t {}_PH[{}] = {{}};\n".format(struct_name, pad_num)
|
||||
struct_offset += pad_num
|
||||
struct_src += "};"
|
||||
struct_src += "\n"
|
||||
struct_src += "#pragma pack()\n"
|
||||
struct_src += "\n"
|
||||
struct_src += "inline void Init{struct_name}(uint8_t* tiling, {struct_name}* const_data)\n".format(
|
||||
struct_name=struct_name
|
||||
)
|
||||
struct_src += "{\n"
|
||||
struct_src += (
|
||||
" (void)memcpy_s(const_data, sizeof({struct_name}), tiling, sizeof({struct_name}));\n".format(
|
||||
struct_name=struct_name
|
||||
)
|
||||
)
|
||||
struct_src += "}\n"
|
||||
struct_src += "\n"
|
||||
rst_source += struct_src
|
||||
rst_source += (
|
||||
""
|
||||
"#undef GET_TILING_DATA\n"
|
||||
"#define GET_TILING_DATA(tiling_data, tiling_arg) \\\n"
|
||||
"{struct_name} tiling_data; \\\n"
|
||||
"Init{struct_name}(tiling_arg, &tiling_data)\n"
|
||||
"\n"
|
||||
).format(struct_name=struct_name)
|
||||
return rst_source
|
||||
|
||||
@classmethod
|
||||
def _get_tiling_whole(cls, ori_file: Path, isTemplateTilingKey: bool = False) -> str:
|
||||
with open(ori_file) as f:
|
||||
content = f.read()
|
||||
return content
|
||||
|
||||
@classmethod
|
||||
def _gen_tiling_h(cls, ori_file: Path, gen_dir: Path):
|
||||
gen_file = Path(gen_dir, "_gen_" + ori_file.name)
|
||||
flag = "op_kernel" in [part for part in ori_file.parts]
|
||||
if not gen_file.exists():
|
||||
if not flag:
|
||||
bgn_src = cls._get_begin_source(ori_file=ori_file, gen_file=gen_file)
|
||||
def_src = cls._get_tiling_source(ori_file=ori_file, isTemplateTilingKey=flag)
|
||||
source = bgn_src + def_src
|
||||
else:
|
||||
source = "\n"
|
||||
cls._write_file(file=gen_file, src=source)
|
||||
logging.info("Generate TilingDefFile: %s", gen_file)
|
||||
return gen_file
|
||||
|
||||
@classmethod
|
||||
def _get_type_size(cls, dtype: str):
|
||||
mp = {
|
||||
"int8_t": 1,
|
||||
"int16_t": 2,
|
||||
"int32_t": 4,
|
||||
"int64_t": 8,
|
||||
"uint8_t": 1,
|
||||
"uint16_t": 2,
|
||||
"uint32_t": 4,
|
||||
"uint64_t": 8,
|
||||
"float": 4,
|
||||
}
|
||||
d_len = mp.get(dtype)
|
||||
if d_len is None:
|
||||
raise ValueError(f"Unknown dtype({dtype})")
|
||||
return d_len
|
||||
|
||||
@classmethod
|
||||
def _get_tmp_src(cls, offset: int, dtype: str, name: str, num: int):
|
||||
source = ""
|
||||
result = 0
|
||||
dtype_size = cls._get_type_size(dtype=dtype)
|
||||
|
||||
if offset % dtype_size != 0:
|
||||
pad_num = dtype_size - (offset % dtype_size)
|
||||
source += " uint8_t {}_PH[{}] = {{}};\n".format(name, pad_num)
|
||||
result += pad_num
|
||||
|
||||
if num == 1:
|
||||
source += " {} {} = 0;\n".format(dtype, name)
|
||||
else:
|
||||
source += " {} {}[{}] = {{}};\n".format(dtype, name, num)
|
||||
result += cls._get_type_size(dtype=dtype) * num
|
||||
return source, result
|
||||
|
||||
@classmethod
|
||||
def gen_tiling_h(cls, ori_files: list[Path], gen_dir: Path):
|
||||
gen_files: list[Path] = []
|
||||
gen_dir.mkdir(parents=True, exist_ok=True)
|
||||
for ori_file in ori_files:
|
||||
if not ori_file.exists():
|
||||
raise ValueError(f"Origin file({ori_file}) not exist.")
|
||||
gen_file = cls._gen_tiling_h(ori_file=ori_file, gen_dir=gen_dir)
|
||||
gen_files.append(gen_file)
|
||||
return gen_files
|
||||
|
||||
@classmethod
|
||||
def gen_tiling_data_h(cls, op: str, gen_files: list[Path], data_file: Path):
|
||||
if not data_file.exists():
|
||||
bgn_src = cls._get_begin_source(ori_file=data_file, gen_file=data_file)
|
||||
def_src = ""
|
||||
for gen_f in gen_files:
|
||||
def_src += '#include "tiling/{op}/{file_name}"\n'.format(op=op, file_name=gen_f.name)
|
||||
source = bgn_src + def_src
|
||||
cls._write_file(file=data_file, src=source)
|
||||
logging.info("Generate TilingDataFile: %s", data_file)
|
||||
return data_file
|
||||
|
||||
@classmethod
|
||||
def gen_tiling_stub_h(cls, data_file: Path, stub_file: Path):
|
||||
if not stub_file.exists():
|
||||
bgn_src = cls._get_begin_source(ori_file=stub_file, gen_file=stub_file)
|
||||
def_src = ""
|
||||
def_src += '#include "{}"\n'.format(data_file.name)
|
||||
def_src += (
|
||||
"\n"
|
||||
"#undef GET_TILING_DATA_WITH_STRUCT\n"
|
||||
"#define GET_TILING_DATA_WITH_STRUCT(tiling_struct, tiling_data, tiling_arg) \\\n"
|
||||
"tiling_struct tiling_data; \\\n"
|
||||
"(void)memcpy_s(&tiling_data, sizeof(tiling_struct), tiling_arg, sizeof(tiling_struct));\n"
|
||||
"\n"
|
||||
)
|
||||
def_src += (
|
||||
"\n"
|
||||
"#undef GET_TILING_DATA_MEMBER\n"
|
||||
"#define GET_TILING_DATA_MEMBER(tiling_type, member, var, tiling) \\\n"
|
||||
"decltype(tiling_type::member) var; \\\n"
|
||||
"size_t offset##var = (size_t)(&((tiling_type *)0)->member); \\\n"
|
||||
"(void)memcpy_s(&var, sizeof(decltype(var)), tiling + offset##var, sizeof(decltype(var))); \n"
|
||||
)
|
||||
source = bgn_src + def_src
|
||||
cls._write_file(file=stub_file, src=source)
|
||||
logging.info("Generate TilingStubFile: %s", stub_file)
|
||||
return stub_file
|
||||
|
||||
@classmethod
|
||||
def main(cls):
|
||||
# 参数注册
|
||||
parser = argparse.ArgumentParser(description="TilingData Generator", epilog="Best Regards!")
|
||||
parser.add_argument("-o", "--operator", required=True, nargs=1, type=str, help="Target operator.")
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--srcs",
|
||||
required=True,
|
||||
action="append",
|
||||
nargs="+",
|
||||
type=Path,
|
||||
help="Origin tiling data define files(.h).",
|
||||
)
|
||||
parser.add_argument("-d", "--dest", required=True, nargs=1, type=Path, help="Generate directory.")
|
||||
# 参数解析
|
||||
result = parser.parse_args()
|
||||
op = result.operator[0].lower()
|
||||
ori_files: list[Path] = []
|
||||
for file in result.srcs:
|
||||
ori_files.append(file[0].absolute())
|
||||
gen_dir = Path(result.dest[0], "tiling/{}".format(op)).absolute()
|
||||
data_file = Path(gen_dir, "tiling_data.h")
|
||||
stub_file = Path(gen_dir, "tiling_stub.h")
|
||||
|
||||
# 流程处理
|
||||
gen_files = cls.gen_tiling_h(ori_files=ori_files, gen_dir=gen_dir)
|
||||
cls.gen_tiling_data_h(op=op, gen_files=gen_files, data_file=data_file)
|
||||
cls.gen_tiling_stub_h(data_file=data_file, stub_file=stub_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(format="%(filename)s:%(lineno)d [%(levelname)s] %(message)s", level=logging.DEBUG)
|
||||
try:
|
||||
Process.main()
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
raise e
|
||||
534
csrc/cmake/scripts/util/ascendc_bin_param_build.py
Normal file
534
csrc/cmake/scripts/util/ascendc_bin_param_build.py
Normal 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)
|
||||
77
csrc/cmake/scripts/util/ascendc_gen_options.py
Normal file
77
csrc/cmake/scripts/util/ascendc_gen_options.py
Normal 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)
|
||||
781
csrc/cmake/scripts/util/ascendc_impl_build.py
Normal file
781
csrc/cmake/scripts/util/ascendc_impl_build.py
Normal 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)
|
||||
360
csrc/cmake/scripts/util/ascendc_ops_config.py
Normal file
360
csrc/cmake/scripts/util/ascendc_ops_config.py
Normal 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()
|
||||
71
csrc/cmake/scripts/util/const_var.py
Normal file
71
csrc/cmake/scripts/util/const_var.py
Normal 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)
|
||||
18
csrc/cmake/scripts/util/gen_version_info.sh
Normal file
18
csrc/cmake/scripts/util/gen_version_info.sh
Normal 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
|
||||
409
csrc/cmake/scripts/util/opdesc_parser.py
Normal file
409
csrc/cmake/scripts/util/opdesc_parser.py
Normal 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
|
||||
429
csrc/cmake/scripts/util/parse_ini_to_json.py
Normal file
429
csrc/cmake/scripts/util/parse_ini_to_json.py
Normal 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)
|
||||
Reference in New Issue
Block a user