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,397 @@
|
|
1
|
+
"""Type narrowing optimization pass.
|
2
|
+
|
3
|
+
This module implements type narrowing based on runtime type checks,
|
4
|
+
allowing subsequent operations to use more specific type information.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from dataclasses import dataclass
|
8
|
+
from typing import Any
|
9
|
+
|
10
|
+
from machine_dialect.mir.basic_block import CFG, BasicBlock
|
11
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
12
|
+
from machine_dialect.mir.mir_instructions import (
|
13
|
+
BinaryOp,
|
14
|
+
ConditionalJump,
|
15
|
+
Copy,
|
16
|
+
LoadConst,
|
17
|
+
MIRInstruction,
|
18
|
+
TypeAssert,
|
19
|
+
TypeCast,
|
20
|
+
TypeCheck,
|
21
|
+
)
|
22
|
+
from machine_dialect.mir.mir_types import MIRType, MIRUnionType
|
23
|
+
from machine_dialect.mir.mir_values import Constant, MIRValue, Variable
|
24
|
+
from machine_dialect.mir.optimization_pass import (
|
25
|
+
FunctionPass,
|
26
|
+
PassInfo,
|
27
|
+
PassType,
|
28
|
+
PreservationLevel,
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
@dataclass
|
33
|
+
class TypeConstraint:
|
34
|
+
"""Type constraint for a value at a program point.
|
35
|
+
|
36
|
+
Attributes:
|
37
|
+
value: The value being constrained.
|
38
|
+
narrowed_type: The narrowed type after a check.
|
39
|
+
valid_blocks: Set of blocks where this constraint is valid.
|
40
|
+
"""
|
41
|
+
|
42
|
+
value: MIRValue
|
43
|
+
narrowed_type: MIRType | MIRUnionType
|
44
|
+
valid_blocks: set[Any]
|
45
|
+
|
46
|
+
|
47
|
+
class TypeNarrowing(FunctionPass):
|
48
|
+
"""Type narrowing optimization pass.
|
49
|
+
|
50
|
+
This pass tracks type constraints from TypeCheck and TypeAssert
|
51
|
+
instructions and propagates narrowed types through dominated blocks.
|
52
|
+
"""
|
53
|
+
|
54
|
+
def __init__(self) -> None:
|
55
|
+
"""Initialize the type narrowing pass."""
|
56
|
+
super().__init__()
|
57
|
+
self.constraints: dict[MIRValue, TypeConstraint] = {}
|
58
|
+
self.dominance_tree: dict[Any, set[Any]] = {}
|
59
|
+
self.stats = {
|
60
|
+
"types_narrowed": 0,
|
61
|
+
"checks_eliminated": 0,
|
62
|
+
"casts_eliminated": 0,
|
63
|
+
"operations_specialized": 0,
|
64
|
+
}
|
65
|
+
|
66
|
+
def get_info(self) -> PassInfo:
|
67
|
+
"""Get pass information.
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
Pass information.
|
71
|
+
"""
|
72
|
+
return PassInfo(
|
73
|
+
name="type-narrowing",
|
74
|
+
description="Narrow types based on runtime type checks",
|
75
|
+
pass_type=PassType.OPTIMIZATION,
|
76
|
+
requires=["dominance"],
|
77
|
+
preserves=PreservationLevel.CFG,
|
78
|
+
)
|
79
|
+
|
80
|
+
def run_on_function(self, function: MIRFunction) -> bool:
|
81
|
+
"""Run type narrowing on a function.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
function: The function to optimize.
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
True if the function was modified.
|
88
|
+
"""
|
89
|
+
modified = False
|
90
|
+
|
91
|
+
# Build dominance tree
|
92
|
+
self._build_dominance_tree(function)
|
93
|
+
|
94
|
+
# First pass: collect type constraints from checks
|
95
|
+
self._collect_type_constraints(function)
|
96
|
+
|
97
|
+
# Second pass: apply narrowed types
|
98
|
+
for block in function.cfg.blocks.values():
|
99
|
+
if self._apply_type_narrowing(block, function):
|
100
|
+
modified = True
|
101
|
+
|
102
|
+
# Third pass: eliminate redundant checks
|
103
|
+
for block in function.cfg.blocks.values():
|
104
|
+
if self._eliminate_redundant_checks(block):
|
105
|
+
modified = True
|
106
|
+
|
107
|
+
return modified
|
108
|
+
|
109
|
+
def _build_dominance_tree(self, function: MIRFunction) -> None:
|
110
|
+
"""Build dominance tree for the function.
|
111
|
+
|
112
|
+
Simple approximation: a block dominates its successors in a
|
113
|
+
straight-line path (no branches).
|
114
|
+
|
115
|
+
Args:
|
116
|
+
function: The function to analyze.
|
117
|
+
"""
|
118
|
+
self.dominance_tree.clear()
|
119
|
+
|
120
|
+
for block in function.cfg.blocks.values():
|
121
|
+
dominated = set()
|
122
|
+
|
123
|
+
# A block dominates itself
|
124
|
+
dominated.add(block)
|
125
|
+
|
126
|
+
# Find blocks dominated by this one
|
127
|
+
# Simple heuristic: single successor with single predecessor
|
128
|
+
current = block
|
129
|
+
while len(current.successors) == 1:
|
130
|
+
successor = current.successors[0]
|
131
|
+
if len(successor.predecessors) == 1:
|
132
|
+
dominated.add(successor)
|
133
|
+
current = successor
|
134
|
+
else:
|
135
|
+
break
|
136
|
+
|
137
|
+
self.dominance_tree[block] = dominated
|
138
|
+
|
139
|
+
def _collect_type_constraints(self, function: MIRFunction) -> None:
|
140
|
+
"""Collect type constraints from type checks.
|
141
|
+
|
142
|
+
Args:
|
143
|
+
function: The function to analyze.
|
144
|
+
"""
|
145
|
+
self.constraints.clear()
|
146
|
+
|
147
|
+
for block in function.cfg.blocks.values():
|
148
|
+
for inst in block.instructions:
|
149
|
+
if isinstance(inst, TypeCheck):
|
150
|
+
# TypeCheck creates a boolean result
|
151
|
+
# Track that after a successful check, the value has the checked type
|
152
|
+
self._add_constraint_from_check(inst, block, function)
|
153
|
+
|
154
|
+
elif isinstance(inst, TypeAssert):
|
155
|
+
# TypeAssert guarantees the type after the assertion
|
156
|
+
self._add_constraint_from_assert(inst, block)
|
157
|
+
|
158
|
+
elif isinstance(inst, ConditionalJump):
|
159
|
+
# Conditional jumps on type checks create constraints
|
160
|
+
self._add_constraint_from_branch(inst, block, function)
|
161
|
+
|
162
|
+
def _add_constraint_from_check(self, inst: TypeCheck, block: BasicBlock, function: MIRFunction) -> None:
|
163
|
+
"""Add constraint from a TypeCheck instruction.
|
164
|
+
|
165
|
+
Args:
|
166
|
+
inst: TypeCheck instruction.
|
167
|
+
block: Containing block.
|
168
|
+
function: The containing function.
|
169
|
+
"""
|
170
|
+
# Find where the check result is used
|
171
|
+
for succ_block in block.successors:
|
172
|
+
for succ_inst in succ_block.instructions:
|
173
|
+
if isinstance(succ_inst, ConditionalJump) and succ_inst.condition == inst.dest:
|
174
|
+
# This check is used in a branch
|
175
|
+
# In the true branch, value has the checked type
|
176
|
+
if succ_inst.true_label:
|
177
|
+
true_block = self._find_block_by_label(function.cfg, succ_inst.true_label)
|
178
|
+
if true_block:
|
179
|
+
dominated = self.dominance_tree.get(true_block, set())
|
180
|
+
constraint = TypeConstraint(
|
181
|
+
value=inst.value, narrowed_type=inst.check_type, valid_blocks=dominated
|
182
|
+
)
|
183
|
+
self.constraints[inst.value] = constraint
|
184
|
+
|
185
|
+
def _add_constraint_from_assert(self, inst: TypeAssert, block: BasicBlock) -> None:
|
186
|
+
"""Add constraint from a TypeAssert instruction.
|
187
|
+
|
188
|
+
Args:
|
189
|
+
inst: TypeAssert instruction.
|
190
|
+
block: Containing block.
|
191
|
+
"""
|
192
|
+
# After an assert, the value has the asserted type in dominated blocks
|
193
|
+
dominated = self.dominance_tree.get(block, set())
|
194
|
+
|
195
|
+
# Include successors in the constraint
|
196
|
+
for succ in block.successors:
|
197
|
+
succ_dominated = self.dominance_tree.get(succ, set())
|
198
|
+
dominated = dominated.union(succ_dominated)
|
199
|
+
|
200
|
+
constraint = TypeConstraint(value=inst.value, narrowed_type=inst.assert_type, valid_blocks=dominated)
|
201
|
+
self.constraints[inst.value] = constraint
|
202
|
+
|
203
|
+
def _add_constraint_from_branch(self, inst: ConditionalJump, block: BasicBlock, function: MIRFunction) -> None:
|
204
|
+
"""Add constraints from conditional branches on type checks.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
inst: ConditionalJump instruction.
|
208
|
+
block: Containing block.
|
209
|
+
function: The containing function.
|
210
|
+
"""
|
211
|
+
# Look for pattern: t = is_type(x, T); if t then ...
|
212
|
+
for prev_inst in reversed(block.instructions):
|
213
|
+
if isinstance(prev_inst, TypeCheck) and prev_inst.dest == inst.condition:
|
214
|
+
# Found the type check
|
215
|
+
if inst.true_label:
|
216
|
+
true_block = self._find_block_by_label(function.cfg, inst.true_label)
|
217
|
+
if true_block:
|
218
|
+
dominated = self.dominance_tree.get(true_block, set())
|
219
|
+
constraint = TypeConstraint(
|
220
|
+
value=prev_inst.value, narrowed_type=prev_inst.check_type, valid_blocks=dominated
|
221
|
+
)
|
222
|
+
self.constraints[prev_inst.value] = constraint
|
223
|
+
break
|
224
|
+
|
225
|
+
def _apply_type_narrowing(self, block: BasicBlock, function: MIRFunction) -> bool:
|
226
|
+
"""Apply narrowed types to operations in a block.
|
227
|
+
|
228
|
+
Args:
|
229
|
+
block: The block to optimize.
|
230
|
+
function: The containing function.
|
231
|
+
|
232
|
+
Returns:
|
233
|
+
True if modifications were made.
|
234
|
+
"""
|
235
|
+
modified = False
|
236
|
+
new_instructions: list[MIRInstruction] = []
|
237
|
+
|
238
|
+
for inst in block.instructions:
|
239
|
+
optimized = self._optimize_with_narrowed_types(inst, block)
|
240
|
+
|
241
|
+
if optimized != inst:
|
242
|
+
new_instructions.append(optimized)
|
243
|
+
self.stats["operations_specialized"] += 1
|
244
|
+
modified = True
|
245
|
+
else:
|
246
|
+
new_instructions.append(inst)
|
247
|
+
|
248
|
+
if modified:
|
249
|
+
block.instructions = new_instructions
|
250
|
+
|
251
|
+
return modified
|
252
|
+
|
253
|
+
def _optimize_with_narrowed_types(self, inst: MIRInstruction, block: BasicBlock) -> MIRInstruction:
|
254
|
+
"""Optimize an instruction using narrowed type information.
|
255
|
+
|
256
|
+
Args:
|
257
|
+
inst: The instruction to optimize.
|
258
|
+
block: The containing block.
|
259
|
+
|
260
|
+
Returns:
|
261
|
+
Optimized instruction or original.
|
262
|
+
"""
|
263
|
+
if isinstance(inst, BinaryOp):
|
264
|
+
# Check if operands have narrowed types
|
265
|
+
left_type = self._get_narrowed_type(inst.left, block)
|
266
|
+
right_type = self._get_narrowed_type(inst.right, block)
|
267
|
+
|
268
|
+
# If both are known to be integers after narrowing, use integer operations
|
269
|
+
if left_type == MIRType.INT and right_type == MIRType.INT:
|
270
|
+
# Could mark this operation as integer-specific
|
271
|
+
# For now, just track the optimization
|
272
|
+
self.stats["types_narrowed"] += 1
|
273
|
+
|
274
|
+
elif isinstance(inst, TypeCast):
|
275
|
+
# Check if cast is unnecessary due to narrowing
|
276
|
+
value_type = self._get_narrowed_type(inst.value, block)
|
277
|
+
|
278
|
+
if value_type == inst.target_type:
|
279
|
+
# Cast is redundant, replace with copy
|
280
|
+
self.stats["casts_eliminated"] += 1
|
281
|
+
return Copy(inst.dest, inst.value, inst.source_location)
|
282
|
+
|
283
|
+
return inst
|
284
|
+
|
285
|
+
def _eliminate_redundant_checks(self, block: BasicBlock) -> bool:
|
286
|
+
"""Eliminate redundant type checks.
|
287
|
+
|
288
|
+
Args:
|
289
|
+
block: The block to optimize.
|
290
|
+
|
291
|
+
Returns:
|
292
|
+
True if modifications were made.
|
293
|
+
"""
|
294
|
+
modified = False
|
295
|
+
new_instructions: list[MIRInstruction] = []
|
296
|
+
|
297
|
+
for inst in block.instructions:
|
298
|
+
if isinstance(inst, TypeCheck):
|
299
|
+
# Check if we already know the type from a constraint
|
300
|
+
narrowed_type = self._get_narrowed_type(inst.value, block)
|
301
|
+
|
302
|
+
if narrowed_type == inst.check_type:
|
303
|
+
# Check will always succeed
|
304
|
+
new_inst = LoadConst(inst.dest, Constant(True, MIRType.BOOL), inst.source_location)
|
305
|
+
new_instructions.append(new_inst)
|
306
|
+
self.stats["checks_eliminated"] += 1
|
307
|
+
modified = True
|
308
|
+
elif isinstance(narrowed_type, MIRType) and isinstance(inst.check_type, MIRType):
|
309
|
+
# Check if types are incompatible
|
310
|
+
if self._types_incompatible(narrowed_type, inst.check_type):
|
311
|
+
# Check will always fail
|
312
|
+
new_inst = LoadConst(inst.dest, Constant(False, MIRType.BOOL), inst.source_location)
|
313
|
+
new_instructions.append(new_inst)
|
314
|
+
self.stats["checks_eliminated"] += 1
|
315
|
+
modified = True
|
316
|
+
else:
|
317
|
+
new_instructions.append(inst)
|
318
|
+
else:
|
319
|
+
new_instructions.append(inst)
|
320
|
+
else:
|
321
|
+
new_instructions.append(inst)
|
322
|
+
|
323
|
+
if modified:
|
324
|
+
block.instructions = new_instructions
|
325
|
+
|
326
|
+
return modified
|
327
|
+
|
328
|
+
def _get_narrowed_type(self, value: MIRValue, block: BasicBlock) -> MIRType | MIRUnionType | None:
|
329
|
+
"""Get the narrowed type of a value in a block.
|
330
|
+
|
331
|
+
Args:
|
332
|
+
value: The value to check.
|
333
|
+
block: The current block.
|
334
|
+
|
335
|
+
Returns:
|
336
|
+
Narrowed type or None.
|
337
|
+
"""
|
338
|
+
if isinstance(value, Constant):
|
339
|
+
return value.type
|
340
|
+
|
341
|
+
constraint = self.constraints.get(value)
|
342
|
+
if constraint and block in constraint.valid_blocks:
|
343
|
+
return constraint.narrowed_type
|
344
|
+
|
345
|
+
# Check if value is a Variable with known type
|
346
|
+
if isinstance(value, Variable):
|
347
|
+
if isinstance(value.type, MIRUnionType):
|
348
|
+
return None
|
349
|
+
return value.type
|
350
|
+
|
351
|
+
return None
|
352
|
+
|
353
|
+
def _types_incompatible(self, type1: MIRType, type2: MIRType) -> bool:
|
354
|
+
"""Check if two types are incompatible.
|
355
|
+
|
356
|
+
Args:
|
357
|
+
type1: First type.
|
358
|
+
type2: Second type.
|
359
|
+
|
360
|
+
Returns:
|
361
|
+
True if types are incompatible.
|
362
|
+
"""
|
363
|
+
# Simple incompatibility check
|
364
|
+
if type1 == type2:
|
365
|
+
return False
|
366
|
+
|
367
|
+
# Numeric types are somewhat compatible
|
368
|
+
numeric_types = {MIRType.INT, MIRType.FLOAT}
|
369
|
+
if type1 in numeric_types and type2 in numeric_types:
|
370
|
+
return False
|
371
|
+
|
372
|
+
# Otherwise, different types are incompatible
|
373
|
+
return True
|
374
|
+
|
375
|
+
def _find_block_by_label(self, cfg: CFG, label: str) -> BasicBlock | None:
|
376
|
+
"""Find a block by its label.
|
377
|
+
|
378
|
+
Args:
|
379
|
+
cfg: The CFG to search in.
|
380
|
+
label: Block label.
|
381
|
+
|
382
|
+
Returns:
|
383
|
+
Block or None.
|
384
|
+
"""
|
385
|
+
# Search through all blocks in the CFG
|
386
|
+
for block in cfg.blocks.values():
|
387
|
+
if hasattr(block, "label") and block.label == label:
|
388
|
+
return block
|
389
|
+
return None
|
390
|
+
|
391
|
+
def finalize(self) -> None:
|
392
|
+
"""Finalize and report statistics."""
|
393
|
+
if any(self.stats.values()):
|
394
|
+
print("Type narrowing optimization statistics:")
|
395
|
+
for key, value in self.stats.items():
|
396
|
+
if value > 0:
|
397
|
+
print(f" {key}: {value}")
|