omena_transform_passes/runtime/
planner.rs1use omena_transform_cst::{
8 TRANSFORM_PASS_CATALOG_LEN, TransformDagEdgeV0, TransformLayer, TransformPassClassV0,
9 TransformPassContractV0, TransformPassDescriptorV0, TransformPassKind,
10 all_transform_pass_kinds, default_transform_dag_edges, default_transform_pass_contracts,
11 default_transform_pass_descriptors, transform_build_profile_from_passes,
12};
13
14use crate::{
15 TransformPassDispatchKindV0, TransformPassExecutionStatus, TransformPassPlanV0,
16 TransformPassRegistryEntryV0, TransformPassRegistryV0, TransformPassesBoundarySummaryV0,
17 TransformPlanPassConflictV0,
18};
19
20pub fn summarize_omena_transform_passes_boundary() -> TransformPassesBoundarySummaryV0 {
21 let registry = default_transform_pass_registry();
22 let registry_entries = registry.entries.clone();
23 let pass_count = registry_entries.len();
24 let semantic_aware_pass_count = registry_entries
25 .iter()
26 .filter(|entry| entry.contract.layer == TransformLayer::SemanticAware)
27 .count();
28 let cascade_aware_pass_count = registry_entries
29 .iter()
30 .filter(|entry| entry.contract.reads_cascade_model)
31 .count();
32 let structural_pass_count = registry_entries
33 .iter()
34 .filter(|entry| entry.descriptor.pass_class == TransformPassClassV0::Structural)
35 .count();
36 let text_local_pass_count = registry_entries
37 .iter()
38 .filter(|entry| entry.descriptor.pass_class == TransformPassClassV0::TextLocal)
39 .count();
40 let module_evaluation_pass_count = registry_entries
41 .iter()
42 .filter(|entry| entry.descriptor.pass_class == TransformPassClassV0::ModuleEvaluation)
43 .count();
44
45 TransformPassesBoundarySummaryV0 {
46 schema_version: "0",
47 product: "omena-transform-passes.boundary",
48 registry_entries,
49 dag_edges: default_transform_dag_edges(),
50 pass_count,
51 full_catalog_registered: pass_count == TRANSFORM_PASS_CATALOG_LEN,
52 semantic_aware_pass_count,
53 cascade_aware_pass_count,
54 structural_pass_count,
55 text_local_pass_count,
56 module_evaluation_pass_count,
57 planner_enforces_dag_edges: true,
58 planner_uses_pass_descriptors: true,
59 ordinal_has_execution_semantics: false,
60 execution_runtime_ready: true,
61 incremental_execution_runtime_ready: true,
62 module_evaluation_native_output_marker: "nativeEditOutput",
63 module_evaluation_requires_native_product_output: true,
64 module_evaluation_requires_oracle_readiness: true,
65 module_evaluation_legacy_output_is_oracle_only: true,
66 module_evaluation_preserves_source_without_native_output: true,
67 implemented_mutation_pass_ids: implemented_mutation_pass_ids(),
68 next_surfaces: Vec::new(),
69 }
70}
71
72pub fn plan_transform_passes(requested: &[TransformPassKind]) -> TransformPassPlanV0 {
73 let requested_pass_ids = requested.iter().map(|pass| pass.id()).collect::<Vec<_>>();
74 let registry = default_transform_pass_registry();
75 let dag_edges = default_transform_dag_edges();
76 let requested_unique = dedupe_requested_passes(requested);
77 let conflicting_unordered_pass_pairs = conflicting_unordered_pass_pairs(
78 requested_unique.as_slice(),
79 registry.entries.as_slice(),
80 dag_edges.as_slice(),
81 );
82 let ordered_passes = order_passes_by_dag(requested);
83 let ordered_pass_ids = ordered_passes
84 .iter()
85 .map(|pass| pass.id())
86 .collect::<Vec<_>>();
87 let satisfied_dag_edge_count = dag_edges
88 .iter()
89 .filter(|edge| {
90 edge_applies(edge, &ordered_pass_ids) && edge_is_satisfied(edge, &ordered_pass_ids)
91 })
92 .count();
93 let violated_dag_edge_count = dag_edges
94 .iter()
95 .filter(|edge| {
96 edge_applies(edge, &ordered_pass_ids) && !edge_is_satisfied(edge, &ordered_pass_ids)
97 })
98 .count();
99
100 TransformPassPlanV0 {
101 schema_version: "0",
102 product: "omena-transform-passes.plan",
103 build_profile: transform_build_profile_from_passes(
104 "descriptor-ordered-transform-plan",
105 ordered_passes.as_slice(),
106 ),
107 requested_pass_ids,
108 ordered_pass_ids,
109 satisfied_dag_edge_count,
110 violated_dag_edge_count,
111 all_requested_registered: requested.iter().all(pass_is_registered),
112 conflicting_unordered_pass_pairs,
113 }
114}
115
116pub fn plan_transform_passes_checked(
117 requested: &[TransformPassKind],
118) -> Result<TransformPassPlanV0, TransformPlanPassConflictV0> {
119 let plan = plan_transform_passes(requested);
120 if let Some(conflict) = plan.conflicting_unordered_pass_pairs.first().cloned() {
121 Err(conflict)
122 } else {
123 Ok(plan)
124 }
125}
126
127#[cfg(feature = "lawvere-trace")]
128pub fn plan_transform_passes_parallel_lawvere_layers(
129 requested: &[TransformPassKind],
130) -> omena_lawvere::TransformPassParallelPlanV0 {
131 omena_lawvere::plan_transform_pass_parallel_layers_v0(requested)
132}
133
134pub fn implemented_mutation_pass_ids() -> Vec<&'static str> {
135 default_transform_pass_registry()
136 .entries
137 .into_iter()
138 .filter(|entry| entry.contract.executes_mutation)
139 .map(|entry| entry.contract.id)
140 .collect()
141}
142
143pub fn default_transform_pass_registry() -> TransformPassRegistryV0 {
144 let contracts = default_transform_pass_contracts();
145 let entries = default_transform_pass_descriptors()
146 .into_iter()
147 .filter_map(|descriptor| {
148 contract_for_pass(descriptor.kind, contracts.as_slice())
149 .cloned()
150 .map(|contract| registry_entry_for_descriptor(contract, descriptor))
151 })
152 .collect::<Vec<_>>();
153 TransformPassRegistryV0 {
154 schema_version: "0",
155 product: "omena-transform-passes.pass-registry",
156 entries,
157 }
158}
159
160fn registry_entry_for_descriptor(
161 contract: TransformPassContractV0,
162 descriptor: TransformPassDescriptorV0,
163) -> TransformPassRegistryEntryV0 {
164 let module_family = contract.family;
165 let dispatch_kind = dispatch_kind_for_descriptor(&descriptor);
166 TransformPassRegistryEntryV0 {
167 module_family,
168 query_family: query_family_for_pass(contract.kind),
169 dispatch_kind,
170 execution_status: TransformPassExecutionStatus::RegistryAndPlannerReady,
171 contract,
172 descriptor,
173 }
174}
175
176fn dispatch_kind_for_descriptor(
177 descriptor: &TransformPassDescriptorV0,
178) -> TransformPassDispatchKindV0 {
179 match descriptor.pass_class {
180 TransformPassClassV0::TextLocal => TransformPassDispatchKindV0::TextLocalSliceRewrite,
181 TransformPassClassV0::Structural => TransformPassDispatchKindV0::StructuralIrTransaction,
182 TransformPassClassV0::ModuleEvaluation => {
183 TransformPassDispatchKindV0::ModuleEvaluationHandler
184 }
185 TransformPassClassV0::Emission => TransformPassDispatchKindV0::EmissionBoundary,
186 }
187}
188
189fn query_family_for_pass(kind: TransformPassKind) -> &'static str {
190 match kind.layer() {
191 TransformLayer::SemanticAware => "semantic-aware-transform-query",
192 TransformLayer::Commodity => "commodity-transform-query",
193 TransformLayer::Emission => "emission-transform-query",
194 TransformLayer::SemanticReadOnly => "semantic-read-only-query",
195 }
196}
197
198fn order_passes_by_dag(requested: &[TransformPassKind]) -> Vec<TransformPassKind> {
199 let mut remaining = dedupe_requested_passes(requested);
200 let registry = default_transform_pass_registry();
201 remaining.sort_by_key(|kind| {
202 descriptor_for_pass(*kind, registry.entries.as_slice())
203 .map(|descriptor| (descriptor.phase, descriptor.phase_order, descriptor.id))
204 .unwrap_or((u8::MAX, u16::MAX, ""))
205 });
206
207 let mut ordered = Vec::with_capacity(remaining.len());
208 while !remaining.is_empty() {
209 let next_index = remaining
210 .iter()
211 .position(|candidate| {
212 !has_incoming_edge_from_remaining(
213 *candidate,
214 &remaining,
215 registry.entries.as_slice(),
216 )
217 })
218 .unwrap_or_default();
219 ordered.push(remaining.remove(next_index));
220 }
221
222 ordered
223}
224
225fn dedupe_requested_passes(requested: &[TransformPassKind]) -> Vec<TransformPassKind> {
226 let mut unique = Vec::new();
227 for pass in requested {
228 if !unique.contains(pass) {
229 unique.push(*pass);
230 }
231 }
232 unique
233}
234
235fn conflicting_unordered_pass_pairs(
236 requested: &[TransformPassKind],
237 registry_entries: &[TransformPassRegistryEntryV0],
238 dag_edges: &[TransformDagEdgeV0],
239) -> Vec<TransformPlanPassConflictV0> {
240 let mut conflicts = Vec::new();
241 for (left_index, left) in requested.iter().enumerate() {
242 for right in requested.iter().skip(left_index + 1) {
243 let Some(left_descriptor) = descriptor_for_pass(*left, registry_entries) else {
244 continue;
245 };
246 let Some(right_descriptor) = descriptor_for_pass(*right, registry_entries) else {
247 continue;
248 };
249 let declared = left_descriptor
250 .conflicts_with
251 .contains(&right_descriptor.id)
252 || right_descriptor
253 .conflicts_with
254 .contains(&left_descriptor.id);
255 if declared
256 && !dag_path_exists(left_descriptor.id, right_descriptor.id, dag_edges)
257 && !dag_path_exists(right_descriptor.id, left_descriptor.id, dag_edges)
258 {
259 conflicts.push(TransformPlanPassConflictV0 {
260 pass_a: left_descriptor.id,
261 pass_b: right_descriptor.id,
262 });
263 }
264 }
265 }
266 conflicts
267}
268
269fn has_incoming_edge_from_remaining(
270 candidate: TransformPassKind,
271 remaining: &[TransformPassKind],
272 registry_entries: &[TransformPassRegistryEntryV0],
273) -> bool {
274 descriptor_for_pass(candidate, registry_entries).is_some_and(|descriptor| {
275 descriptor.depends_on.iter().any(|dependency| {
276 remaining
277 .iter()
278 .any(|other| other.id() == *dependency && *other != candidate)
279 })
280 })
281}
282
283fn edge_applies(edge: &TransformDagEdgeV0, ordered_pass_ids: &[&'static str]) -> bool {
284 ordered_pass_ids.contains(&edge.from) && ordered_pass_ids.contains(&edge.to)
285}
286
287fn edge_is_satisfied(edge: &TransformDagEdgeV0, ordered_pass_ids: &[&'static str]) -> bool {
288 let from = position_of_pass_id(edge.from, ordered_pass_ids);
289 let to = position_of_pass_id(edge.to, ordered_pass_ids);
290 match (from, to) {
291 (Some(from), Some(to)) => from < to,
292 _ => false,
293 }
294}
295
296fn dag_path_exists(from: &'static str, to: &'static str, dag_edges: &[TransformDagEdgeV0]) -> bool {
297 let mut stack = vec![from];
298 let mut visited = Vec::new();
299 while let Some(current) = stack.pop() {
300 if current == to {
301 return true;
302 }
303 if visited.contains(¤t) {
304 continue;
305 }
306 visited.push(current);
307 for edge in dag_edges.iter().filter(|edge| edge.from == current) {
308 stack.push(edge.to);
309 }
310 }
311 false
312}
313
314fn position_of_pass_id(pass_id: &'static str, ordered_pass_ids: &[&'static str]) -> Option<usize> {
315 ordered_pass_ids
316 .iter()
317 .position(|ordered_pass_id| *ordered_pass_id == pass_id)
318}
319
320fn pass_is_registered(pass: &TransformPassKind) -> bool {
321 default_transform_pass_registry()
322 .entries
323 .iter()
324 .any(|entry| entry.contract.kind == *pass)
325}
326
327fn contract_for_pass(
328 pass: TransformPassKind,
329 contracts: &[TransformPassContractV0],
330) -> Option<&TransformPassContractV0> {
331 contracts.iter().find(|contract| contract.kind == pass)
332}
333
334fn descriptor_for_pass(
335 pass: TransformPassKind,
336 registry_entries: &[TransformPassRegistryEntryV0],
337) -> Option<&TransformPassDescriptorV0> {
338 registry_entries
339 .iter()
340 .find(|entry| entry.descriptor.kind == pass)
341 .map(|entry| &entry.descriptor)
342}
343
344pub(crate) fn transform_pass_kind_from_id(pass_id: &str) -> Option<TransformPassKind> {
345 all_transform_pass_kinds()
346 .into_iter()
347 .find(|kind| kind.id() == pass_id)
348}