1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: abi.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_bridges.hxx"
34 // This is an implementation of the x86-64 ABI as described in 'System V
35 // Application Binary Interface, AMD64 Architecture Processor Supplement'
36 // (http://www.x86-64.org/documentation/abi-0.95.pdf)
38 // The code in this file is a modification of src/x86/ffi64.c from libffi
39 // (http://sources.redhat.com/libffi/) which is under the following license:
41 /* -----------------------------------------------------------------------
42 ffi.c - Copyright (c) 2002 Bo Thorsen <bo@suse.de>
44 x86-64 Foreign Function Interface
46 Permission is hereby granted, free of charge, to any person obtaining
47 a copy of this software and associated documentation files (the
48 ``Software''), to deal in the Software without restriction, including
49 without limitation the rights to use, copy, modify, merge, publish,
50 distribute, sublicense, and/or sell copies of the Software, and to
51 permit persons to whom the Software is furnished to do so, subject to
52 the following conditions:
54 The above copyright notice and this permission notice shall be included
55 in all copies or substantial portions of the Software.
57 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
58 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
59 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
60 IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
61 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
62 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
63 OTHER DEALINGS IN THE SOFTWARE.
64 ----------------------------------------------------------------------- */
68 #include <rtl/ustring.hxx>
70 using namespace x86_64
;
74 /* Registers for argument passing. */
75 long gpr
[MAX_GPR_REGS
];
76 __int128_t sse
[MAX_SSE_REGS
];
78 /* Stack space for arguments. */
82 /* Register class used for passing given 64bit part of the argument.
83 These represent classes as documented by the PS ABI, with the exception
84 of SSESF, SSEDF classes, that are basically SSE class, just gcc will
85 use SF or DFmode move instead of DImode to avoid reformating penalties.
87 Similary we play games with INTEGERSI_CLASS to use cheaper SImode moves
88 whenever possible (upper half does contain padding).
94 X86_64_INTEGERSI_CLASS
,
104 #define MAX_CLASSES 4
106 #define ALIGN(v, a) (((((size_t) (v))-1) | ((a)-1))+1)
108 /* x86-64 register passing implementation. See x86-64 ABI for details. Goal
109 of this code is to classify each 8bytes of incoming argument by the register
110 class and assign registers accordingly. */
112 /* Return the union class of CLASS1 and CLASS2.
113 See the x86-64 PS ABI for details. */
115 static enum x86_64_reg_class
116 merge_classes (enum x86_64_reg_class class1
, enum x86_64_reg_class class2
)
118 /* Rule #1: If both classes are equal, this is the resulting class. */
119 if (class1
== class2
)
122 /* Rule #2: If one of the classes is NO_CLASS, the resulting class is
124 if (class1
== X86_64_NO_CLASS
)
126 if (class2
== X86_64_NO_CLASS
)
129 /* Rule #3: If one of the classes is MEMORY, the result is MEMORY. */
130 if (class1
== X86_64_MEMORY_CLASS
|| class2
== X86_64_MEMORY_CLASS
)
131 return X86_64_MEMORY_CLASS
;
133 /* Rule #4: If one of the classes is INTEGER, the result is INTEGER. */
134 if ((class1
== X86_64_INTEGERSI_CLASS
&& class2
== X86_64_SSESF_CLASS
)
135 || (class2
== X86_64_INTEGERSI_CLASS
&& class1
== X86_64_SSESF_CLASS
))
136 return X86_64_INTEGERSI_CLASS
;
137 if (class1
== X86_64_INTEGER_CLASS
|| class1
== X86_64_INTEGERSI_CLASS
138 || class2
== X86_64_INTEGER_CLASS
|| class2
== X86_64_INTEGERSI_CLASS
)
139 return X86_64_INTEGER_CLASS
;
141 /* Rule #5: If one of the classes is X87 or X87UP class, MEMORY is used. */
142 if (class1
== X86_64_X87_CLASS
|| class1
== X86_64_X87UP_CLASS
143 || class2
== X86_64_X87_CLASS
|| class2
== X86_64_X87UP_CLASS
)
144 return X86_64_MEMORY_CLASS
;
146 /* Rule #6: Otherwise class SSE is used. */
147 return X86_64_SSE_CLASS
;
150 /* Classify the argument of type TYPE and mode MODE.
151 CLASSES will be filled by the register class used to pass each word
152 of the operand. The number of words is returned. In case the parameter
153 should be passed in memory, 0 is returned. As a special case for zero
154 sized containers, classes[0] will be NO_CLASS and 1 is returned.
156 See the x86-64 PS ABI for details.
159 classify_argument( typelib_TypeDescriptionReference
*pTypeRef
, enum x86_64_reg_class classes
[], int &rByteOffset
)
161 /* First, align to the right place. */
162 rByteOffset
= ALIGN( rByteOffset
, pTypeRef
->pType
->nAlignment
);
164 switch ( pTypeRef
->eTypeClass
)
166 case typelib_TypeClass_VOID
:
167 classes
[0] = X86_64_NO_CLASS
;
169 case typelib_TypeClass_CHAR
:
170 case typelib_TypeClass_BOOLEAN
:
171 case typelib_TypeClass_BYTE
:
172 case typelib_TypeClass_SHORT
:
173 case typelib_TypeClass_UNSIGNED_SHORT
:
174 case typelib_TypeClass_LONG
:
175 case typelib_TypeClass_UNSIGNED_LONG
:
176 case typelib_TypeClass_HYPER
:
177 case typelib_TypeClass_UNSIGNED_HYPER
:
178 case typelib_TypeClass_ENUM
:
179 if ( ( rByteOffset
% 8 + pTypeRef
->pType
->nSize
) <= 4 )
180 classes
[0] = X86_64_INTEGERSI_CLASS
;
182 classes
[0] = X86_64_INTEGER_CLASS
;
184 case typelib_TypeClass_FLOAT
:
185 if ( ( rByteOffset
% 8 ) == 0 )
186 classes
[0] = X86_64_SSESF_CLASS
;
188 classes
[0] = X86_64_SSE_CLASS
;
190 case typelib_TypeClass_DOUBLE
:
191 classes
[0] = X86_64_SSEDF_CLASS
;
194 classes[0] = X86_64_X87_CLASS;
195 classes[1] = X86_64_X87UP_CLASS;
197 case typelib_TypeClass_STRING
:
198 case typelib_TypeClass_TYPE
:
199 case typelib_TypeClass_ANY
:
200 case typelib_TypeClass_TYPEDEF
:
201 case typelib_TypeClass_UNION
:
202 case typelib_TypeClass_SEQUENCE
:
203 case typelib_TypeClass_ARRAY
:
204 case typelib_TypeClass_INTERFACE
:
206 case typelib_TypeClass_STRUCT
:
207 case typelib_TypeClass_EXCEPTION
:
209 typelib_TypeDescription
* pTypeDescr
= 0;
210 TYPELIB_DANGER_GET( &pTypeDescr
, pTypeRef
);
212 const int UNITS_PER_WORD
= 8;
213 int words
= ( pTypeDescr
->nSize
+ UNITS_PER_WORD
- 1 ) / UNITS_PER_WORD
;
214 enum x86_64_reg_class subclasses
[MAX_CLASSES
];
216 /* If the struct is larger than 16 bytes, pass it on the stack. */
217 if ( pTypeDescr
->nSize
> 16 )
219 TYPELIB_DANGER_RELEASE( pTypeDescr
);
223 for ( int i
= 0; i
< words
; i
++ )
224 classes
[i
] = X86_64_NO_CLASS
;
226 const typelib_CompoundTypeDescription
*pStruct
= reinterpret_cast<const typelib_CompoundTypeDescription
*>( pTypeDescr
);
228 /* Merge the fields of structure. */
229 for ( sal_Int32 nMember
= 0; nMember
< pStruct
->nMembers
; ++nMember
)
231 typelib_TypeDescriptionReference
*pTypeInStruct
= pStruct
->ppTypeRefs
[ nMember
];
233 int num
= classify_argument( pTypeInStruct
, subclasses
, rByteOffset
);
237 TYPELIB_DANGER_RELEASE( pTypeDescr
);
241 for ( int i
= 0; i
< num
; i
++ )
243 int pos
= rByteOffset
/ 8;
244 classes
[i
+ pos
] = merge_classes( subclasses
[i
], classes
[i
+ pos
] );
247 if ( pTypeInStruct
->eTypeClass
!= typelib_TypeClass_STRUCT
)
248 rByteOffset
= pStruct
->pMemberOffsets
[ nMember
];
251 TYPELIB_DANGER_RELEASE( pTypeDescr
);
253 /* Final merger cleanup. */
254 for ( int i
= 0; i
< words
; i
++ )
256 /* If one class is MEMORY, everything should be passed in
258 if ( classes
[i
] == X86_64_MEMORY_CLASS
)
261 /* The X86_64_SSEUP_CLASS should be always preceded by
263 if ( classes
[i
] == X86_64_SSEUP_CLASS
264 && ( i
== 0 || classes
[i
- 1] != X86_64_SSE_CLASS
) )
265 classes
[i
] = X86_64_SSE_CLASS
;
267 /* X86_64_X87UP_CLASS should be preceded by X86_64_X87_CLASS. */
268 if ( classes
[i
] == X86_64_X87UP_CLASS
269 && ( i
== 0 || classes
[i
- 1] != X86_64_X87_CLASS
) )
270 classes
[i
] = X86_64_SSE_CLASS
;
276 #if OSL_DEBUG_LEVEL > 1
277 OSL_TRACE( "Unhandled case: pType->eTypeClass == %d\n", pTypeRef
->eTypeClass
);
281 return 0; /* Never reached. */
284 /* Examine the argument and return set number of register required in each
285 class. Return 0 iff parameter should be passed in memory. */
286 bool x86_64::examine_argument( typelib_TypeDescriptionReference
*pTypeRef
, bool bInReturn
, int &nUsedGPR
, int &nUsedSSE
)
288 enum x86_64_reg_class classes
[MAX_CLASSES
];
292 n
= classify_argument( pTypeRef
, classes
, offset
);
299 for ( n
--; n
>= 0; n
-- )
300 switch ( classes
[n
] )
302 case X86_64_INTEGER_CLASS
:
303 case X86_64_INTEGERSI_CLASS
:
306 case X86_64_SSE_CLASS
:
307 case X86_64_SSESF_CLASS
:
308 case X86_64_SSEDF_CLASS
:
311 case X86_64_NO_CLASS
:
312 case X86_64_SSEUP_CLASS
:
314 case X86_64_X87_CLASS
:
315 case X86_64_X87UP_CLASS
:
320 #if OSL_DEBUG_LEVEL > 1
321 OSL_TRACE( "Unhandled case: classes[n] == %d\n", classes
[n
] );
328 bool x86_64::return_in_hidden_param( typelib_TypeDescriptionReference
*pTypeRef
)
332 return examine_argument( pTypeRef
, true, g
, s
) == 0;
335 void x86_64::fill_struct( typelib_TypeDescriptionReference
*pTypeRef
, void * const *pGPR
, void * const *pSSE
, void *pStruct
)
337 enum x86_64_reg_class classes
[MAX_CLASSES
];
341 n
= classify_argument( pTypeRef
, classes
, offset
);
343 sal_uInt64
*pStructAlign
= reinterpret_cast<sal_uInt64
*>( pStruct
);
344 for ( n
--; n
>= 0; n
-- )
345 switch ( classes
[n
] )
347 case X86_64_INTEGER_CLASS
:
348 case X86_64_INTEGERSI_CLASS
:
349 *pStructAlign
++ = *reinterpret_cast<sal_uInt64
*>( *pGPR
++ );
351 case X86_64_SSE_CLASS
:
352 case X86_64_SSESF_CLASS
:
353 case X86_64_SSEDF_CLASS
:
354 *pStructAlign
++ = *reinterpret_cast<sal_uInt64
*>( *pSSE
++ );
361 /* Functions to load floats and double to an SSE register placeholder. */
362 extern void float2sse (float, __int128_t
*);
363 extern void double2sse (double, __int128_t
*);
364 extern void floatfloat2sse (void *, __int128_t
*);
366 /* Functions to put the floats and doubles back. */
367 extern float sse2float (__int128_t
*);
368 extern double sse2double (__int128_t
*);
369 extern void sse2floatfloat(__int128_t
*, void *);
373 ffi_prep_args (stackLayout
*stack
, extended_cif
*ecif
)
376 int gprcount
, ssecount
, i
, g
, s
;
378 void *argp
= &stack
->argspace
;
381 /* First check if the return value should be passed in memory. If so,
382 pass the pointer as the first argument. */
383 gprcount
= ssecount
= 0;
384 if (ecif
->cif
->rtype
->type
!= FFI_TYPE_VOID
385 && examine_argument (ecif
->cif
->rtype
, 1, &g
, &s
) == 0)
386 (void *)stack
->gpr
[gprcount
++] = ecif
->rvalue
;
388 for (i
=ecif
->cif
->nargs
, p_arg
=ecif
->cif
->arg_types
, p_argv
= ecif
->avalue
;
389 i
!=0; i
--, p_arg
++, p_argv
++)
393 switch ((*p_arg
)->type
)
396 case FFI_TYPE_SINT16
:
397 case FFI_TYPE_SINT32
:
398 case FFI_TYPE_SINT64
:
400 case FFI_TYPE_UINT16
:
401 case FFI_TYPE_UINT32
:
402 case FFI_TYPE_UINT64
:
403 case FFI_TYPE_POINTER
:
404 if (gprcount
< MAX_GPR_REGS
)
406 stack
->gpr
[gprcount
] = 0;
407 stack
->gpr
[gprcount
++] = *(long long *)(*p_argv
);
413 if (ssecount
< MAX_SSE_REGS
)
415 float2sse (*(float *)(*p_argv
), &stack
->sse
[ssecount
++]);
420 case FFI_TYPE_DOUBLE
:
421 if (ssecount
< MAX_SSE_REGS
)
423 double2sse (*(double *)(*p_argv
), &stack
->sse
[ssecount
++]);
432 /* Either all places in registers where filled, or this is a
433 type that potentially goes into a memory slot. */
434 if (examine_argument (*p_arg
, 0, &g
, &s
) == 0
435 || gprcount
+ g
> MAX_GPR_REGS
|| ssecount
+ s
> MAX_SSE_REGS
)
437 /* Pass this argument in memory. */
438 argp
= (void *)ALIGN(argp
, (*p_arg
)->alignment
);
439 memcpy (argp
, *p_argv
, (*p_arg
)->size
);
440 argp
+= (*p_arg
)->size
;
444 /* All easy cases are eliminated. Now fire the big guns. */
446 enum x86_64_reg_class classes
[MAX_CLASSES
];
447 int offset
= 0, j
, num
;
450 num
= classify_argument (*p_arg
, classes
, &offset
);
451 for (j
=0, a
=*p_argv
; j
<num
; j
++, a
+=8)
455 case X86_64_INTEGER_CLASS
:
456 case X86_64_INTEGERSI_CLASS
:
457 stack
->gpr
[gprcount
++] = *(long long *)a
;
459 case X86_64_SSE_CLASS
:
460 floatfloat2sse (a
, &stack
->sse
[ssecount
++]);
462 case X86_64_SSESF_CLASS
:
463 float2sse (*(float *)a
, &stack
->sse
[ssecount
++]);
465 case X86_64_SSEDF_CLASS
:
466 double2sse (*(double *)a
, &stack
->sse
[ssecount
++]);
476 /* Perform machine dependent cif processing. */
478 ffi_prep_cif_machdep (ffi_cif
*cif
)
480 int gprcount
, ssecount
, i
, g
, s
;
482 gprcount
= ssecount
= 0;
484 /* Reset the byte count. We handle this size estimation here. */
487 /* If the return value should be passed in memory, pass the pointer
488 as the first argument. The actual memory isn't allocated here. */
489 if (cif
->rtype
->type
!= FFI_TYPE_VOID
490 && examine_argument (cif
->rtype
, 1, &g
, &s
) == 0)
493 /* Go over all arguments and determine the way they should be passed.
494 If it's in a register and there is space for it, let that be so. If
495 not, add it's size to the stack byte count. */
496 for (i
=0; i
<cif
->nargs
; i
++)
498 if (examine_argument (cif
->arg_types
[i
], 0, &g
, &s
) == 0
499 || gprcount
+ g
> MAX_GPR_REGS
|| ssecount
+ s
> MAX_SSE_REGS
)
501 /* This is passed in memory. First align to the basic type. */
502 cif
->bytes
= ALIGN(cif
->bytes
, cif
->arg_types
[i
]->alignment
);
504 /* Stack arguments are *always* at least 8 byte aligned. */
505 cif
->bytes
= ALIGN(cif
->bytes
, 8);
507 /* Now add the size of this argument. */
508 cif
->bytes
+= cif
->arg_types
[i
]->size
;
517 /* Set the flag for the closures return. */
518 switch (cif
->rtype
->type
)
521 case FFI_TYPE_STRUCT
:
522 case FFI_TYPE_SINT64
:
524 case FFI_TYPE_DOUBLE
:
525 case FFI_TYPE_LONGDOUBLE
:
526 cif
->flags
= (unsigned) cif
->rtype
->type
;
529 case FFI_TYPE_UINT64
:
530 cif
->flags
= FFI_TYPE_SINT64
;
534 cif
->flags
= FFI_TYPE_INT
;
551 ffi_fill_return_value (return_value
*rv
, extended_cif
*ecif
)
553 enum x86_64_reg_class classes
[MAX_CLASSES
];
556 __int128_t
*sse
= rv
->sse
;
560 /* This is needed because of the way x86-64 handles signed short
562 switch (ecif
->cif
->rtype
->type
)
565 sc
= *(signed char *)gpr
;
566 *(long long *)ecif
->rvalue
= (long long)sc
;
568 case FFI_TYPE_SINT16
:
569 ss
= *(signed short *)gpr
;
570 *(long long *)ecif
->rvalue
= (long long)ss
;
577 num
= classify_argument (ecif
->cif
->rtype
, classes
, &i
);
580 /* Return in memory. */
581 ecif
->rvalue
= (void *) rv
->gpr
[0];
582 else if (num
== 2 && classes
[0] == X86_64_X87_CLASS
&&
583 classes
[1] == X86_64_X87UP_CLASS
)
584 /* This is a long double (this is easiest to handle this way instead
585 of an eightbyte at a time as in the loop below. */
586 *((long double *)ecif
->rvalue
) = rv
->st0
;
591 for (i
=0, a
=ecif
->rvalue
; i
<num
; i
++, a
+=8)
595 case X86_64_INTEGER_CLASS
:
596 case X86_64_INTEGERSI_CLASS
:
597 *(long long *)a
= *gpr
;
600 case X86_64_SSE_CLASS
:
601 sse2floatfloat (sse
++, a
);
603 case X86_64_SSESF_CLASS
:
604 *(float *)a
= sse2float (sse
++);
606 case X86_64_SSEDF_CLASS
:
607 *(double *)a
= sse2double (sse
++);
620 extern void ffi_call_UNIX64(void (*)(stackLayout
*, extended_cif
*),
621 void (*) (return_value
*, extended_cif
*),
622 /*@out@*/ extended_cif
*,
623 unsigned, /*@out@*/ unsigned *, void (*fn
)());
627 void ffi_call(/*@dependent@*/ ffi_cif
*cif
,
629 /*@out@*/ void *rvalue
,
630 /*@dependent@*/ void **avalue
)
636 ecif
.avalue
= avalue
;
638 /* If the return value is a struct and we don't have a return */
639 /* value address then we need to make one */
641 if ((rvalue
== NULL
) &&
642 (examine_argument (cif
->rtype
, 1, &dummy
, &dummy
) == 0))
645 ecif
.rvalue
= alloca(cif
->rtype
->size
);
649 ecif
.rvalue
= rvalue
;
651 /* Stack must always be 16byte aligned. Make it so. */
652 cif
->bytes
= ALIGN(cif
->bytes
, 16);
657 /* Calling 32bit code from 64bit is not possible */
663 ffi_call_UNIX64 (ffi_prep_args
, ffi_fill_return_value
, &ecif
,
664 cif
->bytes
, ecif
.rvalue
, fn
);
674 extern void ffi_closure_UNIX64(void);
677 ffi_prep_closure (ffi_closure
* closure
,
679 void (*fun
)(ffi_cif
*, void*, void**, void*),
682 volatile unsigned short *tramp
;
684 /* FFI_ASSERT (cif->abi == FFI_OSF); */
686 tramp
= (volatile unsigned short *) &closure
->tramp
[0];
687 tramp
[0] = 0xbb49; /* mov <code>, %r11 */
688 tramp
[5] = 0xba49; /* mov <data>, %r10 */
689 tramp
[10] = 0xff49; /* jmp *%r11 */
691 *(void * volatile *) &tramp
[1] = ffi_closure_UNIX64
;
692 *(void * volatile *) &tramp
[6] = closure
;
696 closure
->user_data
= user_data
;
702 ffi_closure_UNIX64_inner(ffi_closure
*closure
, va_list l
, void *rp
)
706 ffi_type
**arg_types
;
710 avalue
= alloca(cif
->nargs
* sizeof(void *));
716 arg_types
= cif
->arg_types
;
718 /* Grab the addresses of the arguments from the stack frame. */
721 switch (arg_types
[i
]->type
)
725 case FFI_TYPE_SINT16
:
726 case FFI_TYPE_UINT16
:
727 case FFI_TYPE_SINT32
:
728 case FFI_TYPE_UINT32
:
729 case FFI_TYPE_SINT64
:
730 case FFI_TYPE_UINT64
:
731 case FFI_TYPE_POINTER
:
733 if (l
->gp_offset
> 48-8)
735 avalue
[i
] = l
->overflow_arg_area
;
736 l
->overflow_arg_area
= (char *)l
->overflow_arg_area
+ 8;
740 avalue
[i
] = (char *)l
->reg_save_area
+ l
->gp_offset
;
746 case FFI_TYPE_STRUCT
:
751 case FFI_TYPE_DOUBLE
:
753 if (l
->fp_offset
> 176-16)
755 avalue
[i
] = l
->overflow_arg_area
;
756 l
->overflow_arg_area
= (char *)l
->overflow_arg_area
+ 8;
760 avalue
[i
] = (char *)l
->reg_save_area
+ l
->fp_offset
;
765 fprintf (stderr
, "double arg %d = %g\n", i
, *(double *)avalue
[i
]);
771 if (l
->fp_offset
> 176-16)
773 avalue
[i
] = l
->overflow_arg_area
;
774 l
->overflow_arg_area
= (char *)l
->overflow_arg_area
+ 8;
778 avalue
[i
] = (char *)l
->reg_save_area
+ l
->fp_offset
;
783 fprintf (stderr
, "float arg %d = %g\n", i
, *(float *)avalue
[i
]);
791 argn
+= ALIGN(arg_types
[i
]->size
, SIZEOF_ARG
) / SIZEOF_ARG
;
795 /* Invoke the closure. */
796 (closure
->fun
) (cif
, rp
, avalue
, closure
->user_data
);
798 /* FIXME: Structs not supported. */
799 FFI_ASSERT(cif
->rtype
->type
!= FFI_TYPE_STRUCT
);
801 /* Tell ffi_closure_UNIX64 how to perform return type promotions. */
803 return cif
->rtype
->type
;