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,366 @@
1
+ """Loop unrolling optimization pass.
2
+
3
+ This module implements loop unrolling to reduce loop overhead and enable
4
+ further optimizations by duplicating the loop body multiple times.
5
+ """
6
+
7
+ from copy import deepcopy
8
+
9
+ from machine_dialect.mir.analyses.loop_analysis import Loop
10
+ from machine_dialect.mir.basic_block import BasicBlock
11
+ from machine_dialect.mir.mir_function import MIRFunction
12
+ from machine_dialect.mir.mir_instructions import (
13
+ BinaryOp,
14
+ ConditionalJump,
15
+ Jump,
16
+ MIRInstruction,
17
+ )
18
+ from machine_dialect.mir.mir_transformer import MIRTransformer
19
+ from machine_dialect.mir.mir_values import Constant, MIRValue
20
+ from machine_dialect.mir.optimization_pass import (
21
+ OptimizationPass,
22
+ PassInfo,
23
+ PassType,
24
+ PreservationLevel,
25
+ )
26
+
27
+
28
+ class LoopUnrolling(OptimizationPass):
29
+ """Unroll small loops to reduce overhead.
30
+
31
+ Attributes:
32
+ unroll_threshold: Maximum unroll factor for loops.
33
+ max_body_size: Maximum number of instructions in loop body.
34
+ stats: Dictionary tracking optimization statistics.
35
+ """
36
+
37
+ def __init__(self) -> None:
38
+ """Initialize loop unrolling pass.
39
+
40
+ Sets default unroll threshold to 4 and maximum body size to 20
41
+ instructions. Initializes statistics tracking.
42
+ """
43
+ super().__init__()
44
+ self.unroll_threshold = 4 # Default unroll factor
45
+ self.max_body_size = 20 # Maximum instructions in loop body
46
+ self.stats = {"unrolled": 0, "loops_processed": 0}
47
+
48
+ def initialize(self) -> None:
49
+ """Initialize the pass before running."""
50
+ self.stats = {"unrolled": 0, "loops_processed": 0}
51
+
52
+ def get_info(self) -> PassInfo:
53
+ """Get pass information.
54
+
55
+ Returns:
56
+ PassInfo object describing this optimization pass.
57
+ """
58
+ return PassInfo(
59
+ name="loop-unrolling",
60
+ description="Unroll small loops to reduce overhead",
61
+ pass_type=PassType.OPTIMIZATION,
62
+ requires=["loop-analysis", "dominance"],
63
+ preserves=PreservationLevel.CFG,
64
+ )
65
+
66
+ def run_on_function(self, function: MIRFunction) -> bool:
67
+ """Run loop unrolling on a function.
68
+
69
+ Args:
70
+ function: The function to optimize.
71
+
72
+ Returns:
73
+ True if the function was modified.
74
+ """
75
+ # Get required analyses
76
+ loop_info = self.get_analysis("loop-analysis", function)
77
+ dominance = self.get_analysis("dominance", function)
78
+
79
+ if not loop_info or not dominance:
80
+ return False
81
+
82
+ # Get unroll threshold from config if available
83
+ if hasattr(self, "config") and self.config:
84
+ self.unroll_threshold = self.config.unroll_threshold
85
+
86
+ transformer = MIRTransformer(function)
87
+ modified = False
88
+
89
+ # Process loops from innermost to outermost
90
+ loops = self._get_loops_in_order(loop_info.loops if hasattr(loop_info, "loops") else [])
91
+
92
+ for loop in loops:
93
+ if self._should_unroll(loop, function):
94
+ if self._unroll_loop(loop, function, transformer):
95
+ modified = True
96
+ self.stats["unrolled"] += 1
97
+ self.stats["loops_processed"] += 1
98
+
99
+ return modified
100
+
101
+ def _get_loops_in_order(self, loops: list[Loop]) -> list[Loop]:
102
+ """Get loops ordered from innermost to outermost.
103
+
104
+ Args:
105
+ loops: List of loops to order.
106
+
107
+ Returns:
108
+ Loops sorted by depth in descending order.
109
+ """
110
+ return sorted(loops, key=lambda loop: loop.depth, reverse=True)
111
+
112
+ def _should_unroll(self, loop: Loop, function: MIRFunction) -> bool:
113
+ """Determine if a loop should be unrolled.
114
+
115
+ Args:
116
+ loop: The loop to check.
117
+ function: The containing function.
118
+
119
+ Returns:
120
+ True if the loop should be unrolled.
121
+ """
122
+ # Check loop size
123
+ total_instructions = sum(len(block.instructions) for block in loop.blocks)
124
+ if total_instructions > self.max_body_size:
125
+ return False
126
+
127
+ # Try to determine iteration count
128
+ iteration_count = self._get_iteration_count(loop, function)
129
+ if iteration_count is None:
130
+ return False
131
+
132
+ # Only unroll if iteration count is reasonable
133
+ return 2 <= iteration_count <= self.unroll_threshold * 2
134
+
135
+ def _get_iteration_count(self, loop: Loop, function: MIRFunction) -> int | None:
136
+ """Try to determine the iteration count of a loop.
137
+
138
+ Args:
139
+ loop: The loop to analyze.
140
+ function: The containing function.
141
+
142
+ Returns:
143
+ Number of iterations if determinable, None otherwise.
144
+
145
+ Note:
146
+ Currently implements a simple heuristic that looks for
147
+ patterns like 'i = 0; while i < N; i++' with constant bounds.
148
+ """
149
+ # Simple heuristic: look for loops with constant bounds
150
+ # This is a simplified version - real implementation would be more sophisticated
151
+
152
+ # Look for pattern: i = 0; while i < N; i++
153
+ header = loop.header
154
+
155
+ # Find the loop condition
156
+ for inst in header.instructions:
157
+ if isinstance(inst, ConditionalJump):
158
+ # Check if we're comparing against a constant
159
+ cond_inst = self._find_defining_instruction(inst.condition, header)
160
+ if isinstance(cond_inst, BinaryOp) and cond_inst.op in ["<", "<=", ">", ">="]:
161
+ # Check if one operand is a constant
162
+ if isinstance(cond_inst.right, Constant) and isinstance(cond_inst.right.value, int):
163
+ # Simple case: comparing against constant
164
+ limit = cond_inst.right.value
165
+ # Assume starting from 0 for now
166
+ if cond_inst.op == "<":
167
+ return limit
168
+ elif cond_inst.op == "<=":
169
+ return limit + 1
170
+
171
+ return None
172
+
173
+ def _find_defining_instruction(self, value: MIRValue, block: BasicBlock) -> MIRInstruction | None:
174
+ """Find the instruction that defines a value.
175
+
176
+ Args:
177
+ value: The value whose definition to find.
178
+ block: The basic block to search in.
179
+
180
+ Returns:
181
+ The instruction that defines the value, or None if not found.
182
+ """
183
+ for inst in block.instructions:
184
+ if value in inst.get_defs():
185
+ return inst
186
+ return None
187
+
188
+ def _unroll_loop(self, loop: Loop, function: MIRFunction, transformer: MIRTransformer) -> bool:
189
+ """Unroll a loop by the unroll factor.
190
+
191
+ Args:
192
+ loop: The loop to unroll.
193
+ function: The containing function.
194
+ transformer: MIR transformer for applying changes.
195
+
196
+ Returns:
197
+ True if the loop was successfully unrolled.
198
+
199
+ Note:
200
+ Implements partial unrolling by duplicating the loop body
201
+ up to the unroll factor, limited to 4 iterations.
202
+ """
203
+ unroll_factor = min(self.unroll_threshold, 4) # Limit unrolling
204
+
205
+ # For simplicity, we'll implement partial unrolling
206
+ # Duplicate the loop body N times
207
+
208
+ # Find the loop body (excluding header)
209
+ loop_body_blocks = [b for b in loop.blocks if b != loop.header]
210
+
211
+ if not loop_body_blocks:
212
+ return False
213
+
214
+ # Create copies of the loop body
215
+ new_blocks = []
216
+ for i in range(1, unroll_factor):
217
+ # Clone each body block
218
+ for block in loop_body_blocks:
219
+ new_block = self._clone_block(block, f"_unroll_{i}")
220
+ new_blocks.append(new_block)
221
+ function.cfg.add_block(new_block)
222
+
223
+ # Connect the cloned blocks
224
+ if new_blocks:
225
+ # Update control flow
226
+ self._connect_unrolled_blocks(loop, new_blocks, loop_body_blocks, unroll_factor)
227
+
228
+ # Update loop increment
229
+ self._update_loop_increment(loop, unroll_factor)
230
+
231
+ transformer.modified = True
232
+ return True
233
+
234
+ return False
235
+
236
+ def _clone_block(self, block: BasicBlock, suffix: str) -> BasicBlock:
237
+ """Clone a basic block with a new label.
238
+
239
+ Args:
240
+ block: Basic block to clone.
241
+ suffix: Suffix to append to the new block's label.
242
+
243
+ Returns:
244
+ New BasicBlock instance with cloned instructions.
245
+ """
246
+ new_label = block.label + suffix
247
+ new_block = BasicBlock(new_label)
248
+
249
+ # Clone instructions
250
+ for inst in block.instructions:
251
+ new_inst = self._clone_instruction(inst, suffix)
252
+ new_block.instructions.append(new_inst)
253
+
254
+ return new_block
255
+
256
+ def _clone_instruction(self, inst: MIRInstruction, suffix: str) -> MIRInstruction:
257
+ """Clone an instruction, updating labels.
258
+
259
+ Args:
260
+ inst: Instruction to clone.
261
+ suffix: Suffix for updating labels.
262
+
263
+ Returns:
264
+ Deep copy of the instruction.
265
+
266
+ Note:
267
+ Currently preserves original jump targets. A more complete
268
+ implementation would update these based on the suffix.
269
+ """
270
+ # Deep copy the instruction
271
+ new_inst = deepcopy(inst)
272
+
273
+ # Update jump targets
274
+ if isinstance(new_inst, Jump):
275
+ # Keep original target for now
276
+ pass
277
+ elif isinstance(new_inst, ConditionalJump):
278
+ # Keep original targets for now
279
+ pass
280
+
281
+ return new_inst
282
+
283
+ def _connect_unrolled_blocks(
284
+ self,
285
+ loop: Loop,
286
+ new_blocks: list[BasicBlock],
287
+ original_body: list[BasicBlock],
288
+ unroll_factor: int,
289
+ ) -> None:
290
+ """Connect the unrolled blocks properly.
291
+
292
+ Args:
293
+ loop: The original loop.
294
+ new_blocks: New unrolled blocks to connect.
295
+ original_body: Original loop body blocks.
296
+ unroll_factor: Number of times the loop was unrolled.
297
+
298
+ Note:
299
+ This is a simplified implementation that chains blocks
300
+ sequentially. A complete implementation would handle
301
+ complex control flow graphs.
302
+ """
303
+ # This is simplified - real implementation would handle complex CFGs
304
+ # For now, we just chain the blocks sequentially
305
+
306
+ # blocks_per_iteration = len(original_body) # Not used currently
307
+
308
+ # Connect original body to first unrolled copy
309
+ if original_body and new_blocks:
310
+ last_original = original_body[-1]
311
+ first_new = new_blocks[0]
312
+
313
+ # Update jump at end of original body
314
+ if last_original.instructions:
315
+ last_inst = last_original.instructions[-1]
316
+ if isinstance(last_inst, Jump):
317
+ # Instead of jumping back to header, jump to unrolled copy
318
+ last_inst.label = first_new.label
319
+
320
+ # Connect unrolled copies to each other
321
+ for i in range(len(new_blocks) - 1):
322
+ current_block = new_blocks[i]
323
+ next_block = new_blocks[i + 1]
324
+
325
+ if current_block.instructions:
326
+ last_inst = current_block.instructions[-1]
327
+ if isinstance(last_inst, Jump):
328
+ last_inst.label = next_block.label
329
+
330
+ def _update_loop_increment(self, loop: Loop, unroll_factor: int) -> None:
331
+ """Update the loop increment to account for unrolling.
332
+
333
+ Args:
334
+ loop: The loop whose increment to update.
335
+ unroll_factor: Number of times the loop was unrolled.
336
+
337
+ Note:
338
+ Searches for increment patterns like 'i = i + 1' and
339
+ updates them to increment by the unroll factor.
340
+ """
341
+ # Find and update the loop counter increment
342
+ # This is simplified - real implementation would be more sophisticated
343
+
344
+ for block in loop.blocks:
345
+ for inst in block.instructions:
346
+ # Look for increment pattern: i = i + 1
347
+ if isinstance(inst, BinaryOp) and inst.op == "+":
348
+ if isinstance(inst.right, Constant) and inst.right.value == 1:
349
+ # Update to increment by unroll_factor
350
+ new_const = Constant(unroll_factor)
351
+ inst.right = new_const
352
+
353
+ def finalize(self) -> None:
354
+ """Finalize the pass.
355
+
356
+ Currently performs no finalization actions.
357
+ """
358
+ pass
359
+
360
+ def get_statistics(self) -> dict[str, int]:
361
+ """Get optimization statistics.
362
+
363
+ Returns:
364
+ Dictionary of statistics.
365
+ """
366
+ return self.stats