infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
trampoline.c
Go to the documentation of this file.
1
36#include "common/utility.h"
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41#if defined(INFIX_OS_MACOS)
42#include <pthread.h>
43#endif
44
45#if defined(INFIX_OS_WINDOWS)
46#include <windows.h>
47#else
48#include <sys/mman.h>
49#include <unistd.h>
50#endif
51
52// Forward Declarations for internal functions
54 infix_reverse_t **, infix_type *, infix_type **, size_t, size_t, void *, void *, bool);
55
56// ABI Specification Declarations & Dispatch
57/*
58 * These extern declarations link to the ABI-specific v-tables defined in files
59 * like `abi_win_x64.c` and `abi_sysv_x64.c`. The preprocessor ensures that only
60 * the v-table for the target platform is linked into the final binary.
61 */
62#if defined(INFIX_ABI_WINDOWS_X64)
65#elif defined(INFIX_ABI_SYSV_X64)
68#elif defined(INFIX_ABI_AAPCS64)
71#endif
72
81#if defined(INFIX_ABI_WINDOWS_X64)
83#elif defined(INFIX_ABI_SYSV_X64)
85#elif defined(INFIX_ABI_AAPCS64)
87#else
88 return nullptr;
89#endif
90}
91
98#if defined(INFIX_ABI_WINDOWS_X64)
100#elif defined(INFIX_ABI_SYSV_X64)
102#elif defined(INFIX_ABI_AAPCS64)
103 return &g_arm64_reverse_spec;
104#else
105 return nullptr;
106#endif
107}
108
109// Code Buffer & Emitter Utilities
115 buf->capacity = 64; // Start with a small initial capacity.
116 buf->arena = arena;
117 buf->code = infix_arena_alloc(arena, buf->capacity, 16);
118 buf->size = 0;
119 buf->error = (buf->code == nullptr);
120}
121
128void code_buffer_append(code_buffer * buf, const void * data, size_t len) {
129 if (buf->error)
130 return; // If already in an error state, do nothing.
131
132 if (len > SIZE_MAX - buf->size) {
133 buf->error = true;
134 return;
135 }
136
137 if (buf->size + len > buf->capacity) {
138 size_t new_capacity = buf->capacity;
139 while (new_capacity < buf->size + len) {
140 if (new_capacity > SIZE_MAX / 2) {
141 buf->error = true;
142 return;
143 }
144 new_capacity *= 2;
145 }
146
147 void * new_code = infix_arena_alloc(buf->arena, new_capacity, 16);
148 if (new_code == nullptr) {
149 buf->error = true;
150 return;
151 }
152 infix_memcpy(new_code, buf->code, buf->size);
153 buf->code = new_code;
154 buf->capacity = new_capacity;
155 }
156 infix_memcpy(buf->code + buf->size, data, len);
157 buf->size += len;
158}
159
161void emit_byte(code_buffer * buf, uint8_t byte) {
162 code_buffer_append(buf, &byte, 1);
163}
164
166void emit_int32(code_buffer * buf, int32_t value) {
167 code_buffer_append(buf, &value, 4);
168}
169
171void emit_int64(code_buffer * buf, int64_t value) {
172 code_buffer_append(buf, &value, 8);
173}
174
175// Internal Type System Helpers
185 if (!type)
186 return true;
187 switch (type->category) {
189 return false;
192 case INFIX_TYPE_ARRAY:
195 case INFIX_TYPE_UNION:
196 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i)
198 return false;
199 return true;
202 return false;
203 for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i)
205 return false;
206 return true;
207 default:
208 return true;
209 }
210}
211
212// Forward Trampoline Implementation
213/*
214 * Implementation for infix_forward_get_unbound_code.
215 * This is a type-safe accessor for the public API.
216 */
218 if (trampoline == nullptr || trampoline->target_fn != nullptr)
219 return nullptr;
220 return (infix_unbound_cif_func)trampoline->exec.rx_ptr;
221}
222
223/*
224 * Implementation for infix_forward_get_code.
225 * This is a type-safe accessor for the public API.
226 */
228 if (trampoline == nullptr || trampoline->target_fn == nullptr)
229 return nullptr;
230 return (infix_cif_func)trampoline->exec.rx_ptr;
231}
232
254 size_t num_args,
255 size_t num_fixed_args,
256 infix_arena_t * source_arena,
257 void * target_fn) {
258 if (out_trampoline == nullptr || (arg_types == nullptr && num_args > 0))
260
261 // Validate the type graphs to ensure they don't contain unresolved placeholders.
265 }
266 for (size_t i = 0; i < num_args; ++i) {
267 if (arg_types[i] == nullptr || !_is_type_graph_resolved(arg_types[i]))
269 }
270
272 if (spec == nullptr)
274
276 infix_call_frame_layout * layout = nullptr;
277 infix_forward_t * handle = nullptr;
278 // Use a temporary arena for layout calculations and code generation.
279 infix_arena_t * temp_arena = infix_arena_create(65536);
280 if (!temp_arena)
282
283 code_buffer buf;
284 code_buffer_init(&buf, temp_arena);
285
286 // JIT Compilation Pipeline
288 temp_arena, &layout, return_type, arg_types, num_args, num_fixed_args, target_fn);
289 if (status != INFIX_SUCCESS)
290 goto cleanup;
291 status = spec->generate_forward_prologue(&buf, layout);
292 if (status != INFIX_SUCCESS)
293 goto cleanup;
294 status = spec->generate_forward_argument_moves(&buf, layout, arg_types, num_args, num_fixed_args);
295 if (status != INFIX_SUCCESS)
296 goto cleanup;
297
298 status = spec->generate_forward_call_instruction(&buf, layout);
299 if (status != INFIX_SUCCESS)
300 goto cleanup;
301
302 status = spec->generate_forward_epilogue(&buf, layout, return_type);
303 if (status != INFIX_SUCCESS)
304 goto cleanup;
305
306 if (buf.error || temp_arena->error) {
308 goto cleanup;
309 }
310
311 // Final Trampoline Handle Assembly
312 handle = infix_calloc(1, sizeof(infix_forward_t));
313 if (handle == nullptr) {
315 goto cleanup;
316 }
317
318 // "Just-Right" Arena Sizing Optimization
319 size_t required_size = source_arena ? source_arena->current_offset : 8192; // Fallback size
320 handle->arena = infix_arena_create(required_size + INFIX_TRAMPOLINE_HEADROOM);
321 if (handle->arena == nullptr) {
323 goto cleanup;
324 }
325
326 // Deep copy all type info into the handle's own arena to make it self-contained.
328 handle->arg_types = infix_arena_alloc(handle->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
329 if ((handle->return_type == nullptr && return_type != nullptr) || (num_args > 0 && handle->arg_types == nullptr)) {
331 goto cleanup;
332 }
333 for (size_t i = 0; i < num_args; ++i) {
334 handle->arg_types[i] = _copy_type_graph_to_arena(handle->arena, arg_types[i]);
335 if (handle->arg_types[i] == nullptr && arg_types[i] != nullptr) {
337 goto cleanup;
338 }
339 }
340 handle->num_args = num_args;
341 handle->num_fixed_args = num_fixed_args;
342 handle->target_fn = target_fn;
343
344 // Allocate executable memory and copy the generated code into it.
345 handle->exec = infix_executable_alloc(buf.size);
346 if (handle->exec.rw_ptr == nullptr) {
348 goto cleanup;
349 }
350 infix_memcpy(handle->exec.rw_ptr, buf.code, buf.size);
351
354 goto cleanup;
355 }
356
357 infix_dump_hex(handle->exec.rx_ptr, handle->exec.size, "Forward Trampoline Machine Code");
358 *out_trampoline = handle;
359
360cleanup:
361 if (status != INFIX_SUCCESS && handle != nullptr)
362 infix_forward_destroy(handle);
363 infix_arena_destroy(temp_arena);
364 return status;
365}
366
367/*
368 * Implementation for infix_forward_create_manual (bound).
369 */
373 size_t num_args,
374 size_t num_fixed_args,
375 void * target_function) {
378 out_trampoline, return_type, arg_types, num_args, num_fixed_args, nullptr, target_function);
379}
380
395 size_t num_args,
396 size_t num_fixed_args) {
399 out_trampoline, return_type, arg_types, num_args, num_fixed_args, nullptr, nullptr);
400}
401
402/*
403 * Implementation for infix_forward_destroy.
404 * Frees all resources associated with a forward trampoline.
405 */
407 if (trampoline == nullptr)
408 return;
409 if (trampoline->arena)
410 infix_arena_destroy(trampoline->arena);
411 infix_executable_free(trampoline->exec);
412 infix_free(trampoline);
413}
414
415// Reverse Trampoline Implementation
416/*
417 * @internal
418 * A helper to get the system's memory page size.
419 */
420static size_t get_page_size() {
421#if defined(INFIX_OS_WINDOWS)
422 SYSTEM_INFO sysInfo;
423 GetSystemInfo(&sysInfo);
424 return sysInfo.dwPageSize;
425#else
426 return sysconf(_SC_PAGESIZE);
427#endif
428}
429
446 size_t num_args,
447 size_t num_fixed_args,
448 void * user_callback_fn,
449 void * user_data,
450 bool is_callback) {
451 if (out_context == nullptr || num_fixed_args > num_args)
453
454 // Validate the user-provided type graphs.
457 if (arg_types == nullptr && num_args > 0)
459 for (size_t i = 0; i < num_args; ++i) {
460 if (arg_types[i] == nullptr || !_is_type_graph_resolved(arg_types[i]))
462 }
463
465 if (spec == nullptr)
467
469 infix_reverse_call_frame_layout * layout = nullptr;
470 infix_reverse_t * context = nullptr;
471 infix_arena_t * temp_arena = nullptr; // For layout calculations and code generation.
472 infix_protected_t prot = {.rw_ptr = nullptr, .size = 0};
473 code_buffer buf;
474
475 temp_arena = infix_arena_create(65536);
476 if (!temp_arena)
478
479 code_buffer_init(&buf, temp_arena);
480
481 // Allocate page-aligned memory for the context struct itself.
482 size_t page_size = get_page_size();
483 size_t context_alloc_size = (sizeof(infix_reverse_t) + page_size - 1) & ~(page_size - 1);
484 prot = infix_protected_alloc(context_alloc_size);
485 if (prot.rw_ptr == nullptr) {
487 goto cleanup;
488 }
489 context = (infix_reverse_t *)prot.rw_ptr;
490 infix_memset(context, 0, context_alloc_size);
491
492 // Create the persistent arena that will be owned by the context.
493 context->arena = infix_arena_create(8192);
494 if (context->arena == nullptr) {
496 goto cleanup;
497 }
498
499 // Initialize common context fields.
500 context->protected_ctx = prot;
501 context->num_args = num_args;
502 context->num_fixed_args = num_fixed_args;
503 context->is_variadic = (num_fixed_args < num_args);
504 context->user_callback_fn = user_callback_fn;
505 context->user_data = user_data;
507 context->cached_forward_trampoline = nullptr; // Default to closure
508
509 // Deep copy type information into the context's own arena.
511 context->arg_types = infix_arena_alloc(context->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
512 if ((context->return_type == nullptr && return_type != nullptr) ||
513 (num_args > 0 && context->arg_types == nullptr)) {
515 goto cleanup;
516 }
517 for (size_t i = 0; i < num_args; ++i) {
518 context->arg_types[i] = _copy_type_graph_to_arena(context->arena, arg_types[i]);
519 if (context->arg_types[i] == nullptr && arg_types[i] != nullptr) {
521 goto cleanup;
522 }
523 }
524
525 if (is_callback) {
526 // --- Type-Safe "Callback" Path ---
527 // The user's handler has a "clean" signature without the context.
528 // The cached forward trampoline must therefore be generated for num_args.
530 context->return_type,
531 context->arg_types,
532 context->num_args,
533 context->num_fixed_args,
534 user_callback_fn);
535 if (status != INFIX_SUCCESS)
536 goto cleanup;
537 }
538
539 // JIT Compilation Pipeline for the Reverse Trampoline Stub
540 status = spec->prepare_reverse_call_frame(temp_arena, &layout, context);
541 if (status != INFIX_SUCCESS)
542 goto cleanup;
543 status = spec->generate_reverse_prologue(&buf, layout);
544 if (status != INFIX_SUCCESS)
545 goto cleanup;
546 status = spec->generate_reverse_argument_marshalling(&buf, layout, context);
547 if (status != INFIX_SUCCESS)
548 goto cleanup;
549 status = spec->generate_reverse_dispatcher_call(&buf, layout, context);
550 if (status != INFIX_SUCCESS)
551 goto cleanup;
552 status = spec->generate_reverse_epilogue(&buf, layout, context);
553 if (status != INFIX_SUCCESS)
554 goto cleanup;
555
556 if (buf.error || temp_arena->error) {
558 goto cleanup;
559 }
560
561 // Allocate executable memory and finalize the JIT code.
562 context->exec = infix_executable_alloc(buf.size);
563 if (context->exec.rw_ptr == nullptr) {
565 goto cleanup;
566 }
567 infix_memcpy(context->exec.rw_ptr, buf.code, buf.size);
568
569 if (!infix_executable_make_executable(context->exec)) {
571 goto cleanup;
572 }
573
574 // Harden the context struct to be read-only.
577 goto cleanup;
578 }
579
580 infix_dump_hex(context->exec.rx_ptr, buf.size, "Reverse Trampoline Machine Code");
581 *out_context = context;
582
583cleanup:
584 if (status != INFIX_SUCCESS && context != nullptr)
585 infix_reverse_destroy(context);
586 infix_arena_destroy(temp_arena);
587 return status;
588}
589
590/*
591 * Implementation for infix_reverse_create_callback_manual.
592 */
596 size_t num_args,
597 size_t num_fixed_args,
598 void * user_callback_fn) {
601 out_context, return_type, arg_types, num_args, num_fixed_args, user_callback_fn, nullptr, true);
602}
603
604/*
605 * Implementation for infix_reverse_create_closure_manual.
606 */
610 size_t num_args,
611 size_t num_fixed_args,
612 infix_closure_handler_fn user_callback_fn,
613 void * user_data) {
616 out_context, return_type, arg_types, num_args, num_fixed_args, (void *)user_callback_fn, user_data, false);
617}
618
619/*
620 * Implementation for infix_reverse_destroy.
621 */
622void infix_reverse_destroy(infix_reverse_t * reverse_trampoline) {
623 if (reverse_trampoline == nullptr)
624 return;
625 if (reverse_trampoline->cached_forward_trampoline)
627 if (reverse_trampoline->arena)
628 infix_arena_destroy(reverse_trampoline->arena);
629 infix_executable_free(reverse_trampoline->exec);
630 infix_protected_free(reverse_trampoline->protected_ctx);
631}
632
633/*
634 * Implementation for infix_reverse_get_code.
635 */
636c23_nodiscard void * infix_reverse_get_code(const infix_reverse_t * reverse_trampoline) {
637 if (reverse_trampoline == nullptr)
638 return nullptr;
639 return reverse_trampoline->exec.rx_ptr;
640}
641
642/*
643 * Implementation for infix_reverse_get_user_data.
644 */
646 if (reverse_trampoline == nullptr)
647 return nullptr;
648 return reverse_trampoline->user_data;
649}
650
651// High-Level Signature API Wrappers
652
657 const char * signature,
658 void * target_function,
659 infix_registry_t * registry) {
661 infix_arena_t * arena = nullptr;
662 infix_type * ret_type = nullptr;
663 infix_function_argument * args = nullptr;
664 size_t num_args = 0, num_fixed = 0;
665 infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
666 if (status != INFIX_SUCCESS)
667 return status;
669 (num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *)) : nullptr;
670 if (num_args > 0 && !arg_types) {
674 }
675 for (size_t i = 0; i < num_args; ++i)
676 arg_types[i] = args[i].type;
678 out_trampoline, ret_type, arg_types, num_args, num_fixed, arena, target_function);
680 return status;
681}
682
683
688 const char * signature,
689 infix_registry_t * registry) {
690 return infix_forward_create(out_trampoline, signature, nullptr, registry);
691}
692
697 const char * signature,
698 void * user_callback_fn,
699 infix_registry_t * registry) {
701 infix_arena_t * arena = nullptr;
702 infix_type * ret_type = nullptr;
703 infix_function_argument * args = nullptr;
704 size_t num_args = 0, num_fixed = 0;
705 infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
706 if (status != INFIX_SUCCESS)
707 return status;
709 (num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *)) : nullptr;
710 if (num_args > 0 && !arg_types) {
714 }
715 for (size_t i = 0; i < num_args; ++i)
716 arg_types[i] = args[i].type;
717 status =
718 infix_reverse_create_callback_manual(out_context, ret_type, arg_types, num_args, num_fixed, user_callback_fn);
720 return status;
721}
722
727 const char * signature,
728 infix_closure_handler_fn user_callback_fn,
729 void * user_data,
730 infix_registry_t * registry) {
732 infix_arena_t * arena = nullptr;
733 infix_type * ret_type = nullptr;
734 infix_function_argument * args = nullptr;
735 size_t num_args = 0, num_fixed = 0;
736 infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
737 if (status != INFIX_SUCCESS)
738 return status;
740 (num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *)) : nullptr;
741 if (num_args > 0 && !arg_types) {
745 }
746 for (size_t i = 0; i < num_args; ++i)
747 arg_types[i] = args[i].type;
749 out_context, ret_type, arg_types, num_args, num_fixed, user_callback_fn, user_data);
751 return status;
752}
753
754// Unity Build Section
755/*
756 * This section implements a unity build for the ABI-specific components.
757 * Instead of relying on the build system to compile and link the correct `abi_*.c`
758 * files, we include them directly into this compilation unit based on the ABI
759 * selected in `infix_config.h`. This simplifies the build process.
760 */
761#if defined(INFIX_ABI_WINDOWS_X64)
762#include "../arch/x64/abi_win_x64.c"
763#include "../arch/x64/abi_x64_emitters.c"
764#elif defined(INFIX_ABI_SYSV_X64)
765#include "../arch/x64/abi_sysv_x64.c"
766#include "../arch/x64/abi_x64_emitters.c"
767#elif defined(INFIX_ABI_AAPCS64)
768#include "../arch/aarch64/abi_arm64.c"
769#include "../arch/aarch64/abi_arm64_emitters.c"
770#else
771#error "No supported ABI was selected for the unity build in trampoline.c."
772#endif
infix_arena_t * arena
Definition 005_layouts.c:57
infix_status status
Definition 103_unions.c:74
void * args[]
Definition 202_in_structs.c:75
infix_type * return_type
Definition 202_in_structs.c:71
infix_type * arg_types[]
Definition 901_call_overhead.c:76
infix_type * ret_type
Definition 901_call_overhead.c:75
const infix_reverse_abi_spec g_arm64_reverse_spec
Definition abi_arm64.c:114
const infix_forward_abi_spec g_arm64_forward_spec
Definition abi_arm64.c:107
const infix_reverse_abi_spec g_sysv_x64_reverse_spec
Definition abi_sysv_x64.c:117
const infix_forward_abi_spec g_sysv_x64_forward_spec
Definition abi_sysv_x64.c:110
const infix_reverse_abi_spec g_win_x64_reverse_spec
The v-table of Windows x64 functions for generating reverse trampolines.
Definition abi_win_x64.c:107
const infix_forward_abi_spec g_win_x64_forward_spec
The v-table of Windows x64 functions for generating forward trampolines.
Definition abi_win_x64.c:99
#define c23_nodiscard
Definition compat_c23.h:93
@ INFIX_CODE_UNRESOLVED_NAMED_TYPE
The type graph that contains an unresolved named reference.
Definition infix.h:1078
@ INFIX_CODE_OUT_OF_MEMORY
Failure to allocate memory. Likely due to lack of system resources.
Definition infix.h:1061
@ INFIX_CATEGORY_ABI
An error related to ABI classification or JIT generation.
Definition infix.h:1044
@ INFIX_CATEGORY_ALLOCATION
An error related to memory allocation.
Definition infix.h:1042
c23_nodiscard infix_status infix_reverse_create_callback(infix_reverse_t **out_context, const char *signature, void *user_callback_fn, infix_registry_t *registry)
Implementation of the public infix_reverse_create_callback API function.
Definition trampoline.c:696
c23_nodiscard infix_status infix_forward_create_unbound(infix_forward_t **out_trampoline, const char *signature, infix_registry_t *registry)
Implementation of the public infix_forward_create_unbound API function.
Definition trampoline.c:687
c23_nodiscard infix_status infix_forward_create(infix_forward_t **out_trampoline, const char *signature, void *target_function, infix_registry_t *registry)
Implementation of the public infix_forward_create API function.
Definition trampoline.c:656
c23_nodiscard infix_status infix_reverse_create_closure(infix_reverse_t **out_context, const char *signature, infix_closure_handler_fn user_callback_fn, void *user_data, infix_registry_t *registry)
Implementation of the public infix_reverse_create_closure API function.
Definition trampoline.c:726
c23_nodiscard infix_status infix_signature_parse(const char *, infix_arena_t **, infix_type **, infix_function_argument **, size_t *, size_t *, infix_registry_t *)
Parses a full function signature string into its constituent infix_type parts.
Definition signature.c:1123
c23_nodiscard infix_cif_func infix_forward_get_code(infix_forward_t *trampoline)
Retrieves the executable code pointer from a bound forward trampoline.
Definition trampoline.c:227
c23_nodiscard void * infix_reverse_get_code(const infix_reverse_t *reverse_trampoline)
Retrieves the executable code pointer from a reverse trampoline.
Definition trampoline.c:636
c23_nodiscard infix_unbound_cif_func infix_forward_get_unbound_code(infix_forward_t *trampoline)
Retrieves the executable code pointer from an unbound forward trampoline.
Definition trampoline.c:217
c23_nodiscard void * infix_reverse_get_user_data(const infix_reverse_t *reverse_trampoline)
Retrieves the user_data stored with a reverse trampoline.
Definition trampoline.c:645
void infix_reverse_destroy(infix_reverse_t *reverse_trampoline)
Frees a reverse trampoline, its JIT-compiled stub, and its context.
Definition trampoline.c:622
c23_nodiscard infix_status infix_forward_create_manual(infix_forward_t **out_trampoline, infix_type *return_type, infix_type **arg_types, size_t num_args, size_t num_fixed_args, void *target_function)
Generates a bound forward-call trampoline for a given function signature.
Definition trampoline.c:370
c23_nodiscard infix_status infix_reverse_create_callback_manual(infix_reverse_t **out_context, infix_type *return_type, infix_type **arg_types, size_t num_args, size_t num_fixed_args, void *user_callback_fn)
Generates a type-safe reverse-call trampoline (callback) from infix_type objects.
Definition trampoline.c:593
c23_nodiscard infix_status infix_reverse_create_closure_manual(infix_reverse_t **out_context, infix_type *return_type, infix_type **arg_types, size_t num_args, size_t num_fixed_args, infix_closure_handler_fn user_callback_fn, void *user_data)
Generates a generic reverse-call trampoline (closure) from infix_type objects.
Definition trampoline.c:607
c23_nodiscard infix_status infix_forward_create_unbound_manual(infix_forward_t **out_trampoline, infix_type *return_type, infix_type **arg_types, size_t num_args, size_t num_fixed_args)
Creates an "unbound" forward-call trampoline for a given function signature.
Definition trampoline.c:392
void infix_forward_destroy(infix_forward_t *trampoline)
Frees a forward trampoline and its associated executable memory.
Definition trampoline.c:406
#define infix_free
A macro for the memory deallocation function.
Definition infix.h:280
void infix_arena_destroy(infix_arena_t *)
Frees an entire memory arena and all objects allocated within it.
Definition arena.c:68
#define infix_memcpy
A macro for copying memory from a source to a destination pointer.
Definition infix.h:290
#define infix_calloc
A macro for the zero-initializing memory allocation function.
Definition infix.h:260
c23_nodiscard void * infix_arena_alloc(infix_arena_t *, size_t, size_t)
Allocates a block of memory from the arena with a specific alignment.
Definition arena.c:86
#define infix_memset
A macro for setting a block of memory to a specific value.
Definition infix.h:300
c23_nodiscard infix_arena_t * infix_arena_create(size_t)
Creates and initializes a new memory arena.
Definition arena.c:41
void(* infix_closure_handler_fn)(infix_context_t *context, void *return_value, void **args)
The signature for a generic "closure" handler.
Definition infix.h:346
void(* infix_cif_func)(void *, void **)
The signature for a "bound" forward-call trampoline.
Definition infix.h:335
void(* infix_unbound_cif_func)(void *, void *, void **)
The signature for a generic "unbound" forward-call trampoline.
Definition infix.h:315
infix_status
An enumeration of all possible success or failure codes from the public API.
Definition infix.h:351
@ INFIX_ERROR_ALLOCATION_FAILED
A memory allocation request failed.
Definition infix.h:353
@ INFIX_ERROR_PROTECTION_FAILED
Failed to change memory permissions (e.g., mprotect or VirtualProtect).
Definition infix.h:357
@ INFIX_ERROR_UNSUPPORTED_ABI
The current platform/ABI is not supported.
Definition infix.h:355
@ INFIX_SUCCESS
The operation completed successfully.
Definition infix.h:352
@ INFIX_ERROR_INVALID_ARGUMENT
An invalid argument was provided to a function.
Definition infix.h:354
@ INFIX_TYPE_UNION
A user-defined union (union).
Definition infix.h:118
@ INFIX_TYPE_ARRAY
A fixed-size array.
Definition infix.h:119
@ INFIX_TYPE_POINTER
A generic void* pointer type.
Definition infix.h:116
@ INFIX_TYPE_NAMED_REFERENCE
A reference to a named type (e.g., struct<Node>).
Definition infix.h:124
@ INFIX_TYPE_REVERSE_TRAMPOLINE
A callback wrapper.
Definition infix.h:120
@ INFIX_TYPE_STRUCT
A user-defined structure (struct).
Definition infix.h:117
#define INFIX_TRAMPOLINE_HEADROOM
Definition infix_config.h:200
Declarations for internal-only functions, types, and constants.
void infix_executable_free(infix_executable_t)
Frees executable memory, creating a guard page to prevent use-after-free.
Definition executor.c:388
c23_nodiscard bool infix_executable_make_executable(infix_executable_t)
Makes a JIT memory region readable and executable (and non-writable).
Definition executor.c:453
void _infix_set_error(infix_error_category_t, infix_error_code_t, size_t)
Sets the thread-local error details for a library-internal error.
Definition error.c:44
c23_nodiscard infix_executable_t infix_executable_alloc(size_t)
Allocates a page-aligned block of W^X-compliant executable memory.
Definition executor.c:286
infix_type * _copy_type_graph_to_arena(infix_arena_t *, const infix_type *)
Performs a deep copy of a type graph from one arena to another.
Definition types.c:610
c23_nodiscard infix_protected_t infix_protected_alloc(size_t)
Allocates a page-aligned block of data memory.
Definition executor.c:507
void _infix_clear_error(void)
Resets the thread-local error state. Called at the start of every public API function.
Definition error.c:76
void infix_protected_free(infix_protected_t)
Frees a block of protected data memory.
Definition executor.c:546
void infix_internal_dispatch_callback_fn_impl(infix_reverse_t *, void *, void **)
The high-level C dispatcher function called by reverse trampoline stubs.
Definition executor.c:610
c23_nodiscard bool infix_protected_make_readonly(infix_protected_t)
Hardens a block of protected data memory to be read-only.
Definition executor.c:577
Definition infix_internals.h:169
uint8_t * code
The buffer holding the machine code.
Definition infix_internals.h:170
bool error
A flag that is set if a memory allocation fails.
Definition infix_internals.h:173
size_t size
The current number of bytes written to the buffer.
Definition infix_internals.h:172
size_t capacity
The allocated capacity of the buffer.
Definition infix_internals.h:171
infix_arena_t * arena
The arena to use for allocations.
Definition infix_internals.h:174
Definition infix_internals.h:130
bool error
A sticky flag that is set if any allocation from this arena fails.
Definition infix_internals.h:134
size_t current_offset
The high-water mark; the offset of the next free byte.
Definition infix_internals.h:133
Definition infix_internals.h:234
size_t size
The total size of the allocated memory region in bytes.
Definition infix_internals.h:50
void * rw_ptr
Pointer with Read+Write permissions (where code is written).
Definition infix_internals.h:49
void * rx_ptr
Pointer with Read+Execute permissions (the callable address).
Definition infix_internals.h:48
Definition infix_internals.h:272
infix_status(* prepare_forward_call_frame)(infix_arena_t *, infix_call_frame_layout **, infix_type *, infix_type **, size_t, size_t, void *)
Analyzes a signature and produces a complete call frame layout.
Definition infix_internals.h:274
infix_status(* generate_forward_call_instruction)(code_buffer *, infix_call_frame_layout *)
Generates the call instruction (e.g., call rax).
Definition infix_internals.h:283
infix_status(* generate_forward_epilogue)(code_buffer *, infix_call_frame_layout *, infix_type *)
Generates the function epilogue, handling the return value and restoring the stack.
Definition infix_internals.h:285
infix_status(* generate_forward_prologue)(code_buffer *, infix_call_frame_layout *)
Generates the function prologue, including stack setup and saving registers.
Definition infix_internals.h:277
infix_status(* generate_forward_argument_moves)(code_buffer *, infix_call_frame_layout *, infix_type **, size_t, size_t)
Generates the instructions to move arguments from the generic void** array to their native locations.
Definition infix_internals.h:280
Definition infix_internals.h:72
infix_executable_t exec
Handle to the executable JIT-compiled stub.
Definition infix_internals.h:74
void * target_fn
If non-NULL, the hardcoded target function for a "bound" trampoline.
Definition infix_internals.h:79
size_t num_args
The total number of arguments.
Definition infix_internals.h:77
size_t num_fixed_args
The number of non-variadic arguments.
Definition infix_internals.h:78
infix_type ** arg_types
An array of infix_type pointers for each argument.
Definition infix_internals.h:76
infix_type * return_type
The infix_type of the trampoline's return value.
Definition infix_internals.h:75
infix_arena_t * arena
The arena that owns all the type metadata for this trampoline.
Definition infix_internals.h:73
Describes a single argument to a function, pairing an optional name with its type.
Definition infix.h:229
infix_type * type
An infix_type describing the argument's type.
Definition infix.h:231
Definition infix_internals.h:60
void * rw_ptr
A pointer to the read-write data memory.
Definition infix_internals.h:61
Definition infix_internals.h:158
Definition infix_internals.h:292
infix_status(* generate_reverse_argument_marshalling)(code_buffer *, infix_reverse_call_frame_layout *, infix_reverse_t *)
Generates instructions to save incoming arguments from registers/stack into a normalized format.
Definition infix_internals.h:298
infix_status(* prepare_reverse_call_frame)(infix_arena_t *, infix_reverse_call_frame_layout **, infix_reverse_t *)
Analyzes a callback signature and produces a stack layout for the JIT stub.
Definition infix_internals.h:294
infix_status(* generate_reverse_dispatcher_call)(code_buffer *, infix_reverse_call_frame_layout *, infix_reverse_t *)
Generates the call to the high-level C dispatcher function.
Definition infix_internals.h:302
infix_status(* generate_reverse_prologue)(code_buffer *, infix_reverse_call_frame_layout *)
Generates the stub's function prologue.
Definition infix_internals.h:296
infix_status(* generate_reverse_epilogue)(code_buffer *, infix_reverse_call_frame_layout *, infix_reverse_t *)
Generates the stub's epilogue, handling the return value and restoring the stack.
Definition infix_internals.h:306
Definition infix_internals.h:256
Definition infix_internals.h:102
infix_type * return_type
The infix_type of the callback's return value.
Definition infix_internals.h:106
void * user_data
Arbitrary user-data pointer associated with this callback.
Definition infix_internals.h:112
void * user_callback_fn
Pointer to the user's actual C callback handler function.
Definition infix_internals.h:111
infix_executable_t exec
Handle to the executable JIT-compiled entry stub.
Definition infix_internals.h:104
infix_internal_dispatch_callback_fn internal_dispatcher
Pointer to the C function that bridges from assembly.
Definition infix_internals.h:113
size_t num_args
The total number of arguments.
Definition infix_internals.h:108
size_t num_fixed_args
The number of non-variadic arguments.
Definition infix_internals.h:109
infix_protected_t protected_ctx
Handle to the memory where this very context struct resides.
Definition infix_internals.h:105
bool is_variadic
True if the function signature is variadic.
Definition infix_internals.h:110
infix_forward_t * cached_forward_trampoline
A pre-compiled forward trampoline for calling a type-safe user C callback.
Definition infix_internals.h:123
infix_arena_t * arena
The arena that owns all type metadata for this callback.
Definition infix_internals.h:103
infix_type ** arg_types
An array of infix_type pointers for each argument.
Definition infix_internals.h:107
infix_type * type
An infix_type describing the member's type.
Definition infix.h:221
The central structure for describing any data type in the FFI system.
Definition infix.h:161
struct infix_type_t::@0::@1 pointer_info
For INFIX_TYPE_POINTER.
union infix_type_t::@0 meta
Type-specific metadata.
struct infix_type_t::@0::@4 func_ptr_info
For INFIX_TYPE_REVERSE_TRAMPOLINE.
infix_struct_member * members
Array of members for the aggregate.
Definition infix.h:177
infix_function_argument * args
Array of function arguments (name and type).
Definition infix.h:188
infix_type_category category
The fundamental category of the type.
Definition infix.h:162
struct infix_type_t::@0::@2 aggregate_info
For INFIX_TYPE_STRUCT and INFIX_TYPE_UNION.
struct infix_type_t::@0::@3 array_info
For INFIX_TYPE_ARRAY.
struct infix_type_t * pointee_type
The type this pointer points to.
Definition infix.h:172
struct infix_type_t * element_type
The type of elements in the array.
Definition infix.h:182
struct infix_type_t * return_type
Reverse trampoline return value.
Definition infix.h:187
size_t num_members
Number of members in the aggregate.
Definition infix.h:178
size_t num_args
The total number of fixed and variadic arguments.
Definition infix.h:189
void emit_byte(code_buffer *buf, uint8_t byte)
Appends a single byte to a code buffer.
Definition trampoline.c:161
c23_nodiscard infix_status _infix_forward_create_internal(infix_forward_t **out_trampoline, infix_type *return_type, infix_type **arg_types, size_t num_args, size_t num_fixed_args, infix_arena_t *source_arena, void *target_fn)
The internal core logic for creating a forward trampoline.
Definition trampoline.c:251
const infix_forward_abi_spec * get_current_forward_abi_spec()
Gets the ABI v-table for the current target platform (forward calls).
Definition trampoline.c:80
void code_buffer_init(code_buffer *buf, infix_arena_t *arena)
Initializes a code buffer for JIT code generation.
Definition trampoline.c:114
static bool _is_type_graph_resolved(infix_type *type)
Definition trampoline.c:184
void emit_int64(code_buffer *buf, int64_t value)
Appends a 64-bit integer to a code buffer.
Definition trampoline.c:171
static infix_status _infix_reverse_create_internal(infix_reverse_t **, infix_type *, infix_type **, size_t, size_t, void *, void *, bool)
Definition trampoline.c:443
void code_buffer_append(code_buffer *buf, const void *data, size_t len)
Appends raw bytes to a code buffer, reallocating if necessary.
Definition trampoline.c:128
static size_t get_page_size()
Definition trampoline.c:420
void emit_int32(code_buffer *buf, int32_t value)
Appends a 32-bit integer to a code buffer.
Definition trampoline.c:166
const infix_reverse_abi_spec * get_current_reverse_abi_spec()
Gets the ABI v-table for the current target platform (reverse calls).
Definition trampoline.c:97
A header for conditionally compiled debugging utilities.
static void infix_dump_hex(c23_maybe_unused const void *data, c23_maybe_unused size_t size, c23_maybe_unused const char *title)
Definition utility.h:115