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
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/>.
37 #if defined(OS_HAS_MMAP) && defined(USE_AMALLOC) && !((defined(OS_CYGWIN) || defined(OS_WIN32)) && defined(POINTER_COMPRESSION))
41 shared_var
bool save_disable
shared_init(false);
43 static const char id
[] = "AJLA" " " __DATE__
" " __TIME__
;
46 static char *save_data
;
47 static size_t save_len
;
49 static size_t last_md
;
52 struct tree_entry entry
;
53 uintptr_t old_position
;
54 uintptr_t new_position
;
58 static struct tree position_tree
;
60 static pointer_t
*pointers
;
61 static size_t pointers_len
;
63 static struct function_descriptor
*fn_descs
;
64 static size_t fn_descs_len
;
66 struct file_descriptor
{
67 struct function_descriptor
*fn_descs
;
70 size_t dependencies_l
;
72 cpu_feature_mask_t cpu_feature_flags
;
73 unsigned char privileged
;
74 unsigned char profiling
;
75 char ajla_id
[sizeof(id
)];
78 static char *loaded_data
;
79 static size_t loaded_data_len
;
81 static bool loaded_data_mapped
;
82 static bool loaded_data_amalloc
;
84 #define loaded_file_descriptor cast_ptr(struct file_descriptor *, loaded_data + loaded_data_len - sizeof(struct file_descriptor))
86 static size_t loaded_fn_idx
;
87 static size_t loaded_fn_cache
;
91 struct tree_entry entry
;
94 char path_name
[FLEXIBLE_ARRAY
];
97 static struct tree dependencies
;
98 static uchar_efficient_t dependencies_failed
;
99 static mutex_t dependencies_mutex
;
102 static int function_compare(const struct module_designator
*md1
, const struct function_designator
*fd1
, struct function_descriptor
*fd2
);
103 static void save_one_entry(arg_t n_arguments
, arg_t n_return_values
, pointer_t
*arguments
, pointer_t
*returns
);
104 static void save_finish_one(const struct module_designator
*md
, const struct function_designator
*fd
, arg_t n_arguments
, arg_t n_return_values
, code_t
*code
, ip_t code_size
, const struct local_variable_flags
*local_variables_flags
, frame_t n_slots
, struct data
*types
, struct line_position
*lp
, size_t lp_size
, void *unoptimized_code_base
, size_t unoptimized_code_size
, size_t *entries
, size_t n_entries
, struct trap_record
*trap_records
, size_t trap_records_size
);
105 static bool dep_get_stream(char **result
, size_t *result_l
);
108 static bool align_output(size_t align
)
111 while (unlikely(save_len
& (align
- 1)) != 0) {
112 if (unlikely(!array_add_mayfail(char, &save_data
, &save_len
, 0, NULL
, &sink
))) {
120 static pointer_t
offset_to_ptr(size_t offset
)
122 tag_t tag
= da_thunk_tag(save_data
+ offset
);
123 if (unlikely(!tag_is_thunk(tag
))) {
124 return pointer_data(data_pointer_tag(num_to_ptr(offset
), tag
));
126 return pointer_thunk(thunk_pointer_tag(num_to_ptr(offset
)));
130 static int position_tree_compare(const struct tree_entry
*t1
, uintptr_t p2
)
132 struct position_map
*pm
= get_struct(t1
, struct position_map
, entry
);
133 if (pm
->old_position
+ pm
->size
<= p2
)
135 if (pm
->old_position
> p2
)
140 static void free_position_tree(struct tree
*t
)
142 while (!tree_is_empty(t
)) {
143 struct position_map
*pm
= get_struct(tree_any(t
), struct position_map
, entry
);
144 tree_delete(&pm
->entry
);
149 static size_t save_range(const void *ptr
, size_t align
, size_t size
, struct stack_entry
*subptrs
, size_t subptrs_l
)
152 size_t data_offset
, payload_offset
, i
;
154 if (unlikely(!align_output(SAVED_DATA_ALIGN
)))
156 data_offset
= save_len
;
157 d
= data_alloc_flexible(saved
, offsets
, subptrs_l
, &sink
);
162 refcount_set_read_only(&d
->refcount_
);
163 da(d
,saved
)->n_offsets
= subptrs_l
;
165 if (unlikely(!array_add_multiple_mayfail(char, &save_data
, &save_len
, d
, offsetof(struct data
, u_
.saved
.offsets
[subptrs_l
]), NULL
, &sink
))) {
172 if (unlikely(!align_output(align
)))
174 payload_offset
= save_len
;
176 if (unlikely(!array_add_multiple_mayfail(char, &save_data
, &save_len
, ptr
, size
, NULL
, &sink
))) {
181 d
= cast_ptr(struct data
*, save_data
+ data_offset
);
182 d
= data_pointer_tag(d
, DATA_TAG_saved
);
183 da(d
,saved
)->total_size
= save_len
- data_offset
;
184 for (i
= 0; i
< subptrs_l
; i
++) {
185 da(d
,saved
)->offsets
[i
] = payload_offset
- data_offset
+ (cast_ptr(const char *, subptrs
[i
].ptr
) - cast_ptr(const char *, ptr
));
186 /*debug("offsets: %zx - %zx (%zx %zx %p %p)", i, da(d,saved)->offsets[i], payload_offset, data_offset, subptrs[i].ptr, ptr);*/
189 return payload_offset
;
192 static size_t save_pointer(pointer_t
*xptr
, bool verify_only
)
195 struct stack_entry
*subptrs
;
197 struct stack_entry
*stk
;
200 size_t ret
= (size_t)-1; /* avoid warning */
202 struct tree processed
;
203 tree_init(&processed
);
205 if (unlikely(!data_save_init_stack(xptr
, &stk
, &stk_l
))) {
212 size_t align
, size
, i
, data_pos
;
213 struct stack_entry ste
;
216 struct tree_entry
*e
;
217 struct tree_insert_position ins
;
219 struct position_map
*pm
;
221 ste
= stk
[stk_l
- 1];
222 p1
= ste
.t
->get_ptr(&ste
);
223 p1_num
= ptr_to_num(p1
);
224 e
= tree_find_for_insert(&position_tree
, position_tree_compare
, p1_num
, &ins
);
226 if (verify_only
&& !e
) {
227 e
= tree_find_for_insert(&processed
, position_tree_compare
, p1_num
, &ins
);
230 pm
= get_struct(e
, struct position_map
, entry
);
231 ret
= p1_num
- pm
->old_position
+ pm
->new_position
;
235 if (unlikely(!ste
.t
->get_properties(&ste
, &align
, &size
, &subptrs
, &subptrs_len
)))
238 ajla_assert_lo(size
!= 0, (file_line
, "save_pointer: size == 0"));
240 sps
= mem_alloc_array_mayfail(mem_calloc_mayfail
, uintptr_t *, 0, 0, subptrs_len
, sizeof(uintptr_t), &sink
);
241 if (unlikely(!sps
)) {
243 goto err_free_subptrs
;
247 for (i
= 0; i
< subptrs_len
; i
++) {
248 struct stack_entry
*subptr
;
251 struct tree_entry
*e2
;
253 subptr
= &subptrs
[i
];
254 if (!subptr
->t
->get_ptr
) {
258 p2
= subptr
->t
->get_ptr(subptr
);
259 p2_num
= ptr_to_num(p2
);
260 e2
= tree_find(&position_tree
, position_tree_compare
, p2_num
);
261 if (verify_only
&& !e2
) {
262 e2
= tree_find(&processed
, position_tree_compare
, p2_num
);
265 if (unlikely(!array_add_mayfail(struct stack_entry
, &stk
, &stk_l
, *subptr
, NULL
, &sink
))) {
271 struct position_map
*subpm
= get_struct(e2
, struct position_map
, entry
);
272 sps
[i
] = subpm
->new_position
- subpm
->old_position
;
283 if (!ste
.t
->wrap_on_save
) {
284 if (unlikely(!align_output(align
)))
287 if (unlikely(!array_add_multiple_mayfail(char, &save_data
, &save_len
, p1
, size
, NULL
, &sink
))) {
292 data_pos
= save_range(p1
, align
, size
, subptrs
, subptrs_len
);
293 if (unlikely(data_pos
== (size_t)-1)) {
297 ste
.t
->fixup_after_copy(save_data
+ data_pos
);
299 for (i
= 0; i
< subptrs_len
; i
++) {
300 size_t offset
= cast_ptr(char *, subptrs
[i
].ptr
) - p1
;
301 subptrs
[i
].t
->fixup_sub_ptr(save_data
+ data_pos
+ offset
, sps
[i
]);
310 pm
= mem_alloc_mayfail(struct position_map
*, sizeof(struct position_map
), &sink
);
315 pm
->old_position
= p1_num
;
316 pm
->new_position
= data_pos
;
318 tree_insert_after_find(&pm
->entry
, &ins
);
325 free_position_tree(&processed
);
336 free_position_tree(&processed
);
340 void save_prepare(void)
344 save_ok
= !save_disable
;
345 last_md
= (size_t)-1;
346 tree_init(&position_tree
);
350 loaded_fn_cache
= (size_t)-1;
351 if (unlikely(!array_init_mayfail(char, &save_data
, &save_len
, &sink
))) {
355 if (unlikely(!array_init_mayfail(struct function_descriptor
, &fn_descs
, &fn_descs_len
, &sink
))) {
361 static int compare_arguments(arg_t n_arguments
, pointer_t
*ptr1
, pointer_t
*ptr2
)
365 for (ai
= 0; ai
< n_arguments
; ai
++) {
366 int c
= data_compare(ptr1
[ai
], ptr2
[ai
], &sink
);
373 static void save_entries_until(pointer_t
*arguments
)
375 struct function_descriptor
*fn_desc
;
377 if (unlikely(!save_ok
))
379 if (loaded_fn_cache
== (size_t)-1)
381 fn_desc
= &loaded_file_descriptor
->fn_descs
[loaded_fn_idx
];
382 dsc
= fn_desc
->data_saved_cache
;
383 while (loaded_fn_cache
< da(dsc
,saved_cache
)->n_entries
) {
384 pointer_t
*dsc_arguments
= da(dsc
,saved_cache
)->pointers
+ loaded_fn_cache
* ((size_t)da(dsc
,saved_cache
)->n_arguments
+ (size_t)da(dsc
,saved_cache
)->n_return_values
);
386 int c
= compare_arguments(da(dsc
,saved_cache
)->n_arguments
, arguments
, dsc_arguments
);
387 if (unlikely(c
== DATA_COMPARE_OOM
)) {
392 internal(file_line
, "save_entries_until: data already present in loaded cache");
396 save_one_entry(da(dsc
,saved_cache
)->n_arguments
, da(dsc
,saved_cache
)->n_return_values
, dsc_arguments
, dsc_arguments
+ da(dsc
,saved_cache
)->n_arguments
);
403 static void save_loaded_function(struct function_descriptor
*fn_desc
)
408 if (unlikely(!array_init_mayfail(pointer_t
, &pointers
, &pointers_len
, &sink
))) {
412 dsc
= fn_desc
->data_saved_cache
;
413 /*debug("saving ld: %p, %lu", fn_desc, fn_desc - loaded_file_descriptor->fn_descs);*/
414 k
= (size_t)da(dsc
,saved_cache
)->n_arguments
+ (size_t)da(dsc
,saved_cache
)->n_return_values
;
415 for (i
= 0; i
< da(dsc
,saved_cache
)->n_entries
; i
++) {
416 pointer_t
*base
= da(dsc
,saved_cache
)->pointers
+ k
* i
;
417 save_one_entry(da(dsc
,saved_cache
)->n_arguments
, da(dsc
,saved_cache
)->n_return_values
, base
, base
+ da(dsc
,saved_cache
)->n_arguments
);
418 if (unlikely(!save_ok
))
421 save_finish_one(fn_desc
->md
,
423 da(dsc
,saved_cache
)->n_arguments
,
424 da(dsc
,saved_cache
)->n_return_values
,
427 fn_desc
->local_variables_flags
,
432 fn_desc
->unoptimized_code_base
,
433 fn_desc
->unoptimized_code_size
,
436 fn_desc
->trap_records
,
437 fn_desc
->trap_records_size
);
440 static void save_functions_until(struct data
*d
)
442 loaded_fn_cache
= (size_t)-1;
443 if (unlikely(!save_ok
))
447 /*debug("save_functions_until: %lu, %lu", loaded_fn_idx, loaded_file_descriptor->fn_descs_len);*/
448 while (loaded_fn_idx
< loaded_file_descriptor
->fn_descs_len
) {
449 struct function_descriptor
*fn_desc
= &loaded_file_descriptor
->fn_descs
[loaded_fn_idx
];
450 /*debug("test loaded: %lu", loaded_fn_idx);*/
452 int c
= function_compare(da(d
,function
)->module_designator
, da(d
,function
)->function_designator
, fn_desc
);
453 if (c
<= 0 && c
!= DATA_COMPARE_OOM
) {
460 save_loaded_function(fn_desc
);
467 static void save_one_entry(arg_t n_arguments
, arg_t n_return_values
, pointer_t
*arguments
, pointer_t
*returns
)
471 for (i
= 0; i
< n_arguments
; i
++) {
473 size_t st
= save_pointer(&arguments
[i
], false);
474 if (unlikely(st
== (size_t)-1)) {
478 ptr
= offset_to_ptr(st
);
479 if (unlikely(!array_add_mayfail(pointer_t
, &pointers
, &pointers_len
, ptr
, NULL
, &sink
))) {
484 for (i
= 0; i
< n_return_values
; i
++) {
486 size_t st
= save_pointer(&returns
[i
], false);
487 if (unlikely(st
== (size_t)-1)) {
491 ptr
= offset_to_ptr(st
);
492 if (unlikely(!array_add_mayfail(pointer_t
, &pointers
, &pointers_len
, ptr
, NULL
, &sink
))) {
499 void save_start_function(struct data
*d
, bool new_cache
)
501 if (!da(d
,function
)->n_return_values
)
503 if (!da(d
,function
)->is_saved
|| new_cache
) {
505 /*const struct module_designator *md = da(d,function)->module_designator;
506 const struct function_designator *fd = da(d,function)->function_designator;
507 debug("save_start_function: %u:%.*s:%u (%lu) - %s", md->path_idx, (int)md->path_len, md->path, fd->entries[0], fd->n_entries, da(d,function)->function_name);*/
508 save_functions_until(d
);
509 if (unlikely(!save_ok
))
511 if (unlikely(!array_init_mayfail(pointer_t
, &pointers
, &pointers_len
, &sink
))) {
518 void save_cache_entry(struct data
*d
, struct cache_entry
*ce
)
524 ajla_assert_lo(!ce
->n_pending
, (file_line
, "save_cache_entry: evaluation is in progress: %lu", (unsigned long)ce
->n_pending
));
525 if (unlikely(!save_ok
))
528 /*debug("save cache entry: %s", da(d,function)->function_name);*/
529 for (i
= 0; i
< da(d
,function
)->n_arguments
; i
++) {
530 if (unlikely(save_pointer(&ce
->arguments
[i
], true) == (size_t)-1)) {
531 /*debug("failed arg %d", i);*/
535 for (i
= 0; i
< da(d
,function
)->n_return_values
; i
++) {
536 if (unlikely(save_pointer(&ce
->returns
[i
].ptr
, true) == (size_t)-1)) {
537 /*debug("failed return %d", i);*/
541 save_entries_until(ce
->arguments
);
544 returns
= mem_alloc_array_mayfail(mem_alloc_mayfail
, pointer_t
*, 0, 0, da(d
,function
)->n_return_values
, sizeof(pointer_t
), &sink
);
545 if (unlikely(!returns
)) {
549 for (i
= 0; i
< da(d
,function
)->n_return_values
; i
++) {
550 returns
[i
] = ce
->returns
[i
].ptr
;
552 save_one_entry(da(d
,function
)->n_arguments
, da(d
,function
)->n_return_values
, ce
->arguments
, returns
);
556 static void save_finish_one(const struct module_designator
*md
, const struct function_designator
*fd
, arg_t n_arguments
, arg_t n_return_values
, code_t
*code
, ip_t code_size
, const struct local_variable_flags
*local_variables_flags
, frame_t n_slots
, struct data
*types
, struct line_position
*lp
, size_t lp_size
, void *unoptimized_code_base
, size_t unoptimized_code_size
, size_t *entries
, size_t n_entries
, struct trap_record
*trap_records
, size_t trap_records_size
)
560 struct function_descriptor fn_desc
;
562 size_t code_off
, lvf_off
, lp_off
, uc_off
, en_off
, tr_off
;
564 pointer_t types_ptr
= pointer_data(types
);
566 if (!n_return_values
)
570 /*debug("save_finish_one: %u:%.*s:%u (%lu)", md->path_idx, (int)md->path_len, md->path, fd->entries[0], fd->n_entries);*/
571 dsc
= data_alloc_flexible(saved_cache
, pointers
, pointers_len
, &sink
);
572 if (unlikely(!dsc
)) {
576 refcount_set_read_only(&dsc
->refcount_
);
577 da(dsc
,saved_cache
)->n_entries
= pointers_len
/ ((size_t)n_arguments
+ (size_t)n_return_values
);
578 da(dsc
,saved_cache
)->n_arguments
= n_arguments
;
579 da(dsc
,saved_cache
)->n_return_values
= n_return_values
;
580 memcpy(da(dsc
,saved_cache
)->pointers
, pointers
, pointers_len
* sizeof(pointer_t
));
581 if (unlikely(!align_output(SAVED_DATA_ALIGN
)))
584 saved_pos
= save_len
;
585 if (unlikely(!array_add_multiple_mayfail(char, &save_data
, &save_len
, dsc
, offsetof(struct data
, u_
.saved_cache
.pointers
[pointers_len
]), NULL
, &sink
))) {
590 code_off
= save_range(code
, align_of(code_t
), (size_t)code_size
* sizeof(code_t
), NULL
, 0);
591 if (unlikely(code_off
== (size_t)-1))
594 lvf_off
= save_range(local_variables_flags
, align_of(struct local_variable_flags
), (size_t)n_slots
* sizeof(struct local_variable_flags
), NULL
, 0);
595 if (unlikely(lvf_off
== (size_t)-1))
598 saved_types
= save_pointer(&types_ptr
, false);
599 if (unlikely(saved_types
== (size_t)-1)) {
604 lp_off
= save_range(lp
, align_of(struct line_position
), (size_t)lp_size
* sizeof(struct line_position
), NULL
, 0);
605 if (unlikely(lp_off
== (size_t)-1))
608 uc_off
= save_range(unoptimized_code_base
, CODE_ALIGNMENT
, unoptimized_code_size
, NULL
, 0);
609 if (unlikely(uc_off
== (size_t)-1))
612 en_off
= save_range(entries
, align_of(size_t), n_entries
* sizeof(size_t), NULL
, 0);
613 if (unlikely(en_off
== (size_t)-1))
616 #ifdef HAVE_CODEGEN_TRAPS
617 tr_off
= save_range(trap_records
, align_of(struct trap_record
), trap_records_size
* sizeof(struct trap_record
), NULL
, 0);
619 tr_off
= save_range(trap_records
, 1, 0, NULL
, 0);
621 if (unlikely(tr_off
== (size_t)-1))
624 if (!(last_md
!= (size_t)-1 && !module_designator_compare(cast_ptr(struct module_designator
*, save_data
+ last_md
), md
))) {
625 last_md
= save_range(md
, align_of(struct module_designator
), module_designator_length(md
), NULL
, 0);
626 if (unlikely(last_md
== (size_t)-1))
630 last_fd
= save_range(fd
, align_of(struct function_designator
), function_designator_length(fd
), NULL
, 0);
631 if (unlikely(last_fd
== (size_t)-1))
634 fn_desc
.data_saved_cache
= num_to_ptr(saved_pos
);
635 fn_desc
.data_saved_cache
= data_pointer_tag(fn_desc
.data_saved_cache
, DATA_TAG_saved_cache
);
636 fn_desc
.code
= num_to_ptr(code_off
);
637 fn_desc
.code_size
= code_size
;
638 fn_desc
.local_variables_flags
= num_to_ptr(lvf_off
);
639 fn_desc
.n_slots
= n_slots
;
640 fn_desc
.types
= num_to_ptr(saved_types
);
641 fn_desc
.types
= data_pointer_tag(fn_desc
.types
, DATA_TAG_function_types
);
642 fn_desc
.lp
= num_to_ptr(lp_off
);
643 fn_desc
.lp_size
= lp_size
;
644 fn_desc
.unoptimized_code_base
= num_to_ptr(uc_off
);
645 fn_desc
.unoptimized_code_size
= unoptimized_code_size
;
646 fn_desc
.entries
= num_to_ptr(en_off
);
647 fn_desc
.n_entries
= n_entries
;
648 fn_desc
.trap_records
= num_to_ptr(tr_off
);
649 fn_desc
.trap_records_size
= trap_records_size
;
650 fn_desc
.md
= num_to_ptr(last_md
);
651 fn_desc
.fd
= num_to_ptr(last_fd
);
652 if (!unlikely(array_add_mayfail(struct function_descriptor
, &fn_descs
, &fn_descs_len
, fn_desc
, NULL
, &sink
))) {
666 void save_finish_function(struct data
*d
)
668 void *unoptimized_code_base
= NULL
;
669 size_t unoptimized_code_size
= 0;
670 size_t *entries
= NULL
;
671 size_t n_entries
= 0;
672 struct trap_record
*trap_records
= NULL
;
673 size_t trap_records_size
= 0;
674 if (loaded_fn_cache
!= (size_t)-1) {
675 save_entries_until(NULL
);
676 if (unlikely(!save_ok
))
679 loaded_fn_cache
= (size_t)-1;
682 if (!pointer_is_thunk(da(d
,function
)->codegen
)) {
685 struct data
*codegen
= pointer_get_data(da(d
,function
)->codegen
);
686 entries
= da(codegen
,codegen
)->offsets
= mem_alloc_array_mayfail(mem_alloc_mayfail
, size_t *, 0, 0, da(codegen
,codegen
)->n_entries
, sizeof(size_t), &sink
);
687 if (unlikely(!entries
)) {
691 n_entries
= da(codegen
,codegen
)->n_entries
;
692 for (i
= 0; i
< n_entries
; i
++)
693 entries
[i
] = da(codegen
,codegen
)->unoptimized_code
[i
] - cast_ptr(char *, da(codegen
,codegen
)->unoptimized_code_base
);
694 unoptimized_code_base
= da(codegen
,codegen
)->unoptimized_code_base
;
695 unoptimized_code_size
= da(codegen
,codegen
)->unoptimized_code_size
;
696 #ifdef HAVE_CODEGEN_TRAPS
697 trap_records
= da(codegen
,codegen
)->trap_records
;
698 trap_records_size
= da(codegen
,codegen
)->trap_records_size
;
702 save_finish_one(da(d
,function
)->module_designator
,
703 da(d
,function
)->function_designator
,
704 da(d
,function
)->n_arguments
,
705 da(d
,function
)->n_return_values
,
706 da(d
,function
)->code
, da(d
,function
)->code_size
,
707 da(d
,function
)->local_variables_flags
,
708 function_n_variables(d
),
709 pointer_get_data(da(d
,function
)->types_ptr
),
711 da(d
,function
)->lp_size
,
712 unoptimized_code_base
,
713 unoptimized_code_size
,
720 static void save_finish_file(void)
722 const int fn_desc_ptrs
= 10;
724 struct stack_entry
*subptrs
;
727 size_t fn_descs_offset
, deps_offset
, file_desc_offset
;
728 struct file_descriptor file_desc
;
735 save_functions_until(NULL
);
737 subptrs
= mem_alloc_array_mayfail(mem_alloc_mayfail
, struct stack_entry
*, 0, 0, fn_descs_len
, sizeof(struct stack_entry
) * fn_desc_ptrs
, &sink
);
738 if (unlikely(!subptrs
)) {
742 for (i
= 0; i
< fn_descs_len
; i
++) {
743 subptrs
[i
* fn_desc_ptrs
+ 0].ptr
= &fn_descs
[i
].data_saved_cache
;
744 subptrs
[i
* fn_desc_ptrs
+ 1].ptr
= &fn_descs
[i
].code
;
745 subptrs
[i
* fn_desc_ptrs
+ 2].ptr
= &fn_descs
[i
].local_variables_flags
;
746 subptrs
[i
* fn_desc_ptrs
+ 3].ptr
= &fn_descs
[i
].types
;
747 subptrs
[i
* fn_desc_ptrs
+ 4].ptr
= &fn_descs
[i
].lp
;
748 subptrs
[i
* fn_desc_ptrs
+ 5].ptr
= &fn_descs
[i
].md
;
749 subptrs
[i
* fn_desc_ptrs
+ 6].ptr
= &fn_descs
[i
].fd
;
750 subptrs
[i
* fn_desc_ptrs
+ 7].ptr
= &fn_descs
[i
].unoptimized_code_base
;
751 subptrs
[i
* fn_desc_ptrs
+ 8].ptr
= &fn_descs
[i
].entries
;
752 subptrs
[i
* fn_desc_ptrs
+ 9].ptr
= &fn_descs
[i
].trap_records
;
753 /*debug("%p %p %zx", fn_descs[i].data_saved_cache, fn_descs[i].md, fn_descs[i].idx);*/
755 fn_descs_offset
= save_range(fn_descs
, align_of(struct function_descriptor
), fn_descs_len
* sizeof(struct function_descriptor
), subptrs
, fn_descs_len
* fn_desc_ptrs
);
757 if (unlikely(fn_descs_offset
== (size_t)-1))
760 file_desc
.fn_descs
= num_to_ptr(fn_descs_offset
);
761 file_desc
.fn_descs_len
= fn_descs_len
;
763 if (unlikely(!dep_get_stream(&deps
, &deps_l
))) {
767 deps_offset
= save_range(deps
, 1, deps_l
, NULL
, 0);
769 if (unlikely(deps_offset
== (size_t)-1))
772 file_desc
.dependencies
= num_to_ptr(deps_offset
);
773 file_desc
.dependencies_l
= deps_l
;
775 file_desc
.base
= num_to_ptr(0);
776 file_desc
.cpu_feature_flags
= cpu_feature_flags
;
777 file_desc
.privileged
= ipret_is_privileged
;
778 file_desc
.profiling
= profiling
;
779 memcpy(file_desc
.ajla_id
, id
, sizeof(id
));
781 subptrs
= mem_alloc_mayfail(struct stack_entry
*, sizeof(struct stack_entry
) * 3, &sink
);
782 if (unlikely(!subptrs
)) {
786 subptrs
[0].ptr
= &file_desc
.fn_descs
;
787 subptrs
[1].ptr
= &file_desc
.dependencies
;
788 subptrs
[2].ptr
= &file_desc
.base
;
789 file_desc_offset
= save_range(&file_desc
, align_of(struct file_descriptor
), sizeof(struct file_descriptor
), subptrs
, 3);
791 if (unlikely(file_desc_offset
== (size_t)-1))
795 static bool adjust_pointers(char *data
, size_t len
, uintptr_t offset
)
800 struct stack_entry
*subptrs
;
801 size_t align
, size
, subptrs_l
, i
;
802 if (unlikely((pos
& (SAVED_DATA_ALIGN
- 1)) != 0)) {
806 ref
= cast_ptr(refcount_t
*, data
+ pos
+ offsetof(struct data
, refcount_
));
807 if (refcount_is_one(ref
)) {
808 pos
+= SAVED_DATA_ALIGN
;
811 if (unlikely(!refcount_is_read_only(ref
)))
812 internal(file_line
, "adjust_pointers: invalid refcount at position %"PRIxMAX
"", (uintmax_t)pos
);
813 if (unlikely(!data_save(data
+ pos
, offset
, &align
, &size
, &subptrs
, &subptrs_l
)))
815 for (i
= 0; i
< subptrs_l
; i
++) {
816 subptrs
[i
].t
->fixup_sub_ptr(subptrs
[i
].ptr
, offset
);
825 static int function_compare(const struct module_designator
*md1
, const struct function_designator
*fd1
, struct function_descriptor
*fd2
)
827 int x
= module_designator_compare(md1
, fd2
->md
);
830 return function_designator_compare(fd1
, fd2
->fd
);
833 struct function_descriptor
*save_find_function_descriptor(const struct module_designator
*md
, const struct function_designator
*fd
)
835 struct function_descriptor
*fn_descs
;
841 fn_descs
= loaded_file_descriptor
->fn_descs
;
842 fn_descs_len
= loaded_file_descriptor
->fn_descs_len
;
843 binary_search(size_t, fn_descs_len
, result
, !(cmp
= function_compare(md
, fd
, &fn_descs
[result
])), cmp
>= 0, return NULL
);
844 return &fn_descs
[result
];
847 static int dep_compare(const struct tree_entry
*e1
, uintptr_t e2
)
849 struct dependence
*d1
= get_struct(e1
, struct dependence
, entry
);
850 const char *n2
= num_to_ptr(e2
);
851 return strcmp(d1
->path_name
, n2
);
854 static bool dep_fingerprint(const char *path_name
, char **result
, size_t *result_l
)
858 if (unlikely(!array_init_mayfail(char, result
, result_l
, &err
)))
860 if (unlikely(!os_stat(dir_none
, path_name
, false, &st
, &err
))) {
861 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &err
.error_class
), sizeof err
.error_class
, NULL
, &err
)))
863 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &err
.error_type
), sizeof err
.error_type
, NULL
, &err
)))
865 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &err
.error_aux
), sizeof err
.error_aux
, NULL
, &err
)))
869 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_mode
), sizeof st
.st_mode
, NULL
, &err
)))
871 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_dev
), sizeof st
.st_dev
, NULL
, &err
)))
874 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_ino
), sizeof st
.st_ino
, NULL
, &err
)))
877 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_size
), sizeof st
.st_size
, NULL
, &err
)))
879 #if defined(HAVE_STRUCT_STAT_ST_ATIM)
880 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_ctim
.tv_sec
), sizeof st
.st_ctim
.tv_sec
, NULL
, &err
)))
882 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_ctim
.tv_nsec
), sizeof st
.st_ctim
.tv_nsec
, NULL
, &err
)))
884 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_mtim
.tv_sec
), sizeof st
.st_mtim
.tv_sec
, NULL
, &err
)))
886 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_mtim
.tv_nsec
), sizeof st
.st_mtim
.tv_nsec
, NULL
, &err
)))
888 #elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
889 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_ctimespec
.tv_sec
), sizeof st
.st_ctimespec
.tv_sec
, NULL
, &err
)))
891 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_ctimespec
.tv_nsec
), sizeof st
.st_ctimespec
.tv_nsec
, NULL
, &err
)))
893 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_mtimespec
.tv_sec
), sizeof st
.st_mtimespec
.tv_sec
, NULL
, &err
)))
895 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_mtimespec
.tv_nsec
), sizeof st
.st_mtimespec
.tv_nsec
, NULL
, &err
)))
898 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_ctime
), sizeof st
.st_ctime
, NULL
, &err
)))
900 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, cast_ptr(char *, &st
.st_mtime
), sizeof st
.st_mtime
, NULL
, &err
)))
906 void save_register_dependence(const char *path_name
)
908 struct tree_insert_position ins
;
910 size_t path_name_len
;
911 struct dependence
*dep
;
913 mutex_lock(&dependencies_mutex
);
914 /*debug("registering dependence: '%s'", path_name);*/
915 if (unlikely(tree_find_for_insert(&dependencies
, dep_compare
, ptr_to_num(path_name
), &ins
) != NULL
))
918 path_name_len
= strlen(path_name
) + 1;
919 dep
= struct_alloc_array_mayfail(mem_alloc_mayfail
, struct dependence
, path_name
, path_name_len
, &sink
);
920 if (unlikely(!dep
)) {
921 dependencies_failed
= true;
924 memcpy(dep
->path_name
, path_name
, path_name_len
);
925 if (unlikely(!dep_fingerprint(dep
->path_name
, &dep
->fingerprint
, &dep
->fingerprint_l
))) {
927 dependencies_failed
= true;
931 tree_insert_after_find(&dep
->entry
, &ins
);
934 mutex_unlock(&dependencies_mutex
);
937 static bool dep_get_stream(char **result
, size_t *result_l
)
940 struct tree_entry
*e
;
941 if (unlikely(!array_init_mayfail(char, result
, result_l
, &sink
)))
943 for (e
= tree_first(&dependencies
); e
; e
= tree_next(e
)) {
944 struct dependence
*dep
= get_struct(e
, struct dependence
, entry
);
945 size_t path_name_len
= strlen(dep
->path_name
) + 1;
946 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, dep
->path_name
, path_name_len
, NULL
, &sink
)))
948 if (unlikely(!array_add_mayfail(char, result
, result_l
, (char)dep
->fingerprint_l
, NULL
, &sink
)))
950 if (unlikely(!array_add_multiple_mayfail(char, result
, result_l
, dep
->fingerprint
, dep
->fingerprint_l
, NULL
, &sink
)))
956 static bool dep_verify(void)
958 const char *ptr
, *end
;
959 ptr
= loaded_file_descriptor
->dependencies
;
960 end
= ptr
+ loaded_file_descriptor
->dependencies_l
;
964 if (unlikely(!dep_fingerprint(ptr
, &fp
, &fp_l
)))
966 ptr
+= strlen(ptr
) + 1;
967 l
= (unsigned char)*ptr
;
969 if (unlikely(l
!= fp_l
) || unlikely(memcmp(ptr
, fp
, fp_l
))) {
976 ptr
= loaded_file_descriptor
->dependencies
;
977 end
= ptr
+ loaded_file_descriptor
->dependencies_l
;
979 struct tree_insert_position ins
;
981 struct dependence
*dep
;
983 const char *path_name
, *fingerprint
;
984 size_t path_name_len
, fingerprint_len
;
986 path_name_len
= strlen(ptr
) + 1;
987 ptr
+= path_name_len
;
988 fingerprint
= ptr
+ 1;
989 fingerprint_len
= (unsigned char)*ptr
;
990 ptr
+= 1 + fingerprint_len
;
992 if (unlikely(tree_find_for_insert(&dependencies
, dep_compare
, ptr_to_num(path_name
), &ins
) != NULL
))
995 dep
= struct_alloc_array_mayfail(mem_alloc_mayfail
, struct dependence
, path_name
, path_name_len
, &sink
);
996 if (unlikely(!dep
)) {
999 memcpy(dep
->path_name
, path_name
, path_name_len
);
1000 dep
->fingerprint_l
= fingerprint_len
;
1001 dep
->fingerprint
= mem_alloc_mayfail(char *, fingerprint_len
, &sink
);
1002 if (unlikely(!dep
->fingerprint
)) {
1006 memcpy(dep
->fingerprint
, fingerprint
, fingerprint_len
);
1007 tree_insert_after_find(&dep
->entry
, &ins
);
1009 ajla_assert_lo(ptr
== end
, (file_line
, "dep_verify: end mismatch: %p != %p", ptr
, end
));
1013 static void unmap_loaded_data(void)
1017 if (likely(loaded_data_mapped
)) {
1018 os_munmap(loaded_data
, loaded_data_len
, true);
1019 } else if (loaded_data_amalloc
) {
1020 amalloc_run_free(loaded_data
, loaded_data_len
);
1024 mem_free(loaded_data
);
1030 static char *save_get_file(void)
1033 char *pn
, *fn
, *ext
;
1035 pn
= str_dup(*program_name
? program_name
: "ajla", -1, &sink
);
1039 if (pn_l
> 5 && !strcasecmp(pn
+ pn_l
- 5, ".ajla"))
1041 #ifndef POINTER_COMPRESSION
1046 if (unlikely(!array_init_mayfail(char, &fn
, &fn_l
, &sink
)))
1048 if (unlikely(!array_add_multiple_mayfail(char, &fn
, &fn_l
, pn
, pn_l
, NULL
, &sink
)))
1050 if (unlikely(!array_add_multiple_mayfail(char, &fn
, &fn_l
, ext
, strlen(ext
), NULL
, &sink
)))
1052 if (unlikely(!array_add_mayfail(char, &fn
, &fn_l
, 0, NULL
, &sink
)))
1059 static void save_load_cache(void)
1066 struct file_descriptor file_desc
;
1068 if (unlikely(save_disable
))
1071 path
= os_get_directory_cache(&sink
);
1072 if (unlikely(!path
))
1074 dir
= os_dir_open(os_cwd
, path
, 0, &sink
);
1076 if (unlikely(!dir_handle_is_valid(dir
)))
1079 file
= save_get_file();
1080 if (unlikely(!file
)) {
1084 h
= os_open(dir
, file
, O_RDONLY
, 0, &sink
);
1087 if (unlikely(!handle_is_valid(h
)))
1090 if (unlikely(!os_fstat(h
, &st
, &sink
))) {
1094 if (unlikely(!S_ISREG(st
.st_mode
))) {
1098 loaded_data_len
= (size_t)st
.st_size
;
1099 if (unlikely((uintmax_t)st
.st_size
!= loaded_data_len
)) {
1103 if (unlikely(loaded_data_len
< sizeof(struct file_descriptor
))) {
1104 warning("too short cache file");
1108 if (unlikely(!os_pread_all(h
, cast_ptr(char *, &file_desc
), sizeof(struct file_descriptor
), st
.st_size
- sizeof(struct file_descriptor
), &sink
))) {
1112 if (unlikely(file_desc
.cpu_feature_flags
!= cpu_feature_flags
) ||
1113 unlikely(file_desc
.privileged
!= ipret_is_privileged
) ||
1114 unlikely(file_desc
.profiling
!= profiling
) ||
1115 unlikely(memcmp(file_desc
.ajla_id
, id
, sizeof(id
)))) {
1121 int prot_flags
= PROT_READ
1127 #ifndef POINTER_COMPRESSION
1128 ptr
= os_mmap(file_desc
.base
, loaded_data_len
, prot_flags
, MAP_PRIVATE
, h
, 0, &sink
);
1129 /*debug("mapped: %p, %lx -> %p", file_desc.base, loaded_data_len, ptr);*/
1130 if (unlikely(ptr
== MAP_FAILED
))
1132 if (unlikely(ptr
!= file_desc
.base
)) {
1133 /*debug("address mismatch");*/
1134 os_munmap(ptr
, loaded_data_len
, true);
1138 loaded_data_mapped
= true;
1140 if (unlikely(!amalloc_ptrcomp_try_reserve_range(file_desc
.base
, loaded_data_len
))) {
1141 /*debug("amalloc_ptrcomp_try_reserve_range failed");*/
1144 ptr
= os_mmap(file_desc
.base
, loaded_data_len
, prot_flags
, MAP_PRIVATE
| MAP_FIXED
, h
, 0, &sink
);
1145 if (unlikely(ptr
== MAP_FAILED
)) {
1146 amalloc_run_free(file_desc
.base
, loaded_data_len
);
1149 if (unlikely(ptr
!= file_desc
.base
))
1150 internal(file_line
, "save_load_cache: os_mmap(MAP_FIXED) returned different pointer: %p != %p", ptr
, file_desc
.base
);
1152 loaded_data_amalloc
= true;
1159 loaded_data
= mem_alloc_mayfail(char *, st
.st_size
, &sink
);
1160 if (unlikely(!loaded_data
)) {
1164 if (unlikely(!os_pread_all(h
, loaded_data
, st
.st_size
, 0, &sink
))) {
1166 mem_free(loaded_data
);
1172 #if defined(CODEGEN_USE_HEAP) || !defined(OS_HAS_MMAP)
1173 /*debug("adjusting pointers: %p, %p", loaded_data, loaded_data + loaded_data_len);*/
1174 adjust_pointers(loaded_data
, loaded_data_len
, ptr_to_num(loaded_data
) - ptr_to_num(loaded_file_descriptor
->base
));
1175 os_code_invalidate_cache(cast_ptr(uint8_t *, loaded_data
), loaded_data_len
, true);
1179 new_ptr
= amalloc_run_alloc(CODE_ALIGNMENT
, loaded_data_len
, false, false);
1180 if (unlikely(!new_ptr
)) {
1181 unmap_loaded_data();
1184 memcpy(new_ptr
, loaded_data
, loaded_data_len
);
1185 mem_free(loaded_data
);
1186 loaded_data
= new_ptr
;
1187 /*debug("adjusting pointers: %p, %p", loaded_data, loaded_data + loaded_data_len);*/
1188 adjust_pointers(loaded_data
, loaded_data_len
, ptr_to_num(loaded_data
) - ptr_to_num(loaded_file_descriptor
->base
));
1189 os_code_invalidate_cache(cast_ptr(uint8_t *, loaded_data
), loaded_data_len
, true);
1190 loaded_data_amalloc
= true;
1194 /*adjust_pointers(loaded_data, loaded_data_len, 0);*/
1198 if (unlikely(!dep_verify())) {
1199 unmap_loaded_data();
1205 for (i
= 0; i
< loaded_file_descriptor
->fn_descs_len
; i
++) {
1206 struct function_descriptor
*fn_desc
= &loaded_file_descriptor
->fn_descs
[i
];
1207 struct data
*dsc
= fn_desc
->data_saved_cache
;
1209 /*const struct module_designator *md = fn_desc->md;
1210 debug("content: %u:%.*s:%lu:%lu", md->path_idx, (int)md->path_len, md->path, fn_desc->fd->n_entries, (long)fn_desc->fd->entries[0]);*/
1212 int c
= function_compare(loaded_file_descriptor
->fn_descs
[i
- 1].md
, loaded_file_descriptor
->fn_descs
[i
- 1].fd
, &loaded_file_descriptor
->fn_descs
[i
]);
1213 if (unlikely(c
>= 0))
1214 internal(file_line
, "save_load_cache: misordered function descriptors: %d (%"PRIuMAX
" / %"PRIuMAX
")", c
, (uintmax_t)i
, (uintmax_t)loaded_file_descriptor
->fn_descs_len
);
1216 k
= (size_t)da(dsc
,saved_cache
)->n_arguments
+ (size_t)da(dsc
,saved_cache
)->n_return_values
;
1217 if (da(dsc
,saved_cache
)->n_entries
) {
1218 for (j
= 0; j
< da(dsc
,saved_cache
)->n_entries
- 1; j
++) {
1219 pointer_t
*p1
= &da(dsc
,saved_cache
)->pointers
[j
* k
];
1220 pointer_t
*p2
= &da(dsc
,saved_cache
)->pointers
[(j
+ 1) * k
];
1221 int c
= compare_arguments(da(dsc
,saved_cache
)->n_arguments
, p1
, p2
);
1222 if (unlikely(c
>= 0) && c
!= DATA_COMPARE_OOM
)
1223 internal(file_line
, "save_load_cache: misordered cache entries: %d", c
);
1231 void name(save_init
)(void)
1235 loaded_data_mapped
= false;
1236 loaded_data_amalloc
= false;
1238 tree_init(&dependencies
);
1239 dependencies_failed
= false;
1240 mutex_init(&dependencies_mutex
);
1244 static void save_stream(void)
1249 char *save_data_mapped
;
1251 path
= os_get_directory_cache(&sink
);
1252 if (unlikely(!path
))
1254 file
= save_get_file();
1259 /*debug("writing file: '%s'", file);*/
1261 save_data_mapped
= amalloc_run_alloc(1, save_len
, false, true);
1262 /*debug("save_stream: %p, %llx", save_data_mapped, save_len);*/
1263 if (save_data_mapped
) {
1264 memcpy(save_data_mapped
, save_data
, save_len
);
1265 /*debug("adjusting pointers when saving");*/
1266 adjust_pointers(save_data_mapped
, save_len
, ptr_to_num(save_data_mapped
));
1267 os_write_atomic(path
, file
, save_data_mapped
, save_len
, &sink
);
1268 amalloc_run_free(save_data_mapped
, save_len
);
1272 os_write_atomic(path
, file
, save_data
, save_len
, &sink
);
1278 void name(save_done
)(void)
1280 /*debug("1: save_data: %p, save_ok %d", save_data, save_ok);*/
1284 free_position_tree(&position_tree
);
1285 /*debug("2: save_data: %p, save_ok %d", save_data, save_ok);*/
1290 mem_free(save_data
);
1295 unmap_loaded_data();
1296 while (!tree_is_empty(&dependencies
)) {
1297 struct dependence
*dep
= get_struct(tree_any(&dependencies
), struct dependence
, entry
);
1298 tree_delete(&dep
->entry
);
1299 mem_free(dep
->fingerprint
);
1302 mutex_done(&dependencies_mutex
);