kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

ffi.c (5450B)


      1 /* External (host-ABI) call marshaller.
      2  *
      3  * The engine has already classified arguments into integer-register and
      4  * fp-register slots per the call's ABIFuncInfo (sret pointer first, byval as a
      5  * pointer, aggregate-in-regs split into chunks). On the supported ABIs (SysV
      6  * x64, AAPCS64, RV64 LP64D) integer and fp arguments are assigned from
      7  * independent register sequences, so calling through one maximal prototype
      8  * `T(u64 x8, <fp> x8)` places the first `nint` integers and `nfp` fp values in
      9  * the correct registers regardless of their original interleaving; unused
     10  * trailing slots are zero and ignored by the callee.
     11  *
     12  * Two fp-argument shapes are needed because a 32-bit `float` and a 64-bit
     13  * `double` occupy the fp register differently (single vs double precision): the
     14  * engine picks `args_fp_is_float` when every fp arg is a 4-byte single, and the
     15  * dispatcher selects the `float`-parameter thunk family. A call mixing float
     16  * and double fp args within one signature is rejected upstream.
     17  *
     18  * Returns mirror this: a value comes back in one or two registers, classified
     19  * into int/fp parts. Two-register returns dispatch through struct-returning
     20  * thunks whose field types steer the ABI to the right return registers; the
     21  * caller copies each part's bytes into the aggregate destination.
     22  *
     23  * The casts deliberately mismatch the callee's real prototype — the classic
     24  * libffi-lite trick. That trips clang's -fsanitize=function, so the dispatcher
     25  * opts out of that one check (clang only; kit self-host never enables it). */
     26 
     27 #include <string.h>
     28 
     29 #include "core/core.h"
     30 #include "interp/interp.h"
     31 
     32 /* 8 integer + 8 fp register slots, fp as double or as single. */
     33 #define IPARAMS u64, u64, u64, u64, u64, u64, u64, u64
     34 #define DPARAMS double, double, double, double, double, double, double, double
     35 #define FPARAMS float, float, float, float, float, float, float, float
     36 
     37 #define IVALS(a)                                                             \
     38   (a)->iargs[0], (a)->iargs[1], (a)->iargs[2], (a)->iargs[3], (a)->iargs[4], \
     39       (a)->iargs[5], (a)->iargs[6], (a)->iargs[7]
     40 #define DVALS(a)                                                             \
     41   (a)->fargs[0], (a)->fargs[1], (a)->fargs[2], (a)->fargs[3], (a)->fargs[4], \
     42       (a)->fargs[5], (a)->fargs[6], (a)->fargs[7]
     43 #define FVALS(a)                                                      \
     44   (a)->fargs_f[0], (a)->fargs_f[1], (a)->fargs_f[2], (a)->fargs_f[3], \
     45       (a)->fargs_f[4], (a)->fargs_f[5], (a)->fargs_f[6], (a)->fargs_f[7]
     46 
     47 /* Two-register return shapes; the field types pick the return-register classes
     48  * (e.g. {u64,u64}=RAX,RDX / X0,X1; {u64,double}=RAX,XMM0 / X0,V0; etc). */
     49 typedef struct {
     50   u64 a, b;
     51 } R_ii;
     52 typedef struct {
     53   u64 a;
     54   double b;
     55 } R_id;
     56 typedef struct {
     57   double a;
     58   u64 b;
     59 } R_di;
     60 typedef struct {
     61   double a, b;
     62 } R_dd;
     63 
     64 /* double-fp-arg thunks, by return shape */
     65 typedef u64 (*t_i_d)(IPARAMS, DPARAMS);
     66 typedef double (*t_d_d)(IPARAMS, DPARAMS);
     67 typedef float (*t_f_d)(IPARAMS, DPARAMS);
     68 typedef R_ii (*t_ii_d)(IPARAMS, DPARAMS);
     69 typedef R_id (*t_id_d)(IPARAMS, DPARAMS);
     70 typedef R_di (*t_di_d)(IPARAMS, DPARAMS);
     71 typedef R_dd (*t_dd_d)(IPARAMS, DPARAMS);
     72 /* float-fp-arg thunks, scalar returns only (mixed signatures are rare) */
     73 typedef u64 (*t_i_f)(IPARAMS, FPARAMS);
     74 typedef double (*t_d_f)(IPARAMS, FPARAMS);
     75 typedef float (*t_f_f)(IPARAMS, FPARAMS);
     76 
     77 #if defined(__clang__)
     78 __attribute__((no_sanitize("function")))
     79 #endif
     80 int interp_ffi_invoke(void* fp, const InterpFfiArgs* a, u64 out[2],
     81                       const char** reason) {
     82   int flt;
     83   out[0] = 0;
     84   out[1] = 0;
     85   if (!fp) {
     86     *reason = "external call target is null";
     87     return 1;
     88   }
     89   if (a->nint > 8u || a->nfp > 8u) {
     90     *reason = "external call has too many register arguments";
     91     return 1;
     92   }
     93   flt = a->args_fp_is_float;
     94 
     95   /* void return */
     96   if (a->ret_is_void || a->ret_nparts == 0u) {
     97     if (flt)
     98       ((t_i_f)fp)(IVALS(a), FVALS(a));
     99     else
    100       ((t_i_d)fp)(IVALS(a), DVALS(a));
    101     return 0;
    102   }
    103 
    104   /* single-register return */
    105   if (a->ret_nparts == 1u) {
    106     if (a->ret_fp[0]) {
    107       if (a->ret_size[0] == 4u) {
    108         float r = flt ? ((t_f_f)fp)(IVALS(a), FVALS(a))
    109                       : ((t_f_d)fp)(IVALS(a), DVALS(a));
    110         u32 b;
    111         memcpy(&b, &r, 4);
    112         out[0] = b;
    113       } else {
    114         double r = flt ? ((t_d_f)fp)(IVALS(a), FVALS(a))
    115                        : ((t_d_d)fp)(IVALS(a), DVALS(a));
    116         memcpy(&out[0], &r, 8);
    117       }
    118     } else {
    119       out[0] = flt ? ((t_i_f)fp)(IVALS(a), FVALS(a))
    120                    : ((t_i_d)fp)(IVALS(a), DVALS(a));
    121     }
    122     return 0;
    123   }
    124 
    125   /* two-register return. A float fp arg combined with a struct return is rare;
    126    * require double fp args here (engine also rejects 4-byte fp return parts).
    127    */
    128   if (flt) {
    129     *reason = "external call: float args with multi-register return";
    130     return 1;
    131   }
    132   if (!a->ret_fp[0] && !a->ret_fp[1]) {
    133     R_ii r = ((t_ii_d)fp)(IVALS(a), DVALS(a));
    134     out[0] = r.a;
    135     out[1] = r.b;
    136   } else if (!a->ret_fp[0] && a->ret_fp[1]) {
    137     R_id r = ((t_id_d)fp)(IVALS(a), DVALS(a));
    138     out[0] = r.a;
    139     memcpy(&out[1], &r.b, 8);
    140   } else if (a->ret_fp[0] && !a->ret_fp[1]) {
    141     R_di r = ((t_di_d)fp)(IVALS(a), DVALS(a));
    142     memcpy(&out[0], &r.a, 8);
    143     out[1] = r.b;
    144   } else {
    145     R_dd r = ((t_dd_d)fp)(IVALS(a), DVALS(a));
    146     memcpy(&out[0], &r.a, 8);
    147     memcpy(&out[1], &r.b, 8);
    148   }
    149   return 0;
    150 }