33import ctypes
44import functools
55import os
6+ import re
67import shutil
78import subprocess
89import sys
1920 artifact_directory ,
2021 cache_lock ,
2122 compilation_cache_key ,
23+ read_manifest ,
2224 write_manifest ,
2325 write_source ,
2426)
2527
28+ _TRITON_AOT_LAUNCHER_SCHEMA = 1
29+ _TRITON_MODULE_PATTERN = re .compile (
30+ r"^CUmodule ([A-Za-z_][A-Za-z0-9_]*)_mod = NULL;$" , re .MULTILINE
31+ )
32+ _TRITON_FUNCTION_PATTERN = re .compile (
33+ r"^CUfunction ([A-Za-z_][A-Za-z0-9_]*)_func = NULL;$" , re .MULTILINE
34+ )
35+ _TRITON_LOADER_PATTERN = re .compile (
36+ r"^void load_([A-Za-z_][A-Za-z0-9_]*)\(\) \{$" , re .MULTILINE
37+ )
38+
2639
2740class TritonMaterializer (Materializer ):
2841 target = Target .TRITON
@@ -49,12 +62,19 @@ def load_built_artifact(self, built: BuiltArtifact):
4962 _runtime_specs ,
5063 )
5164
52- library = ctypes .CDLL (built .binary_path )
53- function = getattr (library , f"{ built .source .kernel_name } _kernel_default" )
54- function .restype = ctypes .c_int
65+ _ , function , enter , leave = _load_aot_exports (
66+ built .binary_path ,
67+ built .source .kernel_name ,
68+ )
5569 specs = _runtime_specs (built .source )
5670
57- return _aot_wrapper (function , _launch_abi_from_dict (built .abi ), specs )
71+ return _aot_wrapper (
72+ function ,
73+ enter ,
74+ leave ,
75+ _launch_abi_from_dict (built .abi ),
76+ specs ,
77+ )
5878
5979
6080def _ensure_c_compiler () -> None :
@@ -182,7 +202,7 @@ def validate(args, kwargs):
182202
183203
184204def _aot_materialize (compilation , * , output_dir ):
185- from ninetoothed .compiler .runtime import Handle , _built_manifest , _publish_library
205+ from ninetoothed .compiler .runtime import Handle , _publish_library
186206
187207 artifact = compilation .artifact
188208 cache_key = compilation_cache_key (compilation )
@@ -195,25 +215,74 @@ def _aot_materialize(compilation, *, output_dir):
195215 cache_library = artifact_directory (cache_key ) / f"{ artifact .kernel_name } .triton.so"
196216
197217 with cache_lock (cache_library ):
198- if not cache_library .is_file ():
199- _compile_aot_library (compilation , source , cache_library )
218+ _ensure_aot_library (compilation , source , cache_library )
200219
201220 write_manifest (
202221 cache_library .with_suffix (".manifest.json" ),
203- _built_manifest (compilation , cache_key , source , cache_library ),
222+ _triton_aot_manifest (compilation , cache_key , source , cache_library ),
204223 )
205224
206225 library_path = _publish_library (
207226 cache_library ,
208227 output_dir ,
209228 f"{ artifact .kernel_name } .triton.so" ,
210229 )
230+ _ , function , enter , leave = _load_aot_exports (
231+ library_path ,
232+ artifact .kernel_name ,
233+ )
234+ wrapped = _aot_wrapper (
235+ function ,
236+ enter ,
237+ leave ,
238+ compilation .launch_abi ,
239+ compilation .kernel .tensors ,
240+ )
241+ handle = Handle (compilation , function , wrapped , source , library_path )
242+ write_manifest (
243+ handle ._built_artifact .manifest_path ,
244+ _triton_aot_manifest (compilation , cache_key , source , library_path ),
245+ )
246+
247+ return handle
248+
249+
250+ def _ensure_aot_library (compilation , source : Path , library : Path ) -> None :
251+ manifest = read_manifest (library .with_suffix (".manifest.json" ))
252+ schema = None if manifest is None else manifest .get ("triton_aot_launcher_schema" )
253+
254+ if not library .is_file () or schema != _TRITON_AOT_LAUNCHER_SCHEMA :
255+ _compile_aot_library (compilation , source , library )
256+
257+
258+ def _triton_aot_manifest (compilation , cache_key , source , library ):
259+ from ninetoothed .compiler .runtime import _built_manifest
260+
261+ return dict (_built_manifest (compilation , cache_key , source , library )) | {
262+ "triton_aot_launcher_schema" : _TRITON_AOT_LAUNCHER_SCHEMA ,
263+ }
264+
265+
266+ def _load_aot_exports (library_path , kernel_name ):
211267 library = ctypes .CDLL (str (library_path ))
212- function = getattr (library , f"{ artifact .kernel_name } _kernel_default" )
268+ function = getattr (library , f"{ kernel_name } _kernel_default" )
269+
270+ try :
271+ enter = library .ninetoothed_triton_enter
272+ leave = library .ninetoothed_triton_leave
273+ except AttributeError as exc :
274+ raise RuntimeError (
275+ "Triton AOT artifact is missing the CUDA context guard exports; "
276+ "rebuild the artifact with the current NineToothed version."
277+ ) from exc
278+
213279 function .restype = ctypes .c_int
214- wrapped = _aot_wrapper (function , compilation .launch_abi , compilation .kernel .tensors )
280+ enter .argtypes = []
281+ enter .restype = ctypes .c_int
282+ leave .argtypes = []
283+ leave .restype = None
215284
216- return Handle ( compilation , function , wrapped , source , library_path )
285+ return library , function , enter , leave
217286
218287
219288def _compile_aot_library (compilation , source : Path , library : Path ) -> None :
@@ -262,6 +331,13 @@ def _compile_aot_library(compilation, source: Path, library: Path) -> None:
262331 if not headers or not sources :
263332 raise RuntimeError ("Triton AOT compiler did not produce C artifacts." )
264333
334+ kernel_names = _triton_aot_kernel_names (sources )
335+ context_guard = temporary / "ninetoothed_triton_context_guard.cu"
336+ context_guard .write_text (
337+ _triton_context_guard_source (kernel_names ),
338+ encoding = "utf-8" ,
339+ )
340+
265341 subprocess .run (
266342 [
267343 sys .executable ,
@@ -279,12 +355,20 @@ def _compile_aot_library(compilation, source: Path, library: Path) -> None:
279355 [
280356 find_nvcc (),
281357 "-shared" ,
358+ "-std=c++17" ,
282359 "-Xcompiler" ,
283360 "-fPIC" ,
361+ "-Xcompiler" ,
362+ "-pthread" ,
284363 "-O3" ,
285- "-lcuda" ,
286364 * (str (path ) for path in sources ),
287365 str (linked .with_suffix (".c" )),
366+ str (context_guard ),
367+ "-lcuda" ,
368+ "-Xlinker" ,
369+ "-z" ,
370+ "-Xlinker" ,
371+ "defs" ,
288372 "-o" ,
289373 str (output ),
290374 ],
@@ -293,6 +377,139 @@ def _compile_aot_library(compilation, source: Path, library: Path) -> None:
293377 os .replace (output , library )
294378
295379
380+ def _triton_aot_kernel_names (sources : tuple [Path , ...]) -> tuple [str , ...]:
381+ """Return low-level kernels with matching module, function, and loader symbols."""
382+ if not sources :
383+ raise ValueError ("Unsupported Triton AOT source format: no C sources found." )
384+
385+ kernel_names = []
386+
387+ for source in sources :
388+ text = source .read_text (encoding = "utf-8" )
389+ modules = _TRITON_MODULE_PATTERN .findall (text )
390+ functions = _TRITON_FUNCTION_PATTERN .findall (text )
391+ loaders = _TRITON_LOADER_PATTERN .findall (text )
392+ module_names = set (modules )
393+
394+ if (
395+ not modules
396+ or len (modules ) != len (module_names )
397+ or len (functions ) != len (set (functions ))
398+ or len (loaders ) != len (set (loaders ))
399+ or module_names != set (functions )
400+ or module_names != set (loaders )
401+ ):
402+ raise ValueError (
403+ f"Unsupported Triton AOT source format in `{ source } `: expected "
404+ "one matching `CUmodule`, `CUfunction`, and `load_*` symbol per "
405+ "low-level kernel."
406+ )
407+
408+ kernel_names .extend (modules )
409+
410+ if len (kernel_names ) != len (set (kernel_names )):
411+ raise ValueError (
412+ "Unsupported Triton AOT source format: duplicate low-level kernel symbols."
413+ )
414+ return tuple (kernel_names )
415+
416+
417+ def _triton_context_guard_source (kernel_names : tuple [str , ...]) -> str :
418+ """Generate the CUDA-context guard linked beside Triton's AOT launchers."""
419+ declarations = "\n " .join (
420+ (
421+ f'extern "C" CUmodule { name } _mod;\n '
422+ f'extern "C" CUfunction { name } _func;\n '
423+ f'extern "C" void load_{ name } (void);'
424+ )
425+ for name in kernel_names
426+ )
427+ resets_and_loads = "\n " .join (
428+ (
429+ f" { name } _mod = nullptr;\n "
430+ f" { name } _func = nullptr;\n "
431+ f" load_{ name } ();\n "
432+ f" if ({ name } _mod == nullptr || { name } _func == nullptr) {{\n "
433+ " launch_mutex.unlock();\n "
434+ " return CUDA_ERROR_INVALID_HANDLE;\n "
435+ " }"
436+ )
437+ for name in kernel_names
438+ )
439+ stores = "\n " .join (
440+ (
441+ f" state.modules[{ index } ] = { name } _mod;\n "
442+ f" state.functions[{ index } ] = { name } _func;"
443+ )
444+ for index , name in enumerate (kernel_names )
445+ )
446+ restores = "\n " .join (
447+ (
448+ f" { name } _mod = found->second.modules[{ index } ];\n "
449+ f" { name } _func = found->second.functions[{ index } ];"
450+ )
451+ for index , name in enumerate (kernel_names )
452+ )
453+
454+ return f"""#include <array>
455+ #include <cuda.h>
456+ #include <mutex>
457+ #include <unordered_map>
458+
459+ { declarations }
460+
461+ namespace {{
462+ struct State {{
463+ std::array<CUmodule, { len (kernel_names )} > modules{{}};
464+ std::array<CUfunction, { len (kernel_names )} > functions{{}};
465+ }};
466+
467+ std::mutex launch_mutex;
468+ std::unordered_map<CUcontext, State> context_states;
469+ }}
470+
471+ extern "C" CUresult ninetoothed_triton_enter(void) {{
472+ launch_mutex.lock();
473+
474+ CUcontext context = nullptr;
475+ CUresult result = cuCtxGetCurrent(&context);
476+
477+ if (result != CUDA_SUCCESS) {{
478+ launch_mutex.unlock();
479+ return result;
480+ }}
481+
482+ if (context == nullptr) {{
483+ launch_mutex.unlock();
484+ return CUDA_ERROR_INVALID_CONTEXT;
485+ }}
486+
487+ try {{
488+ auto found = context_states.find(context);
489+
490+ if (found == context_states.end()) {{
491+ { resets_and_loads }
492+
493+ State state{{}};
494+ { stores }
495+ context_states.emplace(context, state);
496+ }} else {{
497+ { restores }
498+ }}
499+ }} catch (...) {{
500+ launch_mutex.unlock();
501+ return CUDA_ERROR_OUT_OF_MEMORY;
502+ }}
503+
504+ return CUDA_SUCCESS;
505+ }}
506+
507+ extern "C" void ninetoothed_triton_leave(void) {{
508+ launch_mutex.unlock();
509+ }}
510+ """
511+
512+
296513def _compile_signature (compilation ) -> str :
297514 specs = {spec .name : spec for spec in compilation .kernel .tensors }
298515 values = []
@@ -421,7 +638,7 @@ def _compile_schedule(compilation) -> tuple[int, int]:
421638 return int (warps ), int (stages )
422639
423640
424- def _aot_wrapper (function , abi , tensor_specs ):
641+ def _aot_wrapper (function , enter , leave , abi , tensor_specs ):
425642 from ninetoothed .compiler .runtime import (
426643 KernelLaunchError ,
427644 _bound_values ,
@@ -462,7 +679,16 @@ def launch(*args, **kwargs):
462679 cuda_scalar = _triton_aot_scalar ,
463680 )
464681 stream = ctypes .c_void_p (torch .cuda .current_stream ().cuda_stream )
465- result = function (stream , * values )
682+ enter_result = enter ()
683+
684+ if enter_result != 0 :
685+ raise KernelLaunchError (enter_result )
686+
687+ try :
688+ result = function (stream , * values )
689+ finally :
690+ leave ()
691+
466692 del keepalive
467693
468694 if result != 0 :
0 commit comments