static VALUE struct_layout_initialize(VALUE self, VALUE field_names, VALUE fields, VALUE size, VALUE align) { StructLayout* layout; ffi_type* ltype; int i; Data_Get_Struct(self, StructLayout, layout); layout->rbFieldMap = rb_hash_new(); layout->rbFieldNames = rb_ary_dup(field_names); layout->size = NUM2INT(size); layout->align = NUM2INT(align); layout->fieldCount = RARRAY_LEN(field_names); layout->fields = xcalloc(layout->fieldCount, sizeof(StructField *)); layout->ffiTypes = xcalloc(layout->fieldCount + 1, sizeof(ffi_type *)); layout->rbFields = rb_ary_new2(layout->fieldCount); layout->base.ffiType->elements = layout->ffiTypes; layout->base.ffiType->size = 0; layout->base.ffiType->alignment = 1; ltype = layout->base.ffiType; for (i = 0; i < (int) layout->fieldCount; ++i) { VALUE rbName = rb_ary_entry(field_names, i); VALUE rbField = rb_hash_aref(fields, rbName); StructField* field; ffi_type* ftype; if (!rb_obj_is_kind_of(rbField, rbffi_StructLayoutFieldClass)) { rb_raise(rb_eTypeError, "wrong type for field %d.", i); } Data_Get_Struct(rbField, StructField, field = layout->fields[i]); if (field->type == NULL || field->type->ffiType == NULL) { rb_raise(rb_eRuntimeError, "type of field %d not supported", i); } ftype = field->type->ffiType; if (ftype->size == 0) { rb_raise(rb_eTypeError, "type of field %d has zero size", i); } rb_hash_aset(layout->rbFieldMap, rbName, rbField); layout->ffiTypes[i] = ftype; rb_ary_push(layout->rbFields, rbField); ltype->size = MAX(ltype->size, field->offset + ftype->size); ltype->alignment = MAX(ltype->alignment, ftype->alignment); } if (ltype->size == 0) { rb_raise(rb_eRuntimeError, "Struct size is zero"); } // Include tail padding ltype->size = FFI_ALIGN(ltype->size, ltype->alignment); return self; }
static void * ffi_align (ffi_type *ty, void *p) { /* Align if necessary */ size_t alignment; #ifdef _WIN32_WCE alignment = 4; #else alignment = ty->alignment; if (alignment < 4) alignment = 4; #endif return (void *) FFI_ALIGN (p, alignment); }
static size_t ffi_put_arg (ffi_type *ty, void *src, void *dst) { size_t z = ty->size; switch (ty->type) { case FFI_TYPE_SINT8: *(UINT32 *)dst = *(SINT8 *)src; break; case FFI_TYPE_UINT8: *(UINT32 *)dst = *(UINT8 *)src; break; case FFI_TYPE_SINT16: *(UINT32 *)dst = *(SINT16 *)src; break; case FFI_TYPE_UINT16: *(UINT32 *)dst = *(UINT16 *)src; break; case FFI_TYPE_INT: case FFI_TYPE_SINT32: case FFI_TYPE_UINT32: case FFI_TYPE_POINTER: case FFI_TYPE_FLOAT: *(UINT32 *)dst = *(UINT32 *)src; break; case FFI_TYPE_SINT64: case FFI_TYPE_UINT64: case FFI_TYPE_DOUBLE: *(UINT64 *)dst = *(UINT64 *)src; break; case FFI_TYPE_STRUCT: case FFI_TYPE_COMPLEX: memcpy (dst, src, z); break; default: abort(); } return FFI_ALIGN (z, 4); }
/* * call-seq: initialize(fields, size, align) * @param [Array<StructLayout::Field>] fields * @param [Numeric] size * @param [Numeric] align * @return [self] * A new StructLayout instance. */ static VALUE struct_layout_initialize(VALUE self, VALUE fields, VALUE size, VALUE align) { StructLayout* layout; ffi_type* ltype; int i; Data_Get_Struct(self, StructLayout, layout); layout->fieldCount = (int) RARRAY_LEN(fields); layout->rbFieldMap = rb_hash_new(); layout->rbFieldNames = rb_ary_new2(layout->fieldCount); layout->size = (int) FFI_ALIGN(NUM2INT(size), NUM2INT(align)); layout->align = NUM2INT(align); layout->fields = xcalloc(layout->fieldCount, sizeof(StructField *)); layout->ffiTypes = xcalloc(layout->fieldCount + 1, sizeof(ffi_type *)); layout->rbFields = rb_ary_new2(layout->fieldCount); layout->referenceFieldCount = 0; layout->base.ffiType->elements = layout->ffiTypes; layout->base.ffiType->size = layout->size; layout->base.ffiType->alignment = layout->align; ltype = layout->base.ffiType; for (i = 0; i < (int) layout->fieldCount; ++i) { VALUE rbField = rb_ary_entry(fields, i); VALUE rbName; StructField* field; ffi_type* ftype; if (!rb_obj_is_kind_of(rbField, rbffi_StructLayoutFieldClass)) { rb_raise(rb_eTypeError, "wrong type for field %d.", i); } rbName = rb_funcall2(rbField, rb_intern("name"), 0, NULL); Data_Get_Struct(rbField, StructField, field); layout->fields[i] = field; if (field->type == NULL || field->type->ffiType == NULL) { rb_raise(rb_eRuntimeError, "type of field %d not supported", i); } ftype = field->type->ffiType; if (ftype->size == 0 && i < ((int) layout->fieldCount - 1)) { rb_raise(rb_eTypeError, "type of field %d has zero size", i); } if (field->referenceRequired) { field->referenceIndex = layout->referenceFieldCount++; } layout->ffiTypes[i] = ftype->size > 0 ? ftype : NULL; st_insert(layout->fieldSymbolTable, rbName, rbField); rb_hash_aset(layout->rbFieldMap, rbName, rbField); rb_ary_push(layout->rbFields, rbField); rb_ary_push(layout->rbFieldNames, rbName); } if (ltype->size == 0) { rb_raise(rb_eRuntimeError, "Struct size is zero"); } return self; }
/* Perform machine dependent cif processing. */ ffi_status FFI_HIDDEN ffi_prep_cif_machdep(ffi_cif *cif) { size_t bytes = 0; int i, n, flags, cabi = cif->abi; switch (cabi) { case FFI_SYSV: case FFI_STDCALL: case FFI_THISCALL: case FFI_FASTCALL: case FFI_MS_CDECL: case FFI_PASCAL: case FFI_REGISTER: break; default: return FFI_BAD_ABI; } switch (cif->rtype->type) { case FFI_TYPE_VOID: flags = X86_RET_VOID; break; case FFI_TYPE_FLOAT: flags = X86_RET_FLOAT; break; case FFI_TYPE_DOUBLE: flags = X86_RET_DOUBLE; break; case FFI_TYPE_LONGDOUBLE: flags = X86_RET_LDOUBLE; break; case FFI_TYPE_UINT8: flags = X86_RET_UINT8; break; case FFI_TYPE_UINT16: flags = X86_RET_UINT16; break; case FFI_TYPE_SINT8: flags = X86_RET_SINT8; break; case FFI_TYPE_SINT16: flags = X86_RET_SINT16; break; case FFI_TYPE_INT: case FFI_TYPE_SINT32: case FFI_TYPE_UINT32: case FFI_TYPE_POINTER: flags = X86_RET_INT32; break; case FFI_TYPE_SINT64: case FFI_TYPE_UINT64: flags = X86_RET_INT64; break; case FFI_TYPE_STRUCT: #ifndef X86 /* ??? This should be a different ABI rather than an ifdef. */ if (cif->rtype->size == 1) flags = X86_RET_STRUCT_1B; else if (cif->rtype->size == 2) flags = X86_RET_STRUCT_2B; else if (cif->rtype->size == 4) flags = X86_RET_INT32; else if (cif->rtype->size == 8) flags = X86_RET_INT64; else #endif { do_struct: switch (cabi) { case FFI_THISCALL: case FFI_FASTCALL: case FFI_STDCALL: case FFI_MS_CDECL: flags = X86_RET_STRUCTARG; break; default: flags = X86_RET_STRUCTPOP; break; } /* Allocate space for return value pointer. */ bytes += FFI_ALIGN (sizeof(void*), FFI_SIZEOF_ARG); } break; case FFI_TYPE_COMPLEX: switch (cif->rtype->elements[0]->type) { case FFI_TYPE_DOUBLE: case FFI_TYPE_LONGDOUBLE: case FFI_TYPE_SINT64: case FFI_TYPE_UINT64: goto do_struct; case FFI_TYPE_FLOAT: case FFI_TYPE_INT: case FFI_TYPE_SINT32: case FFI_TYPE_UINT32: flags = X86_RET_INT64; break; case FFI_TYPE_SINT16: case FFI_TYPE_UINT16: flags = X86_RET_INT32; break; case FFI_TYPE_SINT8: case FFI_TYPE_UINT8: flags = X86_RET_STRUCT_2B; break; default: return FFI_BAD_TYPEDEF; } break; default: return FFI_BAD_TYPEDEF; } cif->flags = flags; for (i = 0, n = cif->nargs; i < n; i++) { ffi_type *t = cif->arg_types[i]; bytes = FFI_ALIGN (bytes, t->alignment); bytes += FFI_ALIGN (t->size, FFI_SIZEOF_ARG); } cif->bytes = FFI_ALIGN (bytes, 16); return FFI_OK; }
/* Perform machine dependent cif processing */ ffi_status ffi_prep_cif_machdep (ffi_cif *cif) { int flags = 0, cabi = cif->abi; size_t bytes = cif->bytes; /* Map out the register placements of VFP register args. The VFP hard-float calling conventions are slightly more sophisticated than the base calling conventions, so we do it here instead of in ffi_prep_args(). */ if (cabi == FFI_VFP) layout_vfp_args (cif); /* Set the return type flag */ switch (cif->rtype->type) { case FFI_TYPE_VOID: flags = ARM_TYPE_VOID; break; case FFI_TYPE_INT: case FFI_TYPE_UINT8: case FFI_TYPE_SINT8: case FFI_TYPE_UINT16: case FFI_TYPE_SINT16: case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: case FFI_TYPE_POINTER: flags = ARM_TYPE_INT; break; case FFI_TYPE_SINT64: case FFI_TYPE_UINT64: flags = ARM_TYPE_INT64; break; case FFI_TYPE_FLOAT: flags = (cabi == FFI_VFP ? ARM_TYPE_VFP_S : ARM_TYPE_INT); break; case FFI_TYPE_DOUBLE: flags = (cabi == FFI_VFP ? ARM_TYPE_VFP_D : ARM_TYPE_INT64); break; case FFI_TYPE_STRUCT: case FFI_TYPE_COMPLEX: if (cabi == FFI_VFP) { int h = vfp_type_p (cif->rtype); flags = ARM_TYPE_VFP_N; if (h == 0x100 + FFI_TYPE_FLOAT) flags = ARM_TYPE_VFP_S; if (h == 0x100 + FFI_TYPE_DOUBLE) flags = ARM_TYPE_VFP_D; if (h != 0) break; } /* A Composite Type not larger than 4 bytes is returned in r0. A Composite Type larger than 4 bytes, or whose size cannot be determined statically ... is stored in memory at an address passed [in r0]. */ if (cif->rtype->size <= 4) flags = ARM_TYPE_INT; else { flags = ARM_TYPE_STRUCT; bytes += 4; } break; default: abort(); } /* Round the stack up to a multiple of 8 bytes. This isn't needed everywhere, but it is on some platforms, and it doesn't harm anything when it isn't needed. */ bytes = FFI_ALIGN (bytes, 8); /* Minimum stack space is the 4 register arguments that we pop. */ if (bytes < 4*4) bytes = 4*4; cif->bytes = bytes; cif->flags = flags; return FFI_OK; }
int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_cif *cif, void (*fun) (ffi_cif *, void *, void **, void *), void *user_data, void *rvalue, unsigned long *pst, ffi_dblfl *pfr) { /* rvalue is the pointer to space for return value in closure assembly */ /* pst is the pointer to parameter save area (r3-r10 are stored into its first 8 slots by ffi_closure_LINUX64) */ /* pfr is the pointer to where f1-f13 are stored in ffi_closure_LINUX64 */ void **avalue; ffi_type **arg_types; unsigned long i, avn, nfixedargs; ffi_dblfl *end_pfr = pfr + NUM_FPR_ARG_REGISTERS64; unsigned long align; avalue = alloca (cif->nargs * sizeof (void *)); /* Copy the caller's structure return value address so that the closure returns the data directly to the caller. */ if (cif->rtype->type == FFI_TYPE_STRUCT && (cif->flags & FLAG_RETURNS_SMST) == 0) { rvalue = (void *) *pst; pst++; } i = 0; avn = cif->nargs; #if _CALL_ELF != 2 nfixedargs = (unsigned) -1; if ((cif->flags & FLAG_COMPAT) == 0) #endif nfixedargs = cif->nfixedargs; arg_types = cif->arg_types; /* Grab the addresses of the arguments from the stack frame. */ while (i < avn) { unsigned int elt, elnum; switch (arg_types[i]->type) { case FFI_TYPE_SINT8: case FFI_TYPE_UINT8: #ifndef __LITTLE_ENDIAN__ avalue[i] = (char *) pst + 7; pst++; break; #endif case FFI_TYPE_SINT16: case FFI_TYPE_UINT16: #ifndef __LITTLE_ENDIAN__ avalue[i] = (char *) pst + 6; pst++; break; #endif case FFI_TYPE_SINT32: case FFI_TYPE_UINT32: #ifndef __LITTLE_ENDIAN__ avalue[i] = (char *) pst + 4; pst++; break; #endif case FFI_TYPE_SINT64: case FFI_TYPE_UINT64: case FFI_TYPE_POINTER: avalue[i] = pst; pst++; break; case FFI_TYPE_STRUCT: if ((cif->abi & FFI_LINUX_STRUCT_ALIGN) != 0) { align = arg_types[i]->alignment; if (align > 16) align = 16; if (align > 1) pst = (unsigned long *) FFI_ALIGN ((size_t) pst, align); } elt = discover_homogeneous_aggregate (arg_types[i], &elnum); if (elt) { #if _CALL_ELF == 2 union { void *v; unsigned long *ul; float *f; double *d; size_t p; } to, from; /* Repackage the aggregate from its parts. The aggregate size is not greater than the space taken by the registers so store back to the register/parameter save arrays. */ if (pfr + elnum <= end_pfr) to.v = pfr; else to.v = pst; avalue[i] = to.v; from.ul = pst; if (elt == FFI_TYPE_FLOAT) { do { if (pfr < end_pfr && i < nfixedargs) { *to.f = (float) pfr->d; pfr++; } else *to.f = *from.f; to.f++; from.f++; } while (--elnum != 0); } else { do { if (pfr < end_pfr && i < nfixedargs) { *to.d = pfr->d; pfr++; } else *to.d = *from.d; to.d++; from.d++; } while (--elnum != 0); } #else if (elt == FFI_TYPE_FLOAT) goto do_float; else goto do_double; #endif } else { #ifndef __LITTLE_ENDIAN__ /* Structures with size less than eight bytes are passed left-padded. */ if (arg_types[i]->size < 8) avalue[i] = (char *) pst + 8 - arg_types[i]->size; else #endif avalue[i] = pst; } pst += (arg_types[i]->size + 7) / 8; break; #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) { if (pfr + 1 < end_pfr && i + 1 < nfixedargs) { avalue[i] = pfr; pfr += 2; } else { if (pfr < end_pfr && i < nfixedargs) { /* Passed partly in f13 and partly on the stack. Move it all to the stack. */ *pst = *(unsigned long *) pfr; pfr++; } avalue[i] = pst; } pst += 2; break; } /* Fall through. */ #endif case FFI_TYPE_DOUBLE: do_double: /* On the outgoing stack all values are aligned to 8 */ /* there are 13 64bit floating point registers */ if (pfr < end_pfr && i < nfixedargs) { avalue[i] = pfr; pfr++; } else avalue[i] = pst; pst++; break; case FFI_TYPE_FLOAT: do_float: if (pfr < end_pfr && i < nfixedargs) { /* Float values are stored as doubles in the ffi_closure_LINUX64 code. Fix them here. */ pfr->f = (float) pfr->d; avalue[i] = pfr; pfr++; } else { #ifndef __LITTLE_ENDIAN__ avalue[i] = (char *) pst + 4; #else avalue[i] = pst; #endif } pst++; break; default: FFI_ASSERT (0); } i++; } (*fun) (cif, rvalue, avalue, user_data); /* Tell ffi_closure_LINUX64 how to perform return type promotions. */ if ((cif->flags & FLAG_RETURNS_SMST) != 0) { if ((cif->flags & FLAG_RETURNS_FP) == 0) return FFI_V2_TYPE_SMALL_STRUCT + cif->rtype->size - 1; else if ((cif->flags & FLAG_RETURNS_64BITS) != 0) return FFI_V2_TYPE_DOUBLE_HOMOG; else return FFI_V2_TYPE_FLOAT_HOMOG; } return cif->rtype->type; }
void FFI_HIDDEN ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack) { const unsigned long bytes = ecif->cif->bytes; const unsigned long flags = ecif->cif->flags; typedef union { char *c; unsigned long *ul; float *f; double *d; size_t p; } valp; /* 'stacktop' points at the previous backchain pointer. */ valp stacktop; /* 'next_arg' points at the space for gpr3, and grows upwards as we use GPR registers, then continues at rest. */ valp gpr_base; valp gpr_end; valp rest; valp next_arg; /* 'fpr_base' points at the space for fpr3, and grows upwards as we use FPR registers. */ valp fpr_base; unsigned int fparg_count; unsigned int i, words, nargs, nfixedargs; ffi_type **ptr; double double_tmp; union { void **v; char **c; signed char **sc; unsigned char **uc; signed short **ss; unsigned short **us; signed int **si; unsigned int **ui; unsigned long **ul; float **f; double **d; } p_argv; unsigned long gprvalue; unsigned long align; stacktop.c = (char *) stack + bytes; gpr_base.ul = stacktop.ul - ASM_NEEDS_REGISTERS64 - NUM_GPR_ARG_REGISTERS64; gpr_end.ul = gpr_base.ul + NUM_GPR_ARG_REGISTERS64; #if _CALL_ELF == 2 rest.ul = stack + 4 + NUM_GPR_ARG_REGISTERS64; #else rest.ul = stack + 6 + NUM_GPR_ARG_REGISTERS64; #endif fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS64; fparg_count = 0; next_arg.ul = gpr_base.ul; /* Check that everything starts aligned properly. */ FFI_ASSERT (((unsigned long) (char *) stack & 0xF) == 0); FFI_ASSERT (((unsigned long) stacktop.c & 0xF) == 0); FFI_ASSERT ((bytes & 0xF) == 0); /* Deal with return values that are actually pass-by-reference. */ if (flags & FLAG_RETVAL_REFERENCE) *next_arg.ul++ = (unsigned long) (char *) ecif->rvalue; /* Now for the arguments. */ p_argv.v = ecif->avalue; nargs = ecif->cif->nargs; #if _CALL_ELF != 2 nfixedargs = (unsigned) -1; if ((flags & FLAG_COMPAT) == 0) #endif nfixedargs = ecif->cif->nfixedargs; for (ptr = ecif->cif->arg_types, i = 0; i < nargs; i++, ptr++, p_argv.v++) { unsigned int elt, elnum; switch ((*ptr)->type) { #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: if ((ecif->cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) { double_tmp = (*p_argv.d)[0]; if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) { *fpr_base.d++ = double_tmp; # if _CALL_ELF != 2 if ((flags & FLAG_COMPAT) != 0) *next_arg.d = double_tmp; # endif } else *next_arg.d = double_tmp; if (++next_arg.ul == gpr_end.ul) next_arg.ul = rest.ul; fparg_count++; double_tmp = (*p_argv.d)[1]; if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) { *fpr_base.d++ = double_tmp; # if _CALL_ELF != 2 if ((flags & FLAG_COMPAT) != 0) *next_arg.d = double_tmp; # endif } else *next_arg.d = double_tmp; if (++next_arg.ul == gpr_end.ul) next_arg.ul = rest.ul; fparg_count++; FFI_ASSERT (__LDBL_MANT_DIG__ == 106); FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); break; } /* Fall through. */ #endif case FFI_TYPE_DOUBLE: do_double: double_tmp = **p_argv.d; if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) { *fpr_base.d++ = double_tmp; #if _CALL_ELF != 2 if ((flags & FLAG_COMPAT) != 0) *next_arg.d = double_tmp; #endif } else *next_arg.d = double_tmp; if (++next_arg.ul == gpr_end.ul) next_arg.ul = rest.ul; fparg_count++; FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); break; case FFI_TYPE_FLOAT: do_float: double_tmp = **p_argv.f; if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) { *fpr_base.d++ = double_tmp; #if _CALL_ELF != 2 if ((flags & FLAG_COMPAT) != 0) { # ifndef __LITTLE_ENDIAN__ next_arg.f[1] = (float) double_tmp; # else next_arg.f[0] = (float) double_tmp; # endif } #endif } else { # ifndef __LITTLE_ENDIAN__ next_arg.f[1] = (float) double_tmp; # else next_arg.f[0] = (float) double_tmp; # endif } if (++next_arg.ul == gpr_end.ul) next_arg.ul = rest.ul; fparg_count++; FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); break; case FFI_TYPE_STRUCT: if ((ecif->cif->abi & FFI_LINUX_STRUCT_ALIGN) != 0) { align = (*ptr)->alignment; if (align > 16) align = 16; if (align > 1) next_arg.p = FFI_ALIGN (next_arg.p, align); } elt = discover_homogeneous_aggregate (*ptr, &elnum); if (elt) { #if _CALL_ELF == 2 union { void *v; float *f; double *d; } arg; arg.v = *p_argv.v; if (elt == FFI_TYPE_FLOAT) { do { double_tmp = *arg.f++; if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) *fpr_base.d++ = double_tmp; else *next_arg.f = (float) double_tmp; if (++next_arg.f == gpr_end.f) next_arg.f = rest.f; fparg_count++; } while (--elnum != 0); if ((next_arg.p & 3) != 0) { if (++next_arg.f == gpr_end.f) next_arg.f = rest.f; } } else do { double_tmp = *arg.d++; if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) *fpr_base.d++ = double_tmp; else *next_arg.d = double_tmp; if (++next_arg.d == gpr_end.d) next_arg.d = rest.d; fparg_count++; } while (--elnum != 0); #else if (elt == FFI_TYPE_FLOAT) goto do_float; else goto do_double; #endif } else { words = ((*ptr)->size + 7) / 8; if (next_arg.ul >= gpr_base.ul && next_arg.ul + words > gpr_end.ul) { size_t first = gpr_end.c - next_arg.c; memcpy (next_arg.c, *p_argv.c, first); memcpy (rest.c, *p_argv.c + first, (*ptr)->size - first); next_arg.c = rest.c + words * 8 - first; } else { char *where = next_arg.c; #ifndef __LITTLE_ENDIAN__ /* Structures with size less than eight bytes are passed left-padded. */ if ((*ptr)->size < 8) where += 8 - (*ptr)->size; #endif memcpy (where, *p_argv.c, (*ptr)->size); next_arg.ul += words; if (next_arg.ul == gpr_end.ul) next_arg.ul = rest.ul; } } break; case FFI_TYPE_UINT8: gprvalue = **p_argv.uc; goto putgpr; case FFI_TYPE_SINT8: gprvalue = **p_argv.sc; goto putgpr; case FFI_TYPE_UINT16: gprvalue = **p_argv.us; goto putgpr; case FFI_TYPE_SINT16: gprvalue = **p_argv.ss; goto putgpr; case FFI_TYPE_UINT32: gprvalue = **p_argv.ui; goto putgpr; case FFI_TYPE_INT: case FFI_TYPE_SINT32: gprvalue = **p_argv.si; goto putgpr; case FFI_TYPE_UINT64: case FFI_TYPE_SINT64: case FFI_TYPE_POINTER: gprvalue = **p_argv.ul; putgpr: *next_arg.ul++ = gprvalue; if (next_arg.ul == gpr_end.ul) next_arg.ul = rest.ul; break; } } FFI_ASSERT (flags & FLAG_4_GPR_ARGUMENTS || (next_arg.ul >= gpr_base.ul && next_arg.ul <= gpr_base.ul + 4)); }
/* Perform machine dependent cif processing */ static ffi_status ffi_prep_cif_linux64_core (ffi_cif *cif) { ffi_type **ptr; unsigned bytes; unsigned i, fparg_count = 0, intarg_count = 0; unsigned flags = cif->flags; unsigned int elt, elnum; #if FFI_TYPE_LONGDOUBLE == FFI_TYPE_DOUBLE /* If compiled without long double support.. */ if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) return FFI_BAD_ABI; #endif /* The machine-independent calculation of cif->bytes doesn't work for us. Redo the calculation. */ #if _CALL_ELF == 2 /* Space for backchain, CR, LR, TOC and the asm's temp regs. */ bytes = (4 + ASM_NEEDS_REGISTERS64) * sizeof (long); /* Space for the general registers. */ bytes += NUM_GPR_ARG_REGISTERS64 * sizeof (long); #else /* Space for backchain, CR, LR, cc/ld doubleword, TOC and the asm's temp regs. */ bytes = (6 + ASM_NEEDS_REGISTERS64) * sizeof (long); /* Space for the mandatory parm save area and general registers. */ bytes += 2 * NUM_GPR_ARG_REGISTERS64 * sizeof (long); #endif /* Return value handling. */ switch (cif->rtype->type) { #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) flags |= FLAG_RETURNS_128BITS; /* Fall through. */ #endif case FFI_TYPE_DOUBLE: flags |= FLAG_RETURNS_64BITS; /* Fall through. */ case FFI_TYPE_FLOAT: flags |= FLAG_RETURNS_FP; break; case FFI_TYPE_UINT128: flags |= FLAG_RETURNS_128BITS; /* Fall through. */ case FFI_TYPE_UINT64: case FFI_TYPE_SINT64: case FFI_TYPE_POINTER: flags |= FLAG_RETURNS_64BITS; break; case FFI_TYPE_STRUCT: #if _CALL_ELF == 2 elt = discover_homogeneous_aggregate (cif->rtype, &elnum); if (elt) { if (elt == FFI_TYPE_DOUBLE) flags |= FLAG_RETURNS_64BITS; flags |= FLAG_RETURNS_FP | FLAG_RETURNS_SMST; break; } if (cif->rtype->size <= 16) { flags |= FLAG_RETURNS_SMST; break; } #endif intarg_count++; flags |= FLAG_RETVAL_REFERENCE; /* Fall through. */ case FFI_TYPE_VOID: flags |= FLAG_RETURNS_NOTHING; break; default: /* Returns 32-bit integer, or similar. Nothing to do here. */ break; } for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) { unsigned int align; switch ((*ptr)->type) { #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE case FFI_TYPE_LONGDOUBLE: if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) { fparg_count++; intarg_count++; } /* Fall through. */ #endif case FFI_TYPE_DOUBLE: case FFI_TYPE_FLOAT: fparg_count++; intarg_count++; if (fparg_count > NUM_FPR_ARG_REGISTERS64) flags |= FLAG_ARG_NEEDS_PSAVE; break; case FFI_TYPE_STRUCT: if ((cif->abi & FFI_LINUX_STRUCT_ALIGN) != 0) { align = (*ptr)->alignment; if (align > 16) align = 16; align = align / 8; if (align > 1) intarg_count = FFI_ALIGN (intarg_count, align); } intarg_count += ((*ptr)->size + 7) / 8; elt = discover_homogeneous_aggregate (*ptr, &elnum); if (elt) { fparg_count += elnum; if (fparg_count > NUM_FPR_ARG_REGISTERS64) flags |= FLAG_ARG_NEEDS_PSAVE; } else { if (intarg_count > NUM_GPR_ARG_REGISTERS64) flags |= FLAG_ARG_NEEDS_PSAVE; } break; case FFI_TYPE_POINTER: case FFI_TYPE_UINT64: case FFI_TYPE_SINT64: case FFI_TYPE_INT: case FFI_TYPE_UINT32: case FFI_TYPE_SINT32: case FFI_TYPE_UINT16: case FFI_TYPE_SINT16: case FFI_TYPE_UINT8: case FFI_TYPE_SINT8: /* Everything else is passed as a 8-byte word in a GPR, either the object itself or a pointer to it. */ intarg_count++; if (intarg_count > NUM_GPR_ARG_REGISTERS64) flags |= FLAG_ARG_NEEDS_PSAVE; break; default: FFI_ASSERT (0); } } if (fparg_count != 0) flags |= FLAG_FP_ARGUMENTS; if (intarg_count > 4) flags |= FLAG_4_GPR_ARGUMENTS; /* Space for the FPR registers, if needed. */ if (fparg_count != 0) bytes += NUM_FPR_ARG_REGISTERS64 * sizeof (double); /* Stack space. */ #if _CALL_ELF == 2 if ((flags & FLAG_ARG_NEEDS_PSAVE) != 0) bytes += intarg_count * sizeof (long); #else if (intarg_count > NUM_GPR_ARG_REGISTERS64) bytes += (intarg_count - NUM_GPR_ARG_REGISTERS64) * sizeof (long); #endif /* The stack space allocated needs to be a multiple of 16 bytes. */ bytes = (bytes + 15) & ~0xF; cif->flags = flags; cif->bytes = bytes; return FFI_OK; }