ffi #foreign: C-variadic tail via args: ..T

Trailing `args: ..T` on a #foreign declaration now lowers to the C
calling convention's `...` instead of sx-side slice-packing. Drops
the per-arity #foreign-shim workaround for callers of variadic C
APIs (__android_log_print, printf-family, etc.). Closes issue-0043.

- IR: Function.is_variadic on inst.Function; declareFunction drops
  the variadic param from the IR signature for foreign+variadic
  decls.
- emit_llvm: LLVMFunctionType receives is_var_arg=1 when the flag
  is set; call lowering passes extras through unchanged.
- Lowering: packVariadicCallArgs early-outs for foreign+variadic
  (no slice-pack); new promoteCVariadicArgs applies C default
  argument promotion (bool/s8/s16/u8/u16 -> s32, f32 -> f64) to
  extras past the fixed param count.
- Test: examples/ffi-foreign-cvariadic.sx + .c exercise s64/f64/s32
  returns through C va_arg over s32/f64/*u8 element types.

134 host + 6 cross tests pass on the WIP-less baseline.
This commit is contained in:
agra
2026-05-22 13:13:43 +03:00
parent cc29cfa7ce
commit 30fed66616
7 changed files with 112 additions and 3 deletions

View File

@@ -0,0 +1,30 @@
#include <stdarg.h>
long long sx_ffi_sum_ints(int n, ...) {
va_list ap;
va_start(ap, n);
long long total = 0;
for (int i = 0; i < n; i++) total += va_arg(ap, int);
va_end(ap);
return total;
}
double sx_ffi_avg_doubles(int n, ...) {
va_list ap;
va_start(ap, n);
double total = 0.0;
for (int i = 0; i < n; i++) total += va_arg(ap, double);
va_end(ap);
if (n == 0) return 0.0;
return total / n;
}
int sx_ffi_count_args(const char *tag, ...) {
(void) tag;
va_list ap;
va_start(ap, tag);
int count = 0;
while (va_arg(ap, const char *) != 0) count++;
va_end(ap);
return count;
}

View File

@@ -0,0 +1,28 @@
// `#foreign` C-variadic tail: trailing `args: ..T` on a foreign fn maps
// to the C calling convention's `...`. Extras at the call site are
// passed via the variadic slot with the standard default argument
// promotion (s8/s16/bool → s32, f32 → f64) applied implicitly.
#import "modules/std.sx";
#import c {
#source "ffi-foreign-cvariadic.c";
};
sx_ffi_sum_ints :: (n: s32, args: ..s32) -> s64 #foreign;
sx_ffi_avg_doubles :: (n: s32, args: ..f64) -> f64 #foreign;
sx_ffi_count_args :: (tag: *u8, args: ..*u8) -> s32 #foreign;
main :: () -> s32 {
print("sum_ints(3, 10, 20, 30) = {}\n", sx_ffi_sum_ints(3, 10, 20, 30));
print("sum_ints(0) = {}\n", sx_ffi_sum_ints(0));
print("avg_doubles(2) = {}\n", sx_ffi_avg_doubles(2, 1.5, 2.5));
print("avg_doubles(3) = {}\n", sx_ffi_avg_doubles(3, 1.0, 2.0, 3.0));
a := "alpha".ptr;
b := "beta".ptr;
g := "gamma".ptr;
sentinel : *u8 = null;
print("count_args(3 strs) = {}\n", sx_ffi_count_args("tag".ptr, a, b, g, sentinel));
0;
}