codegen: a small improvement in do_bswap and do_brev
[ajla.git] / module.c
blob65391064c7a3f974e4290740f6d2cc7d849d534f
1 /*
2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #include "ajla.h"
21 #ifndef FILE_OMIT
23 #include "args.h"
24 #include "mem_al.h"
25 #include "str.h"
26 #include "tree.h"
27 #include "rwlock.h"
28 #include "builtin.h"
29 #include "funct.h"
30 #include "pcode.h"
31 #include "array.h"
32 #include "profile.h"
33 #include "save.h"
35 #include "module.h"
37 pointer_t *start_fn;
38 shared_var pointer_t *optimizer_fn;
40 static struct tree modules;
41 rwlock_decl(modules_mutex);
43 struct module_function {
44 struct tree_entry entry;
45 pointer_t function;
46 pointer_t optimizer;
47 struct function_designator fd;
50 struct module {
51 struct tree_entry entry;
52 struct tree functions;
53 struct module_designator md;
56 static pointer_t module_create_optimizer_reference(struct module *m, struct function_designator *fd)
58 size_t i;
59 ajla_flat_option_t program;
60 int_default_t path_idx;
61 struct data *filename;
62 int_default_t *np;
63 struct data *nesting_path;
64 struct data *fn_ref;
65 struct thunk *result;
66 ajla_error_t err;
68 program = m->md.program;
70 path_idx = m->md.path_idx;
71 if (path_idx < 0 || (uint_default_t)path_idx != m->md.path_idx) {
72 return pointer_error(error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW), NULL, NULL pass_file_line);
75 filename = array_from_flat_mem(type_get_fixed(0, true), cast_ptr(const char *, m->md.path), m->md.path_len, &err);
76 if (unlikely(!filename)) {
77 return pointer_error(err, NULL, NULL pass_file_line);
80 np = mem_alloc_array_mayfail(mem_alloc_mayfail, int_default_t *, 0, 0, fd->n_entries, sizeof(int_default_t), &err);
81 if (unlikely(!np)) {
82 data_dereference(filename);
83 return pointer_error(err, NULL, NULL pass_file_line);
85 for (i = 0; i < fd->n_entries; i++) {
86 int_default_t e = (int_default_t)fd->entries[i];
87 if (unlikely(e < 0) || unlikely(e != fd->entries[i])) {
88 data_dereference(filename);
89 mem_free(np);
90 return pointer_error(error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW), NULL, NULL pass_file_line);
92 np[i] = e;
94 nesting_path = array_from_flat_mem(type_get_int(INT_DEFAULT_N), cast_ptr(const char *, np), fd->n_entries, &err);
95 mem_free(np);
96 if (unlikely(!nesting_path)) {
97 data_dereference(filename);
98 return pointer_error(err, NULL, NULL pass_file_line);
101 fn_ref = data_alloc_function_reference_mayfail(4, &err pass_file_line);
102 if (unlikely(!fn_ref)) {
103 data_dereference(filename);
104 data_dereference(nesting_path);
105 return pointer_error(err, NULL, NULL pass_file_line);
107 da(fn_ref,function_reference)->is_indirect = false;
108 da(fn_ref,function_reference)->u.direct = optimizer_fn;
110 data_fill_function_reference_flat(fn_ref, 0, type_get_int(INT_DEFAULT_N), cast_ptr(unsigned char *, &path_idx));
111 data_fill_function_reference(fn_ref, 1, pointer_data(filename));
112 data_fill_function_reference_flat(fn_ref, 2, type_get_flat_option(), cast_ptr(unsigned char *, &program));
113 data_fill_function_reference(fn_ref, 3, pointer_data(nesting_path));
115 if (unlikely(!thunk_alloc_function_call(pointer_data(fn_ref), 1, &result, &err))) {
116 data_dereference(fn_ref);
117 return pointer_error(err, NULL, NULL pass_file_line);
120 return pointer_thunk(result);
123 static bool module_function_init(struct module *m, struct module_function *mf, ajla_error_t attr_unused *mayfail)
125 pointer_t ptr, optr;
126 union internal_arg ia[3];
127 if (m->md.path_idx > 0) {
128 optr = module_create_optimizer_reference(m, &mf->fd);
129 ia[0].ptr = &mf->optimizer;
130 ia[1].ptr = &m->md;
131 ia[2].ptr = &mf->fd;
132 ptr = function_build_internal_thunk(pcode_build_function_from_array, 3, ia);
133 } else {
134 ia[0].ptr = &m->md;
135 ia[1].ptr = &mf->fd;
136 optr = function_build_internal_thunk(pcode_array_from_builtin, 2, ia);
137 ptr = function_build_internal_thunk(pcode_build_function_from_builtin, 2, ia);
139 mf->function = ptr;
140 mf->optimizer = optr;
141 return true;
144 static int function_test(const struct tree_entry *e, uintptr_t id)
146 const struct function_designator *fd = cast_cpp(const struct function_designator *, num_to_ptr(id));
147 const struct module_function *mf = get_struct(e, struct module_function, entry);
148 return function_designator_compare(&mf->fd, fd);
151 static struct module_function *module_find_function(struct module *m, const struct function_designator *fd, bool create, ajla_error_t *mayfail)
153 struct tree_insert_position ins;
154 struct tree_entry *e;
155 struct module_function *mf;
157 e = tree_find_for_insert(&m->functions, function_test, ptr_to_num(fd), &ins);
158 if (e)
159 return get_struct(e, struct module_function, entry);
161 if (!create)
162 return NULL;
164 mf = struct_alloc_array_mayfail(mem_alloc_mayfail, struct module_function, fd.entries, fd->n_entries, mayfail);
165 if (unlikely(!mf))
166 return NULL;
168 mf->fd.n_entries = fd->n_entries;
169 memcpy(mf->fd.entries, fd->entries, fd->n_entries * sizeof(fd->entries[0]));
171 if (unlikely(!module_function_init(m, mf, mayfail))) {
172 mem_free(mf);
173 return NULL;
176 tree_insert_after_find(&mf->entry, &ins);
178 return mf;
181 static int module_test(const struct tree_entry *e, uintptr_t id)
183 const struct module_designator *md = cast_cpp(const struct module_designator *, num_to_ptr(id));
184 const struct module *m = get_struct(e, struct module, entry);
185 return module_designator_compare(&m->md, md);
188 static struct module *module_find(const struct module_designator *md, bool create, ajla_error_t *mayfail)
190 struct tree_insert_position ins;
191 struct tree_entry *e;
192 struct module *m;
194 e = tree_find_for_insert(&modules, module_test, ptr_to_num(md), &ins);
195 if (likely(e != NULL))
196 return get_struct(e, struct module, entry);
198 if (!create)
199 return NULL;
201 m = struct_alloc_array_mayfail(mem_alloc_mayfail, struct module, md.path, md->path_len, mayfail);
202 if (unlikely(!m))
203 return NULL;
205 m->md.path_len = md->path_len;
206 m->md.path_idx = md->path_idx;
207 m->md.program = md->program;
208 memcpy(m->md.path, md->path, md->path_len);
210 tree_init(&m->functions);
212 tree_insert_after_find(&m->entry, &ins);
214 return m;
217 pointer_t *module_load_function(const struct module_designator *md, const struct function_designator *fd, bool optimizer, ajla_error_t *mayfail)
219 struct module *m;
220 struct module_function *mf;
221 bool create = false;
223 rwlock_lock_read(&modules_mutex);
224 retry:
225 m = module_find(md, create, mayfail);
226 if (!m)
227 goto lock_for_write;
229 mf = module_find_function(m, fd, create, mayfail);
230 if (!mf)
231 goto lock_for_write;
233 if (!create)
234 rwlock_unlock_read(&modules_mutex);
235 else
236 rwlock_unlock_write(&modules_mutex);
238 if (optimizer)
239 return &mf->optimizer;
240 else
241 return &mf->function;
243 lock_for_write:
244 if (unlikely(create)) {
245 rwlock_unlock_write(&modules_mutex);
246 return NULL;
248 create = true;
249 rwlock_unlock_read(&modules_mutex);
250 rwlock_lock_write(&modules_mutex);
251 goto retry;
255 static void module_finish_function(struct module_function *mf)
257 if (!pointer_is_thunk(mf->function)) {
258 struct data *d = pointer_get_data(mf->function);
259 struct tree_entry *e;
260 bool new_cache;
261 if (profiling) {
262 profile_collect(da(d,function)->function_name, load_relaxed(&da(d,function)->profiling_counter), load_relaxed(&da(d,function)->call_counter));
264 if (profiling_escapes) {
265 ip_t ip_rel;
266 for (ip_rel = 0; ip_rel < da(d,function)->code_size; ip_rel++) {
267 struct stack_trace_entry ste;
268 profile_counter_t profiling_counter = load_relaxed(&da(d,function)->escape_data[ip_rel].counter);
269 if (likely(!profiling_counter))
270 continue;
271 if (unlikely(!stack_trace_get_location(d, ip_rel, &ste)))
272 continue;
273 profile_escape_collect(ste.function_name, profiling_counter, ste.line, da(d,function)->code[ip_rel], load_relaxed(&da(d,function)->escape_data[ip_rel].line));
276 new_cache = false;
277 #ifdef HAVE_CODEGEN
278 if (likely(!pointer_is_thunk(da(d,function)->codegen))) {
279 struct data *codegen = pointer_get_data(da(d,function)->codegen);
280 if (unlikely(!da(codegen,codegen)->is_saved))
281 new_cache = true;
283 #endif
284 for (e = tree_first(&da(d,function)->cache); e && !new_cache; e = tree_next(e)) {
285 struct cache_entry *ce = get_struct(e, struct cache_entry, entry);
286 if (ce->save && da(d,function)->module_designator) {
287 new_cache = true;
288 break;
291 save_start_function(d, new_cache);
292 while ((e = tree_first(&da(d,function)->cache))) {
293 struct cache_entry *ce = get_struct(e, struct cache_entry, entry);
294 tree_delete(&ce->entry);
295 if (ce->save && da(d,function)->module_designator) {
296 /*debug("saving: %s", da(d,function)->function_name);*/
297 save_cache_entry(d, ce);
299 free_cache_entry(d, ce);
301 save_finish_function(d);
305 static void module_free_function(struct module_function *mf)
307 pointer_dereference(mf->function);
308 pointer_dereference(mf->optimizer);
312 struct module_designator *module_designator_alloc(unsigned path_idx, const uint8_t *path, size_t path_len, bool program, ajla_error_t *mayfail)
314 struct module_designator *md = struct_alloc_array_mayfail(mem_alloc_mayfail, struct module_designator, path, path_len, mayfail);
315 if (unlikely(!md))
316 return NULL;
317 md->path_idx = path_idx;
318 md->path_len = path_len;
319 md->program = program;
320 memcpy(md->path, path, path_len);
321 return md;
324 void module_designator_free(struct module_designator *md)
326 mem_free(md);
329 size_t module_designator_length(const struct module_designator *md)
331 return offsetof(struct module_designator, path[md->path_len]);
334 int module_designator_compare(const struct module_designator *md1, const struct module_designator *md2)
336 if (md1->path_idx < md2->path_idx)
337 return -1;
338 if (md1->path_idx > md2->path_idx)
339 return 1;
340 if (md1->program != md2->program)
341 return md1->program - md2->program;
342 if (md1->path_len < md2->path_len)
343 return -1;
344 if (md1->path_len > md2->path_len)
345 return 1;
346 return memcmp(md1->path, md2->path, md1->path_len);
349 struct function_designator *function_designator_alloc(const pcode_t *p, ajla_error_t *mayfail)
351 size_t i;
352 size_t n_entries = p[0];
353 struct function_designator *fd;
354 ajla_assert_lo(p[0] > 0, (file_line, "function_designator_alloc: invalid lenfth %ld", (long)p[0]));
355 fd = struct_alloc_array_mayfail(mem_alloc_mayfail, struct function_designator, entries, n_entries, mayfail);
356 if (unlikely(!fd))
357 return NULL;
358 fd->n_entries = n_entries;
359 for (i = 0; i < n_entries; i++)
360 fd->entries[i] = p[1 + i];
361 return fd;
364 struct function_designator *function_designator_alloc_single(pcode_t idx, ajla_error_t *mayfail)
366 pcode_t p[2];
367 p[0] = 1;
368 p[1] = idx;
369 return function_designator_alloc(p, mayfail);
372 void function_designator_free(struct function_designator *fd)
374 mem_free(fd);
377 size_t function_designator_length(const struct function_designator *fd)
379 return offsetof(struct function_designator, entries[fd->n_entries]);
382 int function_designator_compare(const struct function_designator *fd1, const struct function_designator *fd2)
384 if (fd1->n_entries < fd2->n_entries)
385 return -1;
386 if (fd1->n_entries > fd2->n_entries)
387 return 1;
388 /*return memcmp(fd1->entries, fd2->entries, fd1->n_entries * sizeof(fd1->entries[0]));*/
390 size_t i;
391 for (i = 0; i < fd1->n_entries; i++) {
392 if (fd1->entries[i] < fd2->entries[i])
393 return -1;
394 if (fd1->entries[i] > fd2->entries[i])
395 return 1;
397 return 0;
402 void name(module_init)(void)
404 const char *n;
405 struct module_designator *md;
406 struct function_designator *fd;
408 tree_init(&modules);
409 rwlock_init(&modules_mutex);
411 fd = function_designator_alloc_single(0, NULL);
413 n = "start";
414 md = module_designator_alloc(0, cast_ptr(const uint8_t *, n), strlen(n), false, NULL);
415 start_fn = module_load_function(md, fd, false, NULL);
416 module_designator_free(md);
418 n = "compiler/compiler";
419 md = module_designator_alloc(0, cast_ptr(const uint8_t *, n), strlen(n), false, NULL);
420 optimizer_fn = module_load_function(md, fd, false, NULL);
421 module_designator_free(md);
423 function_designator_free(fd);
426 void name(module_done)(void)
428 struct tree_entry *e1, *e2;
429 save_prepare();
430 for (e1 = tree_first(&modules); e1; e1 = tree_next(e1)) {
431 struct module *m = get_struct(e1, struct module, entry);
432 /*debug("saving: %.*s", (int)m->md.path_len, m->md.path);*/
433 for (e2 = tree_first(&m->functions); e2; e2 = tree_next(e2)) {
434 struct module_function *mf = get_struct(e2, struct module_function, entry);
435 module_finish_function(mf);
438 while (!tree_is_empty(&modules)) {
439 struct module *m = get_struct(tree_any(&modules), struct module, entry);
440 tree_delete(&m->entry);
441 while (!tree_is_empty(&m->functions)) {
442 struct module_function *mf = get_struct(tree_any(&m->functions), struct module_function, entry);
443 module_free_function(mf);
444 tree_delete(&mf->entry);
445 mem_free(mf);
447 mem_free(m);
449 rwlock_done(&modules_mutex);
452 #endif