==> src/backend.c <==1/* backend.rs's free functions and the trait defaults, over the C tables. */
2#include "mesh/backend.h"
3
4#include "mesh/abi/base.h"
5
6#include <stdlib.h>
7#include <string.h>
8#include <sys/stat.h>
9
10uint32_t mesh_caps_word(const mesh_caps *c) { return mesh_backend_word(c->kind, c->family); }
11const char *mesh_caps_backend_name(const mesh_caps *c) { return MESH_BACKENDS[c->kind]; }
12
13void mesh_caps_copy(mesh_caps *dst, const mesh_caps *src) {
14 *dst = *src;
15 dst->name = mesh_strdup(src->name ? src->name : "");
16 dst->arch = mesh_strdup(src->arch ? src->arch : "");
17}
18
19void mesh_caps_free(mesh_caps *c) {
20 free(c->name);
21 free(c->arch);
22 *c = (mesh_caps){0};
23}
24
25char *mesh_backend_label(uint32_t word) {
26 uint32_t k = mesh_backend_kind(word), f = mesh_backend_family(word);
27 if (k == MESH_BACKEND_CUDA) return mesh_strdup("cuda");
28 if (k == MESH_BACKEND_WASM) return mesh_strdup("wasm");
29 if (k == MESH_BACKEND_METAL) return f > 0 ? mesh_fmt("metal-apple%u", (unsigned)f) : mesh_strdup("metal");
30 return mesh_fmt("backend%u", (unsigned)k);
31}
32
33bool mesh_kernel_regs(const mesh_kernel *k, uint32_t *out, char **err) { return k->ops->regs(k, out, err); }
34bool mesh_kernel_local_bytes(const mesh_kernel *k, uint32_t *out, char **err) { return k->ops->local_bytes(k, out, err); }
35bool mesh_kernel_max_blocks_per_sm(const mesh_kernel *k, uint32_t *out, char **err) {
36 return k->ops->max_blocks_per_sm(k, out, err);
37}
38uint64_t mesh_kernel_scratch(const mesh_kernel *k) { return k->ops->scratch(k); }
39bool mesh_kernel_global_u32(const mesh_kernel *k, const char *name, uint32_t *out, char **err) {
40 return k->ops->global_u32(k, name, out, err);
41}
42
43void mesh_kernel_drop(mesh_kernel *k) {
44 if (!k) return;
45 if (k->ops->drop) k->ops->drop(k);
46 free(k);
47}
48
49void mesh_stream_drop(mesh_stream *s) {
50 if (!s) return;
51 if (s->drop) s->drop(s);
52 free(s);
53}
54
55void mesh_prepared_free(mesh_prepared *p) {
56 mesh_manifest_free(&p->manifest);
57 free(p->code);
58 mesh_payload_free(&p->src);
59 mesh_kernel_drop(p->kernel);
60 *p = (mesh_prepared){0};
61}
62
63char *mesh_backend_crashed(const mesh_backend *b, const mesh_stream *st, const mesh_kernel *k) {
64 return b->ops->crashed ? b->ops->crashed(b, st, k) : NULL;
65}
66bool mesh_backend_owns_halt(const mesh_backend *b) { return b->ops->owns_halt && b->ops->owns_halt(b); }
67bool mesh_backend_reads_live(const mesh_backend *b) { return b->ops->reads_live && b->ops->reads_live(b); }
68void mesh_backend_sample(const mesh_backend *b) {
69 if (b->ops->sample) b->ops->sample(b);
70}
71uint32_t mesh_backend_online_timeout_ms(const mesh_backend *b) {
72 return b->ops->online_timeout_ms ? b->ops->online_timeout_ms(b) : 2000;
73}
74void mesh_backend_destroy(mesh_backend *b) {
75 if (b && b->ops->destroy) b->ops->destroy(b);
76}
77
78static void join_backends(mesh_text *t, char *const *v, size_t n) {
79 for (size_t i = 0; i < n; i++) mesh_text_printf(t, "%s%s", i ? ", " : "", v[i]);
80}
81
82bool mesh_pick_exec(const mesh_manifest *m, const uint32_t *have, size_t nhave, uint32_t *exec, char **err) {
83 for (size_t i = 0; i < m->nbackends; i++)
84 for (size_t j = 0; j < nhave; j++)
85 if (strcmp(MESH_BACKENDS[have[j]], m->backends[i]) == 0) {
86 *exec = have[j];
87 return true;
88 }
89 mesh_text t = {0};
90 mesh_text_printf(&t, "unsupported: %s runs on ", m->name);
91 join_backends(&t, m->backends, m->nbackends);
92 mesh_text_puts(&t, " (manifest `backends`); this meshd runs ");
93 for (size_t j = 0; j < nhave; j++) mesh_text_printf(&t, "%s%s", j ? ", " : "", MESH_BACKENDS[have[j]]);
94 if (err) {
95 free(*err);
96 *err = mesh_text_take(&t);
97 } else {
98 mesh_text_free(&t);
99 }
100 return false;
101}
102
103const char *mesh_native_target(void) {
104#if defined(__APPLE__) && defined(__aarch64__)
105 return "aarch64-apple-darwin";
106#elif defined(__APPLE__)
107 return "x86_64-apple-darwin";
108#elif defined(__aarch64__)
109 return "aarch64-unknown-linux-gnu";
110#elif defined(__x86_64__)
111 return "x86_64-unknown-linux-gnu";
112#else
113 return "unknown";
114#endif
115}
116
117char *mesh_native_binary(const char *dir, const char *name) { return mesh_fmt("%s/%s/%s", dir, mesh_native_target(), name); }
118
119bool mesh_runs_here(const char *dir, const mesh_manifest *m, const uint32_t *have, size_t nhave) {
120 uint32_t exec;
121 if (!mesh_pick_exec(m, have, nhave, &exec, NULL)) return false;
122 if (exec != MESH_BACKEND_NATIVE) return true;
123 char *bin = mesh_native_binary(dir, m->name);
124 struct stat st;
125 bool file = stat(bin, &st) == 0 && S_ISREG(st.st_mode);
126 free(bin);
127 return file;
128}
129
130bool mesh_check_manifest(const mesh_payload *src, uint32_t kind, mesh_manifest *out, char **err) {
131 char *why = NULL;
132 if (!mesh_manifest_parse(src->manifest, strlen(src->manifest), out, &why)) {
133 mesh_errf(err, "manifest: %s: %s", src->dir, why);
134 free(why);
135 return false;
136 }
137 if (out->abi != MESH_RT_ABI) {
138 mesh_errf(err, "abi: %s declares abi %u, meshd runs %u", out->name, (unsigned)out->abi, (unsigned)MESH_RT_ABI);
139 goto fail;
140 }
141 const char *me = MESH_BACKENDS[kind];
142 bool listed = false;
143 for (size_t i = 0; i < out->nbackends; i++) listed = listed || strcmp(out->backends[i], me) == 0;
144 if (!listed) {
145 mesh_text t = {0};
146 mesh_text_printf(&t, "unsupported: %s runs on ", out->name);
147 join_backends(&t, out->backends, out->nbackends);
148 mesh_text_printf(&t, " (manifest `backends`); this meshd runs %s", me);
149 mesh_errf(err, "%s", mesh_text_str(&t));
150 mesh_text_free(&t);
151 goto fail;
152 }
153 if (out->sms != 0) {
154 mesh_errf(err, "manifest: %s: sms = %u: per-program green partitions are not implemented", out->name, (unsigned)out->sms);
155 goto fail;
156 }
157 return true;
158fail:
159 mesh_manifest_free(out);
160 return false;
161}
162
163bool mesh_prepare_any(const mesh_compiler_entry *comps, size_t n, mesh_payload *src, const uint64_t *expect, const char *root,
164 mesh_prepared *out, char **err) {
165 mesh_manifest m;
166 char *why = NULL;
167 if (!mesh_manifest_parse(src->manifest, strlen(src->manifest), &m, &why)) {
168 mesh_errf(err, "manifest: %s: %s", src->dir, why);
169 free(why);
170 mesh_payload_free(src);
171 return false;
172 }
173 uint32_t *kinds = mesh_alloc((n ? n : 1) * sizeof *kinds);
174 for (size_t i = 0; i < n; i++) kinds[i] = comps[i].kind;
175 uint32_t exec;
176 bool ok = mesh_pick_exec(&m, kinds, n, &exec, err);
177 free(kinds);
178 if (!ok) {
179 mesh_manifest_free(&m);
180 mesh_payload_free(src);
181 return false;
182 }
183 const mesh_compiler *comp = NULL;
184 for (size_t i = 0; i < n && !comp; i++)
185 if (comps[i].kind == exec) comp = comps[i].comp;
186 if (comp->ops->needs_ptx(comp) && src->ptx_len == 0) {
187 mesh_errf(err, "manifest: %s/%s.ptx (%s): no PTX in the catalog (the %s image)", src->dir, m.name, m.name, MESH_BACKENDS[exec]);
188 mesh_manifest_free(&m);
189 mesh_payload_free(src);
190 return false;
191 }
192 mesh_manifest_free(&m);
193 return comp->ops->prepare(comp, src, expect, root, out, err);
194}
195
196/* The registered exit cleanups, by order. */
197static struct {
198 int order;
199 void (*fn)(void);
200} cleanups[16];
201static size_t ncleanups;
202
203void mesh_backend_on_cleanup(int order, void (*fn)(void)) {
204 for (size_t i = 0; i < ncleanups; i++)
205 if (cleanups[i].fn == fn) return;
206 if (ncleanups == sizeof cleanups / sizeof cleanups[0]) return;
207 size_t at = ncleanups++;
208 while (at > 0 && cleanups[at - 1].order > order) {
209 cleanups[at] = cleanups[at - 1];
210 at--;
211 }
212 cleanups[at].order = order;
213 cleanups[at].fn = fn;
214}
215
216void mesh_backend_cleanup(void) {
217 for (size_t i = 0; i < ncleanups; i++) cleanups[i].fn();
218}