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,372 @@
|
|
1
|
+
"""Branch prediction optimization pass.
|
2
|
+
|
3
|
+
This module uses profiling data to optimize branch layout and convert
|
4
|
+
predictable branches to more efficient code patterns.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from dataclasses import dataclass
|
8
|
+
|
9
|
+
from machine_dialect.mir.basic_block import BasicBlock
|
10
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
11
|
+
from machine_dialect.mir.mir_instructions import (
|
12
|
+
ConditionalJump,
|
13
|
+
Jump,
|
14
|
+
)
|
15
|
+
from machine_dialect.mir.mir_module import MIRModule
|
16
|
+
from machine_dialect.mir.optimization_pass import (
|
17
|
+
ModulePass,
|
18
|
+
PassInfo,
|
19
|
+
PassType,
|
20
|
+
PreservationLevel,
|
21
|
+
)
|
22
|
+
from machine_dialect.mir.profiling.profile_data import ProfileData
|
23
|
+
|
24
|
+
|
25
|
+
@dataclass
|
26
|
+
class BranchInfo:
|
27
|
+
"""Information about a branch for optimization.
|
28
|
+
|
29
|
+
Attributes:
|
30
|
+
block: The basic block containing the branch.
|
31
|
+
instruction: The branch instruction.
|
32
|
+
taken_probability: Probability of branch being taken.
|
33
|
+
is_predictable: Whether branch is highly predictable.
|
34
|
+
is_loop_header: Whether this is a loop header branch.
|
35
|
+
"""
|
36
|
+
|
37
|
+
block: BasicBlock
|
38
|
+
instruction: ConditionalJump
|
39
|
+
taken_probability: float
|
40
|
+
is_predictable: bool
|
41
|
+
is_loop_header: bool = False
|
42
|
+
|
43
|
+
|
44
|
+
class BranchPredictionOptimization(ModulePass):
|
45
|
+
"""Branch prediction optimization pass.
|
46
|
+
|
47
|
+
This pass uses profile data to:
|
48
|
+
1. Reorder basic blocks for better branch prediction
|
49
|
+
2. Convert highly predictable branches to conditional moves
|
50
|
+
3. Add branch hints for the VM
|
51
|
+
"""
|
52
|
+
|
53
|
+
def __init__(self, profile_data: ProfileData | None = None, predictability_threshold: float = 0.95) -> None:
|
54
|
+
"""Initialize branch prediction optimization.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
profile_data: Optional profiling data with branch statistics.
|
58
|
+
predictability_threshold: Threshold for considering branch predictable.
|
59
|
+
"""
|
60
|
+
super().__init__()
|
61
|
+
self.profile_data = profile_data
|
62
|
+
self.predictability_threshold = predictability_threshold
|
63
|
+
self.stats = {
|
64
|
+
"branches_analyzed": 0,
|
65
|
+
"blocks_reordered": 0,
|
66
|
+
"branches_converted_to_select": 0,
|
67
|
+
"branch_hints_added": 0,
|
68
|
+
}
|
69
|
+
|
70
|
+
def get_info(self) -> PassInfo:
|
71
|
+
"""Get pass information.
|
72
|
+
|
73
|
+
Returns:
|
74
|
+
Pass information.
|
75
|
+
"""
|
76
|
+
return PassInfo(
|
77
|
+
name="branch-prediction",
|
78
|
+
description="Optimize branches using profile data",
|
79
|
+
pass_type=PassType.OPTIMIZATION,
|
80
|
+
requires=[],
|
81
|
+
preserves=PreservationLevel.CFG,
|
82
|
+
)
|
83
|
+
|
84
|
+
def finalize(self) -> None:
|
85
|
+
"""Finalize the pass after running."""
|
86
|
+
pass
|
87
|
+
|
88
|
+
def run_on_module(self, module: MIRModule) -> bool:
|
89
|
+
"""Run branch prediction optimization on a module.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
module: The module to optimize.
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
True if the module was modified.
|
96
|
+
"""
|
97
|
+
modified = False
|
98
|
+
|
99
|
+
# Process each function
|
100
|
+
for function in module.functions.values():
|
101
|
+
if self._optimize_function_branches(function):
|
102
|
+
modified = True
|
103
|
+
|
104
|
+
return modified
|
105
|
+
|
106
|
+
def _optimize_function_branches(self, function: MIRFunction) -> bool:
|
107
|
+
"""Optimize branches in a function.
|
108
|
+
|
109
|
+
Args:
|
110
|
+
function: The function to optimize.
|
111
|
+
|
112
|
+
Returns:
|
113
|
+
True if the function was modified.
|
114
|
+
"""
|
115
|
+
modified = False
|
116
|
+
|
117
|
+
# Collect branch information
|
118
|
+
branches = self._collect_branch_info(function)
|
119
|
+
|
120
|
+
# Phase 1: Convert highly predictable branches to select instructions
|
121
|
+
for branch_info in branches:
|
122
|
+
if branch_info.is_predictable and self._can_convert_to_select(branch_info):
|
123
|
+
if self._convert_to_select(branch_info):
|
124
|
+
self.stats["branches_converted_to_select"] += 1
|
125
|
+
modified = True
|
126
|
+
|
127
|
+
# Phase 2: Reorder blocks for better branch prediction
|
128
|
+
if self._reorder_blocks(function, branches):
|
129
|
+
self.stats["blocks_reordered"] += 1
|
130
|
+
modified = True
|
131
|
+
|
132
|
+
# Phase 3: Add branch hints
|
133
|
+
for branch_info in branches:
|
134
|
+
if self._add_branch_hint(branch_info):
|
135
|
+
self.stats["branch_hints_added"] += 1
|
136
|
+
modified = True
|
137
|
+
|
138
|
+
return modified
|
139
|
+
|
140
|
+
def _collect_branch_info(self, function: MIRFunction) -> list[BranchInfo]:
|
141
|
+
"""Collect information about branches in a function.
|
142
|
+
|
143
|
+
Args:
|
144
|
+
function: The function to analyze.
|
145
|
+
|
146
|
+
Returns:
|
147
|
+
List of branch information.
|
148
|
+
"""
|
149
|
+
branches = []
|
150
|
+
|
151
|
+
for block_name, block in function.cfg.blocks.items():
|
152
|
+
# Check if block ends with conditional jump
|
153
|
+
if block.instructions:
|
154
|
+
last_inst = block.instructions[-1]
|
155
|
+
if isinstance(last_inst, ConditionalJump):
|
156
|
+
self.stats["branches_analyzed"] += 1
|
157
|
+
|
158
|
+
# Get profile data if available
|
159
|
+
taken_prob = 0.5 # Default to unpredictable
|
160
|
+
is_predictable = False
|
161
|
+
|
162
|
+
if self.profile_data:
|
163
|
+
# Look up branch profile
|
164
|
+
branch_key = f"{function.name}:{block_name}"
|
165
|
+
if branch_key in self.profile_data.branches:
|
166
|
+
branch_profile = self.profile_data.branches[branch_key]
|
167
|
+
taken_prob = branch_profile.taken_probability
|
168
|
+
is_predictable = branch_profile.predictable
|
169
|
+
else:
|
170
|
+
# Simple heuristic: backwards branches (loops) are likely taken
|
171
|
+
if last_inst.true_label < block_name:
|
172
|
+
taken_prob = 0.9
|
173
|
+
is_predictable = True
|
174
|
+
|
175
|
+
# Check if it's a loop header
|
176
|
+
is_loop_header = self._is_loop_header(function, block_name)
|
177
|
+
|
178
|
+
branches.append(
|
179
|
+
BranchInfo(
|
180
|
+
block=block,
|
181
|
+
instruction=last_inst,
|
182
|
+
taken_probability=taken_prob,
|
183
|
+
is_predictable=is_predictable
|
184
|
+
or (
|
185
|
+
taken_prob >= self.predictability_threshold
|
186
|
+
or taken_prob <= (1 - self.predictability_threshold)
|
187
|
+
),
|
188
|
+
is_loop_header=is_loop_header,
|
189
|
+
)
|
190
|
+
)
|
191
|
+
|
192
|
+
return branches
|
193
|
+
|
194
|
+
def _is_loop_header(self, function: MIRFunction, block_name: str) -> bool:
|
195
|
+
"""Check if a block is a loop header.
|
196
|
+
|
197
|
+
Args:
|
198
|
+
function: The function containing the block.
|
199
|
+
block_name: Name of the block to check.
|
200
|
+
|
201
|
+
Returns:
|
202
|
+
True if the block is a loop header.
|
203
|
+
"""
|
204
|
+
# Simple heuristic: block has a back edge to itself
|
205
|
+
block = function.cfg.blocks.get(block_name)
|
206
|
+
if block and block.instructions:
|
207
|
+
last_inst = block.instructions[-1]
|
208
|
+
if isinstance(last_inst, ConditionalJump | Jump):
|
209
|
+
# Check if it jumps back to itself or an earlier block
|
210
|
+
targets = []
|
211
|
+
if isinstance(last_inst, ConditionalJump):
|
212
|
+
targets = [last_inst.true_label]
|
213
|
+
if last_inst.false_label:
|
214
|
+
targets.append(last_inst.false_label)
|
215
|
+
elif isinstance(last_inst, Jump):
|
216
|
+
targets = [last_inst.label]
|
217
|
+
|
218
|
+
for target in targets:
|
219
|
+
if target <= block_name: # Back edge
|
220
|
+
return True
|
221
|
+
|
222
|
+
return False
|
223
|
+
|
224
|
+
def _can_convert_to_select(self, branch_info: BranchInfo) -> bool:
|
225
|
+
"""Check if a branch can be converted to a select instruction.
|
226
|
+
|
227
|
+
Args:
|
228
|
+
branch_info: Information about the branch.
|
229
|
+
|
230
|
+
Returns:
|
231
|
+
True if conversion is possible.
|
232
|
+
"""
|
233
|
+
# Don't convert loop headers (would break loop structure)
|
234
|
+
if branch_info.is_loop_header:
|
235
|
+
return False
|
236
|
+
|
237
|
+
# Check if both targets are simple (single assignment followed by join)
|
238
|
+
# This is a simplified check - real implementation would be more thorough
|
239
|
+
|
240
|
+
# For now, only convert very simple patterns
|
241
|
+
block = branch_info.block
|
242
|
+
if len(block.instructions) < 2:
|
243
|
+
return False
|
244
|
+
|
245
|
+
# Look for pattern: compare, branch
|
246
|
+
# Could be converted to: select
|
247
|
+
|
248
|
+
return False # Conservative for now
|
249
|
+
|
250
|
+
def _convert_to_select(self, branch_info: BranchInfo) -> bool:
|
251
|
+
"""Convert a predictable branch to a select instruction.
|
252
|
+
|
253
|
+
Args:
|
254
|
+
branch_info: Information about the branch to convert.
|
255
|
+
|
256
|
+
Returns:
|
257
|
+
True if conversion was performed.
|
258
|
+
"""
|
259
|
+
# This would convert patterns like:
|
260
|
+
# if (cond) { x = a; } else { x = b; }
|
261
|
+
# To:
|
262
|
+
# x = select(cond, a, b)
|
263
|
+
|
264
|
+
# For now, not implemented
|
265
|
+
return False
|
266
|
+
|
267
|
+
def _reorder_blocks(self, function: MIRFunction, branches: list[BranchInfo]) -> bool:
|
268
|
+
"""Reorder blocks to optimize branch prediction.
|
269
|
+
|
270
|
+
Args:
|
271
|
+
function: The function to reorder.
|
272
|
+
branches: Branch information.
|
273
|
+
|
274
|
+
Returns:
|
275
|
+
True if blocks were reordered.
|
276
|
+
"""
|
277
|
+
# Strategy: Place likely successors immediately after their predecessors
|
278
|
+
# This makes the likely path fall-through (no taken branch)
|
279
|
+
|
280
|
+
modified = False
|
281
|
+
new_order: list[str] = []
|
282
|
+
visited: set[str] = set()
|
283
|
+
|
284
|
+
# Start with entry block
|
285
|
+
entry = function.cfg.entry_block
|
286
|
+
if not entry:
|
287
|
+
return False
|
288
|
+
|
289
|
+
# Build new order using likely paths
|
290
|
+
work_list: list[str] = [entry.label]
|
291
|
+
|
292
|
+
while work_list:
|
293
|
+
current = work_list.pop(0)
|
294
|
+
if current in visited:
|
295
|
+
continue
|
296
|
+
|
297
|
+
visited.add(current)
|
298
|
+
new_order.append(current)
|
299
|
+
|
300
|
+
# Find branch in this block
|
301
|
+
block = function.cfg.blocks.get(current)
|
302
|
+
if not block:
|
303
|
+
continue
|
304
|
+
|
305
|
+
# Find likely successor
|
306
|
+
for branch_info in branches:
|
307
|
+
if branch_info.block == block:
|
308
|
+
# Add likely target first (so it's next in layout)
|
309
|
+
if branch_info.taken_probability >= 0.5:
|
310
|
+
# Likely to take branch
|
311
|
+
if branch_info.instruction.true_label not in visited:
|
312
|
+
work_list.insert(0, branch_info.instruction.true_label)
|
313
|
+
if branch_info.instruction.false_label and branch_info.instruction.false_label not in visited:
|
314
|
+
work_list.append(branch_info.instruction.false_label)
|
315
|
+
else:
|
316
|
+
# Likely to fall through
|
317
|
+
if branch_info.instruction.false_label and branch_info.instruction.false_label not in visited:
|
318
|
+
work_list.insert(0, branch_info.instruction.false_label)
|
319
|
+
if branch_info.instruction.true_label not in visited:
|
320
|
+
work_list.append(branch_info.instruction.true_label)
|
321
|
+
break
|
322
|
+
else:
|
323
|
+
# No branch, add successors in order
|
324
|
+
# (would need CFG successor information here)
|
325
|
+
pass
|
326
|
+
|
327
|
+
# Add any unvisited blocks
|
328
|
+
for block_name in function.cfg.blocks:
|
329
|
+
if block_name not in visited:
|
330
|
+
new_order.append(block_name)
|
331
|
+
|
332
|
+
# Check if order changed
|
333
|
+
old_order = list(function.cfg.blocks.keys())
|
334
|
+
if new_order != old_order:
|
335
|
+
# Reorder the blocks dictionary
|
336
|
+
# Note: In Python 3.7+, dict preserves insertion order
|
337
|
+
new_blocks = {}
|
338
|
+
for block_name in new_order:
|
339
|
+
if block_name in function.cfg.blocks:
|
340
|
+
new_blocks[block_name] = function.cfg.blocks[block_name]
|
341
|
+
function.cfg.blocks = new_blocks
|
342
|
+
modified = True
|
343
|
+
|
344
|
+
return modified
|
345
|
+
|
346
|
+
def _add_branch_hint(self, branch_info: BranchInfo) -> bool:
|
347
|
+
"""Add branch prediction hint to instruction.
|
348
|
+
|
349
|
+
Args:
|
350
|
+
branch_info: Information about the branch.
|
351
|
+
|
352
|
+
Returns:
|
353
|
+
True if hint was added.
|
354
|
+
"""
|
355
|
+
# Add hint as metadata on the instruction
|
356
|
+
# The VM can use this to optimize branch prediction
|
357
|
+
|
358
|
+
inst = branch_info.instruction
|
359
|
+
|
360
|
+
# Add prediction hint attribute using type: ignore for dynamic attributes
|
361
|
+
if not hasattr(inst, "prediction_hint"):
|
362
|
+
if branch_info.taken_probability >= 0.5:
|
363
|
+
inst.prediction_hint = "likely_taken" # type: ignore[attr-defined]
|
364
|
+
else:
|
365
|
+
inst.prediction_hint = "likely_not_taken" # type: ignore[attr-defined]
|
366
|
+
|
367
|
+
# Also store the probability for more precise optimization
|
368
|
+
inst.taken_probability = branch_info.taken_probability # type: ignore[attr-defined]
|
369
|
+
|
370
|
+
return True
|
371
|
+
|
372
|
+
return False
|