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,315 @@
|
|
1
|
+
"""Alias analysis for MIR.
|
2
|
+
|
3
|
+
This module provides alias analysis to determine when different variables
|
4
|
+
may refer to the same memory location, supporting optimizations like
|
5
|
+
escape analysis and redundant load elimination.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from dataclasses import dataclass, field
|
9
|
+
from enum import Enum
|
10
|
+
|
11
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
12
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
13
|
+
from machine_dialect.mir.mir_instructions import (
|
14
|
+
Copy,
|
15
|
+
GetAttr,
|
16
|
+
LoadVar,
|
17
|
+
MIRInstruction,
|
18
|
+
Phi,
|
19
|
+
SetAttr,
|
20
|
+
StoreVar,
|
21
|
+
)
|
22
|
+
from machine_dialect.mir.mir_types import MIRType
|
23
|
+
from machine_dialect.mir.mir_values import Variable
|
24
|
+
from machine_dialect.mir.optimization_pass import (
|
25
|
+
FunctionAnalysisPass,
|
26
|
+
PassInfo,
|
27
|
+
PassType,
|
28
|
+
PreservationLevel,
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
class AliasType(Enum):
|
33
|
+
"""Type of aliasing relationship."""
|
34
|
+
|
35
|
+
NO_ALIAS = "no_alias" # Definitely different locations
|
36
|
+
MAY_ALIAS = "may_alias" # Might be the same location
|
37
|
+
MUST_ALIAS = "must_alias" # Definitely the same location
|
38
|
+
PARTIAL_ALIAS = "partial_alias" # Overlapping but not identical
|
39
|
+
|
40
|
+
|
41
|
+
@dataclass
|
42
|
+
class AliasSet:
|
43
|
+
"""Set of potentially aliasing values.
|
44
|
+
|
45
|
+
Attributes:
|
46
|
+
members: Values that may alias each other.
|
47
|
+
alias_type: Type of aliasing relationship.
|
48
|
+
attributes: Attributes accessed on these values.
|
49
|
+
"""
|
50
|
+
|
51
|
+
members: set[Variable] = field(default_factory=set)
|
52
|
+
alias_type: AliasType = AliasType.MAY_ALIAS
|
53
|
+
attributes: set[str] = field(default_factory=set)
|
54
|
+
|
55
|
+
def add_member(self, var: Variable) -> None:
|
56
|
+
"""Add a variable to the alias set.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
var: Variable to add.
|
60
|
+
"""
|
61
|
+
self.members.add(var)
|
62
|
+
|
63
|
+
def merge(self, other: "AliasSet") -> None:
|
64
|
+
"""Merge another alias set into this one.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
other: Alias set to merge.
|
68
|
+
"""
|
69
|
+
self.members.update(other.members)
|
70
|
+
self.attributes.update(other.attributes)
|
71
|
+
# Downgrade alias type to be conservative
|
72
|
+
if other.alias_type == AliasType.MAY_ALIAS or self.alias_type == AliasType.MAY_ALIAS:
|
73
|
+
self.alias_type = AliasType.MAY_ALIAS
|
74
|
+
|
75
|
+
|
76
|
+
class AliasInfo:
|
77
|
+
"""Container for alias analysis results."""
|
78
|
+
|
79
|
+
def __init__(self) -> None:
|
80
|
+
"""Initialize alias information."""
|
81
|
+
self.alias_sets: list[AliasSet] = []
|
82
|
+
self.var_to_set: dict[str, AliasSet] = {}
|
83
|
+
|
84
|
+
def get_aliases(self, var: Variable) -> set[Variable]:
|
85
|
+
"""Get all variables that may alias with the given variable.
|
86
|
+
|
87
|
+
Args:
|
88
|
+
var: Variable to query.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
Set of potentially aliasing variables.
|
92
|
+
"""
|
93
|
+
alias_set = self.var_to_set.get(var.name)
|
94
|
+
if alias_set:
|
95
|
+
return alias_set.members - {var}
|
96
|
+
return set()
|
97
|
+
|
98
|
+
def may_alias(self, var1: Variable, var2: Variable) -> bool:
|
99
|
+
"""Check if two variables may alias.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
var1: First variable.
|
103
|
+
var2: Second variable.
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
True if variables may alias.
|
107
|
+
"""
|
108
|
+
set1 = self.var_to_set.get(var1.name)
|
109
|
+
set2 = self.var_to_set.get(var2.name)
|
110
|
+
return set1 is set2 if (set1 and set2) else False
|
111
|
+
|
112
|
+
def must_alias(self, var1: Variable, var2: Variable) -> bool:
|
113
|
+
"""Check if two variables must alias.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
var1: First variable.
|
117
|
+
var2: Second variable.
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
True if variables must alias.
|
121
|
+
"""
|
122
|
+
set1 = self.var_to_set.get(var1.name)
|
123
|
+
set2 = self.var_to_set.get(var2.name)
|
124
|
+
if set1 is set2 and set1:
|
125
|
+
return set1.alias_type == AliasType.MUST_ALIAS
|
126
|
+
return False
|
127
|
+
|
128
|
+
|
129
|
+
class AliasAnalysis(FunctionAnalysisPass):
|
130
|
+
"""Analyze pointer aliasing relationships."""
|
131
|
+
|
132
|
+
def get_info(self) -> PassInfo:
|
133
|
+
"""Get pass information.
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
Pass information.
|
137
|
+
"""
|
138
|
+
return PassInfo(
|
139
|
+
name="alias-analysis",
|
140
|
+
description="Analyze variable aliasing relationships",
|
141
|
+
pass_type=PassType.ANALYSIS,
|
142
|
+
requires=[],
|
143
|
+
preserves=PreservationLevel.ALL,
|
144
|
+
)
|
145
|
+
|
146
|
+
def run_on_function(self, function: MIRFunction) -> AliasInfo:
|
147
|
+
"""Analyze aliasing in a function.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
function: The function to analyze.
|
151
|
+
|
152
|
+
Returns:
|
153
|
+
Alias analysis results.
|
154
|
+
"""
|
155
|
+
alias_info = AliasInfo()
|
156
|
+
|
157
|
+
# Initialize each variable in its own set
|
158
|
+
self._initialize_variables(function, alias_info)
|
159
|
+
|
160
|
+
# Analyze instructions to find aliasing
|
161
|
+
for block in function.cfg.blocks.values():
|
162
|
+
self._analyze_block(block, alias_info)
|
163
|
+
|
164
|
+
# Handle phi nodes specially
|
165
|
+
self._analyze_phi_nodes(function, alias_info)
|
166
|
+
|
167
|
+
return alias_info
|
168
|
+
|
169
|
+
def _initialize_variables(self, function: MIRFunction, alias_info: AliasInfo) -> None:
|
170
|
+
"""Initialize alias sets for all variables.
|
171
|
+
|
172
|
+
Args:
|
173
|
+
function: The function being analyzed.
|
174
|
+
alias_info: Alias information to populate.
|
175
|
+
"""
|
176
|
+
# Create initial alias sets for locals
|
177
|
+
for var_name in function.locals:
|
178
|
+
var = Variable(var_name, MIRType.INT)
|
179
|
+
alias_set = AliasSet()
|
180
|
+
alias_set.add_member(var)
|
181
|
+
alias_set.alias_type = AliasType.NO_ALIAS # Initially no aliases
|
182
|
+
alias_info.alias_sets.append(alias_set)
|
183
|
+
alias_info.var_to_set[var_name] = alias_set
|
184
|
+
|
185
|
+
# Create initial sets for parameters
|
186
|
+
for param in function.params:
|
187
|
+
alias_set = AliasSet()
|
188
|
+
alias_set.add_member(param)
|
189
|
+
# Parameters may alias with external data
|
190
|
+
alias_set.alias_type = AliasType.MAY_ALIAS
|
191
|
+
alias_info.alias_sets.append(alias_set)
|
192
|
+
alias_info.var_to_set[param.name] = alias_set
|
193
|
+
|
194
|
+
def _analyze_block(self, block: BasicBlock, alias_info: AliasInfo) -> None:
|
195
|
+
"""Analyze aliasing in a block.
|
196
|
+
|
197
|
+
Args:
|
198
|
+
block: The block to analyze.
|
199
|
+
alias_info: Alias information to update.
|
200
|
+
"""
|
201
|
+
for inst in block.instructions:
|
202
|
+
self._analyze_instruction(inst, alias_info)
|
203
|
+
|
204
|
+
def _analyze_instruction(self, inst: MIRInstruction, alias_info: AliasInfo) -> None:
|
205
|
+
"""Analyze aliasing effects of an instruction.
|
206
|
+
|
207
|
+
Args:
|
208
|
+
inst: The instruction to analyze.
|
209
|
+
alias_info: Alias information to update.
|
210
|
+
"""
|
211
|
+
# Copy creates must-alias relationship
|
212
|
+
if isinstance(inst, Copy):
|
213
|
+
if isinstance(inst.source, Variable) and isinstance(inst.dest, Variable):
|
214
|
+
self._merge_alias_sets(inst.source, inst.dest, AliasType.MUST_ALIAS, alias_info)
|
215
|
+
|
216
|
+
# Load creates may-alias relationship
|
217
|
+
elif isinstance(inst, LoadVar):
|
218
|
+
if isinstance(inst.dest, Variable) and isinstance(inst.var, Variable):
|
219
|
+
self._merge_alias_sets(inst.var, inst.dest, AliasType.MAY_ALIAS, alias_info)
|
220
|
+
|
221
|
+
# Store can create aliasing through memory
|
222
|
+
elif isinstance(inst, StoreVar):
|
223
|
+
if isinstance(inst.source, Variable) and isinstance(inst.var, Variable):
|
224
|
+
self._merge_alias_sets(inst.var, inst.source, AliasType.MAY_ALIAS, alias_info)
|
225
|
+
|
226
|
+
# GetAttr tracks attribute access
|
227
|
+
elif isinstance(inst, GetAttr):
|
228
|
+
if isinstance(inst.obj, Variable) and isinstance(inst.dest, Variable):
|
229
|
+
obj_set = alias_info.var_to_set.get(inst.obj.name)
|
230
|
+
if obj_set:
|
231
|
+
obj_set.attributes.add(inst.attr)
|
232
|
+
# Result may alias with the field
|
233
|
+
self._merge_alias_sets(inst.obj, inst.dest, AliasType.PARTIAL_ALIAS, alias_info)
|
234
|
+
|
235
|
+
# SetAttr tracks attribute modification
|
236
|
+
elif isinstance(inst, SetAttr):
|
237
|
+
if isinstance(inst.obj, Variable):
|
238
|
+
obj_set = alias_info.var_to_set.get(inst.obj.name)
|
239
|
+
if obj_set:
|
240
|
+
obj_set.attributes.add(inst.attr)
|
241
|
+
|
242
|
+
def _merge_alias_sets(
|
243
|
+
self,
|
244
|
+
var1: Variable,
|
245
|
+
var2: Variable,
|
246
|
+
alias_type: AliasType,
|
247
|
+
alias_info: AliasInfo,
|
248
|
+
) -> None:
|
249
|
+
"""Merge alias sets for two variables.
|
250
|
+
|
251
|
+
Args:
|
252
|
+
var1: First variable.
|
253
|
+
var2: Second variable.
|
254
|
+
alias_type: Type of aliasing.
|
255
|
+
alias_info: Alias information to update.
|
256
|
+
"""
|
257
|
+
set1 = alias_info.var_to_set.get(var1.name)
|
258
|
+
set2 = alias_info.var_to_set.get(var2.name)
|
259
|
+
|
260
|
+
if not set1 and not set2:
|
261
|
+
# Create new set for both
|
262
|
+
new_set = AliasSet()
|
263
|
+
new_set.add_member(var1)
|
264
|
+
new_set.add_member(var2)
|
265
|
+
new_set.alias_type = alias_type
|
266
|
+
alias_info.alias_sets.append(new_set)
|
267
|
+
alias_info.var_to_set[var1.name] = new_set
|
268
|
+
alias_info.var_to_set[var2.name] = new_set
|
269
|
+
elif set1 and not set2:
|
270
|
+
# Add var2 to set1
|
271
|
+
set1.add_member(var2)
|
272
|
+
if alias_type == AliasType.MAY_ALIAS or set1.alias_type != alias_type:
|
273
|
+
set1.alias_type = AliasType.MAY_ALIAS
|
274
|
+
alias_info.var_to_set[var2.name] = set1
|
275
|
+
elif not set1 and set2:
|
276
|
+
# Add var1 to set2
|
277
|
+
set2.add_member(var1)
|
278
|
+
if alias_type == AliasType.MAY_ALIAS or set2.alias_type != alias_type:
|
279
|
+
set2.alias_type = AliasType.MAY_ALIAS
|
280
|
+
alias_info.var_to_set[var1.name] = set2
|
281
|
+
elif set1 and set2 and set1 is not set2:
|
282
|
+
# Merge sets
|
283
|
+
set1.merge(set2)
|
284
|
+
if alias_type == AliasType.MAY_ALIAS:
|
285
|
+
set1.alias_type = AliasType.MAY_ALIAS
|
286
|
+
# Update all members of set2 to point to set1
|
287
|
+
for member in set2.members:
|
288
|
+
alias_info.var_to_set[member.name] = set1
|
289
|
+
# Remove set2 from list
|
290
|
+
alias_info.alias_sets.remove(set2)
|
291
|
+
|
292
|
+
def _analyze_phi_nodes(self, function: MIRFunction, alias_info: AliasInfo) -> None:
|
293
|
+
"""Analyze phi nodes for aliasing.
|
294
|
+
|
295
|
+
Args:
|
296
|
+
function: The function being analyzed.
|
297
|
+
alias_info: Alias information to update.
|
298
|
+
"""
|
299
|
+
for block in function.cfg.blocks.values():
|
300
|
+
for inst in block.instructions:
|
301
|
+
if isinstance(inst, Phi):
|
302
|
+
# All phi inputs may alias with the output
|
303
|
+
if isinstance(inst.dest, Variable):
|
304
|
+
for value, _ in inst.incoming:
|
305
|
+
if isinstance(value, Variable):
|
306
|
+
self._merge_alias_sets(
|
307
|
+
inst.dest,
|
308
|
+
value,
|
309
|
+
AliasType.MAY_ALIAS,
|
310
|
+
alias_info,
|
311
|
+
)
|
312
|
+
|
313
|
+
def finalize(self) -> None:
|
314
|
+
"""Finalize the pass."""
|
315
|
+
pass
|
@@ -0,0 +1,49 @@
|
|
1
|
+
"""Dominance analysis for MIR.
|
2
|
+
|
3
|
+
This module provides a pass wrapper for dominance analysis.
|
4
|
+
"""
|
5
|
+
|
6
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
7
|
+
from machine_dialect.mir.optimization_pass import (
|
8
|
+
FunctionAnalysisPass,
|
9
|
+
PassInfo,
|
10
|
+
PassType,
|
11
|
+
PreservationLevel,
|
12
|
+
)
|
13
|
+
from machine_dialect.mir.ssa_construction import DominanceInfo
|
14
|
+
|
15
|
+
|
16
|
+
class DominanceAnalysis(FunctionAnalysisPass):
|
17
|
+
"""Analysis pass that computes dominance information."""
|
18
|
+
|
19
|
+
def get_info(self) -> PassInfo:
|
20
|
+
"""Get pass information.
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
Pass information.
|
24
|
+
"""
|
25
|
+
return PassInfo(
|
26
|
+
name="dominance",
|
27
|
+
description="Compute dominance information",
|
28
|
+
pass_type=PassType.ANALYSIS,
|
29
|
+
requires=[],
|
30
|
+
preserves=PreservationLevel.ALL,
|
31
|
+
)
|
32
|
+
|
33
|
+
def run_on_function(self, function: MIRFunction) -> DominanceInfo:
|
34
|
+
"""Compute dominance for a function.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
function: The function to analyze.
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
Dominance information.
|
41
|
+
"""
|
42
|
+
return DominanceInfo(function.cfg)
|
43
|
+
|
44
|
+
def finalize(self) -> None:
|
45
|
+
"""Finalize the analysis pass.
|
46
|
+
|
47
|
+
Nothing to clean up for dominance analysis.
|
48
|
+
"""
|
49
|
+
pass
|
@@ -0,0 +1,286 @@
|
|
1
|
+
"""Escape analysis for MIR variables.
|
2
|
+
|
3
|
+
This module analyzes variable escape behavior to determine which variables
|
4
|
+
can be safely allocated on the stack instead of the heap, improving memory
|
5
|
+
efficiency and cache locality.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from dataclasses import dataclass, field
|
9
|
+
from enum import Enum
|
10
|
+
|
11
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
12
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
13
|
+
from machine_dialect.mir.mir_instructions import (
|
14
|
+
Call,
|
15
|
+
Copy,
|
16
|
+
MIRInstruction,
|
17
|
+
Return,
|
18
|
+
SetAttr,
|
19
|
+
StoreVar,
|
20
|
+
)
|
21
|
+
from machine_dialect.mir.mir_types import MIRType
|
22
|
+
from machine_dialect.mir.mir_values import Variable
|
23
|
+
from machine_dialect.mir.optimization_pass import (
|
24
|
+
FunctionAnalysisPass,
|
25
|
+
PassInfo,
|
26
|
+
PassType,
|
27
|
+
PreservationLevel,
|
28
|
+
)
|
29
|
+
|
30
|
+
|
31
|
+
class EscapeState(Enum):
|
32
|
+
"""Escape state of a variable."""
|
33
|
+
|
34
|
+
NO_ESCAPE = "no_escape" # Can be stack allocated
|
35
|
+
ARG_ESCAPE = "arg_escape" # Escapes as function argument
|
36
|
+
RETURN_ESCAPE = "return_escape" # Escapes via return
|
37
|
+
HEAP_ESCAPE = "heap_escape" # Stored in heap object
|
38
|
+
GLOBAL_ESCAPE = "global_escape" # Escapes globally
|
39
|
+
|
40
|
+
|
41
|
+
@dataclass
|
42
|
+
class VariableEscapeInfo:
|
43
|
+
"""Information about a variable's escape behavior.
|
44
|
+
|
45
|
+
Attributes:
|
46
|
+
variable: The variable being analyzed.
|
47
|
+
state: Current escape state.
|
48
|
+
escape_sites: Instructions where escape occurs.
|
49
|
+
aliases: Other variables that alias this one.
|
50
|
+
stack_eligible: Whether eligible for stack allocation.
|
51
|
+
"""
|
52
|
+
|
53
|
+
variable: Variable
|
54
|
+
state: EscapeState = EscapeState.NO_ESCAPE
|
55
|
+
escape_sites: list[MIRInstruction] = field(default_factory=list)
|
56
|
+
aliases: set[Variable] = field(default_factory=set)
|
57
|
+
stack_eligible: bool = True
|
58
|
+
|
59
|
+
def mark_escape(self, state: EscapeState, site: MIRInstruction) -> None:
|
60
|
+
"""Mark variable as escaping.
|
61
|
+
|
62
|
+
Args:
|
63
|
+
state: New escape state.
|
64
|
+
site: Instruction causing escape.
|
65
|
+
"""
|
66
|
+
# Upgrade escape state (more restrictive wins)
|
67
|
+
if state == EscapeState.GLOBAL_ESCAPE or self.state == EscapeState.GLOBAL_ESCAPE:
|
68
|
+
self.state = EscapeState.GLOBAL_ESCAPE
|
69
|
+
self.stack_eligible = False
|
70
|
+
elif state == EscapeState.HEAP_ESCAPE or self.state == EscapeState.HEAP_ESCAPE:
|
71
|
+
self.state = EscapeState.HEAP_ESCAPE
|
72
|
+
self.stack_eligible = False
|
73
|
+
elif state == EscapeState.RETURN_ESCAPE or self.state == EscapeState.RETURN_ESCAPE:
|
74
|
+
self.state = EscapeState.RETURN_ESCAPE
|
75
|
+
self.stack_eligible = False
|
76
|
+
elif state == EscapeState.ARG_ESCAPE or self.state == EscapeState.ARG_ESCAPE:
|
77
|
+
self.state = EscapeState.ARG_ESCAPE
|
78
|
+
self.stack_eligible = False
|
79
|
+
|
80
|
+
self.escape_sites.append(site)
|
81
|
+
|
82
|
+
|
83
|
+
class EscapeInfo:
|
84
|
+
"""Container for escape analysis results."""
|
85
|
+
|
86
|
+
def __init__(self) -> None:
|
87
|
+
"""Initialize escape information."""
|
88
|
+
self.variable_info: dict[str, VariableEscapeInfo] = {}
|
89
|
+
self.stack_eligible: set[Variable] = set()
|
90
|
+
self.escaping_vars: set[Variable] = set()
|
91
|
+
|
92
|
+
def get_info(self, var: Variable) -> VariableEscapeInfo | None:
|
93
|
+
"""Get escape info for a variable.
|
94
|
+
|
95
|
+
Args:
|
96
|
+
var: The variable to query.
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
Escape information or None.
|
100
|
+
"""
|
101
|
+
return self.variable_info.get(var.name)
|
102
|
+
|
103
|
+
def is_stack_eligible(self, var: Variable) -> bool:
|
104
|
+
"""Check if variable can be stack allocated.
|
105
|
+
|
106
|
+
Args:
|
107
|
+
var: The variable to check.
|
108
|
+
|
109
|
+
Returns:
|
110
|
+
True if stack eligible.
|
111
|
+
"""
|
112
|
+
info = self.get_info(var)
|
113
|
+
return info.stack_eligible if info else False
|
114
|
+
|
115
|
+
def does_escape(self, var: Variable) -> bool:
|
116
|
+
"""Check if variable escapes.
|
117
|
+
|
118
|
+
Args:
|
119
|
+
var: The variable to check.
|
120
|
+
|
121
|
+
Returns:
|
122
|
+
True if variable escapes.
|
123
|
+
"""
|
124
|
+
info = self.get_info(var)
|
125
|
+
return info.state != EscapeState.NO_ESCAPE if info else False
|
126
|
+
|
127
|
+
|
128
|
+
class EscapeAnalysis(FunctionAnalysisPass):
|
129
|
+
"""Analyze variable escape behavior for stack allocation optimization."""
|
130
|
+
|
131
|
+
def get_info(self) -> PassInfo:
|
132
|
+
"""Get pass information.
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
Pass information.
|
136
|
+
"""
|
137
|
+
return PassInfo(
|
138
|
+
name="escape-analysis",
|
139
|
+
description="Analyze variable escape behavior",
|
140
|
+
pass_type=PassType.ANALYSIS,
|
141
|
+
requires=[],
|
142
|
+
preserves=PreservationLevel.ALL,
|
143
|
+
)
|
144
|
+
|
145
|
+
def run_on_function(self, function: MIRFunction) -> EscapeInfo:
|
146
|
+
"""Analyze escape behavior in a function.
|
147
|
+
|
148
|
+
Args:
|
149
|
+
function: The function to analyze.
|
150
|
+
|
151
|
+
Returns:
|
152
|
+
Escape analysis results.
|
153
|
+
"""
|
154
|
+
escape_info = EscapeInfo()
|
155
|
+
|
156
|
+
# Initialize info for all variables
|
157
|
+
self._initialize_variables(function, escape_info)
|
158
|
+
|
159
|
+
# Analyze each block
|
160
|
+
for block in function.cfg.blocks.values():
|
161
|
+
self._analyze_block(block, escape_info)
|
162
|
+
|
163
|
+
# Propagate escape through aliases
|
164
|
+
self._propagate_escapes(escape_info)
|
165
|
+
|
166
|
+
# Collect final results
|
167
|
+
self._finalize_results(escape_info)
|
168
|
+
|
169
|
+
return escape_info
|
170
|
+
|
171
|
+
def _initialize_variables(self, function: MIRFunction, escape_info: EscapeInfo) -> None:
|
172
|
+
"""Initialize escape info for all variables.
|
173
|
+
|
174
|
+
Args:
|
175
|
+
function: The function being analyzed.
|
176
|
+
escape_info: Escape information to populate.
|
177
|
+
"""
|
178
|
+
# Add local variables
|
179
|
+
for var_name in function.locals:
|
180
|
+
var = Variable(var_name, MIRType.INT) # Default to INT for simplicity
|
181
|
+
escape_info.variable_info[var_name] = VariableEscapeInfo(variable=var)
|
182
|
+
|
183
|
+
# Parameters are more complex - they're already "from outside"
|
184
|
+
# but we track if they escape further
|
185
|
+
for param in function.params:
|
186
|
+
info = VariableEscapeInfo(variable=param)
|
187
|
+
# Parameters themselves don't escape just by existing
|
188
|
+
escape_info.variable_info[param.name] = info
|
189
|
+
|
190
|
+
def _analyze_block(self, block: BasicBlock, escape_info: EscapeInfo) -> None:
|
191
|
+
"""Analyze escape behavior in a block.
|
192
|
+
|
193
|
+
Args:
|
194
|
+
block: The block to analyze.
|
195
|
+
escape_info: Escape information to update.
|
196
|
+
"""
|
197
|
+
for inst in block.instructions:
|
198
|
+
self._analyze_instruction(inst, escape_info)
|
199
|
+
|
200
|
+
def _analyze_instruction(self, inst: MIRInstruction, escape_info: EscapeInfo) -> None:
|
201
|
+
"""Analyze escape behavior of an instruction.
|
202
|
+
|
203
|
+
Args:
|
204
|
+
inst: The instruction to analyze.
|
205
|
+
escape_info: Escape information to update.
|
206
|
+
"""
|
207
|
+
# Check for returns
|
208
|
+
if isinstance(inst, Return):
|
209
|
+
if inst.value and isinstance(inst.value, Variable):
|
210
|
+
info = escape_info.variable_info.get(inst.value.name)
|
211
|
+
if info:
|
212
|
+
info.mark_escape(EscapeState.RETURN_ESCAPE, inst)
|
213
|
+
|
214
|
+
# Check for function calls
|
215
|
+
elif isinstance(inst, Call):
|
216
|
+
# Arguments to calls may escape
|
217
|
+
for arg in inst.args:
|
218
|
+
if isinstance(arg, Variable):
|
219
|
+
info = escape_info.variable_info.get(arg.name)
|
220
|
+
if info:
|
221
|
+
# Conservative: assume args escape
|
222
|
+
# Could be refined with interprocedural analysis
|
223
|
+
info.mark_escape(EscapeState.ARG_ESCAPE, inst)
|
224
|
+
|
225
|
+
# Check for stores to heap objects
|
226
|
+
elif isinstance(inst, SetAttr):
|
227
|
+
if isinstance(inst.value, Variable):
|
228
|
+
info = escape_info.variable_info.get(inst.value.name)
|
229
|
+
if info:
|
230
|
+
info.mark_escape(EscapeState.HEAP_ESCAPE, inst)
|
231
|
+
|
232
|
+
# Check for copies (creates aliases)
|
233
|
+
elif isinstance(inst, Copy):
|
234
|
+
if isinstance(inst.source, Variable) and isinstance(inst.dest, Variable):
|
235
|
+
src_info = escape_info.variable_info.get(inst.source.name)
|
236
|
+
dest_info = escape_info.variable_info.get(inst.dest.name)
|
237
|
+
if src_info and dest_info:
|
238
|
+
# Track aliasing
|
239
|
+
src_info.aliases.add(inst.dest)
|
240
|
+
dest_info.aliases.add(inst.source)
|
241
|
+
|
242
|
+
# Check for stores to global/external locations
|
243
|
+
elif isinstance(inst, StoreVar):
|
244
|
+
if isinstance(inst.source, Variable):
|
245
|
+
info = escape_info.variable_info.get(inst.source.name)
|
246
|
+
if info:
|
247
|
+
# Storing to another variable might be escape
|
248
|
+
# Conservative for now
|
249
|
+
if inst.var.name not in escape_info.variable_info:
|
250
|
+
# Storing to unknown location
|
251
|
+
info.mark_escape(EscapeState.GLOBAL_ESCAPE, inst)
|
252
|
+
|
253
|
+
def _propagate_escapes(self, escape_info: EscapeInfo) -> None:
|
254
|
+
"""Propagate escape states through aliases.
|
255
|
+
|
256
|
+
Args:
|
257
|
+
escape_info: Escape information to update.
|
258
|
+
"""
|
259
|
+
# If a variable escapes, all its aliases escape
|
260
|
+
changed = True
|
261
|
+
while changed:
|
262
|
+
changed = False
|
263
|
+
for var_info in escape_info.variable_info.values():
|
264
|
+
if var_info.state != EscapeState.NO_ESCAPE:
|
265
|
+
for alias in var_info.aliases:
|
266
|
+
alias_info = escape_info.variable_info.get(alias.name)
|
267
|
+
if alias_info and alias_info.state == EscapeState.NO_ESCAPE:
|
268
|
+
alias_info.state = var_info.state
|
269
|
+
alias_info.stack_eligible = False
|
270
|
+
changed = True
|
271
|
+
|
272
|
+
def _finalize_results(self, escape_info: EscapeInfo) -> None:
|
273
|
+
"""Finalize escape analysis results.
|
274
|
+
|
275
|
+
Args:
|
276
|
+
escape_info: Escape information to finalize.
|
277
|
+
"""
|
278
|
+
for var_info in escape_info.variable_info.values():
|
279
|
+
if var_info.stack_eligible:
|
280
|
+
escape_info.stack_eligible.add(var_info.variable)
|
281
|
+
else:
|
282
|
+
escape_info.escaping_vars.add(var_info.variable)
|
283
|
+
|
284
|
+
def finalize(self) -> None:
|
285
|
+
"""Finalize the pass."""
|
286
|
+
pass
|