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
|
||||
Reference in New Issue
Block a user