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,554 @@
|
|
1
|
+
"""Comprehensive tests for optimization_pipeline module.
|
2
|
+
|
3
|
+
Tests all aspects of the optimization pipeline including optimization levels,
|
4
|
+
pipeline configuration, and custom pipeline building.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Any
|
8
|
+
from unittest.mock import MagicMock, patch
|
9
|
+
|
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
|
+
LoadConst,
|
15
|
+
Return,
|
16
|
+
)
|
17
|
+
from machine_dialect.mir.mir_module import MIRModule
|
18
|
+
from machine_dialect.mir.mir_types import MIRType
|
19
|
+
from machine_dialect.mir.mir_values import Constant, Temp
|
20
|
+
from machine_dialect.mir.optimization_pass import ModulePass, PassInfo, PassType, PreservationLevel
|
21
|
+
from machine_dialect.mir.optimization_pipeline import (
|
22
|
+
OptimizationLevel,
|
23
|
+
OptimizationPipeline,
|
24
|
+
PipelineBuilder,
|
25
|
+
create_o0_pipeline,
|
26
|
+
create_o1_pipeline,
|
27
|
+
create_o2_pipeline,
|
28
|
+
create_o3_pipeline,
|
29
|
+
create_size_pipeline,
|
30
|
+
)
|
31
|
+
from machine_dialect.mir.optimizations.inlining import FunctionInlining
|
32
|
+
|
33
|
+
|
34
|
+
class MockPass(ModulePass):
|
35
|
+
"""Mock pass for testing."""
|
36
|
+
|
37
|
+
def __init__(self, name: str = "mock-pass", modified: bool = True) -> None:
|
38
|
+
"""Initialize mock pass.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
name: Name of the pass.
|
42
|
+
modified: Whether the pass modifies the module.
|
43
|
+
"""
|
44
|
+
super().__init__()
|
45
|
+
self._name = name
|
46
|
+
self._modified = modified
|
47
|
+
self.run_count = 0
|
48
|
+
self.stats = {"test_stat": 42}
|
49
|
+
|
50
|
+
def get_info(self) -> PassInfo:
|
51
|
+
"""Get pass information.
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
Pass information.
|
55
|
+
"""
|
56
|
+
return PassInfo(
|
57
|
+
name=self._name,
|
58
|
+
description="Mock pass for testing",
|
59
|
+
pass_type=PassType.OPTIMIZATION,
|
60
|
+
requires=[],
|
61
|
+
preserves=PreservationLevel.ALL,
|
62
|
+
)
|
63
|
+
|
64
|
+
def run_on_module(self, module: MIRModule) -> bool:
|
65
|
+
"""Run the pass on a module.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
module: The module to run on.
|
69
|
+
|
70
|
+
Returns:
|
71
|
+
Whether the module was modified.
|
72
|
+
"""
|
73
|
+
self.run_count += 1
|
74
|
+
return self._modified
|
75
|
+
|
76
|
+
def finalize(self) -> None:
|
77
|
+
"""Finalize the pass."""
|
78
|
+
pass
|
79
|
+
|
80
|
+
def get_statistics(self) -> dict[str, Any]:
|
81
|
+
"""Get pass statistics.
|
82
|
+
|
83
|
+
Returns:
|
84
|
+
Dictionary of statistics.
|
85
|
+
"""
|
86
|
+
return self.stats
|
87
|
+
|
88
|
+
|
89
|
+
def create_test_module() -> MIRModule:
|
90
|
+
"""Create a test module with optimization opportunities.
|
91
|
+
|
92
|
+
Returns:
|
93
|
+
A test MIR module.
|
94
|
+
"""
|
95
|
+
module = MIRModule("test")
|
96
|
+
|
97
|
+
# Create main function with constant folding opportunities
|
98
|
+
main_func = MIRFunction("main")
|
99
|
+
|
100
|
+
# Create basic blocks
|
101
|
+
entry = BasicBlock("entry")
|
102
|
+
main_func.cfg.add_block(entry)
|
103
|
+
main_func.cfg.set_entry_block(entry)
|
104
|
+
|
105
|
+
# Add instructions with optimization opportunities
|
106
|
+
t0 = Temp(MIRType.INT, 0)
|
107
|
+
t1 = Temp(MIRType.INT, 1)
|
108
|
+
t2 = Temp(MIRType.INT, 2)
|
109
|
+
t3 = Temp(MIRType.INT, 3)
|
110
|
+
t4 = Temp(MIRType.INT, 4)
|
111
|
+
|
112
|
+
# Constant folding opportunity: 2 + 3 = 5
|
113
|
+
entry.add_instruction(LoadConst(t0, Constant(2, MIRType.INT), (1, 1)))
|
114
|
+
entry.add_instruction(LoadConst(t1, Constant(3, MIRType.INT), (1, 1)))
|
115
|
+
entry.add_instruction(BinaryOp(t2, "+", t0, t1, (1, 1)))
|
116
|
+
|
117
|
+
# Strength reduction opportunity: x * 4 -> x << 2
|
118
|
+
entry.add_instruction(LoadConst(t3, Constant(4, MIRType.INT), (1, 1)))
|
119
|
+
entry.add_instruction(BinaryOp(t4, "*", t2, t3, (1, 1)))
|
120
|
+
|
121
|
+
# Return result
|
122
|
+
entry.add_instruction(Return((1, 1), t4))
|
123
|
+
|
124
|
+
module.add_function(main_func)
|
125
|
+
return module
|
126
|
+
|
127
|
+
|
128
|
+
class TestOptimizationLevel:
|
129
|
+
"""Tests for OptimizationLevel enum."""
|
130
|
+
|
131
|
+
def test_optimization_levels(self) -> None:
|
132
|
+
"""Test all optimization level values."""
|
133
|
+
assert OptimizationLevel.O0.value == "O0"
|
134
|
+
assert OptimizationLevel.O1.value == "O1"
|
135
|
+
assert OptimizationLevel.O2.value == "O2"
|
136
|
+
assert OptimizationLevel.O3.value == "O3"
|
137
|
+
assert OptimizationLevel.Os.value == "Os"
|
138
|
+
|
139
|
+
def test_optimization_level_members(self) -> None:
|
140
|
+
"""Test that all expected optimization levels exist."""
|
141
|
+
levels = list(OptimizationLevel)
|
142
|
+
assert len(levels) == 5
|
143
|
+
assert OptimizationLevel.O0 in levels
|
144
|
+
assert OptimizationLevel.O1 in levels
|
145
|
+
assert OptimizationLevel.O2 in levels
|
146
|
+
assert OptimizationLevel.O3 in levels
|
147
|
+
assert OptimizationLevel.Os in levels
|
148
|
+
|
149
|
+
|
150
|
+
class TestOptimizationPipeline:
|
151
|
+
"""Tests for OptimizationPipeline class."""
|
152
|
+
|
153
|
+
def test_initialization(self) -> None:
|
154
|
+
"""Test pipeline initialization."""
|
155
|
+
pipeline = OptimizationPipeline()
|
156
|
+
assert pipeline.pass_manager is not None
|
157
|
+
assert isinstance(pipeline.stats, dict)
|
158
|
+
assert len(pipeline.stats) == 0
|
159
|
+
|
160
|
+
def test_register_all_passes(self) -> None:
|
161
|
+
"""Test that all passes are registered."""
|
162
|
+
pipeline = OptimizationPipeline()
|
163
|
+
# Check that passes were registered (indirectly through registry)
|
164
|
+
assert pipeline.pass_manager is not None
|
165
|
+
|
166
|
+
def test_get_passes_for_o0(self) -> None:
|
167
|
+
"""Test O0 optimization level (no optimization)."""
|
168
|
+
pipeline = OptimizationPipeline()
|
169
|
+
passes = pipeline.get_passes_for_level(OptimizationLevel.O0)
|
170
|
+
assert len(passes) == 0
|
171
|
+
|
172
|
+
def test_get_passes_for_o1(self) -> None:
|
173
|
+
"""Test O1 optimization level (basic optimization)."""
|
174
|
+
pipeline = OptimizationPipeline()
|
175
|
+
passes = pipeline.get_passes_for_level(OptimizationLevel.O1)
|
176
|
+
|
177
|
+
# O1 should include basic passes
|
178
|
+
assert len(passes) > 0
|
179
|
+
# Check pass names (can be MockPass or None)
|
180
|
+
pass_names = [p.get_info().name if p else None for p in passes]
|
181
|
+
# Filter out None values
|
182
|
+
pass_names = [name for name in pass_names if name is not None]
|
183
|
+
assert len(pass_names) > 0
|
184
|
+
|
185
|
+
def test_get_passes_for_o2(self) -> None:
|
186
|
+
"""Test O2 optimization level (standard optimization)."""
|
187
|
+
pipeline = OptimizationPipeline()
|
188
|
+
passes = pipeline.get_passes_for_level(OptimizationLevel.O2)
|
189
|
+
|
190
|
+
# O2 should include more passes
|
191
|
+
assert len(passes) > 0
|
192
|
+
|
193
|
+
# Check for inlining pass
|
194
|
+
inlining_passes = [p for p in passes if isinstance(p, FunctionInlining)]
|
195
|
+
assert len(inlining_passes) == 1
|
196
|
+
assert inlining_passes[0].size_threshold == 30
|
197
|
+
|
198
|
+
def test_get_passes_for_o3(self) -> None:
|
199
|
+
"""Test O3 optimization level (aggressive optimization)."""
|
200
|
+
pipeline = OptimizationPipeline()
|
201
|
+
passes = pipeline.get_passes_for_level(OptimizationLevel.O3)
|
202
|
+
|
203
|
+
# O3 should include aggressive inlining
|
204
|
+
inlining_passes = [p for p in passes if isinstance(p, FunctionInlining)]
|
205
|
+
assert len(inlining_passes) == 1
|
206
|
+
assert inlining_passes[0].size_threshold == 100 # More aggressive than O2
|
207
|
+
|
208
|
+
# O3 should have more passes than O2
|
209
|
+
o2_passes = pipeline.get_passes_for_level(OptimizationLevel.O2)
|
210
|
+
assert len(passes) >= len(o2_passes)
|
211
|
+
|
212
|
+
def test_get_passes_for_os(self) -> None:
|
213
|
+
"""Test Os optimization level (optimize for size)."""
|
214
|
+
pipeline = OptimizationPipeline()
|
215
|
+
passes = pipeline.get_passes_for_level(OptimizationLevel.Os)
|
216
|
+
|
217
|
+
# Os should NOT include inlining (increases size)
|
218
|
+
inlining_passes = [p for p in passes if isinstance(p, FunctionInlining)]
|
219
|
+
assert len(inlining_passes) == 0
|
220
|
+
|
221
|
+
# Should have some optimization passes
|
222
|
+
assert len(passes) > 0
|
223
|
+
|
224
|
+
def test_optimize_o0(self) -> None:
|
225
|
+
"""Test optimization with O0 level."""
|
226
|
+
pipeline = OptimizationPipeline()
|
227
|
+
module = create_test_module()
|
228
|
+
|
229
|
+
modified = pipeline.optimize(module, OptimizationLevel.O0)
|
230
|
+
|
231
|
+
# O0 should not modify the module
|
232
|
+
assert not modified
|
233
|
+
assert pipeline.stats["level"] == "O0"
|
234
|
+
assert len(pipeline.stats["passes_run"]) == 0
|
235
|
+
assert pipeline.stats["total_modifications"] == 0
|
236
|
+
|
237
|
+
def test_optimize_with_modifications(self) -> None:
|
238
|
+
"""Test optimization that modifies the module."""
|
239
|
+
pipeline = OptimizationPipeline()
|
240
|
+
module = create_test_module()
|
241
|
+
|
242
|
+
# Create mock passes
|
243
|
+
mock_pass1 = MockPass("pass1", modified=True)
|
244
|
+
mock_pass2 = MockPass("pass2", modified=False)
|
245
|
+
mock_pass3 = MockPass("pass3", modified=True)
|
246
|
+
|
247
|
+
# Mock get_passes_for_level to return our mock passes
|
248
|
+
with patch.object(pipeline, "get_passes_for_level", return_value=[mock_pass1, mock_pass2, mock_pass3]):
|
249
|
+
modified = pipeline.optimize(module, OptimizationLevel.O1)
|
250
|
+
|
251
|
+
assert modified
|
252
|
+
assert pipeline.stats["level"] == "O1"
|
253
|
+
assert pipeline.stats["passes_run"] == ["pass1", "pass2", "pass3"]
|
254
|
+
assert pipeline.stats["total_modifications"] == 2 # pass1 and pass3 modified
|
255
|
+
assert "pass1" in pipeline.stats["pass_stats"]
|
256
|
+
assert "pass2" in pipeline.stats["pass_stats"]
|
257
|
+
assert "pass3" in pipeline.stats["pass_stats"]
|
258
|
+
|
259
|
+
def test_optimize_with_custom_pipeline(self) -> None:
|
260
|
+
"""Test optimization with custom pipeline."""
|
261
|
+
pipeline = OptimizationPipeline()
|
262
|
+
module = create_test_module()
|
263
|
+
|
264
|
+
# Mock the pass manager registry
|
265
|
+
mock_registry = MagicMock()
|
266
|
+
mock_pass1 = MockPass("custom-pass1", modified=True)
|
267
|
+
mock_pass2 = MockPass("custom-pass2", modified=False)
|
268
|
+
|
269
|
+
def get_pass(name: str) -> ModulePass | None:
|
270
|
+
if name == "custom-pass1":
|
271
|
+
return mock_pass1
|
272
|
+
elif name == "custom-pass2":
|
273
|
+
return mock_pass2
|
274
|
+
return None
|
275
|
+
|
276
|
+
mock_registry.get_pass = get_pass
|
277
|
+
pipeline.pass_manager.registry = mock_registry
|
278
|
+
|
279
|
+
modified = pipeline.optimize_with_custom_pipeline(module, ["custom-pass1", "custom-pass2", "nonexistent-pass"])
|
280
|
+
|
281
|
+
assert modified
|
282
|
+
assert mock_pass1.run_count == 1
|
283
|
+
assert mock_pass2.run_count == 1
|
284
|
+
|
285
|
+
def test_get_statistics(self) -> None:
|
286
|
+
"""Test getting optimization statistics."""
|
287
|
+
pipeline = OptimizationPipeline()
|
288
|
+
|
289
|
+
# Initially empty
|
290
|
+
stats = pipeline.get_statistics()
|
291
|
+
assert stats == {}
|
292
|
+
|
293
|
+
# After optimization
|
294
|
+
module = create_test_module()
|
295
|
+
pipeline.optimize(module, OptimizationLevel.O0)
|
296
|
+
|
297
|
+
stats = pipeline.get_statistics()
|
298
|
+
assert stats["level"] == "O0"
|
299
|
+
assert "passes_run" in stats
|
300
|
+
assert "total_modifications" in stats
|
301
|
+
assert "pass_stats" in stats
|
302
|
+
|
303
|
+
|
304
|
+
class TestPipelineBuilder:
|
305
|
+
"""Tests for PipelineBuilder class."""
|
306
|
+
|
307
|
+
def test_initialization(self) -> None:
|
308
|
+
"""Test builder initialization."""
|
309
|
+
builder = PipelineBuilder()
|
310
|
+
assert builder.passes == []
|
311
|
+
assert builder.pass_configs == {}
|
312
|
+
|
313
|
+
def test_add_pass(self) -> None:
|
314
|
+
"""Test adding a pass."""
|
315
|
+
builder = PipelineBuilder()
|
316
|
+
result = builder.add_pass("test-pass")
|
317
|
+
|
318
|
+
assert result is builder # Returns self for chaining
|
319
|
+
assert "test-pass" in builder.passes
|
320
|
+
assert "test-pass" not in builder.pass_configs
|
321
|
+
|
322
|
+
def test_add_pass_with_config(self) -> None:
|
323
|
+
"""Test adding a pass with configuration."""
|
324
|
+
builder = PipelineBuilder()
|
325
|
+
result = builder.add_pass("test-pass", threshold=10, enabled=True)
|
326
|
+
|
327
|
+
assert result is builder
|
328
|
+
assert "test-pass" in builder.passes
|
329
|
+
assert "test-pass" in builder.pass_configs
|
330
|
+
assert builder.pass_configs["test-pass"]["threshold"] == 10
|
331
|
+
assert builder.pass_configs["test-pass"]["enabled"] is True
|
332
|
+
|
333
|
+
def test_add_cleanup_passes(self) -> None:
|
334
|
+
"""Test adding cleanup passes."""
|
335
|
+
builder = PipelineBuilder()
|
336
|
+
result = builder.add_cleanup_passes()
|
337
|
+
|
338
|
+
assert result is builder
|
339
|
+
assert "dce" in builder.passes
|
340
|
+
assert "jump-threading" in builder.passes
|
341
|
+
assert "peephole" in builder.passes
|
342
|
+
|
343
|
+
def test_add_algebraic_passes(self) -> None:
|
344
|
+
"""Test adding algebraic optimization passes."""
|
345
|
+
builder = PipelineBuilder()
|
346
|
+
result = builder.add_algebraic_passes()
|
347
|
+
|
348
|
+
assert result is builder
|
349
|
+
assert "constant-propagation" in builder.passes
|
350
|
+
assert "strength-reduction" in builder.passes
|
351
|
+
assert "cse" in builder.passes
|
352
|
+
|
353
|
+
def test_add_loop_passes(self) -> None:
|
354
|
+
"""Test adding loop optimization passes."""
|
355
|
+
builder = PipelineBuilder()
|
356
|
+
result = builder.add_loop_passes()
|
357
|
+
|
358
|
+
assert result is builder
|
359
|
+
assert "licm" in builder.passes
|
360
|
+
|
361
|
+
def test_repeat(self) -> None:
|
362
|
+
"""Test repeating the pipeline."""
|
363
|
+
builder = PipelineBuilder()
|
364
|
+
builder.add_pass("pass1").add_pass("pass2")
|
365
|
+
|
366
|
+
result = builder.repeat(3)
|
367
|
+
|
368
|
+
assert result is builder
|
369
|
+
assert builder.passes == ["pass1", "pass2", "pass1", "pass2", "pass1", "pass2"]
|
370
|
+
|
371
|
+
def test_repeat_once(self) -> None:
|
372
|
+
"""Test repeat with times=1 (no repetition)."""
|
373
|
+
builder = PipelineBuilder()
|
374
|
+
builder.add_pass("pass1").add_pass("pass2")
|
375
|
+
|
376
|
+
result = builder.repeat(1)
|
377
|
+
|
378
|
+
assert result is builder
|
379
|
+
assert builder.passes == ["pass1", "pass2"]
|
380
|
+
|
381
|
+
def test_build(self) -> None:
|
382
|
+
"""Test building the pipeline."""
|
383
|
+
builder = PipelineBuilder()
|
384
|
+
builder.add_pass("pass1").add_pass("pass2")
|
385
|
+
|
386
|
+
result = builder.build()
|
387
|
+
|
388
|
+
assert result == ["pass1", "pass2"]
|
389
|
+
assert result is not builder.passes # Should be a copy
|
390
|
+
|
391
|
+
def test_chaining(self) -> None:
|
392
|
+
"""Test method chaining."""
|
393
|
+
builder = PipelineBuilder()
|
394
|
+
|
395
|
+
result = builder.add_pass("inline").add_algebraic_passes().add_loop_passes().add_cleanup_passes().repeat(2)
|
396
|
+
|
397
|
+
assert result is builder
|
398
|
+
expected = [
|
399
|
+
"inline",
|
400
|
+
"constant-propagation",
|
401
|
+
"strength-reduction",
|
402
|
+
"cse",
|
403
|
+
"licm",
|
404
|
+
"dce",
|
405
|
+
"jump-threading",
|
406
|
+
"peephole",
|
407
|
+
"inline",
|
408
|
+
"constant-propagation",
|
409
|
+
"strength-reduction",
|
410
|
+
"cse",
|
411
|
+
"licm",
|
412
|
+
"dce",
|
413
|
+
"jump-threading",
|
414
|
+
"peephole",
|
415
|
+
]
|
416
|
+
assert builder.passes == expected
|
417
|
+
|
418
|
+
def test_complex_pipeline(self) -> None:
|
419
|
+
"""Test building a complex custom pipeline."""
|
420
|
+
builder = PipelineBuilder()
|
421
|
+
builder.add_pass("inline", size_threshold=50)
|
422
|
+
builder.add_algebraic_passes()
|
423
|
+
builder.add_pass("inline", size_threshold=100)
|
424
|
+
builder.add_loop_passes()
|
425
|
+
builder.add_cleanup_passes()
|
426
|
+
pipeline = builder.build()
|
427
|
+
|
428
|
+
# inline + 3 algebraic + inline + 1 loop + 3 cleanup = 9 total
|
429
|
+
assert len(pipeline) == 9
|
430
|
+
assert pipeline[0] == "inline"
|
431
|
+
assert pipeline[4] == "inline"
|
432
|
+
assert pipeline[-1] == "peephole"
|
433
|
+
|
434
|
+
|
435
|
+
class TestConvenienceFunctions:
|
436
|
+
"""Tests for convenience functions."""
|
437
|
+
|
438
|
+
def test_create_o0_pipeline(self) -> None:
|
439
|
+
"""Test creating O0 pipeline."""
|
440
|
+
pipeline = create_o0_pipeline()
|
441
|
+
assert isinstance(pipeline, OptimizationPipeline)
|
442
|
+
assert pipeline.pass_manager is not None
|
443
|
+
|
444
|
+
def test_create_o1_pipeline(self) -> None:
|
445
|
+
"""Test creating O1 pipeline."""
|
446
|
+
pipeline = create_o1_pipeline()
|
447
|
+
assert isinstance(pipeline, OptimizationPipeline)
|
448
|
+
assert pipeline.pass_manager is not None
|
449
|
+
|
450
|
+
def test_create_o2_pipeline(self) -> None:
|
451
|
+
"""Test creating O2 pipeline."""
|
452
|
+
pipeline = create_o2_pipeline()
|
453
|
+
assert isinstance(pipeline, OptimizationPipeline)
|
454
|
+
assert pipeline.pass_manager is not None
|
455
|
+
|
456
|
+
def test_create_o3_pipeline(self) -> None:
|
457
|
+
"""Test creating O3 pipeline."""
|
458
|
+
pipeline = create_o3_pipeline()
|
459
|
+
assert isinstance(pipeline, OptimizationPipeline)
|
460
|
+
assert pipeline.pass_manager is not None
|
461
|
+
|
462
|
+
def test_create_size_pipeline(self) -> None:
|
463
|
+
"""Test creating size optimization pipeline."""
|
464
|
+
pipeline = create_size_pipeline()
|
465
|
+
assert isinstance(pipeline, OptimizationPipeline)
|
466
|
+
assert pipeline.pass_manager is not None
|
467
|
+
|
468
|
+
def test_all_pipelines_can_optimize(self) -> None:
|
469
|
+
"""Test that all convenience pipelines can run optimization."""
|
470
|
+
create_test_module() # Validate it can create a module
|
471
|
+
|
472
|
+
pipelines = [
|
473
|
+
(create_o0_pipeline(), OptimizationLevel.O0),
|
474
|
+
(create_o1_pipeline(), OptimizationLevel.O1),
|
475
|
+
(create_o2_pipeline(), OptimizationLevel.O2),
|
476
|
+
(create_o3_pipeline(), OptimizationLevel.O3),
|
477
|
+
(create_size_pipeline(), OptimizationLevel.Os),
|
478
|
+
]
|
479
|
+
|
480
|
+
for pipeline, level in pipelines:
|
481
|
+
test_module = create_test_module() # Fresh module for each test
|
482
|
+
# Should not raise any exceptions
|
483
|
+
pipeline.optimize(test_module, level)
|
484
|
+
stats = pipeline.get_statistics()
|
485
|
+
assert stats["level"] == level.value
|
486
|
+
|
487
|
+
|
488
|
+
class TestEdgeCases:
|
489
|
+
"""Tests for edge cases and error conditions."""
|
490
|
+
|
491
|
+
def test_empty_custom_pipeline(self) -> None:
|
492
|
+
"""Test custom pipeline with empty pass list."""
|
493
|
+
pipeline = OptimizationPipeline()
|
494
|
+
module = create_test_module()
|
495
|
+
|
496
|
+
modified = pipeline.optimize_with_custom_pipeline(module, [])
|
497
|
+
|
498
|
+
assert not modified
|
499
|
+
|
500
|
+
def test_nonexistent_passes_in_custom_pipeline(self) -> None:
|
501
|
+
"""Test custom pipeline with only nonexistent passes."""
|
502
|
+
pipeline = OptimizationPipeline()
|
503
|
+
module = create_test_module()
|
504
|
+
|
505
|
+
# Mock registry to return None for all passes
|
506
|
+
mock_registry = MagicMock()
|
507
|
+
mock_registry.get_pass.return_value = None
|
508
|
+
pipeline.pass_manager.registry = mock_registry
|
509
|
+
|
510
|
+
modified = pipeline.optimize_with_custom_pipeline(module, ["nonexistent1", "nonexistent2"])
|
511
|
+
|
512
|
+
assert not modified
|
513
|
+
|
514
|
+
def test_pipeline_builder_empty_repeat(self) -> None:
|
515
|
+
"""Test repeating an empty pipeline."""
|
516
|
+
builder = PipelineBuilder()
|
517
|
+
result = builder.repeat(5)
|
518
|
+
|
519
|
+
assert result is builder
|
520
|
+
assert builder.passes == []
|
521
|
+
|
522
|
+
def test_pipeline_builder_zero_repeat(self) -> None:
|
523
|
+
"""Test repeat with times=0."""
|
524
|
+
builder = PipelineBuilder()
|
525
|
+
builder.add_pass("pass1")
|
526
|
+
|
527
|
+
# Repeat 0 times should remove all passes
|
528
|
+
result = builder.repeat(0)
|
529
|
+
|
530
|
+
assert result is builder
|
531
|
+
# After repeat(0), we should have no passes since (times - 1) = -1 means no extension
|
532
|
+
assert builder.passes == ["pass1"] # Original pass remains, no additional copies
|
533
|
+
|
534
|
+
def test_pass_without_get_statistics(self) -> None:
|
535
|
+
"""Test handling passes without get_statistics method."""
|
536
|
+
pipeline = OptimizationPipeline()
|
537
|
+
module = create_test_module()
|
538
|
+
|
539
|
+
# Create a mock pass without get_statistics
|
540
|
+
mock_pass = MagicMock(spec=ModulePass)
|
541
|
+
mock_pass.get_info.return_value = PassInfo(
|
542
|
+
"test", "Test pass", PassType.OPTIMIZATION, [], PreservationLevel.ALL
|
543
|
+
)
|
544
|
+
mock_pass.run_on_module.return_value = True
|
545
|
+
# Delete get_statistics to simulate it not existing
|
546
|
+
del mock_pass.get_statistics
|
547
|
+
|
548
|
+
with patch.object(pipeline, "get_passes_for_level", return_value=[mock_pass]):
|
549
|
+
pipeline.optimize(module, OptimizationLevel.O1)
|
550
|
+
|
551
|
+
# Should handle gracefully
|
552
|
+
stats = pipeline.get_statistics()
|
553
|
+
assert stats["passes_run"] == ["test"]
|
554
|
+
assert "test" not in stats["pass_stats"] # No stats for this pass
|