1use std::collections::BTreeMap;
14use std::fmt::Write;
15
16use crate::codegen_sva::fabric_rtl::Divergence;
17
18use super::cells::{routine, CellOp};
19use super::int64::FAULT_BITS;
20use super::lane::{split_u64, LInst, LOp, LStmt, LaneShape, Routine};
21use super::region::{RStmt, RTerm, RegionCfg};
22
23pub fn routine_symbol(op: CellOp, shape: LaneShape) -> String {
25 format!("lr_{}_w{}m{}", op.name(), shape.word, shape.mul)
26}
27
28fn mask(shape: LaneShape) -> String {
29 format!("0x{:X}u", shape.mask())
30}
31
32fn inst_c(out: &mut String, i: &LInst, shape: LaneShape, pad: &str) {
33 let m = mask(shape);
34 let w = shape.word;
35 let d = i.dst;
36 match i.op {
37 LOp::Const(k) => writeln!(out, "{pad}r[{d}] = {k}u;"),
38 LOp::Mov(a) => writeln!(out, "{pad}r[{d}] = r[{a}];"),
39 LOp::Add { a, b, cin } => {
40 let c = i.dst2.expect("an add writes its carry");
41 writeln!(
42 out,
43 "{pad}{{ uint64_t s_ = (uint64_t)r[{a}] + (uint64_t)r[{b}] + (uint64_t)r[{cin}]; r[{d}] = (uint32_t)(s_ & {m}); r[{c}] = (uint32_t)(s_ >> {w}); }}"
44 )
45 }
46 LOp::Sub { a, b, bin } => {
47 let c = i.dst2.expect("a subtract writes its borrow");
48 writeln!(
49 out,
50 "{pad}{{ int64_t s_ = (int64_t)r[{a}] - (int64_t)r[{b}] - (int64_t)r[{bin}]; r[{d}] = (uint32_t)((uint64_t)s_ & {m}); r[{c}] = (uint32_t)(s_ < 0); }}"
51 )
52 }
53 LOp::Mul { a, b } => writeln!(out, "{pad}r[{d}] = r[{a}] * r[{b}];"),
54 LOp::And { a, b } => writeln!(out, "{pad}r[{d}] = r[{a}] & r[{b}];"),
55 LOp::Or { a, b } => writeln!(out, "{pad}r[{d}] = r[{a}] | r[{b}];"),
56 LOp::Xor { a, b } => writeln!(out, "{pad}r[{d}] = r[{a}] ^ r[{b}];"),
57 LOp::Not { a } => writeln!(out, "{pad}r[{d}] = ~r[{a}] & {m};"),
58 LOp::Shl { a, k } => writeln!(out, "{pad}r[{d}] = (r[{a}] << {k}) & {m};"),
59 LOp::Shr { a, k } => writeln!(out, "{pad}r[{d}] = r[{a}] >> {k};"),
60 LOp::Ltu { a, b } => writeln!(out, "{pad}r[{d}] = r[{a}] < r[{b}];"),
61 LOp::Eq { a, b } => writeln!(out, "{pad}r[{d}] = r[{a}] == r[{b}];"),
62 LOp::Sel { c, t, f } => writeln!(out, "{pad}r[{d}] = r[{c}] ? r[{t}] : r[{f}];"),
63 }
64 .expect("writing to a String");
65}
66
67fn body_c(out: &mut String, body: &[LStmt], shape: LaneShape, depth: usize) {
68 let pad = " ".repeat(depth + 1);
69 for s in body {
70 match s {
71 LStmt::Inst(i) => inst_c(out, i, shape, &pad),
72 LStmt::Repeat { times, body } => {
73 writeln!(out, "{pad}for (uint32_t t{depth}_ = 0; t{depth}_ < {times}u; t{depth}_++) {{").expect("write");
74 body_c(out, body, shape, depth + 1);
75 writeln!(out, "{pad}}}").expect("write");
76 }
77 }
78 }
79}
80
81pub fn routine_c(r: &Routine, symbol: &str) -> String {
85 let mut out = String::new();
86 writeln!(out, "static void {symbol}(const uint32_t *in, uint32_t *out) {{").expect("write");
87 writeln!(out, " uint32_t r[{}] = {{0}};", r.regs.max(1)).expect("write");
88 for (k, reg) in r.inputs.iter().enumerate() {
89 writeln!(out, " r[{reg}] = in[{k}];").expect("write");
90 }
91 body_c(&mut out, &r.body, r.shape, 0);
92 for (k, reg) in r.outputs.iter().enumerate() {
93 writeln!(out, " out[{k}] = r[{reg}];").expect("write");
94 }
95 writeln!(out, "}}\n").expect("write");
96 out
97}
98
99fn ops_of(region: &RegionCfg) -> Vec<CellOp> {
101 let mut ops = Vec::new();
102 for b in ®ion.blocks {
103 for s in &b.stmts {
104 let op = match s {
105 RStmt::Cell { op, .. } => *op,
106 RStmt::Select { cmp, .. } => CellOp::Select(*cmp),
107 _ => continue,
108 };
109 if !ops.contains(&op) {
110 ops.push(op);
111 }
112 }
113 }
114 ops
115}
116
117pub fn region_c(region: &RegionCfg, shape: LaneShape, symbol: &str) -> String {
120 let n = shape.words_for(64);
121 let nf = shape.words_for(FAULT_BITS);
122 let w = shape.word;
123 let mut out = String::new();
124 writeln!(out, "static int {symbol}(const int64_t *args, int64_t *out, uint32_t *fault) {{").expect("write");
125 writeln!(out, " uint32_t v[{}][{n}] = {{{{0}}}};", region.regs.max(1)).expect("write");
126 writeln!(out, " uint32_t in_[{}], o_[{}];", 4 * n, n + nf).expect("write");
127 for i in 0..region.params.len() {
128 for k in 0..n {
129 writeln!(out, " v[{i}][{k}] = (uint32_t)(((uint64_t)args[{i}] >> {}) & {});", k as u32 * w, mask(shape)).expect("write");
130 }
131 }
132 writeln!(out, " goto B0;").expect("write");
133 let copy = |out: &mut String, dst: &str, src: &str| {
134 writeln!(out, " memcpy({dst}, {src}, sizeof(uint32_t) * {n});").expect("write");
135 };
136 for (bi, b) in region.blocks.iter().enumerate() {
137 writeln!(out, "B{bi}:;").expect("write");
138 for s in &b.stmts {
139 match *s {
140 RStmt::Const { dst, value } => {
141 for (k, word) in split_u64(shape, value as u64, n).iter().enumerate() {
142 writeln!(out, " v[{dst}][{k}] = {word}u;").expect("write");
143 }
144 }
145 RStmt::Copy { dst, src } => copy(&mut out, &format!("v[{dst}]"), &format!("v[{src}]")),
146 RStmt::Cell { dst, op, a, b } => {
147 copy(&mut out, "in_", &format!("v[{a}]"));
148 copy(&mut out, &format!("in_ + {n}"), &format!("v[{b}]"));
149 writeln!(out, " {}(in_, o_);", routine_symbol(op, shape)).expect("write");
150 if op.faults() {
151 let joined: Vec<String> = (0..nf).map(|k| format!("((uint32_t)o_[{}] << {})", n + k, k as u32 * w)).collect();
152 writeln!(out, " {{ uint32_t f_ = {}; if (f_) {{ *fault = f_; return 0; }} }}", joined.join(" | ")).expect("write");
153 }
154 copy(&mut out, &format!("v[{dst}]"), "o_");
155 }
156 RStmt::Select { dst, cmp, a, b, t, e } => {
157 copy(&mut out, "in_", &format!("v[{a}]"));
158 copy(&mut out, &format!("in_ + {n}"), &format!("v[{b}]"));
159 copy(&mut out, &format!("in_ + {}", 2 * n), &format!("v[{t}]"));
160 copy(&mut out, &format!("in_ + {}", 3 * n), &format!("v[{e}]"));
161 writeln!(out, " {}(in_, o_);", routine_symbol(CellOp::Select(cmp), shape)).expect("write");
162 copy(&mut out, &format!("v[{dst}]"), "o_");
163 }
164 }
165 }
166 match b.term {
167 RTerm::Jump(t) => writeln!(out, " goto B{t};"),
168 RTerm::Branch { cond, then_, else_ } => {
169 let any: Vec<String> = (0..n).map(|k| format!("v[{cond}][{k}]")).collect();
170 writeln!(out, " if ({}) goto B{then_}; else goto B{else_};", any.join(" | "))
171 }
172 RTerm::Return(r) => {
173 let joined: Vec<String> = (0..n).map(|k| format!("((uint64_t)v[{r}][{k}] << {})", k as u32 * w)).collect();
174 writeln!(out, " *out = (int64_t)({});\n return 1;", joined.join(" | "))
175 }
176 }
177 .expect("write");
178 }
179 writeln!(out, "}}\n").expect("write");
180 out
181}
182
183pub fn model_c(regions: &[(u32, RegionCfg)], shape: LaneShape, device: &str) -> String {
187 let mut out = String::new();
188 writeln!(out, "/* The {device} device model: lane routines at {}-bit words with {}-bit multipliers, and the regions they run. */", shape.word, shape.mul)
189 .expect("write");
190 writeln!(out, "#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n").expect("write");
191 let mut used: BTreeMap<String, CellOp> = BTreeMap::new();
192 for (_, r) in regions {
193 for op in ops_of(r) {
194 used.insert(routine_symbol(op, shape), op);
195 }
196 }
197 for (sym, op) in &used {
198 out.push_str(&routine_c(&routine(shape, *op), sym));
199 }
200 for (id, r) in regions {
201 out.push_str(®ion_c(r, shape, &format!("logos_region_{id}")));
202 }
203 let count = regions.iter().map(|(id, _)| *id as usize + 1).max().unwrap_or(0).max(1);
204 let codes = Divergence::ALL.iter().map(|d| d.code()).max().unwrap_or(0) as usize + 1;
205 writeln!(out, "static uint64_t logos_dev_done[{count}];").expect("write");
206 writeln!(out, "static uint64_t logos_dev_declined[{count}][{codes}];").expect("write");
207 writeln!(out, "static uint64_t logos_dev_off_calls[{count}];").expect("write");
208 writeln!(out, "static int logos_dev_mode = -1;\n").expect("write");
209 let names: Vec<String> = (0..count)
210 .map(|i| {
211 regions.iter().find(|(id, _)| *id as usize == i).map(|(_, r)| format!("\"{}\"", r.name.replace('\\', "\\\\").replace('"', "\\\""))).unwrap_or_else(|| "\"\"".to_string())
212 })
213 .collect();
214 writeln!(out, "static const char *logos_dev_names[{count}] = {{ {} }};\n", names.join(", ")).expect("write");
215 let reasons: Vec<String> = (0..codes)
216 .map(|c| format!("\"{}\"", Divergence::from_code(c as u32).map(|d| d.name()).unwrap_or("none")))
217 .collect();
218 writeln!(out, "static const char *logos_dev_reasons[{codes}] = {{ {} }};\n", reasons.join(", ")).expect("write");
219 writeln!(
220 out,
221 r#"static void logos_dev_account(void) {{
222 const char *path = getenv("LOGOS_ACCEL_STATS");
223 if (!path || !*path) return;
224 FILE *f = fopen(path, "a");
225 if (!f) return;
226 fprintf(f, "{{\"device\":\"{device}\",\"regions\":[");
227 for (int i = 0; i < {count}; i++) {{
228 fprintf(f, "%s{{\"region\":%d,\"name\":\"%s\",\"done\":%llu,\"offload_off\":%llu,\"declined\":{{", i ? "," : "", i, logos_dev_names[i], (unsigned long long)logos_dev_done[i], (unsigned long long)logos_dev_off_calls[i]);
229 int first = 1;
230 for (int c = 1; c < {codes}; c++) {{
231 if (!logos_dev_declined[i][c]) continue;
232 fprintf(f, "%s\"%s\":%llu", first ? "" : ",", logos_dev_reasons[c], (unsigned long long)logos_dev_declined[i][c]);
233 first = 0;
234 }}
235 fprintf(f, "}}}}");
236 }}
237 fprintf(f, "]}}\n");
238 fclose(f);
239}}
240
241int logos_dev_call(int region, const int64_t *args, int64_t *out) {{
242 if (logos_dev_mode < 0) {{
243 const char *m = getenv("LOGOS_ACCEL_OFFLOAD");
244 logos_dev_mode = (m && strcmp(m, "off") == 0) ? 0 : 1;
245 atexit(logos_dev_account);
246 }}
247 if (region < 0 || region >= {count}) return 0;
248 if (!logos_dev_mode) {{ logos_dev_off_calls[region]++; return 0; }}
249 uint32_t fault = 0;
250 int ok = 0;
251 switch (region) {{"#
252 )
253 .expect("write");
254 for (id, _) in regions {
255 writeln!(out, " case {id}: ok = logos_region_{id}(args, out, &fault); break;").expect("write");
256 }
257 writeln!(
258 out,
259 r#" default: return 0;
260 }}
261 if (ok) logos_dev_done[region]++;
262 else if (fault < {codes}) logos_dev_declined[region][fault]++;
263 return ok;
264}}"#
265 )
266 .expect("write");
267 out
268}