machine-dialect 0.1.0a1__py3-none-any.whl

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.
Files changed (268) hide show
  1. machine_dialect/__main__.py +667 -0
  2. machine_dialect/agent/__init__.py +5 -0
  3. machine_dialect/agent/agent.py +360 -0
  4. machine_dialect/ast/__init__.py +95 -0
  5. machine_dialect/ast/ast_node.py +35 -0
  6. machine_dialect/ast/call_expression.py +82 -0
  7. machine_dialect/ast/dict_extraction.py +60 -0
  8. machine_dialect/ast/expressions.py +439 -0
  9. machine_dialect/ast/literals.py +309 -0
  10. machine_dialect/ast/program.py +35 -0
  11. machine_dialect/ast/statements.py +1433 -0
  12. machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
  13. machine_dialect/ast/tests/test_boolean_literal.py +29 -0
  14. machine_dialect/ast/tests/test_collection_hir.py +138 -0
  15. machine_dialect/ast/tests/test_define_statement.py +142 -0
  16. machine_dialect/ast/tests/test_desugar.py +541 -0
  17. machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
  18. machine_dialect/cfg/__init__.py +6 -0
  19. machine_dialect/cfg/config.py +156 -0
  20. machine_dialect/cfg/examples.py +221 -0
  21. machine_dialect/cfg/generate_with_ai.py +187 -0
  22. machine_dialect/cfg/openai_generation.py +200 -0
  23. machine_dialect/cfg/parser.py +94 -0
  24. machine_dialect/cfg/tests/__init__.py +1 -0
  25. machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
  26. machine_dialect/cfg/tests/test_config.py +188 -0
  27. machine_dialect/cfg/tests/test_examples.py +391 -0
  28. machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
  29. machine_dialect/cfg/tests/test_openai_generation.py +256 -0
  30. machine_dialect/codegen/__init__.py +5 -0
  31. machine_dialect/codegen/bytecode_module.py +89 -0
  32. machine_dialect/codegen/bytecode_serializer.py +300 -0
  33. machine_dialect/codegen/opcodes.py +101 -0
  34. machine_dialect/codegen/register_codegen.py +1996 -0
  35. machine_dialect/codegen/symtab.py +208 -0
  36. machine_dialect/codegen/tests/__init__.py +1 -0
  37. machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
  38. machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
  39. machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
  40. machine_dialect/codegen/tests/test_symtab.py +418 -0
  41. machine_dialect/codegen/vm_serializer.py +621 -0
  42. machine_dialect/compiler/__init__.py +18 -0
  43. machine_dialect/compiler/compiler.py +197 -0
  44. machine_dialect/compiler/config.py +149 -0
  45. machine_dialect/compiler/context.py +149 -0
  46. machine_dialect/compiler/phases/__init__.py +19 -0
  47. machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
  48. machine_dialect/compiler/phases/codegen.py +40 -0
  49. machine_dialect/compiler/phases/hir_generation.py +39 -0
  50. machine_dialect/compiler/phases/mir_generation.py +86 -0
  51. machine_dialect/compiler/phases/optimization.py +110 -0
  52. machine_dialect/compiler/phases/parsing.py +39 -0
  53. machine_dialect/compiler/pipeline.py +143 -0
  54. machine_dialect/compiler/tests/__init__.py +1 -0
  55. machine_dialect/compiler/tests/test_compiler.py +568 -0
  56. machine_dialect/compiler/vm_runner.py +173 -0
  57. machine_dialect/errors/__init__.py +32 -0
  58. machine_dialect/errors/exceptions.py +369 -0
  59. machine_dialect/errors/messages.py +82 -0
  60. machine_dialect/errors/tests/__init__.py +0 -0
  61. machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
  62. machine_dialect/errors/tests/test_name_errors.py +118 -0
  63. machine_dialect/helpers/__init__.py +0 -0
  64. machine_dialect/helpers/stopwords.py +225 -0
  65. machine_dialect/helpers/validators.py +30 -0
  66. machine_dialect/lexer/__init__.py +9 -0
  67. machine_dialect/lexer/constants.py +23 -0
  68. machine_dialect/lexer/lexer.py +907 -0
  69. machine_dialect/lexer/tests/__init__.py +0 -0
  70. machine_dialect/lexer/tests/helpers.py +86 -0
  71. machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
  72. machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
  73. machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
  74. machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
  75. machine_dialect/lexer/tests/test_comments.py +200 -0
  76. machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
  77. machine_dialect/lexer/tests/test_lexer_position.py +113 -0
  78. machine_dialect/lexer/tests/test_list_tokens.py +282 -0
  79. machine_dialect/lexer/tests/test_stopwords.py +80 -0
  80. machine_dialect/lexer/tests/test_strict_equality.py +129 -0
  81. machine_dialect/lexer/tests/test_token.py +41 -0
  82. machine_dialect/lexer/tests/test_tokenization.py +294 -0
  83. machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
  84. machine_dialect/lexer/tests/test_url_literals.py +169 -0
  85. machine_dialect/lexer/tokens.py +487 -0
  86. machine_dialect/linter/__init__.py +10 -0
  87. machine_dialect/linter/__main__.py +144 -0
  88. machine_dialect/linter/linter.py +154 -0
  89. machine_dialect/linter/rules/__init__.py +8 -0
  90. machine_dialect/linter/rules/base.py +112 -0
  91. machine_dialect/linter/rules/statement_termination.py +99 -0
  92. machine_dialect/linter/tests/__init__.py +1 -0
  93. machine_dialect/linter/tests/mdrules/__init__.py +0 -0
  94. machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
  95. machine_dialect/linter/tests/test_linter.py +81 -0
  96. machine_dialect/linter/tests/test_rules.py +110 -0
  97. machine_dialect/linter/tests/test_violations.py +71 -0
  98. machine_dialect/linter/violations.py +51 -0
  99. machine_dialect/mir/__init__.py +69 -0
  100. machine_dialect/mir/analyses/__init__.py +20 -0
  101. machine_dialect/mir/analyses/alias_analysis.py +315 -0
  102. machine_dialect/mir/analyses/dominance_analysis.py +49 -0
  103. machine_dialect/mir/analyses/escape_analysis.py +286 -0
  104. machine_dialect/mir/analyses/loop_analysis.py +272 -0
  105. machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
  106. machine_dialect/mir/analyses/type_analysis.py +448 -0
  107. machine_dialect/mir/analyses/use_def_chains.py +232 -0
  108. machine_dialect/mir/basic_block.py +385 -0
  109. machine_dialect/mir/dataflow.py +445 -0
  110. machine_dialect/mir/debug_info.py +208 -0
  111. machine_dialect/mir/hir_to_mir.py +1738 -0
  112. machine_dialect/mir/mir_dumper.py +366 -0
  113. machine_dialect/mir/mir_function.py +167 -0
  114. machine_dialect/mir/mir_instructions.py +1877 -0
  115. machine_dialect/mir/mir_interpreter.py +556 -0
  116. machine_dialect/mir/mir_module.py +225 -0
  117. machine_dialect/mir/mir_printer.py +480 -0
  118. machine_dialect/mir/mir_transformer.py +410 -0
  119. machine_dialect/mir/mir_types.py +367 -0
  120. machine_dialect/mir/mir_validation.py +455 -0
  121. machine_dialect/mir/mir_values.py +268 -0
  122. machine_dialect/mir/optimization_config.py +233 -0
  123. machine_dialect/mir/optimization_pass.py +251 -0
  124. machine_dialect/mir/optimization_pipeline.py +355 -0
  125. machine_dialect/mir/optimizations/__init__.py +84 -0
  126. machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
  127. machine_dialect/mir/optimizations/branch_prediction.py +372 -0
  128. machine_dialect/mir/optimizations/constant_propagation.py +634 -0
  129. machine_dialect/mir/optimizations/cse.py +398 -0
  130. machine_dialect/mir/optimizations/dce.py +288 -0
  131. machine_dialect/mir/optimizations/inlining.py +551 -0
  132. machine_dialect/mir/optimizations/jump_threading.py +487 -0
  133. machine_dialect/mir/optimizations/licm.py +405 -0
  134. machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
  135. machine_dialect/mir/optimizations/strength_reduction.py +422 -0
  136. machine_dialect/mir/optimizations/tail_call.py +207 -0
  137. machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
  138. machine_dialect/mir/optimizations/type_narrowing.py +397 -0
  139. machine_dialect/mir/optimizations/type_specialization.py +447 -0
  140. machine_dialect/mir/optimizations/type_specific.py +906 -0
  141. machine_dialect/mir/optimize_mir.py +89 -0
  142. machine_dialect/mir/pass_manager.py +391 -0
  143. machine_dialect/mir/profiling/__init__.py +26 -0
  144. machine_dialect/mir/profiling/profile_collector.py +318 -0
  145. machine_dialect/mir/profiling/profile_data.py +372 -0
  146. machine_dialect/mir/profiling/profile_reader.py +272 -0
  147. machine_dialect/mir/profiling/profile_writer.py +226 -0
  148. machine_dialect/mir/register_allocation.py +302 -0
  149. machine_dialect/mir/reporting/__init__.py +17 -0
  150. machine_dialect/mir/reporting/optimization_reporter.py +314 -0
  151. machine_dialect/mir/reporting/report_formatter.py +289 -0
  152. machine_dialect/mir/ssa_construction.py +342 -0
  153. machine_dialect/mir/tests/__init__.py +1 -0
  154. machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
  155. machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
  156. machine_dialect/mir/tests/test_algebraic_division.py +126 -0
  157. machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
  158. machine_dialect/mir/tests/test_basic_block.py +425 -0
  159. machine_dialect/mir/tests/test_branch_prediction.py +459 -0
  160. machine_dialect/mir/tests/test_call_lowering.py +168 -0
  161. machine_dialect/mir/tests/test_collection_lowering.py +604 -0
  162. machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
  163. machine_dialect/mir/tests/test_custom_passes.py +166 -0
  164. machine_dialect/mir/tests/test_debug_info.py +285 -0
  165. machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
  166. machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
  167. machine_dialect/mir/tests/test_double_negation.py +231 -0
  168. machine_dialect/mir/tests/test_escape_analysis.py +233 -0
  169. machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
  170. machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
  171. machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
  172. machine_dialect/mir/tests/test_inlining.py +435 -0
  173. machine_dialect/mir/tests/test_licm.py +472 -0
  174. machine_dialect/mir/tests/test_mir_dumper.py +313 -0
  175. machine_dialect/mir/tests/test_mir_instructions.py +445 -0
  176. machine_dialect/mir/tests/test_mir_module.py +860 -0
  177. machine_dialect/mir/tests/test_mir_printer.py +387 -0
  178. machine_dialect/mir/tests/test_mir_types.py +123 -0
  179. machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
  180. machine_dialect/mir/tests/test_mir_validation.py +378 -0
  181. machine_dialect/mir/tests/test_mir_values.py +168 -0
  182. machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
  183. machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
  184. machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
  185. machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
  186. machine_dialect/mir/tests/test_pass_manager.py +294 -0
  187. machine_dialect/mir/tests/test_pass_registration.py +64 -0
  188. machine_dialect/mir/tests/test_profiling.py +356 -0
  189. machine_dialect/mir/tests/test_register_allocation.py +307 -0
  190. machine_dialect/mir/tests/test_report_formatters.py +372 -0
  191. machine_dialect/mir/tests/test_ssa_construction.py +433 -0
  192. machine_dialect/mir/tests/test_tail_call.py +236 -0
  193. machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
  194. machine_dialect/mir/tests/test_type_narrowing.py +277 -0
  195. machine_dialect/mir/tests/test_type_specialization.py +421 -0
  196. machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
  197. machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
  198. machine_dialect/mir/type_inference.py +368 -0
  199. machine_dialect/parser/__init__.py +12 -0
  200. machine_dialect/parser/enums.py +45 -0
  201. machine_dialect/parser/parser.py +3655 -0
  202. machine_dialect/parser/protocols.py +11 -0
  203. machine_dialect/parser/symbol_table.py +169 -0
  204. machine_dialect/parser/tests/__init__.py +0 -0
  205. machine_dialect/parser/tests/helper_functions.py +193 -0
  206. machine_dialect/parser/tests/test_action_statements.py +334 -0
  207. machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
  208. machine_dialect/parser/tests/test_call_statements.py +154 -0
  209. machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
  210. machine_dialect/parser/tests/test_collection_mutations.py +264 -0
  211. machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
  212. machine_dialect/parser/tests/test_define_integration.py +468 -0
  213. machine_dialect/parser/tests/test_define_statements.py +311 -0
  214. machine_dialect/parser/tests/test_dict_extraction.py +115 -0
  215. machine_dialect/parser/tests/test_empty_literal.py +155 -0
  216. machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
  217. machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
  218. machine_dialect/parser/tests/test_if_empty_block.py +61 -0
  219. machine_dialect/parser/tests/test_if_statements.py +299 -0
  220. machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
  221. machine_dialect/parser/tests/test_infix_expressions.py +680 -0
  222. machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
  223. machine_dialect/parser/tests/test_interaction_statements.py +269 -0
  224. machine_dialect/parser/tests/test_list_literals.py +277 -0
  225. machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
  226. machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
  227. machine_dialect/parser/tests/test_parse_errors.py +114 -0
  228. machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
  229. machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
  230. machine_dialect/parser/tests/test_program.py +13 -0
  231. machine_dialect/parser/tests/test_return_statements.py +89 -0
  232. machine_dialect/parser/tests/test_set_statements.py +152 -0
  233. machine_dialect/parser/tests/test_strict_equality.py +258 -0
  234. machine_dialect/parser/tests/test_symbol_table.py +217 -0
  235. machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
  236. machine_dialect/parser/tests/test_utility_statements.py +423 -0
  237. machine_dialect/parser/token_buffer.py +159 -0
  238. machine_dialect/repl/__init__.py +3 -0
  239. machine_dialect/repl/repl.py +426 -0
  240. machine_dialect/repl/tests/__init__.py +0 -0
  241. machine_dialect/repl/tests/test_repl.py +606 -0
  242. machine_dialect/semantic/__init__.py +12 -0
  243. machine_dialect/semantic/analyzer.py +906 -0
  244. machine_dialect/semantic/error_messages.py +189 -0
  245. machine_dialect/semantic/tests/__init__.py +1 -0
  246. machine_dialect/semantic/tests/test_analyzer.py +364 -0
  247. machine_dialect/semantic/tests/test_error_messages.py +104 -0
  248. machine_dialect/tests/edge_cases/__init__.py +10 -0
  249. machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
  250. machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
  251. machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
  252. machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
  253. machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
  254. machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
  255. machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
  256. machine_dialect/tests/integration/test_list_compilation.py +395 -0
  257. machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
  258. machine_dialect/type_checking/__init__.py +21 -0
  259. machine_dialect/type_checking/tests/__init__.py +1 -0
  260. machine_dialect/type_checking/tests/test_type_system.py +230 -0
  261. machine_dialect/type_checking/type_system.py +270 -0
  262. machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
  263. machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
  264. machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
  265. machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
  266. machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
  267. machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
  268. machine_dialect_vm/__init__.pyi +15 -0
@@ -0,0 +1,272 @@
1
+ """Loop analysis for MIR.
2
+
3
+ This module provides loop detection and characterization for
4
+ optimization passes like LICM and loop unrolling.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+
9
+ from machine_dialect.mir.basic_block import BasicBlock
10
+ from machine_dialect.mir.mir_function import MIRFunction
11
+ from machine_dialect.mir.optimization_pass import (
12
+ FunctionAnalysisPass,
13
+ PassInfo,
14
+ PassType,
15
+ PreservationLevel,
16
+ )
17
+ from machine_dialect.mir.ssa_construction import DominanceInfo
18
+
19
+
20
+ @dataclass
21
+ class Loop:
22
+ """Represents a natural loop in the CFG.
23
+
24
+ Attributes:
25
+ header: Loop header block.
26
+ back_edge: Back edge (latch -> header).
27
+ blocks: Set of blocks in the loop.
28
+ exits: Set of exit blocks from the loop.
29
+ depth: Nesting depth (0 for outermost).
30
+ parent: Parent loop if nested.
31
+ children: Child loops if any.
32
+ """
33
+
34
+ header: BasicBlock
35
+ back_edge: tuple[BasicBlock, BasicBlock] # (from, to)
36
+ blocks: set[BasicBlock]
37
+ exits: set[BasicBlock]
38
+ depth: int
39
+ parent: "Loop | None" = None
40
+ children: list["Loop"] = field(default_factory=list)
41
+
42
+ def contains(self, block: BasicBlock) -> bool:
43
+ """Check if a block is in this loop.
44
+
45
+ Args:
46
+ block: Block to check.
47
+
48
+ Returns:
49
+ True if block is in the loop.
50
+ """
51
+ return block in self.blocks
52
+
53
+ def is_inner_loop(self) -> bool:
54
+ """Check if this is an inner loop (no children).
55
+
56
+ Returns:
57
+ True if this is an inner loop.
58
+ """
59
+ return len(self.children) == 0
60
+
61
+
62
+ class LoopInfo:
63
+ """Container for loop information in a function."""
64
+
65
+ def __init__(self) -> None:
66
+ """Initialize loop info."""
67
+ self.loops: list[Loop] = []
68
+ self.loop_map: dict[BasicBlock, Loop] = {} # Header -> Loop
69
+ self.block_to_loop: dict[BasicBlock, Loop] = {} # Block -> Innermost loop
70
+
71
+ def get_loop(self, header: BasicBlock) -> Loop | None:
72
+ """Get loop by header block.
73
+
74
+ Args:
75
+ header: Loop header block.
76
+
77
+ Returns:
78
+ The loop or None.
79
+ """
80
+ return self.loop_map.get(header)
81
+
82
+ def get_innermost_loop(self, block: BasicBlock) -> Loop | None:
83
+ """Get the innermost loop containing a block.
84
+
85
+ Args:
86
+ block: The block.
87
+
88
+ Returns:
89
+ The innermost loop or None.
90
+ """
91
+ return self.block_to_loop.get(block)
92
+
93
+ def get_loops_at_depth(self, depth: int) -> list[Loop]:
94
+ """Get all loops at a specific depth.
95
+
96
+ Args:
97
+ depth: Nesting depth.
98
+
99
+ Returns:
100
+ List of loops at that depth.
101
+ """
102
+ return [loop for loop in self.loops if loop.depth == depth]
103
+
104
+ def get_inner_loops(self) -> list[Loop]:
105
+ """Get all inner loops.
106
+
107
+ Returns:
108
+ List of inner loops.
109
+ """
110
+ return [loop for loop in self.loops if loop.is_inner_loop()]
111
+
112
+
113
+ class LoopAnalysis(FunctionAnalysisPass):
114
+ """Analysis pass that identifies loops in the CFG."""
115
+
116
+ def get_info(self) -> PassInfo:
117
+ """Get pass information.
118
+
119
+ Returns:
120
+ Pass information.
121
+ """
122
+ return PassInfo(
123
+ name="loop-analysis",
124
+ description="Identify and characterize loops",
125
+ pass_type=PassType.ANALYSIS,
126
+ requires=["dominance"],
127
+ preserves=PreservationLevel.ALL,
128
+ )
129
+
130
+ def run_on_function(self, function: MIRFunction) -> LoopInfo:
131
+ """Analyze loops in a function.
132
+
133
+ Args:
134
+ function: The function to analyze.
135
+
136
+ Returns:
137
+ Loop information.
138
+ """
139
+ loop_info = LoopInfo()
140
+
141
+ # Get dominance information
142
+ dominance = DominanceInfo(function.cfg)
143
+
144
+ # Find back edges (edges where target dominates source)
145
+ back_edges = self._find_back_edges(function, dominance)
146
+
147
+ # Build natural loops from back edges
148
+ for source, target in back_edges:
149
+ loop = self._build_loop(source, target, dominance)
150
+ loop_info.loops.append(loop)
151
+ loop_info.loop_map[target] = loop
152
+
153
+ # Determine loop nesting
154
+ self._determine_nesting(loop_info)
155
+
156
+ # Build block-to-loop mapping
157
+ for loop in loop_info.loops:
158
+ for block in loop.blocks:
159
+ # Map to innermost loop
160
+ if block not in loop_info.block_to_loop:
161
+ loop_info.block_to_loop[block] = loop
162
+ else:
163
+ # Keep the innermost (highest depth)
164
+ if loop.depth > loop_info.block_to_loop[block].depth:
165
+ loop_info.block_to_loop[block] = loop
166
+
167
+ return loop_info
168
+
169
+ def _find_back_edges(
170
+ self,
171
+ function: MIRFunction,
172
+ dominance: DominanceInfo,
173
+ ) -> list[tuple[BasicBlock, BasicBlock]]:
174
+ """Find back edges in the CFG.
175
+
176
+ Args:
177
+ function: The function.
178
+ dominance: Dominance information.
179
+
180
+ Returns:
181
+ List of back edges (source, target).
182
+ """
183
+ back_edges = []
184
+
185
+ for block in function.cfg.blocks.values():
186
+ for succ in block.successors:
187
+ # Check if successor dominates this block
188
+ if dominance.dominates(succ, block):
189
+ back_edges.append((block, succ))
190
+
191
+ return back_edges
192
+
193
+ def _build_loop(
194
+ self,
195
+ latch: BasicBlock,
196
+ header: BasicBlock,
197
+ dominance: DominanceInfo,
198
+ ) -> Loop:
199
+ """Build a natural loop from a back edge.
200
+
201
+ Args:
202
+ latch: Latch block (source of back edge).
203
+ header: Header block (target of back edge).
204
+ dominance: Dominance information.
205
+
206
+ Returns:
207
+ The constructed loop.
208
+ """
209
+ # Natural loop consists of header and all blocks that can
210
+ # reach latch without going through header
211
+ loop_blocks = {header, latch}
212
+ worklist = [latch]
213
+
214
+ while worklist:
215
+ block = worklist.pop()
216
+ for pred in block.predecessors:
217
+ if pred not in loop_blocks:
218
+ loop_blocks.add(pred)
219
+ worklist.append(pred)
220
+
221
+ # Find exit blocks
222
+ exits = set()
223
+ for block in loop_blocks:
224
+ for succ in block.successors:
225
+ if succ not in loop_blocks:
226
+ exits.add(succ)
227
+
228
+ return Loop(
229
+ header=header,
230
+ back_edge=(latch, header),
231
+ blocks=loop_blocks,
232
+ exits=exits,
233
+ depth=0, # Will be set later
234
+ )
235
+
236
+ def _determine_nesting(self, loop_info: LoopInfo) -> None:
237
+ """Determine loop nesting relationships.
238
+
239
+ Args:
240
+ loop_info: Loop information to update.
241
+ """
242
+ # Sort loops by size (smaller loops are likely inner)
243
+ loops = sorted(loop_info.loops, key=lambda loop: len(loop.blocks))
244
+
245
+ # Determine parent-child relationships
246
+ for i, inner in enumerate(loops):
247
+ for outer in loops[i + 1 :]:
248
+ # Check if inner is contained in outer
249
+ if inner.header in outer.blocks and inner.blocks.issubset(outer.blocks):
250
+ if inner.parent is None:
251
+ inner.parent = outer
252
+ outer.children.append(inner)
253
+
254
+ # Set depths
255
+ for loop in loop_info.loops:
256
+ if loop.parent is None:
257
+ self._set_depth(loop, 0)
258
+
259
+ def _set_depth(self, loop: Loop, depth: int) -> None:
260
+ """Set loop depth recursively.
261
+
262
+ Args:
263
+ loop: The loop.
264
+ depth: Depth to set.
265
+ """
266
+ loop.depth = depth
267
+ for child in loop.children:
268
+ self._set_depth(child, depth + 1)
269
+
270
+ def finalize(self) -> None:
271
+ """Finalize the pass."""
272
+ pass