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,272 @@
1
+ """Profile reader for loading persisted profile data.
2
+
3
+ This module implements deserialization of profile data from disk
4
+ for use in profile-guided optimization.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from machine_dialect.mir.profiling.profile_data import (
12
+ BasicBlockProfile,
13
+ BranchProfile,
14
+ FunctionProfile,
15
+ IndirectCallProfile,
16
+ LoopProfile,
17
+ ProfileData,
18
+ )
19
+
20
+
21
+ class ProfileReader:
22
+ """Reads profile data from disk in various formats."""
23
+
24
+ def __init__(self) -> None:
25
+ """Initialize the profile reader."""
26
+ pass
27
+
28
+ def read_json(self, filepath: Path | str) -> ProfileData:
29
+ """Read profile data from JSON format.
30
+
31
+ Args:
32
+ filepath: Path to input file.
33
+
34
+ Returns:
35
+ Loaded profile data.
36
+
37
+ Raises:
38
+ FileNotFoundError: If file doesn't exist.
39
+ json.JSONDecodeError: If file is not valid JSON.
40
+ """
41
+ filepath = Path(filepath)
42
+
43
+ if not filepath.exists():
44
+ raise FileNotFoundError(f"Profile file not found: {filepath}")
45
+
46
+ with open(filepath) as f:
47
+ data = json.load(f)
48
+
49
+ return self._dict_to_profile(data)
50
+
51
+ def read_binary(self, filepath: Path | str) -> ProfileData:
52
+ """Read profile data from binary format.
53
+
54
+ Args:
55
+ filepath: Path to input file.
56
+
57
+ Returns:
58
+ Loaded profile data.
59
+
60
+ Raises:
61
+ FileNotFoundError: If file doesn't exist.
62
+ pickle.UnpicklingError: If file is not valid pickle format.
63
+ """
64
+ import pickle
65
+
66
+ filepath = Path(filepath)
67
+
68
+ if not filepath.exists():
69
+ raise FileNotFoundError(f"Profile file not found: {filepath}")
70
+
71
+ with open(filepath, "rb") as f:
72
+ data = pickle.load(f)
73
+ if not isinstance(data, ProfileData):
74
+ raise ValueError(f"Invalid profile data type: {type(data)}")
75
+ return data
76
+
77
+ def read_auto(self, filepath: Path | str) -> ProfileData:
78
+ """Automatically detect format and read profile data.
79
+
80
+ Args:
81
+ filepath: Path to input file.
82
+
83
+ Returns:
84
+ Loaded profile data.
85
+
86
+ Raises:
87
+ FileNotFoundError: If file doesn't exist.
88
+ ValueError: If format cannot be determined.
89
+ """
90
+ filepath = Path(filepath)
91
+
92
+ if not filepath.exists():
93
+ raise FileNotFoundError(f"Profile file not found: {filepath}")
94
+
95
+ # Try to detect format by extension
96
+ if filepath.suffix == ".json":
97
+ return self.read_json(filepath)
98
+ elif filepath.suffix in [".pkl", ".pickle", ".bin"]:
99
+ return self.read_binary(filepath)
100
+
101
+ # Try to detect by content
102
+ try:
103
+ return self.read_json(filepath)
104
+ except json.JSONDecodeError:
105
+ try:
106
+ return self.read_binary(filepath)
107
+ except Exception as e:
108
+ raise ValueError(f"Cannot determine profile format: {e}") from e
109
+
110
+ def merge_profiles(self, filepaths: list[Path | str]) -> ProfileData:
111
+ """Merge multiple profile files into one.
112
+
113
+ Args:
114
+ filepaths: List of profile file paths.
115
+
116
+ Returns:
117
+ Merged profile data.
118
+ """
119
+ if not filepaths:
120
+ raise ValueError("No profile files provided")
121
+
122
+ # Read first profile as base
123
+ merged = self.read_auto(filepaths[0])
124
+
125
+ # Merge remaining profiles
126
+ for filepath in filepaths[1:]:
127
+ profile = self.read_auto(filepath)
128
+ merged.merge(profile)
129
+
130
+ return merged
131
+
132
+ def _dict_to_profile(self, data: dict[str, Any]) -> ProfileData:
133
+ """Convert dictionary to profile data.
134
+
135
+ Args:
136
+ data: Dictionary representation.
137
+
138
+ Returns:
139
+ Profile data object.
140
+ """
141
+ profile = ProfileData(
142
+ module_name=data.get("module_name", "default"),
143
+ total_samples=data.get("total_samples", 0),
144
+ metadata=data.get("metadata", {}),
145
+ )
146
+
147
+ # Load functions
148
+ for name, func_data in data.get("functions", {}).items():
149
+ profile.functions[name] = self._dict_to_function(func_data)
150
+
151
+ # Load branches
152
+ for loc, branch_data in data.get("branches", {}).items():
153
+ profile.branches[loc] = self._dict_to_branch(branch_data)
154
+
155
+ # Load loops
156
+ for loc, loop_data in data.get("loops", {}).items():
157
+ profile.loops[loc] = self._dict_to_loop(loop_data)
158
+
159
+ # Load blocks
160
+ for loc, block_data in data.get("blocks", {}).items():
161
+ profile.blocks[loc] = self._dict_to_block(block_data)
162
+
163
+ # Load indirect calls
164
+ for loc, call_data in data.get("indirect_calls", {}).items():
165
+ profile.indirect_calls[loc] = self._dict_to_indirect_call(call_data)
166
+
167
+ return profile
168
+
169
+ def _dict_to_function(self, data: dict[str, Any]) -> FunctionProfile:
170
+ """Convert dictionary to function profile."""
171
+ profile = FunctionProfile(
172
+ name=data["name"],
173
+ call_count=data.get("call_count", 0),
174
+ total_cycles=data.get("total_cycles", 0),
175
+ avg_cycles=data.get("avg_cycles", 0.0),
176
+ call_sites=data.get("call_sites", {}),
177
+ hot=data.get("hot", False),
178
+ inline_benefit=data.get("inline_benefit", 0.0),
179
+ )
180
+ return profile
181
+
182
+ def _dict_to_branch(self, data: dict[str, Any]) -> BranchProfile:
183
+ """Convert dictionary to branch profile."""
184
+ profile = BranchProfile(
185
+ location=data["location"],
186
+ taken_count=data.get("taken_count", 0),
187
+ not_taken_count=data.get("not_taken_count", 0),
188
+ taken_probability=data.get("taken_probability", 0.5),
189
+ predictable=data.get("predictable", False),
190
+ )
191
+ return profile
192
+
193
+ def _dict_to_loop(self, data: dict[str, Any]) -> LoopProfile:
194
+ """Convert dictionary to loop profile."""
195
+ min_iter_raw = data.get("min_iterations")
196
+ min_iter: int = 2**31 - 1 # Use max int instead of infinity
197
+ if min_iter_raw is not None:
198
+ min_iter = int(min_iter_raw)
199
+
200
+ profile = LoopProfile(
201
+ location=data["location"],
202
+ entry_count=data.get("entry_count", 0),
203
+ total_iterations=data.get("total_iterations", 0),
204
+ avg_iterations=data.get("avg_iterations", 0.0),
205
+ max_iterations=data.get("max_iterations", 0),
206
+ min_iterations=min_iter,
207
+ hot=data.get("hot", False),
208
+ unroll_benefit=data.get("unroll_benefit", 0.0),
209
+ )
210
+ return profile
211
+
212
+ def _dict_to_block(self, data: dict[str, Any]) -> BasicBlockProfile:
213
+ """Convert dictionary to basic block profile."""
214
+ profile = BasicBlockProfile(
215
+ location=data["location"],
216
+ execution_count=data.get("execution_count", 0),
217
+ instruction_count=data.get("instruction_count", 0),
218
+ total_cycles=data.get("total_cycles", 0),
219
+ avg_cycles=data.get("avg_cycles", 0.0),
220
+ hot=data.get("hot", False),
221
+ )
222
+ return profile
223
+
224
+ def _dict_to_indirect_call(self, data: dict[str, Any]) -> IndirectCallProfile:
225
+ """Convert dictionary to indirect call profile."""
226
+ profile = IndirectCallProfile(
227
+ location=data["location"],
228
+ targets=data.get("targets", {}),
229
+ total_calls=data.get("total_calls", 0),
230
+ most_common_target=data.get("most_common_target"),
231
+ devirtualization_benefit=data.get("devirtualization_benefit", 0.0),
232
+ )
233
+ return profile
234
+
235
+ def validate_profile(self, profile_data: ProfileData) -> list[str]:
236
+ """Validate profile data for consistency.
237
+
238
+ Args:
239
+ profile_data: Profile data to validate.
240
+
241
+ Returns:
242
+ List of validation warnings/errors.
243
+ """
244
+ warnings = []
245
+
246
+ # Check for empty profile
247
+ if profile_data.total_samples == 0:
248
+ warnings.append("Profile has no samples")
249
+
250
+ # Check function consistency
251
+ for name, func in profile_data.functions.items():
252
+ if func.call_count == 0 and func.total_cycles > 0:
253
+ warnings.append(f"Function {name} has cycles but no calls")
254
+ if func.call_count > 0 and func.avg_cycles == 0:
255
+ warnings.append(f"Function {name} has calls but no average cycles")
256
+
257
+ # Check branch consistency
258
+ for loc, branch in profile_data.branches.items():
259
+ total = branch.taken_count + branch.not_taken_count
260
+ if total == 0:
261
+ warnings.append(f"Branch {loc} has no executions")
262
+ elif abs(branch.taken_probability - (branch.taken_count / total)) > 0.01:
263
+ warnings.append(f"Branch {loc} has inconsistent probability")
264
+
265
+ # Check loop consistency
266
+ for loc, loop in profile_data.loops.items():
267
+ if loop.entry_count == 0 and loop.total_iterations > 0:
268
+ warnings.append(f"Loop {loc} has iterations but no entries")
269
+ if loop.max_iterations < loop.min_iterations:
270
+ warnings.append(f"Loop {loc} has max < min iterations")
271
+
272
+ return warnings
@@ -0,0 +1,226 @@
1
+ """Profile writer for persisting profile data.
2
+
3
+ This module implements serialization of profile data to disk for
4
+ reuse across compilation sessions.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from machine_dialect.mir.profiling.profile_data import (
12
+ BasicBlockProfile,
13
+ BranchProfile,
14
+ FunctionProfile,
15
+ IndirectCallProfile,
16
+ LoopProfile,
17
+ ProfileData,
18
+ )
19
+
20
+
21
+ class ProfileWriter:
22
+ """Writes profile data to disk in various formats."""
23
+
24
+ def __init__(self) -> None:
25
+ """Initialize the profile writer."""
26
+ pass
27
+
28
+ def write_json(self, profile_data: ProfileData, filepath: Path | str) -> None:
29
+ """Write profile data to JSON format.
30
+
31
+ Args:
32
+ profile_data: Profile data to write.
33
+ filepath: Path to output file.
34
+ """
35
+ filepath = Path(filepath)
36
+ filepath.parent.mkdir(parents=True, exist_ok=True)
37
+
38
+ # Convert profile data to JSON-serializable format
39
+ data = self._profile_to_dict(profile_data)
40
+
41
+ # Write to file with pretty formatting
42
+ with open(filepath, "w") as f:
43
+ json.dump(data, f, indent=2, sort_keys=True)
44
+
45
+ def write_binary(self, profile_data: ProfileData, filepath: Path | str) -> None:
46
+ """Write profile data to efficient binary format.
47
+
48
+ Args:
49
+ profile_data: Profile data to write.
50
+ filepath: Path to output file.
51
+ """
52
+ import pickle
53
+
54
+ filepath = Path(filepath)
55
+ filepath.parent.mkdir(parents=True, exist_ok=True)
56
+
57
+ # Use pickle for binary serialization
58
+ with open(filepath, "wb") as f:
59
+ pickle.dump(profile_data, f, protocol=pickle.HIGHEST_PROTOCOL)
60
+
61
+ def write_summary(self, profile_data: ProfileData, filepath: Path | str) -> None:
62
+ """Write human-readable profile summary.
63
+
64
+ Args:
65
+ profile_data: Profile data to summarize.
66
+ filepath: Path to output file.
67
+ """
68
+ filepath = Path(filepath)
69
+ filepath.parent.mkdir(parents=True, exist_ok=True)
70
+
71
+ with open(filepath, "w") as f:
72
+ # Write header
73
+ f.write(f"Profile Summary for Module: {profile_data.module_name}\n")
74
+ f.write("=" * 60 + "\n\n")
75
+
76
+ # Write statistics
77
+ summary = profile_data.get_summary()
78
+ f.write(f"Total Samples: {summary['total_samples']}\n\n")
79
+
80
+ # Function statistics
81
+ f.write("Functions:\n")
82
+ f.write(f" Total: {summary['functions']['total']}\n")
83
+ f.write(f" Hot: {summary['functions']['hot']}\n\n")
84
+
85
+ # Hot functions details
86
+ if profile_data.functions:
87
+ f.write("Hot Functions (Top 10):\n")
88
+ sorted_funcs = sorted(profile_data.functions.values(), key=lambda x: x.call_count, reverse=True)[:10]
89
+ for func in sorted_funcs:
90
+ f.write(f" {func.name}:\n")
91
+ f.write(f" Calls: {func.call_count}\n")
92
+ f.write(f" Avg Cycles: {func.avg_cycles:.2f}\n")
93
+ if func.inline_benefit > 0:
94
+ f.write(f" Inline Benefit: {func.inline_benefit:.2f}\n")
95
+ f.write("\n")
96
+
97
+ # Branch statistics
98
+ f.write("Branches:\n")
99
+ f.write(f" Total: {summary['branches']['total']}\n")
100
+ f.write(f" Predictable: {summary['branches']['predictable']}\n\n")
101
+
102
+ # Predictable branches details
103
+ predictable = [b for b in profile_data.branches.values() if b.predictable]
104
+ if predictable:
105
+ f.write("Predictable Branches (Top 10):\n")
106
+ for branch in predictable[:10]:
107
+ f.write(f" {branch.location}:\n")
108
+ f.write(f" Taken: {branch.taken_probability:.1%}\n")
109
+ f.write("\n")
110
+
111
+ # Loop statistics
112
+ f.write("Loops:\n")
113
+ f.write(f" Total: {summary['loops']['total']}\n")
114
+ f.write(f" Hot: {summary['loops']['hot']}\n\n")
115
+
116
+ # Hot loops details
117
+ hot_loops = [loop for loop in profile_data.loops.values() if loop.hot]
118
+ if hot_loops:
119
+ f.write("Hot Loops (Top 10):\n")
120
+ sorted_loops = sorted(hot_loops, key=lambda x: x.total_iterations, reverse=True)[:10]
121
+ for loop in sorted_loops:
122
+ f.write(f" {loop.location}:\n")
123
+ f.write(f" Iterations: {loop.total_iterations}\n")
124
+ f.write(f" Avg per Entry: {loop.avg_iterations:.2f}\n")
125
+ if loop.unroll_benefit > 0:
126
+ f.write(f" Unroll Benefit: {loop.unroll_benefit:.2f}\n")
127
+ f.write("\n")
128
+
129
+ # Indirect call statistics
130
+ f.write("Indirect Calls:\n")
131
+ f.write(f" Total: {summary['indirect_calls']['total']}\n")
132
+ f.write(f" Devirtualizable: {summary['indirect_calls']['devirtualizable']}\n\n")
133
+
134
+ # Devirtualization opportunities
135
+ devirt = [c for c in profile_data.indirect_calls.values() if c.devirtualization_benefit > 50]
136
+ if devirt:
137
+ f.write("Devirtualization Opportunities:\n")
138
+ for call in devirt:
139
+ f.write(f" {call.location}:\n")
140
+ f.write(f" Target: {call.most_common_target}\n")
141
+ f.write(f" Benefit: {call.devirtualization_benefit:.2f}\n")
142
+
143
+ def _profile_to_dict(self, profile_data: ProfileData) -> dict[str, Any]:
144
+ """Convert profile data to dictionary.
145
+
146
+ Args:
147
+ profile_data: Profile data to convert.
148
+
149
+ Returns:
150
+ Dictionary representation.
151
+ """
152
+ return {
153
+ "module_name": profile_data.module_name,
154
+ "total_samples": profile_data.total_samples,
155
+ "metadata": profile_data.metadata,
156
+ "functions": {name: self._function_to_dict(prof) for name, prof in profile_data.functions.items()},
157
+ "branches": {loc: self._branch_to_dict(prof) for loc, prof in profile_data.branches.items()},
158
+ "loops": {loc: self._loop_to_dict(prof) for loc, prof in profile_data.loops.items()},
159
+ "blocks": {loc: self._block_to_dict(prof) for loc, prof in profile_data.blocks.items()},
160
+ "indirect_calls": {
161
+ loc: self._indirect_call_to_dict(prof) for loc, prof in profile_data.indirect_calls.items()
162
+ },
163
+ }
164
+
165
+ def _function_to_dict(self, profile: FunctionProfile) -> dict[str, Any]:
166
+ """Convert function profile to dictionary."""
167
+ return {
168
+ "name": profile.name,
169
+ "call_count": profile.call_count,
170
+ "total_cycles": profile.total_cycles,
171
+ "avg_cycles": profile.avg_cycles,
172
+ "call_sites": profile.call_sites,
173
+ "hot": profile.hot,
174
+ "inline_benefit": profile.inline_benefit,
175
+ }
176
+
177
+ def _branch_to_dict(self, profile: BranchProfile) -> dict[str, Any]:
178
+ """Convert branch profile to dictionary."""
179
+ return {
180
+ "location": profile.location,
181
+ "taken_count": profile.taken_count,
182
+ "not_taken_count": profile.not_taken_count,
183
+ "taken_probability": profile.taken_probability,
184
+ "predictable": profile.predictable,
185
+ }
186
+
187
+ def _loop_to_dict(self, profile: LoopProfile) -> dict[str, Any]:
188
+ """Convert loop profile to dictionary."""
189
+ # Handle max int as None for JSON
190
+ min_iter = profile.min_iterations
191
+ if min_iter == 2**31 - 1: # Max int sentinel value
192
+ min_iter_value: int | None = None
193
+ else:
194
+ min_iter_value = min_iter
195
+
196
+ return {
197
+ "location": profile.location,
198
+ "entry_count": profile.entry_count,
199
+ "total_iterations": profile.total_iterations,
200
+ "avg_iterations": profile.avg_iterations,
201
+ "max_iterations": profile.max_iterations,
202
+ "min_iterations": min_iter_value,
203
+ "hot": profile.hot,
204
+ "unroll_benefit": profile.unroll_benefit,
205
+ }
206
+
207
+ def _block_to_dict(self, profile: BasicBlockProfile) -> dict[str, Any]:
208
+ """Convert basic block profile to dictionary."""
209
+ return {
210
+ "location": profile.location,
211
+ "execution_count": profile.execution_count,
212
+ "instruction_count": profile.instruction_count,
213
+ "total_cycles": profile.total_cycles,
214
+ "avg_cycles": profile.avg_cycles,
215
+ "hot": profile.hot,
216
+ }
217
+
218
+ def _indirect_call_to_dict(self, profile: IndirectCallProfile) -> dict[str, Any]:
219
+ """Convert indirect call profile to dictionary."""
220
+ return {
221
+ "location": profile.location,
222
+ "targets": profile.targets,
223
+ "total_calls": profile.total_calls,
224
+ "most_common_target": profile.most_common_target,
225
+ "devirtualization_benefit": profile.devirtualization_benefit,
226
+ }