logicaffeine_compile/tensor_graph/
shim.rs1use super::fuse::ChipRule;
12use super::graph::{Op, QuantFormat, Sym, TensorGraph};
13use super::numerics::{Bound, Contract, Keep};
14use super::symbols::ClassId;
15
16#[derive(Clone, Debug, PartialEq, Eq)]
18pub enum Allow {
19 Native,
20 Decompose,
21 Refuse(String),
22}
23
24impl Allow {
25 pub fn is_native(&self) -> bool {
26 matches!(self, Allow::Native)
27 }
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
32pub enum Support {
33 Native,
35 Decompose,
37 Refuse(&'static str),
39}
40
41pub trait Shim {
43 fn name(&self) -> String;
45
46 fn supports(&self, class: ClassId) -> Support;
49
50 fn fits(&self, op: &Op, g: &TensorGraph) -> Result<(), String>;
52
53 fn activation(&self, _consumer: &Op, _operand: usize, _g: &TensorGraph) -> Option<QuantFormat> {
55 None
56 }
57
58 fn rules(&self) -> Vec<(Position, ChipRule<'_>)> {
60 Vec::new()
61 }
62
63 fn numerics(&self) -> Option<Contract>;
65}
66
67pub fn allows(shim: &dyn Shim, op: &Op, g: &TensorGraph) -> Allow {
71 let fit = |op: &Op| shim.fits(op, g).map_or_else(Allow::Refuse, |()| Allow::Native);
72 let Sym::Fused(f) = &op.sym else { return fit(op) };
73 match shim.supports(f.class) {
74 Support::Native => fit(op),
75 Support::Decompose => {
76 for part in &f.parts {
77 if let Allow::Refuse(why) = allows(shim, part, g) {
78 return Allow::Refuse(format!("{} runs as its parts, and its {} is refused: {why}", f.class, part.kind()));
79 }
80 }
81 Allow::Decompose
82 }
83 Support::Refuse(why) => Allow::Refuse(format!("{}: {why}", f.class)),
84 }
85}
86
87#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub enum Position {
90 Before(&'static str),
91 After(&'static str),
92 End,
93}
94
95#[derive(Clone, Copy, Debug, Default)]
98pub struct ReferenceShim;
99
100impl Shim for ReferenceShim {
101 fn name(&self) -> String {
102 "reference".to_string()
103 }
104
105 fn supports(&self, _: ClassId) -> Support {
106 Support::Native
107 }
108
109 fn fits(&self, _: &Op, _: &TensorGraph) -> Result<(), String> {
110 Ok(())
111 }
112
113 fn numerics(&self) -> Option<Contract> {
114 Some(Contract { point: "the meaning".to_string(), activations: None, cache: Keep::Exact, state: Keep::Exact, attention: None, fused: Bound::EXACT, accumulator_bits: 64 })
115 }
116}
117
118pub struct Linking<'a> {
121 pub base: &'a dyn Shim,
122 pub classes: &'a [ClassId],
123}
124
125impl Shim for Linking<'_> {
126 fn name(&self) -> String {
127 format!("{} linking {:?}", self.base.name(), self.classes)
128 }
129
130 fn supports(&self, class: ClassId) -> Support {
131 if self.classes.contains(&class) {
132 Support::Native
133 } else {
134 self.base.supports(class)
135 }
136 }
137
138 fn fits(&self, op: &Op, g: &TensorGraph) -> Result<(), String> {
139 match &op.sym {
140 Sym::Fused(f) if self.classes.contains(&f.class) => Ok(()),
141 _ => self.base.fits(op, g),
142 }
143 }
144
145 fn activation(&self, consumer: &Op, operand: usize, g: &TensorGraph) -> Option<QuantFormat> {
146 self.base.activation(consumer, operand, g)
147 }
148
149 fn rules(&self) -> Vec<(Position, ChipRule<'_>)> {
150 self.base.rules()
151 }
152
153 fn numerics(&self) -> Option<Contract> {
154 self.base.numerics()
155 }
156}