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,554 @@
1
+ """Comprehensive tests for optimization_pipeline module.
2
+
3
+ Tests all aspects of the optimization pipeline including optimization levels,
4
+ pipeline configuration, and custom pipeline building.
5
+ """
6
+
7
+ from typing import Any
8
+ from unittest.mock import MagicMock, patch
9
+
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
+ LoadConst,
15
+ Return,
16
+ )
17
+ from machine_dialect.mir.mir_module import MIRModule
18
+ from machine_dialect.mir.mir_types import MIRType
19
+ from machine_dialect.mir.mir_values import Constant, Temp
20
+ from machine_dialect.mir.optimization_pass import ModulePass, PassInfo, PassType, PreservationLevel
21
+ from machine_dialect.mir.optimization_pipeline import (
22
+ OptimizationLevel,
23
+ OptimizationPipeline,
24
+ PipelineBuilder,
25
+ create_o0_pipeline,
26
+ create_o1_pipeline,
27
+ create_o2_pipeline,
28
+ create_o3_pipeline,
29
+ create_size_pipeline,
30
+ )
31
+ from machine_dialect.mir.optimizations.inlining import FunctionInlining
32
+
33
+
34
+ class MockPass(ModulePass):
35
+ """Mock pass for testing."""
36
+
37
+ def __init__(self, name: str = "mock-pass", modified: bool = True) -> None:
38
+ """Initialize mock pass.
39
+
40
+ Args:
41
+ name: Name of the pass.
42
+ modified: Whether the pass modifies the module.
43
+ """
44
+ super().__init__()
45
+ self._name = name
46
+ self._modified = modified
47
+ self.run_count = 0
48
+ self.stats = {"test_stat": 42}
49
+
50
+ def get_info(self) -> PassInfo:
51
+ """Get pass information.
52
+
53
+ Returns:
54
+ Pass information.
55
+ """
56
+ return PassInfo(
57
+ name=self._name,
58
+ description="Mock pass for testing",
59
+ pass_type=PassType.OPTIMIZATION,
60
+ requires=[],
61
+ preserves=PreservationLevel.ALL,
62
+ )
63
+
64
+ def run_on_module(self, module: MIRModule) -> bool:
65
+ """Run the pass on a module.
66
+
67
+ Args:
68
+ module: The module to run on.
69
+
70
+ Returns:
71
+ Whether the module was modified.
72
+ """
73
+ self.run_count += 1
74
+ return self._modified
75
+
76
+ def finalize(self) -> None:
77
+ """Finalize the pass."""
78
+ pass
79
+
80
+ def get_statistics(self) -> dict[str, Any]:
81
+ """Get pass statistics.
82
+
83
+ Returns:
84
+ Dictionary of statistics.
85
+ """
86
+ return self.stats
87
+
88
+
89
+ def create_test_module() -> MIRModule:
90
+ """Create a test module with optimization opportunities.
91
+
92
+ Returns:
93
+ A test MIR module.
94
+ """
95
+ module = MIRModule("test")
96
+
97
+ # Create main function with constant folding opportunities
98
+ main_func = MIRFunction("main")
99
+
100
+ # Create basic blocks
101
+ entry = BasicBlock("entry")
102
+ main_func.cfg.add_block(entry)
103
+ main_func.cfg.set_entry_block(entry)
104
+
105
+ # Add instructions with optimization opportunities
106
+ t0 = Temp(MIRType.INT, 0)
107
+ t1 = Temp(MIRType.INT, 1)
108
+ t2 = Temp(MIRType.INT, 2)
109
+ t3 = Temp(MIRType.INT, 3)
110
+ t4 = Temp(MIRType.INT, 4)
111
+
112
+ # Constant folding opportunity: 2 + 3 = 5
113
+ entry.add_instruction(LoadConst(t0, Constant(2, MIRType.INT), (1, 1)))
114
+ entry.add_instruction(LoadConst(t1, Constant(3, MIRType.INT), (1, 1)))
115
+ entry.add_instruction(BinaryOp(t2, "+", t0, t1, (1, 1)))
116
+
117
+ # Strength reduction opportunity: x * 4 -> x << 2
118
+ entry.add_instruction(LoadConst(t3, Constant(4, MIRType.INT), (1, 1)))
119
+ entry.add_instruction(BinaryOp(t4, "*", t2, t3, (1, 1)))
120
+
121
+ # Return result
122
+ entry.add_instruction(Return((1, 1), t4))
123
+
124
+ module.add_function(main_func)
125
+ return module
126
+
127
+
128
+ class TestOptimizationLevel:
129
+ """Tests for OptimizationLevel enum."""
130
+
131
+ def test_optimization_levels(self) -> None:
132
+ """Test all optimization level values."""
133
+ assert OptimizationLevel.O0.value == "O0"
134
+ assert OptimizationLevel.O1.value == "O1"
135
+ assert OptimizationLevel.O2.value == "O2"
136
+ assert OptimizationLevel.O3.value == "O3"
137
+ assert OptimizationLevel.Os.value == "Os"
138
+
139
+ def test_optimization_level_members(self) -> None:
140
+ """Test that all expected optimization levels exist."""
141
+ levels = list(OptimizationLevel)
142
+ assert len(levels) == 5
143
+ assert OptimizationLevel.O0 in levels
144
+ assert OptimizationLevel.O1 in levels
145
+ assert OptimizationLevel.O2 in levels
146
+ assert OptimizationLevel.O3 in levels
147
+ assert OptimizationLevel.Os in levels
148
+
149
+
150
+ class TestOptimizationPipeline:
151
+ """Tests for OptimizationPipeline class."""
152
+
153
+ def test_initialization(self) -> None:
154
+ """Test pipeline initialization."""
155
+ pipeline = OptimizationPipeline()
156
+ assert pipeline.pass_manager is not None
157
+ assert isinstance(pipeline.stats, dict)
158
+ assert len(pipeline.stats) == 0
159
+
160
+ def test_register_all_passes(self) -> None:
161
+ """Test that all passes are registered."""
162
+ pipeline = OptimizationPipeline()
163
+ # Check that passes were registered (indirectly through registry)
164
+ assert pipeline.pass_manager is not None
165
+
166
+ def test_get_passes_for_o0(self) -> None:
167
+ """Test O0 optimization level (no optimization)."""
168
+ pipeline = OptimizationPipeline()
169
+ passes = pipeline.get_passes_for_level(OptimizationLevel.O0)
170
+ assert len(passes) == 0
171
+
172
+ def test_get_passes_for_o1(self) -> None:
173
+ """Test O1 optimization level (basic optimization)."""
174
+ pipeline = OptimizationPipeline()
175
+ passes = pipeline.get_passes_for_level(OptimizationLevel.O1)
176
+
177
+ # O1 should include basic passes
178
+ assert len(passes) > 0
179
+ # Check pass names (can be MockPass or None)
180
+ pass_names = [p.get_info().name if p else None for p in passes]
181
+ # Filter out None values
182
+ pass_names = [name for name in pass_names if name is not None]
183
+ assert len(pass_names) > 0
184
+
185
+ def test_get_passes_for_o2(self) -> None:
186
+ """Test O2 optimization level (standard optimization)."""
187
+ pipeline = OptimizationPipeline()
188
+ passes = pipeline.get_passes_for_level(OptimizationLevel.O2)
189
+
190
+ # O2 should include more passes
191
+ assert len(passes) > 0
192
+
193
+ # Check for inlining pass
194
+ inlining_passes = [p for p in passes if isinstance(p, FunctionInlining)]
195
+ assert len(inlining_passes) == 1
196
+ assert inlining_passes[0].size_threshold == 30
197
+
198
+ def test_get_passes_for_o3(self) -> None:
199
+ """Test O3 optimization level (aggressive optimization)."""
200
+ pipeline = OptimizationPipeline()
201
+ passes = pipeline.get_passes_for_level(OptimizationLevel.O3)
202
+
203
+ # O3 should include aggressive inlining
204
+ inlining_passes = [p for p in passes if isinstance(p, FunctionInlining)]
205
+ assert len(inlining_passes) == 1
206
+ assert inlining_passes[0].size_threshold == 100 # More aggressive than O2
207
+
208
+ # O3 should have more passes than O2
209
+ o2_passes = pipeline.get_passes_for_level(OptimizationLevel.O2)
210
+ assert len(passes) >= len(o2_passes)
211
+
212
+ def test_get_passes_for_os(self) -> None:
213
+ """Test Os optimization level (optimize for size)."""
214
+ pipeline = OptimizationPipeline()
215
+ passes = pipeline.get_passes_for_level(OptimizationLevel.Os)
216
+
217
+ # Os should NOT include inlining (increases size)
218
+ inlining_passes = [p for p in passes if isinstance(p, FunctionInlining)]
219
+ assert len(inlining_passes) == 0
220
+
221
+ # Should have some optimization passes
222
+ assert len(passes) > 0
223
+
224
+ def test_optimize_o0(self) -> None:
225
+ """Test optimization with O0 level."""
226
+ pipeline = OptimizationPipeline()
227
+ module = create_test_module()
228
+
229
+ modified = pipeline.optimize(module, OptimizationLevel.O0)
230
+
231
+ # O0 should not modify the module
232
+ assert not modified
233
+ assert pipeline.stats["level"] == "O0"
234
+ assert len(pipeline.stats["passes_run"]) == 0
235
+ assert pipeline.stats["total_modifications"] == 0
236
+
237
+ def test_optimize_with_modifications(self) -> None:
238
+ """Test optimization that modifies the module."""
239
+ pipeline = OptimizationPipeline()
240
+ module = create_test_module()
241
+
242
+ # Create mock passes
243
+ mock_pass1 = MockPass("pass1", modified=True)
244
+ mock_pass2 = MockPass("pass2", modified=False)
245
+ mock_pass3 = MockPass("pass3", modified=True)
246
+
247
+ # Mock get_passes_for_level to return our mock passes
248
+ with patch.object(pipeline, "get_passes_for_level", return_value=[mock_pass1, mock_pass2, mock_pass3]):
249
+ modified = pipeline.optimize(module, OptimizationLevel.O1)
250
+
251
+ assert modified
252
+ assert pipeline.stats["level"] == "O1"
253
+ assert pipeline.stats["passes_run"] == ["pass1", "pass2", "pass3"]
254
+ assert pipeline.stats["total_modifications"] == 2 # pass1 and pass3 modified
255
+ assert "pass1" in pipeline.stats["pass_stats"]
256
+ assert "pass2" in pipeline.stats["pass_stats"]
257
+ assert "pass3" in pipeline.stats["pass_stats"]
258
+
259
+ def test_optimize_with_custom_pipeline(self) -> None:
260
+ """Test optimization with custom pipeline."""
261
+ pipeline = OptimizationPipeline()
262
+ module = create_test_module()
263
+
264
+ # Mock the pass manager registry
265
+ mock_registry = MagicMock()
266
+ mock_pass1 = MockPass("custom-pass1", modified=True)
267
+ mock_pass2 = MockPass("custom-pass2", modified=False)
268
+
269
+ def get_pass(name: str) -> ModulePass | None:
270
+ if name == "custom-pass1":
271
+ return mock_pass1
272
+ elif name == "custom-pass2":
273
+ return mock_pass2
274
+ return None
275
+
276
+ mock_registry.get_pass = get_pass
277
+ pipeline.pass_manager.registry = mock_registry
278
+
279
+ modified = pipeline.optimize_with_custom_pipeline(module, ["custom-pass1", "custom-pass2", "nonexistent-pass"])
280
+
281
+ assert modified
282
+ assert mock_pass1.run_count == 1
283
+ assert mock_pass2.run_count == 1
284
+
285
+ def test_get_statistics(self) -> None:
286
+ """Test getting optimization statistics."""
287
+ pipeline = OptimizationPipeline()
288
+
289
+ # Initially empty
290
+ stats = pipeline.get_statistics()
291
+ assert stats == {}
292
+
293
+ # After optimization
294
+ module = create_test_module()
295
+ pipeline.optimize(module, OptimizationLevel.O0)
296
+
297
+ stats = pipeline.get_statistics()
298
+ assert stats["level"] == "O0"
299
+ assert "passes_run" in stats
300
+ assert "total_modifications" in stats
301
+ assert "pass_stats" in stats
302
+
303
+
304
+ class TestPipelineBuilder:
305
+ """Tests for PipelineBuilder class."""
306
+
307
+ def test_initialization(self) -> None:
308
+ """Test builder initialization."""
309
+ builder = PipelineBuilder()
310
+ assert builder.passes == []
311
+ assert builder.pass_configs == {}
312
+
313
+ def test_add_pass(self) -> None:
314
+ """Test adding a pass."""
315
+ builder = PipelineBuilder()
316
+ result = builder.add_pass("test-pass")
317
+
318
+ assert result is builder # Returns self for chaining
319
+ assert "test-pass" in builder.passes
320
+ assert "test-pass" not in builder.pass_configs
321
+
322
+ def test_add_pass_with_config(self) -> None:
323
+ """Test adding a pass with configuration."""
324
+ builder = PipelineBuilder()
325
+ result = builder.add_pass("test-pass", threshold=10, enabled=True)
326
+
327
+ assert result is builder
328
+ assert "test-pass" in builder.passes
329
+ assert "test-pass" in builder.pass_configs
330
+ assert builder.pass_configs["test-pass"]["threshold"] == 10
331
+ assert builder.pass_configs["test-pass"]["enabled"] is True
332
+
333
+ def test_add_cleanup_passes(self) -> None:
334
+ """Test adding cleanup passes."""
335
+ builder = PipelineBuilder()
336
+ result = builder.add_cleanup_passes()
337
+
338
+ assert result is builder
339
+ assert "dce" in builder.passes
340
+ assert "jump-threading" in builder.passes
341
+ assert "peephole" in builder.passes
342
+
343
+ def test_add_algebraic_passes(self) -> None:
344
+ """Test adding algebraic optimization passes."""
345
+ builder = PipelineBuilder()
346
+ result = builder.add_algebraic_passes()
347
+
348
+ assert result is builder
349
+ assert "constant-propagation" in builder.passes
350
+ assert "strength-reduction" in builder.passes
351
+ assert "cse" in builder.passes
352
+
353
+ def test_add_loop_passes(self) -> None:
354
+ """Test adding loop optimization passes."""
355
+ builder = PipelineBuilder()
356
+ result = builder.add_loop_passes()
357
+
358
+ assert result is builder
359
+ assert "licm" in builder.passes
360
+
361
+ def test_repeat(self) -> None:
362
+ """Test repeating the pipeline."""
363
+ builder = PipelineBuilder()
364
+ builder.add_pass("pass1").add_pass("pass2")
365
+
366
+ result = builder.repeat(3)
367
+
368
+ assert result is builder
369
+ assert builder.passes == ["pass1", "pass2", "pass1", "pass2", "pass1", "pass2"]
370
+
371
+ def test_repeat_once(self) -> None:
372
+ """Test repeat with times=1 (no repetition)."""
373
+ builder = PipelineBuilder()
374
+ builder.add_pass("pass1").add_pass("pass2")
375
+
376
+ result = builder.repeat(1)
377
+
378
+ assert result is builder
379
+ assert builder.passes == ["pass1", "pass2"]
380
+
381
+ def test_build(self) -> None:
382
+ """Test building the pipeline."""
383
+ builder = PipelineBuilder()
384
+ builder.add_pass("pass1").add_pass("pass2")
385
+
386
+ result = builder.build()
387
+
388
+ assert result == ["pass1", "pass2"]
389
+ assert result is not builder.passes # Should be a copy
390
+
391
+ def test_chaining(self) -> None:
392
+ """Test method chaining."""
393
+ builder = PipelineBuilder()
394
+
395
+ result = builder.add_pass("inline").add_algebraic_passes().add_loop_passes().add_cleanup_passes().repeat(2)
396
+
397
+ assert result is builder
398
+ expected = [
399
+ "inline",
400
+ "constant-propagation",
401
+ "strength-reduction",
402
+ "cse",
403
+ "licm",
404
+ "dce",
405
+ "jump-threading",
406
+ "peephole",
407
+ "inline",
408
+ "constant-propagation",
409
+ "strength-reduction",
410
+ "cse",
411
+ "licm",
412
+ "dce",
413
+ "jump-threading",
414
+ "peephole",
415
+ ]
416
+ assert builder.passes == expected
417
+
418
+ def test_complex_pipeline(self) -> None:
419
+ """Test building a complex custom pipeline."""
420
+ builder = PipelineBuilder()
421
+ builder.add_pass("inline", size_threshold=50)
422
+ builder.add_algebraic_passes()
423
+ builder.add_pass("inline", size_threshold=100)
424
+ builder.add_loop_passes()
425
+ builder.add_cleanup_passes()
426
+ pipeline = builder.build()
427
+
428
+ # inline + 3 algebraic + inline + 1 loop + 3 cleanup = 9 total
429
+ assert len(pipeline) == 9
430
+ assert pipeline[0] == "inline"
431
+ assert pipeline[4] == "inline"
432
+ assert pipeline[-1] == "peephole"
433
+
434
+
435
+ class TestConvenienceFunctions:
436
+ """Tests for convenience functions."""
437
+
438
+ def test_create_o0_pipeline(self) -> None:
439
+ """Test creating O0 pipeline."""
440
+ pipeline = create_o0_pipeline()
441
+ assert isinstance(pipeline, OptimizationPipeline)
442
+ assert pipeline.pass_manager is not None
443
+
444
+ def test_create_o1_pipeline(self) -> None:
445
+ """Test creating O1 pipeline."""
446
+ pipeline = create_o1_pipeline()
447
+ assert isinstance(pipeline, OptimizationPipeline)
448
+ assert pipeline.pass_manager is not None
449
+
450
+ def test_create_o2_pipeline(self) -> None:
451
+ """Test creating O2 pipeline."""
452
+ pipeline = create_o2_pipeline()
453
+ assert isinstance(pipeline, OptimizationPipeline)
454
+ assert pipeline.pass_manager is not None
455
+
456
+ def test_create_o3_pipeline(self) -> None:
457
+ """Test creating O3 pipeline."""
458
+ pipeline = create_o3_pipeline()
459
+ assert isinstance(pipeline, OptimizationPipeline)
460
+ assert pipeline.pass_manager is not None
461
+
462
+ def test_create_size_pipeline(self) -> None:
463
+ """Test creating size optimization pipeline."""
464
+ pipeline = create_size_pipeline()
465
+ assert isinstance(pipeline, OptimizationPipeline)
466
+ assert pipeline.pass_manager is not None
467
+
468
+ def test_all_pipelines_can_optimize(self) -> None:
469
+ """Test that all convenience pipelines can run optimization."""
470
+ create_test_module() # Validate it can create a module
471
+
472
+ pipelines = [
473
+ (create_o0_pipeline(), OptimizationLevel.O0),
474
+ (create_o1_pipeline(), OptimizationLevel.O1),
475
+ (create_o2_pipeline(), OptimizationLevel.O2),
476
+ (create_o3_pipeline(), OptimizationLevel.O3),
477
+ (create_size_pipeline(), OptimizationLevel.Os),
478
+ ]
479
+
480
+ for pipeline, level in pipelines:
481
+ test_module = create_test_module() # Fresh module for each test
482
+ # Should not raise any exceptions
483
+ pipeline.optimize(test_module, level)
484
+ stats = pipeline.get_statistics()
485
+ assert stats["level"] == level.value
486
+
487
+
488
+ class TestEdgeCases:
489
+ """Tests for edge cases and error conditions."""
490
+
491
+ def test_empty_custom_pipeline(self) -> None:
492
+ """Test custom pipeline with empty pass list."""
493
+ pipeline = OptimizationPipeline()
494
+ module = create_test_module()
495
+
496
+ modified = pipeline.optimize_with_custom_pipeline(module, [])
497
+
498
+ assert not modified
499
+
500
+ def test_nonexistent_passes_in_custom_pipeline(self) -> None:
501
+ """Test custom pipeline with only nonexistent passes."""
502
+ pipeline = OptimizationPipeline()
503
+ module = create_test_module()
504
+
505
+ # Mock registry to return None for all passes
506
+ mock_registry = MagicMock()
507
+ mock_registry.get_pass.return_value = None
508
+ pipeline.pass_manager.registry = mock_registry
509
+
510
+ modified = pipeline.optimize_with_custom_pipeline(module, ["nonexistent1", "nonexistent2"])
511
+
512
+ assert not modified
513
+
514
+ def test_pipeline_builder_empty_repeat(self) -> None:
515
+ """Test repeating an empty pipeline."""
516
+ builder = PipelineBuilder()
517
+ result = builder.repeat(5)
518
+
519
+ assert result is builder
520
+ assert builder.passes == []
521
+
522
+ def test_pipeline_builder_zero_repeat(self) -> None:
523
+ """Test repeat with times=0."""
524
+ builder = PipelineBuilder()
525
+ builder.add_pass("pass1")
526
+
527
+ # Repeat 0 times should remove all passes
528
+ result = builder.repeat(0)
529
+
530
+ assert result is builder
531
+ # After repeat(0), we should have no passes since (times - 1) = -1 means no extension
532
+ assert builder.passes == ["pass1"] # Original pass remains, no additional copies
533
+
534
+ def test_pass_without_get_statistics(self) -> None:
535
+ """Test handling passes without get_statistics method."""
536
+ pipeline = OptimizationPipeline()
537
+ module = create_test_module()
538
+
539
+ # Create a mock pass without get_statistics
540
+ mock_pass = MagicMock(spec=ModulePass)
541
+ mock_pass.get_info.return_value = PassInfo(
542
+ "test", "Test pass", PassType.OPTIMIZATION, [], PreservationLevel.ALL
543
+ )
544
+ mock_pass.run_on_module.return_value = True
545
+ # Delete get_statistics to simulate it not existing
546
+ del mock_pass.get_statistics
547
+
548
+ with patch.object(pipeline, "get_passes_for_level", return_value=[mock_pass]):
549
+ pipeline.optimize(module, OptimizationLevel.O1)
550
+
551
+ # Should handle gracefully
552
+ stats = pipeline.get_statistics()
553
+ assert stats["passes_run"] == ["test"]
554
+ assert "test" not in stats["pass_stats"] # No stats for this pass