1use std::collections::HashMap;
10use std::sync::{Arc, Mutex, OnceLock};
11
12use serde::{Deserialize, Serialize};
13
14use crate::codegen_sva::fabric_rtl::{AluOp, CmpKind, FuncUnit};
15
16use super::int64::{self, n64, nfault, no_fault};
17use super::lane::{LaneShape, Routine};
18use super::multiword::{Builder, Mw};
19use super::softfloat;
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub enum CellOp {
24 Alu(AluOp),
25 Unit(FuncUnit),
26 Select(CmpKind),
28}
29
30impl CellOp {
31 pub fn name(self) -> String {
32 match self {
33 CellOp::Alu(op) => format!("alu_{op:?}").to_lowercase(),
34 CellOp::Unit(u) => format!("unit_{u:?}").to_lowercase(),
35 CellOp::Select(c) => format!("select_{c:?}").to_lowercase(),
36 }
37 }
38
39 pub fn operands(self) -> usize {
41 match self {
42 CellOp::Select(_) => 4,
43 _ => 2,
44 }
45 }
46
47 pub fn faults(self) -> bool {
49 !matches!(self, CellOp::Select(_))
50 }
51}
52
53fn binary(
54 shape: LaneShape,
55 name: String,
56 f: impl FnOnce(&mut Builder, &Mw, &Mw) -> (Mw, Mw),
57) -> Routine {
58 let mut b = Builder::new(shape);
59 let n = n64(&b);
60 let x = b.mw_input(n);
61 let y = b.mw_input(n);
62 let (v, fault) = f(&mut b, &x, &y);
63 assert_eq!(v.len(), n);
64 assert_eq!(fault.len(), nfault(&b));
65 let inputs = x.0.iter().chain(&y.0).copied().collect();
66 let outputs = v.0.iter().chain(&fault.0).copied().collect();
67 b.finish(name, inputs, outputs)
68}
69
70fn exact(
71 f: impl FnOnce(&mut Builder, &Mw, &Mw) -> Mw,
72) -> impl FnOnce(&mut Builder, &Mw, &Mw) -> (Mw, Mw) {
73 move |b, x, y| {
74 let v = f(b, x, y);
75 let ok = no_fault(b);
76 (v, ok)
77 }
78}
79
80fn predicate(
81 f: impl FnOnce(&mut Builder, &Mw, &Mw) -> u32,
82) -> impl FnOnce(&mut Builder, &Mw, &Mw) -> (Mw, Mw) {
83 move |b, x, y| {
84 let n = x.len();
85 let p = f(b, x, y);
86 let v = int64::bool64(b, p, n);
87 let ok = no_fault(b);
88 (v, ok)
89 }
90}
91
92fn int_compare(b: &mut Builder, c: CmpKind, x: &Mw, y: &Mw) -> u32 {
94 match c {
95 CmpKind::Lt => b.mw_lts(x, y),
96 CmpKind::Gt => b.mw_lts(y, x),
97 CmpKind::Le => {
98 let gt = b.mw_lts(y, x);
99 b.bit_not(gt)
100 }
101 CmpKind::Ge => {
102 let lt = b.mw_lts(x, y);
103 b.bit_not(lt)
104 }
105 CmpKind::Eq => b.mw_eq(x, y),
106 CmpKind::Ne => {
107 let e = b.mw_eq(x, y);
108 b.bit_not(e)
109 }
110 }
111}
112
113pub fn build(shape: LaneShape, op: CellOp) -> Routine {
115 let name = op.name();
116 match op {
117 CellOp::Alu(alu) => match alu {
118 AluOp::Add => binary(shape, name, int64::add),
119 AluOp::Sub => binary(shape, name, int64::sub),
120 AluOp::Mul => binary(shape, name, int64::mul),
121 AluOp::And => binary(shape, name, exact(|b, x, y| b.mw_and(x, y))),
122 AluOp::Or => binary(shape, name, exact(|b, x, y| b.mw_or(x, y))),
123 AluOp::Xor => binary(shape, name, exact(|b, x, y| b.mw_xor(x, y))),
124 AluOp::Shl => binary(shape, name, exact(int64::shl)),
125 AluOp::Shr => binary(shape, name, exact(int64::shr)),
126 AluOp::UShr => binary(shape, name, exact(int64::ushr)),
127 },
128 CellOp::Unit(unit) => match unit {
129 FuncUnit::DivS => binary(shape, name, int64::div),
130 FuncUnit::ModS => binary(shape, name, int64::rem),
131 FuncUnit::FloorDivS => binary(shape, name, int64::floor_div),
132 FuncUnit::Pow => binary(shape, name, int64::pow),
133 FuncUnit::MulHiU => binary(shape, name, exact(int64::mul_hi_u)),
134 FuncUnit::MulOvfS => binary(shape, name, exact(int64::mul_ovf_s)),
135 FuncUnit::PopCount => binary(shape, name, exact(|b, x, _| int64::popcount(b, x))),
136 FuncUnit::ApproxEqF => binary(shape, name, predicate(softfloat::approx_eq_ints)),
137 FuncUnit::FAddF => binary(shape, name, exact(|b, x, y| softfloat::add(b, x, y, false))),
138 FuncUnit::FSubF => binary(shape, name, exact(|b, x, y| softfloat::add(b, x, y, true))),
139 FuncUnit::FMulF => binary(shape, name, exact(softfloat::mul)),
140 FuncUnit::FDivF => binary(shape, name, |b, x, y| {
141 let v = softfloat::div(b, x, y);
142 let uy = softfloat::unpack(b, y);
143 let ok = no_fault(b);
144 let f = int64::fault_when(
145 b,
146 uy.is_zero,
147 crate::codegen_sva::fabric_rtl::Divergence::FloatDivideByZero,
148 &ok,
149 );
150 (v, f)
151 }),
152 FuncUnit::FSqrtF => binary(shape, name, exact(|b, x, _| softfloat::sqrt(b, x))),
153 FuncUnit::FFloorF => {
154 binary(shape, name, exact(|b, x, _| softfloat::floor_to_int(b, x)))
155 }
156 FuncUnit::IntToF => binary(shape, name, exact(|b, x, _| softfloat::from_int(b, x))),
157 FuncUnit::FLtF => binary(
158 shape,
159 name,
160 predicate(|b, x, y| softfloat::compare(b, x, y).0),
161 ),
162 FuncUnit::FGtF => binary(
163 shape,
164 name,
165 predicate(|b, x, y| softfloat::compare(b, y, x).0),
166 ),
167 FuncUnit::FLeF => binary(
168 shape,
169 name,
170 predicate(|b, x, y| softfloat::compare(b, x, y).1),
171 ),
172 FuncUnit::FGeF => binary(
173 shape,
174 name,
175 predicate(|b, x, y| softfloat::compare(b, y, x).1),
176 ),
177 FuncUnit::FEqF => binary(
178 shape,
179 name,
180 predicate(|b, x, y| softfloat::compare(b, x, y).2),
181 ),
182 FuncUnit::FNeF => binary(
183 shape,
184 name,
185 predicate(|b, x, y| {
186 let e = softfloat::compare(b, x, y).2;
187 b.bit_not(e)
188 }),
189 ),
190 },
191 CellOp::Select(c) => {
192 let mut b = Builder::new(shape);
193 let n = n64(&b);
194 let ca = b.mw_input(n);
195 let cb = b.mw_input(n);
196 let t = b.mw_input(n);
197 let e = b.mw_input(n);
198 let p = int_compare(&mut b, c, &ca, &cb);
199 let v = b.mw_sel(p, &t, &e);
200 let inputs = [&ca, &cb, &t, &e]
201 .iter()
202 .flat_map(|m| m.0.iter().copied())
203 .collect();
204 b.finish(name, inputs, v.0)
205 }
206 }
207}
208
209pub fn routine(shape: LaneShape, op: CellOp) -> Arc<Routine> {
211 static CACHE: OnceLock<Mutex<HashMap<(LaneShape, CellOp), Arc<Routine>>>> = OnceLock::new();
212 let cache = CACHE.get_or_init(|| Mutex::new(HashMap::new()));
213 if let Some(r) = cache.lock().expect("the routine cache").get(&(shape, op)) {
214 return r.clone();
215 }
216 let built = Arc::new(build(shape, op));
217 cache
218 .lock()
219 .expect("the routine cache")
220 .entry((shape, op))
221 .or_insert(built)
222 .clone()
223}
224
225pub fn all_ops() -> Vec<CellOp> {
227 let alu = [
228 AluOp::Add,
229 AluOp::Sub,
230 AluOp::Mul,
231 AluOp::And,
232 AluOp::Or,
233 AluOp::Xor,
234 AluOp::Shl,
235 AluOp::Shr,
236 AluOp::UShr,
237 ];
238 let units = [
239 FuncUnit::DivS,
240 FuncUnit::ModS,
241 FuncUnit::FloorDivS,
242 FuncUnit::Pow,
243 FuncUnit::MulHiU,
244 FuncUnit::ApproxEqF,
245 FuncUnit::FAddF,
246 FuncUnit::FSubF,
247 FuncUnit::FMulF,
248 FuncUnit::FDivF,
249 FuncUnit::FSqrtF,
250 FuncUnit::FFloorF,
251 FuncUnit::MulOvfS,
252 FuncUnit::IntToF,
253 FuncUnit::FLtF,
254 FuncUnit::FGtF,
255 FuncUnit::FLeF,
256 FuncUnit::FGeF,
257 FuncUnit::FEqF,
258 FuncUnit::FNeF,
259 FuncUnit::PopCount,
260 ];
261 let cmps = [
262 CmpKind::Lt,
263 CmpKind::Gt,
264 CmpKind::Le,
265 CmpKind::Ge,
266 CmpKind::Eq,
267 CmpKind::Ne,
268 ];
269 alu.into_iter()
270 .map(CellOp::Alu)
271 .chain(units.into_iter().map(CellOp::Unit))
272 .chain(cmps.into_iter().map(CellOp::Select))
273 .collect()
274}