deps: update libffi to 3.8.0

PR-URL: https://github.com/nodejs/node/pull/65154
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Node.js GitHub Bot authored and GitHub committed 2026-08-11 00:35:36 +00:00
1 parent 2f65b902d8
commit 01f201888e
50 files changed
+2565 -159

No files matched your search

+607
View File
@@ -1,3 +1,610 @@
commit 12ffd1f9dc56fcea79d2f742f424301ae668d663
Author: Anthony Green <green@moxielogic.com>
Date: Sat Aug 8 18:08:29 2026 -0400
README: order 3.8.0 notes by decreasing importance
Lead with new capabilities (VECTOR types, ffi_call_plan_size, ppc64
_Complex long double), then correctness fixes by severity, then the
trampoline caching optimization.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit 8f2a41d9d89dd8ee2c2438f1e2f9cf04aa9a53d4
Author: Anthony Green <green@moxielogic.com>
Date: Sat Aug 8 18:05:33 2026 -0400
Release 3.8.0
Bump version to 3.8.0, soname to libffi.so.8.5.0 (libtool 13:0:5) for the
new public interfaces added this cycle (FFI_TYPE_VECTOR, ffi_call_plan_size),
date the README history section, and update doc/version.texi.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit d956fe177ccfd4bb91ae7cb3ccaa0f8935a76522
Author: Anthony Green <green@moxielogic.com>
Date: Sat Aug 8 17:38:40 2026 -0400
testsuite: distribute plan_size.c
The ffi_call_plan_size test added in #1006 was not listed in EXTRA_DIST,
so it would be omitted from release tarballs (it still runs from a git
checkout, where dejagnu globs *.c). Add it alongside the other plan_*.c
tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit 670a0327b7d1576712a3cad7b9297f59f23d5430
Author: Anthony Green <green@moxielogic.com>
Date: Sat Aug 8 17:09:22 2026 -0400
README: note i386 BSD small-struct register return
Follow-up to #1010, which returns small structs in registers on i386
FreeBSD/OpenBSD but did not update the History section.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit f744bc303fa4c69f1202ce283b866ebc768e0432
Merge: abc18be0 5b8fa3fe
Author: Anthony Green <green@moxielogic.com>
Date: Sat Aug 8 17:09:02 2026 -0400
Merge pull request #1010 from DTW-Thalion/x86-bsd-small-struct-return
x86: return small structs in registers on the BSD i386 targets
commit abc18be0d9ba9cc37c955b317e63cd52fd0d90ee
Merge: ed742112 5f24e6a0
Author: Anthony Green <green@moxielogic.com>
Date: Sat Aug 8 16:57:36 2026 -0400
Merge pull request #1006 from rvandermeulen/call-plan-size
call_plan: add ffi_call_plan_size to report a plan's allocation
commit ed7421122880e4daff87f1c8623d508a2e2c5c9a
Merge: e43f2548 e6db2d38
Author: Anthony Green <green@moxielogic.com>
Date: Sat Aug 8 16:41:24 2026 -0400
Merge pull request #1009 from libffi/fix-jumptable-desync-family
Fix FFI_TYPE_LAST/vector jump-table desyncs on ia64, ppc64 (BE ELFv2), and aarch64
commit e6db2d38decee8bf6321472dff5147ad311e639a
Author: Anthony Green <green@moxielogic.com>
Date: Fri Aug 7 17:00:49 2026 -0400
aarch64: reject sub-4-byte vector lanes in HVA classification
is_vfp_type() maps a homogeneous vector aggregate's lane width onto the
S/D/Q register classes via FFI_TYPE_FLOAT + intlog2(reg_size) - 2, and
encodes the result as an AARCH64_RET_* code. A lane narrower than 4 bytes
(e.g. a struct of two 2-byte vectors, which libffi's own initialize_vector
accepts) yields intlog2(reg_size) < 2, producing a code below
AARCH64_RET_S4. extend_hfa_type() then computes a negative jump-table
offset (h - AARCH64_RET_S4) and branches before its table -- a wild
computed branch during ffi_call argument marshalling.
Such a type has no short-vector register class under AAPCS64, so reject it
in is_vfp_type() (returning 0 routes it through the generic aggregate
path). Fixing it at the source covers both the argument path
(extend_hfa_type) and the return path. Verified on aarch64 (Fedora under
qemu-aarch64): a call passing such an HVA segfaults before the fix and
returns correctly after it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit 159268174ece06f6854c6d1bca1a9b95961f6ae9
Author: Anthony Green <green@moxielogic.com>
Date: Fri Aug 7 08:18:51 2026 -0400
powerpc64: fix big-endian ELFv2 closure returns of 5/6/7-byte structs
On big-endian ppc64 ELFv2, ffi_closure_helper_LINUX64 returns the load
codes PPC64_LD_STRUCT_5/6/7 (17/18/19) for closures returning a 5-, 6-,
or 7-byte struct, but linux64_closure.S only defined return jump-table
entries through PPC64_LD_STRUCT_3 (16). The E() macro places each 16-byte
slot with .align 4 (no .org), so codes 17/18/19 fell through into the
.Lmoredouble continuation: the closure loaded FP registers and returned
without writing r3, so the ELFv2 caller read back the computed jump
target -- a libffi code address -- as the struct value (wrong result plus
a code-pointer disclosure). Little-endian ELFv2 is unaffected (those
codes alias PPC_LD_R3/I64); big-endian ELFv1 returns structs by reference
and never emits the codes.
Add the three missing handlers, loading the struct right-justified into
r3 per the ELFv2 convention. Verified on big-endian ppc64 ELFv2 (Adélie
Linux under qemu-ppc64): testsuite/libffi.closures/cls_{5,6,7}_1_byte.c
abort before the fix and pass after it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit 3fdd99b5d2fb4c8f940d82fc4b1e530e743c685b
Author: Anthony Green <green@moxielogic.com>
Date: Fri Aug 7 06:11:00 2026 -0400
ia64: fix return jump-table desync after FFI_TYPE_LAST bump
The .Lst_table/.Lld_table return-value dispatch tables in unix.S are
indexed by the FFI_IA64_TYPE_SMALL_STRUCT/HFA_* codes, which are
FFI_TYPE_LAST-relative, but the tables hardcoded 20 entries assuming
FFI_TYPE_LAST == FFI_TYPE_COMPLEX (15). The conditional __int128
support added in 3.6.0 advanced FFI_TYPE_LAST to SINT128 (17), and
FFI_TYPE_VECTOR advanced it to 18, shifting SMALL_STRUCT to 19 -- so a
small-struct return dispatched to the HFA-ldouble handler's 16-byte
stfe store, an out-of-bounds write past rvalue, and HFA returns indexed
off the end of the table entirely.
Add the missing UINT128/SINT128/VECTOR slots to both tables (pointing at
the existing not-implemented void handler, matching FFI_TYPE_COMPLEX)
and a FFI_TYPE_LAST tripwire, mirroring the pa and win64 guards.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit 5b8fa3fed84ce17768eeb379f5b81663172482e9
Author: Todd White <todd.white@thalion.global>
Date: Fri Aug 7 19:40:05 2026 -0400
x86: return small structs in registers on the BSD i386 targets
i386 FreeBSD and OpenBSD return a struct of 1, 2, 4 or 8 bytes in eax and
edx, as Darwin and win32 do. ffi_prep_cif_machdep applied the size test
only under X86_WIN32 and X86_DARWIN, so on these targets it recorded
X86_RET_STRUCTPOP and allocated a return pointer the callee never writes,
and a struct return through ffi_call or through a closure read a value
that was never stored.
configure.host already maps i?86-*-freebsd* and i?86-*-openbsd* to
TARGET=X86_FREEBSD, and include/ffi.h.in defines that name, so extend the
condition to it. Sizes 3, 6 and 12 continue to be returned in memory.
commit 5f24e6a05574b1aa74cca77b1ecd6413a8105f62
Author: Ryan VanderMeulen <rvandermeulen@mozilla.com>
Date: Wed Aug 5 11:09:51 2026 -0400
call_plan: add ffi_call_plan_size to report a plan's allocation
ffi_call_plan is opaque, so an embedder that tracks the memory a long-lived
plan holds has no way to ask how big it is. The only options are to hardcode
a guess or to hardcode knowledge of the private struct layout, and both go
stale silently on the next release.
The x86-64 backend records the byte count in ffi_plan at the point it is
passed to malloc, so the reported value cannot drift from the allocation it
describes; ffi_call_plan_size adds that to the handle and treats a signature
with no fast path as owning nothing beyond it. The generic backend's plan is
a bare handle, so it reports sizeof (struct ffi_call_plan). The counter lives
in ffi_plan rather than in the handle so that only plans that actually own a
move-list pay for it, and plans without one pay nothing.
Computing the size in the query from cif->nargs instead would duplicate
build_plan's allocation formula in a second place, and would report the wrong
number if the cif were re-prepared with a different argument count after the
plan was built.
The new symbol gets its own version node rather than joining
LIBFFI_CALL_PLAN_8.4, which shipped in 3.7.0: adding to a released node would
let a binary that needs ffi_call_plan_size look satisfiable against a 3.7.x
library that exports the node without the symbol, turning a clean link error
into a runtime failure. libtool-version is left alone, since rule 2 in that
file defers version updates to immediately before a release.
commit e43f254881f9010a26c48f595928461c0432c7b4
Merge: 2fd434cd 04d721cc
Author: Anthony Green <green@moxielogic.com>
Date: Thu Aug 6 00:40:32 2026 -0400
Merge pull request #1008 from libffi/fix-win64-vector-small-struct-flags
x86: fix Win64 small-struct returns broken by FFI_TYPE_VECTOR
commit 04d721cc448316dbba5af506be46315efabd80e3
Author: Anthony Green <green@moxielogic.com>
Date: Wed Aug 5 22:59:29 2026 -0400
x86: fix Win64 small-struct returns broken by FFI_TYPE_VECTOR
Adding FFI_TYPE_VECTOR (#1000) moved FFI_TYPE_LAST from FFI_TYPE_SINT128
(17) to FFI_TYPE_VECTOR (18). The Win64 return pseudo-types
FFI_TYPE_SMALL_STRUCT_1B/2B/4B = FFI_TYPE_LAST + 1..3
are FFI_TYPE_LAST-relative, so they shifted from 18/19/20 to 19/20/21.
The win64.S / win64_intel.S return-value dispatch is a computed jump
table indexed by cif->flags (base + flags*8) whose handlers are emitted
contiguously right after FFI_TYPE_SINT128, with no slot for value 18.
Under the sequential E() variant used by the MSVC/ml64 build, the
size-1/2/4 small-struct handlers therefore sat one 8-byte slot below the
flag values ffiw64.c now emits, so small structs returned by value were
written with the wrong width (or fell off the table into abort). This
showed up as 14 execution failures in the "Windows 64-bit Visual C++" CI
job (s55, struct3, struct_by_value_small, struct_return_2H, the small
cls_* / single_entry_structs closures, and bhaible DGTEST 47/53/55).
Add an FFI_TYPE_VECTOR abort stub between SINT128 and SMALL_STRUCT_1B in
both tables so the jump table stays contiguous and the small-struct
entries realign with their (shifted) code values. Win64 does not marshal
vectors -- ffi_prep_cif_core rejects them since FFI_TARGET_HAS_VECTOR_TYPE
is undefined there -- so the slot is never reached at runtime.
Also add a pa-style compile-time tripwire to src/x86/ffitarget.h so the
next generic type added bumps FFI_TYPE_LAST and #errors until the win64
tables are updated in step. 32-bit x86 is unaffected: sysv.S indexes its
store table by the independent X86_RET_* enum, not FFI_TYPE_LAST.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit 2fd434cd9ada4d3d97b355e62c3ce3a969682230
Merge: a00279c2 2aa33761
Author: Anthony Green <green@moxielogic.com>
Date: Sun Aug 2 10:18:45 2026 -0400
Merge pull request #1005 from libffi/tramp-cache-unsupported-verdict
tramp: cache the static trampoline "unsupported" verdict
commit 2aa33761c0536339f9f322902b9bb3a981114724
Merge: 76883c62 a00279c2
Author: Anthony Green <green@moxielogic.com>
Date: Sun Aug 2 10:18:33 2026 -0400
Merge branch 'master' into tramp-cache-unsupported-verdict
commit 76883c628a5273f71fb025e75bf1076adae3bb4b
Author: Anthony Green <green@moxielogic.com>
Date: Sun Aug 2 10:05:22 2026 -0400
tramp: cache the static trampoline "unsupported" verdict
ffi_tramp_init() bailed out with a plain `return 0` when the system page
size exceeds the trampoline code table mapping, without recording the
outcome in tramp_globals.status. Because that early return was the only
failure exit that left status as UNINITIALIZED, every subsequent
ffi_tramp_alloc()/ffi_tramp_is_supported() call re-ran the full
initialization (ffi_tramp_arch(), sysconf(), etc.) instead of
short-circuiting on the cached verdict like the other two failure paths.
The comparison is between two process-lifetime invariants -- map_size is
a compile-time constant from ffi_tramp_arch(), and page_size is fixed for
the life of the process (and only checked when sysconf() returned a valid
value) -- so it can never flip. Caching FAILED is therefore safe and
matches the intent of the status field.
Affects hosts with pages larger than the 16K table, in practice 64K-page
aarch64 kernels, where static trampolines are correctly declined but the
decline was recomputed on every closure allocation. No functional change
on 4K/16K-page hosts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit a00279c2dc8e191ae5136b46bf6ae0e7a8da5b7a
Author: Anthony Green <green@moxielogic.com>
Date: Sat Aug 1 07:17:23 2026 -0400
Note unreleased development changes in README history
Add a "Development source only" History block for changes on master
since 3.7.1: FFI_TYPE_VECTOR SIMD support (#1000), powerpc64 _Complex
long double (#1003), and the powerpc Darwin closure fix (#1002).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit ce77ca107a5cb0d10d5525c9422f0207e6c79ebf
Merge: d257b084 19dbdb53
Author: Anthony Green <green@moxielogic.com>
Date: Sat Aug 1 07:09:17 2026 -0400
Merge pull request #1000 from edusperoni/feat/vector-types
Add FFI_TYPE_VECTOR: vector (SIMD) type support with libffi-computed layout
commit d257b08495e95248f66b7bd50dd106ea19124df9
Merge: 333d87cf b2170647
Author: Anthony Green <green@moxielogic.com>
Date: Tue Jul 28 00:47:29 2026 -0400
Merge pull request #1004 from libffi/fix-1002-ppc-darwin-closure
powerpc: fix Darwin closure returns broken by #951
commit b2170647583461f42dc2d9f201211fcafda2429f
Author: Anthony Green <green@moxielogic.com>
Date: Mon Jul 27 20:08:15 2026 -0400
powerpc: fix Darwin closure returns broken by #951
PR #951 (840add3b) changed the shared PowerPC closure helper,
ffi_closure_helper_common, to return a small PPC_LD_* jump-table index
instead of the ffi_type*, and rewrote aix_closure.S to consume it -- but
left darwin_closure.S expecting the old ffi_type* and dereferencing it.
With the helper now returning a small integer, ffi_closure_ASM
dereferenced e.g. 0 (PPC_LD_NONE, a void return) as a pointer, faulting
on a load from address 0. This crashed essentially every closure call
-- including every gobject-introspection signal handler -- on 32- and
64-bit PowerPC Darwin (SIGBUS at ffi_closure_ASM, dar=0; issue #1002).
Convert darwin_closure.S to the PPC_LD_* convention, mirroring
aix_closure.S: drop the ffi_type* dereference, use the returned index
directly, and reorder the return-value jump table into PPC_LD_* order
(NONE, R3, R3R4, F32, F64, F128, U8, S8, U16, S16, and on ppc64 U32, S32).
Darwin, unlike AIX, returns small structs by value in registers, which
the existing assembly handles (Lsmallstruct/Lfour/Lstructend). The
helper's return code is a single small integer with no room for
cif->rtype, which that assembly needs, so for a by-value struct return
the helper now stashes cif->rtype in the first parameter-save slot (dead
by return time) and returns a new PPC_LD_STRUCT code; the PPC_LD_STRUCT
fragment recovers it and drives the unchanged struct machinery. By-
reference struct returns still return PPC_LD_NONE.
Based on the approach in a patch by Sergey Fedorov (@barracuda156); the
jump table here is reordered to the PPC_LD_* layout so that float,
double, long double, sub-word and 64-bit returns also dispatch correctly.
I have no PowerPC Darwin hardware; the jump-table fragment offsets were
checked by assembling for powerpc and powerpc64, but runtime
confirmation on 10.5/10.6 is still needed.
Fixes #1002.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit 333d87cf201ee279c9870fb5ad3e48e3a08aa6e5
Merge: 46cb2e38 d7cd3a61
Author: Anthony Green <green@moxielogic.com>
Date: Mon Jul 27 08:52:34 2026 -0400
Merge pull request #1003 from libffi/fix-ppc64le-complex-longdouble
powerpc64: implement _Complex long double for both IBM-128 and IEEE-128
commit d7cd3a6194885c85255c77782a05a153a42b29a5
Author: Anthony Green <green@moxielogic.com>
Date: Mon Jul 27 07:06:39 2026 -0400
powerpc64: implement _Complex long double for both IBM-128 and IEEE-128
Complex support for POWERPC64 ELFv2 (f0ca157, #970) defined
FFI_TARGET_HAS_COMPLEX_TYPE, which flips complex.exp from marking the
libffi.complex suite UNSUPPORTED to running it. _Complex long double
was deliberately deferred with FFI_BAD_TYPEDEF, so ffi_prep_cif failed
and every libffi.complex/*longdouble* test aborted. This was not caught
upstream because an XFAIL entry in the rlgl CI policy masked the FAILs.
Implement both long double formats:
- IBM-128 (double-double): each _Complex long double is passed and
returned as four doubles (real hi/lo, imag hi/lo) in f1-f4, with a
GPR shadow doubleword per FPR, and returned as a double homogeneous
aggregate.
- IEEE binary128: real in v2, imag in v3; each half occupies a vector
register (or a 16-byte-aligned parameter save slot with two GPR
shadow doublewords) and is returned via the vector-homogeneous
small-struct path.
discover_homogeneous_aggregate now accepts FFI_TYPE_LONGDOUBLE as a
_Complex inner type so struct-of-complex-longdouble is treated as an HFA.
Covers ffi_prep_cif, ffi_prep_args64, and the closure decode/return
paths.
Fixes #1001.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
commit debc00a0114d8530d6d691862028c607aa17dd6a
Author: Anthony Green <green@moxielogic.com>
Date: Sun Jul 26 07:28:36 2026 -0400
Update doc version
commit 19dbdb53e869e07fbff05c86d634e8c08c9a7f61
Author: Eduardo Speroni <edusperoni@gmail.com>
Date: Tue Jul 21 20:32:24 2026 -0300
testsuite: fix vector suite CI failures on gcc and MSVC
Two fixes for the libffi.vector suite:
- vector_double4.c: the non-aarch64 branch built its own void_args
array and never read the already-populated args, tripping gcc's
-Wunused-but-set-variable (an excess-errors FAIL on Linux x86-64
with gcc; clang does not emit this warning). Use args for the
negative argument-passing check instead.
- vector.exp: the suite only probed FFI_TARGET_HAS_VECTOR_TYPE, but
the tests are written with the GCC/Clang vector extension. On
Windows ARM64 the aarch64 port enables the feature while MSVC
cannot compile __attribute__ ((vector_size)), so every test failed
to build. Add a compile probe and mark the suite unsupported when
the compiler lacks the syntax.
commit 71a95a2cd433b151b3fcf83a0a830eb9aa38fa3a
Author: Eduardo Speroni <edusperoni@gmail.com>
Date: Tue Jul 21 16:44:26 2026 -0300
testsuite: add libffi.vector suite for vector (SIMD) types
Model a new testsuite/libffi.vector/ directory on testsuite/libffi.complex:
vector.exp reuses the same dg/run-many-tests driver and skips every test as
"unsupported" on ports whose headers do not define
FFI_TARGET_HAS_VECTOR_TYPE (libffi_feature_test), so unsupported targets
still compile the gating cleanly.
Vector types are built with the portable __attribute__((vector_size)) via a
small make_vector_type() helper (vector.h); each test cross-checks the value
returned through ffi against a direct native call. Coverage:
- vector_float32x4 / vector_float32x2 / vector_double2 / vector_int32x4:
pass and return 8- and 16-byte float, double and integer vectors
(float32x4 is the vec4 shape of libffi/libffi#773);
- vector_args_spill: ten vectors interleaved with int/double scalars,
exhausting the vector argument registers and spilling to the stack;
- vector_vec3: Clang-only ext_vector_type(3), verifying the 12->16 byte
power-of-two padding matches a natively compiled callee (a no-op on
other compilers);
- vector_double4: on AArch64 a 32-byte vector round-trips (by reference /
in memory); elsewhere ffi_prep_cif must return FFI_BAD_TYPEDEF, checked
for both return and argument;
- vector_hva: a struct of two identical vectors (HVA) passes and returns
on both AArch64 (Q-register pair) and x86-64 (SSE struct classification);
- cls_vector: a closure receiving vector arguments and returning a vector;
- vector_validate: heterogeneous lanes, an empty vector, and a non-scalar
lane are each rejected with FFI_BAD_TYPEDEF, and a well-formed vector is
accepted with the computed power-of-two size and min(size,16) alignment.
The files are added to testsuite/Makefile.am EXTRA_DIST, matching how
libffi.complex is distributed.
References: libffi/libffi#414, libffi/libffi#773.
commit 93b274cec912ac2395575a8bd4dfcb527f61599c
Author: Eduardo Speroni <edusperoni@gmail.com>
Date: Tue Jul 21 16:32:04 2026 -0300
x86-64: marshal vector (SIMD) types per the System V psABI
Define FFI_TARGET_HAS_VECTOR_TYPE for the SysV x86-64 backend (ffi64.c;
32-bit x86 and the Windows ffiw64.c backend are excluded) and integrate
FFI_TYPE_VECTOR into the existing psABI classifier without restructuring
it:
- classify_argument gains a FFI_TYPE_VECTOR case: an 8-byte vector is
one SSE eightbyte (X86_64_SSE_CLASS); a 16-byte vector is one %xmm
register (X86_64_SSE_CLASS + X86_64_SSEUP_CLASS). The existing
INTEGERSI/SSESF/SSEDF/UINT128 handling is untouched, and the SSE+SSEUP
argument marshalling already merges both eightbytes into one %xmm.
- ffi_prep_cif_machdep classifies vector returns symmetrically: 8 bytes
in %xmm0 (UNIX64_RET_XMM64), 16 bytes in %xmm0 (UNIX64_RET_XMM128).
- Vectors wider than 16 bytes return FFI_BAD_TYPEDEF from
ffi_prep_cif_machdep, for both returns and arguments. Correct
%ymm/%zmm passing needs unix64.S register-save changes and is left as
a v1 limitation rather than silently passing them in memory.
Closures need no separate change: the closure paths reuse
classify_argument for arguments and cif->flags for the return.
References: libffi/libffi#414.
commit 5eaa8a389de61fc3b056f62c48ceade1931b5413
Author: Eduardo Speroni <edusperoni@gmail.com>
Date: Tue Jul 21 16:30:12 2026 -0300
aarch64: marshal vector (SIMD) types per AAPCS64
Define FFI_TARGET_HAS_VECTOR_TYPE for AArch64 and teach is_vfp_type to
classify FFI_TYPE_VECTOR, so ffi_call and closures pass and return
vectors the way AAPCS64 (and current GCC/Clang) do:
- 8- and 16-byte vectors travel in a single V/Q register (a Short
Vector), for float, double and integer lane types alike;
- homogeneous vector aggregates -- a struct of up to four identical
8- or 16-byte vectors -- travel in that many consecutive V/Q
registers (an HVA), e.g. struct{float32x4 a,b} in {q0,q1};
- a bare vector wider than 16 bytes (e.g. a 32-byte double4) has no
short-vector register class, so is_vfp_type returns 0 and the
existing composite path passes it by invisible reference and returns
it in memory -- exactly what a natively compiled callee expects.
is_simd() reports the width of one Neon register slot (a bare vector's
whole size, or one lane vector of an HVA); is_vfp_type() encodes
num_registers slots of that width onto the existing AARCH64_RET_{D,Q}*
codes via intlog2. is_hfa0/is_hfa1 recurse through FFI_TYPE_VECTOR so
HVA homogeneity is checked, and the three fundamental-type dispatch
switches (machdep return, ffi_call_int, ffi_closure_SYSV_inner) route
FFI_TYPE_VECTOR through is_vfp_type alongside FFI_TYPE_STRUCT.
Ported from the battle-tested NativeScript aarch64 vector marshaller,
adapted to the FFI_TYPE_VECTOR API and extended so that integer-lane
vectors (e.g. int32x4) are classified into V registers too -- the
original only handled floating-point lanes.
References: libffi/libffi#414, libffi/libffi#773 (aarch64 vec4 return).
commit b6b8be54acc90f7db1dcf3d1c91238a5a9bca185
Author: Eduardo Speroni <edusperoni@gmail.com>
Date: Tue Jul 21 16:26:33 2026 -0300
core: add FFI_TYPE_VECTOR fundamental type with computed layout
Introduce a portable API for marshalling vector (SIMD) types -- the
values produced by GCC's __attribute__((vector_size)) and Clang's
ext_vector_type. This answers the stalled PR #414 and the maintainer's
2018 design questions
(https://sourceware.org/legacy-ml/libffi-discuss/2018/msg00020.html):
rather than requiring callers to hand-compute a vector's size and
alignment (and gating the feature behind configure), libffi now derives
the layout itself and the type code is defined unconditionally.
A vector is described exactly like a struct: type == FFI_TYPE_VECTOR and
a NULL-terminated elements[] array, except every element must point to
the SAME fundamental scalar (float, double, or a fixed-width integer
UINT8..SINT64) and the count is the number of lanes. The caller leaves
size and alignment at zero; ffi_prep_cif computes:
size = lane_size * count, rounded up to the next power of two
(matches Clang ext_vector_type storage: 3 x float -> 16,
3 x double -> 32; GCC vector_size already requires pow2
totals so it is identical there);
alignment = min(size, 16).
Validation (identical scalar lanes, count >= 1, scalar-only) yields
FFI_BAD_TYPEDEF otherwise.
- include/ffi.h.in: FFI_TYPE_VECTOR = 18 (after SINT128 = 17),
FFI_TYPE_LAST bumped. Defined unconditionally, no configure gating.
- src/prep_cif.c: initialize_vector() computes the layout in
initialize_aggregate; ffi_type_contains_vector() rejects vectors
(including nested in structs, argument or return) with
FFI_BAD_TYPEDEF on any port that does not define
FFI_TARGET_HAS_VECTOR_TYPE -- no aborts. Vector returns reserve the
hidden return-pointer slot like structs.
- src/raw_api.c, src/java_raw_api.c: plumb FFI_TYPE_VECTOR alongside
FFI_TYPE_STRUCT, mirroring how FFI_TYPE_COMPLEX is handled.
- src/debug.c: ffi_type_test requires elements != NULL for vectors.
- src/pa/ffitarget.h: bump the FFI_PA_TYPE_LAST tripwire; PA gates
vectors out in prep_cif so its jump tables are never reached.
- doc/libffi.texi: new "Vector Types" node documenting the API, the
computed-layout rule, the psABI framing, and the per-port support
table.
No port defines FFI_TARGET_HAS_VECTOR_TYPE yet, so this commit rejects
every vector signature; the per-architecture ports follow.
References: libffi/libffi#414, libffi/libffi#773.
commit 46cb2e3871059f7f5113329ddcca818de3a8cfae
Merge: ca86812c 8cd11a77
Author: Anthony Green <green@moxielogic.com>
Date: Fri Jul 10 16:51:18 2026 -0400
Merge pull request #998 from bgilbert/tests
testsuite: Remember to distribute tests added for 3.7.1
commit 8cd11a772d8a0b687f43390697baa002ae6504d5
Author: Benjamin Gilbert <bgilbert@backtick.net>
Date: Fri Jul 10 11:56:55 2026 -0700
testsuite: Remember to distribute tests added for 3.7.1
commit ca86812cd430cff3018e491ba75a4f3c9ea969d2
Author: Anthony Green <green@moxielogic.com>
Date: Fri Jul 10 10:56:47 2026 -0400
ci: Don't publish rlgl reports on tag pushes
A tag and its commit fire two CI runs at the same SHA. Both run the
publish-reports job, which deploys a fixed-name github-pages artifact
via actions/deploy-pages; the two deployments collide and one fails
with BlobNotFound (seen on the v3.7.1 tag run). The same-SHA branch
push already publishes the reports, so gate the job off tag pushes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
commit 5c1c43091ed611fdea774374355eb938c73a9157
Author: Anthony Green <green@moxielogic.com>
Date: Fri Jul 10 09:50:53 2026 -0400
+22
View File
@@ -201,6 +201,28 @@ History
See the git log for details at http://github.com/libffi/libffi.
3.8.0 August-8-2026
Add FFI_TYPE_VECTOR (SIMD) type support with libffi-computed
layout, for aarch64 and x86-64 (#1000, closes #773).
Add ffi_call_plan_size to report the total memory a reusable call
plan owns, for embedders that account for the memory held by
long-lived plans.
Add powerpc64 ELFv2 _Complex long double support for both
IBM-128 (double-double) and IEEE-128 formats (#1003, closes #1001).
Fix powerpc64 big-endian ELFv2 closures returning 5-, 6-, or
7-byte structs: missing return jump-table entries produced a
wrong result and leaked a libffi code pointer.
Fix ia64 return-value jump-table desync after the FFI_TYPE_LAST
bump, which corrupted small-struct and HFA returns.
Fix powerpc Darwin closure returns broken by #951 (#1002).
Return small (1, 2, 4 or 8 byte) structs in registers on the i386
FreeBSD and OpenBSD targets, matching the platform ABI and
fixing a segfault on struct returns through ffi_call and closures.
Cache the static trampoline "unsupported" result on hosts whose
page size exceeds the trampoline table mapping, avoiding
redundant re-initialization on every closure allocation
(e.g. 64K-page aarch64).
3.7.1 July-10-2026
Fix aarch64 ffi_call memory corruption when passing many large
structs by value.
+14 -14
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for libffi 3.7.1.
# Generated by GNU Autoconf 2.71 for libffi 3.8.0.
#
# Report bugs to <http://github.com/libffi/libffi/issues>.
#
@@ -621,8 +621,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='libffi'
PACKAGE_TARNAME='libffi'
PACKAGE_VERSION='3.7.1'
PACKAGE_STRING='libffi 3.7.1'
PACKAGE_VERSION='3.8.0'
PACKAGE_STRING='libffi 3.8.0'
PACKAGE_BUGREPORT='http://github.com/libffi/libffi/issues'
PACKAGE_URL=''
@@ -1417,7 +1417,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures libffi 3.7.1 to adapt to many kinds of systems.
\`configure' configures libffi 3.8.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1489,7 +1489,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of libffi 3.7.1:";;
short | recursive ) echo "Configuration of libffi 3.8.0:";;
esac
cat <<\_ACEOF
@@ -1628,7 +1628,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
libffi configure 3.7.1
libffi configure 3.8.0
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2259,7 +2259,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by libffi $as_me 3.7.1, which was
It was created by libffi $as_me 3.8.0, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -3233,10 +3233,10 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_config_headers="$ac_config_headers fficonfig.h"
FFI_VERSION_STRING="3.7.1"
ffi_version_major=`echo "3.7.1" | cut -d. -f1`
ffi_version_minor=`echo "3.7.1" | cut -d. -f2 | sed 's/[^0-9].*//'`
ffi_version_micro=`echo "3.7.1" | cut -d. -f3 | sed 's/[^0-9].*//'`
FFI_VERSION_STRING="3.8.0"
ffi_version_major=`echo "3.8.0" | cut -d. -f1`
ffi_version_minor=`echo "3.8.0" | cut -d. -f2 | sed 's/[^0-9].*//'`
ffi_version_micro=`echo "3.8.0" | cut -d. -f3 | sed 's/[^0-9].*//'`
FFI_VERSION_NUMBER=`expr ${ffi_version_major:-0} \* 10000 + ${ffi_version_minor:-0} \* 100 + ${ffi_version_micro:-0}`
@@ -3986,7 +3986,7 @@ fi
# Define the identity of the package.
PACKAGE='libffi'
VERSION='3.7.1'
VERSION='3.8.0'
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -20661,7 +20661,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by libffi $as_me 3.7.1, which was
This file was extended by libffi $as_me 3.8.0, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -20729,7 +20729,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
libffi config.status 3.7.1
libffi config.status 3.8.0
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
+1 -1
View File
@@ -2,7 +2,7 @@ dnl Process this with autoconf to create configure
AC_PREREQ([2.68])
AC_INIT([libffi],[3.7.1],[http://github.com/libffi/libffi/issues])
AC_INIT([libffi],[3.8.0],[http://github.com/libffi/libffi/issues])
AC_CONFIG_HEADERS([fficonfig.h])
dnl Derive the version macros from AC_INIT so they cannot drift when the
+116 -16
View File
@@ -301,6 +301,7 @@ File: libffi.info, Node: Types, Next: Multiple ABIs, Prev: Simple Example, U
* Type Example:: Structure type example.
* Complex:: Complex types.
* Complex Type Example:: Complex type example.
* Vector Types:: Vector (SIMD) types.

File: libffi.info, Node: Primitive Types, Next: Structures, Up: Types
@@ -660,7 +661,7 @@ functions ‘ffi_prep_cif’ and ‘ffi_prep_args’ abort the program if they
encounter a complex type.

File: libffi.info, Node: Complex Type Example, Prev: Complex, Up: Types
File: libffi.info, Node: Complex Type Example, Next: Vector Types, Prev: Complex, Up: Types
2.3.7 Complex Type Example
--------------------------
@@ -746,6 +747,92 @@ compilers that support them:
The new type descriptors can then be used like one of the built-in
type descriptors in the previous example.

File: libffi.info, Node: Vector Types, Prev: Complex Type Example, Up: Types
2.3.8 Vector Types
------------------
‘libffi’ can marshal vector (SIMD) types -- the values produced by GCC's
‘__attribute__((vector_size (N)))’ and Clang's ‘ext_vector_type’ -- on
the platforms listed in the support table below. A vector is described
just like a structure, except that every element pointer refers to the
_same_ fundamental scalar type and the number of elements is the number
of vector lanes.
-- Data type: ffi_type
‘size_t size’
This must be set to ‘0’. ‘libffi’ computes the storage size
(see below) from the element type and lane count.
‘unsigned short alignment’
This must be set to ‘0’. ‘libffi’ computes the alignment.
‘unsigned short type’
For a vector type, this must be set to ‘FFI_TYPE_VECTOR’.
‘ffi_type **elements’
This is a ‘NULL’-terminated array of pointers to ‘ffi_type’
objects. Every entry must point to the same scalar element
type, and the number of entries is the vector's lane count N
(N >= 1). The element type must be one of ‘ffi_type_float’,
‘ffi_type_double’, or a fixed-width integer (‘ffi_type_uint8’
through ‘ffi_type_sint64’); ‘long double’ and aggregate
element types are not permitted.
Computed layout
...............
Because the caller leaves ‘size’ and ‘alignment’ at ‘0’, ‘libffi’
derives them so that applications need not encode compiler- or
platform-specific rules:
• ‘size’ is lane\_size \times N rounded _up_ to the next power of
two. This matches Clang's ‘ext_vector_type’ storage -- for example
a three-lane ‘float’ vector occupies 16 bytes and a three-lane
‘double’ vector occupies 32 bytes. GCC's ‘vector_size’ already
requires power-of-two byte totals, so the rule is identical there.
• ‘alignment’ is ‘min(size, 16)’.
If the element list is heterogeneous, empty, or uses a disallowed
element type, ‘ffi_prep_cif’ returns ‘FFI_BAD_TYPEDEF’.
psABI framing
.............
At the call boundary the platform's processor-specific ABI (AAPCS64 on
AArch64, the System V x86-64 psABI on x86-64) decides how a vector is
passed and returned, independently of which compiler produced it. The
historical divergence between GCC's ‘vector_size’ and Clang's
‘ext_vector_type’ concerns only in-memory _layout_ (notably the padding
of odd-lane vectors such as ‘float3’); the power-of-two size rule above
pins that layout down, so a value marshalled by ‘libffi’ matches what a
natively compiled caller or callee expects.
Per-port support
................
Port Vector support
--------------------------------------------------------------------------
AArch64 (AAPCS64) 8- and 16-byte vectors in a single V/Q register;
homogeneous vector aggregates (structs of up to
four identical 8- or 16-byte vectors) in
consecutive V/Q registers. A bare vector larger
than 16 bytes (for example a 32-byte ‘double4’)
has no short-vector register class and is passed
and returned in memory, exactly as AAPCS64 and
current compilers do.
x86-64 (System V 8- and 16-byte vectors in an SSE register (‘%xmm0’
psABI) for returns). A bare vector larger than 16 bytes
needs ‘%ymm’/‘%zmm’ register handling that this
port does not yet implement, so ‘ffi_prep_cif’
returns ‘FFI_BAD_TYPEDEF’ for it.
Other ports Not supported: ‘ffi_prep_cif’ returns
‘FFI_BAD_TYPEDEF’ for any signature that mentions
a vector type, including one nested inside a
struct.

File: libffi.info, Node: Multiple ABIs, Next: Reusable Call Plans, Prev: Types, Up: Using libffi
@@ -795,6 +882,14 @@ prepared ‘ffi_cif’.
is harmless. The ‘ffi_cif’ the plan was built from is not
affected.
-- Function: size_t ffi_call_plan_size (ffi_call_plan *PLAN)
Returns the total number of bytes ‘libffi’ allocated for PLAN,
including any internal argument-placement data it owns. Returns
zero when PLAN is ‘NULL’. The result does not include the
‘ffi_cif’, which the caller owns. This is intended for embedders
that account for the memory held by long-lived plans and would
otherwise have to guess at the size of an opaque type.

File: libffi.info, Node: The Closure API, Next: Closure Example, Prev: Reusable Call Plans, Up: Using libffi
@@ -1056,6 +1151,7 @@ Index
* ffi_call_plan_alloc: Reusable Call Plans. (line 12)
* ffi_call_plan_free: Reusable Call Plans. (line 32)
* ffi_call_plan_invoke: Reusable Call Plans. (line 22)
* ffi_call_plan_size: Reusable Call Plans. (line 37)
* ffi_closure_alloc: The Closure API. (line 19)
* ffi_closure_free: The Closure API. (line 26)
* FFI_CLOSURES: The Closure API. (line 13)
@@ -1075,6 +1171,8 @@ Index
* ffi_type <1>: Structures. (line 10)
* ffi_type <2>: Complex. (line 15)
* ffi_type <3>: Complex. (line 15)
* ffi_type <4>: Vector Types. (line 13)
* ffi_type <5>: Vector Types. (line 13)
* ffi_type_complex_double: Primitive Types. (line 82)
* ffi_type_complex_float: Primitive Types. (line 79)
* ffi_type_complex_longdouble: Primitive Types. (line 85)
@@ -1101,6 +1199,7 @@ Index
* ffi_type_void: Primitive Types. (line 10)
* Foreign Function Interface: Introduction. (line 31)
* size_t: The Basics. (line 125)
* size_t <1>: Reusable Call Plans. (line 37)
* unsigned int: The Basics. (line 122)
* unsigned long: The Basics. (line 117)
* void: The Basics. (line 72)
@@ -1118,21 +1217,22 @@ Node: Using libffi4569
Node: The Basics5172
Node: Simple Example11346
Node: Types12403
Node: Primitive Types12914
Node: Structures15231
Node: Size and Alignment16342
Node: Arrays Unions Enums18613
Node: Type Example21590
Node: Complex22896
Node: Complex Type Example24410
Node: Multiple ABIs27462
Node: Reusable Call Plans27849
Node: The Closure API29566
Node: Closure Example33908
Node: Thread Safety35552
Node: Memory Usage36385
Node: Missing Features37660
Node: Index38037
Node: Primitive Types12967
Node: Structures15284
Node: Size and Alignment16395
Node: Arrays Unions Enums18666
Node: Type Example21643
Node: Complex22949
Node: Complex Type Example24463
Node: Vector Types27536
Node: Multiple ABIs31575
Node: Reusable Call Plans31962
Node: The Closure API34155
Node: Closure Example38497
Node: Thread Safety40141
Node: Memory Usage40974
Node: Missing Features42249
Node: Index42626

End Tag Table
BIN
View File
Binary file not shown.
+98
View File
@@ -320,6 +320,7 @@ int main()
* Type Example:: Structure type example.
* Complex:: Complex types.
* Complex Type Example:: Complex type example.
* Vector Types:: Vector (SIMD) types.
@end menu
@node Primitive Types
@@ -802,6 +803,93 @@ FFI_COMPLEX_TYPEDEF(uchar, unsigned char, ffi_type_uint8);
The new type descriptors can then be used like one of the built-in
type descriptors in the previous example.
@node Vector Types
@subsection Vector Types
@code{libffi} can marshal vector (SIMD) types --- the values produced
by GCC's @code{__attribute__((vector_size (N)))} and Clang's
@code{ext_vector_type} --- on the platforms listed in the support table
below. A vector is described just like a structure, except that every
element pointer refers to the @emph{same} fundamental scalar type and the
number of elements is the number of vector lanes.
@tindex ffi_type
@deftp {Data type} ffi_type
@table @code
@item size_t size
This must be set to @code{0}. @code{libffi} computes the storage size
(see below) from the element type and lane count.
@item unsigned short alignment
This must be set to @code{0}. @code{libffi} computes the alignment.
@item unsigned short type
For a vector type, this must be set to @code{FFI_TYPE_VECTOR}.
@item ffi_type **elements
This is a @samp{NULL}-terminated array of pointers to @code{ffi_type}
objects. Every entry must point to the same scalar element type, and the
number of entries is the vector's lane count @math{N} (@math{N >= 1}). The
element type must be one of @code{ffi_type_float}, @code{ffi_type_double},
or a fixed-width integer (@code{ffi_type_uint8} through
@code{ffi_type_sint64}); @code{long double} and aggregate element types are
not permitted.
@end table
@end deftp
@subsubheading Computed layout
Because the caller leaves @code{size} and @code{alignment} at @code{0},
@code{libffi} derives them so that applications need not encode
compiler- or platform-specific rules:
@itemize @bullet
@item
@code{size} is @math{lane\_size \times N} rounded @emph{up} to the next
power of two. This matches Clang's @code{ext_vector_type} storage --- for
example a three-lane @code{float} vector occupies 16 bytes and a three-lane
@code{double} vector occupies 32 bytes. GCC's @code{vector_size} already
requires power-of-two byte totals, so the rule is identical there.
@item
@code{alignment} is @code{min(size, 16)}.
@end itemize
If the element list is heterogeneous, empty, or uses a disallowed element
type, @code{ffi_prep_cif} returns @code{FFI_BAD_TYPEDEF}.
@subsubheading psABI framing
At the call boundary the platform's processor-specific ABI (AAPCS64 on
AArch64, the System V x86-64 psABI on x86-64) decides how a vector is
passed and returned, independently of which compiler produced it. The
historical divergence between GCC's @code{vector_size} and Clang's
@code{ext_vector_type} concerns only in-memory @emph{layout} (notably the
padding of odd-lane vectors such as @code{float3}); the power-of-two size
rule above pins that layout down, so a value marshalled by @code{libffi}
matches what a natively compiled caller or callee expects.
@subsubheading Per-port support
@multitable @columnfractions .28 .72
@headitem Port @tab Vector support
@item AArch64 (AAPCS64)
@tab 8- and 16-byte vectors in a single V/Q register; homogeneous vector
aggregates (structs of up to four identical 8- or 16-byte vectors) in
consecutive V/Q registers. A bare vector larger than 16 bytes (for
example a 32-byte @code{double4}) has no short-vector register class and is
passed and returned in memory, exactly as AAPCS64 and current compilers do.
@item x86-64 (System V psABI)
@tab 8- and 16-byte vectors in an SSE register (@code{%xmm0} for returns).
A bare vector larger than 16 bytes needs @code{%ymm}/@code{%zmm} register
handling that this port does not yet implement, so @code{ffi_prep_cif}
returns @code{FFI_BAD_TYPEDEF} for it.
@item Other ports
@tab Not supported: @code{ffi_prep_cif} returns @code{FFI_BAD_TYPEDEF} for
any signature that mentions a vector type, including one nested inside a
struct.
@end multitable
@node Multiple ABIs
@section Multiple ABIs
@@ -853,6 +941,16 @@ Releases a plan returned by @code{ffi_call_plan_alloc}. Passing
not affected.
@end defun
@findex ffi_call_plan_size
@defun size_t ffi_call_plan_size (ffi_call_plan *@var{plan})
Returns the total number of bytes @code{libffi} allocated for @var{plan},
including any internal argument-placement data it owns. Returns zero when
@var{plan} is @code{NULL}. The result does not include the
@code{ffi_cif}, which the caller owns. This is intended for embedders that
account for the memory held by long-lived plans and would otherwise have to
guess at the size of an opaque type.
@end defun
@node The Closure API
@section The Closure API
+4 -4
View File
@@ -1,4 +1,4 @@
@set UPDATED 10 July 2026
@set UPDATED-MONTH July 2026
@set EDITION 3.7.1
@set VERSION 3.7.1
@set UPDATED 8 August 2026
@set UPDATED-MONTH August 2026
@set EDITION 3.8.0
@set VERSION 3.8.0
+4 -4
View File
@@ -1,4 +1,4 @@
@set UPDATED 10 July 2026
@set UPDATED-MONTH July 2026
@set EDITION 3.7.1
@set VERSION 3.7.1
@set UPDATED 8 August 2026
@set UPDATED-MONTH August 2026
@set EDITION 3.8.0
@set VERSION 3.8.0
+2 -2
View File
@@ -7,8 +7,8 @@ import sys
from pathlib import Path
LIBFFI_VERSION = '3.7.1'
LIBFFI_VERSION_NUMBER = '30701'
LIBFFI_VERSION = '3.8.0'
LIBFFI_VERSION_NUMBER = '30800'
def normalize_arch(target_arch):
aliases = {
+10 -2
View File
@@ -79,9 +79,10 @@ extern "C" {
#define FFI_TYPE_COMPLEX 15
#define FFI_TYPE_UINT128 16
#define FFI_TYPE_SINT128 17
#define FFI_TYPE_VECTOR 18
/* This should always refer to the last type code (for sanity checks). */
#define FFI_TYPE_LAST FFI_TYPE_SINT128
#define FFI_TYPE_LAST FFI_TYPE_VECTOR
#include <ffitarget.h>
@@ -535,7 +536,11 @@ void ffi_call(ffi_cif *cif,
ffi_call_plan_alloc returns NULL only on allocation failure; a signature
with no fast path is still valid and ffi_call_plan_invoke falls back to
ffi_call for it. A plan is immutable once built, so it may be shared and
invoked concurrently from multiple threads. */
invoked concurrently from multiple threads.
ffi_call_plan_size reports the total number of bytes libffi allocated for a
plan, so that callers tracking the footprint of long-lived plans do not have
to guess at the size of an opaque type. */
typedef struct ffi_call_plan ffi_call_plan;
FFI_API
@@ -550,6 +555,9 @@ void ffi_call_plan_invoke (ffi_call_plan *plan,
FFI_API
void ffi_call_plan_free (ffi_call_plan *plan);
FFI_API
size_t ffi_call_plan_size (ffi_call_plan *plan);
FFI_API
ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type,
size_t *offsets);
+9
View File
@@ -69,6 +69,15 @@ LIBFFI_CALL_PLAN_8.4 {
ffi_call_plan_free;
} LIBFFI_BASE_8.1;
/* ----------------------------------------------------------------------
Call plan footprint query (ffi_call_plan_size). A fresh node because
LIBFFI_CALL_PLAN_8.4 has already shipped.
-------------------------------------------------------------------- */
LIBFFI_CALL_PLAN_8.5 {
global:
ffi_call_plan_size;
} LIBFFI_CALL_PLAN_8.4;
#ifdef FFI_TARGET_HAS_COMPLEX_TYPE
LIBFFI_COMPLEX_8.0 {
global:
+1 -1
View File
@@ -26,4 +26,4 @@
# release, then set age to 0.
#
# CURRENT:REVISION:AGE
12:1:4
13:0:5
+114 -6
View File
@@ -92,6 +92,19 @@ ffi_clear_cache (void *start, void *end)
#endif
/* Return the base-2 logarithm of N (N assumed to be a power of two). Used
to map a vector register width (8 or 16 bytes) onto the D-/Q-register
AARCH64_RET_* encoding. */
static int
intlog2 (int n)
{
int level = 0;
while (n >>= 1)
++level;
return level;
}
/* A subroutine of is_vfp_type. Given a structure type, return the type code
of the first non-structure element. Recurse for structure elements.
Return -1 if the structure is in fact empty, i.e. no nested elements. */
@@ -106,7 +119,8 @@ is_hfa0 (const ffi_type *ty)
for (i = 0; elements[i]; ++i)
{
ret = elements[i]->type;
if (ret == FFI_TYPE_STRUCT || ret == FFI_TYPE_COMPLEX)
if (ret == FFI_TYPE_STRUCT || ret == FFI_TYPE_VECTOR
|| ret == FFI_TYPE_COMPLEX)
{
ret = is_hfa0 (elements[i]);
if (ret < 0)
@@ -118,6 +132,33 @@ is_hfa0 (const ffi_type *ty)
return ret;
}
/* A subroutine of is_vfp_type. Return the size in bytes of the vector (SIMD)
member of TY, i.e. the width of a single Neon register slot, or 0 if TY
neither is nor contains a vector. For a bare vector this is its whole size;
for a homogeneous vector aggregate it is the size of one lane vector. */
static size_t
is_simd (const ffi_type *ty)
{
ffi_type **elements;
int i;
if (ty->type == FFI_TYPE_VECTOR)
return ty->size;
elements = ty->elements;
if (elements != NULL)
for (i = 0; elements[i]; ++i)
{
int t = elements[i]->type;
if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_COMPLEX
|| t == FFI_TYPE_VECTOR)
return is_simd (elements[i]);
}
return 0;
}
/* A subroutine of is_vfp_type. Given a structure type, return true if all
of the non-structure elements are the same as CANDIDATE. */
@@ -131,7 +172,8 @@ is_hfa1 (const ffi_type *ty, int candidate)
for (i = 0; elements[i]; ++i)
{
int t = elements[i]->type;
if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_COMPLEX)
if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_VECTOR
|| t == FFI_TYPE_COMPLEX)
{
if (!is_hfa1 (elements[i], candidate))
return 0;
@@ -156,7 +198,7 @@ is_vfp_type (const ffi_type *ty)
{
ffi_type **elements;
int candidate, i;
size_t size, ele_count;
size_t size, ele_count, simd_size;
/* Quickest tests first. */
candidate = ty->type;
@@ -181,18 +223,24 @@ is_vfp_type (const ffi_type *ty)
}
return 0;
case FFI_TYPE_STRUCT:
case FFI_TYPE_VECTOR:
break;
}
/* No HFA types are smaller than 4 bytes, or larger than 64 bytes. */
/* No HFA/HVA types are smaller than 4 bytes, or larger than 64 bytes. */
size = ty->size;
if (size < 4 || size > 64)
return 0;
/* Find the type of the first non-structure member. */
/* Determine the width of the vector (SIMD) member, if any: 0 for a plain
floating-point HFA, else the size in bytes of one Neon register slot. */
simd_size = is_simd (ty);
/* Find the type of the first non-aggregate member. */
elements = ty->elements;
candidate = elements[0]->type;
if (candidate == FFI_TYPE_STRUCT || candidate == FFI_TYPE_COMPLEX)
if (candidate == FFI_TYPE_STRUCT || candidate == FFI_TYPE_VECTOR
|| candidate == FFI_TYPE_COMPLEX)
{
for (i = 0; ; ++i)
{
@@ -202,6 +250,63 @@ is_vfp_type (const ffi_type *ty)
}
}
if (simd_size)
{
/* Vector or homogeneous vector aggregate (HVA). A single Neon slot is
at most 16 bytes (a Q register). A bare vector wider than 16 bytes
(e.g. a 32-byte double4) has no short-vector register class under
AAPCS64, so bail and let the generic composite path pass it by
reference / return it in memory -- matching what current compilers do.
The scalar lane type does not affect register selection (an integer
and a floating-point 16-byte vector both occupy one Q register), so,
unlike the floating-point HFA path below, CANDIDATE is used only to
confirm the lanes are homogeneous. */
size_t reg_size = simd_size;
int num_registers;
int first_level_element_type;
/* A Neon register slot is an S (4B), D (8B) or Q (16B). A lane narrower
than 4 bytes has no short-vector register class under AAPCS64 and would
map below AARCH64_RET_S4, making extend_hfa_type() branch before its
jump table; reject it and let the generic aggregate path handle it. */
if (reg_size < 4 || reg_size > 16 || size % reg_size != 0)
return 0;
num_registers = (int) (size / reg_size);
if (num_registers > 4)
return 0;
/* For an aggregate, every member must itself be a vector (or nested
vector aggregate) of the same register width: this rejects a struct
that mixes a bare scalar with a vector even when the scalar's type
matches the vector's lane type. A bare vector needs no such check --
its lanes were validated when its layout was computed. */
if (ty->type != FFI_TYPE_VECTOR)
for (i = 0; elements[i]; ++i)
if (is_simd (elements[i]) != reg_size)
return 0;
/* Every lane must be the identical scalar type across the whole HVA
(this rejects, e.g., an aggregate mixing float and integer vectors). */
for (i = 0; elements[i]; ++i)
{
int t = elements[i]->type;
if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_VECTOR
|| t == FFI_TYPE_COMPLEX)
{
if (!is_hfa1 (elements[i], candidate))
return 0;
}
else if (t != candidate)
return 0;
}
/* Reuse the AARCH64_RET_{S,D,Q}* codes, which are laid out as
(type * 4) + (4 - count) with FLOAT->S(4B), DOUBLE->D(8B),
LONGDOUBLE->Q(16B). Map the register width onto that type axis. */
first_level_element_type = FFI_TYPE_FLOAT + intlog2 ((int) reg_size) - 2;
return first_level_element_type * 4 + (4 - num_registers);
}
/* If the first member is not a floating point type, it's not an HFA.
Also quickly re-check the size of the structure. */
switch (candidate)
@@ -614,6 +719,7 @@ ffi_prep_cif_machdep (ffi_cif *cif)
case FFI_TYPE_DOUBLE:
case FFI_TYPE_LONGDOUBLE:
case FFI_TYPE_STRUCT:
case FFI_TYPE_VECTOR:
case FFI_TYPE_COMPLEX:
flags = is_vfp_type (rtype);
if (flags == 0)
@@ -802,6 +908,7 @@ ffi_call_int (ffi_cif *cif, void (*fn)(void), void *orig_rvalue,
case FFI_TYPE_DOUBLE:
case FFI_TYPE_LONGDOUBLE:
case FFI_TYPE_STRUCT:
case FFI_TYPE_VECTOR:
case FFI_TYPE_COMPLEX:
{
h = is_vfp_type (ty);
@@ -1089,6 +1196,7 @@ ffi_closure_SYSV_inner (ffi_cif *cif,
case FFI_TYPE_DOUBLE:
case FFI_TYPE_LONGDOUBLE:
case FFI_TYPE_STRUCT:
case FFI_TYPE_VECTOR:
case FFI_TYPE_COMPLEX:
h = is_vfp_type (ty);
if (h)
+4
View File
@@ -94,6 +94,10 @@ typedef enum ffi_abi
#define FFI_TARGET_HAS_COMPLEX_TYPE
#endif
/* AAPCS64 passes 8- and 16-byte vectors in V/Q registers and homogeneous
vector aggregates in consecutive V/Q registers; see is_vfp_type. */
#define FFI_TARGET_HAS_VECTOR_TYPE
#define FFI_TARGET_HAS_INT128 1
#endif
+2 -1
View File
@@ -54,7 +54,8 @@ void ffi_type_test(ffi_type *a, const char *file, int line)
FFI_ASSERT_AT(a->type <= FFI_TYPE_LAST, file, line);
FFI_ASSERT_AT(a->type == FFI_TYPE_VOID || a->size > 0, file, line);
FFI_ASSERT_AT(a->type == FFI_TYPE_VOID || a->alignment > 0, file, line);
FFI_ASSERT_AT((a->type != FFI_TYPE_STRUCT && a->type != FFI_TYPE_COMPLEX)
FFI_ASSERT_AT((a->type != FFI_TYPE_STRUCT && a->type != FFI_TYPE_COMPLEX
&& a->type != FFI_TYPE_VECTOR)
|| a->elements != NULL, file, line);
FFI_ASSERT_AT(a->type != FFI_TYPE_COMPLEX
|| (a->elements != NULL
+11
View File
@@ -38,3 +38,14 @@
#define FFI_IA64_TYPE_HFA_FLOAT (FFI_TYPE_LAST + 2)
#define FFI_IA64_TYPE_HFA_DOUBLE (FFI_TYPE_LAST + 3)
#define FFI_IA64_TYPE_HFA_LDOUBLE (FFI_TYPE_LAST + 4)
/* Tripwire: the .Lst_table / .Lld_table return-value jump tables in unix.S place
the FFI_IA64_TYPE_* pseudo-types (which are FFI_TYPE_LAST-relative) immediately
after the generic FFI_TYPE_* codes. Adding a new generic type bumps
FFI_TYPE_LAST, shifts those codes, and desyncs the tables -- silently
misdispatching small-struct/HFA returns. When this fires: add a matching slot
for the new type to both tables in unix.S, then bump FFI_IA64_TYPE_LAST. */
#define FFI_IA64_TYPE_LAST FFI_TYPE_VECTOR
#if FFI_TYPE_LAST != FFI_IA64_TYPE_LAST
# error "new FFI_TYPE_* added: sync the unix.S jump tables and bump FFI_IA64_TYPE_LAST"
#endif
+6
View File
@@ -553,6 +553,9 @@ ffi_closure_unix:
data8 @pcrel(.Lst_void) // FFI_TYPE_STRUCT
data8 @pcrel(.Lst_int64) // FFI_TYPE_POINTER
data8 @pcrel(.Lst_void) // FFI_TYPE_COMPLEX (not implemented)
data8 @pcrel(.Lst_void) // FFI_TYPE_UINT128 (not implemented)
data8 @pcrel(.Lst_void) // FFI_TYPE_SINT128 (not implemented)
data8 @pcrel(.Lst_void) // FFI_TYPE_VECTOR (rejected in ffi_prep_cif_core)
data8 @pcrel(.Lst_small_struct) // FFI_IA64_TYPE_SMALL_STRUCT
data8 @pcrel(.Lst_hfa_float) // FFI_IA64_TYPE_HFA_FLOAT
data8 @pcrel(.Lst_hfa_double) // FFI_IA64_TYPE_HFA_DOUBLE
@@ -575,6 +578,9 @@ ffi_closure_unix:
data8 @pcrel(.Lld_void) // FFI_TYPE_STRUCT
data8 @pcrel(.Lld_int) // FFI_TYPE_POINTER
data8 @pcrel(.Lld_void) // FFI_TYPE_COMPLEX (not implemented)
data8 @pcrel(.Lld_void) // FFI_TYPE_UINT128 (not implemented)
data8 @pcrel(.Lld_void) // FFI_TYPE_SINT128 (not implemented)
data8 @pcrel(.Lld_void) // FFI_TYPE_VECTOR (rejected in ffi_prep_cif_core)
data8 @pcrel(.Lld_small_struct) // FFI_IA64_TYPE_SMALL_STRUCT
data8 @pcrel(.Lld_hfa_float) // FFI_IA64_TYPE_HFA_FLOAT
data8 @pcrel(.Lld_hfa_double) // FFI_IA64_TYPE_HFA_DOUBLE
+2 -1
View File
@@ -58,7 +58,8 @@ ffi_java_raw_size (ffi_cif *cif)
result += 2 * FFI_SIZEOF_JAVA_RAW;
break;
case FFI_TYPE_STRUCT:
/* No structure parameters in Java. */
case FFI_TYPE_VECTOR:
/* No structure or vector parameters in Java. */
abort();
case FFI_TYPE_COMPLEX:
/* Not supported yet. */
+7 -2
View File
@@ -89,8 +89,13 @@ typedef enum ffi_abi {
to the default case and is mapped to FFI_TYPE_INT, so cif->flags never
exceeds FFI_TYPE_COMPLEX and the existing tables remain sufficient. Bump
FFI_PA_TYPE_LAST to the current FFI_TYPE_LAST once you have confirmed any
newly added generic type is likewise handled (or the tables extended). */
#define FFI_PA_TYPE_LAST FFI_TYPE_SINT128
newly added generic type is likewise handled (or the tables extended).
FFI_TYPE_VECTOR (18) is likewise not reached here: PA does not define
FFI_TARGET_HAS_VECTOR_TYPE, so ffi_prep_cif_core rejects any vector
signature with FFI_BAD_TYPEDEF before machdep runs. Bumping the tripwire
past it is therefore safe. */
#define FFI_PA_TYPE_LAST FFI_TYPE_VECTOR
/* Tripwire: when a new generic type is added FFI_TYPE_LAST changes and this
fires, forcing a review of ffi_prep_cif_machdep and the linux.S / hpux32.S
+56 -75
View File
@@ -186,19 +186,17 @@ LCFI1:
/* Make the call. */
bl BLCLS_HELP
/* r3 contains the rtype pointer... save it since we will need
it later. */
sg r3,LINKAGE_SIZE(r1) ; ffi_type * result_type
lg r0,0(r3) ; size => r0
lhz r3,FFI_TYPE_TYPE(r3) ; type => r3
/* r3 now holds a small PPC_LD_* jump-table index (see the PPC_LD_*
defines in ffi_darwin.c), not an ffi_type* as this file previously
assumed: ffi_closure_helper_common cannot return both an ffi_type*
and the dispatch index through r3, so it returns the index. The
helper has already intercepted by-reference struct returns (writing
the result to the caller`s buffer and returning PPC_LD_NONE); for a
by-value struct return it returns PPC_LD_STRUCT and stashes cif->rtype
in the first parameter-save slot, which the PPC_LD_STRUCT fragment
below recovers. */
/* The helper will have intercepted structure returns and inserted
the caller`s destination address for structs returned by ref. */
/* r3 contains the return type so use it to look up in a table
so we know how to deal with each type. */
addi r5,r1,(SAVE_SIZE-RESULT_BYTES) /* Otherwise, our return is here. */
addi r5,r1,(SAVE_SIZE-RESULT_BYTES) /* Our return value is here. */
bl Lget_ret_type0_addr /* Get pointer to Lret_type0 into LR. */
mflr r4 /* Move to r4. */
slwi r3,r3,4 /* Now multiply return type by 16. */
@@ -218,43 +216,60 @@ LFE1:
Lget_ret_type0_addr:
blrl
/* case FFI_TYPE_VOID */
/* The fragments below are indexed by the PPC_LD_* return code that
ffi_closure_helper_common handed back in r3, so their order must match the
PPC_LD_* values in ffi_darwin.c. Each is exactly 16 bytes (four
instructions), except the final PPC_LD_STRUCT fragment. */
/* case PPC_LD_NONE (void, or a struct returned by reference) */
Lret_type0:
b Lfinish
nop
nop
nop
/* case FFI_TYPE_INT */
/* case PPC_LD_R3 (one GPR: int, pointer, and on ppc64 also 64-bit ints) */
Lret_type1:
lg r3,0(r5)
b Lfinish
nop
nop
/* case FFI_TYPE_FLOAT */
/* case PPC_LD_R3R4 (two GPRs: the 32-bit ABI`s 64-bit integer) */
Lret_type2:
#if defined(__ppc64__)
lg r3,0(r5)
lg r4,8(r5)
#else
lwz r3,0(r5)
lwz r4,4(r5)
#endif
b Lfinish
nop
/* case PPC_LD_F32 */
Lret_type3:
lfs f1,0(r5)
b Lfinish
nop
nop
/* case FFI_TYPE_DOUBLE */
Lret_type3:
/* case PPC_LD_F64 */
Lret_type4:
lfd f1,0(r5)
b Lfinish
nop
nop
/* case FFI_TYPE_LONGDOUBLE */
Lret_type4:
/* case PPC_LD_F128 (128-bit long double: two doubles) */
Lret_type5:
lfd f1,0(r5)
lfd f2,8(r5)
b Lfinish
nop
/* case FFI_TYPE_UINT8 */
Lret_type5:
/* case PPC_LD_U8 */
Lret_type6:
#if defined(__ppc64__)
lbz r3,7(r5)
#else
@@ -264,8 +279,8 @@ Lret_type5:
nop
nop
/* case FFI_TYPE_SINT8 */
Lret_type6:
/* case PPC_LD_S8 */
Lret_type7:
#if defined(__ppc64__)
lbz r3,7(r5)
#else
@@ -275,8 +290,8 @@ Lret_type6:
b Lfinish
nop
/* case FFI_TYPE_UINT16 */
Lret_type7:
/* case PPC_LD_U16 */
Lret_type8:
#if defined(__ppc64__)
lhz r3,6(r5)
#else
@@ -286,8 +301,8 @@ Lret_type7:
nop
nop
/* case FFI_TYPE_SINT16 */
Lret_type8:
/* case PPC_LD_S16 */
Lret_type9:
#if defined(__ppc64__)
lha r3,6(r5)
#else
@@ -297,77 +312,43 @@ Lret_type8:
nop
nop
/* case FFI_TYPE_UINT32 */
Lret_type9:
#if defined(__ppc64__)
lwz r3,4(r5)
#else
lwz r3,0(r5)
#endif
b Lfinish
nop
nop
/* case FFI_TYPE_SINT32 */
/* case PPC_LD_U32 (ppc64 only; the 32-bit ABI aliases U32 to PPC_LD_R3) */
Lret_type10:
#if defined(__ppc64__)
lwz r3,4(r5)
#else
lwz r3,0(r5)
#endif
b Lfinish
nop
nop
/* case FFI_TYPE_UINT64 */
/* case PPC_LD_S32 (ppc64 only; the 32-bit ABI aliases S32 to PPC_LD_R3) */
Lret_type11:
#if defined(__ppc64__)
lg r3,0(r5)
lwa r3,4(r5)
b Lfinish
nop
#else
lwz r3,0(r5)
lwz r4,4(r5)
b Lfinish
nop
#endif
nop
/* case FFI_TYPE_SINT64 */
Lret_type12:
#if defined(__ppc64__)
lg r3,0(r5)
b Lfinish
nop
#else
lwz r3,0(r5)
lwz r4,4(r5)
b Lfinish
#endif
nop
/* case FFI_TYPE_STRUCT */
Lret_type13:
/* case PPC_LD_STRUCT (a by-value struct return). This is the final,
variable-length fragment, so it need not be padded to 16 bytes. The helper
stashed cif->rtype in the first parameter-save slot (see ffi_darwin.c),
because the small dispatch index in r3 left no room for it. */
Lret_type_struct:
lg r6,PARENT_PARM_BASE(r1) ; cif->rtype
sg r6,LINKAGE_SIZE(r1) ; where the struct code below expects it
lg r0,0(r6) ; size => r0
#if defined(__ppc64__)
lg r3,0(r5) ; we need at least this...
cmpi 0,r0,4
bgt Lstructend ; not a special small case
b Lsmallstruct ; see if we need more.
#else
cmpwi 0,r0,4
bgt Lfinish ; not by value
lg r3,0(r5)
lg r3,0(r5) ; a <=4-byte struct, returned in r3
b Lfinish
#endif
/* case FFI_TYPE_POINTER */
Lret_type14:
lg r3,0(r5)
b Lfinish
nop
nop
#if defined(__ppc64__)
Lsmallstruct:
beq Lfour ; continuation of Lret13.
beq Lfour ; continuation of Lret_type_struct.
cmpi 0,r0,3
beq Lfinish ; don`t adjust this - can`t be any floats here...
srdi r3,r3,48
+23
View File
@@ -60,11 +60,13 @@ struct ffi_aix_trampoline_struct {
# define PPC_LD_S32 PPC_LD_R3
# define PPC_LD_PTR PPC_LD_R3
# define PPC_LD_I64 PPC_LD_R3R4
# define PPC_LD_STRUCT 10
#else
# define PPC_LD_U32 10
# define PPC_LD_S32 11
# define PPC_LD_PTR PPC_LD_R3
# define PPC_LD_I64 PPC_LD_R3
# define PPC_LD_STRUCT 12
#endif
extern void ffi_closure_ASM (void);
@@ -1260,6 +1262,13 @@ ffi_closure_helper_common (ffi_cif* cif,
long i, avn;
ffi_dblfl * end_pfr = pfr + NUM_FPR_ARG_REGISTERS;
unsigned size_al;
int struct_ret_by_value = 0;
/* When a struct is returned by value, ffi_closure_ASM's jump-table
dispatch carries only a small integer return code (see PPC_LD_* above),
with no room for cif->rtype. We hand cif->rtype back in the first
parameter-save slot -- which is dead by the time we return -- for the
PPC_LD_STRUCT fragment in darwin_closure.S to recover. */
unsigned long * pgr0 = pgr;
#if defined(POWERPC_DARWIN64)
unsigned fpsused = 0;
#endif
@@ -1275,12 +1284,16 @@ ffi_closure_helper_common (ffi_cif* cif,
rvalue = (void *) *pgr;
pgr++;
}
else
struct_ret_by_value = 1;
#elif defined(DARWIN_PPC)
if (cif->rtype->size > 4)
{
rvalue = (void *) *pgr;
pgr++;
}
else
struct_ret_by_value = 1;
#else /* assume we return by ref. */
rvalue = (void *) *pgr;
pgr++;
@@ -1480,7 +1493,17 @@ ffi_closure_helper_common (ffi_cif* cif,
switch (cif->rtype->type)
{
case FFI_TYPE_VOID:
return PPC_LD_NONE;
case FFI_TYPE_STRUCT:
/* A by-reference struct return needs nothing further here: the result
was written straight to the caller's buffer. A by-value struct
return is loaded into registers by darwin_closure.S, which needs
cif->rtype -- hand it back in the first parameter-save slot. */
if (struct_ret_by_value)
{
*pgr0 = (unsigned long) cif->rtype;
return PPC_LD_STRUCT;
}
return PPC_LD_NONE;
case FFI_TYPE_FLOAT:
return PPC_LD_F32;
+121 -16
View File
@@ -107,8 +107,13 @@ discover_homogeneous_aggregate (ffi_abi abi,
unsigned int inner_elnum = 0;
unsigned int inner
= discover_homogeneous_aggregate (abi, t->elements[0], &inner_elnum);
if (inner == FFI_TYPE_FLOAT || inner == FFI_TYPE_DOUBLE)
if (inner == FFI_TYPE_FLOAT || inner == FFI_TYPE_DOUBLE
|| inner == FFI_TYPE_LONGDOUBLE)
{
/* A _Complex of an FP base counts as two of that base: an
FP-HFA struct member. For IBM-128 long double each half is
itself two FPRs (inner_elnum == 2), so a _Complex long double
contributes four FPRs. */
*elnum = 2 * inner_elnum;
return inner;
}
@@ -257,11 +262,17 @@ homogeneous:
goto homogeneous;
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
case FFI_TYPE_LONGDOUBLE:
/* Only the 64-bit long double case is wired up; IBM-128 and
IEEE-binary128 _Complex are left as a follow-up. */
if ((cif->abi & (FFI_LINUX_LONG_DOUBLE_128
| FFI_LINUX_LONG_DOUBLE_IEEE128)) != 0)
return FFI_BAD_TYPEDEF;
if ((cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
{
/* IEEE-128 _Complex long double: real in v2, imag in v3.
Return via the vector-homogeneous small-struct path. */
flags |= FLAG_RETURNS_SMST | FLAG_RETURNS_VEC;
break;
}
/* IBM-128 _Complex long double is returned like a homogeneous
aggregate of doubles: real in f1:f2, imag in f3:f4. (For a
64-bit long double this reduces to the FFI_TYPE_DOUBLE case,
real in f1 and imag in f2.) */
flags |= FLAG_RETURNS_SMST;
rtype = FFI_TYPE_DOUBLE;
goto homogeneous;
@@ -393,11 +404,21 @@ homogeneous:
break;
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
case FFI_TYPE_LONGDOUBLE:
if ((cif->abi & (FFI_LINUX_LONG_DOUBLE_128
| FFI_LINUX_LONG_DOUBLE_IEEE128)) != 0)
return FFI_BAD_TYPEDEF;
fparg_count += 2;
intarg_count += 2;
if ((cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
{
/* Two IEEE-128 halves: each occupies a vector register plus
two GPR shadow doublewords, the pair 16-byte aligned. */
vecarg_count += 2;
intarg_count = (intarg_count + 1) & ~0x1;
intarg_count += 4;
if (vecarg_count > NUM_VEC_ARG_REGISTERS64)
flags |= FLAG_ARG_NEEDS_PSAVE;
break;
}
/* IBM-128: each half is a pair of FPRs, and each FPR half
consumes a GPR shadow doubleword -- four of each in total. */
fparg_count += 4;
intarg_count += 4;
if (fparg_count > NUM_FPR_ARG_REGISTERS64)
flags |= FLAG_ARG_NEEDS_PSAVE;
break;
@@ -755,10 +776,51 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
case FFI_TYPE_COMPLEX:
elt = (*ptr)->elements[0]->type;
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
/* 64-bit long double is equivalent to double; the IBM-128 and
IEEE-binary128 variants were rejected in prep_cif. */
if (elt == FFI_TYPE_LONGDOUBLE
&& (ecif->cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
{
/* IEEE-128 _Complex long double: each half goes in its own
vector register (or the parameter save area), 16-byte
aligned, consuming two GPR shadow doublewords. */
float128 *cval = (float128 *) *p_argv.v;
unsigned int j;
for (j = 0; j < 2; j++)
{
next_arg.p = FFI_ALIGN (next_arg.p, 16);
if (next_arg.ul == gpr_end.ul)
next_arg.ul = rest.ul;
if (vecarg_count < NUM_VEC_ARG_REGISTERS64 && i < nfixedargs)
memcpy (vec_base.f128++, cval + j, sizeof (float128));
else
memcpy (next_arg.f128, cval + j, sizeof (float128));
if (++next_arg.f128 == gpr_end.f128)
next_arg.f128 = rest.f128;
vecarg_count++;
}
FFI_ASSERT (flags & FLAG_VEC_ARGUMENTS);
break;
}
if (elt == FFI_TYPE_LONGDOUBLE)
elt = FFI_TYPE_DOUBLE;
{
/* IBM-128 _Complex long double: four doubles (real hi/lo,
imag hi/lo) into consecutive FPRs, each with a GPR shadow
doubleword. */
double *cval = (double *) *p_argv.v;
unsigned int j;
for (j = 0; j < 4; j++)
{
double_tmp = cval[j];
if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
*fpr_base.d++ = double_tmp;
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;
}
#endif
if (elt == FFI_TYPE_FLOAT)
{
@@ -1336,8 +1398,45 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
unsigned int j;
elt = arg_types[i]->elements[0]->type;
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
if (elt == FFI_TYPE_LONGDOUBLE
&& (cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
{
/* IEEE-128: each half arrives in a vector register (or the
16-byte-aligned parameter save area) with two GPR shadow
doublewords. */
float128 *cval = alloca (2 * sizeof (float128));
if (((unsigned long) pst & 0xF) != 0)
++pst;
for (j = 0; j < 2; j++)
{
if (pvec < end_pvec && i < nfixedargs)
memcpy (&cval[j], pvec++, sizeof (float128));
else
memcpy (&cval[j], pst, sizeof (float128));
pst += 2;
}
avalue[i] = cval;
break;
}
if (elt == FFI_TYPE_LONGDOUBLE)
elt = FFI_TYPE_DOUBLE;
{
/* IBM-128: four doubles, each in an FPR (or one GPR shadow
doubleword) -- real hi/lo then imag hi/lo. */
double *cval = alloca (4 * sizeof (double));
for (j = 0; j < 4; j++)
{
if (pfr < end_pfr && i < nfixedargs)
{
cval[j] = pfr->d;
pfr++;
}
else
cval[j] = *(double *) pst;
pst++;
}
avalue[i] = cval;
break;
}
#endif
if (elt == FFI_TYPE_FLOAT)
{
@@ -1448,7 +1547,13 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
int inner = cif->rtype->elements[0]->type;
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
if (inner == FFI_TYPE_LONGDOUBLE)
inner = FFI_TYPE_DOUBLE;
{
/* IEEE-128 _Complex long double returns in v2:v3; IBM-128 in
f1:f2 (real) and f3:f4 (imag), i.e. as a double HFA. */
if ((cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
return PPC64_LD_VECTOR_HOMOG;
inner = FFI_TYPE_DOUBLE;
}
#endif
if (inner == FFI_TYPE_FLOAT)
return PPC64_LD_FLOAT_HOMOG;
+15
View File
@@ -345,6 +345,21 @@ E PPC64_LD_STRUCT_3
lwz %r3, RETVAL+4(%r1)
srd %r3, %r3, 8
epilogue
E PPC64_LD_STRUCT_5
ld %r3, RETVAL+0(%r1)
srdi %r3, %r3, 24
epilogue
E PPC64_LD_STRUCT_6
ld %r3, RETVAL+0(%r1)
srdi %r3, %r3, 16
epilogue
E PPC64_LD_STRUCT_7
ld %r3, RETVAL+0(%r1)
srdi %r3, %r3, 8
epilogue
#endif
.Lmoredouble:
+109 -1
View File
@@ -32,6 +32,72 @@
#define STACK_ARG_SIZE(x) FFI_ALIGN(x, FFI_SIZEOF_ARG)
/* Compute the machine-independent layout of a vector (SIMD) type.
A vector is described exactly like a struct -- arg->elements is a
NULL-terminated array of pointers -- but every element must point to the
SAME fundamental scalar type, and the count is the number of lanes. The
caller leaves arg->size and arg->alignment as zero; libffi derives them:
size = lane_size * lane_count, rounded UP to the next power of two
(matching Clang's ext_vector_type storage, e.g. 3 x float
-> 16; GCC's vector_size already requires power-of-two totals
so the rule is identical there);
alignment = min(size, 16).
Only float, double and the fixed-width integer scalars (UINT8..SINT64) are
valid lane types. Anything else -- a heterogeneous element list, an
aggregate lane, long double, or a zero-length vector -- is FFI_BAD_TYPEDEF. */
static ffi_status
initialize_vector (ffi_type *arg)
{
ffi_type **ptr = arg->elements;
ffi_type *elem;
size_t count = 0;
size_t total, p2;
if (UNLIKELY (ptr == NULL || *ptr == NULL))
return FFI_BAD_TYPEDEF;
elem = *ptr;
switch (elem->type)
{
case FFI_TYPE_FLOAT:
case FFI_TYPE_DOUBLE:
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_UINT64:
case FFI_TYPE_SINT64:
break;
default:
return FFI_BAD_TYPEDEF;
}
/* Every lane must be the identical scalar type. */
for (; *ptr != NULL; ptr++)
{
if ((*ptr)->type != elem->type || (*ptr)->size != elem->size)
return FFI_BAD_TYPEDEF;
count++;
}
if (UNLIKELY (count < 1 || elem->size == 0))
return FFI_BAD_TYPEDEF;
total = elem->size * count;
for (p2 = 1; p2 < total; p2 <<= 1)
;
arg->size = p2;
arg->alignment = p2 < 16 ? p2 : 16;
return FFI_OK;
}
/* Perform machine independent initialization of aggregate type
specifications. */
@@ -42,6 +108,9 @@ static ffi_status initialize_aggregate(ffi_type *arg, size_t *offsets)
if (UNLIKELY(arg == NULL || arg->elements == NULL))
return FFI_BAD_TYPEDEF;
if (arg->type == FFI_TYPE_VECTOR)
return initialize_vector (arg);
arg->size = 0;
arg->alignment = 0;
@@ -92,6 +161,28 @@ static ffi_status initialize_aggregate(ffi_type *arg, size_t *offsets)
return FFI_OK;
}
#ifndef FFI_TARGET_HAS_VECTOR_TYPE
/* Recursively test whether TY is, or contains, a vector (SIMD) type. Ports
that do not define FFI_TARGET_HAS_VECTOR_TYPE cannot marshal vectors, so
ffi_prep_cif_core rejects any signature that mentions one (directly or
nested inside a struct) with FFI_BAD_TYPEDEF rather than aborting. */
static int
ffi_type_contains_vector (ffi_type *ty)
{
ffi_type **p;
if (ty == NULL)
return 0;
if (ty->type == FFI_TYPE_VECTOR)
return 1;
if (ty->type == FFI_TYPE_STRUCT && ty->elements != NULL)
for (p = ty->elements; *p != NULL; p++)
if (ffi_type_contains_vector (*p))
return 1;
return 0;
}
#endif /* !FFI_TARGET_HAS_VECTOR_TYPE */
#ifndef __CRIS__
/* The CRIS ABI specifies structure elements to have byte
alignment only, so it completely overrides this functions,
@@ -129,6 +220,15 @@ ffi_status FFI_HIDDEN ffi_prep_cif_core(ffi_cif *cif, ffi_abi abi,
cif->nargs = ntotalargs;
cif->rtype = rtype;
#ifndef FFI_TARGET_HAS_VECTOR_TYPE
/* Vector (SIMD) types are only marshalled on ports that opt in. */
if (ffi_type_contains_vector (rtype))
return FFI_BAD_TYPEDEF;
for (i = 0; i < ntotalargs; i++)
if (ffi_type_contains_vector (atypes[i]))
return FFI_BAD_TYPEDEF;
#endif
cif->flags = 0;
#if (defined(_M_ARM64) || defined(__aarch64__)) && defined(_WIN32)
cif->is_variadic = isvariadic;
@@ -152,7 +252,8 @@ ffi_status FFI_HIDDEN ffi_prep_cif_core(ffi_cif *cif, ffi_abi abi,
/* x86, x86-64 and s390 stack space allocation is handled in prep_machdep. */
#if !defined FFI_TARGET_SPECIFIC_STACK_SPACE_ALLOCATION
/* Make space for the return structure pointer */
if (cif->rtype->type == FFI_TYPE_STRUCT
if ((cif->rtype->type == FFI_TYPE_STRUCT
|| cif->rtype->type == FFI_TYPE_VECTOR)
#ifdef TILE
&& (cif->rtype->size > 10 * FFI_SIZEOF_ARG)
#endif
@@ -316,4 +417,11 @@ ffi_call_plan_free (ffi_call_plan *plan)
free (plan);
}
size_t
ffi_call_plan_size (ffi_call_plan *plan)
{
/* The generic plan is a bare handle; there is no separate move-list. */
return plan != NULL ? sizeof (struct ffi_call_plan) : 0;
}
#endif /* generic ffi_call_plan fallback */
+5 -3
View File
@@ -42,7 +42,7 @@ ffi_raw_size (ffi_cif *cif)
for (i = cif->nargs-1; i >= 0; i--, at++)
{
#if !FFI_NO_STRUCTS
if ((*at)->type == FFI_TYPE_STRUCT)
if ((*at)->type == FFI_TYPE_STRUCT || (*at)->type == FFI_TYPE_VECTOR)
result += FFI_ALIGN (sizeof (void*), FFI_SIZEOF_ARG);
else
#endif
@@ -82,8 +82,9 @@ ffi_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args)
break;
#endif
#if !FFI_NO_STRUCTS
#if !FFI_NO_STRUCTS
case FFI_TYPE_STRUCT:
case FFI_TYPE_VECTOR:
*args = (raw++)->ptr;
break;
#endif
@@ -110,7 +111,7 @@ ffi_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args)
for (i = 0; i < cif->nargs; i++, tp++, args++)
{
#if !FFI_NO_STRUCTS
if ((*tp)->type == FFI_TYPE_STRUCT)
if ((*tp)->type == FFI_TYPE_STRUCT || (*tp)->type == FFI_TYPE_VECTOR)
{
*args = (raw++)->ptr;
}
@@ -172,6 +173,7 @@ ffi_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw)
#if !FFI_NO_STRUCTS
case FFI_TYPE_STRUCT:
case FFI_TYPE_VECTOR:
(raw++)->ptr = *args;
break;
#endif
+11 -1
View File
@@ -417,9 +417,19 @@ ffi_tramp_init (void)
&tramp_globals.map_size);
tramp_globals.ntramp = tramp_globals.map_size / tramp_globals.size;
/*
* The trampoline code table is a single, fixed-size mapping. If the
* system page size is larger than that mapping, the static trampoline
* mechanism cannot be used. Both values are invariant for the life of
* the process, so cache the FAILED verdict rather than re-running the
* whole initialization on every allocation.
*/
page_size = sysconf (_SC_PAGESIZE);
if (page_size >= 0 && (size_t)page_size > tramp_globals.map_size)
return 0;
{
tramp_globals.status = TRAMP_GLOBALS_FAILED;
return 0;
}
if (ffi_tramp_init_os ())
{
+1 -1
View File
@@ -118,7 +118,7 @@ ffi_prep_cif_machdep(ffi_cif *cif)
break;
case FFI_TYPE_STRUCT:
{
#if defined(X86_WIN32) || defined(X86_DARWIN)
#if defined(X86_WIN32) || defined(X86_DARWIN) || defined(X86_FREEBSD)
size_t size = cif->rtype->size;
if (size == 1)
flags = X86_RET_STRUCT_1B;
+54 -2
View File
@@ -330,6 +330,25 @@ classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
}
return words;
}
case FFI_TYPE_VECTOR:
/* A Short Vector occupies SSE registers: an 8-byte vector is a single
SSE eightbyte; a 16-byte vector is one %xmm register (SSE + SSEUP).
Wider vectors would need %ymm/%zmm handling this port does not
implement; classify them as memory here and reject them outright in
ffi_prep_cif_machdep so the caller gets FFI_BAD_TYPEDEF, not a
silently wrong in-memory pass. */
if (type->size == 8)
{
classes[0] = X86_64_SSE_CLASS;
return 1;
}
else if (type->size == 16)
{
classes[0] = X86_64_SSE_CLASS;
classes[1] = X86_64_SSEUP_CLASS;
return 2;
}
return 0;
case FFI_TYPE_COMPLEX:
{
ffi_type *inner = type->elements[0];
@@ -533,6 +552,16 @@ ffi_prep_cif_machdep (ffi_cif *cif)
}
}
break;
case FFI_TYPE_VECTOR:
/* An 8-byte vector returns in the low half of %xmm0; a 16-byte vector
fills %xmm0 (SSE + SSEUP). Wider vectors are unsupported here. */
if (rtype_size == 8)
flags = UNIX64_RET_XMM64;
else if (rtype_size == 16)
flags = UNIX64_RET_XMM128;
else
return FFI_BAD_TYPEDEF;
break;
case FFI_TYPE_COMPLEX:
switch (rtype->elements[0]->type)
{
@@ -577,6 +606,15 @@ ffi_prep_cif_machdep (ffi_cif *cif)
return FFI_BAD_TYPEDEF;
}
/* Reject vectors wider than 16 bytes as arguments: correct %ymm/%zmm
passing needs unix64.S register-save changes that are out of scope for
this port, and classify_argument would otherwise silently treat them as
an in-memory aggregate. */
for (i = 0, avn = cif->nargs; i < avn; i++)
if (cif->arg_types[i]->type == FFI_TYPE_VECTOR
&& cif->arg_types[i]->size > 16)
return FFI_BAD_TYPEDEF;
/* Go over all arguments and determine the way they should be passed.
If it's in a register and there is space for it, let that be so. If
not, add it's size to the stack byte count. */
@@ -782,6 +820,7 @@ typedef struct
unsigned fast; /* nonzero -> lean trampoline eligible */
unsigned retcode; /* UNIX64_RET_* (low byte of flags) for the store */
int thunk_n; /* >=0 -> ffi_gp_thunks[thunk_n], else -1 */
unsigned alloc_bytes; /* malloc'd size, reported by ffi_call_plan_size */
ffi_move moves[];
} ffi_plan;
@@ -828,7 +867,7 @@ build_plan (ffi_cif *cif)
unsigned i, avn = cif->nargs;
enum x86_64_reg_class classes[MAX_CLASSES];
unsigned nm, gprcount, ssecount;
size_t argp_off;
size_t argp_off, nbytes;
ffi_plan *plan;
int all_gp64 = 1; /* every arg is exactly one 64-bit GP move? */
@@ -848,9 +887,11 @@ build_plan (ffi_cif *cif)
}
/* One self-contained allocation: header + moves, released with plain free(). */
plan = malloc (sizeof (ffi_plan) + sizeof (ffi_move) * (2 * avn + 1));
nbytes = sizeof (ffi_plan) + sizeof (ffi_move) * (2 * avn + 1);
plan = malloc (nbytes);
if (plan == NULL)
return NULL;
plan->alloc_bytes = (unsigned) nbytes;
nm = gprcount = ssecount = 0;
argp_off = 0;
@@ -1070,6 +1111,17 @@ ffi_call_plan_free (ffi_call_plan *plan)
}
}
size_t
ffi_call_plan_size (ffi_call_plan *plan)
{
if (plan == NULL)
return 0;
/* The move-list carries its own size; a signature with no fast path owns
nothing beyond the handle. */
return sizeof (struct ffi_call_plan)
+ (plan->fast != NULL ? plan->fast->alloc_bytes : 0);
}
extern void
ffi_call_efi64(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue);
#endif
+19
View File
@@ -58,6 +58,13 @@
#define FFI_TARGET_HAS_INT128
#endif
/* The System V x86-64 psABI passes 8- and 16-byte vectors in SSE registers;
this is implemented by the ffi64.c (FFI_UNIX64) backend only. 32-bit x86
and the Windows x86-64 backend (ffiw64.c) do not marshal vectors. */
#if defined(X86_64) && !defined(X86_WIN64)
#define FFI_TARGET_HAS_VECTOR_TYPE
#endif
/* ---- Generic type definitions ----------------------------------------- */
#ifndef LIBFFI_ASM
@@ -138,6 +145,18 @@ typedef enum ffi_abi {
#define FFI_TYPE_SMALL_STRUCT_4B (FFI_TYPE_LAST + 3)
#define FFI_TYPE_MS_STRUCT (FFI_TYPE_LAST + 4)
/* Tripwire: the win64.S / win64_intel.S return-value jump tables use one
8-byte slot per code value and place the FFI_TYPE_SMALL_STRUCT_* pseudo-types
(which are FFI_TYPE_LAST-relative) immediately after the generic FFI_TYPE_*
codes. Adding a new generic type bumps FFI_TYPE_LAST, shifts those codes,
and opens a gap in the tables that silently misaligns small-struct returns.
When this fires: add a matching E() slot for the new type in both win64.S
and win64_intel.S, then bump FFI_X86_TYPE_LAST to match. */
#define FFI_X86_TYPE_LAST FFI_TYPE_VECTOR
#if FFI_TYPE_LAST != FFI_X86_TYPE_LAST
# error "new FFI_TYPE_* added: sync the win64.S/win64_intel.S jump tables and bump FFI_X86_TYPE_LAST"
#endif
#if defined (X86_64) || defined(X86_WIN64) \
|| (defined (__x86_64__) && defined (X86_DARWIN))
/* 4 bytes of ENDBR64 + 7 bytes of LEA + 6 bytes of JMP + 7 bytes of NOP
+5
View File
@@ -151,6 +151,11 @@ E(0b, FFI_TYPE_UINT128)
E(0b, FFI_TYPE_SINT128)
movdqu %xmm0, (%r8)
epilogue
/* Win64 does not marshal vectors (ffi_prep_cif_core rejects them), but the
FFI_TYPE_SMALL_STRUCT_* codes are FFI_TYPE_LAST-relative, so this slot must
exist to keep the table contiguous and the small-struct entries aligned. */
E(0b, FFI_TYPE_VECTOR)
call PLT(C(abort))
E(0b, FFI_TYPE_SMALL_STRUCT_1B)
movb %al, (%r8)
epilogue
+5
View File
@@ -152,6 +152,11 @@ E(0b, FFI_TYPE_UINT128)
E(0b, FFI_TYPE_SINT128)
movdqu xmmword ptr [r8], xmm0
epilogue
/* Win64 does not marshal vectors (ffi_prep_cif_core rejects them), but the
FFI_TYPE_SMALL_STRUCT_* codes are FFI_TYPE_LAST-relative, so this slot must
exist to keep the table contiguous and the small-struct entries aligned. */
E(0b, FFI_TYPE_VECTOR)
call PLT(C(abort))
E(0b, FFI_TYPE_SMALL_STRUCT_1B)
mov byte ptr [r8], al ; movb %al, (%r8)
epilogue
+11 -3
View File
@@ -13,15 +13,17 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \
libffi.bhaible/alignof.h libffi.bhaible/bhaible.exp libffi.bhaible/test-call.c \
libffi.bhaible/test-callback.c libffi.bhaible/testcases.c libffi.call/align_mixed.c \
libffi.call/align_stdcall.c libffi.call/bpo_38748.c libffi.call/call.exp \
libffi.call/closure_thiscall_fastcall_pop.c \
libffi.call/err_bad_typedef.c libffi.call/ffitest.h libffi.call/float.c \
libffi.call/float1.c libffi.call/float2.c libffi.call/float3.c \
libffi.call/float4.c libffi.call/float_va.c libffi.call/i128-1.c \
libffi.call/large_struct_by_value.c libffi.call/many.c \
libffi.call/many2.c libffi.call/many_double.c libffi.call/many_mixed.c \
libffi.call/many2.c libffi.call/many_double.c \
libffi.call/many_large_structs.c libffi.call/many_mixed.c \
libffi.call/many_small_structs.c \
libffi.call/negint.c libffi.call/offsets.c libffi.call/overread.c \
libffi.call/plan.c libffi.call/plan_mixed.c libffi.call/plan_spill.c \
libffi.call/plan_struct.c libffi.call/plan_var.c \
libffi.call/plan_struct.c libffi.call/plan_size.c libffi.call/plan_var.c \
libffi.call/pr1172638.c libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \
libffi.call/return_dbl1.c libffi.call/return_dbl2.c libffi.call/return_fl.c \
libffi.call/return_fl1.c libffi.call/return_fl2.c libffi.call/return_fl3.c \
@@ -90,4 +92,10 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \
libffi.complex/return_complex_float.c libffi.complex/return_complex_longdouble.c libffi.go/aa-direct.c \
libffi.go/closure1.c libffi.go/ffitest.h libffi.go/go.exp \
libffi.go/static-chain.h Makefile.am Makefile.in \
libffi.threads/ffitest.h libffi.threads/threads.exp libffi.threads/tsan.c
libffi.threads/ffitest.h libffi.threads/threads.exp libffi.threads/tsan.c \
libffi.vector/vector.exp libffi.vector/ffitest.h libffi.vector/vector.h \
libffi.vector/vector_float32x4.c libffi.vector/vector_float32x2.c \
libffi.vector/vector_double2.c libffi.vector/vector_int32x4.c \
libffi.vector/vector_args_spill.c libffi.vector/vector_vec3.c \
libffi.vector/vector_double4.c libffi.vector/vector_hva.c \
libffi.vector/cls_vector.c libffi.vector/vector_validate.c
+11 -3
View File
@@ -301,15 +301,17 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \
libffi.bhaible/alignof.h libffi.bhaible/bhaible.exp libffi.bhaible/test-call.c \
libffi.bhaible/test-callback.c libffi.bhaible/testcases.c libffi.call/align_mixed.c \
libffi.call/align_stdcall.c libffi.call/bpo_38748.c libffi.call/call.exp \
libffi.call/closure_thiscall_fastcall_pop.c \
libffi.call/err_bad_typedef.c libffi.call/ffitest.h libffi.call/float.c \
libffi.call/float1.c libffi.call/float2.c libffi.call/float3.c \
libffi.call/float4.c libffi.call/float_va.c libffi.call/i128-1.c \
libffi.call/large_struct_by_value.c libffi.call/many.c \
libffi.call/many2.c libffi.call/many_double.c libffi.call/many_mixed.c \
libffi.call/many2.c libffi.call/many_double.c \
libffi.call/many_large_structs.c libffi.call/many_mixed.c \
libffi.call/many_small_structs.c \
libffi.call/negint.c libffi.call/offsets.c libffi.call/overread.c \
libffi.call/plan.c libffi.call/plan_mixed.c libffi.call/plan_spill.c \
libffi.call/plan_struct.c libffi.call/plan_var.c \
libffi.call/plan_struct.c libffi.call/plan_size.c libffi.call/plan_var.c \
libffi.call/pr1172638.c libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \
libffi.call/return_dbl1.c libffi.call/return_dbl2.c libffi.call/return_fl.c \
libffi.call/return_fl1.c libffi.call/return_fl2.c libffi.call/return_fl3.c \
@@ -378,7 +380,13 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \
libffi.complex/return_complex_float.c libffi.complex/return_complex_longdouble.c libffi.go/aa-direct.c \
libffi.go/closure1.c libffi.go/ffitest.h libffi.go/go.exp \
libffi.go/static-chain.h Makefile.am Makefile.in \
libffi.threads/ffitest.h libffi.threads/threads.exp libffi.threads/tsan.c
libffi.threads/ffitest.h libffi.threads/threads.exp libffi.threads/tsan.c \
libffi.vector/vector.exp libffi.vector/ffitest.h libffi.vector/vector.h \
libffi.vector/vector_float32x4.c libffi.vector/vector_float32x2.c \
libffi.vector/vector_double2.c libffi.vector/vector_int32x4.c \
libffi.vector/vector_args_spill.c libffi.vector/vector_vec3.c \
libffi.vector/vector_double4.c libffi.vector/vector_hva.c \
libffi.vector/cls_vector.c libffi.vector/vector_validate.c
all: all-am
@@ -0,0 +1,131 @@
/* Area: closure, ffi_prep_closure_loc
Purpose: Check i386 THISCALL/FASTCALL closures pop the stack correctly.
Limitations: i386 + GNU inline asm only; a no-op elsewhere.
PR: none.
Originator: i386 closure stack-pop accounting regression.
THISCALL and FASTCALL are callee-clean: the closure must remove its
stack-resident arguments on return (ret $n). When a 64-bit integer or
a struct argument is placed on the stack, the closure return path used
to compute the pop as cif->bytes - narg_reg*4 with narg_reg force-bumped
to 2, discounting register slots that were never used and under-popping
the stack. A caller that relies on callee cleanup is then left with the
argument bytes where its return address should be.
This test invokes the generated closure through a minimal callee-clean
call site and checks that ESP is balanced across the call (delta 0).
Without the fix the delta is 8 (FASTCALL uint64) or 4 (THISCALL). */
/* { dg-do run } */
#include "ffitest.h"
#if defined(__i386__) && defined(__GNUC__) && !defined(__APPLE__)
static uint64_t received;
static int ran;
static void
cb (ffi_cif *cif, void *resp, void **args, void *userdata)
{
(void) cif; (void) resp; (void) userdata;
received = *(uint64_t *) args[cif->nargs - 1];
ran++;
}
/* Push an 8-byte stack argument, load ECX (the thiscall "this" register,
ignored by the fastcall callee), call the closure, and return how many
bytes the callee under-popped (0 == it popped exactly what was pushed).
Every operand is read into a register up front, while ESP is still at
its incoming value, so nothing is referenced through an ESP-relative
memory operand after we start moving ESP (which would otherwise read a
stale slot, on clang at -O2 in particular). The stack is then 16-byte
aligned at the call as the i386 psABI requires, so the -O2-built closure
body may use aligned SSE without faulting; the alignment cancels out of
the delta. ESP is restored to its exact incoming value before the delta
is stored, so a wrong pop cannot corrupt our frame. Not using EBX keeps
this compatible with -fPIC; the delta is returned via memory so no free
register is needed for it. */
static int
esp_delta (void *code, uint64_t stackarg, unsigned ecxv)
{
unsigned delta;
unsigned lo = (unsigned) stackarg;
unsigned hi = (unsigned) (stackarg >> 32);
__asm__ volatile (
"movl %[lo], %%eax\n\t" /* stash all operands in registers */
"movl %[hi], %%edx\n\t" /* before ESP moves */
"movl %[code], %%edi\n\t"
"movl %[ecxv], %%ecx\n\t" /* thiscall 'this' */
"movl %%esp, %%esi\n\t" /* remember the real esp */
"andl $-16, %%esp\n\t" /* 16-byte align, then bias by the */
"subl $8, %%esp\n\t" /* 8 arg bytes so 'call' is 0 mod 16 */
"pushl %%edx\n\t" /* high dword */
"pushl %%eax\n\t" /* low dword */
"calll *%%edi\n\t"
"movl %%esi, %%eax\n\t" /* recompute esp just before the */
"andl $-16, %%eax\n\t" /* pushes... */
"subl $8, %%eax\n\t"
"subl %%esp, %%eax\n\t" /* eax = under-popped byte count */
"movl %%esi, %%esp\n\t" /* restore before touching memory */
"movl %%eax, %[delta]\n\t"
: [delta] "=m" (delta)
: [lo] "m" (lo), [hi] "m" (hi), [code] "m" (code), [ecxv] "m" (ecxv)
: "memory", "cc", "eax", "ecx", "edx", "esi", "edi");
return (int) delta;
}
static int
check_abi (ffi_abi abi, unsigned nargs, ffi_type **atypes, unsigned ecx)
{
ffi_cif cif;
ffi_closure *closure;
void *code;
int delta;
closure = ffi_closure_alloc (sizeof (ffi_closure), &code);
CHECK (closure != NULL);
CHECK (ffi_prep_cif (&cif, abi, nargs, &ffi_type_void, atypes) == FFI_OK);
CHECK (ffi_prep_closure_loc (closure, &cif, cb, NULL, code) == FFI_OK);
ran = 0;
received = 0;
delta = esp_delta (code, 0x1122334455667788ULL, ecx);
CHECK (ran == 1);
CHECK (received == 0x1122334455667788ULL);
ffi_closure_free (closure);
return delta;
}
int
main (void)
{
ffi_type *fastcall_args[1] = { &ffi_type_uint64 };
ffi_type *thiscall_args[2] = { &ffi_type_pointer, &ffi_type_uint64 };
int d;
/* FASTCALL void cb(uint64_t): the uint64 is stack-resident; pop must be 8. */
d = check_abi (FFI_FASTCALL, 1, fastcall_args, 0);
printf ("FASTCALL uint64 esp delta: %d\n", d);
CHECK (d == 0);
/* THISCALL void cb(void*, uint64_t): 'this' in ECX, uint64 on the stack;
pop must be 8 (not 4). */
d = check_abi (FFI_THISCALL, 2, thiscall_args, 0xdeadbeef);
printf ("THISCALL this+uint64 esp delta: %d\n", d);
CHECK (d == 0);
exit (0);
}
#else
int
main (void)
{
/* Not an i386 GNU target: nothing to check here. */
exit (0);
}
#endif
+88
View File
@@ -0,0 +1,88 @@
/* Area: ffi_call
Purpose: Pass many large by-value structs on AArch64.
Limitations: none.
PR: none.
Originator: AArch64 large-struct stack accounting regression.
Regression test: on AArch64, composites larger than 16 bytes are passed
by invisible reference. ffi_call copies each payload into the argument
slab (growing down from the top) and, once X0-X7 are exhausted, also
spills the by-ref pointer into the same slab (the NSAA, growing up).
The generic prep_cif budget in cif->bytes only charged the payload copy,
not the 8-byte pointer slot, so with enough large structs the two regions
collided and a later payload copy overwrote an already-spilled pointer,
leaving the callee with a corrupt pointer for a by-value argument.
Passing sixteen 32-byte (non-HFA) structs by value -- eight more than the
argument registers -- must marshal every argument intact. */
/* { dg-do run } */
#include "ffitest.h"
#define NARGS 16
#define SSIZE 32
typedef struct { unsigned char b[SSIZE]; } big_struct;
/* Sum every byte of every argument. A corrupted by-ref pointer makes the
callee read the wrong memory, so the sum no longer matches. */
static int ABI_ATTR
sum_bytes (big_struct s0, big_struct s1, big_struct s2, big_struct s3,
big_struct s4, big_struct s5, big_struct s6, big_struct s7,
big_struct s8, big_struct s9, big_struct s10, big_struct s11,
big_struct s12, big_struct s13, big_struct s14, big_struct s15)
{
big_struct *all[NARGS];
int i, j, sum = 0;
all[0] = &s0; all[1] = &s1; all[2] = &s2; all[3] = &s3;
all[4] = &s4; all[5] = &s5; all[6] = &s6; all[7] = &s7;
all[8] = &s8; all[9] = &s9; all[10] = &s10; all[11] = &s11;
all[12] = &s12; all[13] = &s13; all[14] = &s14; all[15] = &s15;
for (i = 0; i < NARGS; i++)
for (j = 0; j < SSIZE; j++)
sum += all[i]->b[j];
return sum;
}
int main (void)
{
ffi_cif cif;
ffi_type *args[NARGS];
void *values[NARGS];
ffi_type bs_type;
ffi_type *bs_elements[SSIZE + 1];
big_struct in[NARGS];
ffi_arg result = 0;
int i, j, expected = 0;
bs_type.size = 0;
bs_type.alignment = 0;
bs_type.type = FFI_TYPE_STRUCT;
for (i = 0; i < SSIZE; i++)
bs_elements[i] = &ffi_type_uchar;
bs_elements[SSIZE] = NULL;
bs_type.elements = bs_elements;
/* Fill struct i with the distinct byte value (i + 1) so any pointer
mix-up between arguments changes the total. */
for (i = 0; i < NARGS; i++)
{
for (j = 0; j < SSIZE; j++)
{
in[i].b[j] = (unsigned char) (i + 1);
expected += (i + 1);
}
args[i] = &bs_type;
values[i] = &in[i];
}
CHECK(ffi_prep_cif(&cif, ABI_NUM, NARGS, &ffi_type_sint, args) == FFI_OK);
ffi_call(&cif, FFI_FN(sum_bytes), &result, values);
CHECK((int) result == expected);
exit(0);
}
+77
View File
@@ -0,0 +1,77 @@
/* Area: ffi_call_plan_size
Purpose: Check that a plan reports its own allocation size, that the
size is stable across invocations, and that a NULL plan has
no footprint.
Limitations: The exact byte count is implementation defined, so this only
checks the invariants callers may rely on.
PR: none.
Originator: ffi_call_plan tests */
/* { dg-do run } */
#include "ffitest.h"
static uint64_t gp2(uint64_t a, uint64_t b)
{
return a + b * 2;
}
static uint64_t gp6(uint64_t a, uint64_t b, uint64_t c,
uint64_t d, uint64_t e, uint64_t f)
{
return a + b * 2 + c * 3 + d * 4 + e * 5 + f * 6;
}
int main (void)
{
ffi_cif cif2, cif6;
ffi_type *args[6];
void *values[6];
ffi_call_plan *plan2, *plan6;
size_t size2, size6;
uint64_t a[6], r;
int i;
for (i = 0; i < 6; i++)
{
args[i] = &ffi_type_uint64;
a[i] = (uint64_t) (i + 1);
values[i] = &a[i];
}
CHECK(ffi_prep_cif(&cif2, FFI_DEFAULT_ABI, 2, &ffi_type_uint64, args)
== FFI_OK);
CHECK(ffi_prep_cif(&cif6, FFI_DEFAULT_ABI, 6, &ffi_type_uint64, args)
== FFI_OK);
/* A NULL plan has no footprint, mirroring ffi_call_plan_free(NULL). */
CHECK(ffi_call_plan_size(NULL) == 0);
plan2 = ffi_call_plan_alloc(&cif2);
CHECK(plan2 != NULL);
plan6 = ffi_call_plan_alloc(&cif6);
CHECK(plan6 != NULL);
size2 = ffi_call_plan_size(plan2);
size6 = ffi_call_plan_size(plan6);
/* Every plan owns at least its handle, and a wider signature never needs
less memory than a narrower one of the same shape. Targets without a
fast path report the same constant for both. */
CHECK(size2 > 0);
CHECK(size6 >= size2);
/* The plan is immutable, so querying it must not disturb invocation and
the reported size must not drift across calls. */
ffi_call_plan_invoke(plan6, FFI_FN(gp6), &r, values);
CHECK(r == gp6(a[0], a[1], a[2], a[3], a[4], a[5]));
CHECK(ffi_call_plan_size(plan6) == size6);
ffi_call_plan_invoke(plan2, FFI_FN(gp2), &r, values);
CHECK(r == gp2(a[0], a[1]));
CHECK(ffi_call_plan_size(plan2) == size2);
ffi_call_plan_free(plan2);
ffi_call_plan_free(plan6);
exit(0);
}
+67
View File
@@ -0,0 +1,67 @@
/* Area: closure_call
Purpose: A closure that receives two vector arguments (and a scalar) and
returns a vector. Exercises the closure argument-extraction and
vector return paths.
Limitations: none.
PR: none.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
typedef float f32x4 __attribute__((vector_size (16)));
static void
cls_vector_fn (ffi_cif *cif __UNUSED__, void *resp, void **args,
void *userdata __UNUSED__)
{
f32x4 a = *(f32x4 *) args[0];
f32x4 b = *(f32x4 *) args[1];
int scale = *(int *) args[2];
f32x4 *r = (f32x4 *) resp;
*r = (a + b) * (float) scale;
}
typedef f32x4 (*cls_vector_t) (f32x4, f32x4, int);
int
main (void)
{
ffi_cif cif;
void *code;
ffi_closure *pcl = ffi_closure_alloc (sizeof (ffi_closure), &code);
ffi_type vec_type;
ffi_type *vec_elems[5];
ffi_type *arg_types[3];
f32x4 a = { 1, 2, 3, 4 };
f32x4 b = { 10, 20, 30, 40 };
f32x4 res;
int scale = 2;
int i;
CHECK (pcl != NULL);
make_vector_type (&vec_type, vec_elems, &ffi_type_float, 4);
arg_types[0] = &vec_type;
arg_types[1] = &vec_type;
arg_types[2] = &ffi_type_sint;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 3, &vec_type, arg_types)
== FFI_OK);
CHECK (ffi_prep_closure_loc (pcl, &cif, cls_vector_fn, NULL, code)
== FFI_OK);
res = ((cls_vector_t) code) (a, b, scale);
for (i = 0; i < 4; i++)
{
float want = (a[i] + b[i]) * (float) scale;
printf ("res[%d] = %g (want %g)\n", i, (double) res[i], (double) want);
CHECK (res[i] == want);
}
exit (0);
}
+1
View File
@@ -0,0 +1 @@
#include "../libffi.call/ffitest.h"
+59
View File
@@ -0,0 +1,59 @@
# Copyright (C) 2026 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
dg-init
libffi-init
global srcdir subdir
# The tests are written with the GCC/Clang vector extension
# (__attribute__ ((vector_size (N)))). A target port can support
# FFI_TYPE_VECTOR at the ABI level while the compiler under test (e.g.
# MSVC) cannot compile that syntax, so probe the compiler with an actual
# compilation, not just a preprocessor check.
proc libffi_vector_syntax_test { } {
set src "vecprobe[pid].c"
set obj "vecprobe[pid].o"
set f [open $src "w"]
puts $f "typedef float probe_v4 __attribute__ ((vector_size (16)));"
puts $f "probe_v4 probe_var;"
puts $f "int main (void) { return 0; }"
close $f
set lines [libffi_target_compile $src $obj object ""]
file delete $src
file delete $obj
return [string match "" $lines]
}
set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.{c,cc}]]
if { [libffi_feature_test "#ifdef FFI_TARGET_HAS_VECTOR_TYPE"]
&& [libffi_vector_syntax_test] } {
run-many-tests $tlist ""
} else {
foreach test $tlist {
unsupported "$test"
}
}
dg-finish
# Local Variables:
# tcl-indent-level:4
# End:
+32
View File
@@ -0,0 +1,32 @@
/* -*-c-*- */
/* Shared helpers for the libffi vector (SIMD) tests.
Vectors are built with the portable GCC/Clang spelling
__attribute__((vector_size (N))) so the tests compile on both compilers.
A vector ffi_type is described exactly like a struct, except every element
points at the SAME scalar ffi_type and the count is the lane count; the
caller leaves size and alignment at zero and libffi computes them. */
#ifndef LIBFFI_VECTOR_H
#define LIBFFI_VECTOR_H
#include "ffitest.h"
/* Build (into the caller-provided ELEMS array of length COUNT + 1 and the
ffi_type object TY) a vector type descriptor of COUNT lanes of scalar type
ELEM. ELEMS must have room for COUNT + 1 pointers (NULL terminator). */
static inline void
make_vector_type (ffi_type *ty, ffi_type **elems, ffi_type *elem,
unsigned count)
{
unsigned i;
for (i = 0; i < count; i++)
elems[i] = elem;
elems[count] = NULL;
ty->size = 0;
ty->alignment = 0;
ty->type = FFI_TYPE_VECTOR;
ty->elements = elems;
}
#endif /* LIBFFI_VECTOR_H */
+85
View File
@@ -0,0 +1,85 @@
/* Area: ffi_call
Purpose: Pass many vector arguments interleaved with scalars, enough to
exhaust the vector argument registers and spill onto the stack.
Limitations: none.
PR: none.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
typedef float f32x4 __attribute__((vector_size (16)));
/* Ten vectors exceeds the 8 vector argument registers on both AArch64 and
x86-64, so v8/v9 are passed on the stack. The scalars are interleaved to
make sure the two register files advance independently. */
static float
mix (int i0, f32x4 v0, f32x4 v1, double d0, f32x4 v2, f32x4 v3,
f32x4 v4, int i1, f32x4 v5, f32x4 v6, f32x4 v7, double d1,
f32x4 v8, f32x4 v9)
{
float acc = 0;
acc += 1 * v0[0] + v0[3];
acc += 2 * v1[0] + v1[3];
acc += 3 * v2[0] + v2[3];
acc += 4 * v3[0] + v3[3];
acc += 5 * v4[0] + v4[3];
acc += 6 * v5[0] + v5[3];
acc += 7 * v6[0] + v6[3];
acc += 8 * v7[0] + v7[3];
acc += 9 * v8[0] + v8[3];
acc += 10 * v9[0] + v9[3];
acc += i0 + i1 + (float) d0 + (float) d1;
return acc;
}
int
main (void)
{
ffi_cif cif;
ffi_type vec_type;
ffi_type *vec_elems[5];
ffi_type *args[14];
void *values[14];
f32x4 v[10];
int i0 = 100, i1 = 7;
double d0 = 3.5, d1 = 0.25;
float r, ref;
unsigned k;
make_vector_type (&vec_type, vec_elems, &ffi_type_float, 4);
for (k = 0; k < 10; k++)
{
f32x4 t = { (float) (k + 1), 0, 0, (float) (100 + k) };
v[k] = t;
}
args[0] = &ffi_type_sint; values[0] = &i0;
args[1] = &vec_type; values[1] = &v[0];
args[2] = &vec_type; values[2] = &v[1];
args[3] = &ffi_type_double; values[3] = &d0;
args[4] = &vec_type; values[4] = &v[2];
args[5] = &vec_type; values[5] = &v[3];
args[6] = &vec_type; values[6] = &v[4];
args[7] = &ffi_type_sint; values[7] = &i1;
args[8] = &vec_type; values[8] = &v[5];
args[9] = &vec_type; values[9] = &v[6];
args[10] = &vec_type; values[10] = &v[7];
args[11] = &ffi_type_double; values[11] = &d1;
args[12] = &vec_type; values[12] = &v[8];
args[13] = &vec_type; values[13] = &v[9];
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 14, &ffi_type_float, args)
== FFI_OK);
ffi_call (&cif, FFI_FN (mix), &r, values);
ref = mix (i0, v[0], v[1], d0, v[2], v[3], v[4], i1, v[5], v[6], v[7],
d1, v[8], v[9]);
printf ("r = %g (want %g)\n", (double) r, (double) ref);
CHECK (r == ref);
exit (0);
}
+53
View File
@@ -0,0 +1,53 @@
/* Area: ffi_call
Purpose: Pass and return a 16-byte double2 vector (single Q/SSE reg).
Limitations: none.
PR: none.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
typedef double d2 __attribute__((vector_size (16)));
static d2
add_d2 (d2 a, d2 b)
{
return a + b;
}
int
main (void)
{
ffi_cif cif;
ffi_type vec_type;
ffi_type *vec_elems[3];
ffi_type *args[2];
void *values[2];
d2 a = { 1.5, 2.5 };
d2 b = { 10.0, 20.0 };
d2 r, ref;
int i;
make_vector_type (&vec_type, vec_elems, &ffi_type_double, 2);
args[0] = &vec_type;
args[1] = &vec_type;
values[0] = &a;
values[1] = &b;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 2, &vec_type, args) == FFI_OK);
CHECK (vec_type.size == 16);
CHECK (vec_type.alignment == 16);
ffi_call (&cif, FFI_FN (add_d2), &r, values);
ref = add_d2 (a, b);
for (i = 0; i < 2; i++)
{
printf ("r[%d] = %g (want %g)\n", i, r[i], ref[i]);
CHECK (r[i] == ref[i]);
}
exit (0);
}
+81
View File
@@ -0,0 +1,81 @@
/* Area: ffi_call
Purpose: A 32-byte double4 vector. On AArch64 a bare vector wider than
16 bytes is passed by reference and returned in memory (no
short-vector register class), so the call must round-trip. On
x86-64 wider-than-16-byte vectors are not implemented, so
ffi_prep_cif must report FFI_BAD_TYPEDEF.
Limitations: none.
PR: none.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
typedef double d4 __attribute__((vector_size (32)));
/* Only called on ports that can actually marshal a 32-byte vector. */
static d4 add_d4 (d4 a, d4 b) __UNUSED__;
static d4
add_d4 (d4 a, d4 b)
{
return a + b;
}
int
main (void)
{
ffi_cif cif;
ffi_type vec_type;
ffi_type *vec_elems[5];
ffi_type *args[2];
make_vector_type (&vec_type, vec_elems, &ffi_type_double, 4);
args[0] = &vec_type;
args[1] = &vec_type;
#if defined(__aarch64__) || defined(_M_ARM64)
{
void *values[2];
d4 a = { 1, 2, 3, 4 };
d4 b = { 10, 20, 30, 40 };
d4 r, ref;
int i;
values[0] = &a;
values[1] = &b;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 2, &vec_type, args) == FFI_OK);
CHECK (vec_type.size == 32);
CHECK (vec_type.alignment == 16);
ffi_call (&cif, FFI_FN (add_d4), &r, values);
ref = add_d4 (a, b);
for (i = 0; i < 4; i++)
{
printf ("r[%d] = %g (want %g)\n", i, r[i], ref[i]);
CHECK (r[i] == ref[i]);
}
}
#else
{
/* x86-64 (and any other opted-in port without >16B support): the >16-byte
vector must be rejected, both as a return type and as an argument. */
ffi_status s_ret, s_arg;
s_ret = ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 0, &vec_type, NULL);
printf ("32-byte vector return: status %d (want %d = FFI_BAD_TYPEDEF)\n",
s_ret, FFI_BAD_TYPEDEF);
CHECK (s_ret == FFI_BAD_TYPEDEF);
s_arg = ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 1, &ffi_type_void, args);
printf ("32-byte vector argument: status %d (want %d = FFI_BAD_TYPEDEF)\n",
s_arg, FFI_BAD_TYPEDEF);
CHECK (s_arg == FFI_BAD_TYPEDEF);
}
#endif
exit (0);
}
+53
View File
@@ -0,0 +1,53 @@
/* Area: ffi_call
Purpose: Pass and return an 8-byte float32x2 vector (single D/SSE reg).
Limitations: none.
PR: none.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
typedef float f32x2 __attribute__((vector_size (8)));
static f32x2
add_f32x2 (f32x2 a, f32x2 b)
{
return a + b;
}
int
main (void)
{
ffi_cif cif;
ffi_type vec_type;
ffi_type *vec_elems[3];
ffi_type *args[2];
void *values[2];
f32x2 a = { 3, 4 };
f32x2 b = { 5, 6 };
f32x2 r, ref;
int i;
make_vector_type (&vec_type, vec_elems, &ffi_type_float, 2);
args[0] = &vec_type;
args[1] = &vec_type;
values[0] = &a;
values[1] = &b;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 2, &vec_type, args) == FFI_OK);
CHECK (vec_type.size == 8);
CHECK (vec_type.alignment == 8);
ffi_call (&cif, FFI_FN (add_f32x2), &r, values);
ref = add_f32x2 (a, b);
for (i = 0; i < 2; i++)
{
printf ("r[%d] = %g (want %g)\n", i, (double) r[i], (double) ref[i]);
CHECK (r[i] == ref[i]);
}
exit (0);
}
+54
View File
@@ -0,0 +1,54 @@
/* Area: ffi_call
Purpose: Pass and return a 16-byte float32x4 vector (the vec4 shape of
libffi/libffi#773).
Limitations: none.
PR: libffi/libffi#773.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
typedef float f32x4 __attribute__((vector_size (16)));
static f32x4
add_f32x4 (f32x4 a, f32x4 b)
{
return a + b;
}
int
main (void)
{
ffi_cif cif;
ffi_type vec_type;
ffi_type *vec_elems[5];
ffi_type *args[2];
void *values[2];
f32x4 a = { 1, 2, 3, 4 };
f32x4 b = { 10, 20, 30, 40 };
f32x4 r, ref;
int i;
make_vector_type (&vec_type, vec_elems, &ffi_type_float, 4);
args[0] = &vec_type;
args[1] = &vec_type;
values[0] = &a;
values[1] = &b;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 2, &vec_type, args) == FFI_OK);
CHECK (vec_type.size == 16);
CHECK (vec_type.alignment == 16);
ffi_call (&cif, FFI_FN (add_f32x4), &r, values);
ref = add_f32x4 (a, b);
for (i = 0; i < 4; i++)
{
printf ("r[%d] = %g (want %g)\n", i, (double) r[i], (double) ref[i]);
CHECK (r[i] == ref[i]);
}
exit (0);
}
+74
View File
@@ -0,0 +1,74 @@
/* Area: ffi_call
Purpose: Pass and return a homogeneous vector aggregate: a struct of two
identical 16-byte vectors. On AArch64 this is an HVA carried in
a pair of Q registers; on x86-64 the existing SSE struct
classification handles it (four SSE eightbytes). Both round-trip.
Limitations: none.
PR: none.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
typedef float f32x4 __attribute__((vector_size (16)));
struct hva2
{
f32x4 a;
f32x4 b;
};
static struct hva2
bump (struct hva2 s)
{
s.a = s.a + 1;
s.b = s.b + 2;
return s;
}
int
main (void)
{
ffi_cif cif;
ffi_type vec_type;
ffi_type *vec_elems[5];
ffi_type struct_type;
ffi_type *struct_elems[3];
ffi_type *args[1];
void *values[1];
struct hva2 in = { { 1, 2, 3, 4 }, { 10, 20, 30, 40 } };
struct hva2 out, ref;
int i;
make_vector_type (&vec_type, vec_elems, &ffi_type_float, 4);
struct_elems[0] = &vec_type;
struct_elems[1] = &vec_type;
struct_elems[2] = NULL;
struct_type.size = 0;
struct_type.alignment = 0;
struct_type.type = FFI_TYPE_STRUCT;
struct_type.elements = struct_elems;
args[0] = &struct_type;
values[0] = &in;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 1, &struct_type, args)
== FFI_OK);
CHECK (struct_type.size == 32);
ffi_call (&cif, FFI_FN (bump), &out, values);
ref = bump (in);
for (i = 0; i < 4; i++)
{
printf ("a[%d] = %g (want %g), b[%d] = %g (want %g)\n",
i, (double) out.a[i], (double) ref.a[i],
i, (double) out.b[i], (double) ref.b[i]);
CHECK (out.a[i] == ref.a[i]);
CHECK (out.b[i] == ref.b[i]);
}
exit (0);
}
+54
View File
@@ -0,0 +1,54 @@
/* Area: ffi_call
Purpose: Pass and return a 16-byte int32x4 integer vector. Integer
lanes still travel in a vector register, unlike an HFA of ints.
Limitations: none.
PR: none.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
typedef int i32x4 __attribute__((vector_size (16)));
static i32x4
add_i32x4 (i32x4 a, i32x4 b)
{
return a + b;
}
int
main (void)
{
ffi_cif cif;
ffi_type vec_type;
ffi_type *vec_elems[5];
ffi_type *args[2];
void *values[2];
i32x4 a = { 1, 2, 3, 4 };
i32x4 b = { 5, 6, 7, 8 };
i32x4 r, ref;
int i;
make_vector_type (&vec_type, vec_elems, &ffi_type_sint32, 4);
args[0] = &vec_type;
args[1] = &vec_type;
values[0] = &a;
values[1] = &b;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 2, &vec_type, args) == FFI_OK);
CHECK (vec_type.size == 16);
CHECK (vec_type.alignment == 16);
ffi_call (&cif, FFI_FN (add_i32x4), &r, values);
ref = add_i32x4 (a, b);
for (i = 0; i < 4; i++)
{
printf ("r[%d] = %d (want %d)\n", i, r[i], ref[i]);
CHECK (r[i] == ref[i]);
}
exit (0);
}
+103
View File
@@ -0,0 +1,103 @@
/* Area: ffi_prep_cif
Purpose: Validate that malformed vector type descriptors are rejected
with FFI_BAD_TYPEDEF, and that a well-formed vector is accepted
with the computed power-of-two size and min(size,16) alignment.
Limitations: none.
PR: none.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
int
main (void)
{
ffi_cif cif;
/* Heterogeneous lanes (float mixed with double) -> FFI_BAD_TYPEDEF. */
{
ffi_type vt;
ffi_type *elems[3];
ffi_type *args[1];
elems[0] = &ffi_type_float;
elems[1] = &ffi_type_double;
elems[2] = NULL;
vt.size = 0;
vt.alignment = 0;
vt.type = FFI_TYPE_VECTOR;
vt.elements = elems;
args[0] = &vt;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 1, &ffi_type_void, args)
== FFI_BAD_TYPEDEF);
}
/* An empty (zero-lane) vector -> FFI_BAD_TYPEDEF. */
{
ffi_type vt;
ffi_type *elems[1];
ffi_type *args[1];
elems[0] = NULL;
vt.size = 0;
vt.alignment = 0;
vt.type = FFI_TYPE_VECTOR;
vt.elements = elems;
args[0] = &vt;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 1, &ffi_type_void, args)
== FFI_BAD_TYPEDEF);
}
/* A non-scalar (struct) lane type -> FFI_BAD_TYPEDEF. */
{
ffi_type inner;
ffi_type *inner_elems[2];
ffi_type vt;
ffi_type *elems[3];
ffi_type *args[1];
inner_elems[0] = &ffi_type_float;
inner_elems[1] = NULL;
inner.size = 0;
inner.alignment = 0;
inner.type = FFI_TYPE_STRUCT;
inner.elements = inner_elems;
elems[0] = &inner;
elems[1] = &inner;
elems[2] = NULL;
vt.size = 0;
vt.alignment = 0;
vt.type = FFI_TYPE_VECTOR;
vt.elements = elems;
args[0] = &vt;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 1, &ffi_type_void, args)
== FFI_BAD_TYPEDEF);
}
/* A well-formed 3 x float vector is accepted with computed layout. */
{
ffi_type vt;
ffi_type *elems[4];
ffi_type *args[1];
make_vector_type (&vt, elems, &ffi_type_float, 3);
args[0] = &vt;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 1, &ffi_type_void, args)
== FFI_OK);
CHECK (vt.size == 16); /* 12 rounded up to 16 */
CHECK (vt.alignment == 16); /* min(16, 16) */
}
/* An 8-byte vector gets alignment 8 = min(8, 16). */
{
ffi_type vt;
ffi_type *elems[3];
ffi_type *args[1];
make_vector_type (&vt, elems, &ffi_type_float, 2);
args[0] = &vt;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 1, &ffi_type_void, args)
== FFI_OK);
CHECK (vt.size == 8);
CHECK (vt.alignment == 8);
}
printf ("vector validation ok\n");
exit (0);
}
+73
View File
@@ -0,0 +1,73 @@
/* Area: ffi_call
Purpose: Pass and return a three-lane float vector. Clang's
ext_vector_type(3) has 12 bytes of data padded to 16-byte
storage; libffi's power-of-two size rule must reproduce that
layout so a natively compiled callee agrees.
Limitations: Clang only (GCC's vector_size requires power-of-two totals and
rejects a 12-byte vector). A no-op on other compilers.
PR: none.
Originator: libffi vector support. */
/* { dg-do run } */
#include "vector.h"
#ifdef __clang__
typedef float f3 __attribute__((ext_vector_type (3)));
static f3
scale3 (f3 v)
{
f3 r;
r[0] = v[0] + 1;
r[1] = v[1] + 2;
r[2] = v[2] + 3;
return r;
}
int
main (void)
{
ffi_cif cif;
ffi_type vec_type;
ffi_type *vec_elems[4];
ffi_type *args[1];
void *values[1];
f3 a = { 10, 20, 30 };
f3 r, ref;
int i;
make_vector_type (&vec_type, vec_elems, &ffi_type_float, 3);
args[0] = &vec_type;
values[0] = &a;
CHECK (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 1, &vec_type, args) == FFI_OK);
/* 3 x float = 12, rounded up to 16 (matches ext_vector_type storage). */
CHECK (vec_type.size == 16);
CHECK (vec_type.alignment == 16);
CHECK (sizeof (f3) == 16);
ffi_call (&cif, FFI_FN (scale3), &r, values);
ref = scale3 (a);
for (i = 0; i < 3; i++)
{
printf ("r[%d] = %g (want %g)\n", i, (double) r[i], (double) ref[i]);
CHECK (r[i] == ref[i]);
}
exit (0);
}
#else
int
main (void)
{
/* ext_vector_type is a Clang extension; nothing to test elsewhere. */
exit (0);
}
#endif