Skip to content

Commit 1850a7b

Browse files
authored
add pybind interface (#9)
1. refactor compiler.py
1 parent 7f7db93 commit 1850a7b

4 files changed

Lines changed: 231 additions & 128 deletions

File tree

CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ add_subdirectory(test)
2323
add_subdirectory(tools/triton-shared-opt)
2424

2525
if (TRITON_SHARED_BUILD_CPU_BACKEND)
26-
add_triton_plugin(TritonShared ${CMAKE_CURRENT_SOURCE_DIR}/triton_shared.cc LINK_LIBS TritonSharedAnalysis TritonTilingExtIR)
26+
get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS)
27+
get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS)
28+
29+
add_triton_plugin(TritonShared ${CMAKE_CURRENT_SOURCE_DIR}/triton_shared.cc LINK_LIBS TritonSharedAnalysis TritonTilingExtIR ${conversion_libs} ${extension_libs} MLIRSparseTensorTransforms MLIRControlFlowTransforms MLIRTensorInferTypeOpInterfaceImpl)
2730
target_link_libraries(TritonShared PRIVATE Python3::Module pybind11::headers)
2831
endif()
2932

backend/compiler.py

Lines changed: 73 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Licensed under the MIT license.
33

44
from 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
66
from dataclasses import dataclass
77
from typing import Any, Dict, Tuple
88
from types import ModuleType
@@ -16,12 +16,6 @@
1616
import triton
1717
from 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

2620
def _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-
4027
def _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):

python/examples/test_unstructured_mask.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@
77
import triton.language as tl
88

99
from triton.backends.triton_shared.driver import CPUDriver
10-
from triton.backends.triton_shared.compiler import _get_triton_shared_opt_path
1110

1211
import os
1312
from pathlib import Path
1413
import subprocess
1514
import tempfile
1615

16+
def _get_triton_shared_opt_path() -> str:
17+
path = os.getenv("TRITON_SHARED_OPT_PATH", "")
18+
if path == "":
19+
raise Exception("TRITON_SHARED_OPT_PATH is not set.")
20+
return path
21+
1722
def run_triton_to_structured(ttir_code):
1823
with tempfile.TemporaryDirectory() as tmpdir:
1924
src_path = os.path.join(tmpdir, "tt.mlir")

0 commit comments

Comments
 (0)