已合并
feat: 抽取原子方法以实现并行编译 wrapper、tiling、kernel #1799
feat: 抽取原子方法以实现并行编译 wrapper、tiling、kernel #1799
已合并
CactiCode创建于 16 天前
6 个文件变更+351-179
@@ -23,7 +23,6 @@ import platform
23import tempfile23import tempfile
24import uuid24import uuid
25from contextlib import contextmanager, nullcontext25from contextlib import contextmanager, nullcontext
26-from concurrent.futures import ThreadPoolExecutor, as_completed
27from threading import Lock26from threading import Lock
28import time27import time
29from dataclasses import dataclass28from dataclasses import dataclass
@@ -333,7 +332,7 @@ def link_pgo_executable(target_file, obj_files, mspti_link_flags):
333 return target_file332 return target_file
334 333 
335 334 
336-def extract_aicore_binary(device_obj_file, output_file):335+def extract_aicore_binary(kernel_obj_path, output_file):
337 objcopy = shutil.which("llvm-objcopy")336 objcopy = shutil.which("llvm-objcopy")
338 if objcopy is None:337 if objcopy is None:
339 objcopy = os.path.join(338 objcopy = os.path.join(
@@ -342,7 +341,7 @@ def extract_aicore_binary(device_obj_file, output_file):
342 if not os.path.isfile(objcopy):341 if not os.path.isfile(objcopy):
343 raise CompileError("llvm-objcopy is required for Inductor PGO device binary")342 raise CompileError("llvm-objcopy is required for Inductor PGO device binary")
344 run_compile_command(343 run_compile_command(
345- [objcopy, "--dump-section", f".aicore_binary={output_file}", device_obj_file],344+ [objcopy, "--dump-section", f".aicore_binary={output_file}", kernel_obj_path],
346 "ExtractPgoDeviceBinary",345 "ExtractPgoDeviceBinary",
347 )346 )
348 if not os.path.isfile(output_file) or os.path.getsize(output_file) == 0:347 if not os.path.isfile(output_file) or os.path.getsize(output_file) == 0:
@@ -831,22 +830,10 @@ def compile_host_objs(args: argparse.Namespace, temp_dir, pch_path=None):
831 if not host_files:830 if not host_files:
832 return []831 return []
833 pch_state = {"path": pch_path, "lock": Lock()}832 pch_state = {"path": pch_path, "lock": Lock()}
834- if len(host_files) == 1:833+ return [
835- return [compile_host_obj_file(args, temp_dir, host_files[0], pch_state)]834+ compile_host_obj_file(args, temp_dir, source_file, pch_state)
836- 835+ for source_file in host_files
837- obj_files = [None] * len(host_files)836+ ]
838- worker_count = get_host_compile_worker_count(len(host_files))
839- with ThreadPoolExecutor(max_workers=worker_count) as executor:
840- future_to_index = {
841- executor.submit(
842- compile_host_obj_file, args, temp_dir, source_file, pch_state
843- ): index
844- for index, source_file in enumerate(host_files)
845- }
846- for future in as_completed(future_to_index):
847- index = future_to_index[future]
848- obj_files[index] = future.result()
849- return obj_files
850 837 
851 838 
852@inductor_compile_duration("CompileHostObj")839@inductor_compile_duration("CompileHostObj")
@@ -909,27 +896,30 @@ def compile_device_obj(args: argparse.Namespace, temp_dir):
909 return f"{temp_dir}/device/{base_device_file}.o"896 return f"{temp_dir}/device/{base_device_file}.o"
910 897 
911 898 
912-@inductor_compile_duration("BuildDeviceSo")899+@inductor_compile_duration("LinkKernelSo")
913-def build_device_so(args: argparse.Namespace, host_obj_path, temp_dir):900+def link_kernel_so(
914- device_obj_path = compile_device_obj(args, temp_dir)901+ args: argparse.Namespace, tiling_obj_paths, temp_dir, kernel_obj_path
902+):
903+ if not kernel_obj_path:
904+ raise ValueError("kernel_obj_path is required for linking")
905+ 
915 target_file = os.path.join(temp_dir, os.path.basename(args.output_file))906 target_file = os.path.join(temp_dir, os.path.basename(args.output_file))
916- obj_files = [device_obj_path]907+ obj_files = [kernel_obj_path]
917- host_obj_paths = normalize_to_list(host_obj_path)908+ tiling_obj_paths = normalize_to_list(tiling_obj_paths)
918- if host_obj_paths:909+ if tiling_obj_paths:
919- obj_files = host_obj_paths + obj_files910+ obj_files = tiling_obj_paths + obj_files
920 obj_files = append_shared_cv_wrapper_so(args, obj_files)911 obj_files = append_shared_cv_wrapper_so(args, obj_files)
921 link_libraries = (912 link_libraries = (
922 CV_HOST_LINK_LIBRARIES913 CV_HOST_LINK_LIBRARIES
923- if host_obj_paths and is_cv_fusion_compile(args)914+ if tiling_obj_paths and is_cv_fusion_compile(args)
924- else (HOST_LINK_LIBRARIES if host_obj_paths else None)915+ else (HOST_LINK_LIBRARIES if tiling_obj_paths else None)
916+ )
917+ return link_shared(
918+ target_file,
919+ obj_files,
920+ link_libraries=link_libraries,
921+ extra_link_options=get_shared_cv_wrapper_rpath_options(args),
925 )922 )
926- with InductorCompileDuration(args, "LinkDeviceSo"):
927- return link_shared(
928- target_file,
929- obj_files,
930- link_libraries=link_libraries,
931- extra_link_options=get_shared_cv_wrapper_rpath_options(args),
932- )
933 923 
934 924 
935def clean_before_modify(temp_dir):925def clean_before_modify(temp_dir):
@@ -1183,56 +1173,64 @@ def try_static_shape_compile(args: argparse.Namespace, temp_dir, so_path):
1183 return True1173 return True
1184 1174 
1185 1175 
1186-def link_host_target(args, temp_dir, pch_path=None):1176+@inductor_compile_duration("LinkTilingSo")
1187- # 处理 host 编译阶段1177+def link_tiling_so(args, tiling_obj_paths, temp_dir):
1188- if pch_path is None:1178+ """Link an existing set of tiling objects into the tiling shared library."""
1189- host_obj_paths = append_shared_cv_wrapper_so(1179+ if not tiling_obj_paths:
1190- args, compile_host_objs(args, temp_dir)1180+ raise ValueError("tiling object files are required for linking")
1191- )1181+ 
1192- else:1182+ tiling_obj_paths = append_shared_cv_wrapper_so(
1193- host_obj_paths = append_shared_cv_wrapper_so(1183+ args, normalize_to_list(tiling_obj_paths)
1194- args, compile_host_objs(args, temp_dir, pch_path)1184+ )
1195- )
1196 so_file = os.path.join(temp_dir, os.path.basename(args.output_file))1185 so_file = os.path.join(temp_dir, os.path.basename(args.output_file))
1197 link_libraries = (1186 link_libraries = (
1198 CV_HOST_LINK_LIBRARIES if is_cv_fusion_compile(args) else HOST_LINK_LIBRARIES1187 CV_HOST_LINK_LIBRARIES if is_cv_fusion_compile(args) else HOST_LINK_LIBRARIES
1199 )1188 )
1200 if getattr(args, "pgo_runner_file", None) is not None:1189 if getattr(args, "pgo_runner_file", None) is not None:
1201 link_libraries = link_libraries + ["ascendcl", "runtime"]1190 link_libraries = link_libraries + ["ascendcl", "runtime"]
1202- with InductorCompileDuration(args, "LinkHostSo"):1191+ return link_shared(
1203- link_shared(1192+ so_file,
1204- so_file,1193+ tiling_obj_paths,
1205- host_obj_paths,1194+ link_libraries=link_libraries,
1206- link_libraries=link_libraries,1195+ extra_link_options=get_shared_cv_wrapper_rpath_options(args),
1207- extra_link_options=get_shared_cv_wrapper_rpath_options(args),1196+ )
1208- )
1209- return so_file
1210 1197 
1211 1198 
1212@inductor_compile_duration("BuildKernelTarget")1199@inductor_compile_duration("BuildKernelTarget")
1213-def link_kernel_target(args, host_obj_path, temp_dir):1200+def build_kernel_target(args, tiling_obj_paths, temp_dir):
1214- if args.stage == "device":1201+ """Compile device objects and build the final kernel shared library."""
1215- if args.tiling_repr is not None or has_inductor_const_tiling_data(1202+ if args.stage == "device" and (
1216- args, temp_dir1203+ args.tiling_repr is not None or has_inductor_const_tiling_data(args, temp_dir)
1217- ):1204+ ):
1218- if args.tiling_repr is not None:1205+ if args.tiling_repr is not None:
1219- print("process static shape kernel with tiling_repr")1206+ print("process static shape kernel with tiling_repr")
1220- static_shape_kernel_proc(args, temp_dir, args.tiling_repr)1207+ static_shape_kernel_proc(args, temp_dir, args.tiling_repr)
1221 1208 
1222- # 首次编译1209+ kernel_obj_path = compile_device_obj(args, temp_dir)
1223- so_file = build_device_so(args, host_obj_path, temp_dir)1210+ so_file = link_kernel_so(args, tiling_obj_paths, temp_dir, kernel_obj_path)
1224- 1211+ if args.stage == "device" or not try_static_shape_compile(args, temp_dir, so_file):
1225- # kernel_compile场景一次性生成so,链接device.o
1226- if args.stage == "device":
1227 return so_file1212 return so_file
1228 1213 
1229- # jit_compile场景,检测是否为静态shape1214+ # 静态 shape 重编译后,重新生成 device.o 并链接最终产物。
1230- re_compile = try_static_shape_compile(args, temp_dir, so_file)1215+ kernel_obj_path = compile_device_obj(args, temp_dir)
1231- if not re_compile:1216+ return link_kernel_so(args, tiling_obj_paths, temp_dir, kernel_obj_path)
1217+ 
1218+ 
1219+@inductor_compile_duration("BuildKernelTargetFromObjects")
1220+def build_kernel_target_from_objects(args, tiling_obj_paths, kernel_obj_path, temp_dir):
1221+ """Build the final kernel from precompiled host and device objects."""
1222+ if not tiling_obj_paths:
1223+ raise ValueError("host object files are required")
1224+ if not kernel_obj_path:
1225+ raise ValueError("device object path is required")
1226+ 
1227+ so_file = link_kernel_so(args, tiling_obj_paths, temp_dir, kernel_obj_path)
1228+ if not try_static_shape_compile(args, temp_dir, so_file):
1232 return so_file1229 return so_file
1233 1230 
1234- # 重编译,最终产物链接host.o+device.o1231+ # 静态 shape 重编译会修改 device 源码需生成新的 device.o
1235- return build_device_so(args, host_obj_path, temp_dir)1232+ kernel_obj_path = compile_device_obj(args, temp_dir)
1233+ return link_kernel_so(args, tiling_obj_paths, temp_dir, kernel_obj_path)
1236 1234 
1237 1235 
1238@inductor_compile_duration("CopyOutput", args_index=1)1236@inductor_compile_duration("CopyOutput", args_index=1)
@@ -1263,7 +1261,11 @@ def build_host_output(args, pch_path=None):
1263 )1261 )
1264 if should_build_sidecars:1262 if should_build_sidecars:
1265 args.pgo_generation = uuid.uuid4().hex1263 args.pgo_generation = uuid.uuid4().hex
1266- so_file = link_host_target(args, args.temp_dir, pch_path)1264+ if pch_path is None:
1265+ tiling_obj_paths = compile_host_objs(args, args.temp_dir)
1266+ else:
1267+ tiling_obj_paths = compile_host_objs(args, args.temp_dir, pch_path)
1268+ so_file = link_tiling_so(args, tiling_obj_paths, args.temp_dir)
1267 if not should_build_sidecars:1269 if not should_build_sidecars:
1268 return so_file1270 return so_file
1269 try:1271 try:
@@ -1284,21 +1286,72 @@ def build_host_output(args, pch_path=None):
1284 return so_file1286 return so_file
1285 1287 
1286 1288 
1289+def parse_object_paths(value):
1290+ if not value:
1291+ return []
1292+ return [path for path in value.split(";") if path]
1293+ 
1294+ 
1295+def validate_artifact_paths(paths, kind):
1296+ if not paths:
1297+ raise CompileError(f"{kind} artifact paths are empty")
1298+ missing = [
1299+ path for path in paths if not isinstance(path, str) or not os.path.isfile(path)
1300+ ]
1301+ if missing:
1302+ raise CompileError(f"{kind} artifact files are missing: {missing}")
1303+ 
1304+ 
1287def main(args):1305def main(args):
1288 print("compile args:", args)1306 print("compile args:", args)
1289 src_directory = os.getcwd()1307 src_directory = os.getcwd()
1290 os.chdir(args.temp_dir)1308 os.chdir(args.temp_dir)
1291 print("change work dir:", os.getcwd())1309 print("change work dir:", os.getcwd())
1310+ # 原子编译 stage 返回结构化 artifact,避免进程间共享 Python 状态。
1292 try:1311 try:
1293 if args.stage == "host":1312 if args.stage == "host":
1294 with host_compile_batch(args) as pch_path:1313 with host_compile_batch(args) as pch_path:
1295 so_file = build_host_output(args, pch_path)1314 so_file = build_host_output(args, pch_path)
1315+ elif args.stage == "host_obj":
1316+ with host_compile_batch(args) as pch_path:
1317+ tiling_obj_paths = compile_host_objs(args, args.temp_dir, pch_path)
1318+ return {
1319+ "version": 1,
Z

这里是为了兼容性考虑么,后续版本会变化吗

likedislike
CactiCode
6 天前 评论:
1320+ "stage": "host_obj",
1321+ "tiling_obj_paths": tiling_obj_paths,
1322+ "tiling_source_paths": normalize_to_list(args.host_files),
1323+ "shared_cv_wrapper_so": getattr(args, "shared_cv_wrapper_so", None),
1324+ }
1325+ elif args.stage == "device_obj":
1326+ kernel_obj_path = compile_device_obj(args, args.temp_dir)
1327+ return {
1328+ "version": 1,
1329+ "stage": "device_obj",
1330+ "kernel_obj_path": kernel_obj_path,
1331+ "kernel_source_path": args.device_files,
1332+ }
1333+ elif args.stage == "link":
1334+ tiling_obj_paths = parse_object_paths(args.tiling_obj_paths)
1335+ kernel_obj_path = args.kernel_obj_path
1336+ validate_artifact_paths(tiling_obj_paths, "host object")
1337+ validate_artifact_paths(
1338+ [kernel_obj_path, args.kernel_source_path], "device artifact"
1339+ )
1340+ args.host_files = parse_object_paths(args.tiling_source_paths)
1341+ args.device_files = args.kernel_source_path
1342+ args.shared_cv_wrapper_so = args.shared_cv_wrapper_so or None
1343+ so_file = build_kernel_target_from_objects(
1344+ args, tiling_obj_paths, kernel_obj_path, args.temp_dir
1345+ )
1296 elif args.stage == "device":1346 elif args.stage == "device":
1297- so_file = link_kernel_target(args, None, args.temp_dir)1347+ so_file = build_kernel_target(args, None, args.temp_dir)
1298 else: # all1348 else: # all
1299 with host_compile_batch(args) as pch_path:1349 with host_compile_batch(args) as pch_path:
1300- host_obj_paths = compile_host_objs(args, args.temp_dir, pch_path)1350+ if pch_path is None:
1301- so_file = link_kernel_target(args, host_obj_paths, args.temp_dir)1351+ tiling_obj_paths = compile_host_objs(args, args.temp_dir)
1352+ else:
1353+ tiling_obj_paths = compile_host_objs(args, args.temp_dir, pch_path)
1354+ so_file = build_kernel_target(args, tiling_obj_paths, args.temp_dir)
1302 if so_file is not None:1355 if so_file is not None:
1303 copy_so_to_output(so_file, args, src_directory)1356 copy_so_to_output(so_file, args, src_directory)
1304 finally:1357 finally:
@@ -20,6 +20,9 @@ import re
20HOST_DEFAULT_CXX11_ABI = "-D_GLIBCXX_USE_CXX11_ABI=1"20HOST_DEFAULT_CXX11_ABI = "-D_GLIBCXX_USE_CXX11_ABI=1"
21HOST_CXX11_ABI_PREFIX = "-D_GLIBCXX_USE_CXX11_ABI="21HOST_CXX11_ABI_PREFIX = "-D_GLIBCXX_USE_CXX11_ABI="
22INDUCTOR_COMPILE_TRACE_LABEL = "InductorCompile"22INDUCTOR_COMPILE_TRACE_LABEL = "InductorCompile"
23+SOURCES_TILING_STRUCT = "tiling_struct_code"
24+SOURCES_HOST_IMPL = "host_impl_code"
25+SOURCES_KERNEL_IMPL = "kernel_impl_code"
23SPLIT_BEGIN_PREFIX = "// AUTOFUSE_SPLIT_FILE_BEGIN:"26SPLIT_BEGIN_PREFIX = "// AUTOFUSE_SPLIT_FILE_BEGIN:"
24SPLIT_END_PREFIX = "// AUTOFUSE_SPLIT_FILE_END:"27SPLIT_END_PREFIX = "// AUTOFUSE_SPLIT_FILE_END:"
25SPLIT_HEADER_KEY = "TilingHead"28SPLIT_HEADER_KEY = "TilingHead"
@@ -119,7 +122,16 @@ def parse_compile_args(argv):
119 type=str,122 type=str,
120 help="Compile options of tiling and kernel.",123 help="Compile options of tiling and kernel.",
121 )124 )
122- return parser.parse_args(argv)125+ parser.add_argument("--tiling_obj_paths", default="", type=str)
126+ parser.add_argument("--tiling_source_paths", default="", type=str)
127+ parser.add_argument("--kernel_obj_path", default="", type=str)
128+ parser.add_argument("--kernel_source_path", default="", type=str)
129+ parser.add_argument("--shared_cv_wrapper_so", default="", type=str)
130+ # 使用 parse_known_args 容忍上层透传的未声明参数,避免触发 SystemExit 终止编译。
131+ args, unknown = parser.parse_known_args(argv)
132+ if unknown:
133+ print(f"[CompileArgs] ignored unrecognized arguments: {unknown}")
134+ return args
123 135 
124 136 
125def generate_file(dst_dir, file_name, text):137def generate_file(dst_dir, file_name, text):
@@ -311,11 +323,42 @@ def has_inductor_pgo_split(host_impl_code):
311 return bool(keys & {SPLIT_PGO_RUNNER_KEY, SPLIT_PGO_DEVICE_SOURCE_KEY})323 return bool(keys & {SPLIT_PGO_RUNNER_KEY, SPLIT_PGO_DEVICE_SOURCE_KEY})
312 324 
313 325 
314-def write_host_sources(host_file_path, base_host_file, graph_name, host_impl_code):326+def write_single_host_source(host_file_path, base_host_file, host_impl_code):
327+ generate_file(host_file_path, base_host_file, host_impl_code)
328+ return os.path.join(host_file_path, base_host_file)
329+ 
330+ 
331+def write_merged_host_sources(host_file_path, base_host_file, host_impl_code):
332+ """Generate one host source file while preserving split headers.
333+ 
334+ cpp 段合并为单个源文件(单文件编译),header 段仍拆出为独立 .h 文件,
335+ 供 cpp 段中间的 #include "autofuse_tiling_func_*.h" 引用。
336+ """
315 if not has_split_host_marker(host_impl_code):337 if not has_split_host_marker(host_impl_code):
316- generate_file(host_file_path, base_host_file, host_impl_code)338+ return write_single_host_source(host_file_path, base_host_file, host_impl_code)
317- return os.path.join(host_file_path, base_host_file)339+ 
318- return write_split_host_sources(host_file_path, graph_name, host_impl_code)340+ headers, cpp_sources = parse_split_host_sources(host_impl_code)
341+ for key, content in headers.items():
342+ generate_file(host_file_path, SPLIT_HEADER_FILES[key], content)
343+ 
344+ is_split_format = is_versioned_split_format(headers)
345+ merged = []
346+ for _key, cpp_content in cpp_sources:
347+ if is_split_format:
348+ content = cpp_content
349+ else:
350+ content = add_split_header_include(cpp_content)
351+ if merged and content.startswith(SPLIT_HEADER_INCLUDE):
352+ content = content[len(SPLIT_HEADER_INCLUDE):] # fmt: skip
353+ merged.append(content.rstrip("\n"))
354+ generate_file(host_file_path, base_host_file, "\n".join(merged) + "\n")
355+ return os.path.join(host_file_path, base_host_file)
356+ 
357+ 
358+def write_host_sources(host_file_path, base_host_file, graph_name, host_impl_code):
Z

不用的逻辑还需要吗

likedislike
CactiCode
6 天前 评论:
359+ # host 编译为单个 cpp 源文件(cpp 段合并),header 段拆出独立 .h 供 include 引用。
360+ # 多文件拆分逻辑保留在 write_split_host_sources 中备用,但当前不调用。
361+ return write_merged_host_sources(host_file_path, base_host_file, host_impl_code)
319 362 
320 363 
321def parse_env_flags(env_name):364def parse_env_flags(env_name):
@@ -395,7 +438,10 @@ def prepare_compile_context(argv, stage, tiling_repr):
395 args = parse_compile_args(argv)438 args = parse_compile_args(argv)
396 args.stage = stage439 args.stage = stage
397 args.tiling_repr = tiling_repr440 args.tiling_repr = tiling_repr
398- if stage == "host" and HOST_CXX11_ABI_PREFIX not in args.compile_options:441+ if (
442+ stage in ("host", "host_obj")
443+ and HOST_CXX11_ABI_PREFIX not in args.compile_options
444+ ):
399 args.compile_options = (445 args.compile_options = (
400 args.compile_options + " " + HOST_DEFAULT_CXX11_ABI446 args.compile_options + " " + HOST_DEFAULT_CXX11_ABI
401 ).strip()447 ).strip()
@@ -413,8 +459,8 @@ def prepare_compile_context(argv, stage, tiling_repr):
413 459 
414def write_compile_host_sources(sources, args, tiling_def_file, base_host_file):460def write_compile_host_sources(sources, args, tiling_def_file, base_host_file):
415 host_file_path = os.path.join(args.temp_dir, "host")461 host_file_path = os.path.join(args.temp_dir, "host")
416- generate_file(host_file_path, tiling_def_file, sources["tiling_struct_code"])462+ generate_file(host_file_path, tiling_def_file, sources[SOURCES_TILING_STRUCT])
417- host_impl_code = sources["host_impl_code"]463+ host_impl_code = sources[SOURCES_HOST_IMPL]
418 if not (464 if not (
419 has_split_host_marker(host_impl_code) and has_inductor_pgo_split(host_impl_code)465 has_split_host_marker(host_impl_code) and has_inductor_pgo_split(host_impl_code)
420 ):466 ):
@@ -422,12 +468,12 @@ def write_compile_host_sources(sources, args, tiling_def_file, base_host_file):
422 host_file_path, base_host_file, args.graph_name, host_impl_code468 host_file_path, base_host_file, args.graph_name, host_impl_code
423 )469 )
424 return470 return
425- if args.stage != "host":471+ if args.stage not in ("host", "host_obj"):
426 raise ascendc_compile.CompileError(472 raise ascendc_compile.CompileError(
427 "Inductor PGO sidecar is supported only in host_compile stage"473 "Inductor PGO sidecar is supported only in host_compile stage"
428 )474 )
429 device_file_path = os.path.join(args.temp_dir, "device")475 device_file_path = os.path.join(args.temp_dir, "device")
430- generate_file(device_file_path, tiling_def_file, sources["tiling_struct_code"])476+ generate_file(device_file_path, tiling_def_file, sources[SOURCES_TILING_STRUCT])
431 args.host_files, args.pgo_runner_file, args.pgo_device_file = (477 args.host_files, args.pgo_runner_file, args.pgo_device_file = (
432 write_inductor_pgo_sources(478 write_inductor_pgo_sources(
433 host_file_path, device_file_path, args.graph_name, host_impl_code479 host_file_path, device_file_path, args.graph_name, host_impl_code
@@ -440,8 +486,8 @@ def write_compile_host_sources(sources, args, tiling_def_file, base_host_file):
440 486 
441def write_compile_device_sources(sources, args, tiling_def_file, base_device_file):487def write_compile_device_sources(sources, args, tiling_def_file, base_device_file):
442 device_file_path = os.path.join(args.temp_dir, "device")488 device_file_path = os.path.join(args.temp_dir, "device")
443- generate_file(device_file_path, tiling_def_file, sources["tiling_struct_code"])489+ generate_file(device_file_path, tiling_def_file, sources[SOURCES_TILING_STRUCT])
444- generate_file(device_file_path, base_device_file, sources["kernel_impl_code"])490+ generate_file(device_file_path, base_device_file, sources[SOURCES_KERNEL_IMPL])
445 args.device_files = os.path.join(device_file_path, base_device_file)491 args.device_files = os.path.join(device_file_path, base_device_file)
446 492 
447 493 
@@ -449,12 +495,12 @@ def execute_compile(sources, args):
449 tiling_def_file = "autofuse_tiling_data.h"495 tiling_def_file = "autofuse_tiling_data.h"
450 base_host_file = args.graph_name + "_tiling_func.cpp"496 base_host_file = args.graph_name + "_tiling_func.cpp"
451 base_device_file = args.graph_name + "_op_kernel.cpp"497 base_device_file = args.graph_name + "_op_kernel.cpp"
452- if args.stage in ["all", "host"]:498+ if args.stage in ["all", "host", "host_obj"]:
453 with InductorCompileDuration(499 with InductorCompileDuration(
454 args.trace_stage, "WriteHostSource", args.graph_name500 args.trace_stage, "WriteHostSource", args.graph_name
455 ):501 ):
456 write_compile_host_sources(sources, args, tiling_def_file, base_host_file)502 write_compile_host_sources(sources, args, tiling_def_file, base_host_file)
457- if args.stage in ["all", "device"]:503+ if args.stage in ["all", "device", "device_obj"]:
458 with InductorCompileDuration(504 with InductorCompileDuration(
459 args.trace_stage, "WriteDeviceSource", args.graph_name505 args.trace_stage, "WriteDeviceSource", args.graph_name
460 ):506 ):
@@ -465,10 +511,33 @@ def execute_compile(sources, args):
465 with InductorCompileDuration(511 with InductorCompileDuration(
466 args.trace_stage, "BuildCompiledArtifacts", args.graph_name512 args.trace_stage, "BuildCompiledArtifacts", args.graph_name
467 ):513 ):
468- ascendc_compile.main(args)514+ result = ascendc_compile.main(args)
515+ if result is not None:
516+ return _compile_result(args, result)
517+ if args.stage == "link":
518+ return args.output_file
469 return args.temp_dir519 return args.temp_dir
470 520 
471 521 
522+def _compile_result(args, result):
523+ if args.stage == "host_obj":
524+ return {
525+ "version": 1,
526+ "stage": args.stage,
527+ "tiling_obj_paths": result["tiling_obj_paths"],
528+ "tiling_source_paths": result["tiling_source_paths"],
529+ "shared_cv_wrapper_so": result.get("shared_cv_wrapper_so"),
530+ }
531+ if args.stage == "device_obj":
532+ return {
533+ "version": 1,
534+ "stage": args.stage,
535+ "kernel_obj_path": result["kernel_obj_path"],
536+ "kernel_source_path": result["kernel_source_path"],
537+ }
538+ return result
539+ 
540+ 
472def compile_core(541def compile_core(
473 sources, argv: List[str], stage="all", tiling_repr=None, trace_stage=None542 sources, argv: List[str], stage="all", tiling_repr=None, trace_stage=None
474):543):
@@ -508,9 +577,9 @@ def compile_core(
508def jit_compile(tiling_def, host_tiling, op_kernel, argv: List[str]):577def jit_compile(tiling_def, host_tiling, op_kernel, argv: List[str]):
509 return compile_core(578 return compile_core(
510 {579 {
511- "tiling_struct_code": tiling_def,580+ SOURCES_TILING_STRUCT: tiling_def,
512- "host_impl_code": host_tiling,581+ SOURCES_HOST_IMPL: host_tiling,
513- "kernel_impl_code": op_kernel,582+ SOURCES_KERNEL_IMPL: op_kernel,
514 },583 },
515 argv,584 argv,
516 trace_stage="jit_compile",585 trace_stage="jit_compile",
@@ -520,9 +589,9 @@ def jit_compile(tiling_def, host_tiling, op_kernel, argv: List[str]):
520def host_compile(tiling_def_code, tiling_impl_code, argv: List[str]):589def host_compile(tiling_def_code, tiling_impl_code, argv: List[str]):
521 return compile_core(590 return compile_core(
522 {591 {
523- "tiling_struct_code": tiling_def_code,592+ SOURCES_TILING_STRUCT: tiling_def_code,
524- "host_impl_code": tiling_impl_code,593+ SOURCES_HOST_IMPL: tiling_impl_code,
525- "kernel_impl_code": None,594+ SOURCES_KERNEL_IMPL: None,
526 },595 },
527 argv,596 argv,
528 "host",597 "host",
@@ -535,9 +604,9 @@ def kernel_compile(
535):604):
536 return compile_core(605 return compile_core(
537 {606 {
538- "tiling_struct_code": tiling_def_code,607+ SOURCES_TILING_STRUCT: tiling_def_code,
539- "host_impl_code": None,608+ SOURCES_HOST_IMPL: None,
540- "kernel_impl_code": kernel_impl_code,609+ SOURCES_KERNEL_IMPL: kernel_impl_code,
541 },610 },
542 argv,611 argv,
543 "device",612 "device",
@@ -546,6 +615,52 @@ def kernel_compile(
546 )615 )
547 616 
548 617 
618+def build_tiling_obj(tiling_def_code, tiling_impl_code, argv: List[str]):
619+ """编译 host 侧 tiling 函数为 .o,不链接。供外层(torchair)并行调度。"""
620+ return compile_core(
621+ {
622+ SOURCES_TILING_STRUCT: tiling_def_code,
623+ SOURCES_HOST_IMPL: tiling_impl_code,
624+ SOURCES_KERNEL_IMPL: None,
625+ },
626+ argv,
627+ "host_obj",
628+ trace_stage="build_tiling_obj",
629+ )
630+ 
631+ 
632+def build_kernel_obj(
633+ tiling_def_code, kernel_impl_code, argv: List[str], *, tiling_repr=None
634+):
635+ """编译 device 侧 kernel 代码为 .o,不链接。供外层(torchair)并行调度。"""
636+ return compile_core(
637+ {
638+ SOURCES_TILING_STRUCT: tiling_def_code,
639+ SOURCES_HOST_IMPL: None,
640+ SOURCES_KERNEL_IMPL: kernel_impl_code,
641+ },
642+ argv,
643+ "device_obj",
644+ tiling_repr,
645+ trace_stage="build_kernel_obj",
646+ )
647+ 
648+ 
649+def build_kernel_so(tiling_def_code, argv: List[str], *, tiling_repr=None):
650+ """链接 host.o + device.o,产出最终 kernel.so。"""
651+ return compile_core(
652+ {
653+ SOURCES_TILING_STRUCT: tiling_def_code,
654+ SOURCES_HOST_IMPL: None,
655+ SOURCES_KERNEL_IMPL: None,
656+ },
657+ argv,
658+ "link",
659+ tiling_repr,
660+ trace_stage="build_kernel_so",
661+ )
662+ 
663+ 
549def get_inductor_pgo_mspti_config_from_dir(mspti_dir):664def get_inductor_pgo_mspti_config_from_dir(mspti_dir):
550 mspti_dir = os.path.realpath(mspti_dir)665 mspti_dir = os.path.realpath(mspti_dir)
551 include_file = os.path.join(mspti_dir, "include", "mspti.h")666 include_file = os.path.join(mspti_dir, "include", "mspti.h")
@@ -12,7 +12,6 @@
12#include <cstdio>12#include <cstdio>
13#include <cstdlib>13#include <cstdlib>
14#include <dlfcn.h>14#include <dlfcn.h>
15-#include <filesystem>
16#include <fstream>15#include <fstream>
17#include <future>16#include <future>
18#include <gtest/gtest.h>17#include <gtest/gtest.h>
@@ -37,33 +36,27 @@ bool HasDynamicSymbol(const std::string &path, const std::string &symbol) {
37 return RunCommand("nm -D " + path + " 2>/dev/null | grep -q ' " + symbol + "$'") == 0;36 return RunCommand("nm -D " + path + " 2>/dev/null | grep -q ' " + symbol + "$'") == 0;
38}37}
39 38 
40-size_t CountFilesWithSuffix(const std::string &dir, const std::string &suffix) {
41- size_t count = 0;
42- for (const auto &entry : std::filesystem::directory_iterator(dir)) {
43- const std::string file_name = entry.path().filename().string();
44- if (file_name.size() >= suffix.size() &&
45- file_name.compare(file_name.size() - suffix.size(), suffix.size(), suffix) == 0) {
46- count++;
47- }
48- }
49- return count;
50-}
51- 
52void VerifySplitHostArtifacts(const std::string &host_dir) {39void VerifySplitHostArtifacts(const std::string &host_dir) {
40+ // host 编译为单个 cpp 源文件(cpp 段合并),header 段拆出独立 .h 供 include 引用。
41+ // 新格式(含 TilingStateHeader)下 codegen 不再单独输出 TilingHead/common.h,
42+ // 公共结构体定义已内联进合并后的 cpp 翻译单元,故 common.h 与 base/entry/tail 一致均不存在。
53 EXPECT_FALSE(FileExists(host_dir + "/autofuse_tiling_func_common.h"));43 EXPECT_FALSE(FileExists(host_dir + "/autofuse_tiling_func_common.h"));
54- EXPECT_FALSE(FileExists(host_dir + "/autofuse_tiling_func_base.h"));
55- EXPECT_FALSE(FileExists(host_dir + "/autofuse_tiling_func_entry.h"));
56- EXPECT_FALSE(FileExists(host_dir + "/autofuse_tiling_func_tail.h"));
57 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_state.h"));44 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_state.h"));
58 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_log.h"));45 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_log.h"));
59 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_pgo.h"));46 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_pgo.h"));
60 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_solver.h"));47 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_solver.h"));
61 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_api.h"));48 ASSERT_TRUE(FileExists(host_dir + "/autofuse_tiling_func_api.h"));
62- const std::string tail = ReadFile(host_dir + "/inductor_topn_tiling_func_schedule_group_tail.cpp");49+ EXPECT_FALSE(FileExists(host_dir + "/autofuse_tiling_func_base.h"));
63- EXPECT_EQ(tail.find("#include"), std::string::npos);50+ EXPECT_FALSE(FileExists(host_dir + "/autofuse_tiling_func_entry.h"));
64- EXPECT_EQ(tail.find("#include \"autofuse_tiling_func_solver.h\""), std::string::npos);51+ EXPECT_FALSE(FileExists(host_dir + "/autofuse_tiling_func_tail.h"));
65- EXPECT_GE(CountFilesWithSuffix(host_dir, ".cpp"), 2U);52+ // cpp 段合并为单个源文件,不再拆分多个 segment cpp(HeaderSelfContainedCheck 生成的
66- EXPECT_GE(CountFilesWithSuffix(host_dir, ".cpp.o"), 2U);53+ // *.self_contained.cpp 仅用于头文件自包含校验,不计入产物结构检查)。
54+ ASSERT_TRUE(FileExists(host_dir + "/inductor_topn_tiling_func.cpp"));
55+ EXPECT_FALSE(FileExists(host_dir + "/inductor_topn_tiling_func_schedule_group_tail.cpp"));
56+ EXPECT_FALSE(FileExists(host_dir + "/inductor_topn_tiling_func_solver_func.cpp"));
57+ const std::string merged = ReadFile(host_dir + "/inductor_topn_tiling_func.cpp");
58+ EXPECT_NE(merged.find("extern \"C\" int64_t AutofuseTiling"), std::string::npos);
59+ EXPECT_NE(merged.find("extern \"C\" int64_t GenerateTopnSolutions"), std::string::npos);
67}60}
68 61 
69std::string HeaderSelfContainedCheck() {62std::string HeaderSelfContainedCheck() {
@@ -165,6 +165,7 @@ def test_inductor_host_link_includes_acl_runtime(ascendc_compile_module, tmp_pat
165 stage="host",165 stage="host",
166 graph_name="empty_tensor_graph",166 graph_name="empty_tensor_graph",
167 pgo_runner_file=None,167 pgo_runner_file=None,
168+ temp_dir=os.fspath(tmp_path),
168 )169 )
169 170 
170 def fake_compile_host_objs(*_args):171 def fake_compile_host_objs(*_args):
@@ -184,6 +185,6 @@ def test_inductor_host_link_includes_acl_runtime(ascendc_compile_module, tmp_pat
184 185 
185 ascendc_compile_module.module.link_shared = fake_link_shared186 ascendc_compile_module.module.link_shared = fake_link_shared
186 187 
187- ascendc_compile_module.link_host_target(args, os.fspath(tmp_path))188+ ascendc_compile_module.build_host_output(args, os.fspath(tmp_path))
188 189 
189 assert "acl_rt" in captured["link_libraries"]190 assert "acl_rt" in captured["link_libraries"]
@@ -523,13 +523,14 @@ def test_main_host_pgo_builds_bundle_and_skips_plain_copy(
523 events = []523 events = []
524 args = _make_host_pgo_args(tmpdir, ("/mspti", [], []))524 args = _make_host_pgo_args(tmpdir, ("/mspti", [], []))
525 525 
526- def fake_link_host_target(*_):526+ def fake_link_tiling_so(*_):
527 return str(tmpdir.join("built_tiling.so"))527 return str(tmpdir.join("built_tiling.so"))
528 528 
529 def fake_build_pgo_sidecars(*_):529 def fake_build_pgo_sidecars(*_):
530 return str(tmpdir.join("built_runner")), str(tmpdir.join("built_kernel"))530 return str(tmpdir.join("built_runner")), str(tmpdir.join("built_kernel"))
531 531 
532- ascendc_compile_module.module.link_host_target = fake_link_host_target532+ ascendc_compile_module.module.compile_host_objs = lambda *_: ["/tmp/build/host.o"]
533+ ascendc_compile_module.module.link_tiling_so = fake_link_tiling_so
533 ascendc_compile_module.module.build_pgo_sidecars = fake_build_pgo_sidecars534 ascendc_compile_module.module.build_pgo_sidecars = fake_build_pgo_sidecars
534 args.pgo_ld_preload = "/mspti/lib64/libmspti.so"535 args.pgo_ld_preload = "/mspti/lib64/libmspti.so"
535 536 
@@ -572,7 +573,7 @@ def test_main_host_pgo_failure_falls_back_to_plain_tiling(
572 copied = []573 copied = []
573 args = _make_host_pgo_args(tmpdir, ("/mspti", [], []))574 args = _make_host_pgo_args(tmpdir, ("/mspti", [], []))
574 575 
575- def fake_link_host_target(*_):576+ def fake_link_tiling_so(*_):
576 return str(tmpdir.join("built_tiling.so"))577 return str(tmpdir.join("built_tiling.so"))
577 578 
578 def fail_build_pgo_sidecars(*_):579 def fail_build_pgo_sidecars(*_):
@@ -581,7 +582,8 @@ def test_main_host_pgo_failure_falls_back_to_plain_tiling(
581 def record_copy(so_file, compile_args, src_dir):582 def record_copy(so_file, compile_args, src_dir):
582 copied.append((so_file, compile_args.output_file, src_dir))583 copied.append((so_file, compile_args.output_file, src_dir))
583 584 
584- ascendc_compile_module.module.link_host_target = fake_link_host_target585+ ascendc_compile_module.module.compile_host_objs = lambda *_: ["/tmp/build/host.o"]
586+ ascendc_compile_module.module.link_tiling_so = fake_link_tiling_so
585 ascendc_compile_module.module.build_pgo_sidecars = fail_build_pgo_sidecars587 ascendc_compile_module.module.build_pgo_sidecars = fail_build_pgo_sidecars
586 ascendc_compile_module.module.copy_so_to_output = record_copy588 ascendc_compile_module.module.copy_so_to_output = record_copy
587 589 
@@ -597,15 +599,19 @@ def test_main_host_pgo_failure_falls_back_to_plain_tiling(
597 assert os.getcwd() == original_dir599 assert os.getcwd() == original_dir
598 600 
599 601 
600-def test_build_host_output_passes_pch_to_host_link(ascendc_compile_module, tmpdir):602+def test_build_host_output_passes_pch_to_host_compile(ascendc_compile_module, tmpdir):
601 args = _make_host_pgo_args(tmpdir, None)603 args = _make_host_pgo_args(tmpdir, None)
602 captured = {}604 captured = {}
603 605 
604- def fake_link_host_target(compile_args, temp_dir, pch_path=None):606+ def fake_compile_host_objs(compile_args, temp_dir, pch_path):
605 captured["pch_path"] = pch_path607 captured["pch_path"] = pch_path
608+ return ["/tmp/build/host.o"]
609+ 
610+ def fake_link_tiling_so(compile_args, tiling_obj_paths, temp_dir):
606 return str(tmpdir.join("built_tiling.so"))611 return str(tmpdir.join("built_tiling.so"))
607 612 
608- ascendc_compile_module.module.link_host_target = fake_link_host_target613+ ascendc_compile_module.module.compile_host_objs = fake_compile_host_objs
614+ ascendc_compile_module.module.link_tiling_so = fake_link_tiling_so
609 615 
610 result = ascendc_compile_module.build_host_output(args, "/tmp/cache/host.pch")616 result = ascendc_compile_module.build_host_output(args, "/tmp/cache/host.pch")
611 617 
@@ -619,7 +625,7 @@ def test_main_host_pgo_without_mspti_skips_sidecars_and_copies_plain_tiling(
619 copied = []625 copied = []
620 args = _make_host_pgo_args(tmpdir, None)626 args = _make_host_pgo_args(tmpdir, None)
621 627 
622- def fake_link_host_target(*_):628+ def fake_build_host_output(*_):
623 return str(tmpdir.join("built_tiling.so"))629 return str(tmpdir.join("built_tiling.so"))
624 630 
625 def fail_build_pgo_sidecars(*_):631 def fail_build_pgo_sidecars(*_):
@@ -628,7 +634,7 @@ def test_main_host_pgo_without_mspti_skips_sidecars_and_copies_plain_tiling(
628 def record_copy(so_file, compile_args, src_dir):634 def record_copy(so_file, compile_args, src_dir):
629 copied.append(so_file)635 copied.append(so_file)
630 636 
631- ascendc_compile_module.module.link_host_target = fake_link_host_target637+ ascendc_compile_module.module.build_host_output = fake_build_host_output
632 ascendc_compile_module.module.build_pgo_sidecars = fail_build_pgo_sidecars638 ascendc_compile_module.module.build_pgo_sidecars = fail_build_pgo_sidecars
633 ascendc_compile_module.module.copy_so_to_output = record_copy639 ascendc_compile_module.module.copy_so_to_output = record_copy
634 640 
@@ -654,15 +660,16 @@ def test_host_target_records_compile_and_link_stage(
654 "stage": "host",660 "stage": "host",
655 "graph_name": "graph",661 "graph_name": "graph",
656 "output_file": str(tmpdir.join("host.so")),662 "output_file": str(tmpdir.join("host.so")),
663+ "temp_dir": str(tmpdir),
657 },664 },
658 )()665 )()
659 666 
660 ascendc_compile_module.module.run_compile_command = _noop_run_compile_command667 ascendc_compile_module.module.run_compile_command = _noop_run_compile_command
661- ascendc_compile_module.link_host_target(args, str(tmpdir))668+ ascendc_compile_module.build_host_output(args, str(tmpdir))
662 669 
663 labels = [item[0] for item in ascendc_compile_module.duration_records]670 labels = [item[0] for item in ascendc_compile_module.duration_records]
664 assert ["InductorCompile", "host", "CompileHostObj", "graph"] in labels671 assert ["InductorCompile", "host", "CompileHostObj", "graph"] in labels
665- assert ["InductorCompile", "host", "LinkHostSo", "graph"] in labels672+ assert ["InductorCompile", "host", "LinkTilingSo", "graph"] in labels
666 assert capsys.readouterr().out == ""673 assert capsys.readouterr().out == ""
667 674 
668 675 
@@ -690,11 +697,11 @@ def test_kernel_target_records_device_compile_and_link_stage(
690 )()697 )()
691 698 
692 ascendc_compile_module.module.run_compile_command = _noop_run_compile_command699 ascendc_compile_module.module.run_compile_command = _noop_run_compile_command
693- ascendc_compile_module.link_kernel_target(args, None, str(tmpdir))700+ ascendc_compile_module.build_kernel_target(args, None, str(tmpdir))
694 701 
695 labels = [item[0] for item in ascendc_compile_module.duration_records]702 labels = [item[0] for item in ascendc_compile_module.duration_records]
696 assert ["InductorCompile", "device", "CompileDeviceObj", "graph"] in labels703 assert ["InductorCompile", "device", "CompileDeviceObj", "graph"] in labels
697- assert ["InductorCompile", "device", "LinkDeviceSo", "graph"] in labels704+ assert ["InductorCompile", "device", "LinkKernelSo", "graph"] in labels
698 705 
699 706 
700def test_compile_device_obj_includes_machine_asc_headers(707def test_compile_device_obj_includes_machine_asc_headers(
@@ -1662,16 +1669,9 @@ def test_compile_host_obj_rejects_multiple_sources_without_compile(
1662 assert "expects exactly one host source" in str(exc_info.value)1669 assert "expects exactly one host source" in str(exc_info.value)
1663 1670 
1664 1671 
1665-def _capture_build_device_so_link(1672+def _capture_link_kernel_so(ascendc_compile_module, args, host_obj_path, temp_dir):
1666- ascendc_compile_module, args, host_obj_path, temp_dir
1667-):
1668 captured = {}1673 captured = {}
1669 1674 
1670- def fake_compile_device_obj(compile_args, temp_dir):
1671- return "/tmp/build/device/kernel.cpp.o"
1672- 
1673- ascendc_compile_module.module.compile_device_obj = fake_compile_device_obj
1674- 
1675 def fake_link_shared(1675 def fake_link_shared(
1676 target_file, obj_files, link_libraries=None, extra_link_options=None1676 target_file, obj_files, link_libraries=None, extra_link_options=None
1677 ):1677 ):
@@ -1681,19 +1681,16 @@ def _capture_build_device_so_link(
1681 return target_file1681 return target_file
1682 1682 
1683 ascendc_compile_module.module.link_shared = fake_link_shared1683 ascendc_compile_module.module.link_shared = fake_link_shared
1684- ascendc_compile_module.build_device_so(args, host_obj_path, temp_dir)1684+ ascendc_compile_module.link_kernel_so(
1685+ args, host_obj_path, temp_dir, "/tmp/build/device/kernel.cpp.o"
1686+ )
1685 return captured1687 return captured
1686 1688 
1687 1689 
1688-def test_build_device_so_links_all_host_objects(ascendc_compile_module):1690+def test_link_kernel_so_links_all_host_objects(ascendc_compile_module):
1689 captured = {}1691 captured = {}
1690 args = _make_compile_args()1692 args = _make_compile_args()
1691 1693 
1692- def fake_compile_device_obj(compile_args, temp_dir):
1693- return "/tmp/build/device/kernel.cpp.o"
1694- 
1695- ascendc_compile_module.module.compile_device_obj = fake_compile_device_obj
1696- 
1697 def fake_link_shared(1694 def fake_link_shared(
1698 target_file, obj_files, link_libraries=None, extra_link_options=None1695 target_file, obj_files, link_libraries=None, extra_link_options=None
1699 ):1696 ):
@@ -1705,14 +1702,16 @@ def test_build_device_so_links_all_host_objects(ascendc_compile_module):
1705 1702 
1706 ascendc_compile_module.module.link_shared = fake_link_shared1703 ascendc_compile_module.module.link_shared = fake_link_shared
1707 1704 
1708- result = ascendc_compile_module.build_device_so(args, ["a.o", "b.o"], "/tmp/build")1705+ result = ascendc_compile_module.link_kernel_so(
1706+ args, ["a.o", "b.o"], "/tmp/build", "/tmp/build/device/kernel.cpp.o"
1707+ )
1709 1708 
1710 assert result == "/tmp/build/kernel.so"1709 assert result == "/tmp/build/kernel.so"
1711 assert captured["obj_files"] == ["a.o", "b.o", "/tmp/build/device/kernel.cpp.o"]1710 assert captured["obj_files"] == ["a.o", "b.o", "/tmp/build/device/kernel.cpp.o"]
1712 assert captured["link_libraries"] == ascendc_compile_module.HOST_LINK_LIBRARIES1711 assert captured["link_libraries"] == ascendc_compile_module.HOST_LINK_LIBRARIES
1713 1712 
1714 1713 
1715-def test_build_device_so_links_shared_cv_wrapper_so_for_cv_compile(1714+def test_link_kernel_so_links_shared_cv_wrapper_so_for_cv_compile(
1716 ascendc_compile_module, tmpdir1715 ascendc_compile_module, tmpdir
1717):1716):
1718 device_dir = tmpdir.mkdir("device")1717 device_dir = tmpdir.mkdir("device")
@@ -1723,7 +1722,7 @@ def test_build_device_so_links_shared_cv_wrapper_so_for_cv_compile(
1723 args.shared_cv_wrapper_so = (1722 args.shared_cv_wrapper_so = (
1724 "/tmp/run/cv_tiling_wrapper_cache/libautofuse_cv_tiling_wrapper.so"1723 "/tmp/run/cv_tiling_wrapper_cache/libautofuse_cv_tiling_wrapper.so"
1725 )1724 )
1726- captured = _capture_build_device_so_link(1725+ captured = _capture_link_kernel_so(
1727 ascendc_compile_module, args, ["graph.o"], "/tmp/build"1726 ascendc_compile_module, args, ["graph.o"], "/tmp/build"
1728 )1727 )
1729 1728 
@@ -1738,7 +1737,7 @@ def test_build_device_so_links_shared_cv_wrapper_so_for_cv_compile(
1738 ]1737 ]
1739 1738 
1740 1739 
1741-def test_build_device_so_ignores_shared_cv_wrapper_so_for_non_cv_compile(1740+def test_link_kernel_so_ignores_shared_cv_wrapper_so_for_non_cv_compile(
1742 ascendc_compile_module, tmpdir1741 ascendc_compile_module, tmpdir
1743):1742):
1744 device_dir = tmpdir.mkdir("device")1743 device_dir = tmpdir.mkdir("device")
@@ -1749,7 +1748,7 @@ def test_build_device_so_ignores_shared_cv_wrapper_so_for_non_cv_compile(
1749 args.shared_cv_wrapper_so = (1748 args.shared_cv_wrapper_so = (
1750 "/tmp/run/cv_tiling_wrapper_cache/libautofuse_cv_tiling_wrapper.so"1749 "/tmp/run/cv_tiling_wrapper_cache/libautofuse_cv_tiling_wrapper.so"
1751 )1750 )
1752- captured = _capture_build_device_so_link(1751+ captured = _capture_link_kernel_so(
1753 ascendc_compile_module, args, ["graph.o"], str(tmpdir)1752 ascendc_compile_module, args, ["graph.o"], str(tmpdir)
1754 )1753 )
1755 1754 
@@ -1757,7 +1756,7 @@ def test_build_device_so_ignores_shared_cv_wrapper_so_for_non_cv_compile(
1757 assert captured["link_libraries"] == ascendc_compile_module.HOST_LINK_LIBRARIES1756 assert captured["link_libraries"] == ascendc_compile_module.HOST_LINK_LIBRARIES
1758 1757 
1759 1758 
1760-def test_link_host_target_links_multiple_host_objects(ascendc_compile_module):1759+def test_build_host_output_links_multiple_host_objects(ascendc_compile_module):
1761 captured = {}1760 captured = {}
1762 args = _make_compile_args(1761 args = _make_compile_args(
1763 [1762 [
@@ -1782,7 +1781,7 @@ def test_link_host_target_links_multiple_host_objects(ascendc_compile_module):
1782 1781 
1783 ascendc_compile_module.module.link_shared = fake_link_shared1782 ascendc_compile_module.module.link_shared = fake_link_shared
1784 1783 
1785- result = ascendc_compile_module.link_host_target(args, "/tmp/build")1784+ result = ascendc_compile_module.link_tiling_so(args, ["a.o", "b.o"], "/tmp/build")
1786 1785 
1787 assert result == "/tmp/build/kernel.so"1786 assert result == "/tmp/build/kernel.so"
1788 assert captured["target_file"] == "/tmp/build/kernel.so"1787 assert captured["target_file"] == "/tmp/build/kernel.so"
@@ -1791,7 +1790,7 @@ def test_link_host_target_links_multiple_host_objects(ascendc_compile_module):
1791 assert "acl_rt" in captured["link_libraries"]1790 assert "acl_rt" in captured["link_libraries"]
1792 1791 
1793 1792 
1794-def _capture_link_host_target_link(ascendc_compile_module, args, temp_dir):1793+def _capture_build_host_output_link(ascendc_compile_module, args, temp_dir):
1795 captured = {}1794 captured = {}
1796 1795 
1797 def fake_compile_host_objs(compile_args, temp_dir):1796 def fake_compile_host_objs(compile_args, temp_dir):
@@ -1808,11 +1807,12 @@ def _capture_link_host_target_link(ascendc_compile_module, args, temp_dir):
1808 return target_file1807 return target_file
1809 1808 
1810 ascendc_compile_module.module.link_shared = fake_link_shared1809 ascendc_compile_module.module.link_shared = fake_link_shared
1811- ascendc_compile_module.link_host_target(args, temp_dir)1810+ args.temp_dir = temp_dir
1811+ ascendc_compile_module.build_host_output(args)
1812 return captured1812 return captured
1813 1813 
1814 1814 
1815-def test_link_host_target_links_shared_cv_wrapper_so_for_cv_compile(1815+def test_build_host_output_links_shared_cv_wrapper_so_for_cv_compile(
1816 ascendc_compile_module, tmpdir1816 ascendc_compile_module, tmpdir
1817):1817):
1818 host_dir = tmpdir.mkdir("host")1818 host_dir = tmpdir.mkdir("host")
@@ -1822,7 +1822,7 @@ def test_link_host_target_links_shared_cv_wrapper_so_for_cv_compile(
1822 args.shared_cv_wrapper_so = (1822 args.shared_cv_wrapper_so = (
1823 "/tmp/run/cv_tiling_wrapper_cache/libautofuse_cv_tiling_wrapper.so"1823 "/tmp/run/cv_tiling_wrapper_cache/libautofuse_cv_tiling_wrapper.so"
1824 )1824 )
1825- captured = _capture_link_host_target_link(1825+ captured = _capture_build_host_output_link(
1826 ascendc_compile_module, args, "/tmp/build"1826 ascendc_compile_module, args, "/tmp/build"
1827 )1827 )
1828 1828 
@@ -1836,7 +1836,7 @@ def test_link_host_target_links_shared_cv_wrapper_so_for_cv_compile(
1836 ]1836 ]
1837 1837 
1838 1838 
1839-def test_link_host_target_ignores_shared_cv_wrapper_so_for_non_cv_compile(1839+def test_build_host_output_ignores_shared_cv_wrapper_so_for_non_cv_compile(
1840 ascendc_compile_module, tmpdir1840 ascendc_compile_module, tmpdir
1841):1841):
1842 host_dir = tmpdir.mkdir("host")1842 host_dir = tmpdir.mkdir("host")
@@ -1846,16 +1846,18 @@ def test_link_host_target_ignores_shared_cv_wrapper_so_for_non_cv_compile(
1846 args.shared_cv_wrapper_so = (1846 args.shared_cv_wrapper_so = (
1847 "/tmp/run/cv_tiling_wrapper_cache/libautofuse_cv_tiling_wrapper.so"1847 "/tmp/run/cv_tiling_wrapper_cache/libautofuse_cv_tiling_wrapper.so"
1848 )1848 )
1849- captured = _capture_link_host_target_link(ascendc_compile_module, args, str(tmpdir))1849+ captured = _capture_build_host_output_link(
1850+ ascendc_compile_module, args, str(tmpdir)
1851+ )
1850 1852 
1851 assert captured["obj_files"] == ["graph.o"]1853 assert captured["obj_files"] == ["graph.o"]
1852 assert captured["link_libraries"] == ascendc_compile_module.HOST_LINK_LIBRARIES1854 assert captured["link_libraries"] == ascendc_compile_module.HOST_LINK_LIBRARIES
1853 1855 
1854 1856 
1855-def test_link_host_target_adds_acl_runtime_for_pgo_proxy(ascendc_compile_module):1857+def test_build_host_output_adds_acl_runtime_for_pgo_proxy(ascendc_compile_module):
1856 args = _make_compile_args(["/tmp/build/host/graph_tiling_func.cpp"])1858 args = _make_compile_args(["/tmp/build/host/graph_tiling_func.cpp"])
1857 args.pgo_runner_file = "/tmp/build/host/graph_tiling_func_PgoRunner.cpp"1859 args.pgo_runner_file = "/tmp/build/host/graph_tiling_func_PgoRunner.cpp"
1858- captured = _capture_link_host_target_link(1860+ captured = _capture_build_host_output_link(
1859 ascendc_compile_module, args, "/tmp/build"1861 ascendc_compile_module, args, "/tmp/build"
1860 )1862 )
1861 1863 
@@ -1865,7 +1867,7 @@ def test_link_host_target_adds_acl_runtime_for_pgo_proxy(ascendc_compile_module)
1865 ]1867 ]
1866 1868 
1867 1869 
1868-def test_link_kernel_target_reuses_host_objects_for_static_recompile(1870+def test_build_kernel_target_reuses_host_objects_for_static_recompile(
1869 ascendc_compile_module,1871 ascendc_compile_module,
1870):1872):
1871 calls = []1873 calls = []
@@ -1879,15 +1881,22 @@ def test_link_kernel_target_reuses_host_objects_for_static_recompile(
1879 fake_try_static_shape_compile1881 fake_try_static_shape_compile
1880 )1882 )
1881 1883 
1882- def fake_build_device_so(compile_args, host_obj_paths, temp_dir):1884+ def fake_compile_device_obj(compile_args, temp_dir):
1883- calls.append(list(host_obj_paths))1885+ return f"/tmp/build/device/kernel_{len(calls) + 1}.o"
1886+ 
1887+ def fake_link_kernel_so(compile_args, tiling_obj_paths, temp_dir, kernel_obj_path):
1888+ calls.append((list(tiling_obj_paths), kernel_obj_path))
1884 return f"/tmp/build/kernel_{len(calls)}.so"1889 return f"/tmp/build/kernel_{len(calls)}.so"
1885 1890 
1886- ascendc_compile_module.module.build_device_so = fake_build_device_so1891+ ascendc_compile_module.module.compile_device_obj = fake_compile_device_obj
1892+ ascendc_compile_module.module.link_kernel_so = fake_link_kernel_so
1887 1893 
1888- result = ascendc_compile_module.link_kernel_target(1894+ result = ascendc_compile_module.build_kernel_target(
1889 args, ["a.o", "b.o"], "/tmp/build"1895 args, ["a.o", "b.o"], "/tmp/build"
1890 )1896 )
1891 1897 
1892 assert result == "/tmp/build/kernel_2.so"1898 assert result == "/tmp/build/kernel_2.so"
1893- assert calls == [["a.o", "b.o"], ["a.o", "b.o"]]1899+ assert [tiling_obj_paths for tiling_obj_paths, _ in calls] == [
1900+ ["a.o", "b.o"],
1901+ ["a.o", "b.o"],
1902+ ]
@@ -254,7 +254,7 @@ def test_execute_compile_keeps_single_host_file_without_marker(
254 assert os.path.exists(host_file)254 assert os.path.exists(host_file)
255 255 
256 256 
257-def test_execute_compile_splits_host_files_with_marker(compile_adapter_module, tmpdir):257+def test_execute_compile_merges_host_files_with_marker(compile_adapter_module, tmpdir):
258 captured = {}258 captured = {}
259 259 
260 def fake_main(args):260 def fake_main(args):
@@ -287,16 +287,17 @@ def test_execute_compile_splits_host_files_with_marker(compile_adapter_module, t
287 )287 )
288 288 
289 host_dir = os.path.join(str(tmpdir), "host")289 host_dir = os.path.join(str(tmpdir), "host")
290+ # host 编译为单个 cpp 源文件(cpp 段合并),header 段拆出独立 .h 供 include 引用。
291+ host_file = os.path.join(host_dir, "graph_tiling_func.cpp")
292+ assert captured["args"].host_files == host_file
290 assert os.path.exists(os.path.join(host_dir, "autofuse_tiling_func_common.h"))293 assert os.path.exists(os.path.join(host_dir, "autofuse_tiling_func_common.h"))
291- assert captured["args"].host_files == [294+ with open(host_file) as f:
292- os.path.join(host_dir, "graph_tiling_func_solver_func.cpp"),295+ merged = f.read()
293- os.path.join(host_dir, "graph_tiling_func_asc_graph0_schedule_result0_g0.cpp"),296+ assert merged.count('#include "autofuse_tiling_func_common.h"') == 1
294- ]297+ assert 'extern "C" int Solver()' in merged
295- for cpp_file in captured["args"].host_files:298+ assert 'extern "C" int TilingFunc()' in merged
296- with open(cpp_file) as f:
297- assert f.read().count('#include "autofuse_tiling_func_common.h"') == 1
298 assert not os.path.exists(299 assert not os.path.exists(
299- os.path.join(host_dir, "graph_tiling_func_TilingHead.cpp")300+ os.path.join(host_dir, "graph_tiling_func_solver_func.cpp")
300 )301 )
301 302 
302 303