mirror of
https://github.com/tile-ai/tilelang.git
synced 2026-10-21 06:40:23 +08:00
* [Debug][TIR] Unify pass tracing on PassInstrument * [Debug] Fix context manager generator annotations * [Debug] Scope pass instrumentation per compile * [JIT] Require lowered modules in source adapters * [Debug] Manage pass timing through compile sessions * [JIT] Clarify compile session orchestration * [JIT] Clarify compile metadata naming * [Debug] Simplify instrumentation session boundaries * [Debug] Clarify instrumentation execution boundaries * [Debug] Organize pass instrumentation infrastructure * [Debug] Trim redundant instrumentation tests
173 lines
5.6 KiB
Python
173 lines
5.6 KiB
Python
"""Tests for the shared nested-pass event instrumentation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
from contextlib import contextmanager
|
|
import threading
|
|
from types import SimpleNamespace
|
|
|
|
from tilelang.instrumentation import (
|
|
CodegenEvent,
|
|
PassEvent,
|
|
PassEventObserver,
|
|
PassInstrumentationTool,
|
|
StackedPassInstrument,
|
|
active_stacked_pass_instruments,
|
|
compile_pass_instrumentation,
|
|
create_pass_instruments,
|
|
current_compile_pass_instrumentation,
|
|
current_pass_phase,
|
|
run_codegen_with_instrumentation,
|
|
pass_phase,
|
|
pass_pipeline,
|
|
register_pass_instrumentation_tool,
|
|
unregister_pass_instrumentation_tool,
|
|
)
|
|
|
|
|
|
class _RecordingObserver(PassEventObserver):
|
|
def __init__(self):
|
|
self.started: list[PassEvent] = []
|
|
self.finished: list[PassEvent] = []
|
|
self.context_entries = 0
|
|
self.context_exits = 0
|
|
|
|
def enter_pass_context(self):
|
|
self.context_entries += 1
|
|
|
|
def exit_pass_context(self):
|
|
self.context_exits += 1
|
|
|
|
def pass_started(self, mod, event):
|
|
self.started.append(event)
|
|
return f"before:{mod}"
|
|
|
|
def pass_finished(self, mod, event, state):
|
|
assert state.startswith("before:")
|
|
self.finished.append(event)
|
|
|
|
|
|
def _info(name: str):
|
|
return SimpleNamespace(name=name)
|
|
|
|
|
|
def test_stacked_instrument_records_nested_parentage_and_start_order():
|
|
observer = _RecordingObserver()
|
|
instrument = StackedPassInstrument(observer)
|
|
|
|
instrument.enter_pass_ctx()
|
|
instrument.run_before_pass("m0", _info("test.Outer"))
|
|
instrument.run_before_pass("m1", _info("test.Inner"))
|
|
instrument.run_after_pass("m2", _info("test.Inner"))
|
|
instrument.run_after_pass("m3", _info("test.Outer"))
|
|
instrument.exit_pass_ctx()
|
|
|
|
assert [event.name for event in observer.started] == ["test.Outer", "test.Inner"]
|
|
assert [event.name for event in observer.finished] == ["test.Inner", "test.Outer"]
|
|
outer, inner = observer.started
|
|
assert (outer.sequence, outer.depth, outer.parent_sequence) == (0, 0, None)
|
|
assert (inner.sequence, inner.depth, inner.parent_sequence) == (1, 1, 0)
|
|
assert observer.context_entries == 1
|
|
assert observer.context_exits == 1
|
|
|
|
|
|
class _RecordingTool(PassInstrumentationTool):
|
|
def __init__(self, label: str, events: list[tuple]):
|
|
self.label = label
|
|
self.events = events
|
|
self.instruments = []
|
|
|
|
def create_pass_instrument(self):
|
|
marker = object()
|
|
self.instruments.append(marker)
|
|
return marker
|
|
|
|
@contextmanager
|
|
def pipeline_scope(self, base_phase):
|
|
with pass_phase(f"{self.label}_{base_phase}"):
|
|
self.events.append(("enter", self.label, current_pass_phase()))
|
|
yield
|
|
self.events.append(("exit", self.label, current_pass_phase()))
|
|
|
|
def run_codegen(self, event: CodegenEvent, next_call):
|
|
self.events.append(("codegen", self.label, event.name))
|
|
return next_call()
|
|
|
|
def finish(self, error):
|
|
self.events.append(("finish", self.label, error))
|
|
|
|
|
|
def test_registered_tools_are_snapshotted_and_composable():
|
|
events = []
|
|
instances = []
|
|
|
|
def factory():
|
|
tool = _RecordingTool("tool", events)
|
|
instances.append(tool)
|
|
return tool
|
|
|
|
register_pass_instrumentation_tool("test", factory)
|
|
try:
|
|
with compile_pass_instrumentation(name="kernel") as session:
|
|
assert session is current_compile_pass_instrumentation()
|
|
with compile_pass_instrumentation(name="nested") as nested:
|
|
assert nested is session
|
|
first = create_pass_instruments()
|
|
second = create_pass_instruments()
|
|
assert first != second
|
|
assert instances[0].instruments == [*first, *second]
|
|
with pass_pipeline("cuda"):
|
|
events.append(("body", current_pass_phase()))
|
|
assert run_codegen_with_instrumentation("target.build.test", "mod", "target", lambda: "result") == "result"
|
|
finally:
|
|
unregister_pass_instrumentation_tool("test")
|
|
|
|
assert events == [
|
|
("enter", "tool", "tool_pipeline_cuda"),
|
|
("body", "tool_pipeline_cuda"),
|
|
("exit", "tool", "tool_pipeline_cuda"),
|
|
("codegen", "tool", "target.build.test"),
|
|
("finish", "tool", None),
|
|
]
|
|
assert current_pass_phase() is None
|
|
assert current_compile_pass_instrumentation() is None
|
|
|
|
|
|
def test_compile_sessions_are_isolated_between_threads():
|
|
barrier = threading.Barrier(2)
|
|
|
|
def compile_one(label):
|
|
tool = _RecordingTool(label, [])
|
|
with compile_pass_instrumentation(
|
|
name=label,
|
|
tools=[tool],
|
|
include_default_tools=False,
|
|
) as session:
|
|
marker = create_pass_instruments()[0]
|
|
barrier.wait()
|
|
assert current_compile_pass_instrumentation() is session
|
|
return session, tool, marker
|
|
|
|
with ThreadPoolExecutor(max_workers=2) as pool:
|
|
left, right = list(pool.map(compile_one, ("left", "right")))
|
|
|
|
assert left[0] is not right[0]
|
|
assert left[1] is not right[1]
|
|
assert left[2] is not right[2]
|
|
assert current_compile_pass_instrumentation() is None
|
|
|
|
|
|
def test_active_instruments_support_tvm_fifo_exit_order():
|
|
first = StackedPassInstrument(_RecordingObserver())
|
|
second = StackedPassInstrument(_RecordingObserver())
|
|
|
|
first.enter_pass_ctx()
|
|
assert active_stacked_pass_instruments() == (first,)
|
|
second.enter_pass_ctx()
|
|
assert active_stacked_pass_instruments() == (first, second)
|
|
first.exit_pass_ctx()
|
|
assert active_stacked_pass_instruments() == (second,)
|
|
second.exit_pass_ctx()
|
|
assert active_stacked_pass_instruments() == ()
|