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.
- machine_dialect/__main__.py +667 -0
- machine_dialect/agent/__init__.py +5 -0
- machine_dialect/agent/agent.py +360 -0
- machine_dialect/ast/__init__.py +95 -0
- machine_dialect/ast/ast_node.py +35 -0
- machine_dialect/ast/call_expression.py +82 -0
- machine_dialect/ast/dict_extraction.py +60 -0
- machine_dialect/ast/expressions.py +439 -0
- machine_dialect/ast/literals.py +309 -0
- machine_dialect/ast/program.py +35 -0
- machine_dialect/ast/statements.py +1433 -0
- machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
- machine_dialect/ast/tests/test_boolean_literal.py +29 -0
- machine_dialect/ast/tests/test_collection_hir.py +138 -0
- machine_dialect/ast/tests/test_define_statement.py +142 -0
- machine_dialect/ast/tests/test_desugar.py +541 -0
- machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
- machine_dialect/cfg/__init__.py +6 -0
- machine_dialect/cfg/config.py +156 -0
- machine_dialect/cfg/examples.py +221 -0
- machine_dialect/cfg/generate_with_ai.py +187 -0
- machine_dialect/cfg/openai_generation.py +200 -0
- machine_dialect/cfg/parser.py +94 -0
- machine_dialect/cfg/tests/__init__.py +1 -0
- machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
- machine_dialect/cfg/tests/test_config.py +188 -0
- machine_dialect/cfg/tests/test_examples.py +391 -0
- machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
- machine_dialect/cfg/tests/test_openai_generation.py +256 -0
- machine_dialect/codegen/__init__.py +5 -0
- machine_dialect/codegen/bytecode_module.py +89 -0
- machine_dialect/codegen/bytecode_serializer.py +300 -0
- machine_dialect/codegen/opcodes.py +101 -0
- machine_dialect/codegen/register_codegen.py +1996 -0
- machine_dialect/codegen/symtab.py +208 -0
- machine_dialect/codegen/tests/__init__.py +1 -0
- machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
- machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
- machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
- machine_dialect/codegen/tests/test_symtab.py +418 -0
- machine_dialect/codegen/vm_serializer.py +621 -0
- machine_dialect/compiler/__init__.py +18 -0
- machine_dialect/compiler/compiler.py +197 -0
- machine_dialect/compiler/config.py +149 -0
- machine_dialect/compiler/context.py +149 -0
- machine_dialect/compiler/phases/__init__.py +19 -0
- machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
- machine_dialect/compiler/phases/codegen.py +40 -0
- machine_dialect/compiler/phases/hir_generation.py +39 -0
- machine_dialect/compiler/phases/mir_generation.py +86 -0
- machine_dialect/compiler/phases/optimization.py +110 -0
- machine_dialect/compiler/phases/parsing.py +39 -0
- machine_dialect/compiler/pipeline.py +143 -0
- machine_dialect/compiler/tests/__init__.py +1 -0
- machine_dialect/compiler/tests/test_compiler.py +568 -0
- machine_dialect/compiler/vm_runner.py +173 -0
- machine_dialect/errors/__init__.py +32 -0
- machine_dialect/errors/exceptions.py +369 -0
- machine_dialect/errors/messages.py +82 -0
- machine_dialect/errors/tests/__init__.py +0 -0
- machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
- machine_dialect/errors/tests/test_name_errors.py +118 -0
- machine_dialect/helpers/__init__.py +0 -0
- machine_dialect/helpers/stopwords.py +225 -0
- machine_dialect/helpers/validators.py +30 -0
- machine_dialect/lexer/__init__.py +9 -0
- machine_dialect/lexer/constants.py +23 -0
- machine_dialect/lexer/lexer.py +907 -0
- machine_dialect/lexer/tests/__init__.py +0 -0
- machine_dialect/lexer/tests/helpers.py +86 -0
- machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
- machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
- machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
- machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
- machine_dialect/lexer/tests/test_comments.py +200 -0
- machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
- machine_dialect/lexer/tests/test_lexer_position.py +113 -0
- machine_dialect/lexer/tests/test_list_tokens.py +282 -0
- machine_dialect/lexer/tests/test_stopwords.py +80 -0
- machine_dialect/lexer/tests/test_strict_equality.py +129 -0
- machine_dialect/lexer/tests/test_token.py +41 -0
- machine_dialect/lexer/tests/test_tokenization.py +294 -0
- machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
- machine_dialect/lexer/tests/test_url_literals.py +169 -0
- machine_dialect/lexer/tokens.py +487 -0
- machine_dialect/linter/__init__.py +10 -0
- machine_dialect/linter/__main__.py +144 -0
- machine_dialect/linter/linter.py +154 -0
- machine_dialect/linter/rules/__init__.py +8 -0
- machine_dialect/linter/rules/base.py +112 -0
- machine_dialect/linter/rules/statement_termination.py +99 -0
- machine_dialect/linter/tests/__init__.py +1 -0
- machine_dialect/linter/tests/mdrules/__init__.py +0 -0
- machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
- machine_dialect/linter/tests/test_linter.py +81 -0
- machine_dialect/linter/tests/test_rules.py +110 -0
- machine_dialect/linter/tests/test_violations.py +71 -0
- machine_dialect/linter/violations.py +51 -0
- machine_dialect/mir/__init__.py +69 -0
- machine_dialect/mir/analyses/__init__.py +20 -0
- machine_dialect/mir/analyses/alias_analysis.py +315 -0
- machine_dialect/mir/analyses/dominance_analysis.py +49 -0
- machine_dialect/mir/analyses/escape_analysis.py +286 -0
- machine_dialect/mir/analyses/loop_analysis.py +272 -0
- machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
- machine_dialect/mir/analyses/type_analysis.py +448 -0
- machine_dialect/mir/analyses/use_def_chains.py +232 -0
- machine_dialect/mir/basic_block.py +385 -0
- machine_dialect/mir/dataflow.py +445 -0
- machine_dialect/mir/debug_info.py +208 -0
- machine_dialect/mir/hir_to_mir.py +1738 -0
- machine_dialect/mir/mir_dumper.py +366 -0
- machine_dialect/mir/mir_function.py +167 -0
- machine_dialect/mir/mir_instructions.py +1877 -0
- machine_dialect/mir/mir_interpreter.py +556 -0
- machine_dialect/mir/mir_module.py +225 -0
- machine_dialect/mir/mir_printer.py +480 -0
- machine_dialect/mir/mir_transformer.py +410 -0
- machine_dialect/mir/mir_types.py +367 -0
- machine_dialect/mir/mir_validation.py +455 -0
- machine_dialect/mir/mir_values.py +268 -0
- machine_dialect/mir/optimization_config.py +233 -0
- machine_dialect/mir/optimization_pass.py +251 -0
- machine_dialect/mir/optimization_pipeline.py +355 -0
- machine_dialect/mir/optimizations/__init__.py +84 -0
- machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
- machine_dialect/mir/optimizations/branch_prediction.py +372 -0
- machine_dialect/mir/optimizations/constant_propagation.py +634 -0
- machine_dialect/mir/optimizations/cse.py +398 -0
- machine_dialect/mir/optimizations/dce.py +288 -0
- machine_dialect/mir/optimizations/inlining.py +551 -0
- machine_dialect/mir/optimizations/jump_threading.py +487 -0
- machine_dialect/mir/optimizations/licm.py +405 -0
- machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
- machine_dialect/mir/optimizations/strength_reduction.py +422 -0
- machine_dialect/mir/optimizations/tail_call.py +207 -0
- machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
- machine_dialect/mir/optimizations/type_narrowing.py +397 -0
- machine_dialect/mir/optimizations/type_specialization.py +447 -0
- machine_dialect/mir/optimizations/type_specific.py +906 -0
- machine_dialect/mir/optimize_mir.py +89 -0
- machine_dialect/mir/pass_manager.py +391 -0
- machine_dialect/mir/profiling/__init__.py +26 -0
- machine_dialect/mir/profiling/profile_collector.py +318 -0
- machine_dialect/mir/profiling/profile_data.py +372 -0
- machine_dialect/mir/profiling/profile_reader.py +272 -0
- machine_dialect/mir/profiling/profile_writer.py +226 -0
- machine_dialect/mir/register_allocation.py +302 -0
- machine_dialect/mir/reporting/__init__.py +17 -0
- machine_dialect/mir/reporting/optimization_reporter.py +314 -0
- machine_dialect/mir/reporting/report_formatter.py +289 -0
- machine_dialect/mir/ssa_construction.py +342 -0
- machine_dialect/mir/tests/__init__.py +1 -0
- machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
- machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
- machine_dialect/mir/tests/test_algebraic_division.py +126 -0
- machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
- machine_dialect/mir/tests/test_basic_block.py +425 -0
- machine_dialect/mir/tests/test_branch_prediction.py +459 -0
- machine_dialect/mir/tests/test_call_lowering.py +168 -0
- machine_dialect/mir/tests/test_collection_lowering.py +604 -0
- machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
- machine_dialect/mir/tests/test_custom_passes.py +166 -0
- machine_dialect/mir/tests/test_debug_info.py +285 -0
- machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
- machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
- machine_dialect/mir/tests/test_double_negation.py +231 -0
- machine_dialect/mir/tests/test_escape_analysis.py +233 -0
- machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
- machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
- machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
- machine_dialect/mir/tests/test_inlining.py +435 -0
- machine_dialect/mir/tests/test_licm.py +472 -0
- machine_dialect/mir/tests/test_mir_dumper.py +313 -0
- machine_dialect/mir/tests/test_mir_instructions.py +445 -0
- machine_dialect/mir/tests/test_mir_module.py +860 -0
- machine_dialect/mir/tests/test_mir_printer.py +387 -0
- machine_dialect/mir/tests/test_mir_types.py +123 -0
- machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
- machine_dialect/mir/tests/test_mir_validation.py +378 -0
- machine_dialect/mir/tests/test_mir_values.py +168 -0
- machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
- machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
- machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
- machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
- machine_dialect/mir/tests/test_pass_manager.py +294 -0
- machine_dialect/mir/tests/test_pass_registration.py +64 -0
- machine_dialect/mir/tests/test_profiling.py +356 -0
- machine_dialect/mir/tests/test_register_allocation.py +307 -0
- machine_dialect/mir/tests/test_report_formatters.py +372 -0
- machine_dialect/mir/tests/test_ssa_construction.py +433 -0
- machine_dialect/mir/tests/test_tail_call.py +236 -0
- machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
- machine_dialect/mir/tests/test_type_narrowing.py +277 -0
- machine_dialect/mir/tests/test_type_specialization.py +421 -0
- machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
- machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
- machine_dialect/mir/type_inference.py +368 -0
- machine_dialect/parser/__init__.py +12 -0
- machine_dialect/parser/enums.py +45 -0
- machine_dialect/parser/parser.py +3655 -0
- machine_dialect/parser/protocols.py +11 -0
- machine_dialect/parser/symbol_table.py +169 -0
- machine_dialect/parser/tests/__init__.py +0 -0
- machine_dialect/parser/tests/helper_functions.py +193 -0
- machine_dialect/parser/tests/test_action_statements.py +334 -0
- machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
- machine_dialect/parser/tests/test_call_statements.py +154 -0
- machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
- machine_dialect/parser/tests/test_collection_mutations.py +264 -0
- machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
- machine_dialect/parser/tests/test_define_integration.py +468 -0
- machine_dialect/parser/tests/test_define_statements.py +311 -0
- machine_dialect/parser/tests/test_dict_extraction.py +115 -0
- machine_dialect/parser/tests/test_empty_literal.py +155 -0
- machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
- machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
- machine_dialect/parser/tests/test_if_empty_block.py +61 -0
- machine_dialect/parser/tests/test_if_statements.py +299 -0
- machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
- machine_dialect/parser/tests/test_infix_expressions.py +680 -0
- machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
- machine_dialect/parser/tests/test_interaction_statements.py +269 -0
- machine_dialect/parser/tests/test_list_literals.py +277 -0
- machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
- machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
- machine_dialect/parser/tests/test_parse_errors.py +114 -0
- machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
- machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
- machine_dialect/parser/tests/test_program.py +13 -0
- machine_dialect/parser/tests/test_return_statements.py +89 -0
- machine_dialect/parser/tests/test_set_statements.py +152 -0
- machine_dialect/parser/tests/test_strict_equality.py +258 -0
- machine_dialect/parser/tests/test_symbol_table.py +217 -0
- machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
- machine_dialect/parser/tests/test_utility_statements.py +423 -0
- machine_dialect/parser/token_buffer.py +159 -0
- machine_dialect/repl/__init__.py +3 -0
- machine_dialect/repl/repl.py +426 -0
- machine_dialect/repl/tests/__init__.py +0 -0
- machine_dialect/repl/tests/test_repl.py +606 -0
- machine_dialect/semantic/__init__.py +12 -0
- machine_dialect/semantic/analyzer.py +906 -0
- machine_dialect/semantic/error_messages.py +189 -0
- machine_dialect/semantic/tests/__init__.py +1 -0
- machine_dialect/semantic/tests/test_analyzer.py +364 -0
- machine_dialect/semantic/tests/test_error_messages.py +104 -0
- machine_dialect/tests/edge_cases/__init__.py +10 -0
- machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
- machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
- machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
- machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
- machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
- machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
- machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
- machine_dialect/tests/integration/test_list_compilation.py +395 -0
- machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
- machine_dialect/type_checking/__init__.py +21 -0
- machine_dialect/type_checking/tests/__init__.py +1 -0
- machine_dialect/type_checking/tests/test_type_system.py +230 -0
- machine_dialect/type_checking/type_system.py +270 -0
- machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
- machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
- machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
- machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
- machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
- machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
- 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
|