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,425 @@
1
+ """Tests for MIR basic blocks and CFG."""
2
+
3
+ from machine_dialect.mir.basic_block import CFG, BasicBlock
4
+ from machine_dialect.mir.mir_instructions import (
5
+ BinaryOp,
6
+ ConditionalJump,
7
+ Jump,
8
+ LoadConst,
9
+ Return,
10
+ )
11
+ from machine_dialect.mir.mir_types import MIRType
12
+ from machine_dialect.mir.mir_values import Temp
13
+
14
+
15
+ class TestBasicBlock:
16
+ """Test basic block functionality."""
17
+
18
+ def test_basic_block_creation(self) -> None:
19
+ """Test creating basic blocks."""
20
+ block = BasicBlock("entry")
21
+ assert block.label == "entry"
22
+ assert block.instructions == []
23
+ assert block.phi_nodes == []
24
+ assert block.predecessors == []
25
+ assert block.successors == []
26
+
27
+ def test_is_terminated(self) -> None:
28
+ """Test checking if block is terminated."""
29
+ block = BasicBlock("bb1")
30
+ t0 = Temp(MIRType.INT, temp_id=0)
31
+
32
+ # Empty block is not terminated
33
+ assert not block.is_terminated()
34
+
35
+ # Block with non-terminator is not terminated
36
+ block.add_instruction(LoadConst(t0, 42, (1, 1)))
37
+ assert not block.is_terminated()
38
+
39
+ # Block with terminator is terminated
40
+ block.add_instruction(Return((1, 1), t0))
41
+ assert block.is_terminated()
42
+
43
+ def test_get_terminator(self) -> None:
44
+ """Test getting block terminator."""
45
+ block = BasicBlock("bb1")
46
+ t0 = Temp(MIRType.INT, temp_id=0)
47
+
48
+ # No terminator initially
49
+ assert block.get_terminator() is None
50
+
51
+ # Add instructions
52
+ block.add_instruction(LoadConst(t0, 5, (1, 1)))
53
+ assert block.get_terminator() is None
54
+
55
+ # Add terminator
56
+ ret = Return((1, 1), t0)
57
+ block.add_instruction(ret)
58
+ assert block.get_terminator() == ret
59
+
60
+ def test_terminator_types(self) -> None:
61
+ """Test different terminator types."""
62
+ # Test Jump
63
+ block1 = BasicBlock("bb1")
64
+ jump = Jump("bb2", (1, 1))
65
+ block1.add_instruction(jump)
66
+ assert block1.is_terminated()
67
+
68
+ # Test ConditionalJump
69
+ block2 = BasicBlock("bb2")
70
+ t0 = Temp(MIRType.BOOL, temp_id=0)
71
+ cjump = ConditionalJump(t0, "then", (1, 1), "else")
72
+ block2.add_instruction(cjump)
73
+ assert block2.is_terminated()
74
+
75
+ # Test Return
76
+ block3 = BasicBlock("bb3")
77
+ ret = Return((1, 1))
78
+ block3.add_instruction(ret)
79
+ assert block3.is_terminated()
80
+
81
+ def test_connect_blocks(self) -> None:
82
+ """Test connecting blocks as predecessors/successors."""
83
+ entry = BasicBlock("entry")
84
+ bb1 = BasicBlock("bb1")
85
+ bb2 = BasicBlock("bb2")
86
+
87
+ entry.add_successor(bb1)
88
+ entry.add_successor(bb2)
89
+
90
+ assert entry.successors == [bb1, bb2]
91
+ assert bb1.predecessors == [entry]
92
+ assert bb2.predecessors == [entry]
93
+
94
+ def test_block_string_representation(self) -> None:
95
+ """Test string representation of blocks."""
96
+ block = BasicBlock("loop_body")
97
+ t0 = Temp(MIRType.INT, temp_id=0)
98
+ t1 = Temp(MIRType.INT, temp_id=1)
99
+
100
+ block.add_instruction(LoadConst(t0, 1, (1, 1)))
101
+ block.add_instruction(BinaryOp(t1, "+", t0, t0, (1, 1)))
102
+ block.add_instruction(Jump("loop_body", (1, 1)))
103
+
104
+ expected = """loop_body:
105
+ t0 = 1
106
+ t1 = t0 + t0
107
+ goto loop_body"""
108
+
109
+ assert str(block) == expected
110
+
111
+
112
+ class TestCFG:
113
+ """Test control flow graph functionality."""
114
+
115
+ def test_cfg_creation(self) -> None:
116
+ """Test creating CFG."""
117
+ cfg = CFG()
118
+ assert cfg.blocks == {}
119
+ assert cfg.entry_block is None
120
+ assert cfg.exit_block is None
121
+
122
+ def test_add_block(self) -> None:
123
+ """Test adding blocks to CFG."""
124
+ cfg = CFG()
125
+ entry = BasicBlock("entry")
126
+ bb1 = BasicBlock("bb1")
127
+
128
+ cfg.add_block(entry)
129
+ cfg.add_block(bb1)
130
+
131
+ assert len(cfg.blocks) == 2
132
+ assert cfg.blocks["entry"] == entry
133
+ assert cfg.blocks["bb1"] == bb1
134
+
135
+ def test_set_entry_block(self) -> None:
136
+ """Test setting entry block."""
137
+ cfg = CFG()
138
+ entry = BasicBlock("entry")
139
+
140
+ cfg.add_block(entry)
141
+ cfg.set_entry_block(entry)
142
+
143
+ assert cfg.entry_block == entry
144
+
145
+ def test_connect_blocks(self) -> None:
146
+ """Test connecting blocks in CFG."""
147
+ cfg = CFG()
148
+ entry = BasicBlock("entry")
149
+ then_block = BasicBlock("then")
150
+ else_block = BasicBlock("else")
151
+ merge = BasicBlock("merge")
152
+
153
+ cfg.add_block(entry)
154
+ cfg.add_block(then_block)
155
+ cfg.add_block(else_block)
156
+ cfg.add_block(merge)
157
+
158
+ cfg.connect(entry, then_block)
159
+ cfg.connect(entry, else_block)
160
+ cfg.connect(then_block, merge)
161
+ cfg.connect(else_block, merge)
162
+
163
+ assert entry.successors == [then_block, else_block]
164
+ assert then_block.predecessors == [entry]
165
+ assert else_block.predecessors == [entry]
166
+ assert merge.predecessors == [then_block, else_block]
167
+
168
+ def test_get_block(self) -> None:
169
+ """Test getting block by label."""
170
+ cfg = CFG()
171
+ bb1 = BasicBlock("bb1")
172
+ cfg.add_block(bb1)
173
+
174
+ assert cfg.get_block("bb1") == bb1
175
+ assert cfg.get_block("nonexistent") is None
176
+
177
+ def test_get_predecessors(self) -> None:
178
+ """Test getting block predecessors."""
179
+ cfg = CFG()
180
+ entry = BasicBlock("entry")
181
+ bb1 = BasicBlock("bb1")
182
+ bb2 = BasicBlock("bb2")
183
+ merge = BasicBlock("merge")
184
+
185
+ cfg.add_block(entry)
186
+ cfg.add_block(bb1)
187
+ cfg.add_block(bb2)
188
+ cfg.add_block(merge)
189
+
190
+ cfg.connect(entry, bb1)
191
+ cfg.connect(entry, bb2)
192
+ cfg.connect(bb1, merge)
193
+ cfg.connect(bb2, merge)
194
+
195
+ assert cfg.get_predecessors(entry) == []
196
+ assert cfg.get_predecessors(bb1) == [entry]
197
+ assert cfg.get_predecessors(merge) == [bb1, bb2]
198
+
199
+ def test_get_successors(self) -> None:
200
+ """Test getting block successors."""
201
+ cfg = CFG()
202
+ entry = BasicBlock("entry")
203
+ bb1 = BasicBlock("bb1")
204
+ bb2 = BasicBlock("bb2")
205
+
206
+ cfg.add_block(entry)
207
+ cfg.add_block(bb1)
208
+ cfg.add_block(bb2)
209
+
210
+ cfg.connect(entry, bb1)
211
+ cfg.connect(entry, bb2)
212
+
213
+ assert cfg.get_successors(entry) == [bb1, bb2]
214
+ assert cfg.get_successors(bb1) == []
215
+ assert cfg.get_successors(bb2) == []
216
+
217
+ def test_compute_dominance_simple(self) -> None:
218
+ """Test dominance computation on simple CFG."""
219
+ cfg = CFG()
220
+ entry = BasicBlock("entry")
221
+ bb1 = BasicBlock("bb1")
222
+ bb2 = BasicBlock("bb2")
223
+
224
+ cfg.add_block(entry)
225
+ cfg.add_block(bb1)
226
+ cfg.add_block(bb2)
227
+ cfg.set_entry_block(entry)
228
+
229
+ cfg.connect(entry, bb1)
230
+ cfg.connect(bb1, bb2)
231
+
232
+ cfg.compute_dominance()
233
+
234
+ # Entry dominates all blocks
235
+ assert entry in cfg.dominators[entry]
236
+ assert entry in cfg.dominators[bb1]
237
+ assert entry in cfg.dominators[bb2]
238
+
239
+ # bb1 dominates bb2
240
+ assert bb1 in cfg.dominators[bb2]
241
+
242
+ # Each block dominates itself
243
+ assert bb1 in cfg.dominators[bb1]
244
+ assert bb2 in cfg.dominators[bb2]
245
+
246
+ def test_compute_dominance_with_branch(self) -> None:
247
+ """Test dominance with branching."""
248
+ cfg = CFG()
249
+ entry = BasicBlock("entry")
250
+ then_block = BasicBlock("then")
251
+ else_block = BasicBlock("else")
252
+ merge = BasicBlock("merge")
253
+
254
+ cfg.add_block(entry)
255
+ cfg.add_block(then_block)
256
+ cfg.add_block(else_block)
257
+ cfg.add_block(merge)
258
+ cfg.set_entry_block(entry)
259
+
260
+ cfg.connect(entry, then_block)
261
+ cfg.connect(entry, else_block)
262
+ cfg.connect(then_block, merge)
263
+ cfg.connect(else_block, merge)
264
+
265
+ cfg.compute_dominance()
266
+
267
+ # Entry dominates all
268
+ for block in [entry, then_block, else_block, merge]:
269
+ assert entry in cfg.dominators[block]
270
+
271
+ # Neither then nor else dominates merge (both paths lead to merge)
272
+ assert then_block not in cfg.dominators[merge]
273
+ assert else_block not in cfg.dominators[merge]
274
+
275
+ # Then doesn't dominate else and vice versa
276
+ assert then_block not in cfg.dominators[else_block]
277
+ assert else_block not in cfg.dominators[then_block]
278
+
279
+ def test_compute_dominance_frontiers(self) -> None:
280
+ """Test dominance frontier computation."""
281
+ cfg = CFG()
282
+ entry = BasicBlock("entry")
283
+ then_block = BasicBlock("then")
284
+ else_block = BasicBlock("else")
285
+ merge = BasicBlock("merge")
286
+
287
+ cfg.add_block(entry)
288
+ cfg.add_block(then_block)
289
+ cfg.add_block(else_block)
290
+ cfg.add_block(merge)
291
+ cfg.set_entry_block(entry)
292
+
293
+ cfg.connect(entry, then_block)
294
+ cfg.connect(entry, else_block)
295
+ cfg.connect(then_block, merge)
296
+ cfg.connect(else_block, merge)
297
+
298
+ cfg.compute_dominance()
299
+ cfg.compute_dominance_frontiers()
300
+
301
+ # Merge is in the dominance frontier of then and else
302
+ assert merge in cfg.dominance_frontiers.get(then_block, [])
303
+ assert merge in cfg.dominance_frontiers.get(else_block, [])
304
+
305
+ # Entry and merge should have empty frontiers in this case
306
+ assert cfg.dominance_frontiers.get(entry, []) == []
307
+ assert cfg.dominance_frontiers.get(merge, []) == []
308
+
309
+ def test_topological_sort(self) -> None:
310
+ """Test topological sorting of blocks."""
311
+ cfg = CFG()
312
+ entry = BasicBlock("entry")
313
+ bb1 = BasicBlock("bb1")
314
+ bb2 = BasicBlock("bb2")
315
+ bb3 = BasicBlock("bb3")
316
+
317
+ cfg.add_block(entry)
318
+ cfg.add_block(bb1)
319
+ cfg.add_block(bb2)
320
+ cfg.add_block(bb3)
321
+ cfg.set_entry_block(entry)
322
+
323
+ cfg.connect(entry, bb1)
324
+ cfg.connect(entry, bb2)
325
+ cfg.connect(bb1, bb3)
326
+ cfg.connect(bb2, bb3)
327
+
328
+ sorted_blocks = cfg.topological_sort()
329
+
330
+ # Entry should be first
331
+ assert sorted_blocks[0] == entry
332
+
333
+ # bb3 should be last (after both bb1 and bb2)
334
+ assert sorted_blocks[-1] == bb3
335
+
336
+ # bb1 and bb2 should be between entry and bb3
337
+ bb1_index = sorted_blocks.index(bb1)
338
+ bb2_index = sorted_blocks.index(bb2)
339
+ bb3_index = sorted_blocks.index(bb3)
340
+ assert bb1_index < bb3_index
341
+ assert bb2_index < bb3_index
342
+
343
+ def test_cfg_string_representation(self) -> None:
344
+ """Test string representation of CFG."""
345
+ cfg = CFG()
346
+ entry = BasicBlock("entry")
347
+ bb1 = BasicBlock("bb1")
348
+
349
+ t0 = Temp(MIRType.INT, temp_id=0)
350
+ entry.add_instruction(LoadConst(t0, 1, (1, 1)))
351
+ entry.add_instruction(Jump("bb1", (1, 1)))
352
+
353
+ bb1.add_instruction(Return((1, 1), t0))
354
+
355
+ cfg.add_block(entry)
356
+ cfg.add_block(bb1)
357
+ cfg.set_entry_block(entry)
358
+ cfg.connect(entry, bb1)
359
+
360
+ result = str(cfg)
361
+ assert "entry:" in result
362
+ assert "bb1:" in result
363
+ assert "t0 = 1" in result
364
+ assert "return t0" in result
365
+
366
+
367
+ class TestCFGWithLoops:
368
+ """Test CFG with loop structures."""
369
+
370
+ def test_loop_dominance(self) -> None:
371
+ """Test dominance in CFG with loops."""
372
+ cfg = CFG()
373
+ entry = BasicBlock("entry")
374
+ loop_header = BasicBlock("loop_header")
375
+ loop_body = BasicBlock("loop_body")
376
+ exit_block = BasicBlock("exit")
377
+
378
+ cfg.add_block(entry)
379
+ cfg.add_block(loop_header)
380
+ cfg.add_block(loop_body)
381
+ cfg.add_block(exit_block)
382
+ cfg.set_entry_block(entry)
383
+
384
+ # Create loop structure
385
+ cfg.connect(entry, loop_header)
386
+ cfg.connect(loop_header, loop_body)
387
+ cfg.connect(loop_body, loop_header) # Back edge
388
+ cfg.connect(loop_header, exit_block)
389
+
390
+ cfg.compute_dominance()
391
+
392
+ # Entry dominates all
393
+ assert entry in cfg.dominators[loop_header]
394
+ assert entry in cfg.dominators[loop_body]
395
+ assert entry in cfg.dominators[exit_block]
396
+
397
+ # Loop header dominates body and exit
398
+ assert loop_header in cfg.dominators[loop_body]
399
+ assert loop_header in cfg.dominators[exit_block]
400
+
401
+ def test_loop_dominance_frontiers(self) -> None:
402
+ """Test dominance frontiers in loops."""
403
+ cfg = CFG()
404
+ entry = BasicBlock("entry")
405
+ loop_header = BasicBlock("loop_header")
406
+ loop_body = BasicBlock("loop_body")
407
+ exit_block = BasicBlock("exit")
408
+
409
+ cfg.add_block(entry)
410
+ cfg.add_block(loop_header)
411
+ cfg.add_block(loop_body)
412
+ cfg.add_block(exit_block)
413
+ cfg.set_entry_block(entry)
414
+
415
+ cfg.connect(entry, loop_header)
416
+ cfg.connect(loop_header, loop_body)
417
+ cfg.connect(loop_body, loop_header) # Back edge
418
+ cfg.connect(loop_header, exit_block)
419
+
420
+ cfg.compute_dominance()
421
+ cfg.compute_dominance_frontiers()
422
+
423
+ # Loop header is in the dominance frontier of loop body
424
+ # (because of the back edge)
425
+ assert loop_header in cfg.dominance_frontiers.get(loop_body, [])