infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
trampoline.c
Go to the documentation of this file.
1
41#include "common/utility.h"
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45
46#if defined(INFIX_OS_MACOS)
47#include <pthread.h>
48#endif
49
50#if defined(INFIX_OS_WINDOWS)
51#include <windows.h>
52#else
53#include <sys/mman.h>
54#include <unistd.h>
55#endif
56
57// Forward Declaration for Internal Creation Function
61 size_t num_args,
62 size_t num_fixed_args,
63 void * user_callback_fn,
64 void * user_data,
65 bool is_callback);
66
67// ABI Specification V-Table Declarations (extern to link to the specific implementations)
68#if defined(INFIX_ABI_WINDOWS_X64)
71#elif defined(INFIX_ABI_SYSV_X64)
74#elif defined(INFIX_ABI_AAPCS64)
77#endif
78
89#if defined(INFIX_ABI_WINDOWS_X64)
91#elif defined(INFIX_ABI_SYSV_X64)
93#elif defined(INFIX_ABI_AAPCS64)
95#else
96 return nullptr;
97#endif
98}
99
107#if defined(INFIX_ABI_WINDOWS_X64)
109#elif defined(INFIX_ABI_SYSV_X64)
111#elif defined(INFIX_ABI_AAPCS64)
112 return &g_arm64_reverse_spec;
113#else
114 return nullptr;
115#endif
116}
117
118// Code Buffer Implementation
119
127 buf->capacity = 64; // Start with a small initial capacity.
128 buf->arena = arena;
129 buf->code = infix_arena_alloc(arena, buf->capacity, 16);
130 buf->size = 0;
131 buf->error = (buf->code == nullptr);
132}
133
146void code_buffer_append(code_buffer * buf, const void * data, size_t len) {
147 if (buf->error)
148 return;
149
150 if (len > SIZE_MAX - buf->size) { // Overflow check
151 buf->error = true;
152 return;
153 }
154
155 if (buf->size + len > buf->capacity) {
156 size_t new_capacity = buf->capacity;
157 while (new_capacity < buf->size + len) {
158 if (new_capacity > SIZE_MAX / 2) { // Overflow check
159 buf->error = true;
160 return;
161 }
162 new_capacity *= 2;
163 }
164
165 void * new_code = infix_arena_alloc(buf->arena, new_capacity, 16);
166 if (new_code == nullptr) {
167 buf->error = true;
168 return;
169 }
170 infix_memcpy(new_code, buf->code, buf->size);
171 buf->code = new_code;
172 buf->capacity = new_capacity;
173 }
174 infix_memcpy(buf->code + buf->size, data, len);
175 buf->size += len;
176}
177
179void emit_byte(code_buffer * buf, uint8_t byte) { code_buffer_append(buf, &byte, 1); }
180
182void emit_int32(code_buffer * buf, int32_t value) { code_buffer_append(buf, &value, 4); }
183
185void emit_int64(code_buffer * buf, int64_t value) { code_buffer_append(buf, &value, 8); }
186
187// Type Graph Validation
188
194
208 if (!type)
209 return true;
210
211 // Cycle detection: if we've seen this node before, we can assume it's resolved
212 // for the purpose of this check, as we'll validate it on the first visit.
213 for (visited_node_t * v = visited_head; v != NULL; v = v->next)
214 if (v->type == type)
215 return true;
216
217 visited_node_t current_visited_node = {.type = type, .next = visited_head};
218
219 switch (type->category) {
221 return false; // Base case: an unresolved reference.
224 case INFIX_TYPE_ARRAY:
225 return _is_type_graph_resolved_recursive(type->meta.array_info.element_type, &current_visited_node);
227 case INFIX_TYPE_UNION:
228 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i)
229 if (!_is_type_graph_resolved_recursive(type->meta.aggregate_info.members[i].type, &current_visited_node))
230 return false;
231 return true;
234 return false;
235 for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i)
236 if (!_is_type_graph_resolved_recursive(type->meta.func_ptr_info.args[i].type, &current_visited_node))
237 return false;
238 return true;
239 default:
240 return true; // Primitives, void, etc., are always resolved.
241 }
242}
243
249
264static size_t _estimate_metadata_size(infix_arena_t * temp_arena,
267 size_t num_args) {
268 size_t total_size = 0;
269 total_size += _infix_estimate_graph_size(temp_arena, return_type);
270
271 if (arg_types != nullptr) {
272 // Add space for the arg_types pointer array itself.
273 total_size += sizeof(infix_type *) * num_args;
274 for (size_t i = 0; i < num_args; ++i)
275 total_size += _infix_estimate_graph_size(temp_arena, arg_types[i]);
276 }
277 return total_size;
278}
279
280// Forward Trampoline API Implementation
281
283 if (trampoline == nullptr || trampoline->target_fn != nullptr)
284 return nullptr;
285 return (infix_unbound_cif_func)trampoline->exec.rx_ptr;
286}
287
289 if (trampoline == nullptr || trampoline->target_fn == nullptr)
290 return nullptr;
291 return (infix_cif_func)trampoline->exec.rx_ptr;
292}
293
318 infix_arena_t * target_arena,
321 size_t num_args,
322 size_t num_fixed_args,
323 void * target_fn) {
324 if (out_trampoline == nullptr || return_type == nullptr || (arg_types == nullptr && num_args > 0)) {
327 }
328
329 // Pre-flight check: ensure all types are resolved before passing to ABI layer.
333 }
334 for (size_t i = 0; i < num_args; ++i) {
335 if (arg_types[i] == nullptr || !_is_type_graph_resolved(arg_types[i])) {
338 }
339 }
340
342 if (spec == nullptr) {
345 }
346
348 infix_call_frame_layout * layout = nullptr;
349 infix_forward_t * handle = nullptr;
350
351 // Use a temporary arena for all intermediate allocations during code generation.
352 infix_arena_t * temp_arena = infix_arena_create(65536);
353 if (!temp_arena) {
356 }
357
358 code_buffer buf;
359 code_buffer_init(&buf, temp_arena);
360
361 // JIT Compilation Pipeline
362 // 1. Prepare: Classify arguments and create the layout blueprint.
364 temp_arena, &layout, return_type, arg_types, num_args, num_fixed_args, target_fn);
365 if (status != INFIX_SUCCESS)
366 goto cleanup;
367
368 // 2. Generate: Emit machine code based on the layout.
369 status = spec->generate_forward_prologue(&buf, layout);
370 if (status != INFIX_SUCCESS)
371 goto cleanup;
372
373 status = spec->generate_forward_argument_moves(&buf, layout, arg_types, num_args, num_fixed_args);
374 if (status != INFIX_SUCCESS)
375 goto cleanup;
376
377 status = spec->generate_forward_call_instruction(&buf, layout);
378 if (status != INFIX_SUCCESS)
379 goto cleanup;
380
381 status = spec->generate_forward_epilogue(&buf, layout, return_type);
382 if (status != INFIX_SUCCESS)
383 goto cleanup;
384
385 if (buf.error || temp_arena->error) {
387 goto cleanup;
388 }
389
390 // Finalize Handle
391 handle = infix_calloc(1, sizeof(infix_forward_t));
392 if (handle == nullptr) {
394 goto cleanup;
395 }
396
397 // "Estimate" stage: Calculate the exact size needed for the handle's private arena.
398 size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
399
400 if (target_arena) {
401 handle->arena = target_arena;
402 handle->is_external_arena = true;
403 }
404 else {
405 handle->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
406 handle->is_external_arena = false;
407 }
408
409 if (handle->arena == nullptr) {
411 goto cleanup;
412 }
413
414 // "Copy" stage: Deep copy all type info into the handle's private arena.
416 if (num_args > 0) {
417 handle->arg_types = infix_arena_alloc(handle->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
418 if (handle->arg_types == nullptr) {
420 goto cleanup;
421 }
422 for (size_t i = 0; i < num_args; ++i) {
423 handle->arg_types[i] = _copy_type_graph_to_arena(handle->arena, arg_types[i]);
424 // Check for allocation failure during copy
425 if (arg_types[i] != nullptr && handle->arg_types[i] == nullptr && !handle->arena->error) {
427 goto cleanup;
428 }
429 }
430 }
431
432 handle->num_args = num_args;
433 handle->num_fixed_args = num_fixed_args;
434 handle->target_fn = target_fn;
435
436 // Allocate and finalize executable memory.
437 handle->exec = infix_executable_alloc(buf.size);
438 if (handle->exec.rw_ptr == nullptr) {
440 goto cleanup;
441 }
442 infix_memcpy(handle->exec.rw_ptr, buf.code, buf.size);
443
446 goto cleanup;
447 }
448
449 infix_dump_hex(handle->exec.rx_ptr, handle->exec.size, "Forward Trampoline Machine Code");
450 *out_trampoline = handle;
451
452cleanup:
453 // If any step failed, ensure the partially created handle is fully destroyed.
454 if (status != INFIX_SUCCESS && handle != nullptr)
455 infix_forward_destroy(handle);
456 // The temporary arena is always destroyed.
457 infix_arena_destroy(temp_arena);
458 return status;
459}
460
482 size_t num_args,
483 size_t num_fixed_args,
484 void * target_function) {
485 // This is part of the "Manual API". It calls the internal implementation directly
486 // without involving the signature parser. `source_arena` is null because the
487 // types are assumed to be managed by the user.
490 out_trampoline, nullptr, return_type, arg_types, num_args, num_fixed_args, target_function);
491}
492
509 size_t num_args,
510 size_t num_fixed_args) {
513 out_trampoline, nullptr, return_type, arg_types, num_args, num_fixed_args, nullptr);
514}
515
524 if (trampoline == nullptr)
525 return;
526 // Destroying the private arena frees all deep-copied type metadata.
527 if (trampoline->arena && !trampoline->is_external_arena)
528 infix_arena_destroy(trampoline->arena);
529 // Free the JIT-compiled executable code.
530 infix_executable_free(trampoline->exec);
531 // Free the handle struct itself.
532 infix_free(trampoline);
533}
534
535// Reverse Trampoline API Implementation
536
542static size_t get_page_size() {
543#if defined(INFIX_OS_WINDOWS)
544 SYSTEM_INFO sysInfo;
545 GetSystemInfo(&sysInfo);
546 return sysInfo.dwPageSize;
547#else
548 // sysconf is the standard POSIX way to get system configuration values.
549 return sysconf(_SC_PAGESIZE);
550#endif
551}
552
579 size_t num_args,
580 size_t num_fixed_args,
581 void * user_callback_fn,
582 void * user_data,
583 bool is_callback) {
584 if (out_context == nullptr || return_type == nullptr || num_fixed_args > num_args) {
587 }
588
589 // Pre-flight check: ensure all types are fully resolved.
593 }
594 if (arg_types == nullptr && num_args > 0) {
597 }
598 for (size_t i = 0; i < num_args; ++i) {
599 if (arg_types[i] == nullptr || !_is_type_graph_resolved(arg_types[i])) {
602 }
603 }
604
606 if (spec == nullptr) {
609 }
610
612 infix_reverse_call_frame_layout * layout = nullptr;
613 infix_reverse_t * context = nullptr;
614 infix_arena_t * temp_arena = nullptr;
615 infix_protected_t prot = {.rw_ptr = nullptr, .size = 0};
616 code_buffer buf;
617
618 temp_arena = infix_arena_create(65536);
619 if (!temp_arena) {
622 }
623
624 code_buffer_init(&buf, temp_arena);
625
626 // Security Hardening: Allocate the context struct itself in special, page-aligned
627 // memory that can be made read-only after initialization.
628 size_t page_size = get_page_size();
629 size_t context_alloc_size = (sizeof(infix_reverse_t) + page_size - 1) & ~(page_size - 1);
630 prot = infix_protected_alloc(context_alloc_size);
631 if (prot.rw_ptr == nullptr) {
633 goto cleanup;
634 }
635 context = (infix_reverse_t *)prot.rw_ptr;
636 infix_memset(context, 0, context_alloc_size);
637
638 // "Estimate" stage: Calculate the exact size needed for the context's private arena.
639 size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
640
641 // Create the context's private arena with the calculated size plus some headroom for safety.
642 context->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
643
644 if (context->arena == nullptr) {
646 goto cleanup;
647 }
648
649 // Populate the context fields.
650 context->protected_ctx = prot;
651 context->num_args = num_args;
652 context->num_fixed_args = num_fixed_args;
653 context->is_variadic = (num_fixed_args < num_args);
654 context->user_callback_fn = user_callback_fn;
655 context->user_data = user_data;
657 context->cached_forward_trampoline = nullptr;
658
659 // "Copy" stage: deep copy all types into the context's private arena.
661 if (num_args > 0) {
662 context->arg_types = infix_arena_alloc(context->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
663 if (context->arg_types == nullptr) {
665 goto cleanup;
666 }
667 for (size_t i = 0; i < num_args; ++i) {
668 context->arg_types[i] = _copy_type_graph_to_arena(context->arena, arg_types[i]);
669 if (arg_types[i] != nullptr && context->arg_types[i] == nullptr) {
671 goto cleanup;
672 }
673 }
674 }
675
676 // Special step for type-safe callbacks: generate and cache a forward trampoline
677 // that will be used to call the user's type-safe C handler.
678 if (is_callback) {
680 context->return_type,
681 context->arg_types,
682 context->num_args,
683 context->num_fixed_args,
684 user_callback_fn);
685 if (status != INFIX_SUCCESS)
686 goto cleanup;
687 }
688
689 // JIT Compilation Pipeline for Reverse Stub
690 status = spec->prepare_reverse_call_frame(temp_arena, &layout, context);
691 if (status != INFIX_SUCCESS)
692 goto cleanup;
693 status = spec->generate_reverse_prologue(&buf, layout);
694 if (status != INFIX_SUCCESS)
695 goto cleanup;
696 status = spec->generate_reverse_argument_marshalling(&buf, layout, context);
697 if (status != INFIX_SUCCESS)
698 goto cleanup;
699 status = spec->generate_reverse_dispatcher_call(&buf, layout, context);
700 if (status != INFIX_SUCCESS)
701 goto cleanup;
702 status = spec->generate_reverse_epilogue(&buf, layout, context);
703 if (status != INFIX_SUCCESS)
704 goto cleanup;
705 // End of Pipeline
706
707 if (buf.error || temp_arena->error) {
709 goto cleanup;
710 }
711
712 context->exec = infix_executable_alloc(buf.size);
713 if (context->exec.rw_ptr == nullptr) {
715 goto cleanup;
716 }
717 infix_memcpy(context->exec.rw_ptr, buf.code, buf.size);
718
719 if (!infix_executable_make_executable(context->exec)) {
721 goto cleanup;
722 }
723
724 // Security Hardening: Make the context memory read-only to prevent runtime corruption.
727 goto cleanup;
728 }
729
730 infix_dump_hex(context->exec.rx_ptr, buf.size, "Reverse Trampoline Machine Code");
731 *out_context = context;
732
733cleanup:
734 if (status != INFIX_SUCCESS) {
735 // If allocation of the context itself failed, prot.rw_ptr will be null.
736 if (prot.rw_ptr != nullptr)
737 infix_reverse_destroy(context);
738 }
739 infix_arena_destroy(temp_arena);
740 return status;
741}
742
756 size_t num_args,
757 size_t num_fixed_args,
758 void * user_callback_fn) {
761 out_context, return_type, arg_types, num_args, num_fixed_args, user_callback_fn, nullptr, true);
762}
763
778 size_t num_args,
779 size_t num_fixed_args,
780 infix_closure_handler_fn user_callback_fn,
781 void * user_data) {
784 out_context, return_type, arg_types, num_args, num_fixed_args, (void *)user_callback_fn, user_data, false);
785}
786
794void infix_reverse_destroy(infix_reverse_t * reverse_trampoline) {
795 if (reverse_trampoline == nullptr)
796 return;
797 // The cached trampoline (if it exists) must also be destroyed.
798 if (reverse_trampoline->cached_forward_trampoline)
800 if (reverse_trampoline->arena)
801 infix_arena_destroy(reverse_trampoline->arena);
802 infix_executable_free(reverse_trampoline->exec);
803 // Free the special read-only memory region for the context struct.
804 infix_protected_free(reverse_trampoline->protected_ctx);
805}
806
813c23_nodiscard void * infix_reverse_get_code(const infix_reverse_t * reverse_trampoline) {
814 if (reverse_trampoline == nullptr)
815 return nullptr;
816 return reverse_trampoline->exec.rx_ptr;
817}
818
825 if (reverse_trampoline == nullptr)
826 return nullptr;
827 return reverse_trampoline->user_data;
828}
829
830// High-Level Signature API Wrappers
831
833 infix_arena_t * target_arena,
834 const char * signature,
835 void * target_function,
838 if (!signature) {
841 }
842
843 infix_arena_t * arena = nullptr;
844 infix_type * ret_type = nullptr;
845 infix_function_argument * args = nullptr;
846 size_t num_args = 0, num_fixed = 0;
847 infix_type ** arg_types = nullptr;
849 if (signature[0] == '@') {
850 if (registry == nullptr) {
851 _infix_set_error(INFIX_CATEGORY_GENERAL, INFIX_CODE_UNKNOWN, 0); // Using @Name requires a registry
853 }
854
855 const infix_type * func_type = infix_registry_lookup_type(registry, &signature[1]);
856 if (func_type == NULL) {
859 }
860
861 if (func_type->category != INFIX_TYPE_REVERSE_TRAMPOLINE) {
862 // The user provided a name for a non-function type (e.g., "@Point")
865 }
866
867 // We have a valid function type from the registry. Now, unpack its components.
869 num_args = func_type->meta.func_ptr_info.num_args;
870 num_fixed = func_type->meta.func_ptr_info.num_fixed_args;
871 args = func_type->meta.func_ptr_info.args;
872
873 // The Manual API needs a temporary arena to hold the arg_types array.
874 infix_arena_t * temp_arena = infix_arena_create(sizeof(infix_type *) * num_args + 128);
875 if (!temp_arena) {
878 }
879
880 if (num_args > 0) {
881 arg_types = infix_arena_alloc(temp_arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
882 if (!arg_types) {
883 infix_arena_destroy(temp_arena);
886 }
887 for (size_t i = 0; i < num_args; ++i)
888 arg_types[i] = args[i].type;
889 }
890 arena = temp_arena;
891 }
892 else {
893 // This is a high-level wrapper. It uses the parser to build the type info first.
894 status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
895 if (status != INFIX_SUCCESS) {
897 return status;
898 }
899 // Extract the `infix_type*` array from the parsed `infix_function_argument` array.
900 arg_types = (num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *))
901 : nullptr;
902 if (num_args > 0 && !arg_types) {
906 }
907 for (size_t i = 0; i < num_args; ++i)
908 arg_types[i] = args[i].type;
909 }
910 // Call the core internal implementation with the parsed types.
911
913 out_trampoline, target_arena, ret_type, arg_types, num_args, num_fixed, target_function);
914
916 return status;
917}
918
920 const char * signature,
921 void * target_function,
923 return infix_forward_create_in_arena(out_trampoline, NULL, signature, target_function, registry);
924}
925
927 const char * signature,
929 return infix_forward_create_in_arena(out_trampoline, NULL, signature, NULL, registry);
930}
931
933 const char * signature,
934 void * user_callback_fn,
936 infix_arena_t * arena = nullptr;
937 infix_type * ret_type = nullptr;
938 infix_function_argument * args = nullptr;
939 size_t num_args = 0, num_fixed = 0;
940 infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
941 if (status != INFIX_SUCCESS) {
943 return status;
944 }
946 (num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *)) : nullptr;
947 if (num_args > 0 && !arg_types) {
951 }
952 for (size_t i = 0; i < num_args; ++i)
953 arg_types[i] = args[i].type;
954
955 // Call the manual API with the parsed types.
956 status =
957 infix_reverse_create_callback_manual(out_context, ret_type, arg_types, num_args, num_fixed, user_callback_fn);
959 return status;
960}
961
963 const char * signature,
964 infix_closure_handler_fn user_callback_fn,
965 void * user_data,
967 infix_arena_t * arena = nullptr;
968 infix_type * ret_type = nullptr;
969 infix_function_argument * args = nullptr;
970 size_t num_args = 0, num_fixed = 0;
971 infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
972 if (status != INFIX_SUCCESS) {
974 return status;
975 }
977 (num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *)) : nullptr;
978 if (num_args > 0 && !arg_types) {
982 }
983 for (size_t i = 0; i < num_args; ++i)
984 arg_types[i] = args[i].type;
985
987 out_context, ret_type, arg_types, num_args, num_fixed, user_callback_fn, user_data);
989 return status;
990}
991
992// ============================================================================
993// UNITY BUILD INCLUDES
994// This section includes the actual ABI implementations at the end of the file.
995// Because `trampoline.c` is the central translation unit, including the
996// correct ABI-specific .c file here makes its functions (`g_win_x64_spec`, etc.)
997// available without needing to add platform-specific logic to the build system.
998// The `infix_config.h` header ensures only one of these #if blocks is active.
999// ============================================================================
1000#if defined(INFIX_ABI_WINDOWS_X64)
1001#include "../arch/x64/abi_win_x64.c"
1002#include "../arch/x64/abi_x64_emitters.c"
1003#elif defined(INFIX_ABI_SYSV_X64)
1004#include "../arch/x64/abi_sysv_x64.c"
1005#include "../arch/x64/abi_x64_emitters.c"
1006#elif defined(INFIX_ABI_AAPCS64)
1007#include "../arch/aarch64/abi_arm64.c"
1008#include "../arch/aarch64/abi_arm64_emitters.c"
1009#else
1010#error "No supported ABI was selected for the unity build in trampoline.c."
1011#endif
infix_arena_t * arena
Definition 005_layouts.c:68
infix_registry_t * registry
Definition 008_registry_introspection.c:35
infix_status status
Definition 103_unions.c:66
void * args[]
Definition 202_in_structs.c:64
infix_type * return_type
Definition 202_in_structs.c:60
infix_type * arg_types[]
Definition 901_call_overhead.c:67
infix_type * ret_type
Definition 901_call_overhead.c:66
const infix_reverse_abi_spec g_arm64_reverse_spec
Definition abi_arm64.c:119
const infix_forward_abi_spec g_arm64_forward_spec
Definition abi_arm64.c:111
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
A compatibility macro for the C23 [[nodiscard]] attribute.
Definition compat_c23.h:113
@ INFIX_CODE_UNRESOLVED_NAMED_TYPE
Definition infix.h:1346
@ INFIX_CODE_UNEXPECTED_TOKEN
Definition infix.h:1335
@ INFIX_CODE_UNKNOWN
Definition infix.h:1327
@ INFIX_CODE_UNSUPPORTED_ABI
Definition infix.h:1344
@ INFIX_CODE_OUT_OF_MEMORY
Definition infix.h:1330
@ INFIX_CATEGORY_ABI
Definition infix.h:1318
@ INFIX_CATEGORY_ALLOCATION
Definition infix.h:1316
@ INFIX_CATEGORY_GENERAL
Definition infix.h:1315
@ INFIX_CATEGORY_PARSER
Definition infix.h:1317
c23_nodiscard infix_status infix_reverse_create_callback(infix_reverse_t **out_context, const char *signature, void *user_callback_fn, infix_registry_t *registry)
Creates a type-safe reverse trampoline (callback).
Definition trampoline.c:932
struct infix_type_t::@0::@1 pointer_info
Metadata for INFIX_TYPE_POINTER.
union infix_type_t::@0 meta
A union containing metadata specific to the type's category.
infix_type * type
Definition infix.h:289
struct infix_type_t::@0::@4 func_ptr_info
Metadata for INFIX_TYPE_REVERSE_TRAMPOLINE.
void(* infix_cif_func)(void *, void **)
A function pointer type for a bound forward trampoline.
Definition infix.h:371
void(* infix_unbound_cif_func)(void *, void *, void **)
A function pointer type for an unbound forward trampoline.
Definition infix.h:361
c23_nodiscard infix_status infix_forward_create_unbound(infix_forward_t **out_trampoline, const char *signature, infix_registry_t *registry)
Creates an "unbound" forward trampoline from a signature string.
Definition trampoline.c:926
c23_nodiscard infix_status infix_forward_create(infix_forward_t **out_trampoline, const char *signature, void *target_function, infix_registry_t *registry)
Creates a "bound" forward trampoline from a signature string.
Definition trampoline.c:919
infix_struct_member * members
Definition infix.h:231
c23_nodiscard infix_status infix_forward_create_in_arena(infix_forward_t **out_trampoline, infix_arena_t *target_arena, const char *signature, void *target_function, infix_registry_t *registry)
Creates a "bound" forward trampoline within a user-provided arena.
Definition trampoline.c:832
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)
Creates a generic reverse trampoline (closure) for stateful callbacks.
Definition trampoline.c:962
infix_function_argument * args
Definition infix.h:244
infix_status
Enumerates the possible status codes returned by infix API functions.
Definition infix.h:389
infix_type_category category
Definition infix.h:213
struct infix_type_t::@0::@2 aggregate_info
Metadata for INFIX_TYPE_STRUCT and INFIX_TYPE_UNION.
struct infix_type_t::@0::@3 array_info
Metadata for INFIX_TYPE_ARRAY.
struct infix_type_t * pointee_type
Definition infix.h:226
infix_type * type
Definition infix.h:279
struct infix_type_t * element_type
Definition infix.h:237
struct infix_type_t * return_type
Definition infix.h:243
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 parts.
Definition signature.c:1091
size_t num_members
Definition infix.h:232
size_t num_fixed_args
Definition infix.h:246
void(* infix_closure_handler_fn)(infix_context_t *, void *, void **)
A function pointer type for a generic closure handler.
Definition infix.h:384
size_t num_args
Definition infix.h:245
@ INFIX_ERROR_ALLOCATION_FAILED
Definition infix.h:391
@ INFIX_ERROR_PROTECTION_FAILED
Definition infix.h:395
@ INFIX_ERROR_UNSUPPORTED_ABI
Definition infix.h:393
@ INFIX_SUCCESS
Definition infix.h:390
@ INFIX_ERROR_INVALID_ARGUMENT
Definition infix.h:392
c23_nodiscard infix_cif_func infix_forward_get_code(infix_forward_t *trampoline)
Gets the callable function pointer from a bound forward trampoline.
Definition trampoline.c:288
c23_nodiscard void * infix_reverse_get_code(const infix_reverse_t *reverse_trampoline)
Gets the native, callable C function pointer from a reverse trampoline.
Definition trampoline.c:813
c23_nodiscard infix_unbound_cif_func infix_forward_get_unbound_code(infix_forward_t *trampoline)
Gets the callable function pointer from an unbound forward trampoline.
Definition trampoline.c:282
c23_nodiscard void * infix_reverse_get_user_data(const infix_reverse_t *reverse_trampoline)
Gets the user-provided data pointer from a closure context.
Definition trampoline.c:824
void infix_reverse_destroy(infix_reverse_t *reverse_trampoline)
Destroys a reverse trampoline and frees all associated memory.
Definition trampoline.c:794
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)
Creates a bound forward trampoline from infix_type objects (Manual API).
Definition trampoline.c:479
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)
Creates a type-safe reverse trampoline (callback) from infix_type objects (Manual API).
Definition trampoline.c:753
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)
Creates a generic reverse trampoline (closure) from infix_type objects (Manual API).
Definition trampoline.c:775
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 trampoline from infix_type objects (Manual API).
Definition trampoline.c:506
void infix_forward_destroy(infix_forward_t *trampoline)
Destroys a forward trampoline and frees all associated memory.
Definition trampoline.c:523
#define infix_free
A macro that can be defined to override the default free function.
Definition infix.h:330
void infix_arena_destroy(infix_arena_t *)
Destroys an arena and frees all memory allocated from it.
Definition arena.c:90
#define infix_memcpy
A macro that can be defined to override the default memcpy function.
Definition infix.h:335
#define infix_calloc
A macro that can be defined to override the default calloc function.
Definition infix.h:320
c23_nodiscard void * infix_arena_alloc(infix_arena_t *, size_t, size_t)
Allocates a block of memory from an arena.
Definition arena.c:126
#define infix_memset
A macro that can be defined to override the default memset function.
Definition infix.h:340
c23_nodiscard infix_arena_t * infix_arena_create(size_t)
Creates a new memory arena.
Definition arena.c:55
c23_nodiscard const infix_type * infix_registry_lookup_type(const infix_registry_t *, const char *)
Retrieves the canonical infix_type object for a given name from the registry.
Definition type_registry.c:802
@ INFIX_TYPE_UNION
Definition infix.h:166
@ INFIX_TYPE_ARRAY
Definition infix.h:167
@ INFIX_TYPE_POINTER
Definition infix.h:164
@ INFIX_TYPE_NAMED_REFERENCE
Definition infix.h:172
@ INFIX_TYPE_REVERSE_TRAMPOLINE
Definition infix.h:168
@ INFIX_TYPE_STRUCT
Definition infix.h:165
#define INFIX_TRAMPOLINE_HEADROOM
Extra bytes to allocate in a trampoline's private arena.
Definition infix_config.h:211
Internal data structures, function prototypes, and constants.
void infix_protected_free(infix_protected_t prot)
Frees a block of protected memory.
Definition executor.c:482
c23_nodiscard bool infix_executable_make_executable(infix_executable_t exec)
Makes a block of JIT memory executable, completing the W^X process.
Definition executor.c:389
void infix_executable_free(infix_executable_t exec)
Frees a block of executable memory and applies guard pages to prevent use-after-free.
Definition executor.c:329
void _infix_set_error(infix_error_category_t category, infix_error_code_t code, size_t position)
Sets the thread-local error state with detailed information.
Definition error.c:155
infix_type * _copy_type_graph_to_arena(infix_arena_t *, const infix_type *)
Performs a deep copy of a type graph into a destination arena.
Definition types.c:971
size_t _infix_estimate_graph_size(infix_arena_t *temp_arena, const infix_type *type)
Estimates the total memory required to deep-copy a complete type graph.
Definition types.c:1084
c23_nodiscard infix_protected_t infix_protected_alloc(size_t size)
Allocates a block of standard memory for later protection.
Definition executor.c:451
c23_nodiscard infix_executable_t infix_executable_alloc(size_t size)
Allocates a block of executable memory using the platform's W^X strategy.
Definition executor.c:234
void _infix_clear_error(void)
Clears the thread-local error state.
Definition error.c:257
c23_nodiscard bool infix_protected_make_readonly(infix_protected_t prot)
Makes a block of memory read-only for security hardening.
Definition executor.c:503
void infix_internal_dispatch_callback_fn_impl(infix_reverse_t *context, void *return_value_ptr, void **args_array)
The universal C entry point for all reverse call trampolines.
Definition executor.c:540
A dynamic buffer for staged machine code generation.
Definition infix_internals.h:204
uint8_t * code
Definition infix_internals.h:205
bool error
Definition infix_internals.h:208
size_t size
Definition infix_internals.h:207
size_t capacity
Definition infix_internals.h:206
infix_arena_t * arena
Definition infix_internals.h:209
Internal definition of a memory arena.
Definition infix_internals.h:146
bool error
Definition infix_internals.h:150
A complete layout blueprint for a forward call frame.
Definition infix_internals.h:297
size_t size
Definition infix_internals.h:68
void * rw_ptr
Definition infix_internals.h:67
void * rx_ptr
Definition infix_internals.h:66
Defines the ABI-specific implementation interface for forward trampolines.
Definition infix_internals.h:343
infix_status(* generate_forward_prologue)(code_buffer *buf, infix_call_frame_layout *layout)
Generates the function prologue (stack setup, saving registers).
Definition infix_internals.h:372
infix_status(* generate_forward_argument_moves)(code_buffer *buf, infix_call_frame_layout *layout, infix_type **arg_types, size_t num_args, size_t num_fixed_args)
Generates code to move arguments from the void** array into registers and/or the stack.
Definition infix_internals.h:382
infix_status(* generate_forward_call_instruction)(code_buffer *buf, infix_call_frame_layout *layout)
Generates the call instruction to the target function.
Definition infix_internals.h:393
infix_status(* prepare_forward_call_frame)(infix_arena_t *arena, infix_call_frame_layout **out_layout, infix_type *ret_type, infix_type **arg_types, size_t num_args, size_t num_fixed_args, void *target_fn)
Analyzes a function signature to create a complete call frame layout.
Definition infix_internals.h:359
infix_status(* generate_forward_epilogue)(code_buffer *buf, infix_call_frame_layout *layout, infix_type *ret_type)
Generates the function epilogue (handling return value, restoring stack, returning).
Definition infix_internals.h:401
Internal definition of a forward trampoline handle.
Definition infix_internals.h:94
infix_executable_t exec
Definition infix_internals.h:98
void * target_fn
Definition infix_internals.h:103
size_t num_args
Definition infix_internals.h:101
bool is_external_arena
Definition infix_internals.h:96
size_t num_fixed_args
Definition infix_internals.h:102
infix_type ** arg_types
Definition infix_internals.h:100
infix_type * return_type
Definition infix_internals.h:99
infix_arena_t * arena
Definition infix_internals.h:95
Describes a single argument to a C function.
Definition infix.h:287
Internal representation of a memory block that will be made read-only.
Definition infix_internals.h:80
void * rw_ptr
Definition infix_internals.h:81
Internal definition of a named type registry.
Definition infix_internals.h:175
Defines the ABI-specific implementation interface for reverse trampolines.
Definition infix_internals.h:413
infix_status(* generate_reverse_argument_marshalling)(code_buffer *buf, infix_reverse_call_frame_layout *layout, infix_reverse_t *context)
Generates code to marshal arguments from their native locations (registers/stack) into a void** array...
Definition infix_internals.h:438
infix_status(* prepare_reverse_call_frame)(infix_arena_t *arena, infix_reverse_call_frame_layout **out_layout, infix_reverse_t *context)
Analyzes a function signature to create a layout for the reverse call stub's stack frame.
Definition infix_internals.h:421
infix_status(* generate_reverse_prologue)(code_buffer *buf, infix_reverse_call_frame_layout *layout)
Generates the reverse stub's prologue (stack setup).
Definition infix_internals.h:430
infix_status(* generate_reverse_dispatcher_call)(code_buffer *buf, infix_reverse_call_frame_layout *layout, infix_reverse_t *context)
Generates the call to the universal C dispatcher (infix_internal_dispatch_callback_fn_impl).
Definition infix_internals.h:448
infix_status(* generate_reverse_epilogue)(code_buffer *buf, infix_reverse_call_frame_layout *layout, infix_reverse_t *context)
Generates the reverse stub's epilogue (handling return value, restoring stack, returning).
Definition infix_internals.h:458
A complete layout blueprint for a reverse call frame.
Definition infix_internals.h:320
Internal definition of a reverse trampoline (callback/closure) handle.
Definition infix_internals.h:121
infix_type * return_type
Definition infix_internals.h:125
void * user_data
Definition infix_internals.h:131
void * user_callback_fn
Definition infix_internals.h:130
infix_executable_t exec
Definition infix_internals.h:123
infix_internal_dispatch_callback_fn internal_dispatcher
Definition infix_internals.h:133
size_t num_args
Definition infix_internals.h:127
size_t num_fixed_args
Definition infix_internals.h:128
infix_protected_t protected_ctx
Definition infix_internals.h:124
bool is_variadic
Definition infix_internals.h:129
infix_forward_t * cached_forward_trampoline
Definition infix_internals.h:135
infix_arena_t * arena
Definition infix_internals.h:122
infix_type ** arg_types
Definition infix_internals.h:126
A semi-opaque structure that describes a C type.
Definition infix.h:211
Definition trampoline.c:190
const infix_type * type
Definition trampoline.c:191
struct visited_node_t * next
Definition trampoline.c:192
void emit_byte(code_buffer *buf, uint8_t byte)
A convenience wrapper to append a single byte to a code buffer.
Definition trampoline.c:179
static bool _is_type_graph_resolved_recursive(const infix_type *type, visited_node_t *visited_head)
Definition trampoline.c:207
c23_nodiscard infix_status _infix_forward_create_impl(infix_forward_t **out_trampoline, infix_arena_t *target_arena, infix_type *return_type, infix_type **arg_types, size_t num_args, size_t num_fixed_args, void *target_fn)
Definition trampoline.c:317
const infix_forward_abi_spec * get_current_forward_abi_spec()
Gets the ABI v-table for forward calls for the current platform.
Definition trampoline.c:88
void code_buffer_init(code_buffer *buf, infix_arena_t *arena)
Initializes a code buffer for JIT code generation.
Definition trampoline.c:126
static bool _is_type_graph_resolved(const infix_type *type)
Definition trampoline.c:248
void emit_int64(code_buffer *buf, int64_t value)
A convenience wrapper to append a 64-bit integer (little-endian) to a code buffer.
Definition trampoline.c:185
void code_buffer_append(code_buffer *buf, const void *data, size_t len)
Appends raw bytes to a code buffer, reallocating within its arena if necessary.
Definition trampoline.c:146
static size_t get_page_size()
Definition trampoline.c:542
void emit_int32(code_buffer *buf, int32_t value)
A convenience wrapper to append a 32-bit integer (little-endian) to a code buffer.
Definition trampoline.c:182
static size_t _estimate_metadata_size(infix_arena_t *temp_arena, infix_type *return_type, infix_type **arg_types, size_t num_args)
Definition trampoline.c:264
static infix_status _infix_reverse_create_internal(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, void *user_data, bool is_callback)
Definition trampoline.c:576
const infix_reverse_abi_spec * get_current_reverse_abi_spec()
Gets the ABI v-table for reverse calls for the current platform.
Definition trampoline.c:106
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:122