Skip to content

Commit 85511d6

Browse files
committed
Add location alias parsing support in IR parser and trace processor
Implements comprehensive support for parsing location aliases in TTIR/TTGIR: Backend changes: - Add ALIAS_WITH_NAME_PATTERN and ALIAS_SIMPLE_PATTERN regex to match alias definitions like #loc16 = loc("pid"(#loc2)) and #loc20 = loc(#loc16) - Update LOC_PATTERN to only match numbered locs (#loc1, #loc2) not bare #loc - Implement alias resolution logic with cycle detection - Track definition lines (def_line), alias names (alias_name), and alias targets (alias_of) for each location - Propagate alias metadata through trace_processor to frontend - Add separate mappings for loc definition lines with kind="loc_def" Testing: - Add comprehensive unit test test_loc_alias_parsing() covering: * Bare #loc references (e.g., #loc13 = loc("x_ptr"(#loc))) * Named aliases with numbered refs (e.g., #loc16 = loc("pid"(#loc2))) * Simple aliases without names (e.g., #loc20 = loc(#loc1)) * Definition line tracking for all loc types This enables the frontend to display alias information and highlight loc definition lines in the code viewer.
1 parent ee8b656 commit 85511d6

File tree

3 files changed

+182
-2
lines changed

3 files changed

+182
-2
lines changed

tests/test_tritonparse.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,70 @@ def count_all_blobs(manager_dir_path):
14421442
# Cleanup test-specific cache
14431443
self.cleanup_test_cache(test_cache_dir, prev_cache_dir)
14441444

1445+
def test_loc_alias_parsing(self):
1446+
"""Test parsing of location aliases in TTIR/TTGIR"""
1447+
from tritonparse.ir_parser import extract_loc_definitions
1448+
1449+
# Test case 1: Bare #loc reference (no number)
1450+
ir_with_bare_loc = """
1451+
module {
1452+
#loc = loc("/tmp/test.py":10:5)
1453+
#loc13 = loc("x_ptr"(#loc))
1454+
func @kernel(%arg0: !tt.ptr<f32> loc(#loc13)) {
1455+
return loc(#loc)
1456+
}
1457+
}
1458+
"""
1459+
locs = extract_loc_definitions(ir_with_bare_loc)
1460+
# Main #loc should be stored with "" key
1461+
assert "" in locs, "Main #loc not found"
1462+
assert locs[""]["file"] == "/tmp/test.py"
1463+
assert locs[""]["line"] == 10
1464+
# Alias #loc13 should resolve to same location
1465+
assert "13" in locs, "#loc13 not found"
1466+
assert locs["13"]["file"] == "/tmp/test.py"
1467+
assert locs["13"]["line"] == 10
1468+
assert locs["13"]["alias_name"] == "x_ptr"
1469+
assert locs["13"]["alias_of"] == ""
1470+
1471+
# Test case 2: Named alias with numbered reference
1472+
ir_with_numbered_alias = """
1473+
#loc = loc("/tmp/test.py":5:0)
1474+
#loc2 = loc("/tmp/test.py":20:28)
1475+
#loc16 = loc("pid"(#loc2))
1476+
%0 = tt.get_program_id x : i32 loc(#loc16)
1477+
"""
1478+
locs = extract_loc_definitions(ir_with_numbered_alias)
1479+
assert "2" in locs
1480+
assert locs["2"]["line"] == 20
1481+
assert "16" in locs
1482+
assert locs["16"]["file"] == "/tmp/test.py"
1483+
assert locs["16"]["line"] == 20
1484+
assert locs["16"]["alias_name"] == "pid"
1485+
assert locs["16"]["alias_of"] == "2"
1486+
1487+
# Test case 3: Simple alias (no name)
1488+
ir_with_simple_alias = """
1489+
#loc = loc("/tmp/test.py":1:1)
1490+
#loc1 = loc("/tmp/test.py":15:10)
1491+
#loc20 = loc(#loc1)
1492+
%1 = arith.constant 0 : i32 loc(#loc20)
1493+
"""
1494+
locs = extract_loc_definitions(ir_with_simple_alias)
1495+
assert "1" in locs
1496+
assert "20" in locs
1497+
assert locs["20"]["file"] == "/tmp/test.py"
1498+
assert locs["20"]["line"] == 15
1499+
assert locs["20"]["alias_of"] == "1"
1500+
assert "alias_name" not in locs["20"]
1501+
1502+
# Test case 4: Definition line tracking
1503+
assert "def_line" in locs[""]
1504+
assert "def_line" in locs["1"]
1505+
assert "def_line" in locs["20"]
1506+
1507+
print("✓ All loc alias parsing tests passed")
1508+
14451509

14461510
if __name__ == "__main__":
14471511
unittest.main()

tritonparse/ir_parser.py

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
# the definition of the #loc directive. they are in the bottom of the IR files
1212
# Example:#loc2 = loc("/tmp/torchinductor_yhao/yp/abcdef.py":20:28)
13-
LOC_PATTERN = re.compile(r'#loc(\d*) = loc\("([^"]+)":(\d+):(\d+)\)')
13+
# Note: This should only match numbered locs like #loc1, #loc2, not bare #loc
14+
LOC_PATTERN = re.compile(r'#loc(\d+) = loc\("([^"]+)":(\d+):(\d+)\)')
1415

1516
# the reference to the #loc directive. they are in the end of lines of the IR files
1617
# Example: loc(#loc2)
@@ -33,6 +34,17 @@
3334
)
3435

3536

37+
# alias loc definitions in TTGIR/TTIR
38+
# Example: #loc16 = loc("pid"(#loc2))
39+
# Example: #loc13 = loc("x_ptr"(#loc)) - bare #loc without number
40+
ALIAS_WITH_NAME_PATTERN = re.compile(
41+
r'#loc(\d+)\s*=\s*loc\("([^"]+)"\s*\(\s*#loc(\d*)\s*\)\s*\)'
42+
)
43+
44+
# Example: #loc20 = loc(#loc16)
45+
ALIAS_SIMPLE_PATTERN = re.compile(r"#loc(\d+)\s*=\s*loc\(\s*#loc(\d*)\s*\)")
46+
47+
3648
def extract_loc_definitions(ir_content: str) -> Dict[str, Dict[str, Any]]:
3749
"""
3850
Extracts location definitions from the given IR content.
@@ -50,6 +62,7 @@ def extract_loc_definitions(ir_content: str) -> Dict[str, Dict[str, Any]]:
5062
"""
5163
locations = {}
5264
# The first #loc directive is a special case. It locates at the top of the IR files
65+
# Store it with empty string "" as key to avoid conflict with #loc1
5366
main_match = re.search(r'#loc = loc\("([^"]+)":(\d+):(\d+)\)', ir_content)
5467
if main_match:
5568
locations[""] = {
@@ -61,6 +74,84 @@ def extract_loc_definitions(ir_content: str) -> Dict[str, Dict[str, Any]]:
6174
for loc_id, filename, line, col in LOC_PATTERN.findall(ir_content):
6275
key = loc_id
6376
locations[key] = {"file": filename, "line": int(line), "column": int(col)}
77+
78+
# Handle alias-style loc definitions that reference another #loc
79+
# Build alias map first: alias_id -> target_id
80+
alias_map: Dict[str, str] = {}
81+
for m in ALIAS_WITH_NAME_PATTERN.finditer(ir_content):
82+
alias_id, _name, target_id = m.groups()
83+
# Empty target_id means bare #loc, map to "" (main loc key)
84+
alias_map[alias_id] = target_id or ""
85+
for m in ALIAS_SIMPLE_PATTERN.finditer(ir_content):
86+
alias_id, target_id = m.groups()
87+
# Empty target_id means bare #loc, map to "" (main loc key)
88+
alias_map[alias_id] = target_id or ""
89+
90+
# Build definition line map and alias name map by scanning lines
91+
def_line_map: Dict[str, int] = {}
92+
alias_name_map: Dict[str, str] = {}
93+
main_loc_line: int = 0
94+
for i, line in enumerate(ir_content.split("\n"), start=1):
95+
if m := ALIAS_WITH_NAME_PATTERN.search(line):
96+
alias_id, name, target_id = m.groups()
97+
def_line_map[alias_id] = i
98+
alias_name_map[alias_id] = name
99+
# ensure alias map is populated even if only found in line scan
100+
# Empty target_id means bare #loc, map to "" (main loc key)
101+
alias_map.setdefault(alias_id, target_id or "")
102+
elif m := ALIAS_SIMPLE_PATTERN.search(line):
103+
alias_id, target_id = m.groups()
104+
def_line_map[alias_id] = i
105+
# Empty target_id means bare #loc, map to "" (main loc key)
106+
alias_map.setdefault(alias_id, target_id or "")
107+
if m2 := LOC_PATTERN.search(line):
108+
base_id, _fn, _ln, _col = m2.groups()
109+
def_line_map[base_id] = i
110+
if re.search(r'#loc\s*=\s*loc\("[^"]+":\d+:\d+\)', line):
111+
# main #loc = loc("file":line:col) without id
112+
main_loc_line = main_loc_line or i
113+
114+
# Resolve aliases to base locations (file/line/column)
115+
resolving_stack = set()
116+
117+
def resolve_alias(current_id: str) -> Dict[str, Any]:
118+
# Already a concrete location
119+
if current_id in locations:
120+
return locations[current_id]
121+
# Detect cycles
122+
if current_id in resolving_stack:
123+
return {}
124+
resolving_stack.add(current_id)
125+
parent_id = alias_map.get(current_id)
126+
result: Dict[str, Any] = {}
127+
if parent_id is not None:
128+
base = resolve_alias(parent_id)
129+
if base:
130+
# copy to avoid sharing the same dict by reference
131+
result = {
132+
"file": base.get("file"),
133+
"line": base.get("line"),
134+
"column": base.get("column"),
135+
}
136+
locations[current_id] = result
137+
resolving_stack.remove(current_id)
138+
return result
139+
140+
for alias_id in list(alias_map.keys()):
141+
if alias_id not in locations:
142+
resolve_alias(alias_id)
143+
144+
# Attach definition line and alias metadata
145+
for k, v in def_line_map.items():
146+
if k in locations:
147+
locations[k]["def_line"] = v
148+
for alias_id, target_id in alias_map.items():
149+
if alias_id in locations:
150+
locations[alias_id]["alias_of"] = target_id
151+
if alias_id in alias_name_map:
152+
locations[alias_id]["alias_name"] = alias_name_map[alias_id]
153+
if main_loc_line and "" in locations:
154+
locations[""]["def_line"] = main_loc_line
64155
return locations
65156

66157

tritonparse/trace_processor.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,37 @@ def generate_source_mappings(
7171
}
7272
elif loc_id in loc_defs:
7373
info = loc_defs[loc_id]
74-
mappings[str(ln)] = {
74+
entry = {
7575
"file": info["file"],
7676
"line": info["line"],
7777
"column": info["column"],
7878
f"{ir_type}_line": ln,
7979
}
80+
# Propagate alias metadata if present
81+
if "alias_name" in info:
82+
entry["alias_name"] = info["alias_name"]
83+
if "alias_of" in info:
84+
entry["loc_id"] = loc_id
85+
mappings[str(ln)] = entry
86+
87+
# Add separate entries for loc definition lines
88+
for loc_id, info in loc_defs.items():
89+
if "def_line" in info:
90+
def_ln = info["def_line"]
91+
# Only create mapping if this line doesn't already have one
92+
if str(def_ln) not in mappings:
93+
entry = {
94+
"file": info["file"],
95+
"line": info["line"],
96+
"column": info["column"],
97+
f"{ir_type}_line": def_ln,
98+
"kind": "loc_def",
99+
}
100+
if "alias_name" in info:
101+
entry["alias_name"] = info["alias_name"]
102+
if "alias_of" in info:
103+
entry["loc_id"] = loc_id
104+
mappings[str(def_ln)] = entry
80105

81106
return mappings
82107

0 commit comments

Comments
 (0)