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,272 @@
|
|
1
|
+
"""Loop analysis for MIR.
|
2
|
+
|
3
|
+
This module provides loop detection and characterization for
|
4
|
+
optimization passes like LICM and loop unrolling.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from dataclasses import dataclass, field
|
8
|
+
|
9
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
10
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
11
|
+
from machine_dialect.mir.optimization_pass import (
|
12
|
+
FunctionAnalysisPass,
|
13
|
+
PassInfo,
|
14
|
+
PassType,
|
15
|
+
PreservationLevel,
|
16
|
+
)
|
17
|
+
from machine_dialect.mir.ssa_construction import DominanceInfo
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass
|
21
|
+
class Loop:
|
22
|
+
"""Represents a natural loop in the CFG.
|
23
|
+
|
24
|
+
Attributes:
|
25
|
+
header: Loop header block.
|
26
|
+
back_edge: Back edge (latch -> header).
|
27
|
+
blocks: Set of blocks in the loop.
|
28
|
+
exits: Set of exit blocks from the loop.
|
29
|
+
depth: Nesting depth (0 for outermost).
|
30
|
+
parent: Parent loop if nested.
|
31
|
+
children: Child loops if any.
|
32
|
+
"""
|
33
|
+
|
34
|
+
header: BasicBlock
|
35
|
+
back_edge: tuple[BasicBlock, BasicBlock] # (from, to)
|
36
|
+
blocks: set[BasicBlock]
|
37
|
+
exits: set[BasicBlock]
|
38
|
+
depth: int
|
39
|
+
parent: "Loop | None" = None
|
40
|
+
children: list["Loop"] = field(default_factory=list)
|
41
|
+
|
42
|
+
def contains(self, block: BasicBlock) -> bool:
|
43
|
+
"""Check if a block is in this loop.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
block: Block to check.
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
True if block is in the loop.
|
50
|
+
"""
|
51
|
+
return block in self.blocks
|
52
|
+
|
53
|
+
def is_inner_loop(self) -> bool:
|
54
|
+
"""Check if this is an inner loop (no children).
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
True if this is an inner loop.
|
58
|
+
"""
|
59
|
+
return len(self.children) == 0
|
60
|
+
|
61
|
+
|
62
|
+
class LoopInfo:
|
63
|
+
"""Container for loop information in a function."""
|
64
|
+
|
65
|
+
def __init__(self) -> None:
|
66
|
+
"""Initialize loop info."""
|
67
|
+
self.loops: list[Loop] = []
|
68
|
+
self.loop_map: dict[BasicBlock, Loop] = {} # Header -> Loop
|
69
|
+
self.block_to_loop: dict[BasicBlock, Loop] = {} # Block -> Innermost loop
|
70
|
+
|
71
|
+
def get_loop(self, header: BasicBlock) -> Loop | None:
|
72
|
+
"""Get loop by header block.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
header: Loop header block.
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
The loop or None.
|
79
|
+
"""
|
80
|
+
return self.loop_map.get(header)
|
81
|
+
|
82
|
+
def get_innermost_loop(self, block: BasicBlock) -> Loop | None:
|
83
|
+
"""Get the innermost loop containing a block.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
block: The block.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
The innermost loop or None.
|
90
|
+
"""
|
91
|
+
return self.block_to_loop.get(block)
|
92
|
+
|
93
|
+
def get_loops_at_depth(self, depth: int) -> list[Loop]:
|
94
|
+
"""Get all loops at a specific depth.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
depth: Nesting depth.
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
List of loops at that depth.
|
101
|
+
"""
|
102
|
+
return [loop for loop in self.loops if loop.depth == depth]
|
103
|
+
|
104
|
+
def get_inner_loops(self) -> list[Loop]:
|
105
|
+
"""Get all inner loops.
|
106
|
+
|
107
|
+
Returns:
|
108
|
+
List of inner loops.
|
109
|
+
"""
|
110
|
+
return [loop for loop in self.loops if loop.is_inner_loop()]
|
111
|
+
|
112
|
+
|
113
|
+
class LoopAnalysis(FunctionAnalysisPass):
|
114
|
+
"""Analysis pass that identifies loops in the CFG."""
|
115
|
+
|
116
|
+
def get_info(self) -> PassInfo:
|
117
|
+
"""Get pass information.
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
Pass information.
|
121
|
+
"""
|
122
|
+
return PassInfo(
|
123
|
+
name="loop-analysis",
|
124
|
+
description="Identify and characterize loops",
|
125
|
+
pass_type=PassType.ANALYSIS,
|
126
|
+
requires=["dominance"],
|
127
|
+
preserves=PreservationLevel.ALL,
|
128
|
+
)
|
129
|
+
|
130
|
+
def run_on_function(self, function: MIRFunction) -> LoopInfo:
|
131
|
+
"""Analyze loops in a function.
|
132
|
+
|
133
|
+
Args:
|
134
|
+
function: The function to analyze.
|
135
|
+
|
136
|
+
Returns:
|
137
|
+
Loop information.
|
138
|
+
"""
|
139
|
+
loop_info = LoopInfo()
|
140
|
+
|
141
|
+
# Get dominance information
|
142
|
+
dominance = DominanceInfo(function.cfg)
|
143
|
+
|
144
|
+
# Find back edges (edges where target dominates source)
|
145
|
+
back_edges = self._find_back_edges(function, dominance)
|
146
|
+
|
147
|
+
# Build natural loops from back edges
|
148
|
+
for source, target in back_edges:
|
149
|
+
loop = self._build_loop(source, target, dominance)
|
150
|
+
loop_info.loops.append(loop)
|
151
|
+
loop_info.loop_map[target] = loop
|
152
|
+
|
153
|
+
# Determine loop nesting
|
154
|
+
self._determine_nesting(loop_info)
|
155
|
+
|
156
|
+
# Build block-to-loop mapping
|
157
|
+
for loop in loop_info.loops:
|
158
|
+
for block in loop.blocks:
|
159
|
+
# Map to innermost loop
|
160
|
+
if block not in loop_info.block_to_loop:
|
161
|
+
loop_info.block_to_loop[block] = loop
|
162
|
+
else:
|
163
|
+
# Keep the innermost (highest depth)
|
164
|
+
if loop.depth > loop_info.block_to_loop[block].depth:
|
165
|
+
loop_info.block_to_loop[block] = loop
|
166
|
+
|
167
|
+
return loop_info
|
168
|
+
|
169
|
+
def _find_back_edges(
|
170
|
+
self,
|
171
|
+
function: MIRFunction,
|
172
|
+
dominance: DominanceInfo,
|
173
|
+
) -> list[tuple[BasicBlock, BasicBlock]]:
|
174
|
+
"""Find back edges in the CFG.
|
175
|
+
|
176
|
+
Args:
|
177
|
+
function: The function.
|
178
|
+
dominance: Dominance information.
|
179
|
+
|
180
|
+
Returns:
|
181
|
+
List of back edges (source, target).
|
182
|
+
"""
|
183
|
+
back_edges = []
|
184
|
+
|
185
|
+
for block in function.cfg.blocks.values():
|
186
|
+
for succ in block.successors:
|
187
|
+
# Check if successor dominates this block
|
188
|
+
if dominance.dominates(succ, block):
|
189
|
+
back_edges.append((block, succ))
|
190
|
+
|
191
|
+
return back_edges
|
192
|
+
|
193
|
+
def _build_loop(
|
194
|
+
self,
|
195
|
+
latch: BasicBlock,
|
196
|
+
header: BasicBlock,
|
197
|
+
dominance: DominanceInfo,
|
198
|
+
) -> Loop:
|
199
|
+
"""Build a natural loop from a back edge.
|
200
|
+
|
201
|
+
Args:
|
202
|
+
latch: Latch block (source of back edge).
|
203
|
+
header: Header block (target of back edge).
|
204
|
+
dominance: Dominance information.
|
205
|
+
|
206
|
+
Returns:
|
207
|
+
The constructed loop.
|
208
|
+
"""
|
209
|
+
# Natural loop consists of header and all blocks that can
|
210
|
+
# reach latch without going through header
|
211
|
+
loop_blocks = {header, latch}
|
212
|
+
worklist = [latch]
|
213
|
+
|
214
|
+
while worklist:
|
215
|
+
block = worklist.pop()
|
216
|
+
for pred in block.predecessors:
|
217
|
+
if pred not in loop_blocks:
|
218
|
+
loop_blocks.add(pred)
|
219
|
+
worklist.append(pred)
|
220
|
+
|
221
|
+
# Find exit blocks
|
222
|
+
exits = set()
|
223
|
+
for block in loop_blocks:
|
224
|
+
for succ in block.successors:
|
225
|
+
if succ not in loop_blocks:
|
226
|
+
exits.add(succ)
|
227
|
+
|
228
|
+
return Loop(
|
229
|
+
header=header,
|
230
|
+
back_edge=(latch, header),
|
231
|
+
blocks=loop_blocks,
|
232
|
+
exits=exits,
|
233
|
+
depth=0, # Will be set later
|
234
|
+
)
|
235
|
+
|
236
|
+
def _determine_nesting(self, loop_info: LoopInfo) -> None:
|
237
|
+
"""Determine loop nesting relationships.
|
238
|
+
|
239
|
+
Args:
|
240
|
+
loop_info: Loop information to update.
|
241
|
+
"""
|
242
|
+
# Sort loops by size (smaller loops are likely inner)
|
243
|
+
loops = sorted(loop_info.loops, key=lambda loop: len(loop.blocks))
|
244
|
+
|
245
|
+
# Determine parent-child relationships
|
246
|
+
for i, inner in enumerate(loops):
|
247
|
+
for outer in loops[i + 1 :]:
|
248
|
+
# Check if inner is contained in outer
|
249
|
+
if inner.header in outer.blocks and inner.blocks.issubset(outer.blocks):
|
250
|
+
if inner.parent is None:
|
251
|
+
inner.parent = outer
|
252
|
+
outer.children.append(inner)
|
253
|
+
|
254
|
+
# Set depths
|
255
|
+
for loop in loop_info.loops:
|
256
|
+
if loop.parent is None:
|
257
|
+
self._set_depth(loop, 0)
|
258
|
+
|
259
|
+
def _set_depth(self, loop: Loop, depth: int) -> None:
|
260
|
+
"""Set loop depth recursively.
|
261
|
+
|
262
|
+
Args:
|
263
|
+
loop: The loop.
|
264
|
+
depth: Depth to set.
|
265
|
+
"""
|
266
|
+
loop.depth = depth
|
267
|
+
for child in loop.children:
|
268
|
+
self._set_depth(child, depth + 1)
|
269
|
+
|
270
|
+
def finalize(self) -> None:
|
271
|
+
"""Finalize the pass."""
|
272
|
+
pass
|