22# Licensed under the MIT license.
33
44from triton .backends .compiler import BaseBackend , GPUTarget
5- from triton ._C .libtriton import ir , passes
5+ from triton ._C .libtriton import ir , passes , triton_shared , llvm
66from dataclasses import dataclass
77from typing import Any , Dict , Tuple
88from types import ModuleType
1616import triton
1717from pathlib import Path
1818
19- def _get_triton_shared_opt_path () -> str :
20- path = os .getenv ("TRITON_SHARED_OPT_PATH" , "" )
21- if path == "" :
22- raise Exception ("TRITON_SHARED_OPT_PATH is not set." )
23- return path
24-
2519
2620def _get_llvm_bin_path (bin_name : str ) -> str :
2721 path = os .getenv ("LLVM_BINARY_DIR" , "" )
@@ -30,13 +24,6 @@ def _get_llvm_bin_path(bin_name: str) -> str:
3024 return os .path .join (path , bin_name )
3125
3226
33- def _dump_ir_if_needed (files ):
34- path = os .getenv ("TRITON_SHARED_DUMP_PATH" , "" )
35- if not path :
36- return
37- for f in files :
38- shutil .copy (f , os .path .join (path , os .path .basename (f )))
39-
4027def _get_sanitizer_type ():
4128 # returns "" if not set
4229 # throws error if set to something other than "asan" or "tsan"
@@ -45,96 +32,11 @@ def _get_sanitizer_type():
4532 if sanitizer_type != "" and sanitizer_type != "asan" and sanitizer_type != "tsan" :
4633 # throw error
4734 raise Exception (f"TRITON_SHARED_SANITIZER_TYPE { sanitizer_type } is invalid." )
48-
49- return sanitizer_type
5035
51- def _ttir_to_ttsharedir (mod ):
52- # Get Triton-MLIR as string
53- ttir_code = str (mod )
54- with tempfile .TemporaryDirectory () as tmpdir :
55- src_path = os .path .join (tmpdir , "tt.mlir" )
56- dst_path = os .path .join (tmpdir , "ttshared.mlir" )
57- Path (src_path ).write_text (ttir_code )
58- _dump_ir_if_needed ([src_path ])
59- triton_shared_opt_path = _get_triton_shared_opt_path ()
60-
61- subprocess_args = [triton_shared_opt_path , src_path , "--triton-to-linalg-experimental" , "--mlir-print-debuginfo" , "-o" , dst_path ]
62-
63- if _get_sanitizer_type () != "" :
64- print ("Building with sanitizer support..." )
65-
66- # has to run before the other passes as operates on the tt dialect
67- subprocess_args .insert (2 , "--add-llvm-debug-info" )
68-
69- subprocess .check_call (subprocess_args )
70- _dump_ir_if_needed ([dst_path ])
71- return Path (dst_path ).read_text ()
72-
73-
74- def _optimize_ttsharedir (ttsharedir : str ):
75- # We don't apply any optimizations now, but we can add passes if needed.
76- return ttsharedir
36+ return sanitizer_type
7737
7838
79- def _ttsharedir_to_llir (ttsharedir : str ):
80- with tempfile .TemporaryDirectory () as tmpdir :
81- ttshared_path = os .path .join (tmpdir , "ttshared.mlir" )
82- llmlir_path = os .path .join (tmpdir , "ll.mlir" )
83- llir_path = os .path .join (tmpdir , "ll.ir" )
84- Path (ttshared_path ).write_text (ttsharedir )
85- mlir_opt_path = _get_llvm_bin_path ("mlir-opt" )
86- # TritonShared-MLIR to LLVM-MLIR
87- subprocess .check_call ([mlir_opt_path , ttshared_path ,
88- "--convert-linalg-to-affine-loops" ,
89- # Note: eliminate-empty-tensors fails when there are multiple func.return ops
90- # in a single kernel which are the results of early returns.
91- # See python/examples/test_early_return.py for examples.
92- # We disable this pass for now since performance on CPU isn't the main
93- # focus at the moment.
94- # "--eliminate-empty-tensors",
95- "--empty-tensor-to-alloc-tensor" ,
96- "--one-shot-bufferize=allow-return-allocs-from-loops=true" ,
97- "--lower-affine" ,
98- "--convert-linalg-to-loops" ,
99- "--expand-strided-metadata" ,
100- "--convert-scf-to-cf" ,
101- "--convert-arith-to-llvm" ,
102- "--convert-math-to-llvm" ,
103- "--convert-complex-to-llvm" ,
104- "--convert-vector-to-llvm" ,
105- "--convert-index-to-llvm" ,
106- "--memref-expand" ,
107- "--finalize-memref-to-llvm" ,
108- "--convert-func-to-llvm" ,
109- "--convert-cf-to-llvm" ,
110- # Lowering memrefs creates more affine.apply ops.
111- # Lowering these affine ops again creates further arith ops,
112- # so we have to run these two passes again here.
113- "--lower-affine" ,
114- "--convert-arith-to-llvm" ,
115- # Remove all unrealized casts created
116- "--reconcile-unrealized-casts" ,
117- "--mlir-print-debuginfo" ,
118- "-o" ,
119- llmlir_path ])
120- _dump_ir_if_needed ([llmlir_path ])
121-
122- # LLVM-MLIR to LLVM-IR
123- mlir_translate_path = _get_llvm_bin_path ("mlir-translate" )
124- subprocess .check_call ([mlir_translate_path , llmlir_path ,
125- "--mlir-to-llvmir" ,
126- "-o" ,
127- llir_path ])
128- _dump_ir_if_needed ([llir_path ])
129- return Path (llir_path ).read_text ()
130-
131-
132- def _optimize_llir (llir : str ):
133- # We don't apply any optimizations now, but we can add passes if needed.
134- return llir
135-
136-
137- def _llir_to_bin (llir : str , metadata ):
39+ def _llir_to_bin (llir : str , metadata , options ):
13840 pattern = r"define void @(\w+)\(.+"
13941 matches = re .findall (pattern , llir )
14042 assert len (matches ) == 1
@@ -150,17 +52,19 @@ def _llir_to_bin(llir: str, metadata):
15052 # using a sanitizer
15153 # invoke pass to append sanitizer attributes
15254 instrumented_src_path = os .path .join (tmpdir , "kernel-instrumented.ll" )
153-
55+
15456 opt_path = _get_llvm_bin_path ("opt" )
15557 top_level_triton_path = os .path .dirname (triton .__file__ )
156- sanitizer_attributes_pass_path = str (next (Path (top_level_triton_path ).rglob ("libSanitizerAttributes.so" ), None ))
58+ sanitizer_attributes_pass_path = str (
59+ next (Path (top_level_triton_path ).rglob ("libSanitizerAttributes.so" ), None ))
15760
15861 if not sanitizer_attributes_pass_path :
15962 raise Exception ("libSanitizerAttributes.so does not exist." )
16063
161- subprocess .check_call ([opt_path , "-load-pass-plugin" , sanitizer_attributes_pass_path ,
162- "-passes=sanitizer-attributes" , f"-sanitizer-type={ sanitizer_type } " , "-S" , src_path ,
163- "-o" , instrumented_src_path ])
64+ subprocess .check_call ([
65+ opt_path , "-load-pass-plugin" , sanitizer_attributes_pass_path , "-passes=sanitizer-attributes" ,
66+ f"-sanitizer-type={ sanitizer_type } " , "-S" , src_path , "-o" , instrumented_src_path
67+ ])
16468
16569 # compile to object file
16670 clang_path = _get_llvm_bin_path ("clang++" )
@@ -171,14 +75,13 @@ def _llir_to_bin(llir: str, metadata):
17175 subprocess_args .extend (["-g" , "-fsanitize=address" , "-mllvm" , "-asan-stack=0" ])
17276 elif sanitizer_type == "tsan" :
17377 subprocess_args .extend (["-g" , "-fsanitize=thread" ])
174-
78+
17579 subprocess .check_call (subprocess_args )
17680 else :
17781 llc_path = _get_llvm_bin_path ("llc" )
17882 subprocess .check_call ([llc_path , src_path , "-filetype=obj" , "-relocation-model=pic" , "-o" , dst_path ])
179-
180- return Path (dst_path ).read_bytes ()
18183
84+ return Path (dst_path ).read_bytes ()
18285
18386
18487@dataclass (frozen = True )
@@ -232,19 +135,13 @@ def pack_metadata(self, metadata):
232135 # Note: We actually don't need any of these except for the name which is
233136 # used in the launch function in driver.py. Putting these in so we're
234137 # consistent with other backends
235- return (
236- metadata .num_warps ,
237- metadata .num_ctas ,
238- metadata .shared ,
239- metadata .cluster_dims [0 ],
240- metadata .cluster_dims [1 ],
241- metadata .cluster_dims [2 ],
242- metadata .name
243- )
138+ return (metadata .num_warps , metadata .num_ctas , metadata .shared , metadata .cluster_dims [0 ],
139+ metadata .cluster_dims [1 ], metadata .cluster_dims [2 ], metadata .name )
244140
245141 # Our compilation pipeline isn't in python like nvidia or amd, no need to load
246142 # dialects. See `triton_shared.cc`
247143 def load_dialects (self , ctx ):
144+ triton_shared .load_dialects (ctx )
248145 return
249146
250147 @staticmethod
@@ -263,12 +160,66 @@ def make_ttir(mod, metadata, options):
263160 pm .run (mod , 'make_ttir' )
264161 return mod
265162
163+ @staticmethod
164+ def make_tt_shared_ir (mod , metadata , options ):
165+ pm = ir .pass_manager (mod .context )
166+ pm .enable_debug ()
167+ if _get_sanitizer_type () != "" :
168+ print ("Building with sanitizer support..." )
169+ # has to run before the other passes as operates on the tt dialect
170+ triton_shared .add_llvm_debug_info (pm )
171+ triton_shared .add_triton_to_linalg_experimental (pm )
172+ pm .run (mod , 'make_tt_shared_ir' )
173+ return mod
174+
175+ @staticmethod
176+ def make_llir (mod , metadata , options ):
177+ pm = ir .pass_manager (mod .context )
178+ pm .enable_debug ()
179+ triton_shared .add_convert_linalg_to_affine_loops (pm )
180+
181+ # Note: eliminate-empty-tensors fails when there are multiple func.return ops
182+ # in a single kernel which are the results of early returns.
183+ # See python/examples/test_early_return.py for examples.
184+ # We disable this pass for now since performance on CPU isn't the main
185+ # focus at the moment.
186+ # triton_shared.add_eliminate_empty_tensors(pm)
187+
188+ triton_shared .add_empty_tensor_to_alloc_tensor (pm )
189+ triton_shared .add_one_shot_bufferize (pm )
190+ triton_shared .add_lower_affine (pm )
191+ triton_shared .add_convert_linalg_to_loops (pm )
192+ triton_shared .add_expand_strided_metadata (pm )
193+ triton_shared .add_convert_scf_to_cf (pm )
194+ triton_shared .add_convert_arith_to_llvm (pm )
195+ triton_shared .add_convert_math_to_llvm (pm )
196+ triton_shared .add_convert_complex_to_llvm (pm )
197+ triton_shared .add_convert_vector_to_llvm (pm )
198+ triton_shared .add_convert_index_to_llvm (pm )
199+ triton_shared .add_memref_expand (pm )
200+ triton_shared .add_finalize_memref_to_llvm (pm )
201+ triton_shared .add_convert_func_to_llvm (pm )
202+ triton_shared .add_convert_cf_to_llvm (pm )
203+ # Lowering memrefs creates more affine.apply ops.
204+ # Lowering these affine ops again creates further arith ops,
205+ # so we have to run these two passes again here.
206+ triton_shared .add_lower_affine (pm )
207+ triton_shared .add_convert_arith_to_llvm (pm )
208+ # Remove all unrealized casts created
209+ triton_shared .add_reconcile_unrealized_casts (pm )
210+ pm .run (mod , 'make_llir' )
211+
212+ # LLVM-IR (MLIR) -> LLVM-IR (LLVM)
213+ llvm .init_targets ()
214+ context = llvm .context ()
215+ llvm_mod = llvm .to_module (mod , context )
216+ return str (llvm_mod )
217+
266218 def add_stages (self , stages , options , language ):
267219 stages ["ttir" ] = lambda src , metadata : self .make_ttir (src , metadata , options )
268- stages ["ttsharedir" ] = lambda src , metadata : _optimize_ttsharedir (_ttir_to_ttsharedir (src ))
269- stages ["llir" ] = lambda src , metadata : _optimize_llir (_ttsharedir_to_llir (src ))
270- stages ["obj" ] = lambda src , metadata : _llir_to_bin (src , metadata )
271-
220+ stages ["ttsharedir" ] = lambda src , metadata : self .make_tt_shared_ir (src , metadata , options )
221+ stages ["llir" ] = lambda src , metadata : self .make_llir (src , metadata , options )
222+ stages ["obj" ] = lambda src , metadata : _llir_to_bin (src , metadata , options )
272223
273224 @functools .lru_cache ()
274225 def hash (self ):
0 commit comments