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,289 @@
|
|
1
|
+
"""Report formatters for optimization reports.
|
2
|
+
|
3
|
+
This module provides different output formats for optimization reports
|
4
|
+
including text, HTML, and JSON.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import json
|
8
|
+
from abc import ABC, abstractmethod
|
9
|
+
from typing import Any
|
10
|
+
|
11
|
+
from machine_dialect.mir.reporting.optimization_reporter import ModuleMetrics
|
12
|
+
|
13
|
+
|
14
|
+
class ReportFormatter(ABC):
|
15
|
+
"""Abstract base class for report formatters."""
|
16
|
+
|
17
|
+
@abstractmethod
|
18
|
+
def format(self, metrics: ModuleMetrics) -> str:
|
19
|
+
"""Format the metrics into a report.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
metrics: Module metrics to format.
|
23
|
+
|
24
|
+
Returns:
|
25
|
+
Formatted report as a string.
|
26
|
+
"""
|
27
|
+
pass
|
28
|
+
|
29
|
+
|
30
|
+
class TextReportFormatter(ReportFormatter):
|
31
|
+
"""Formats reports as plain text."""
|
32
|
+
|
33
|
+
def __init__(self, detailed: bool = True) -> None:
|
34
|
+
"""Initialize text formatter.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
detailed: Whether to include detailed per-pass statistics.
|
38
|
+
"""
|
39
|
+
self.detailed = detailed
|
40
|
+
|
41
|
+
def format(self, metrics: ModuleMetrics) -> str:
|
42
|
+
"""Format metrics as text report.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
metrics: Module metrics.
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
Text report.
|
49
|
+
"""
|
50
|
+
lines = []
|
51
|
+
summary = metrics.get_summary()
|
52
|
+
|
53
|
+
# Header
|
54
|
+
lines.append("=" * 70)
|
55
|
+
lines.append(f"OPTIMIZATION REPORT - {summary['module_name']}")
|
56
|
+
lines.append("=" * 70)
|
57
|
+
lines.append("")
|
58
|
+
|
59
|
+
# Summary section
|
60
|
+
lines.append("SUMMARY")
|
61
|
+
lines.append("-" * 30)
|
62
|
+
lines.append(f"Optimization Level: {summary['optimization_level']}")
|
63
|
+
lines.append(f"Total Passes Run: {summary['total_passes']}")
|
64
|
+
lines.append(f"Total Time: {summary['total_time_ms']:.2f}ms")
|
65
|
+
|
66
|
+
if summary["total_time_ms"] > 0:
|
67
|
+
avg_time = summary["total_time_ms"] / summary["total_passes"]
|
68
|
+
lines.append(f"Average Pass Time: {avg_time:.2f}ms")
|
69
|
+
lines.append("")
|
70
|
+
|
71
|
+
# Passes applied
|
72
|
+
if summary["passes_applied"]:
|
73
|
+
lines.append("PASSES APPLIED")
|
74
|
+
lines.append("-" * 30)
|
75
|
+
for i, pass_name in enumerate(summary["passes_applied"], 1):
|
76
|
+
lines.append(f"{i:2}. {pass_name}")
|
77
|
+
lines.append("")
|
78
|
+
|
79
|
+
# Overall improvements
|
80
|
+
if summary["improvements"]:
|
81
|
+
lines.append("OVERALL IMPROVEMENTS")
|
82
|
+
lines.append("-" * 30)
|
83
|
+
for metric, improvement in sorted(summary["improvements"].items()):
|
84
|
+
if improvement > 0:
|
85
|
+
lines.append(f" {metric:30} {improvement:6.1f}% reduction")
|
86
|
+
elif improvement < 0:
|
87
|
+
lines.append(f" {metric:30} {-improvement:6.1f}% increase")
|
88
|
+
lines.append("")
|
89
|
+
|
90
|
+
# Total metrics
|
91
|
+
if summary["total_metrics"]:
|
92
|
+
lines.append("TOTAL OPTIMIZATIONS")
|
93
|
+
lines.append("-" * 30)
|
94
|
+
for metric, value in sorted(summary["total_metrics"].items()):
|
95
|
+
if value > 0:
|
96
|
+
lines.append(f" {metric:30} {value:6}")
|
97
|
+
lines.append("")
|
98
|
+
|
99
|
+
# Detailed pass statistics
|
100
|
+
if self.detailed and metrics.pass_metrics:
|
101
|
+
lines.append("=" * 70)
|
102
|
+
lines.append("DETAILED PASS STATISTICS")
|
103
|
+
lines.append("=" * 70)
|
104
|
+
|
105
|
+
for i, pass_metrics in enumerate(metrics.pass_metrics, 1):
|
106
|
+
lines.append("")
|
107
|
+
lines.append(f"[{i}] {pass_metrics.pass_name}")
|
108
|
+
lines.append("-" * 50)
|
109
|
+
lines.append(f" Phase: {pass_metrics.phase}")
|
110
|
+
lines.append(f" Time: {pass_metrics.time_ms:.2f}ms")
|
111
|
+
|
112
|
+
if pass_metrics.metrics:
|
113
|
+
lines.append(" Changes:")
|
114
|
+
for key, value in sorted(pass_metrics.metrics.items()):
|
115
|
+
if value > 0:
|
116
|
+
lines.append(f" - {key}: {value}")
|
117
|
+
|
118
|
+
# Calculate improvements
|
119
|
+
improvements = []
|
120
|
+
for key in pass_metrics.before_stats:
|
121
|
+
if key in pass_metrics.after_stats:
|
122
|
+
before = pass_metrics.before_stats[key]
|
123
|
+
after = pass_metrics.after_stats[key]
|
124
|
+
if before != after:
|
125
|
+
improvement = pass_metrics.get_improvement(key)
|
126
|
+
improvements.append((key, before, after, improvement))
|
127
|
+
|
128
|
+
if improvements:
|
129
|
+
lines.append(" Impact:")
|
130
|
+
for key, before, after, improvement in improvements:
|
131
|
+
if improvement > 0:
|
132
|
+
lines.append(f" - {key}: {before} → {after} (-{improvement:.1f}%)")
|
133
|
+
else:
|
134
|
+
lines.append(f" - {key}: {before} → {after} (+{-improvement:.1f}%)")
|
135
|
+
|
136
|
+
# Function metrics
|
137
|
+
if metrics.function_metrics:
|
138
|
+
lines.append("")
|
139
|
+
lines.append("=" * 70)
|
140
|
+
lines.append("FUNCTION-LEVEL STATISTICS")
|
141
|
+
lines.append("=" * 70)
|
142
|
+
|
143
|
+
for func_name, func_metrics in metrics.function_metrics.items():
|
144
|
+
lines.append("")
|
145
|
+
lines.append(f"Function: {func_name}")
|
146
|
+
lines.append("-" * 30)
|
147
|
+
for key, value in sorted(func_metrics.items()):
|
148
|
+
lines.append(f" {key:20} {value}")
|
149
|
+
|
150
|
+
lines.append("")
|
151
|
+
lines.append("=" * 70)
|
152
|
+
return "\n".join(lines)
|
153
|
+
|
154
|
+
|
155
|
+
class HTMLReportFormatter(ReportFormatter):
|
156
|
+
"""Formats reports as HTML."""
|
157
|
+
|
158
|
+
def format(self, metrics: ModuleMetrics) -> str:
|
159
|
+
"""Format metrics as HTML report.
|
160
|
+
|
161
|
+
Args:
|
162
|
+
metrics: Module metrics.
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
HTML report.
|
166
|
+
"""
|
167
|
+
summary = metrics.get_summary()
|
168
|
+
|
169
|
+
html = []
|
170
|
+
html.append("<!DOCTYPE html>")
|
171
|
+
html.append("<html>")
|
172
|
+
html.append("<head>")
|
173
|
+
html.append("<title>Optimization Report</title>")
|
174
|
+
html.append("<style>")
|
175
|
+
html.append(
|
176
|
+
"""
|
177
|
+
body { font-family: monospace; margin: 20px; }
|
178
|
+
h1 { color: #333; }
|
179
|
+
h2 { color: #666; border-bottom: 2px solid #ddd; }
|
180
|
+
table { border-collapse: collapse; width: 100%; margin: 20px 0; }
|
181
|
+
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
182
|
+
th { background-color: #f2f2f2; }
|
183
|
+
.improvement { color: green; font-weight: bold; }
|
184
|
+
.regression { color: red; font-weight: bold; }
|
185
|
+
.metric { background-color: #f9f9f9; }
|
186
|
+
.pass-header { background-color: #e9e9e9; font-weight: bold; }
|
187
|
+
"""
|
188
|
+
)
|
189
|
+
html.append("</style>")
|
190
|
+
html.append("</head>")
|
191
|
+
html.append("<body>")
|
192
|
+
|
193
|
+
# Header
|
194
|
+
html.append(f"<h1>Optimization Report - {summary['module_name']}</h1>")
|
195
|
+
|
196
|
+
# Summary
|
197
|
+
html.append("<h2>Summary</h2>")
|
198
|
+
html.append("<table>")
|
199
|
+
html.append(f"<tr><td>Optimization Level</td><td>{summary['optimization_level']}</td></tr>")
|
200
|
+
html.append(f"<tr><td>Total Passes</td><td>{summary['total_passes']}</td></tr>")
|
201
|
+
html.append(f"<tr><td>Total Time</td><td>{summary['total_time_ms']:.2f}ms</td></tr>")
|
202
|
+
html.append("</table>")
|
203
|
+
|
204
|
+
# Passes Applied
|
205
|
+
if summary["passes_applied"]:
|
206
|
+
html.append("<h2>Passes Applied</h2>")
|
207
|
+
html.append("<ol>")
|
208
|
+
for pass_name in summary["passes_applied"]:
|
209
|
+
html.append(f"<li>{pass_name}</li>")
|
210
|
+
html.append("</ol>")
|
211
|
+
|
212
|
+
# Improvements
|
213
|
+
if summary["improvements"]:
|
214
|
+
html.append("<h2>Overall Improvements</h2>")
|
215
|
+
html.append("<table>")
|
216
|
+
html.append("<tr><th>Metric</th><th>Improvement</th></tr>")
|
217
|
+
for metric, improvement in sorted(summary["improvements"].items()):
|
218
|
+
if improvement > 0:
|
219
|
+
html.append(f"<tr><td>{metric}</td><td class='improvement'>-{improvement:.1f}%</td></tr>")
|
220
|
+
elif improvement < 0:
|
221
|
+
html.append(f"<tr><td>{metric}</td><td class='regression'>+{-improvement:.1f}%</td></tr>")
|
222
|
+
html.append("</table>")
|
223
|
+
|
224
|
+
# Detailed Pass Statistics
|
225
|
+
if metrics.pass_metrics:
|
226
|
+
html.append("<h2>Detailed Pass Statistics</h2>")
|
227
|
+
html.append("<table>")
|
228
|
+
html.append("<tr><th>Pass</th><th>Phase</th><th>Time (ms)</th><th>Metrics</th></tr>")
|
229
|
+
|
230
|
+
for pass_metrics in metrics.pass_metrics:
|
231
|
+
metrics_str = ", ".join(f"{k}: {v}" for k, v in pass_metrics.metrics.items() if v > 0)
|
232
|
+
html.append("<tr>")
|
233
|
+
html.append(f"<td>{pass_metrics.pass_name}</td>")
|
234
|
+
html.append(f"<td>{pass_metrics.phase}</td>")
|
235
|
+
html.append(f"<td>{pass_metrics.time_ms:.2f}</td>")
|
236
|
+
html.append(f"<td>{metrics_str or 'N/A'}</td>")
|
237
|
+
html.append("</tr>")
|
238
|
+
|
239
|
+
html.append("</table>")
|
240
|
+
|
241
|
+
html.append("</body>")
|
242
|
+
html.append("</html>")
|
243
|
+
|
244
|
+
return "\n".join(html)
|
245
|
+
|
246
|
+
|
247
|
+
class JSONReportFormatter(ReportFormatter):
|
248
|
+
"""Formats reports as JSON."""
|
249
|
+
|
250
|
+
def format(self, metrics: ModuleMetrics) -> str:
|
251
|
+
"""Format metrics as JSON report.
|
252
|
+
|
253
|
+
Args:
|
254
|
+
metrics: Module metrics.
|
255
|
+
|
256
|
+
Returns:
|
257
|
+
JSON report.
|
258
|
+
"""
|
259
|
+
data: dict[str, Any] = {
|
260
|
+
"module_name": metrics.module_name,
|
261
|
+
"optimization_level": metrics.optimization_level,
|
262
|
+
"total_time_ms": metrics.total_time_ms,
|
263
|
+
"summary": metrics.get_summary(),
|
264
|
+
"passes": [],
|
265
|
+
"functions": metrics.function_metrics,
|
266
|
+
}
|
267
|
+
|
268
|
+
# Add detailed pass information
|
269
|
+
for pass_metrics in metrics.pass_metrics:
|
270
|
+
pass_data: dict[str, Any] = {
|
271
|
+
"name": pass_metrics.pass_name,
|
272
|
+
"phase": pass_metrics.phase,
|
273
|
+
"time_ms": pass_metrics.time_ms,
|
274
|
+
"metrics": pass_metrics.metrics,
|
275
|
+
"before_stats": pass_metrics.before_stats,
|
276
|
+
"after_stats": pass_metrics.after_stats,
|
277
|
+
"improvements": {},
|
278
|
+
}
|
279
|
+
|
280
|
+
# Calculate improvements
|
281
|
+
for key in pass_metrics.before_stats:
|
282
|
+
if key in pass_metrics.after_stats:
|
283
|
+
improvement = pass_metrics.get_improvement(key)
|
284
|
+
if improvement != 0:
|
285
|
+
pass_data["improvements"][key] = improvement
|
286
|
+
|
287
|
+
data["passes"].append(pass_data)
|
288
|
+
|
289
|
+
return json.dumps(data, indent=2)
|
@@ -0,0 +1,342 @@
|
|
1
|
+
"""SSA (Static Single Assignment) construction for MIR.
|
2
|
+
|
3
|
+
This module implements SSA construction with dominance frontier calculation
|
4
|
+
and phi node insertion for the MIR representation.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from collections import OrderedDict, defaultdict
|
8
|
+
|
9
|
+
from machine_dialect.mir.basic_block import CFG, BasicBlock
|
10
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
11
|
+
from machine_dialect.mir.mir_instructions import Copy, LoadConst, LoadVar, MIRInstruction, Phi, StoreVar
|
12
|
+
from machine_dialect.mir.mir_values import Variable
|
13
|
+
|
14
|
+
|
15
|
+
class DominanceInfo:
|
16
|
+
"""Dominance information for a control flow graph."""
|
17
|
+
|
18
|
+
def __init__(self, cfg: CFG) -> None:
|
19
|
+
"""Initialize dominance information.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
cfg: The control flow graph to analyze.
|
23
|
+
"""
|
24
|
+
self.cfg = cfg
|
25
|
+
self.dominators: dict[BasicBlock, set[BasicBlock]] = {}
|
26
|
+
self.immediate_dominators: dict[BasicBlock, BasicBlock | None] = {}
|
27
|
+
self.dominance_frontier: dict[BasicBlock, set[BasicBlock]] = {}
|
28
|
+
self.dominator_tree_children: dict[BasicBlock, set[BasicBlock]] = defaultdict(set)
|
29
|
+
|
30
|
+
self._compute_dominators()
|
31
|
+
self._compute_immediate_dominators()
|
32
|
+
self._compute_dominance_frontier()
|
33
|
+
|
34
|
+
def _compute_dominators(self) -> None:
|
35
|
+
"""Compute dominators for all blocks using iterative algorithm."""
|
36
|
+
entry = self.cfg.entry_block
|
37
|
+
if not entry:
|
38
|
+
return
|
39
|
+
|
40
|
+
# Initialize dominators
|
41
|
+
blocks = list(self.cfg.blocks.values())
|
42
|
+
self.dominators[entry] = {entry}
|
43
|
+
|
44
|
+
# All other blocks are initially dominated by all blocks
|
45
|
+
for block in blocks:
|
46
|
+
if block != entry:
|
47
|
+
self.dominators[block] = set(blocks)
|
48
|
+
|
49
|
+
# Iterate until fixed point
|
50
|
+
changed = True
|
51
|
+
while changed:
|
52
|
+
changed = False
|
53
|
+
for block in blocks:
|
54
|
+
if block == entry:
|
55
|
+
continue
|
56
|
+
|
57
|
+
# Compute new dominators
|
58
|
+
new_doms = set(blocks)
|
59
|
+
for pred in block.predecessors:
|
60
|
+
if pred in self.dominators:
|
61
|
+
new_doms &= self.dominators[pred]
|
62
|
+
new_doms.add(block)
|
63
|
+
|
64
|
+
# Check if changed
|
65
|
+
if new_doms != self.dominators[block]:
|
66
|
+
self.dominators[block] = new_doms
|
67
|
+
changed = True
|
68
|
+
|
69
|
+
def _compute_immediate_dominators(self) -> None:
|
70
|
+
"""Compute immediate dominators from dominator sets."""
|
71
|
+
for block in self.dominators:
|
72
|
+
# Immediate dominator is the unique dominator that doesn't
|
73
|
+
# dominate any other dominators
|
74
|
+
doms = self.dominators[block] - {block}
|
75
|
+
if not doms:
|
76
|
+
self.immediate_dominators[block] = None
|
77
|
+
continue
|
78
|
+
|
79
|
+
# Find the immediate dominator
|
80
|
+
idom = None
|
81
|
+
for dom in doms:
|
82
|
+
# Check if dom dominates any other dominator
|
83
|
+
is_immediate = True
|
84
|
+
for other in doms:
|
85
|
+
if other != dom and dom in self.dominators.get(other, set()):
|
86
|
+
is_immediate = False
|
87
|
+
break
|
88
|
+
if is_immediate:
|
89
|
+
idom = dom
|
90
|
+
break
|
91
|
+
|
92
|
+
self.immediate_dominators[block] = idom
|
93
|
+
if idom:
|
94
|
+
self.dominator_tree_children[idom].add(block)
|
95
|
+
|
96
|
+
def _compute_dominance_frontier(self) -> None:
|
97
|
+
"""Compute dominance frontier for all blocks."""
|
98
|
+
# Initialize empty frontiers
|
99
|
+
for block in self.dominators:
|
100
|
+
self.dominance_frontier[block] = set()
|
101
|
+
|
102
|
+
# For each block
|
103
|
+
for block in self.dominators:
|
104
|
+
# Check each successor
|
105
|
+
for succ in block.successors:
|
106
|
+
# Walk up dominator tree from block
|
107
|
+
runner: BasicBlock | None = block
|
108
|
+
while runner and runner != self.immediate_dominators.get(succ):
|
109
|
+
self.dominance_frontier[runner].add(succ)
|
110
|
+
runner = self.immediate_dominators.get(runner)
|
111
|
+
|
112
|
+
def dominates(self, a: BasicBlock, b: BasicBlock) -> bool:
|
113
|
+
"""Check if block a dominates block b.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
a: Potential dominator block.
|
117
|
+
b: Block to check.
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
True if a dominates b.
|
121
|
+
"""
|
122
|
+
return a in self.dominators.get(b, set())
|
123
|
+
|
124
|
+
def strictly_dominates(self, a: BasicBlock, b: BasicBlock) -> bool:
|
125
|
+
"""Check if block a strictly dominates block b.
|
126
|
+
|
127
|
+
Args:
|
128
|
+
a: Potential dominator block.
|
129
|
+
b: Block to check.
|
130
|
+
|
131
|
+
Returns:
|
132
|
+
True if a strictly dominates b (dominates and a != b).
|
133
|
+
"""
|
134
|
+
return a != b and self.dominates(a, b)
|
135
|
+
|
136
|
+
|
137
|
+
class SSAConstructor:
|
138
|
+
"""Constructs SSA form for MIR functions."""
|
139
|
+
|
140
|
+
def __init__(self, function: MIRFunction) -> None:
|
141
|
+
"""Initialize SSA constructor.
|
142
|
+
|
143
|
+
Args:
|
144
|
+
function: The function to convert to SSA form.
|
145
|
+
"""
|
146
|
+
self.function = function
|
147
|
+
self.dominance = DominanceInfo(function.cfg)
|
148
|
+
self.variable_definitions: dict[Variable, set[BasicBlock]] = defaultdict(set)
|
149
|
+
self.variable_uses: dict[Variable, set[BasicBlock]] = defaultdict(set)
|
150
|
+
self.phi_nodes: dict[tuple[BasicBlock, Variable], Phi] = {}
|
151
|
+
self.variable_stacks: dict[Variable, list[Variable]] = defaultdict(list)
|
152
|
+
self.version_counters: dict[str, int] = defaultdict(int)
|
153
|
+
|
154
|
+
def construct_ssa(self) -> None:
|
155
|
+
"""Convert the function to SSA form."""
|
156
|
+
self._collect_variable_info()
|
157
|
+
self._insert_phi_nodes()
|
158
|
+
self._rename_variables()
|
159
|
+
|
160
|
+
def _collect_variable_info(self) -> None:
|
161
|
+
"""Collect information about variable definitions and uses."""
|
162
|
+
for block in self.function.cfg.blocks.values():
|
163
|
+
for inst in block.instructions:
|
164
|
+
# Check for variable definitions
|
165
|
+
for def_val in inst.get_defs():
|
166
|
+
if isinstance(def_val, Variable):
|
167
|
+
self.variable_definitions[def_val].add(block)
|
168
|
+
|
169
|
+
# Check for variable uses
|
170
|
+
for use_val in inst.get_uses():
|
171
|
+
if isinstance(use_val, Variable):
|
172
|
+
self.variable_uses[use_val].add(block)
|
173
|
+
|
174
|
+
def _insert_phi_nodes(self) -> None:
|
175
|
+
"""Insert phi nodes at dominance frontiers."""
|
176
|
+
# For each variable
|
177
|
+
for var in self.variable_definitions:
|
178
|
+
# Get blocks where variable is defined
|
179
|
+
def_blocks = self.variable_definitions[var]
|
180
|
+
|
181
|
+
# Compute iterated dominance frontier
|
182
|
+
worklist = list(def_blocks)
|
183
|
+
phi_blocks = set()
|
184
|
+
processed = set()
|
185
|
+
|
186
|
+
while worklist:
|
187
|
+
block = worklist.pop()
|
188
|
+
if block in processed:
|
189
|
+
continue
|
190
|
+
processed.add(block)
|
191
|
+
|
192
|
+
# Add phi nodes at dominance frontier
|
193
|
+
for df_block in self.dominance.dominance_frontier.get(block, set()):
|
194
|
+
if df_block not in phi_blocks:
|
195
|
+
phi_blocks.add(df_block)
|
196
|
+
|
197
|
+
# Create phi node
|
198
|
+
phi_var = self._new_version(var)
|
199
|
+
# TODO: Review if (0, 0) is the best approach for generated phi nodes' source location
|
200
|
+
phi = Phi(phi_var, [], (0, 0)) # Default source location for generated phi nodes
|
201
|
+
|
202
|
+
# Insert phi in phi_nodes list
|
203
|
+
df_block.phi_nodes.append(phi)
|
204
|
+
self.phi_nodes[(df_block, var)] = phi
|
205
|
+
|
206
|
+
# Phi node is also a definition
|
207
|
+
if df_block not in def_blocks:
|
208
|
+
worklist.append(df_block)
|
209
|
+
|
210
|
+
def _rename_variables(self) -> None:
|
211
|
+
"""Rename variables to SSA form."""
|
212
|
+
entry = self.function.cfg.entry_block
|
213
|
+
if entry:
|
214
|
+
self._rename_block(entry, set())
|
215
|
+
|
216
|
+
def _rename_block(self, block: BasicBlock, visited: set[BasicBlock]) -> None:
|
217
|
+
"""Rename variables in a block and its dominated blocks.
|
218
|
+
|
219
|
+
Args:
|
220
|
+
block: The block to process.
|
221
|
+
visited: Set of already visited blocks.
|
222
|
+
"""
|
223
|
+
if block in visited:
|
224
|
+
return
|
225
|
+
visited.add(block)
|
226
|
+
|
227
|
+
# Save stack state
|
228
|
+
stack_state: dict[Variable, int] = {}
|
229
|
+
for var in self.variable_stacks:
|
230
|
+
stack_state[var] = len(self.variable_stacks[var])
|
231
|
+
|
232
|
+
# Process phi nodes first
|
233
|
+
for phi in block.phi_nodes:
|
234
|
+
# Find original variable for this phi
|
235
|
+
for (phi_block, var), stored_phi in self.phi_nodes.items():
|
236
|
+
if phi_block == block and stored_phi == phi:
|
237
|
+
# Push new version (phi.dest is MIRValue but should be Variable)
|
238
|
+
if isinstance(phi.dest, Variable):
|
239
|
+
self.variable_stacks[var].append(phi.dest)
|
240
|
+
break
|
241
|
+
|
242
|
+
# Rebuild instruction list, preserving LoadConst and other non-Variable instructions
|
243
|
+
new_instructions: list[MIRInstruction] = []
|
244
|
+
temp_definitions = OrderedDict() # Track LoadConst for ordering
|
245
|
+
|
246
|
+
# Rename uses and definitions
|
247
|
+
for inst in block.instructions:
|
248
|
+
if isinstance(inst, Phi):
|
249
|
+
continue # Already handled in phi_nodes list
|
250
|
+
|
251
|
+
# Special handling for LoadConst - preserve as-is
|
252
|
+
if isinstance(inst, LoadConst):
|
253
|
+
# LoadConst defines a Temp, not a Variable, so don't rename
|
254
|
+
# Just preserve the instruction
|
255
|
+
new_instructions.append(inst)
|
256
|
+
if hasattr(inst.dest, "name"):
|
257
|
+
temp_definitions[inst.dest] = inst
|
258
|
+
continue
|
259
|
+
|
260
|
+
# Rename uses for Variable-based instructions
|
261
|
+
if isinstance(inst, StoreVar):
|
262
|
+
# Special case: StoreVar uses source, defines var
|
263
|
+
if isinstance(inst.source, Variable):
|
264
|
+
if self.variable_stacks[inst.source]:
|
265
|
+
inst.source = self.variable_stacks[inst.source][-1]
|
266
|
+
elif isinstance(inst, LoadVar):
|
267
|
+
# Special case: LoadVar uses var, defines dest
|
268
|
+
if self.variable_stacks[inst.var]:
|
269
|
+
inst.var = self.variable_stacks[inst.var][-1]
|
270
|
+
else:
|
271
|
+
# General case: rename all uses
|
272
|
+
for use_val in inst.get_uses():
|
273
|
+
if isinstance(use_val, Variable):
|
274
|
+
if self.variable_stacks[use_val]:
|
275
|
+
inst.replace_use(use_val, self.variable_stacks[use_val][-1])
|
276
|
+
|
277
|
+
# Rename definitions
|
278
|
+
for def_val in inst.get_defs():
|
279
|
+
if isinstance(def_val, Variable):
|
280
|
+
new_var = self._new_version(def_val)
|
281
|
+
if isinstance(inst, StoreVar):
|
282
|
+
inst.var = new_var
|
283
|
+
elif isinstance(inst, Copy) and inst.dest == def_val:
|
284
|
+
inst.dest = new_var
|
285
|
+
# Push new version
|
286
|
+
self.variable_stacks[def_val].append(new_var)
|
287
|
+
|
288
|
+
new_instructions.append(inst)
|
289
|
+
|
290
|
+
# Replace the instruction list with the rebuilt one
|
291
|
+
block.instructions = new_instructions
|
292
|
+
|
293
|
+
# Update phi nodes in successors
|
294
|
+
for succ in block.successors:
|
295
|
+
# Phi nodes are now in the phi_nodes list, not instructions
|
296
|
+
for phi in succ.phi_nodes:
|
297
|
+
# Find original variable for this phi
|
298
|
+
for (phi_block, var), stored_phi in self.phi_nodes.items():
|
299
|
+
if phi_block == succ and stored_phi == phi:
|
300
|
+
# Add incoming value from this block
|
301
|
+
if self.variable_stacks[var]:
|
302
|
+
phi.add_incoming(
|
303
|
+
self.variable_stacks[var][-1],
|
304
|
+
block.label,
|
305
|
+
)
|
306
|
+
break
|
307
|
+
|
308
|
+
# Process dominated blocks
|
309
|
+
for child in self.dominance.dominator_tree_children.get(block, set()):
|
310
|
+
self._rename_block(child, visited)
|
311
|
+
|
312
|
+
# Restore stack state
|
313
|
+
for var, old_size in stack_state.items():
|
314
|
+
while len(self.variable_stacks[var]) > old_size:
|
315
|
+
self.variable_stacks[var].pop()
|
316
|
+
|
317
|
+
def _new_version(self, var: Variable) -> Variable:
|
318
|
+
"""Create a new SSA version of a variable.
|
319
|
+
|
320
|
+
Args:
|
321
|
+
var: The original variable.
|
322
|
+
|
323
|
+
Returns:
|
324
|
+
A new versioned variable.
|
325
|
+
"""
|
326
|
+
base_name = var.name
|
327
|
+
version = self.version_counters[base_name]
|
328
|
+
self.version_counters[base_name] += 1
|
329
|
+
|
330
|
+
# Create new variable with version number
|
331
|
+
new_var = Variable(base_name, var.type, version=version)
|
332
|
+
return new_var
|
333
|
+
|
334
|
+
|
335
|
+
def construct_ssa(function: MIRFunction) -> None:
|
336
|
+
"""Convert a MIR function to SSA form.
|
337
|
+
|
338
|
+
Args:
|
339
|
+
function: The function to convert.
|
340
|
+
"""
|
341
|
+
constructor = SSAConstructor(function)
|
342
|
+
constructor.construct_ssa()
|
@@ -0,0 +1 @@
|
|
1
|
+
"""Tests for Machine Dialectâ„¢ MIR."""
|