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,366 @@
|
|
1
|
+
"""Loop unrolling optimization pass.
|
2
|
+
|
3
|
+
This module implements loop unrolling to reduce loop overhead and enable
|
4
|
+
further optimizations by duplicating the loop body multiple times.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from copy import deepcopy
|
8
|
+
|
9
|
+
from machine_dialect.mir.analyses.loop_analysis import Loop
|
10
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
11
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
12
|
+
from machine_dialect.mir.mir_instructions import (
|
13
|
+
BinaryOp,
|
14
|
+
ConditionalJump,
|
15
|
+
Jump,
|
16
|
+
MIRInstruction,
|
17
|
+
)
|
18
|
+
from machine_dialect.mir.mir_transformer import MIRTransformer
|
19
|
+
from machine_dialect.mir.mir_values import Constant, MIRValue
|
20
|
+
from machine_dialect.mir.optimization_pass import (
|
21
|
+
OptimizationPass,
|
22
|
+
PassInfo,
|
23
|
+
PassType,
|
24
|
+
PreservationLevel,
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
class LoopUnrolling(OptimizationPass):
|
29
|
+
"""Unroll small loops to reduce overhead.
|
30
|
+
|
31
|
+
Attributes:
|
32
|
+
unroll_threshold: Maximum unroll factor for loops.
|
33
|
+
max_body_size: Maximum number of instructions in loop body.
|
34
|
+
stats: Dictionary tracking optimization statistics.
|
35
|
+
"""
|
36
|
+
|
37
|
+
def __init__(self) -> None:
|
38
|
+
"""Initialize loop unrolling pass.
|
39
|
+
|
40
|
+
Sets default unroll threshold to 4 and maximum body size to 20
|
41
|
+
instructions. Initializes statistics tracking.
|
42
|
+
"""
|
43
|
+
super().__init__()
|
44
|
+
self.unroll_threshold = 4 # Default unroll factor
|
45
|
+
self.max_body_size = 20 # Maximum instructions in loop body
|
46
|
+
self.stats = {"unrolled": 0, "loops_processed": 0}
|
47
|
+
|
48
|
+
def initialize(self) -> None:
|
49
|
+
"""Initialize the pass before running."""
|
50
|
+
self.stats = {"unrolled": 0, "loops_processed": 0}
|
51
|
+
|
52
|
+
def get_info(self) -> PassInfo:
|
53
|
+
"""Get pass information.
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
PassInfo object describing this optimization pass.
|
57
|
+
"""
|
58
|
+
return PassInfo(
|
59
|
+
name="loop-unrolling",
|
60
|
+
description="Unroll small loops to reduce overhead",
|
61
|
+
pass_type=PassType.OPTIMIZATION,
|
62
|
+
requires=["loop-analysis", "dominance"],
|
63
|
+
preserves=PreservationLevel.CFG,
|
64
|
+
)
|
65
|
+
|
66
|
+
def run_on_function(self, function: MIRFunction) -> bool:
|
67
|
+
"""Run loop unrolling on a function.
|
68
|
+
|
69
|
+
Args:
|
70
|
+
function: The function to optimize.
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
True if the function was modified.
|
74
|
+
"""
|
75
|
+
# Get required analyses
|
76
|
+
loop_info = self.get_analysis("loop-analysis", function)
|
77
|
+
dominance = self.get_analysis("dominance", function)
|
78
|
+
|
79
|
+
if not loop_info or not dominance:
|
80
|
+
return False
|
81
|
+
|
82
|
+
# Get unroll threshold from config if available
|
83
|
+
if hasattr(self, "config") and self.config:
|
84
|
+
self.unroll_threshold = self.config.unroll_threshold
|
85
|
+
|
86
|
+
transformer = MIRTransformer(function)
|
87
|
+
modified = False
|
88
|
+
|
89
|
+
# Process loops from innermost to outermost
|
90
|
+
loops = self._get_loops_in_order(loop_info.loops if hasattr(loop_info, "loops") else [])
|
91
|
+
|
92
|
+
for loop in loops:
|
93
|
+
if self._should_unroll(loop, function):
|
94
|
+
if self._unroll_loop(loop, function, transformer):
|
95
|
+
modified = True
|
96
|
+
self.stats["unrolled"] += 1
|
97
|
+
self.stats["loops_processed"] += 1
|
98
|
+
|
99
|
+
return modified
|
100
|
+
|
101
|
+
def _get_loops_in_order(self, loops: list[Loop]) -> list[Loop]:
|
102
|
+
"""Get loops ordered from innermost to outermost.
|
103
|
+
|
104
|
+
Args:
|
105
|
+
loops: List of loops to order.
|
106
|
+
|
107
|
+
Returns:
|
108
|
+
Loops sorted by depth in descending order.
|
109
|
+
"""
|
110
|
+
return sorted(loops, key=lambda loop: loop.depth, reverse=True)
|
111
|
+
|
112
|
+
def _should_unroll(self, loop: Loop, function: MIRFunction) -> bool:
|
113
|
+
"""Determine if a loop should be unrolled.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
loop: The loop to check.
|
117
|
+
function: The containing function.
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
True if the loop should be unrolled.
|
121
|
+
"""
|
122
|
+
# Check loop size
|
123
|
+
total_instructions = sum(len(block.instructions) for block in loop.blocks)
|
124
|
+
if total_instructions > self.max_body_size:
|
125
|
+
return False
|
126
|
+
|
127
|
+
# Try to determine iteration count
|
128
|
+
iteration_count = self._get_iteration_count(loop, function)
|
129
|
+
if iteration_count is None:
|
130
|
+
return False
|
131
|
+
|
132
|
+
# Only unroll if iteration count is reasonable
|
133
|
+
return 2 <= iteration_count <= self.unroll_threshold * 2
|
134
|
+
|
135
|
+
def _get_iteration_count(self, loop: Loop, function: MIRFunction) -> int | None:
|
136
|
+
"""Try to determine the iteration count of a loop.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
loop: The loop to analyze.
|
140
|
+
function: The containing function.
|
141
|
+
|
142
|
+
Returns:
|
143
|
+
Number of iterations if determinable, None otherwise.
|
144
|
+
|
145
|
+
Note:
|
146
|
+
Currently implements a simple heuristic that looks for
|
147
|
+
patterns like 'i = 0; while i < N; i++' with constant bounds.
|
148
|
+
"""
|
149
|
+
# Simple heuristic: look for loops with constant bounds
|
150
|
+
# This is a simplified version - real implementation would be more sophisticated
|
151
|
+
|
152
|
+
# Look for pattern: i = 0; while i < N; i++
|
153
|
+
header = loop.header
|
154
|
+
|
155
|
+
# Find the loop condition
|
156
|
+
for inst in header.instructions:
|
157
|
+
if isinstance(inst, ConditionalJump):
|
158
|
+
# Check if we're comparing against a constant
|
159
|
+
cond_inst = self._find_defining_instruction(inst.condition, header)
|
160
|
+
if isinstance(cond_inst, BinaryOp) and cond_inst.op in ["<", "<=", ">", ">="]:
|
161
|
+
# Check if one operand is a constant
|
162
|
+
if isinstance(cond_inst.right, Constant) and isinstance(cond_inst.right.value, int):
|
163
|
+
# Simple case: comparing against constant
|
164
|
+
limit = cond_inst.right.value
|
165
|
+
# Assume starting from 0 for now
|
166
|
+
if cond_inst.op == "<":
|
167
|
+
return limit
|
168
|
+
elif cond_inst.op == "<=":
|
169
|
+
return limit + 1
|
170
|
+
|
171
|
+
return None
|
172
|
+
|
173
|
+
def _find_defining_instruction(self, value: MIRValue, block: BasicBlock) -> MIRInstruction | None:
|
174
|
+
"""Find the instruction that defines a value.
|
175
|
+
|
176
|
+
Args:
|
177
|
+
value: The value whose definition to find.
|
178
|
+
block: The basic block to search in.
|
179
|
+
|
180
|
+
Returns:
|
181
|
+
The instruction that defines the value, or None if not found.
|
182
|
+
"""
|
183
|
+
for inst in block.instructions:
|
184
|
+
if value in inst.get_defs():
|
185
|
+
return inst
|
186
|
+
return None
|
187
|
+
|
188
|
+
def _unroll_loop(self, loop: Loop, function: MIRFunction, transformer: MIRTransformer) -> bool:
|
189
|
+
"""Unroll a loop by the unroll factor.
|
190
|
+
|
191
|
+
Args:
|
192
|
+
loop: The loop to unroll.
|
193
|
+
function: The containing function.
|
194
|
+
transformer: MIR transformer for applying changes.
|
195
|
+
|
196
|
+
Returns:
|
197
|
+
True if the loop was successfully unrolled.
|
198
|
+
|
199
|
+
Note:
|
200
|
+
Implements partial unrolling by duplicating the loop body
|
201
|
+
up to the unroll factor, limited to 4 iterations.
|
202
|
+
"""
|
203
|
+
unroll_factor = min(self.unroll_threshold, 4) # Limit unrolling
|
204
|
+
|
205
|
+
# For simplicity, we'll implement partial unrolling
|
206
|
+
# Duplicate the loop body N times
|
207
|
+
|
208
|
+
# Find the loop body (excluding header)
|
209
|
+
loop_body_blocks = [b for b in loop.blocks if b != loop.header]
|
210
|
+
|
211
|
+
if not loop_body_blocks:
|
212
|
+
return False
|
213
|
+
|
214
|
+
# Create copies of the loop body
|
215
|
+
new_blocks = []
|
216
|
+
for i in range(1, unroll_factor):
|
217
|
+
# Clone each body block
|
218
|
+
for block in loop_body_blocks:
|
219
|
+
new_block = self._clone_block(block, f"_unroll_{i}")
|
220
|
+
new_blocks.append(new_block)
|
221
|
+
function.cfg.add_block(new_block)
|
222
|
+
|
223
|
+
# Connect the cloned blocks
|
224
|
+
if new_blocks:
|
225
|
+
# Update control flow
|
226
|
+
self._connect_unrolled_blocks(loop, new_blocks, loop_body_blocks, unroll_factor)
|
227
|
+
|
228
|
+
# Update loop increment
|
229
|
+
self._update_loop_increment(loop, unroll_factor)
|
230
|
+
|
231
|
+
transformer.modified = True
|
232
|
+
return True
|
233
|
+
|
234
|
+
return False
|
235
|
+
|
236
|
+
def _clone_block(self, block: BasicBlock, suffix: str) -> BasicBlock:
|
237
|
+
"""Clone a basic block with a new label.
|
238
|
+
|
239
|
+
Args:
|
240
|
+
block: Basic block to clone.
|
241
|
+
suffix: Suffix to append to the new block's label.
|
242
|
+
|
243
|
+
Returns:
|
244
|
+
New BasicBlock instance with cloned instructions.
|
245
|
+
"""
|
246
|
+
new_label = block.label + suffix
|
247
|
+
new_block = BasicBlock(new_label)
|
248
|
+
|
249
|
+
# Clone instructions
|
250
|
+
for inst in block.instructions:
|
251
|
+
new_inst = self._clone_instruction(inst, suffix)
|
252
|
+
new_block.instructions.append(new_inst)
|
253
|
+
|
254
|
+
return new_block
|
255
|
+
|
256
|
+
def _clone_instruction(self, inst: MIRInstruction, suffix: str) -> MIRInstruction:
|
257
|
+
"""Clone an instruction, updating labels.
|
258
|
+
|
259
|
+
Args:
|
260
|
+
inst: Instruction to clone.
|
261
|
+
suffix: Suffix for updating labels.
|
262
|
+
|
263
|
+
Returns:
|
264
|
+
Deep copy of the instruction.
|
265
|
+
|
266
|
+
Note:
|
267
|
+
Currently preserves original jump targets. A more complete
|
268
|
+
implementation would update these based on the suffix.
|
269
|
+
"""
|
270
|
+
# Deep copy the instruction
|
271
|
+
new_inst = deepcopy(inst)
|
272
|
+
|
273
|
+
# Update jump targets
|
274
|
+
if isinstance(new_inst, Jump):
|
275
|
+
# Keep original target for now
|
276
|
+
pass
|
277
|
+
elif isinstance(new_inst, ConditionalJump):
|
278
|
+
# Keep original targets for now
|
279
|
+
pass
|
280
|
+
|
281
|
+
return new_inst
|
282
|
+
|
283
|
+
def _connect_unrolled_blocks(
|
284
|
+
self,
|
285
|
+
loop: Loop,
|
286
|
+
new_blocks: list[BasicBlock],
|
287
|
+
original_body: list[BasicBlock],
|
288
|
+
unroll_factor: int,
|
289
|
+
) -> None:
|
290
|
+
"""Connect the unrolled blocks properly.
|
291
|
+
|
292
|
+
Args:
|
293
|
+
loop: The original loop.
|
294
|
+
new_blocks: New unrolled blocks to connect.
|
295
|
+
original_body: Original loop body blocks.
|
296
|
+
unroll_factor: Number of times the loop was unrolled.
|
297
|
+
|
298
|
+
Note:
|
299
|
+
This is a simplified implementation that chains blocks
|
300
|
+
sequentially. A complete implementation would handle
|
301
|
+
complex control flow graphs.
|
302
|
+
"""
|
303
|
+
# This is simplified - real implementation would handle complex CFGs
|
304
|
+
# For now, we just chain the blocks sequentially
|
305
|
+
|
306
|
+
# blocks_per_iteration = len(original_body) # Not used currently
|
307
|
+
|
308
|
+
# Connect original body to first unrolled copy
|
309
|
+
if original_body and new_blocks:
|
310
|
+
last_original = original_body[-1]
|
311
|
+
first_new = new_blocks[0]
|
312
|
+
|
313
|
+
# Update jump at end of original body
|
314
|
+
if last_original.instructions:
|
315
|
+
last_inst = last_original.instructions[-1]
|
316
|
+
if isinstance(last_inst, Jump):
|
317
|
+
# Instead of jumping back to header, jump to unrolled copy
|
318
|
+
last_inst.label = first_new.label
|
319
|
+
|
320
|
+
# Connect unrolled copies to each other
|
321
|
+
for i in range(len(new_blocks) - 1):
|
322
|
+
current_block = new_blocks[i]
|
323
|
+
next_block = new_blocks[i + 1]
|
324
|
+
|
325
|
+
if current_block.instructions:
|
326
|
+
last_inst = current_block.instructions[-1]
|
327
|
+
if isinstance(last_inst, Jump):
|
328
|
+
last_inst.label = next_block.label
|
329
|
+
|
330
|
+
def _update_loop_increment(self, loop: Loop, unroll_factor: int) -> None:
|
331
|
+
"""Update the loop increment to account for unrolling.
|
332
|
+
|
333
|
+
Args:
|
334
|
+
loop: The loop whose increment to update.
|
335
|
+
unroll_factor: Number of times the loop was unrolled.
|
336
|
+
|
337
|
+
Note:
|
338
|
+
Searches for increment patterns like 'i = i + 1' and
|
339
|
+
updates them to increment by the unroll factor.
|
340
|
+
"""
|
341
|
+
# Find and update the loop counter increment
|
342
|
+
# This is simplified - real implementation would be more sophisticated
|
343
|
+
|
344
|
+
for block in loop.blocks:
|
345
|
+
for inst in block.instructions:
|
346
|
+
# Look for increment pattern: i = i + 1
|
347
|
+
if isinstance(inst, BinaryOp) and inst.op == "+":
|
348
|
+
if isinstance(inst.right, Constant) and inst.right.value == 1:
|
349
|
+
# Update to increment by unroll_factor
|
350
|
+
new_const = Constant(unroll_factor)
|
351
|
+
inst.right = new_const
|
352
|
+
|
353
|
+
def finalize(self) -> None:
|
354
|
+
"""Finalize the pass.
|
355
|
+
|
356
|
+
Currently performs no finalization actions.
|
357
|
+
"""
|
358
|
+
pass
|
359
|
+
|
360
|
+
def get_statistics(self) -> dict[str, int]:
|
361
|
+
"""Get optimization statistics.
|
362
|
+
|
363
|
+
Returns:
|
364
|
+
Dictionary of statistics.
|
365
|
+
"""
|
366
|
+
return self.stats
|