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,455 @@
|
|
1
|
+
"""MIR validation and verification framework.
|
2
|
+
|
3
|
+
This module provides validation and verification for MIR programs,
|
4
|
+
ensuring correctness and well-formedness of the intermediate representation.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from machine_dialect.mir.basic_block import CFG, BasicBlock
|
8
|
+
from machine_dialect.mir.mir_function import MIRFunction
|
9
|
+
from machine_dialect.mir.mir_instructions import (
|
10
|
+
BinaryOp,
|
11
|
+
Call,
|
12
|
+
ConditionalJump,
|
13
|
+
Copy,
|
14
|
+
Jump,
|
15
|
+
LoadConst,
|
16
|
+
LoadVar,
|
17
|
+
MIRInstruction,
|
18
|
+
Phi,
|
19
|
+
Return,
|
20
|
+
StoreVar,
|
21
|
+
UnaryOp,
|
22
|
+
)
|
23
|
+
from machine_dialect.mir.mir_module import MIRModule
|
24
|
+
from machine_dialect.mir.mir_types import MIRType
|
25
|
+
from machine_dialect.mir.mir_values import FunctionRef, Temp, Variable
|
26
|
+
|
27
|
+
|
28
|
+
class ValidationError(Exception):
|
29
|
+
"""Exception raised for validation errors."""
|
30
|
+
|
31
|
+
pass
|
32
|
+
|
33
|
+
|
34
|
+
class MIRValidator:
|
35
|
+
"""Validates MIR modules, functions, and instructions."""
|
36
|
+
|
37
|
+
def __init__(self) -> None:
|
38
|
+
"""Initialize the validator."""
|
39
|
+
self.errors: list[str] = []
|
40
|
+
self.warnings: list[str] = []
|
41
|
+
|
42
|
+
def validate_module(self, module: MIRModule) -> bool:
|
43
|
+
"""Validate an entire MIR module.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
module: The module to validate.
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
True if validation passes, False otherwise.
|
50
|
+
"""
|
51
|
+
self.errors.clear()
|
52
|
+
self.warnings.clear()
|
53
|
+
|
54
|
+
# Check module structure
|
55
|
+
if not module.name:
|
56
|
+
self.errors.append("Module must have a name")
|
57
|
+
|
58
|
+
# Validate each function
|
59
|
+
for func_name, func in module.functions.items():
|
60
|
+
if not self._validate_function(func, func_name):
|
61
|
+
self.errors.append(f"Function '{func_name}' validation failed")
|
62
|
+
|
63
|
+
# Check main function if specified
|
64
|
+
if module.main_function:
|
65
|
+
if module.main_function not in module.functions:
|
66
|
+
self.errors.append(f"Main function '{module.main_function}' not found in module")
|
67
|
+
|
68
|
+
return len(self.errors) == 0
|
69
|
+
|
70
|
+
def _validate_function(self, func: MIRFunction, name: str) -> bool:
|
71
|
+
"""Validate a MIR function.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
func: The function to validate.
|
75
|
+
name: The function name.
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
True if validation passes, False otherwise.
|
79
|
+
"""
|
80
|
+
func_errors = []
|
81
|
+
|
82
|
+
# Check function name matches
|
83
|
+
if func.name != name:
|
84
|
+
func_errors.append(f"Function name mismatch: {func.name} vs {name}")
|
85
|
+
|
86
|
+
# Validate parameters
|
87
|
+
param_names = set()
|
88
|
+
for param in func.params:
|
89
|
+
if not isinstance(param, Variable):
|
90
|
+
func_errors.append(f"Parameter must be Variable, got {type(param)}")
|
91
|
+
elif param.name in param_names:
|
92
|
+
func_errors.append(f"Duplicate parameter name: {param.name}")
|
93
|
+
else:
|
94
|
+
param_names.add(param.name)
|
95
|
+
|
96
|
+
# Validate CFG
|
97
|
+
if not self._validate_cfg(func.cfg):
|
98
|
+
func_errors.append("CFG validation failed")
|
99
|
+
|
100
|
+
# Validate each block
|
101
|
+
for block_label, block in func.cfg.blocks.items():
|
102
|
+
if not self._validate_block(block, block_label, func):
|
103
|
+
func_errors.append(f"Block '{block_label}' validation failed")
|
104
|
+
|
105
|
+
# Check that all locals are defined
|
106
|
+
for local in func.locals.values():
|
107
|
+
if not isinstance(local, Variable):
|
108
|
+
func_errors.append(f"Local must be Variable, got {type(local)}")
|
109
|
+
|
110
|
+
# Check that all temporaries are valid
|
111
|
+
for temp in func.temporaries:
|
112
|
+
if not isinstance(temp, Temp):
|
113
|
+
func_errors.append(f"Temporary must be Temp, got {type(temp)}")
|
114
|
+
|
115
|
+
if func_errors:
|
116
|
+
self.errors.extend(func_errors)
|
117
|
+
return False
|
118
|
+
return True
|
119
|
+
|
120
|
+
def _validate_cfg(self, cfg: CFG) -> bool:
|
121
|
+
"""Validate a control flow graph.
|
122
|
+
|
123
|
+
Args:
|
124
|
+
cfg: The CFG to validate.
|
125
|
+
|
126
|
+
Returns:
|
127
|
+
True if validation passes, False otherwise.
|
128
|
+
"""
|
129
|
+
cfg_errors = []
|
130
|
+
|
131
|
+
# Check entry block exists
|
132
|
+
if cfg.entry_block is None:
|
133
|
+
cfg_errors.append("CFG must have an entry block")
|
134
|
+
elif cfg.entry_block not in cfg.blocks.values():
|
135
|
+
cfg_errors.append("Entry block not in CFG blocks")
|
136
|
+
|
137
|
+
# Check block connectivity
|
138
|
+
for block_label, block in cfg.blocks.items():
|
139
|
+
if block.label != block_label:
|
140
|
+
cfg_errors.append(f"Block label mismatch: {block.label} vs {block_label}")
|
141
|
+
|
142
|
+
# Check predecessors
|
143
|
+
for pred in block.predecessors:
|
144
|
+
if block not in pred.successors:
|
145
|
+
cfg_errors.append(f"Inconsistent CFG: {pred.label} -> {block.label} not bidirectional")
|
146
|
+
|
147
|
+
# Check successors
|
148
|
+
for succ in block.successors:
|
149
|
+
if block not in succ.predecessors:
|
150
|
+
cfg_errors.append(f"Inconsistent CFG: {block.label} -> {succ.label} not bidirectional")
|
151
|
+
|
152
|
+
# Check for unreachable blocks
|
153
|
+
if cfg.entry_block:
|
154
|
+
reachable = self._compute_reachable_blocks(cfg)
|
155
|
+
for block in cfg.blocks.values():
|
156
|
+
if block not in reachable:
|
157
|
+
self.warnings.append(f"Block '{block.label}' is unreachable")
|
158
|
+
|
159
|
+
if cfg_errors:
|
160
|
+
self.errors.extend(cfg_errors)
|
161
|
+
return False
|
162
|
+
return True
|
163
|
+
|
164
|
+
def _compute_reachable_blocks(self, cfg: CFG) -> set[BasicBlock]:
|
165
|
+
"""Compute reachable blocks from entry.
|
166
|
+
|
167
|
+
Args:
|
168
|
+
cfg: The control flow graph.
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
Set of reachable blocks.
|
172
|
+
"""
|
173
|
+
if not cfg.entry_block:
|
174
|
+
return set()
|
175
|
+
|
176
|
+
reachable = set()
|
177
|
+
worklist = [cfg.entry_block]
|
178
|
+
|
179
|
+
while worklist:
|
180
|
+
block = worklist.pop()
|
181
|
+
if block in reachable:
|
182
|
+
continue
|
183
|
+
reachable.add(block)
|
184
|
+
|
185
|
+
# Add successors to worklist
|
186
|
+
for succ in block.successors:
|
187
|
+
if succ not in reachable:
|
188
|
+
worklist.append(succ)
|
189
|
+
|
190
|
+
return reachable
|
191
|
+
|
192
|
+
def _validate_block(self, block: BasicBlock, label: str, func: MIRFunction) -> bool:
|
193
|
+
"""Validate a basic block.
|
194
|
+
|
195
|
+
Args:
|
196
|
+
block: The block to validate.
|
197
|
+
label: Expected block label.
|
198
|
+
func: The containing function.
|
199
|
+
|
200
|
+
Returns:
|
201
|
+
True if validation passes, False otherwise.
|
202
|
+
"""
|
203
|
+
block_errors = []
|
204
|
+
|
205
|
+
# Check label
|
206
|
+
if block.label != label:
|
207
|
+
block_errors.append(f"Block label mismatch: {block.label} vs {label}")
|
208
|
+
|
209
|
+
# Validate instructions
|
210
|
+
for i, inst in enumerate(block.instructions):
|
211
|
+
if not self._validate_instruction(inst, block, func):
|
212
|
+
block_errors.append(f"Instruction {i} validation failed: {inst}")
|
213
|
+
|
214
|
+
# Check terminator
|
215
|
+
if block.instructions:
|
216
|
+
last_inst = block.instructions[-1]
|
217
|
+
if isinstance(last_inst, Jump | ConditionalJump | Return):
|
218
|
+
# Valid terminator
|
219
|
+
pass
|
220
|
+
else:
|
221
|
+
# Check if block has successors
|
222
|
+
if block.successors:
|
223
|
+
self.warnings.append(f"Block '{block.label}' has successors but no terminator")
|
224
|
+
|
225
|
+
# Validate phi nodes
|
226
|
+
for phi in block.phi_nodes:
|
227
|
+
if not self._validate_instruction(phi, block, func):
|
228
|
+
return False
|
229
|
+
|
230
|
+
# Check that no phi nodes are in regular instructions (they should be in phi_nodes)
|
231
|
+
for inst in block.instructions:
|
232
|
+
if isinstance(inst, Phi):
|
233
|
+
block_errors.append("Phi nodes must be in phi_nodes list, not instructions")
|
234
|
+
|
235
|
+
if block_errors:
|
236
|
+
self.errors.extend(block_errors)
|
237
|
+
return False
|
238
|
+
return True
|
239
|
+
|
240
|
+
def _validate_instruction(self, inst: MIRInstruction, block: BasicBlock, func: MIRFunction) -> bool:
|
241
|
+
"""Validate a single instruction.
|
242
|
+
|
243
|
+
Args:
|
244
|
+
inst: The instruction to validate.
|
245
|
+
block: The containing block.
|
246
|
+
func: The containing function.
|
247
|
+
|
248
|
+
Returns:
|
249
|
+
True if validation passes, False otherwise.
|
250
|
+
"""
|
251
|
+
# Type-specific validation
|
252
|
+
if isinstance(inst, BinaryOp):
|
253
|
+
return self._validate_binary_op(inst)
|
254
|
+
elif isinstance(inst, UnaryOp):
|
255
|
+
return self._validate_unary_op(inst)
|
256
|
+
elif isinstance(inst, Call):
|
257
|
+
return self._validate_call(inst, func)
|
258
|
+
elif isinstance(inst, Jump):
|
259
|
+
return self._validate_jump(inst, func)
|
260
|
+
elif isinstance(inst, ConditionalJump):
|
261
|
+
return self._validate_conditional_jump(inst, func)
|
262
|
+
elif isinstance(inst, Phi):
|
263
|
+
return self._validate_phi(inst, block, func)
|
264
|
+
elif isinstance(inst, Return):
|
265
|
+
return self._validate_return(inst, func)
|
266
|
+
elif isinstance(inst, Copy | LoadConst | LoadVar | StoreVar):
|
267
|
+
# These are generally valid if their operands are valid
|
268
|
+
return True
|
269
|
+
else:
|
270
|
+
# Unknown instruction type is valid (for extensibility)
|
271
|
+
return True
|
272
|
+
|
273
|
+
def _validate_binary_op(self, inst: BinaryOp) -> bool:
|
274
|
+
"""Validate a binary operation.
|
275
|
+
|
276
|
+
Args:
|
277
|
+
inst: The binary operation.
|
278
|
+
|
279
|
+
Returns:
|
280
|
+
True if valid.
|
281
|
+
"""
|
282
|
+
valid_ops = {
|
283
|
+
"+",
|
284
|
+
"-",
|
285
|
+
"*",
|
286
|
+
"/",
|
287
|
+
"%",
|
288
|
+
"^",
|
289
|
+
"==",
|
290
|
+
"!=",
|
291
|
+
"<",
|
292
|
+
">",
|
293
|
+
"<=",
|
294
|
+
">=",
|
295
|
+
"and",
|
296
|
+
"or",
|
297
|
+
}
|
298
|
+
if inst.op not in valid_ops:
|
299
|
+
self.errors.append(f"Invalid binary operator: {inst.op}")
|
300
|
+
return False
|
301
|
+
return True
|
302
|
+
|
303
|
+
def _validate_unary_op(self, inst: UnaryOp) -> bool:
|
304
|
+
"""Validate a unary operation.
|
305
|
+
|
306
|
+
Args:
|
307
|
+
inst: The unary operation.
|
308
|
+
|
309
|
+
Returns:
|
310
|
+
True if valid.
|
311
|
+
"""
|
312
|
+
valid_ops = {"-", "not"}
|
313
|
+
if inst.op not in valid_ops:
|
314
|
+
self.errors.append(f"Invalid unary operator: {inst.op}")
|
315
|
+
return False
|
316
|
+
return True
|
317
|
+
|
318
|
+
def _validate_call(self, inst: Call, func: MIRFunction) -> bool:
|
319
|
+
"""Validate a call instruction.
|
320
|
+
|
321
|
+
Args:
|
322
|
+
inst: The call instruction.
|
323
|
+
func: The containing function.
|
324
|
+
|
325
|
+
Returns:
|
326
|
+
True if valid.
|
327
|
+
"""
|
328
|
+
if not isinstance(inst.func, FunctionRef):
|
329
|
+
self.errors.append(f"Call target must be FunctionRef, got {type(inst.func)}")
|
330
|
+
return False
|
331
|
+
return True
|
332
|
+
|
333
|
+
def _validate_jump(self, inst: Jump, func: MIRFunction) -> bool:
|
334
|
+
"""Validate a jump instruction.
|
335
|
+
|
336
|
+
Args:
|
337
|
+
inst: The jump instruction.
|
338
|
+
func: The containing function.
|
339
|
+
|
340
|
+
Returns:
|
341
|
+
True if valid.
|
342
|
+
"""
|
343
|
+
if inst.label not in func.cfg.blocks:
|
344
|
+
self.errors.append(f"Jump target '{inst.label}' not found")
|
345
|
+
return False
|
346
|
+
return True
|
347
|
+
|
348
|
+
def _validate_conditional_jump(self, inst: ConditionalJump, func: MIRFunction) -> bool:
|
349
|
+
"""Validate a conditional jump instruction.
|
350
|
+
|
351
|
+
Args:
|
352
|
+
inst: The conditional jump instruction.
|
353
|
+
func: The containing function.
|
354
|
+
|
355
|
+
Returns:
|
356
|
+
True if valid.
|
357
|
+
"""
|
358
|
+
if inst.true_label not in func.cfg.blocks:
|
359
|
+
self.errors.append(f"Jump target '{inst.true_label}' not found")
|
360
|
+
return False
|
361
|
+
if inst.false_label and inst.false_label not in func.cfg.blocks:
|
362
|
+
self.errors.append(f"Jump target '{inst.false_label}' not found")
|
363
|
+
return False
|
364
|
+
return True
|
365
|
+
|
366
|
+
def _validate_phi(self, inst: Phi, block: BasicBlock, func: MIRFunction) -> bool:
|
367
|
+
"""Validate a phi node.
|
368
|
+
|
369
|
+
Args:
|
370
|
+
inst: The phi node.
|
371
|
+
block: The containing block.
|
372
|
+
func: The containing function.
|
373
|
+
|
374
|
+
Returns:
|
375
|
+
True if valid.
|
376
|
+
"""
|
377
|
+
# Check that incoming blocks are predecessors
|
378
|
+
incoming_labels = {label for _, label in inst.incoming}
|
379
|
+
pred_labels = {pred.label for pred in block.predecessors}
|
380
|
+
|
381
|
+
for label in incoming_labels:
|
382
|
+
if label not in pred_labels:
|
383
|
+
self.errors.append(f"Phi node has incoming from '{label}' which is not a predecessor")
|
384
|
+
return False
|
385
|
+
|
386
|
+
# Warn if missing predecessors
|
387
|
+
for pred_label in pred_labels:
|
388
|
+
if pred_label not in incoming_labels:
|
389
|
+
self.warnings.append(f"Phi node missing incoming value from predecessor '{pred_label}'")
|
390
|
+
|
391
|
+
return True
|
392
|
+
|
393
|
+
def _validate_return(self, inst: Return, func: MIRFunction) -> bool:
|
394
|
+
"""Validate a return instruction.
|
395
|
+
|
396
|
+
Args:
|
397
|
+
inst: The return instruction.
|
398
|
+
func: The containing function.
|
399
|
+
|
400
|
+
Returns:
|
401
|
+
True if valid.
|
402
|
+
"""
|
403
|
+
# Check return type consistency
|
404
|
+
if func.return_type == MIRType.EMPTY:
|
405
|
+
if inst.value is not None:
|
406
|
+
self.warnings.append("Function returns EMPTY but return has value")
|
407
|
+
else:
|
408
|
+
if inst.value is None:
|
409
|
+
self.warnings.append(f"Function returns {func.return_type} but return has no value")
|
410
|
+
|
411
|
+
return True
|
412
|
+
|
413
|
+
def get_errors(self) -> list[str]:
|
414
|
+
"""Get validation errors.
|
415
|
+
|
416
|
+
Returns:
|
417
|
+
List of error messages.
|
418
|
+
"""
|
419
|
+
return self.errors.copy()
|
420
|
+
|
421
|
+
def get_warnings(self) -> list[str]:
|
422
|
+
"""Get validation warnings.
|
423
|
+
|
424
|
+
Returns:
|
425
|
+
List of warning messages.
|
426
|
+
"""
|
427
|
+
return self.warnings.copy()
|
428
|
+
|
429
|
+
|
430
|
+
def validate_module(module: MIRModule) -> tuple[bool, list[str], list[str]]:
|
431
|
+
"""Validate a MIR module.
|
432
|
+
|
433
|
+
Args:
|
434
|
+
module: The module to validate.
|
435
|
+
|
436
|
+
Returns:
|
437
|
+
Tuple of (success, errors, warnings).
|
438
|
+
"""
|
439
|
+
validator = MIRValidator()
|
440
|
+
success = validator.validate_module(module)
|
441
|
+
return success, validator.get_errors(), validator.get_warnings()
|
442
|
+
|
443
|
+
|
444
|
+
def validate_function(func: MIRFunction) -> tuple[bool, list[str], list[str]]:
|
445
|
+
"""Validate a MIR function.
|
446
|
+
|
447
|
+
Args:
|
448
|
+
func: The function to validate.
|
449
|
+
|
450
|
+
Returns:
|
451
|
+
Tuple of (success, errors, warnings).
|
452
|
+
"""
|
453
|
+
validator = MIRValidator()
|
454
|
+
success = validator._validate_function(func, func.name)
|
455
|
+
return success, validator.get_errors(), validator.get_warnings()
|