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.
Files changed (268) hide show
  1. machine_dialect/__main__.py +667 -0
  2. machine_dialect/agent/__init__.py +5 -0
  3. machine_dialect/agent/agent.py +360 -0
  4. machine_dialect/ast/__init__.py +95 -0
  5. machine_dialect/ast/ast_node.py +35 -0
  6. machine_dialect/ast/call_expression.py +82 -0
  7. machine_dialect/ast/dict_extraction.py +60 -0
  8. machine_dialect/ast/expressions.py +439 -0
  9. machine_dialect/ast/literals.py +309 -0
  10. machine_dialect/ast/program.py +35 -0
  11. machine_dialect/ast/statements.py +1433 -0
  12. machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
  13. machine_dialect/ast/tests/test_boolean_literal.py +29 -0
  14. machine_dialect/ast/tests/test_collection_hir.py +138 -0
  15. machine_dialect/ast/tests/test_define_statement.py +142 -0
  16. machine_dialect/ast/tests/test_desugar.py +541 -0
  17. machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
  18. machine_dialect/cfg/__init__.py +6 -0
  19. machine_dialect/cfg/config.py +156 -0
  20. machine_dialect/cfg/examples.py +221 -0
  21. machine_dialect/cfg/generate_with_ai.py +187 -0
  22. machine_dialect/cfg/openai_generation.py +200 -0
  23. machine_dialect/cfg/parser.py +94 -0
  24. machine_dialect/cfg/tests/__init__.py +1 -0
  25. machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
  26. machine_dialect/cfg/tests/test_config.py +188 -0
  27. machine_dialect/cfg/tests/test_examples.py +391 -0
  28. machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
  29. machine_dialect/cfg/tests/test_openai_generation.py +256 -0
  30. machine_dialect/codegen/__init__.py +5 -0
  31. machine_dialect/codegen/bytecode_module.py +89 -0
  32. machine_dialect/codegen/bytecode_serializer.py +300 -0
  33. machine_dialect/codegen/opcodes.py +101 -0
  34. machine_dialect/codegen/register_codegen.py +1996 -0
  35. machine_dialect/codegen/symtab.py +208 -0
  36. machine_dialect/codegen/tests/__init__.py +1 -0
  37. machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
  38. machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
  39. machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
  40. machine_dialect/codegen/tests/test_symtab.py +418 -0
  41. machine_dialect/codegen/vm_serializer.py +621 -0
  42. machine_dialect/compiler/__init__.py +18 -0
  43. machine_dialect/compiler/compiler.py +197 -0
  44. machine_dialect/compiler/config.py +149 -0
  45. machine_dialect/compiler/context.py +149 -0
  46. machine_dialect/compiler/phases/__init__.py +19 -0
  47. machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
  48. machine_dialect/compiler/phases/codegen.py +40 -0
  49. machine_dialect/compiler/phases/hir_generation.py +39 -0
  50. machine_dialect/compiler/phases/mir_generation.py +86 -0
  51. machine_dialect/compiler/phases/optimization.py +110 -0
  52. machine_dialect/compiler/phases/parsing.py +39 -0
  53. machine_dialect/compiler/pipeline.py +143 -0
  54. machine_dialect/compiler/tests/__init__.py +1 -0
  55. machine_dialect/compiler/tests/test_compiler.py +568 -0
  56. machine_dialect/compiler/vm_runner.py +173 -0
  57. machine_dialect/errors/__init__.py +32 -0
  58. machine_dialect/errors/exceptions.py +369 -0
  59. machine_dialect/errors/messages.py +82 -0
  60. machine_dialect/errors/tests/__init__.py +0 -0
  61. machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
  62. machine_dialect/errors/tests/test_name_errors.py +118 -0
  63. machine_dialect/helpers/__init__.py +0 -0
  64. machine_dialect/helpers/stopwords.py +225 -0
  65. machine_dialect/helpers/validators.py +30 -0
  66. machine_dialect/lexer/__init__.py +9 -0
  67. machine_dialect/lexer/constants.py +23 -0
  68. machine_dialect/lexer/lexer.py +907 -0
  69. machine_dialect/lexer/tests/__init__.py +0 -0
  70. machine_dialect/lexer/tests/helpers.py +86 -0
  71. machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
  72. machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
  73. machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
  74. machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
  75. machine_dialect/lexer/tests/test_comments.py +200 -0
  76. machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
  77. machine_dialect/lexer/tests/test_lexer_position.py +113 -0
  78. machine_dialect/lexer/tests/test_list_tokens.py +282 -0
  79. machine_dialect/lexer/tests/test_stopwords.py +80 -0
  80. machine_dialect/lexer/tests/test_strict_equality.py +129 -0
  81. machine_dialect/lexer/tests/test_token.py +41 -0
  82. machine_dialect/lexer/tests/test_tokenization.py +294 -0
  83. machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
  84. machine_dialect/lexer/tests/test_url_literals.py +169 -0
  85. machine_dialect/lexer/tokens.py +487 -0
  86. machine_dialect/linter/__init__.py +10 -0
  87. machine_dialect/linter/__main__.py +144 -0
  88. machine_dialect/linter/linter.py +154 -0
  89. machine_dialect/linter/rules/__init__.py +8 -0
  90. machine_dialect/linter/rules/base.py +112 -0
  91. machine_dialect/linter/rules/statement_termination.py +99 -0
  92. machine_dialect/linter/tests/__init__.py +1 -0
  93. machine_dialect/linter/tests/mdrules/__init__.py +0 -0
  94. machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
  95. machine_dialect/linter/tests/test_linter.py +81 -0
  96. machine_dialect/linter/tests/test_rules.py +110 -0
  97. machine_dialect/linter/tests/test_violations.py +71 -0
  98. machine_dialect/linter/violations.py +51 -0
  99. machine_dialect/mir/__init__.py +69 -0
  100. machine_dialect/mir/analyses/__init__.py +20 -0
  101. machine_dialect/mir/analyses/alias_analysis.py +315 -0
  102. machine_dialect/mir/analyses/dominance_analysis.py +49 -0
  103. machine_dialect/mir/analyses/escape_analysis.py +286 -0
  104. machine_dialect/mir/analyses/loop_analysis.py +272 -0
  105. machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
  106. machine_dialect/mir/analyses/type_analysis.py +448 -0
  107. machine_dialect/mir/analyses/use_def_chains.py +232 -0
  108. machine_dialect/mir/basic_block.py +385 -0
  109. machine_dialect/mir/dataflow.py +445 -0
  110. machine_dialect/mir/debug_info.py +208 -0
  111. machine_dialect/mir/hir_to_mir.py +1738 -0
  112. machine_dialect/mir/mir_dumper.py +366 -0
  113. machine_dialect/mir/mir_function.py +167 -0
  114. machine_dialect/mir/mir_instructions.py +1877 -0
  115. machine_dialect/mir/mir_interpreter.py +556 -0
  116. machine_dialect/mir/mir_module.py +225 -0
  117. machine_dialect/mir/mir_printer.py +480 -0
  118. machine_dialect/mir/mir_transformer.py +410 -0
  119. machine_dialect/mir/mir_types.py +367 -0
  120. machine_dialect/mir/mir_validation.py +455 -0
  121. machine_dialect/mir/mir_values.py +268 -0
  122. machine_dialect/mir/optimization_config.py +233 -0
  123. machine_dialect/mir/optimization_pass.py +251 -0
  124. machine_dialect/mir/optimization_pipeline.py +355 -0
  125. machine_dialect/mir/optimizations/__init__.py +84 -0
  126. machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
  127. machine_dialect/mir/optimizations/branch_prediction.py +372 -0
  128. machine_dialect/mir/optimizations/constant_propagation.py +634 -0
  129. machine_dialect/mir/optimizations/cse.py +398 -0
  130. machine_dialect/mir/optimizations/dce.py +288 -0
  131. machine_dialect/mir/optimizations/inlining.py +551 -0
  132. machine_dialect/mir/optimizations/jump_threading.py +487 -0
  133. machine_dialect/mir/optimizations/licm.py +405 -0
  134. machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
  135. machine_dialect/mir/optimizations/strength_reduction.py +422 -0
  136. machine_dialect/mir/optimizations/tail_call.py +207 -0
  137. machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
  138. machine_dialect/mir/optimizations/type_narrowing.py +397 -0
  139. machine_dialect/mir/optimizations/type_specialization.py +447 -0
  140. machine_dialect/mir/optimizations/type_specific.py +906 -0
  141. machine_dialect/mir/optimize_mir.py +89 -0
  142. machine_dialect/mir/pass_manager.py +391 -0
  143. machine_dialect/mir/profiling/__init__.py +26 -0
  144. machine_dialect/mir/profiling/profile_collector.py +318 -0
  145. machine_dialect/mir/profiling/profile_data.py +372 -0
  146. machine_dialect/mir/profiling/profile_reader.py +272 -0
  147. machine_dialect/mir/profiling/profile_writer.py +226 -0
  148. machine_dialect/mir/register_allocation.py +302 -0
  149. machine_dialect/mir/reporting/__init__.py +17 -0
  150. machine_dialect/mir/reporting/optimization_reporter.py +314 -0
  151. machine_dialect/mir/reporting/report_formatter.py +289 -0
  152. machine_dialect/mir/ssa_construction.py +342 -0
  153. machine_dialect/mir/tests/__init__.py +1 -0
  154. machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
  155. machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
  156. machine_dialect/mir/tests/test_algebraic_division.py +126 -0
  157. machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
  158. machine_dialect/mir/tests/test_basic_block.py +425 -0
  159. machine_dialect/mir/tests/test_branch_prediction.py +459 -0
  160. machine_dialect/mir/tests/test_call_lowering.py +168 -0
  161. machine_dialect/mir/tests/test_collection_lowering.py +604 -0
  162. machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
  163. machine_dialect/mir/tests/test_custom_passes.py +166 -0
  164. machine_dialect/mir/tests/test_debug_info.py +285 -0
  165. machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
  166. machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
  167. machine_dialect/mir/tests/test_double_negation.py +231 -0
  168. machine_dialect/mir/tests/test_escape_analysis.py +233 -0
  169. machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
  170. machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
  171. machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
  172. machine_dialect/mir/tests/test_inlining.py +435 -0
  173. machine_dialect/mir/tests/test_licm.py +472 -0
  174. machine_dialect/mir/tests/test_mir_dumper.py +313 -0
  175. machine_dialect/mir/tests/test_mir_instructions.py +445 -0
  176. machine_dialect/mir/tests/test_mir_module.py +860 -0
  177. machine_dialect/mir/tests/test_mir_printer.py +387 -0
  178. machine_dialect/mir/tests/test_mir_types.py +123 -0
  179. machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
  180. machine_dialect/mir/tests/test_mir_validation.py +378 -0
  181. machine_dialect/mir/tests/test_mir_values.py +168 -0
  182. machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
  183. machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
  184. machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
  185. machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
  186. machine_dialect/mir/tests/test_pass_manager.py +294 -0
  187. machine_dialect/mir/tests/test_pass_registration.py +64 -0
  188. machine_dialect/mir/tests/test_profiling.py +356 -0
  189. machine_dialect/mir/tests/test_register_allocation.py +307 -0
  190. machine_dialect/mir/tests/test_report_formatters.py +372 -0
  191. machine_dialect/mir/tests/test_ssa_construction.py +433 -0
  192. machine_dialect/mir/tests/test_tail_call.py +236 -0
  193. machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
  194. machine_dialect/mir/tests/test_type_narrowing.py +277 -0
  195. machine_dialect/mir/tests/test_type_specialization.py +421 -0
  196. machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
  197. machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
  198. machine_dialect/mir/type_inference.py +368 -0
  199. machine_dialect/parser/__init__.py +12 -0
  200. machine_dialect/parser/enums.py +45 -0
  201. machine_dialect/parser/parser.py +3655 -0
  202. machine_dialect/parser/protocols.py +11 -0
  203. machine_dialect/parser/symbol_table.py +169 -0
  204. machine_dialect/parser/tests/__init__.py +0 -0
  205. machine_dialect/parser/tests/helper_functions.py +193 -0
  206. machine_dialect/parser/tests/test_action_statements.py +334 -0
  207. machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
  208. machine_dialect/parser/tests/test_call_statements.py +154 -0
  209. machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
  210. machine_dialect/parser/tests/test_collection_mutations.py +264 -0
  211. machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
  212. machine_dialect/parser/tests/test_define_integration.py +468 -0
  213. machine_dialect/parser/tests/test_define_statements.py +311 -0
  214. machine_dialect/parser/tests/test_dict_extraction.py +115 -0
  215. machine_dialect/parser/tests/test_empty_literal.py +155 -0
  216. machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
  217. machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
  218. machine_dialect/parser/tests/test_if_empty_block.py +61 -0
  219. machine_dialect/parser/tests/test_if_statements.py +299 -0
  220. machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
  221. machine_dialect/parser/tests/test_infix_expressions.py +680 -0
  222. machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
  223. machine_dialect/parser/tests/test_interaction_statements.py +269 -0
  224. machine_dialect/parser/tests/test_list_literals.py +277 -0
  225. machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
  226. machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
  227. machine_dialect/parser/tests/test_parse_errors.py +114 -0
  228. machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
  229. machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
  230. machine_dialect/parser/tests/test_program.py +13 -0
  231. machine_dialect/parser/tests/test_return_statements.py +89 -0
  232. machine_dialect/parser/tests/test_set_statements.py +152 -0
  233. machine_dialect/parser/tests/test_strict_equality.py +258 -0
  234. machine_dialect/parser/tests/test_symbol_table.py +217 -0
  235. machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
  236. machine_dialect/parser/tests/test_utility_statements.py +423 -0
  237. machine_dialect/parser/token_buffer.py +159 -0
  238. machine_dialect/repl/__init__.py +3 -0
  239. machine_dialect/repl/repl.py +426 -0
  240. machine_dialect/repl/tests/__init__.py +0 -0
  241. machine_dialect/repl/tests/test_repl.py +606 -0
  242. machine_dialect/semantic/__init__.py +12 -0
  243. machine_dialect/semantic/analyzer.py +906 -0
  244. machine_dialect/semantic/error_messages.py +189 -0
  245. machine_dialect/semantic/tests/__init__.py +1 -0
  246. machine_dialect/semantic/tests/test_analyzer.py +364 -0
  247. machine_dialect/semantic/tests/test_error_messages.py +104 -0
  248. machine_dialect/tests/edge_cases/__init__.py +10 -0
  249. machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
  250. machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
  251. machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
  252. machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
  253. machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
  254. machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
  255. machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
  256. machine_dialect/tests/integration/test_list_compilation.py +395 -0
  257. machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
  258. machine_dialect/type_checking/__init__.py +21 -0
  259. machine_dialect/type_checking/tests/__init__.py +1 -0
  260. machine_dialect/type_checking/tests/test_type_system.py +230 -0
  261. machine_dialect/type_checking/type_system.py +270 -0
  262. machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
  263. machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
  264. machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
  265. machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
  266. machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
  267. machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
  268. 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}")