tritonparse 0.3.1.dev20251029071541__tar.gz → 0.3.1.dev20251030071508__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tritonparse might be problematic. Click here for more details.

Files changed (120) hide show
  1. {tritonparse-0.3.1.dev20251029071541/tritonparse.egg-info → tritonparse-0.3.1.dev20251030071508}/PKG-INFO +1 -1
  2. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/test_tritonparse.py +57 -0
  3. tritonparse-0.3.1.dev20251030071508/tritonparse/ir_analysis.py +427 -0
  4. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/ir_parser.py +52 -0
  5. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/trace_processor.py +5 -0
  6. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508/tritonparse.egg-info}/PKG-INFO +1 -1
  7. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/pages/IRAnalysis.tsx +72 -1
  8. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/utils/dataLoader.ts +1 -0
  9. tritonparse-0.3.1.dev20251029071541/tritonparse/ir_analysis.py +0 -77
  10. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.ci/README.md +0 -0
  11. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.ci/install-project.sh +0 -0
  12. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.ci/install-triton.sh +0 -0
  13. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.ci/run-tests.sh +0 -0
  14. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.ci/setup.sh +0 -0
  15. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.github/PAGES_SETUP.md +0 -0
  16. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.github/workflows/deploy-pages-standalone.yml +0 -0
  17. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.github/workflows/deploy-pages.yml +0 -0
  18. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.github/workflows/nightly-pypi.yml +0 -0
  19. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.github/workflows/test.yml +0 -0
  20. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/.gitignore +0 -0
  21. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/CHANGELOG.md +0 -0
  22. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/CODE_OF_CONDUCT.md +0 -0
  23. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/CONTRIBUTING.md +0 -0
  24. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/LICENSE +0 -0
  25. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/Makefile +0 -0
  26. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/README.md +0 -0
  27. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/__init__.py +0 -0
  28. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/pyproject.toml +0 -0
  29. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/run.py +0 -0
  30. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/setup.cfg +0 -0
  31. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/README.md +0 -0
  32. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/__init__.py +0 -0
  33. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/example_output/logs/dedicated_log_triton_trace_findhao_.ndjson +0 -0
  34. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/example_output/parsed_output/dedicated_log_triton_trace_findhao__mapped.ndjson.gz +0 -0
  35. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/example_output/parsed_output/f0_fc0_a0_cai-.ndjson.gz +0 -0
  36. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/example_output/parsed_output/log_file_list.json +0 -0
  37. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/example_output/parsed_output_complex/dedicated_log_triton_trace_findhao__mapped.ndjson.gz +0 -0
  38. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/example_output/parsed_output_complex/log_file_list.json +0 -0
  39. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tests/test_add.py +0 -0
  40. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/__init__.py +0 -0
  41. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/__main__.py +0 -0
  42. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/cli.py +0 -0
  43. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/common.py +0 -0
  44. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/context_manager.py +0 -0
  45. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/event_diff.py +0 -0
  46. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/extract_source_mappings.py +0 -0
  47. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/mapper.py +0 -0
  48. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/__init__.py +0 -0
  49. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/cli.py +0 -0
  50. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/function_extractor.py +0 -0
  51. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/ingestion/ndjson.py +0 -0
  52. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/orchestrator.py +0 -0
  53. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/placeholder_replacer.py +0 -0
  54. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/templates/__init__.py +0 -0
  55. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/templates/example.py +0 -0
  56. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/templates/loader.py +0 -0
  57. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/templates/tritonbench.py +0 -0
  58. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/types.py +0 -0
  59. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/reproducer/utils.py +0 -0
  60. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/shared_vars.py +0 -0
  61. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/source_type.py +0 -0
  62. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/sourcemap_utils.py +0 -0
  63. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/structured_logging.py +0 -0
  64. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/tools/__init__.py +0 -0
  65. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/tools/decompress_bin_ndjson.py +0 -0
  66. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/tools/disasm.py +0 -0
  67. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/tools/format_fix.py +0 -0
  68. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/tools/load_tensor.py +0 -0
  69. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/tools/prettify_ndjson.py +0 -0
  70. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/tools/readme.md +0 -0
  71. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/tp_logger.py +0 -0
  72. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse/utils.py +0 -0
  73. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse.egg-info/SOURCES.txt +0 -0
  74. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse.egg-info/dependency_links.txt +0 -0
  75. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse.egg-info/entry_points.txt +0 -0
  76. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse.egg-info/requires.txt +0 -0
  77. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/tritonparse.egg-info/top_level.txt +0 -0
  78. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/eslint.config.js +0 -0
  79. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/index.html +0 -0
  80. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/package-lock.json +0 -0
  81. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/package.json +0 -0
  82. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/public/dedicated_log_triton_trace_findhao__mapped.ndjson.gz +0 -0
  83. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/public/f0_fc0_a0_cai-.ndjson +0 -0
  84. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/public/favicon.ico +0 -0
  85. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/public/logo.svg +0 -0
  86. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/scripts/inline-html.js +0 -0
  87. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/scripts/update_deps.sh +0 -0
  88. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/App.css +0 -0
  89. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/App.tsx +0 -0
  90. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/assets/react.svg +0 -0
  91. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/ArgumentViewer.tsx +0 -0
  92. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/Callstack.tsx +0 -0
  93. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/CodeComparisonView.tsx +0 -0
  94. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/CodeViewer.css +0 -0
  95. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/CodeViewer.tsx +0 -0
  96. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/CompilationInfo.tsx +0 -0
  97. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/CopyCodeButton.tsx +0 -0
  98. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/DataSourceSelector.tsx +0 -0
  99. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/DiffComparisonView.tsx +0 -0
  100. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/DiffViewer.tsx +0 -0
  101. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/ExternalLink.tsx +0 -0
  102. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/SingleCodeViewer.tsx +0 -0
  103. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/StackDiffViewer.tsx +0 -0
  104. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/ToggleSwitch.tsx +0 -0
  105. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/TritonIRs.tsx +0 -0
  106. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/components/WelcomeScreen.tsx +0 -0
  107. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/context/FileDiffSession.tsx +0 -0
  108. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/index.css +0 -0
  109. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/main.tsx +0 -0
  110. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/pages/CodeView.tsx +0 -0
  111. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/pages/FileDiffView.tsx +0 -0
  112. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/pages/KernelOverview.tsx +0 -0
  113. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/utils/fbDetection.ts +0 -0
  114. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/utils/safeImport.ts +0 -0
  115. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/utils/tensor.ts +0 -0
  116. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/src/vite-env.d.ts +0 -0
  117. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/tsconfig.app.json +0 -0
  118. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/tsconfig.json +0 -0
  119. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/tsconfig.node.json +0 -0
  120. {tritonparse-0.3.1.dev20251029071541 → tritonparse-0.3.1.dev20251030071508}/website/vite.config.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tritonparse
3
- Version: 0.3.1.dev20251029071541
3
+ Version: 0.3.1.dev20251030071508
4
4
  Summary: TritonParse: A Compiler Tracer, Visualizer, and mini-Reproducer Generator for Triton Kernels
5
5
  Author-email: Yueming Hao <yhao@meta.com>
6
6
  License-Expression: BSD-3-Clause
@@ -139,6 +139,63 @@ def clear_all_caches(*kernels):
139
139
  class TestTritonparseCPU(unittest.TestCase):
140
140
  """CPU-only tests (no CUDA required)"""
141
141
 
142
+ def test_callsite_parsing(self):
143
+ """Test parsing of callsite locations in TTIR/TTGIR"""
144
+ from tritonparse.ir_parser import extract_loc_definitions
145
+ from tritonparse.trace_processor import generate_source_mappings
146
+
147
+ # Test MLIR callsite location definitions
148
+ ir_with_callsite = """
149
+ module {
150
+ #loc7 = loc("/tmp/test.py":1091:8)
151
+ #loc57 = loc("/tmp/test.py":421:16)
152
+ #loc58 = loc("/tmp/test.py":853:16)
153
+ #loc190 = loc(callsite(#loc58 at #loc7))
154
+ #loc220 = loc(callsite(#loc57 at #loc190))
155
+ %0 = tt.load %ptr loc(#loc220)
156
+ }
157
+ """
158
+ # Extract loc definitions
159
+ locs = extract_loc_definitions(ir_with_callsite)
160
+
161
+ # Verify loc220 (nested callsite)
162
+ self.assertIn("220", locs)
163
+ self.assertEqual(locs["220"]["file"], "/tmp/test.py")
164
+ self.assertEqual(locs["220"]["line"], 421) # Inherited from callee loc57
165
+ self.assertEqual(locs["220"]["column"], 16)
166
+ self.assertTrue(locs["220"].get("is_callsite"))
167
+ self.assertEqual(locs["220"]["callsite_callee"], "57")
168
+ self.assertEqual(locs["220"]["callsite_caller"], "190")
169
+
170
+ # Verify loc190 (simple callsite)
171
+ self.assertIn("190", locs)
172
+ self.assertEqual(locs["190"]["line"], 853) # Inherited from callee loc58
173
+ self.assertTrue(locs["190"].get("is_callsite"))
174
+ self.assertEqual(locs["190"]["callsite_callee"], "58")
175
+ self.assertEqual(locs["190"]["callsite_caller"], "7")
176
+
177
+ # Test source mappings generation
178
+ mappings = generate_source_mappings(ir_with_callsite, "ttir")
179
+
180
+ # Find the line with tt.load
181
+ line_with_load = None
182
+ for line_num, content in enumerate(ir_with_callsite.split("\n"), start=1):
183
+ if "tt.load" in content:
184
+ line_with_load = str(line_num)
185
+ break
186
+
187
+ self.assertIsNotNone(line_with_load)
188
+ self.assertIn(line_with_load, mappings)
189
+
190
+ mapping = mappings[line_with_load]
191
+ self.assertEqual(mapping["file"], "/tmp/test.py")
192
+ self.assertEqual(mapping["line"], 421) # From loc220 -> loc57
193
+ self.assertTrue(mapping.get("is_callsite"))
194
+ self.assertEqual(mapping["callsite_callee"], "57")
195
+ self.assertEqual(mapping["callsite_caller"], "190")
196
+
197
+ print("✓ Callsite parsing tests passed")
198
+
142
199
  def test_convert(self):
143
200
  """Test convert function with various data types"""
144
201
  # Test with primitive types
@@ -0,0 +1,427 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import logging
4
+
5
+ from .sourcemap_utils import load_ir_contents
6
+
7
+
8
+ logger = logging.getLogger("IRAnalysis")
9
+
10
+
11
+ def process_amd_bufferop(ir_content: str, io_keys: list[str]) -> dict[str, int]:
12
+ def make_key(prefix: str) -> str:
13
+ return f"{prefix}_count"
14
+
15
+ io_keys = [(make_key(prefix), prefix) for prefix in io_keys]
16
+ output: dict[str, int] = {}
17
+ for dict_key, _ in io_keys:
18
+ output[dict_key] = 0
19
+ if ir_content:
20
+ for line in ir_content.split("\n"):
21
+ for dict_key, code_key in io_keys:
22
+ if code_key in line:
23
+ output[dict_key] += 1
24
+ return output
25
+
26
+
27
+ def process_amd_ttgir_bufferops(
28
+ key: str,
29
+ file_content: dict[str, str],
30
+ file_path: dict[str, str],
31
+ ) -> dict[str, int]:
32
+ ir_content = load_ir_contents(key, file_content, file_path)
33
+ # TODO: Add atomics
34
+ io_keys = ["tt.load", "tt.store", "amdgpu.buffer_load", "amdgpu.buffer_store"]
35
+ return process_amd_bufferop(ir_content, io_keys)
36
+
37
+
38
+ def process_amd_gcn_bufferops(
39
+ key: str,
40
+ file_content: dict[str, str],
41
+ file_path: dict[str, str],
42
+ ) -> dict[str, int]:
43
+ ir_content = load_ir_contents(key, file_content, file_path)
44
+ # TODO: Add atomics
45
+ io_keys = ["global_load", "global_store", "buffer_load", "buffer_store"]
46
+ return process_amd_bufferop(ir_content, io_keys)
47
+
48
+
49
+ def find_loop_bounds(ir_content: str) -> list[tuple[int, int]]:
50
+ """
51
+ Find the bounds of all scf.for loops in the IR content.
52
+ These are the only candidates for Software Pipelining (SWP).
53
+
54
+ A loop starts with 'scf.for' and ends when its closing brace '}' is found.
55
+ Brace counts are tracked to determine when each loop closes.
56
+
57
+ Args:
58
+ ir_content: The IR content as a string.
59
+
60
+ Returns:
61
+ A list of tuples (start_line, end_line) for each scf.for loop found.
62
+ Line numbers are 0-indexed.
63
+ """
64
+ if not ir_content:
65
+ return []
66
+
67
+ loop_bounds: list[tuple[int, int]] = []
68
+ lines = ir_content.split("\n")
69
+
70
+ # Stack to track loop starts and their brace counts
71
+ # Each entry is (start_line, brace_count_at_start)
72
+ loop_stack: list[tuple[int, int]] = []
73
+ current_brace_count = 0
74
+
75
+ for line_idx, line in enumerate(lines):
76
+ # Check if this line starts a new scf.for loop
77
+ if "scf.for" in line:
78
+ loop_stack.append((line_idx, current_brace_count))
79
+
80
+ # Count braces on this line
81
+ for char in line:
82
+ if char == "{":
83
+ current_brace_count += 1
84
+ elif char == "}":
85
+ current_brace_count -= 1
86
+
87
+ # Check if we've closed any loops
88
+ while loop_stack and current_brace_count <= loop_stack[-1][1]:
89
+ start_line, _start_brace_count = loop_stack.pop()
90
+ # The loop ends at this line
91
+ loop_bounds.append((start_line, line_idx))
92
+
93
+ return loop_bounds
94
+
95
+
96
+ def find_inner_loop_bounds(ir_content: str) -> list[tuple[int, int]]:
97
+ """
98
+ Find the bounds of inner scf.for loops (loops without nested loops inside).
99
+
100
+ Inner loops are the primary candidates for Software Pipelining (SWP) as they
101
+ represent the innermost computation that can be optimized.
102
+
103
+ Args:
104
+ ir_content: The IR content as a string.
105
+
106
+ Returns:
107
+ A list of tuples (start_line, end_line) for each inner scf.for loop found.
108
+ Line numbers are 0-indexed.
109
+ """
110
+ all_loops = find_loop_bounds(ir_content)
111
+
112
+ if not all_loops:
113
+ return []
114
+
115
+ # Filter to keep only inner loops (loops that don't contain other loops)
116
+ inner_loops: list[tuple[int, int]] = []
117
+
118
+ for i, (start_i, end_i) in enumerate(all_loops):
119
+ # Check if any other loop is nested inside this loop
120
+ has_nested_loop = False
121
+ for j, (start_j, end_j) in enumerate(all_loops):
122
+ if i != j:
123
+ # Check if loop j is nested inside loop i
124
+ if start_i < start_j and end_j < end_i:
125
+ has_nested_loop = True
126
+ break
127
+
128
+ # If no nested loops found, this is an inner loop
129
+ if not has_nested_loop:
130
+ inner_loops.append((start_i, end_i))
131
+
132
+ return inner_loops
133
+
134
+
135
+ def find_loop_pipelining(
136
+ ttir_content: str,
137
+ ttgir_content: str,
138
+ ttir_loop_start: int,
139
+ ttir_loop_end: int,
140
+ loop_index: int,
141
+ ttir_to_ttgir_mapping: dict[str, dict],
142
+ ttgir_to_source_mapping: dict[str, dict],
143
+ python_source_content: str | None,
144
+ python_source_start_line: int,
145
+ ) -> dict[str, list[str]]:
146
+ """
147
+ Find pipelining information for a specific loop by identifying tt.load and tt.dot operations
148
+ in TTIR and mapping them to their corresponding operations in the original Python source code.
149
+
150
+ For each tt.load or tt.dot operation found in the TTIR loop, this function uses source
151
+ mappings to find the corresponding operations in TTGIR, then maps them back to the original
152
+ Python source code. Operations are categorized into three sections:
153
+ - prologue: Operations that appear before the loop body
154
+ - loop_body: Operations that appear within the loop body
155
+ - epilogue: Operations that appear after the loop body
156
+
157
+ Operations are merged together (both loads and dots) and sorted in program order
158
+ within each section.
159
+
160
+ Args:
161
+ ttir_content: The TTIR content as a string.
162
+ ttgir_content: The TTGIR content as a string.
163
+ ttir_loop_start: The starting line number of the loop in TTIR (0-indexed).
164
+ ttir_loop_end: The ending line number of the loop in TTIR (0-indexed).
165
+ ttir_to_ttgir_mapping: Source mapping from TTIR lines to TTGIR lines.
166
+ ttgir_to_source_mapping: Source mapping from TTGIR lines to original Python source.
167
+ python_source_content: The original Python source code content.
168
+
169
+ Returns:
170
+ A dictionary containing:
171
+ - "prologue": List of Python source line strings in program order
172
+ - "loop_body": List of Python source line strings in program order
173
+ - "epilogue": List of Python source line strings in program order
174
+ """
175
+ if not ttir_content or not ttgir_content:
176
+ return {
177
+ "prologue": [],
178
+ "loop_body": [],
179
+ "epilogue": [],
180
+ }
181
+
182
+ ttir_lines = ttir_content.split("\n")
183
+ ttgir_lines = ttgir_content.split("\n")
184
+ python_lines = python_source_content.split("\n") if python_source_content else []
185
+
186
+ def apply_trailing_space(op: str) -> str:
187
+ """
188
+ Add a trailing space to all ops to avoid false positives like
189
+ warp_group_dot and warp_group_dot_wait.
190
+ """
191
+ return op + " "
192
+
193
+ # Step 1: Find tt.load and tt.dot operations in TTIR loop
194
+ ttir_pipeline_lines: list[int] = []
195
+ pipeline_tt_ops = ["tt.load", "tt.dot"]
196
+ pipeline_tt_ops = [apply_trailing_space(op) for op in pipeline_tt_ops]
197
+ pipeline_ttgir_ops = [
198
+ "tt.load",
199
+ "tt.dot",
200
+ "async_copy_global_to_local",
201
+ "warp_group_dot",
202
+ ]
203
+ pipeline_ttgir_ops = [apply_trailing_space(op) for op in pipeline_ttgir_ops]
204
+ for line_idx in range(ttir_loop_start, min(ttir_loop_end + 1, len(ttir_lines))):
205
+ line = ttir_lines[line_idx]
206
+ for op in pipeline_tt_ops:
207
+ if op in line:
208
+ ttir_pipeline_lines.append(line_idx)
209
+ break
210
+
211
+ # Step 2: Find the corresponding loop in TTGIR using source mappings
212
+ # Map the TTIR loop bounds to TTGIR using source mappings
213
+ ttgir_inner_loops = find_inner_loop_bounds(ttgir_content)
214
+
215
+ if not ttgir_inner_loops:
216
+ # No loop found in TTGIR, return empty results
217
+ return {
218
+ "prologue": [],
219
+ "loop_body": [],
220
+ "epilogue": [],
221
+ }
222
+
223
+ # Use the first inner loop as the reference
224
+ # TODO: Implement more sophisticated mapping logic to match TTIR loops to TTGIR loops
225
+ ttgir_loop_start, ttgir_loop_end = ttgir_inner_loops[loop_index]
226
+
227
+ # Step 3: Map TTIR operations to TTGIR operations using source mappings
228
+ # and categorize them by their position relative to the TTGIR loop
229
+ # Store as (line_number, source_line) to maintain order before extracting just the source
230
+ prologue_ops: list[tuple[int, str]] = []
231
+ loop_body_ops: list[tuple[int, str]] = []
232
+ epilogue_ops: list[tuple[int, str]] = []
233
+
234
+ for ttir_line in ttir_pipeline_lines:
235
+ # Convert 0-indexed line to 1-indexed string key for mapping lookup
236
+ ttir_line_key = str(ttir_line + 1)
237
+
238
+ # Get the corresponding TTGIR lines from the source mapping
239
+ if ttir_line_key in ttir_to_ttgir_mapping:
240
+ ttgir_lines_list = ttir_to_ttgir_mapping[ttir_line_key].get(
241
+ "ttgir_lines", []
242
+ )
243
+
244
+ # For each mapped TTGIR line, categorize it
245
+ for ttgir_line in ttgir_lines_list:
246
+ # Convert back to 0-indexed
247
+ ttgir_line_idx = ttgir_line - 1
248
+
249
+ # Get the actual TTGIR line content to check if it's relevant
250
+ if ttgir_line_idx < len(ttgir_lines):
251
+ ttgir_source_line = ttgir_lines[ttgir_line_idx].strip()
252
+
253
+ # Only keep mappings to the "compute" op.
254
+ if any(op in ttgir_source_line for op in pipeline_ttgir_ops):
255
+ # Map TTGIR line back to Python source
256
+ ttgir_line_key = str(ttgir_line)
257
+ python_source_line = ttgir_source_line # Default to TTGIR line
258
+
259
+ if ttgir_line_key in ttgir_to_source_mapping:
260
+ source_info = ttgir_to_source_mapping[ttgir_line_key]
261
+ python_line_num = source_info.get("line")
262
+
263
+ if python_line_num and python_lines:
264
+ # Account for the offset: the Python source may not start at line 1
265
+ # python_line_num is the absolute line number in the original file
266
+ # python_source_start_line is where the extracted code starts
267
+ # So we need to subtract the offset to get the index in our python_lines array
268
+ python_line_idx = (
269
+ python_line_num - python_source_start_line
270
+ )
271
+ if 0 <= python_line_idx < len(python_lines):
272
+ python_source_line = python_lines[
273
+ python_line_idx
274
+ ].strip()
275
+
276
+ if ttgir_line_idx < ttgir_loop_start:
277
+ prologue_ops.append((ttgir_line_idx, python_source_line))
278
+ elif ttgir_loop_start <= ttgir_line_idx <= ttgir_loop_end:
279
+ loop_body_ops.append((ttgir_line_idx, python_source_line))
280
+ else:
281
+ epilogue_ops.append((ttgir_line_idx, python_source_line))
282
+
283
+ # Step 4: Sort each section by line number to maintain program order
284
+ prologue_ops.sort(key=lambda x: x[0])
285
+ loop_body_ops.sort(key=lambda x: x[0])
286
+ epilogue_ops.sort(key=lambda x: x[0])
287
+
288
+ # Extract just the source lines (without line numbers)
289
+ prologue_lines = [line for _, line in prologue_ops]
290
+ loop_body_lines = [line for _, line in loop_body_ops]
291
+ epilogue_lines = [line for _, line in epilogue_ops]
292
+
293
+ # Log the pipelining results
294
+ logger.info(
295
+ f"Loop pipelining results (TTIR lines {ttir_loop_start}-{ttir_loop_end}):"
296
+ )
297
+ logger.info(f" Prologue ({len(prologue_lines)} ops):")
298
+ for line in prologue_lines:
299
+ logger.info(f" {line}")
300
+ logger.info(f" Loop Body ({len(loop_body_lines)} ops):")
301
+ for line in loop_body_lines:
302
+ logger.info(f" {line}")
303
+ logger.info(f" Epilogue ({len(epilogue_lines)} ops):")
304
+ for line in epilogue_lines:
305
+ logger.info(f" {line}")
306
+
307
+ return {
308
+ "prologue": prologue_lines,
309
+ "loop_body": loop_body_lines,
310
+ "epilogue": epilogue_lines,
311
+ }
312
+
313
+
314
+ def generate_loop_schedule(
315
+ ttir_key: str,
316
+ ttgir_key: str,
317
+ file_content: dict[str, str],
318
+ file_path: dict[str, str],
319
+ source_mappings: dict[str, dict],
320
+ python_source_content: str | None,
321
+ python_source_start_line: int,
322
+ ) -> list[dict]:
323
+ """
324
+ Generate loop schedule information by finding inner scf.for loops in TTIR
325
+ and analyzing their pipelining potential using source mappings.
326
+
327
+ Only inner loops (loops without nested loops) are considered as they are
328
+ the primary candidates for Software Pipelining (SWP).
329
+
330
+ Args:
331
+ ttir_key: Key for the TTIR file.
332
+ ttgir_key: Key for the TTGIR file.
333
+ file_content: Dictionary mapping file keys to content.
334
+ file_path: Dictionary mapping file keys to file paths.
335
+ source_mappings: Dictionary containing source mappings between IR stages.
336
+ python_source_content: The original Python source code content.
337
+ python_source_start_line: The starting line number of the Python source in the original file.
338
+
339
+ Returns:
340
+ A list of dictionaries, each containing:
341
+ - "loop_bounds": Tuple of (start_line, end_line) for the loop in TTIR
342
+ - "pipelining": Dictionary with Python source lines for operations
343
+ """
344
+ ttir_content = load_ir_contents(ttir_key, file_content, file_path)
345
+ ttgir_content = load_ir_contents(ttgir_key, file_content, file_path)
346
+
347
+ # Get the TTIR to TTGIR mapping and TTGIR to source mapping
348
+ ttir_to_ttgir_mapping = source_mappings.get("ttir", {})
349
+ ttgir_to_source_mapping = source_mappings.get("ttgir", {})
350
+
351
+ # Find only inner loops (loops without nested loops inside)
352
+ inner_loop_bounds = find_inner_loop_bounds(ttir_content)
353
+ # TODO: Fix loop mapping with multiple loops.
354
+ inner_loop_bounds = inner_loop_bounds[:1]
355
+
356
+ # For each inner loop, find pipelining information
357
+ loop_schedules = []
358
+ for i, (loop_start, loop_end) in enumerate(inner_loop_bounds):
359
+ pipelining_info = find_loop_pipelining(
360
+ ttir_content,
361
+ ttgir_content,
362
+ loop_start,
363
+ loop_end,
364
+ i,
365
+ ttir_to_ttgir_mapping,
366
+ ttgir_to_source_mapping,
367
+ python_source_content,
368
+ python_source_start_line,
369
+ )
370
+ loop_schedules.append(pipelining_info)
371
+
372
+ return loop_schedules
373
+
374
+
375
+ def _generate_ir_analysis(entry: str):
376
+ payload = entry.setdefault("payload", {})
377
+ file_content = payload.get("file_content", {})
378
+ file_path = payload.get("file_path", {})
379
+ source_mappings = payload.get("source_mappings", {})
380
+
381
+ # Find the IR file keys
382
+ ttir_key = next((k for k in file_content if k.endswith(".ttir")), None)
383
+ ttgir_key = next((k for k in file_content if k.endswith(".ttgir")), None)
384
+ amdgcn_key = next((k for k in file_content if k.endswith(".amdgcn")), None)
385
+ # Skip if no IR files found
386
+ if not (ttir_key or ttgir_key or amdgcn_key):
387
+ logger.debug("No IR found")
388
+ return {}
389
+ ir_analysis = {}
390
+ if amdgcn_key and ttgir_key:
391
+ # Add BufferOps information
392
+ ttgir_bufferops_info = process_amd_ttgir_bufferops(
393
+ ttgir_key, file_content, file_path
394
+ )
395
+ gcn_bufferops_info = process_amd_gcn_bufferops(
396
+ amdgcn_key, file_content, file_path
397
+ )
398
+ io_counts = {}
399
+ # NDJSON format requires a newline at the end of each line
400
+ if ttgir_bufferops_info:
401
+ io_counts["amd_ttgir_bufferops_count"] = ttgir_bufferops_info
402
+ if gcn_bufferops_info:
403
+ io_counts["amd_gcn_bufferops_count"] = gcn_bufferops_info
404
+ if io_counts:
405
+ ir_analysis["io_counts"] = io_counts
406
+ if ttir_key and ttgir_key:
407
+ # Get Python source content and start line if available
408
+ python_source_content = None
409
+ python_source_start_line = 1 # Default to 1 if not available
410
+ python_source_info = payload.get("python_source")
411
+ if python_source_info:
412
+ python_source_content = python_source_info.get("code")
413
+ python_source_start_line = python_source_info.get("start_line", 1)
414
+
415
+ # Add loop schedule information
416
+ loop_schedule = generate_loop_schedule(
417
+ ttir_key,
418
+ ttgir_key,
419
+ file_content,
420
+ file_path,
421
+ source_mappings,
422
+ python_source_content,
423
+ python_source_start_line,
424
+ )
425
+ if loop_schedule:
426
+ ir_analysis["loop_schedules"] = loop_schedule
427
+ return ir_analysis
@@ -44,6 +44,14 @@ ALIAS_WITH_NAME_PATTERN = re.compile(
44
44
  # Example: #loc20 = loc(#loc16)
45
45
  ALIAS_SIMPLE_PATTERN = re.compile(r"#loc(\d+)\s*=\s*loc\(\s*#loc(\d*)\s*\)")
46
46
 
47
+ # Callsite loc definitions in TTIR/TTGIR
48
+ # Example: #loc220 = loc(callsite(#loc57 at #loc190))
49
+ # Captures: loc_id, callee_loc_id, caller_loc_id
50
+ # Note: Uses (\d*) to match optional numbers (for bare #loc references)
51
+ CALLSITE_PATTERN = re.compile(
52
+ r"#loc(\d+)\s*=\s*loc\(\s*callsite\(\s*#loc(\d*)\s+at\s+#loc(\d*)\s*\)\s*\)"
53
+ )
54
+
47
55
 
48
56
  def extract_loc_definitions(ir_content: str) -> Dict[str, Dict[str, Any]]:
49
57
  """
@@ -141,6 +149,50 @@ def extract_loc_definitions(ir_content: str) -> Dict[str, Dict[str, Any]]:
141
149
  for alias_id, target_id in alias_map.items():
142
150
  if alias_id not in locations:
143
151
  resolve_alias(alias_id)
152
+
153
+ # Collect callsite definitions
154
+ callsite_defs = []
155
+ for i, line in enumerate(ir_content.split("\n"), start=1):
156
+ if m := CALLSITE_PATTERN.search(line):
157
+ loc_id, callee_id, caller_id = m.groups()
158
+ # Empty strings map to main loc key ""
159
+ callsite_defs.append((loc_id, callee_id or "", caller_id or "", i))
160
+
161
+ # Resolve callsite definitions
162
+ # A callsite inherits the location from its callee (the code being called)
163
+ # and stores a reference to its caller (the code doing the calling)
164
+ for loc_id, callee_id, caller_id, def_line in callsite_defs:
165
+ if loc_id not in locations: # Avoid overwriting existing definitions
166
+ if callee_id in locations:
167
+ # Inherit location info from callee
168
+ callee_info = locations[callee_id]
169
+ locations[loc_id] = {
170
+ "file": callee_info["file"],
171
+ "line": callee_info["line"],
172
+ "column": callee_info["column"],
173
+ "def_line": def_line,
174
+ "is_callsite": True,
175
+ "callsite_callee": callee_id,
176
+ "callsite_caller": caller_id,
177
+ }
178
+ else:
179
+ logger.warning(
180
+ f"Callsite #loc{loc_id} references undefined callee #loc{callee_id}"
181
+ )
182
+ # Note: We don't add this callsite to locations since callee is missing
183
+
184
+ # Verify caller references (warning only, don't block)
185
+ for loc_id, _callee_id, caller_id, _def_line in callsite_defs:
186
+ if loc_id in locations and caller_id and caller_id not in locations:
187
+ logger.warning(
188
+ f"Callsite #loc{loc_id} references undefined caller #loc{caller_id}"
189
+ )
190
+
191
+ # Attach definition line and alias metadata
192
+ for k, v in def_line_map.items():
193
+ if k in locations:
194
+ locations[k]["def_line"] = v
195
+ for alias_id, target_id in alias_map.items():
144
196
  if alias_id in locations:
145
197
  locations[alias_id]["alias_of"] = target_id
146
198
  if alias_id in alias_name_map:
@@ -77,6 +77,11 @@ def generate_source_mappings(
77
77
  "column": info["column"],
78
78
  f"{ir_type}_line": ln,
79
79
  }
80
+ # Propagate callsite metadata if present
81
+ if info.get("is_callsite"):
82
+ entry["is_callsite"] = True
83
+ entry["callsite_callee"] = info["callsite_callee"]
84
+ entry["callsite_caller"] = info["callsite_caller"]
80
85
  # Propagate alias metadata if present
81
86
  if "alias_name" in info:
82
87
  entry["alias_name"] = info["alias_name"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tritonparse
3
- Version: 0.3.1.dev20251029071541
3
+ Version: 0.3.1.dev20251030071508
4
4
  Summary: TritonParse: A Compiler Tracer, Visualizer, and mini-Reproducer Generator for Triton Kernels
5
5
  Author-email: Yueming Hao <yhao@meta.com>
6
6
  License-Expression: BSD-3-Clause
@@ -27,6 +27,7 @@ const IRAnalysis: React.FC<IRAnalysisProps> = ({ kernels, selectedKernel }) => {
27
27
  const io_counts = kernel.ir_analysis?.io_counts;
28
28
  const ttgir_info = io_counts?.["amd_ttgir_bufferops_count"];
29
29
  const amdgcn_info = io_counts?.["amd_gcn_bufferops_count"];
30
+ const loop_schedule = kernel.ir_analysis?.loop_schedules;
30
31
  const getCount = (info: Record<string, number> | undefined, key: string): string => { return info?.[key]?.toString() ?? "N/A"; };
31
32
 
32
33
  return (
@@ -44,7 +45,7 @@ const IRAnalysis: React.FC<IRAnalysisProps> = ({ kernels, selectedKernel }) => {
44
45
  AMD BufferOps Information
45
46
  </h3>
46
47
 
47
- <div className="bg-gray-50 p-4 rounded-md border border-gray-200">
48
+ <div className="bg-gray-50 p-4 rounded-md border border-gray-200 mb-6">
48
49
  <div className="grid grid-cols-[repeat(auto-fit,_minmax(180px,_1fr))] gap-3">
49
50
  {ttgir_info && (
50
51
  <>
@@ -90,6 +91,76 @@ const IRAnalysis: React.FC<IRAnalysisProps> = ({ kernels, selectedKernel }) => {
90
91
  </div>
91
92
  </>
92
93
  )}
94
+
95
+ {loop_schedule && loop_schedule.length > 0 && (
96
+ <>
97
+ <h3 className="text-lg font-medium mb-3 text-gray-800">
98
+ Software Pipelining Schedule
99
+ </h3>
100
+
101
+ {loop_schedule.map((schedule: any, loopIndex: number) => {
102
+ const prologue = schedule?.prologue || [];
103
+ const loopBody = schedule?.loop_body || [];
104
+ const epilogue = schedule?.epilogue || [];
105
+
106
+ return (
107
+ <div key={loopIndex} className="bg-gray-50 p-4 rounded-md border border-gray-200 mb-4">
108
+ <h4 className="text-md font-semibold mb-2 text-gray-700">
109
+ Software Pipelining for Loop {loopIndex + 1}
110
+ </h4>
111
+
112
+ {/* Prologue */}
113
+ {prologue.length > 0 && (
114
+ <div className="mb-3">
115
+ <div className="text-sm font-medium text-gray-600 mb-1">Prologue:</div>
116
+ <div className="bg-white p-2 rounded border border-gray-200 font-mono text-xs">
117
+ {prologue.map((line: string, idx: number) => (
118
+ <div key={idx} className="text-gray-700">
119
+ {line}
120
+ </div>
121
+ ))}
122
+ </div>
123
+ </div>
124
+ )}
125
+
126
+ {/* Loop Body */}
127
+ <div className="mb-3">
128
+ <div className="text-sm font-medium text-gray-600 mb-1">Loop Body:</div>
129
+ <div className="bg-white p-2 rounded border border-gray-200">
130
+ <div className="font-mono text-xs text-gray-500 mb-1">for (...) {'{'}</div>
131
+ <div className="pl-4 font-mono text-xs">
132
+ {loopBody.length > 0 ? (
133
+ loopBody.map((line: string, idx: number) => (
134
+ <div key={idx} className="text-gray-700">
135
+ {line}
136
+ </div>
137
+ ))
138
+ ) : (
139
+ <div className="text-gray-400 italic">No operations in loop body</div>
140
+ )}
141
+ </div>
142
+ <div className="font-mono text-xs text-gray-500 mt-1">{'}'}</div>
143
+ </div>
144
+ </div>
145
+
146
+ {/* Epilogue */}
147
+ {epilogue.length > 0 && (
148
+ <div>
149
+ <div className="text-sm font-medium text-gray-600 mb-1">Epilogue:</div>
150
+ <div className="bg-white p-2 rounded border border-gray-200 font-mono text-xs">
151
+ {epilogue.map((line: string, idx: number) => (
152
+ <div key={idx} className="text-gray-700">
153
+ {line}
154
+ </div>
155
+ ))}
156
+ </div>
157
+ </div>
158
+ )}
159
+ </div>
160
+ );
161
+ })}
162
+ </>
163
+ )}
93
164
  </div>
94
165
  </div>
95
166
  );
@@ -176,6 +176,7 @@ export interface CompilationMetadata {
176
176
  export interface IRAnalysisData {
177
177
  // Mapping from IR stage -> <IO type -> count>
178
178
  io_counts?: Record<string, Record<string, number>>;
179
+ loop_schedules?: [Record<string, [string]>];
179
180
  }
180
181
 
181
182
  /**
@@ -1,77 +0,0 @@
1
- # Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- import logging
4
-
5
- from .sourcemap_utils import load_ir_contents
6
-
7
-
8
- logger = logging.getLogger("IRAnalysis")
9
-
10
-
11
- def process_amd_bufferop(ir_content: str, io_keys: list[str]) -> dict[str, int]:
12
- def make_key(prefix: str) -> str:
13
- return f"{prefix}_count"
14
-
15
- io_keys = [(make_key(prefix), prefix) for prefix in io_keys]
16
- output: dict[str, int] = {}
17
- for dict_key, _ in io_keys:
18
- output[dict_key] = 0
19
- if ir_content:
20
- for line in ir_content.split("\n"):
21
- for dict_key, code_key in io_keys:
22
- if code_key in line:
23
- output[dict_key] += 1
24
- return output
25
-
26
-
27
- def process_amd_ttgir_bufferops(
28
- key: str,
29
- file_content: dict[str, str],
30
- file_path: dict[str, str],
31
- ) -> dict[str, int]:
32
- ir_content = load_ir_contents(key, file_content, file_path)
33
- # TODO: Add atomics
34
- io_keys = ["tt.load", "tt.store", "amdgpu.buffer_load", "amdgpu.buffer_store"]
35
- return process_amd_bufferop(ir_content, io_keys)
36
-
37
-
38
- def process_amd_gcn_bufferops(
39
- key: str,
40
- file_content: dict[str, str],
41
- file_path: dict[str, str],
42
- ) -> dict[str, int]:
43
- ir_content = load_ir_contents(key, file_content, file_path)
44
- # TODO: Add atomics
45
- io_keys = ["global_load", "global_store", "buffer_load", "buffer_store"]
46
- return process_amd_bufferop(ir_content, io_keys)
47
-
48
-
49
- def _generate_ir_analysis(entry: str):
50
- payload = entry.setdefault("payload", {})
51
- file_content = payload.get("file_content", {})
52
- file_path = payload.get("file_path", {})
53
-
54
- # Find the IR file keys
55
- ttgir_key = next((k for k in file_content if k.endswith(".ttgir")), None)
56
- amdgcn_key = next((k for k in file_content if k.endswith(".amdgcn")), None)
57
- # Skip if no IR files found
58
- if not (ttgir_key or amdgcn_key):
59
- logger.debug("No AMD IR found")
60
- return {}
61
- ir_analysis = {}
62
- if amdgcn_key:
63
- ttgir_bufferops_info = process_amd_ttgir_bufferops(
64
- ttgir_key, file_content, file_path
65
- )
66
- gcn_bufferops_info = process_amd_gcn_bufferops(
67
- amdgcn_key, file_content, file_path
68
- )
69
- io_counts = {}
70
- # NDJSON format requires a newline at the end of each line
71
- if ttgir_bufferops_info:
72
- io_counts["amd_ttgir_bufferops_count"] = ttgir_bufferops_info
73
- if gcn_bufferops_info:
74
- io_counts["amd_gcn_bufferops_count"] = gcn_bufferops_info
75
- if io_counts:
76
- ir_analysis["io_counts"] = io_counts
77
- return ir_analysis