4949import sys
5050
5151from iron .common .device_utils import get_kernel_dir
52+ from aie .utils .compile .utils import compile_cxx_core_function , compile_mlir_module
5253
5354# Global Functions
5455# ##########################################################################
@@ -487,20 +488,42 @@ def generate_mlir(output_artifact, generator):
487488 f .write (mlir_code )
488489
489490
491+ def _aiecc_work_dir (mlir_filename : str ) -> Path :
492+ """Directory aiecc writes its own 'aie.mlir' copy and '.prj' project directory
493+ into for the given MLIR source artifact's filename.
494+
495+ compile_mlir_module() always names its copy of the source "aie.mlir" inside
496+ the work_dir it's given, rather than reusing the artifact's own filename, so
497+ each MLIR source needs its own work_dir to avoid colliding with every other
498+ artifact's aiecc output in the flat build directory. Callers that need to
499+ find aiecc's project directory afterward (e.g. for a runtime-parameters
500+ scratchpad) should derive it from this same function rather than
501+ re-deriving the convention.
502+ """
503+ p = Path (mlir_filename )
504+ return p .parent / (p .name + ".d" )
505+
506+
507+ def _link_build_outputs_into (work_dir : Path , build_dir : Path ) -> None :
508+ """Symlink every file already built in build_dir into work_dir.
509+
510+ aiecc resolves an MLIR module's relative kernel-object references (e.g.
511+ ``link_with = "axpy.o"``, produced by KernelCompilationRule /
512+ ArchiveCompilationRule into the flat build_dir) against work_dir, since
513+ that's where compile_mlir_module() writes its own copy of the MLIR
514+ source. Symlinking makes those lookups succeed without copying kernel
515+ objects into every artifact's own work_dir.
516+ """
517+ for entry in build_dir .iterdir ():
518+ if entry .is_dir ():
519+ continue
520+ link = work_dir / entry .name
521+ if not link .exists ():
522+ link .symlink_to (entry .resolve ())
523+
524+
490525class AieccCompilationRule (CompilationRule ):
491- def __init__ (
492- self , build_dir , peano_dir , mlir_aie_dir , use_chess = False , * args , ** kwargs
493- ):
494- self .build_dir = build_dir
495- # AIECC_PATH lets a build point at a locally-built aiecc (e.g. a compiler under
496- # development) without replacing the installed one. Default = the installed aiecc.
497- _aiecc_override = os .environ .get ("AIECC_PATH" )
498- self .aiecc_path = (
499- Path (_aiecc_override )
500- if _aiecc_override
501- else Path (mlir_aie_dir ) / "bin" / "aiecc"
502- )
503- self .peano_dir = peano_dir
526+ def __init__ (self , use_chess = False , * args , ** kwargs ):
504527 self .use_chess = use_chess
505528 super ().__init__ (* args , ** kwargs )
506529
@@ -514,41 +537,32 @@ def compile(self, graph):
514537 commands = []
515538
516539 for artifact in worklist :
517- compile_cmd = [
518- str ( self . aiecc_path ),
519- "-v" ,
540+ mlir_source = artifact . mlir_input
541+ work_dir = _aiecc_work_dir ( mlir_source . filename )
542+ options = [
520543 f"-j{ os .environ .get ('AIECC_JOBS' , '1' )} " ,
521- ]
522- if self .use_chess :
523- compile_cmd += [
524- "--xchesscc" ,
525- "--xbridge" ,
526- ]
527- else :
528- compile_cmd += [
529- "--no-xchesscc" ,
530- "--no-xbridge" ,
531- "--peano" ,
532- str (self .peano_dir ),
533- ]
534- compile_cmd += [
535544 "--expand-load-pdis" ,
536- "--get-full-elf" ,
537- "--full-elf-name" ,
538- os .path .abspath (artifact .filename ),
539- # Unrequested, params.txt is never written and
540- # SequenceFullELFCallable.params silently returns None. It has
541- # no name flag of its own, so --output-dir is what puts it in
542- # the project directory that property reads.
543545 "--get-scratchpad-parameters" ,
544- "--output-dir" ,
545- os .path .abspath (artifact .mlir_input .filename ) + ".prj" ,
546- * artifact .extra_flags ,
547- os .path .abspath (artifact .mlir_input .filename ),
548- ]
549- commands .append (
550- ShellCompilationCommand (compile_cmd , cwd = str (self .build_dir ))
551- )
546+ ] + artifact .extra_flags
547+
548+ def _compile (
549+ artifact = artifact ,
550+ mlir_source = mlir_source ,
551+ work_dir = work_dir ,
552+ options = options ,
553+ ):
554+ work_dir .mkdir (parents = True , exist_ok = True )
555+ _link_build_outputs_into (work_dir , Path (mlir_source .filename ).parent )
556+ compile_mlir_module (
557+ Path (mlir_source .filename ).read_text (),
558+ full_elf_path = os .path .abspath (artifact .filename ),
559+ work_dir = str (work_dir ),
560+ options = options ,
561+ use_chess = self .use_chess ,
562+ verbose = True ,
563+ )
564+
565+ commands .append (PythonCallbackCompilationCommand (_compile ))
552566 artifact .available = True
553567
554568 return commands
@@ -575,57 +589,53 @@ def compile(self, graph):
575589 commands = []
576590 # Now we know for each mlir source if we need to generate an xclbin, an insts.bin or both for it
577591 for mlir_source in mlir_sources :
578- compile_cmd = [
579- str (self .aiecc_path ),
580- "-v" ,
581- f"-j{ os .environ .get ('AIECC_JOBS' , '1' )} " ,
582- ]
583- if self .use_chess :
584- compile_cmd += [
585- "--xchesscc" ,
586- "--xbridge" ,
587- ]
588- else :
589- compile_cmd += [
590- "--no-xchesscc" ,
591- "--no-xbridge" ,
592- "--peano" ,
593- str (self .peano_dir ),
594- ]
595- compile_cmd += [
596- "--dynamic-objFifos" ,
597- ]
592+ options = [f"-j{ os .environ .get ('AIECC_JOBS' , '1' )} " ]
593+ xclbin_path = None
594+ insts_path = None
598595 do_compile_xclbin = mlir_source in mlir_sources_to_xclbins
599596 do_compile_insts_bin = mlir_source in mlir_sources_to_insts
600597 if do_compile_xclbin :
601598 first_xclbin = mlir_sources_to_xclbins [mlir_source ][
602599 0
603600 ] # TODO: this does not handle the case of multiple xclbins with different kernel names or flags from the same MLIR
604- compile_cmd += first_xclbin .extra_flags + [
605- "--get-xclbin" ,
606- "--xclbin-name=" + os .path .abspath (first_xclbin .filename ),
607- "--xclbin-kernel-name=" + first_xclbin .kernel_name ,
601+ xclbin_path = os .path .abspath (first_xclbin .filename )
602+ options += first_xclbin .extra_flags + [
603+ f"--xclbin-kernel-name={ first_xclbin .kernel_name } " ,
608604 ]
609605 if first_xclbin .xclbin_input is not None :
610- compile_cmd += [
606+ options . append (
611607 "--xclbin-input="
612608 + os .path .abspath (first_xclbin .xclbin_input .filename )
613- ]
609+ )
614610 if do_compile_insts_bin :
615611 first_insts_bin = mlir_sources_to_insts [mlir_source ][
616612 0
617613 ] # TODO: this does not handle the case of multiple insts.bins with different flags from the same MLIR
618- # Outputs are selected by --get-<name>; asking only for the insts is what
619- # "--no-compile" used to mean, so there is nothing to opt out of here.
620- compile_cmd += first_insts_bin .extra_flags + [
621- "--get-npu-insts" ,
622- "--npu-insts-name=" + os .path .abspath (first_insts_bin .filename ),
623- ]
624- compile_cmd += [os .path .abspath (mlir_source .filename )]
614+ insts_path = os .path .abspath (first_insts_bin .filename )
615+ options += first_insts_bin .extra_flags
625616
626- commands .append (
627- ShellCompilationCommand (compile_cmd , cwd = str (self .build_dir ))
628- )
617+ work_dir = _aiecc_work_dir (mlir_source .filename )
618+
619+ def _compile (
620+ mlir_source = mlir_source ,
621+ xclbin_path = xclbin_path ,
622+ insts_path = insts_path ,
623+ options = options ,
624+ work_dir = work_dir ,
625+ ):
626+ work_dir .mkdir (parents = True , exist_ok = True )
627+ _link_build_outputs_into (work_dir , Path (mlir_source .filename ).parent )
628+ compile_mlir_module (
629+ Path (mlir_source .filename ).read_text (),
630+ insts_path = insts_path ,
631+ xclbin_path = xclbin_path ,
632+ work_dir = str (work_dir ),
633+ options = options ,
634+ use_chess = self .use_chess ,
635+ verbose = True ,
636+ )
637+
638+ commands .append (PythonCallbackCompilationCommand (_compile ))
629639
630640 # There may be multiple targets that require an xclbin/insts.bin from the same MLIR with different names; copy them
631641 for sources_to in [mlir_sources_to_xclbins , mlir_sources_to_insts ]:
@@ -718,7 +728,6 @@ def matches(self, artifacts):
718728 return any (artifacts .get_worklist (KernelObjectArtifact ))
719729
720730 def compile (self , artifacts ):
721- include_path = Path (self .mlir_aie_dir ) / "include"
722731 worklist = artifacts .get_worklist (KernelObjectArtifact )
723732 commands = []
724733
@@ -738,41 +747,28 @@ def compile(self, artifacts):
738747 "Expected KernelObject dependency to be a C source file"
739748 )
740749
741- if self .use_chess :
742- wrapper_path = Path (self .mlir_aie_dir ) / "bin" / "xchesscc_wrapper"
743- cmd = (
744- [
745- str (wrapper_path ),
746- kernel_dir , # e.g. "aie2" or "aie2p"
747- f"-I{ str (include_path )} " ,
748- f"-I{ str (runtime_lib_include_path )} " ,
749- ]
750- + artifact .extra_flags
751- + ["-c" , source_file .filename , "-o" , artifact .filename ]
752- )
753- else :
754- clang_path = Path (self .peano_dir ) / "bin" / "clang++"
755- target = f"{ kernel_dir } -none-unknown-elf"
756- cmd = (
757- [
758- str (clang_path ),
759- "-O2" ,
760- "-std=c++20" ,
761- f"--target={ target } " ,
762- "-D__AIE_API_AIE_ADF_HPP__" ,
763- "-Wno-parentheses" ,
764- "-Wno-attributes" ,
765- "-Wno-macro-redefined" ,
766- "-Wno-empty-body" ,
767- "-Wno-missing-template-arg-list-after-template-kw" ,
768- f"-I{ str (include_path )} " ,
769- f"-I{ str (runtime_lib_include_path )} " ,
770- ]
771- + artifact .extra_flags
772- + ["-c" , source_file .filename , "-o" , artifact .filename ]
773- )
750+ # -Wno-missing-template-arg-list-after-template-kw only applies to
751+ # the Peano (clang) path: xchesscc's own front end doesn't
752+ # recognize it.
753+ compile_args = list (artifact .extra_flags )
754+ if not self .use_chess :
755+ compile_args = [
756+ "-Wno-missing-template-arg-list-after-template-kw"
757+ ] + compile_args
774758
775- commands .append (ShellCompilationCommand (cmd ))
759+ commands .append (
760+ PythonCallbackCompilationCommand (
761+ partial (
762+ compile_cxx_core_function ,
763+ source_path = source_file .filename ,
764+ target_arch = kernel_dir ,
765+ output_path = artifact .filename ,
766+ include_dirs = [str (runtime_lib_include_path )],
767+ compile_args = compile_args ,
768+ use_chess = self .use_chess ,
769+ )
770+ )
771+ )
776772 if artifact .rename_symbols :
777773 commands .extend (self ._rename_symbols (artifact ))
778774 if artifact .prefix_symbols :
0 commit comments