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,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, [])
|