onnx_runtime_memory/
validate.rs1use std::collections::HashMap;
8
9use onnx_runtime_ir::{Graph, ValueId};
10
11use crate::error::ValidateError;
12use crate::liveness::compute_liveness;
13use crate::options::PlanOptions;
14use crate::oracle::static_size_oracle;
15use crate::plan::{ActivationPlan, SlotId};
16use crate::view_map::ViewMap;
17
18pub fn validate<F>(
29 plan: &ActivationPlan,
30 graph: &Graph,
31 view_map: &ViewMap,
32 size_oracle: F,
33 options: &PlanOptions,
34) -> Result<(), ValidateError>
35where
36 F: Fn(ValueId) -> Option<usize>,
37{
38 let live = compute_liveness(graph, view_map, options).map_err(|_| ValidateError::Cycle)?;
39
40 let slot_cap: HashMap<SlotId, usize> = plan
41 .slots
42 .iter()
43 .map(|s| (s.id, s.capacity_bytes))
44 .collect();
45
46 let mut by_slot: HashMap<SlotId, Vec<ValueId>> = HashMap::new();
48 for &owner in live.intervals.keys() {
49 let Some(&sid) = plan.assignments.get(&owner) else {
50 return Err(ValidateError::MissingAssignment { value: owner });
51 };
52 let Some(&cap) = slot_cap.get(&sid) else {
53 return Err(ValidateError::UnknownSlot {
54 value: owner,
55 slot: sid,
56 });
57 };
58 if let Some(need) = size_oracle(owner)
59 && need > cap
60 {
61 return Err(ValidateError::UndersizedSlot {
62 value: owner,
63 slot: sid,
64 needed: need,
65 capacity: cap,
66 });
67 }
68 by_slot.entry(sid).or_default().push(owner);
69 }
70
71 for (&sid, members) in &by_slot {
72 for i in 0..members.len() {
73 for j in (i + 1)..members.len() {
74 let a = members[i];
75 let b = members[j];
76 if live.intervals[&a].overlaps(&live.intervals[&b]) {
77 return Err(ValidateError::SlotConflict { a, b, slot: sid });
78 }
79 }
80 }
81 }
82
83 for vid in graph.values.keys() {
85 if !view_map.is_view(vid) {
86 continue;
87 }
88 let root = view_map.root(vid);
89 let Some(root_interval) = live.intervals.get(&root) else {
90 continue; };
92 let view_use = graph
93 .consumers(vid)
94 .into_iter()
95 .filter_map(|consumer| live.order_index.get(&consumer).copied())
96 .max();
97 let view_use = match view_use {
98 Some(u) => u.max(view_output_end(graph, &live, vid)),
99 None => view_output_end(graph, &live, vid),
100 };
101 if view_use > root_interval.use_end {
102 return Err(ValidateError::ViewOutlivesSource {
103 view: vid,
104 source_owner: root,
105 view_use,
106 source_end: root_interval.use_end,
107 });
108 }
109 }
110
111 Ok(())
112}
113
114fn view_output_end(graph: &Graph, live: &crate::liveness::Liveness, vid: ValueId) -> usize {
116 if graph.outputs.contains(&vid) {
117 live.last_index
118 } else {
119 0
120 }
121}
122
123pub fn validate_static(
125 plan: &ActivationPlan,
126 graph: &Graph,
127 view_map: &ViewMap,
128 options: &PlanOptions,
129) -> Result<(), ValidateError> {
130 let oracle = static_size_oracle(graph);
131 validate(plan, graph, view_map, oracle, options)
132}