infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
trampoline.c
Go to the documentation of this file.
1
39#include "common/utility.h"
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#if defined(INFIX_OS_MACOS)
44#include <pthread.h>
45#endif
46#if defined(INFIX_OS_WINDOWS)
47#include <windows.h>
48#else
49#include <sys/mman.h>
50#include <unistd.h>
51#endif
52// Forward Declaration for Internal Creation Function
56 size_t num_args,
57 size_t num_fixed_args,
58 void * user_callback_fn,
59 void * user_data,
60 bool is_callback);
61// ABI Specification V-Table Declarations (extern to link to the specific implementations)
62#if defined(INFIX_ABI_WINDOWS_X64)
66#elif defined(INFIX_ABI_SYSV_X64)
70#elif defined(INFIX_ABI_AAPCS64)
74#endif
85#if defined(INFIX_ABI_WINDOWS_X64)
87#elif defined(INFIX_ABI_SYSV_X64)
89#elif defined(INFIX_ABI_AAPCS64)
91#else
92 return nullptr;
93#endif
94}
102#if defined(INFIX_ABI_WINDOWS_X64)
104#elif defined(INFIX_ABI_SYSV_X64)
106#elif defined(INFIX_ABI_AAPCS64)
107 return &g_arm64_reverse_spec;
108#else
109 return nullptr;
110#endif
111}
118#if defined(INFIX_ABI_WINDOWS_X64)
120#elif defined(INFIX_ABI_SYSV_X64)
122#elif defined(INFIX_ABI_AAPCS64)
124#else
125 return nullptr;
126#endif
127}
128// Code Buffer Implementation
136 buf->capacity = 64; // Start with a small initial capacity.
137 buf->arena = arena;
138 buf->code = infix_arena_alloc(arena, buf->capacity, 16);
139 buf->size = 0;
140 buf->error = (buf->code == nullptr);
141}
154void code_buffer_append(code_buffer * buf, const void * data, size_t len) {
155 if (buf->error)
156 return;
157 if (len > SIZE_MAX - buf->size) { // Overflow check
158 buf->error = true;
159 return;
160 }
161 if (buf->size + len > buf->capacity) {
162 size_t new_capacity = buf->capacity;
163 while (new_capacity < buf->size + len) {
164 if (new_capacity > SIZE_MAX / 2) { // Overflow check
165 buf->error = true;
166 return;
167 }
168 new_capacity *= 2;
169 }
170 void * new_code = infix_arena_alloc(buf->arena, new_capacity, 16);
171 if (new_code == nullptr) {
172 buf->error = true;
173 return;
174 }
175 infix_memcpy(new_code, buf->code, buf->size);
176 buf->code = new_code;
177 buf->capacity = new_capacity;
178 }
179 infix_memcpy(buf->code + buf->size, data, len);
180 buf->size += len;
181}
183void emit_byte(code_buffer * buf, uint8_t byte) { code_buffer_append(buf, &byte, 1); }
185void emit_int32(code_buffer * buf, int32_t value) { code_buffer_append(buf, &value, 4); }
187void emit_int64(code_buffer * buf, int64_t value) { code_buffer_append(buf, &value, 8); }
188// Type Graph Validation
207 if (!type)
208 return true;
209 // Cycle detection: if we've seen this node before, we can assume it's resolved
210 // for the purpose of this check, as we'll validate it on the first visit.
211 for (visited_node_t * v = visited_head; v != NULL; v = v->next)
212 if (v->type == type)
213 return true;
214 visited_node_t current_visited_node = {.type = type, .next = visited_head};
215 switch (type->category) {
217 return false; // Base case: an unresolved reference.
220 case INFIX_TYPE_ARRAY:
221 return _is_type_graph_resolved_recursive(type->meta.array_info.element_type, &current_visited_node);
223 case INFIX_TYPE_UNION:
224 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i)
225 if (!_is_type_graph_resolved_recursive(type->meta.aggregate_info.members[i].type, &current_visited_node))
226 return false;
227 return true;
230 return false;
231 for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i)
232 if (!_is_type_graph_resolved_recursive(type->meta.func_ptr_info.args[i].type, &current_visited_node))
233 return false;
234 return true;
235 default:
236 return true; // Primitives, void, etc., are always resolved.
237 }
238}
258static size_t _estimate_metadata_size(infix_arena_t * temp_arena,
261 size_t num_args) {
262 size_t total_size = 0;
263 total_size += _infix_estimate_graph_size(temp_arena, return_type);
264 if (arg_types != nullptr) {
265 // Add space for the arg_types pointer array itself.
266 total_size += sizeof(infix_type *) * num_args;
267 for (size_t i = 0; i < num_args; ++i)
268 total_size += _infix_estimate_graph_size(temp_arena, arg_types[i]);
269 }
270 return total_size;
271}
272// Forward Trampoline API Implementation
274 if (trampoline == nullptr || trampoline->is_direct_trampoline || trampoline->target_fn != nullptr)
275 return nullptr;
276 return (infix_unbound_cif_func)trampoline->exec.rx_ptr;
277}
279 if (trampoline == nullptr || trampoline->is_direct_trampoline || trampoline->target_fn == nullptr)
280 return nullptr;
281 return (infix_cif_func)trampoline->exec.rx_ptr;
282}
284 if (trampoline == nullptr || !trampoline->is_direct_trampoline)
285 return nullptr;
286 return (infix_direct_cif_func)trampoline->exec.rx_ptr;
287}
312 infix_arena_t * target_arena,
315 size_t num_args,
316 size_t num_fixed_args,
317 void * target_fn) {
318 if (out_trampoline == nullptr || return_type == nullptr || (arg_types == nullptr && num_args > 0)) {
321 }
322 // Pre-flight check: ensure all types are resolved before passing to ABI layer.
326 }
327 for (size_t i = 0; i < num_args; ++i) {
328 if (arg_types[i] == nullptr || !_is_type_graph_resolved(arg_types[i])) {
331 }
332 }
334 if (spec == nullptr) {
337 }
339 infix_call_frame_layout * layout = nullptr;
340 infix_forward_t * handle = nullptr;
341 // Use a temporary arena for all intermediate allocations during code generation.
342 infix_arena_t * temp_arena = infix_arena_create(65536);
343 if (!temp_arena) {
346 }
347 code_buffer buf;
348 code_buffer_init(&buf, temp_arena);
349 // JIT Compilation Pipeline
350 // 1. Prepare: Classify arguments and create the layout blueprint.
352 temp_arena, &layout, return_type, arg_types, num_args, num_fixed_args, target_fn);
353 if (status != INFIX_SUCCESS)
354 goto cleanup;
355 // 2. Generate: Emit machine code based on the layout.
356 status = spec->generate_forward_prologue(&buf, layout);
357 if (status != INFIX_SUCCESS)
358 goto cleanup;
359 status = spec->generate_forward_argument_moves(&buf, layout, arg_types, num_args, num_fixed_args);
360 if (status != INFIX_SUCCESS)
361 goto cleanup;
362 status = spec->generate_forward_call_instruction(&buf, layout);
363 if (status != INFIX_SUCCESS)
364 goto cleanup;
365 status = spec->generate_forward_epilogue(&buf, layout, return_type);
366 if (status != INFIX_SUCCESS)
367 goto cleanup;
368 if (buf.error || temp_arena->error) {
370 goto cleanup;
371 }
372 // Finalize Handle
373 handle = infix_calloc(1, sizeof(infix_forward_t));
374 if (handle == nullptr) {
376 goto cleanup;
377 }
378 // "Estimate" stage: Calculate the exact size needed for the handle's private arena.
379 size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
380 if (target_arena) {
381 handle->arena = target_arena;
382 handle->is_external_arena = true;
383 }
384 else {
385 handle->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
386 handle->is_external_arena = false;
387 }
388 if (handle->arena == nullptr) {
390 goto cleanup;
391 }
392 // "Copy" stage: Deep copy all type info into the handle's private arena.
394 if (num_args > 0) {
395 handle->arg_types = infix_arena_alloc(handle->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
396 if (handle->arg_types == nullptr) {
398 goto cleanup;
399 }
400 for (size_t i = 0; i < num_args; ++i) {
401 handle->arg_types[i] = _copy_type_graph_to_arena(handle->arena, arg_types[i]);
402 // Check for allocation failure during copy
403 if (arg_types[i] != nullptr && handle->arg_types[i] == nullptr && !handle->arena->error) {
405 goto cleanup;
406 }
407 }
408 }
409 handle->num_args = num_args;
410 handle->num_fixed_args = num_fixed_args;
411 handle->target_fn = target_fn;
412 // Allocate and finalize executable memory.
413 handle->exec = infix_executable_alloc(buf.size);
414 if (handle->exec.rw_ptr == nullptr) {
416 goto cleanup;
417 }
418 infix_memcpy(handle->exec.rw_ptr, buf.code, buf.size);
421 goto cleanup;
422 }
423 infix_dump_hex(handle->exec.rx_ptr, handle->exec.size, "Forward Trampoline Machine Code");
424 *out_trampoline = handle;
425cleanup:
426 // If any step failed, ensure the partially created handle is fully destroyed.
427 if (status != INFIX_SUCCESS && handle != nullptr)
428 infix_forward_destroy(handle);
429 // The temporary arena is always destroyed.
430 infix_arena_destroy(temp_arena);
431 return status;
432}
461 size_t num_args,
462 void * target_fn,
464 // 1. Validation and Setup
465 if (!out_trampoline || !return_type || (!arg_types && num_args > 0) || !target_fn || !handlers) {
468 }
469
471 if (spec == nullptr) {
474 }
475
477 infix_direct_call_frame_layout * layout = nullptr;
478 infix_forward_t * handle = nullptr;
479 infix_arena_t * temp_arena = infix_arena_create(65536);
480 if (!temp_arena) {
483 }
484 code_buffer buf;
485 code_buffer_init(&buf, temp_arena);
486
487 // 2. JIT Compilation Pipeline
489 temp_arena, &layout, return_type, arg_types, num_args, handlers, target_fn);
490 if (status != INFIX_SUCCESS)
491 goto cleanup;
492
493 status = spec->generate_direct_forward_prologue(&buf, layout);
494 if (status != INFIX_SUCCESS)
495 goto cleanup;
496
498 if (status != INFIX_SUCCESS)
499 goto cleanup;
500
502 if (status != INFIX_SUCCESS)
503 goto cleanup;
504
506 if (status != INFIX_SUCCESS)
507 goto cleanup;
508
509 if (buf.error || temp_arena->error) {
511 goto cleanup;
512 }
513
514 // 3. Finalize Handle
515 handle = infix_calloc(1, sizeof(infix_forward_t));
516 if (handle == nullptr) {
518 goto cleanup;
519 }
520
521 handle->is_direct_trampoline = true; // Mark this as a direct marshalling trampoline.
522
523 size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
524 handle->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
525 handle->is_external_arena = false;
526 if (handle->arena == nullptr) {
528 goto cleanup;
529 }
530
532 if (num_args > 0) {
533 handle->arg_types = infix_arena_alloc(handle->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
534 if (!handle->arg_types) {
536 goto cleanup;
537 }
538 for (size_t i = 0; i < num_args; ++i) {
539 handle->arg_types[i] = _copy_type_graph_to_arena(handle->arena, arg_types[i]);
540 if (arg_types[i] && !handle->arg_types[i] && !handle->arena->error) {
542 goto cleanup;
543 }
544 }
545 }
546 handle->num_args = num_args;
547 handle->num_fixed_args = num_args; // Direct trampolines are always fixed-arity.
548 handle->target_fn = target_fn;
549
550 // 4. Allocate and Finalize Executable Memory
551 handle->exec = infix_executable_alloc(buf.size);
552 if (handle->exec.rw_ptr == nullptr) {
554 goto cleanup;
555 }
556 infix_memcpy(handle->exec.rw_ptr, buf.code, buf.size);
559 goto cleanup;
560 }
561
562 infix_dump_hex(handle->exec.rx_ptr, handle->exec.size, "Direct-Marshalling Forward Trampoline Machine Code");
563 *out_trampoline = handle;
564
565cleanup:
566 if (status != INFIX_SUCCESS && handle != nullptr)
567 infix_forward_destroy(handle);
568 infix_arena_destroy(temp_arena);
569 return status;
570}
592 size_t num_args,
593 size_t num_fixed_args,
594 void * target_function) {
595 // This is part of the "Manual API". It calls the internal implementation directly
596 // without involving the signature parser. `source_arena` is null because the
597 // types are assumed to be managed by the user.
600 out_trampoline, nullptr, return_type, arg_types, num_args, num_fixed_args, target_function);
601}
618 size_t num_args,
619 size_t num_fixed_args) {
622 out_trampoline, nullptr, return_type, arg_types, num_args, num_fixed_args, nullptr);
623}
632 if (trampoline == nullptr)
633 return;
634 // Destroying the private arena frees all deep-copied type metadata.
635 if (trampoline->arena && !trampoline->is_external_arena)
636 infix_arena_destroy(trampoline->arena);
637 // Free the JIT-compiled executable code.
638 infix_executable_free(trampoline->exec);
639 // Free the handle struct itself.
640 infix_free(trampoline);
641}
642// Reverse Trampoline API Implementation
648static size_t get_page_size() {
649#if defined(INFIX_OS_WINDOWS)
650 SYSTEM_INFO sysInfo;
651 GetSystemInfo(&sysInfo);
652 return sysInfo.dwPageSize;
653#else
654 // sysconf is the standard POSIX way to get system configuration values.
655 return sysconf(_SC_PAGESIZE);
656#endif
657}
684 size_t num_args,
685 size_t num_fixed_args,
686 void * user_callback_fn,
687 void * user_data,
688 bool is_callback) {
689 if (out_context == nullptr || return_type == nullptr || num_fixed_args > num_args) {
692 }
693 // Pre-flight check: ensure all types are fully resolved.
697 }
698 if (arg_types == nullptr && num_args > 0) {
701 }
702 for (size_t i = 0; i < num_args; ++i) {
703 if (arg_types[i] == nullptr || !_is_type_graph_resolved(arg_types[i])) {
706 }
707 }
709 if (spec == nullptr) {
712 }
714 infix_reverse_call_frame_layout * layout = nullptr;
715 infix_reverse_t * context = nullptr;
716 infix_arena_t * temp_arena = nullptr;
717 infix_protected_t prot = {.rw_ptr = nullptr, .size = 0};
718 code_buffer buf;
719 temp_arena = infix_arena_create(65536);
720 if (!temp_arena) {
723 }
724 code_buffer_init(&buf, temp_arena);
725 // Security Hardening: Allocate the context struct itself in special, page-aligned
726 // memory that can be made read-only after initialization.
727 size_t page_size = get_page_size();
728 size_t context_alloc_size = (sizeof(infix_reverse_t) + page_size - 1) & ~(page_size - 1);
729 prot = infix_protected_alloc(context_alloc_size);
730 if (prot.rw_ptr == nullptr) {
732 goto cleanup;
733 }
734 context = (infix_reverse_t *)prot.rw_ptr;
735 infix_memset(context, 0, context_alloc_size);
736 // "Estimate" stage: Calculate the exact size needed for the context's private arena.
737 size_t required_metadata_size = _estimate_metadata_size(temp_arena, return_type, arg_types, num_args);
738 // Create the context's private arena with the calculated size plus some headroom for safety.
739 context->arena = infix_arena_create(required_metadata_size + INFIX_TRAMPOLINE_HEADROOM);
740 if (context->arena == nullptr) {
742 goto cleanup;
743 }
744 // Populate the context fields.
745 context->protected_ctx = prot;
746 context->num_args = num_args;
747 context->num_fixed_args = num_fixed_args;
748 context->is_variadic = (num_fixed_args < num_args);
749 context->user_callback_fn = user_callback_fn;
750 context->user_data = user_data;
752 context->cached_forward_trampoline = nullptr;
753 // "Copy" stage: deep copy all types into the context's private arena.
755 if (num_args > 0) {
756 context->arg_types = infix_arena_alloc(context->arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
757 if (context->arg_types == nullptr) {
759 goto cleanup;
760 }
761 for (size_t i = 0; i < num_args; ++i) {
762 context->arg_types[i] = _copy_type_graph_to_arena(context->arena, arg_types[i]);
763 if (arg_types[i] != nullptr && context->arg_types[i] == nullptr) {
765 goto cleanup;
766 }
767 }
768 }
769 // Special step for type-safe callbacks: generate and cache a forward trampoline
770 // that will be used to call the user's type-safe C handler.
771 if (is_callback) {
773 context->return_type,
774 context->arg_types,
775 context->num_args,
776 context->num_fixed_args,
777 user_callback_fn);
778 if (status != INFIX_SUCCESS)
779 goto cleanup;
780 }
781 // JIT Compilation Pipeline for Reverse Stub
782 status = spec->prepare_reverse_call_frame(temp_arena, &layout, context);
783 if (status != INFIX_SUCCESS)
784 goto cleanup;
785 status = spec->generate_reverse_prologue(&buf, layout);
786 if (status != INFIX_SUCCESS)
787 goto cleanup;
788 status = spec->generate_reverse_argument_marshalling(&buf, layout, context);
789 if (status != INFIX_SUCCESS)
790 goto cleanup;
791 status = spec->generate_reverse_dispatcher_call(&buf, layout, context);
792 if (status != INFIX_SUCCESS)
793 goto cleanup;
794 status = spec->generate_reverse_epilogue(&buf, layout, context);
795 if (status != INFIX_SUCCESS)
796 goto cleanup;
797 // End of Pipeline
798 if (buf.error || temp_arena->error) {
800 goto cleanup;
801 }
802 context->exec = infix_executable_alloc(buf.size);
803 if (context->exec.rw_ptr == nullptr) {
805 goto cleanup;
806 }
807 infix_memcpy(context->exec.rw_ptr, buf.code, buf.size);
808 if (!infix_executable_make_executable(context->exec)) {
810 goto cleanup;
811 }
812 // Security Hardening: Make the context memory read-only to prevent runtime corruption.
815 goto cleanup;
816 }
817 infix_dump_hex(context->exec.rx_ptr, buf.size, "Reverse Trampoline Machine Code");
818 *out_context = context;
819cleanup:
820 if (status != INFIX_SUCCESS) {
821 // If allocation of the context itself failed, prot.rw_ptr will be null.
822 if (prot.rw_ptr != nullptr)
823 infix_reverse_destroy(context);
824 }
825 infix_arena_destroy(temp_arena);
826 return status;
827}
841 size_t num_args,
842 size_t num_fixed_args,
843 void * user_callback_fn) {
846 out_context, return_type, arg_types, num_args, num_fixed_args, user_callback_fn, nullptr, true);
847}
862 size_t num_args,
863 size_t num_fixed_args,
864 infix_closure_handler_fn user_callback_fn,
865 void * user_data) {
868 out_context, return_type, arg_types, num_args, num_fixed_args, (void *)user_callback_fn, user_data, false);
869}
877void infix_reverse_destroy(infix_reverse_t * reverse_trampoline) {
878 if (reverse_trampoline == nullptr)
879 return;
880 // The cached trampoline (if it exists) must also be destroyed.
881 if (reverse_trampoline->cached_forward_trampoline)
883 if (reverse_trampoline->arena)
884 infix_arena_destroy(reverse_trampoline->arena);
885 infix_executable_free(reverse_trampoline->exec);
886 // Free the special read-only memory region for the context struct.
887 infix_protected_free(reverse_trampoline->protected_ctx);
888}
895c23_nodiscard void * infix_reverse_get_code(const infix_reverse_t * reverse_trampoline) {
896 if (reverse_trampoline == nullptr)
897 return nullptr;
898 return reverse_trampoline->exec.rx_ptr;
899}
906 if (reverse_trampoline == nullptr)
907 return nullptr;
908 return reverse_trampoline->user_data;
909}
910// High-Level Signature API Wrappers
912 infix_arena_t * target_arena,
913 const char * signature,
914 void * target_function,
917 if (!signature) {
920 }
921 infix_arena_t * arena = nullptr;
922 infix_type * ret_type = nullptr;
923 infix_function_argument * args = nullptr;
924 size_t num_args = 0, num_fixed = 0;
925 infix_type ** arg_types = nullptr;
927 if (signature[0] == '@') {
928 if (registry == nullptr) {
929 _infix_set_error(INFIX_CATEGORY_GENERAL, INFIX_CODE_UNKNOWN, 0); // Using @Name requires a registry
931 }
932 const infix_type * func_type = infix_registry_lookup_type(registry, &signature[1]);
933 if (func_type == NULL) {
936 }
937 if (func_type->category != INFIX_TYPE_REVERSE_TRAMPOLINE) {
938 // The user provided a name for a non-function type (e.g., "@Point")
941 }
942 // We have a valid function type from the registry. Now, unpack its components.
944 num_args = func_type->meta.func_ptr_info.num_args;
945 num_fixed = func_type->meta.func_ptr_info.num_fixed_args;
946 args = func_type->meta.func_ptr_info.args;
947 // The Manual API needs a temporary arena to hold the arg_types array.
948 infix_arena_t * temp_arena = infix_arena_create(sizeof(infix_type *) * num_args + 128);
949 if (!temp_arena) {
952 }
953 if (num_args > 0) {
954 arg_types = infix_arena_alloc(temp_arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
955 if (!arg_types) {
956 infix_arena_destroy(temp_arena);
959 }
960 for (size_t i = 0; i < num_args; ++i)
961 arg_types[i] = args[i].type;
962 }
963 arena = temp_arena;
964 }
965 else {
966 // This is a high-level wrapper. It uses the parser to build the type info first.
967 status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
968 if (status != INFIX_SUCCESS) {
970 return status;
971 }
972 // Extract the `infix_type*` array from the parsed `infix_function_argument` array.
973 arg_types = (num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *))
974 : nullptr;
975 if (num_args > 0 && !arg_types) {
979 }
980 for (size_t i = 0; i < num_args; ++i)
981 arg_types[i] = args[i].type;
982 }
983 // Call the core internal implementation with the parsed types.
985 out_trampoline, target_arena, ret_type, arg_types, num_args, num_fixed, target_function);
987 return status;
988}
990 const char * signature,
991 void * target_function,
993 return infix_forward_create_in_arena(out_trampoline, NULL, signature, target_function, registry);
994}
996 const char * signature,
998 return infix_forward_create_in_arena(out_trampoline, NULL, signature, NULL, registry);
999}
1001 const char * signature,
1002 void * target_function,
1006 if (!signature || !target_function || !handlers) {
1009 }
1010
1011 infix_arena_t * arena = nullptr;
1012 infix_type * ret_type = nullptr;
1013 infix_function_argument * args = nullptr;
1014 size_t num_args = 0, num_fixed = 0;
1015 infix_type ** arg_types = nullptr;
1016
1017 // Parse the signature to get the type graph.
1018 infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
1019 if (status != INFIX_SUCCESS) {
1021 return status;
1022 }
1023
1024 // Convert the parsed `infix_function_argument*` array to an `infix_type**` array.
1025 if (num_args > 0) {
1026 arg_types = infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *));
1027 if (!arg_types) {
1031 }
1032 for (size_t i = 0; i < num_args; ++i)
1033 arg_types[i] = args[i].type;
1034 }
1035
1036 // Call the core internal implementation with the parsed types and provided handlers.
1037 status =
1038 _infix_forward_create_direct_impl(out_trampoline, ret_type, arg_types, num_args, target_function, handlers);
1039
1040 // Clean up the temporary arena used by the parser.
1042 return status;
1043}
1045 const char * signature,
1046 void * user_callback_fn,
1048 infix_arena_t * arena = nullptr;
1049 infix_type * ret_type = nullptr;
1050 infix_function_argument * args = nullptr;
1051 size_t num_args = 0, num_fixed = 0;
1052 infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
1053 if (status != INFIX_SUCCESS) {
1055 return status;
1056 }
1058 (num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *)) : nullptr;
1059 if (num_args > 0 && !arg_types) {
1063 }
1064 for (size_t i = 0; i < num_args; ++i)
1065 arg_types[i] = args[i].type;
1066 // Call the manual API with the parsed types.
1067 status =
1068 infix_reverse_create_callback_manual(out_context, ret_type, arg_types, num_args, num_fixed, user_callback_fn);
1070 return status;
1071}
1073 const char * signature,
1074 infix_closure_handler_fn user_callback_fn,
1075 void * user_data,
1077 infix_arena_t * arena = nullptr;
1078 infix_type * ret_type = nullptr;
1079 infix_function_argument * args = nullptr;
1080 size_t num_args = 0, num_fixed = 0;
1081 infix_status status = infix_signature_parse(signature, &arena, &ret_type, &args, &num_args, &num_fixed, registry);
1082 if (status != INFIX_SUCCESS) {
1084 return status;
1085 }
1087 (num_args > 0) ? infix_arena_alloc(arena, sizeof(infix_type *) * num_args, _Alignof(infix_type *)) : nullptr;
1088 if (num_args > 0 && !arg_types) {
1092 }
1093 for (size_t i = 0; i < num_args; ++i)
1094 arg_types[i] = args[i].type;
1096 out_context, ret_type, arg_types, num_args, num_fixed, user_callback_fn, user_data);
1098 return status;
1099}
1100// ============================================================================
1101// UNITY BUILD INCLUDES
1102// This section includes the actual ABI implementations at the end of the file.
1103// Because `trampoline.c` is the central translation unit, including the
1104// correct ABI-specific .c file here makes its functions (`g_win_x64_spec`, etc.)
1105// available without needing to add platform-specific logic to the build system.
1106// The `infix_config.h` header ensures only one of these #if blocks is active.
1107// ============================================================================
1108#if defined(INFIX_ABI_WINDOWS_X64)
1109#include "../arch/x64/abi_win_x64.c"
1110#include "../arch/x64/abi_x64_emitters.c"
1111#elif defined(INFIX_ABI_SYSV_X64)
1112#include "../arch/x64/abi_sysv_x64.c"
1113#include "../arch/x64/abi_x64_emitters.c"
1114#elif defined(INFIX_ABI_AAPCS64)
1115#include "../arch/aarch64/abi_arm64.c"
1116#include "../arch/aarch64/abi_arm64_emitters.c"
1117#else
1118#error "No supported ABI was selected for the unity build in trampoline.c."
1119#endif
infix_arena_t * arena
Definition 005_layouts.c:60
infix_registry_t * registry
Definition 008_registry_introspection.c:32
infix_status status
Definition 103_unions.c:59
void * args[]
Definition 202_in_structs.c:57
infix_type * return_type
Definition 202_in_structs.c:53
infix_type * arg_types[]
Definition 901_call_overhead.c:61
infix_type * ret_type
Definition 901_call_overhead.c:60
infix_direct_arg_handler_t handlers[2]
Definition 901_call_overhead.c:103
const infix_reverse_abi_spec g_arm64_reverse_spec
Definition abi_arm64.c:116
const infix_forward_abi_spec g_arm64_forward_spec
Definition abi_arm64.c:95
const infix_direct_forward_abi_spec g_arm64_direct_forward_spec
Definition abi_arm64.c:139
const infix_reverse_abi_spec g_sysv_x64_reverse_spec
Definition abi_sysv_x64.c:113
const infix_direct_forward_abi_spec g_sysv_x64_direct_forward_spec
Definition abi_sysv_x64.c:137
const infix_forward_abi_spec g_sysv_x64_forward_spec
Definition abi_sysv_x64.c:92
const infix_direct_forward_abi_spec g_win_x64_direct_forward_spec
Definition abi_win_x64.c:125
const infix_reverse_abi_spec g_win_x64_reverse_spec
Definition abi_win_x64.c:101
const infix_forward_abi_spec g_win_x64_forward_spec
Definition abi_win_x64.c:81
#define c23_nodiscard
A compatibility macro for the C23 [[nodiscard]] attribute.
Definition compat_c23.h:106
c23_nodiscard infix_status infix_forward_create_direct(infix_forward_t **out_trampoline, const char *signature, void *target_function, infix_direct_arg_handler_t *handlers, infix_registry_t *registry)
Creates a forward trampoline with direct, JIT-bound marshalling.
Definition trampoline.c:1000
void(* infix_direct_cif_func)(void *, void **)
A function pointer for a direct marshalling forward trampoline.
Definition infix.h:1291
c23_nodiscard infix_direct_cif_func infix_forward_get_direct_code(infix_forward_t *trampoline)
Gets the callable function pointer from a direct marshalling trampoline.
Definition trampoline.c:283
@ INFIX_CODE_UNRESOLVED_NAMED_TYPE
Definition infix.h:1242
@ INFIX_CODE_UNEXPECTED_TOKEN
Definition infix.h:1232
@ INFIX_CODE_UNKNOWN
Definition infix.h:1226
@ INFIX_CODE_UNSUPPORTED_ABI
Definition infix.h:1240
@ INFIX_CODE_OUT_OF_MEMORY
Definition infix.h:1228
@ INFIX_CATEGORY_ABI
Definition infix.h:1218
@ INFIX_CATEGORY_ALLOCATION
Definition infix.h:1216
@ INFIX_CATEGORY_GENERAL
Definition infix.h:1215
@ INFIX_CATEGORY_PARSER
Definition infix.h:1217
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:1044
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:266
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:336
void(* infix_unbound_cif_func)(void *, void *, void **)
A function pointer type for an unbound forward trampoline.
Definition infix.h:327
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:995
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:989
infix_struct_member * members
Definition infix.h:211
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:911
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:1072
infix_function_argument * args
Definition infix.h:224
infix_status
Enumerates the possible status codes returned by infix API functions.
Definition infix.h:352
infix_type_category category
Definition infix.h:196
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:207
infix_type * type
Definition infix.h:254
struct infix_type_t * element_type
Definition infix.h:217
struct infix_type_t * return_type
Definition infix.h:223
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:1068
size_t num_members
Definition infix.h:212
size_t num_fixed_args
Definition infix.h:226
void(* infix_closure_handler_fn)(infix_context_t *, void *, void **)
A function pointer type for a generic closure handler.
Definition infix.h:348
size_t num_args
Definition infix.h:225
@ INFIX_ERROR_ALLOCATION_FAILED
Definition infix.h:354
@ INFIX_ERROR_PROTECTION_FAILED
Definition infix.h:358
@ INFIX_ERROR_UNSUPPORTED_ABI
Definition infix.h:356
@ INFIX_SUCCESS
Definition infix.h:353
@ INFIX_ERROR_INVALID_ARGUMENT
Definition infix.h:355
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:278
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:895
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:273
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:905
void infix_reverse_destroy(infix_reverse_t *reverse_trampoline)
Destroys a reverse trampoline and frees all associated memory.
Definition trampoline.c:877
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:589
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:838
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:859
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:615
void infix_forward_destroy(infix_forward_t *trampoline)
Destroys a forward trampoline and frees all associated memory.
Definition trampoline.c:631
#define infix_free
A macro that can be defined to override the default free function.
Definition infix.h:301
void infix_arena_destroy(infix_arena_t *)
Destroys an arena and frees all memory allocated from it.
Definition arena.c:83
#define infix_memcpy
A macro that can be defined to override the default memcpy function.
Definition infix.h:305
#define infix_calloc
A macro that can be defined to override the default calloc function.
Definition infix.h:293
c23_nodiscard void * infix_arena_alloc(infix_arena_t *, size_t, size_t)
Allocates a block of memory from an arena.
Definition arena.c:117
#define infix_memset
A macro that can be defined to override the default memset function.
Definition infix.h:309
c23_nodiscard infix_arena_t * infix_arena_create(size_t)
Creates a new memory arena.
Definition arena.c:52
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:740
@ INFIX_TYPE_UNION
Definition infix.h:152
@ INFIX_TYPE_ARRAY
Definition infix.h:153
@ INFIX_TYPE_POINTER
Definition infix.h:150
@ INFIX_TYPE_NAMED_REFERENCE
Definition infix.h:158
@ INFIX_TYPE_REVERSE_TRAMPOLINE
Definition infix.h:154
@ INFIX_TYPE_STRUCT
Definition infix.h:151
#define INFIX_TRAMPOLINE_HEADROOM
Extra bytes to allocate in a trampoline's private arena.
Definition infix_config.h:201
Internal data structures, function prototypes, and constants.
void infix_protected_free(infix_protected_t prot)
Frees a block of protected memory.
Definition executor.c:437
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:350
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:292
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:165
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:1125
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:1230
c23_nodiscard infix_protected_t infix_protected_alloc(size_t size)
Allocates a block of standard memory for later protection.
Definition executor.c:407
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:205
void _infix_clear_error(void)
Clears the thread-local error state.
Definition error.c:258
c23_nodiscard bool infix_protected_make_readonly(infix_protected_t prot)
Makes a block of memory read-only for security hardening.
Definition executor.c:457
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:492
A dynamic buffer for staged machine code generation.
Definition infix_internals.h:192
uint8_t * code
Definition infix_internals.h:193
bool error
Definition infix_internals.h:196
size_t size
Definition infix_internals.h:195
size_t capacity
Definition infix_internals.h:194
infix_arena_t * arena
Definition infix_internals.h:197
Internal definition of a memory arena.
Definition infix_internals.h:138
bool error
Definition infix_internals.h:142
A complete layout blueprint for a forward call frame.
Definition infix_internals.h:281
A struct containing all the necessary handlers for a single function argument.
Definition infix.h:1354
A complete layout blueprint for a direct marshalling forward call frame.
Definition infix_internals.h:464
Defines the ABI-specific implementation interface for direct marshalling forward trampolines.
Definition infix_internals.h:478
infix_status(* generate_direct_forward_call_instruction)(code_buffer *buf, infix_direct_call_frame_layout *layout)
Generates the call instruction to the target function.
Definition infix_internals.h:492
infix_status(* generate_direct_forward_argument_moves)(code_buffer *buf, infix_direct_call_frame_layout *layout)
Generates code to call marshallers and move arguments into their native locations.
Definition infix_internals.h:490
infix_status(* prepare_direct_forward_call_frame)(infix_arena_t *arena, infix_direct_call_frame_layout **out_layout, infix_type *ret_type, infix_type **arg_types, size_t num_args, infix_direct_arg_handler_t *handlers, void *target_fn)
Analyzes a function signature to create a complete direct call frame layout.
Definition infix_internals.h:480
infix_status(* generate_direct_forward_epilogue)(code_buffer *buf, infix_direct_call_frame_layout *layout, infix_type *ret_type)
Generates the function epilogue (handling return value, calling write-back handlers,...
Definition infix_internals.h:495
infix_status(* generate_direct_forward_prologue)(code_buffer *buf, infix_direct_call_frame_layout *layout)
Generates the function prologue (stack setup, saving registers).
Definition infix_internals.h:488
size_t size
Definition infix_internals.h:64
void * rw_ptr
Definition infix_internals.h:63
void * rx_ptr
Definition infix_internals.h:62
Defines the ABI-specific implementation interface for forward trampolines.
Definition infix_internals.h:325
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:354
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:364
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:375
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:341
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:383
Internal definition of a forward trampoline handle.
Definition infix_internals.h:88
infix_executable_t exec
Definition infix_internals.h:92
void * target_fn
Definition infix_internals.h:97
size_t num_args
Definition infix_internals.h:95
bool is_external_arena
Definition infix_internals.h:90
size_t num_fixed_args
Definition infix_internals.h:96
infix_type ** arg_types
Definition infix_internals.h:94
infix_type * return_type
Definition infix_internals.h:93
infix_arena_t * arena
Definition infix_internals.h:89
bool is_direct_trampoline
Definition infix_internals.h:98
Describes a single argument to a C function.
Definition infix.h:264
Internal representation of a memory block that will be made read-only.
Definition infix_internals.h:75
void * rw_ptr
Definition infix_internals.h:76
Internal definition of a named type registry.
Definition infix_internals.h:165
Defines the ABI-specific implementation interface for reverse trampolines.
Definition infix_internals.h:394
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:419
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:402
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:411
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:429
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:439
A complete layout blueprint for a reverse call frame.
Definition infix_internals.h:303
Internal definition of a reverse trampoline (callback/closure) handle.
Definition infix_internals.h:114
infix_type * return_type
Definition infix_internals.h:118
void * user_data
Definition infix_internals.h:124
void * user_callback_fn
Definition infix_internals.h:123
infix_executable_t exec
Definition infix_internals.h:116
infix_internal_dispatch_callback_fn internal_dispatcher
Definition infix_internals.h:126
size_t num_args
Definition infix_internals.h:120
size_t num_fixed_args
Definition infix_internals.h:121
infix_protected_t protected_ctx
Definition infix_internals.h:117
bool is_variadic
Definition infix_internals.h:122
infix_forward_t * cached_forward_trampoline
Definition infix_internals.h:128
infix_arena_t * arena
Definition infix_internals.h:115
infix_type ** arg_types
Definition infix_internals.h:119
A semi-opaque structure that describes a C type.
Definition infix.h:194
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:183
static bool _is_type_graph_resolved_recursive(const infix_type *type, visited_node_t *visited_head)
Definition trampoline.c:206
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:311
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:84
void code_buffer_init(code_buffer *buf, infix_arena_t *arena)
Initializes a code buffer for JIT code generation.
Definition trampoline.c:135
static bool _is_type_graph_resolved(const infix_type *type)
Definition trampoline.c:243
c23_nodiscard infix_status _infix_forward_create_direct_impl(infix_forward_t **out_trampoline, infix_type *return_type, infix_type **arg_types, size_t num_args, void *target_fn, infix_direct_arg_handler_t *handlers)
Definition trampoline.c:458
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:187
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:154
static size_t get_page_size()
Definition trampoline.c:648
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:185
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:258
const infix_direct_forward_abi_spec * get_current_direct_forward_abi_spec()
Gets the ABI v-table for direct marshalling forward calls for the current platform.
Definition trampoline.c:117
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:681
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:101
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