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,324 @@
1
+ """Tests for SSA variable handling in register bytecode generation.
2
+
3
+ This module tests the proper handling of SSA-renamed variables (version > 0)
4
+ versus regular variables (version = 0) in the register-based bytecode generator.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import pytest
10
+
11
+ from machine_dialect.codegen.register_codegen import RegisterBytecodeGenerator
12
+ from machine_dialect.mir.basic_block import CFG, BasicBlock
13
+ from machine_dialect.mir.mir_function import MIRFunction
14
+ from machine_dialect.mir.mir_instructions import (
15
+ BinaryOp,
16
+ Call,
17
+ ConditionalJump,
18
+ Copy,
19
+ LoadConst,
20
+ LoadVar,
21
+ Return,
22
+ StoreVar,
23
+ )
24
+ from machine_dialect.mir.mir_module import MIRModule
25
+ from machine_dialect.mir.mir_types import MIRType
26
+ from machine_dialect.mir.mir_values import Constant, FunctionRef, Variable
27
+
28
+
29
+ class TestSSAVariableHandling:
30
+ """Test SSA variable handling in bytecode generation."""
31
+
32
+ def test_ssa_variable_allocation(self) -> None:
33
+ """Test that SSA variables are properly allocated to registers."""
34
+ # Create a simple function with SSA variables
35
+ func = MIRFunction("test_func", [], MIRType.INT)
36
+ cfg = CFG()
37
+
38
+ # Create blocks
39
+ entry = BasicBlock("entry")
40
+ cfg.add_block(entry)
41
+ cfg.set_entry_block(entry)
42
+
43
+ # Create SSA variables
44
+ x_0 = Variable("x", MIRType.INT, version=0) # Non-SSA
45
+ x_1 = Variable("x", MIRType.INT, version=1) # SSA
46
+ x_2 = Variable("x", MIRType.INT, version=2) # SSA
47
+
48
+ # Add instructions
49
+ entry.add_instruction(LoadConst(x_0, Constant(10, MIRType.INT), (0, 0)))
50
+ entry.add_instruction(Copy(x_1, x_0, (0, 0))) # SSA copy
51
+ entry.add_instruction(BinaryOp(x_2, "+", x_1, Constant(5, MIRType.INT), (0, 0)))
52
+ entry.add_instruction(Return((0, 0), x_2))
53
+
54
+ func.cfg = cfg
55
+
56
+ # Generate bytecode
57
+ module = MIRModule("test")
58
+ module.add_function(func)
59
+
60
+ generator = RegisterBytecodeGenerator(debug=False)
61
+ _ = generator.generate(module)
62
+
63
+ # Check that SSA variables are allocated
64
+ assert generator.allocation is not None
65
+ if generator.allocation:
66
+ assert x_1 in generator.allocation.value_to_register
67
+ assert x_2 in generator.allocation.value_to_register
68
+
69
+ # Check that they got different registers
70
+ reg_x1 = generator.allocation.value_to_register[x_1]
71
+ reg_x2 = generator.allocation.value_to_register[x_2]
72
+ assert reg_x1 != reg_x2
73
+
74
+ def test_ssa_variable_in_copy(self) -> None:
75
+ """Test Copy instruction with SSA variables."""
76
+ func = MIRFunction("test_copy", [], MIRType.INT)
77
+ cfg = CFG()
78
+
79
+ entry = BasicBlock("entry")
80
+ cfg.add_block(entry)
81
+ cfg.set_entry_block(entry)
82
+
83
+ # Create variables
84
+ x_1 = Variable("x", MIRType.INT, version=1) # SSA
85
+ y_0 = Variable("y", MIRType.INT, version=0) # Non-SSA
86
+
87
+ # First, x_1 must be defined
88
+ entry.add_instruction(LoadConst(x_1, Constant(42, MIRType.INT), (0, 0)))
89
+ # Copy from SSA to non-SSA
90
+ entry.add_instruction(Copy(y_0, x_1, (0, 0)))
91
+ entry.add_instruction(Return((0, 0), y_0))
92
+
93
+ func.cfg = cfg
94
+
95
+ module = MIRModule("test")
96
+ module.add_function(func)
97
+
98
+ generator = RegisterBytecodeGenerator(debug=False)
99
+ _ = generator.generate(module)
100
+
101
+ # Check that x_1 is allocated
102
+ assert generator.allocation is not None
103
+ if generator.allocation:
104
+ assert x_1 in generator.allocation.value_to_register
105
+
106
+ def test_ssa_variable_not_allocated_error(self) -> None:
107
+ """Test that using an unallocated SSA variable raises an error."""
108
+ func = MIRFunction("test_error", [], MIRType.INT)
109
+ cfg = CFG()
110
+
111
+ entry = BasicBlock("entry")
112
+ cfg.add_block(entry)
113
+ cfg.set_entry_block(entry)
114
+
115
+ # Create an SSA variable but don't define it properly
116
+ x_1 = Variable("x", MIRType.INT, version=1)
117
+
118
+ # Create a Copy instruction that uses the undefined SSA variable
119
+ dest_temp = func.new_temp(MIRType.INT)
120
+ copy_inst = Copy(dest_temp, x_1, (0, 0))
121
+
122
+ entry.add_instruction(copy_inst)
123
+ entry.add_instruction(Return((0, 0), Constant(0)))
124
+
125
+ func.cfg = cfg
126
+ module = MIRModule("test")
127
+ module.add_function(func)
128
+
129
+ generator = RegisterBytecodeGenerator(debug=False)
130
+
131
+ # Manually generate the function to get the allocation
132
+ # but then remove x_1 from the allocation to simulate a bug
133
+ generator.bytecode = bytearray()
134
+ generator.constants = []
135
+ generator.block_offsets = {}
136
+ generator.instruction_offsets = []
137
+ generator.pending_jumps = []
138
+
139
+ # Allocate registers normally
140
+ generator.allocation = generator.allocator.allocate_function(func)
141
+
142
+ # Now remove x_1 from the allocation to simulate it not being allocated
143
+ if x_1 in generator.allocation.value_to_register:
144
+ del generator.allocation.value_to_register[x_1]
145
+
146
+ # Try to generate the copy instruction - this should fail
147
+ with pytest.raises(RuntimeError) as context:
148
+ generator.instruction_offsets.append(len(generator.bytecode))
149
+ generator.generate_copy(copy_inst)
150
+
151
+ assert "SSA variable" in str(context.value)
152
+ assert "not allocated to register" in str(context.value)
153
+
154
+ def test_mixed_ssa_and_global_variables(self) -> None:
155
+ """Test handling of both SSA and global variables in the same function."""
156
+ func = MIRFunction("test_mixed", [], MIRType.INT)
157
+ cfg = CFG()
158
+
159
+ entry = BasicBlock("entry")
160
+ cfg.add_block(entry)
161
+ cfg.set_entry_block(entry)
162
+
163
+ # Create variables
164
+ global_var = Variable("global", MIRType.INT, version=0) # Global
165
+ local_1 = Variable("local", MIRType.INT, version=1) # SSA
166
+ local_2 = Variable("local", MIRType.INT, version=2) # SSA
167
+
168
+ # Instructions
169
+ entry.add_instruction(LoadVar(local_1, global_var, (0, 0))) # Load global into SSA
170
+ entry.add_instruction(BinaryOp(local_2, "+", local_1, Constant(10, MIRType.INT), (0, 0)))
171
+ entry.add_instruction(StoreVar(global_var, local_2, (0, 0))) # Store SSA to global
172
+ entry.add_instruction(Return((0, 0), local_2))
173
+
174
+ func.cfg = cfg
175
+ module = MIRModule("test")
176
+ module.add_function(func)
177
+
178
+ generator = RegisterBytecodeGenerator(debug=False)
179
+ _ = generator.generate(module)
180
+
181
+ # Check allocations
182
+ assert generator.allocation is not None
183
+ if generator.allocation:
184
+ # SSA variables should be allocated
185
+ assert local_1 in generator.allocation.value_to_register
186
+ assert local_2 in generator.allocation.value_to_register
187
+ # Global variable should NOT be allocated (it's loaded by name)
188
+ assert global_var not in generator.allocation.value_to_register
189
+
190
+ def test_function_parameters_as_ssa(self) -> None:
191
+ """Test that function parameters work correctly with SSA versioning."""
192
+ # Parameters start with version 0 but are allocated to registers
193
+ param_n = Variable("n", MIRType.INT, version=0)
194
+
195
+ func = MIRFunction("fibonacci", [param_n], MIRType.INT)
196
+ cfg = CFG()
197
+
198
+ entry = BasicBlock("entry")
199
+ cfg.add_block(entry)
200
+ cfg.set_entry_block(entry)
201
+
202
+ # Create SSA versions of the parameter
203
+ n_1 = Variable("n", MIRType.INT, version=1)
204
+
205
+ # Use the parameter
206
+ entry.add_instruction(Copy(param_n, n_1, (0, 0)))
207
+ entry.add_instruction(Return((0, 0), n_1))
208
+
209
+ func.cfg = cfg
210
+ module = MIRModule("test")
211
+ module.add_function(func)
212
+
213
+ generator = RegisterBytecodeGenerator(debug=False)
214
+ _ = generator.generate(module)
215
+
216
+ # Check that both parameter and SSA version are allocated
217
+ assert generator.allocation is not None
218
+ if generator.allocation:
219
+ assert param_n in generator.allocation.value_to_register
220
+ assert n_1 in generator.allocation.value_to_register
221
+
222
+ def test_recursive_function_with_ssa(self) -> None:
223
+ """Test a recursive function with SSA variables."""
224
+ # Create a simplified recursive function
225
+ param_n = Variable("n", MIRType.INT, version=0)
226
+
227
+ func = MIRFunction("recursive", [param_n], MIRType.INT)
228
+ cfg = CFG()
229
+
230
+ # Create blocks
231
+ entry = BasicBlock("entry")
232
+ base_case = BasicBlock("base_case")
233
+ recursive_case = BasicBlock("recursive_case")
234
+
235
+ cfg.add_block(entry)
236
+ cfg.add_block(base_case)
237
+ cfg.add_block(recursive_case)
238
+ cfg.set_entry_block(entry)
239
+
240
+ # Connect blocks
241
+ cfg.connect(entry, base_case)
242
+ cfg.connect(entry, recursive_case)
243
+
244
+ # Entry: check if n <= 1
245
+ cond = func.new_temp(MIRType.BOOL)
246
+ entry.add_instruction(BinaryOp(cond, "<=", param_n, Constant(1, MIRType.INT), (0, 0)))
247
+ entry.add_instruction(ConditionalJump(cond, "base_case", (0, 0), "recursive_case"))
248
+
249
+ # Base case: return 1
250
+ base_case.add_instruction(Return((0, 0), Constant(1)))
251
+
252
+ # Recursive case: return recursive(n - 1) + n
253
+ n_minus_1 = Variable("n_minus_1", MIRType.INT, version=1) # SSA
254
+ recursive_result = func.new_temp(MIRType.INT)
255
+ final_result = func.new_temp(MIRType.INT)
256
+
257
+ recursive_case.add_instruction(BinaryOp(n_minus_1, "-", param_n, Constant(1, MIRType.INT), (0, 0)))
258
+ recursive_case.add_instruction(Call(recursive_result, FunctionRef("recursive"), [n_minus_1], (0, 0)))
259
+ recursive_case.add_instruction(BinaryOp(final_result, "+", recursive_result, param_n, (0, 0)))
260
+ recursive_case.add_instruction(Return((0, 0), final_result))
261
+
262
+ func.cfg = cfg
263
+ module = MIRModule("test")
264
+ module.add_function(func)
265
+
266
+ generator = RegisterBytecodeGenerator(debug=False)
267
+ _ = generator.generate(module)
268
+
269
+ # Check that SSA variable is allocated
270
+ assert generator.allocation is not None
271
+ if generator.allocation:
272
+ assert n_minus_1 in generator.allocation.value_to_register
273
+
274
+ def test_is_ssa_variable_helper(self) -> None:
275
+ """Test the is_ssa_variable helper method."""
276
+ generator = RegisterBytecodeGenerator(debug=False)
277
+
278
+ # Test SSA variables (version > 0)
279
+ ssa_var = Variable("x", MIRType.INT, version=1)
280
+ assert generator.is_ssa_variable(ssa_var)
281
+
282
+ ssa_var2 = Variable("y", MIRType.INT, version=5)
283
+ assert generator.is_ssa_variable(ssa_var2)
284
+
285
+ # Test non-SSA variables (version = 0)
286
+ regular_var = Variable("z", MIRType.INT, version=0)
287
+ assert not generator.is_ssa_variable(regular_var)
288
+
289
+ # Test non-Variable types
290
+ # Create a dummy function to get a proper Temp
291
+ dummy_func = MIRFunction("dummy", [], MIRType.INT)
292
+ temp = dummy_func.new_temp(MIRType.INT)
293
+ assert not generator.is_ssa_variable(temp)
294
+
295
+ constant = Constant(42, MIRType.INT)
296
+ assert not generator.is_ssa_variable(constant)
297
+
298
+ def test_debug_mode(self) -> None:
299
+ """Test that debug mode controls output."""
300
+ func = MIRFunction("test_debug", [], MIRType.INT)
301
+ cfg = CFG()
302
+
303
+ entry = BasicBlock("entry")
304
+ cfg.add_block(entry)
305
+ cfg.set_entry_block(entry)
306
+
307
+ x = Variable("x", MIRType.INT, version=1)
308
+ entry.add_instruction(LoadConst(x, Constant(42, MIRType.INT), (0, 0)))
309
+ entry.add_instruction(Return((0, 0), x))
310
+
311
+ func.cfg = cfg
312
+ module = MIRModule("test")
313
+ module.add_function(func)
314
+
315
+ # Test with debug=False (no output expected)
316
+ generator_no_debug = RegisterBytecodeGenerator(debug=False)
317
+ bytecode_no_debug = generator_no_debug.generate(module)
318
+
319
+ # Test with debug=True (would produce output if we captured it)
320
+ generator_debug = RegisterBytecodeGenerator(debug=True)
321
+ bytecode_debug = generator_debug.generate(module)
322
+
323
+ # Both should produce the same bytecode
324
+ assert bytecode_no_debug.chunks[0].bytecode == bytecode_debug.chunks[0].bytecode