diff --git a/deps/libffi/ChangeLog b/deps/libffi/ChangeLog index 0dde93e6adad..f64d5d5c46ac 100644 --- a/deps/libffi/ChangeLog +++ b/deps/libffi/ChangeLog @@ -1,3 +1,610 @@ +commit 12ffd1f9dc56fcea79d2f742f424301ae668d663 +Author: Anthony Green +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) + +commit 8f2a41d9d89dd8ee2c2438f1e2f9cf04aa9a53d4 +Author: Anthony Green +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) + +commit d956fe177ccfd4bb91ae7cb3ccaa0f8935a76522 +Author: Anthony Green +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) + +commit 670a0327b7d1576712a3cad7b9297f59f23d5430 +Author: Anthony Green +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) + +commit f744bc303fa4c69f1202ce283b866ebc768e0432 +Merge: abc18be0 5b8fa3fe +Author: Anthony Green +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 +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 +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 +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) + +commit 159268174ece06f6854c6d1bca1a9b95961f6ae9 +Author: Anthony Green +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) + +commit 3fdd99b5d2fb4c8f940d82fc4b1e530e743c685b +Author: Anthony Green +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) + +commit 5b8fa3fed84ce17768eeb379f5b81663172482e9 +Author: Todd White +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 +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 +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 +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) + +commit 2fd434cd9ada4d3d97b355e62c3ce3a969682230 +Merge: a00279c2 2aa33761 +Author: Anthony Green +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 +Date: Sun Aug 2 10:18:33 2026 -0400 + + Merge branch 'master' into tramp-cache-unsupported-verdict + +commit 76883c628a5273f71fb025e75bf1076adae3bb4b +Author: Anthony Green +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) + +commit a00279c2dc8e191ae5136b46bf6ae0e7a8da5b7a +Author: Anthony Green +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) + +commit ce77ca107a5cb0d10d5525c9422f0207e6c79ebf +Merge: d257b084 19dbdb53 +Author: Anthony Green +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 +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 +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) + +commit 333d87cf201ee279c9870fb5ad3e48e3a08aa6e5 +Merge: 46cb2e38 d7cd3a61 +Author: Anthony Green +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 +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) + +commit debc00a0114d8530d6d691862028c607aa17dd6a +Author: Anthony Green +Date: Sun Jul 26 07:28:36 2026 -0400 + + Update doc version + +commit 19dbdb53e869e07fbff05c86d634e8c08c9a7f61 +Author: Eduardo Speroni +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 +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 +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 +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 +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 +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 +Date: Fri Jul 10 11:56:55 2026 -0700 + + testsuite: Remember to distribute tests added for 3.7.1 + +commit ca86812cd430cff3018e491ba75a4f3c9ea969d2 +Author: Anthony Green +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 + commit 5c1c43091ed611fdea774374355eb938c73a9157 Author: Anthony Green Date: Fri Jul 10 09:50:53 2026 -0400 diff --git a/deps/libffi/README.md b/deps/libffi/README.md index 19f78f632b0c..797d0fd9aa8e 100644 --- a/deps/libffi/README.md +++ b/deps/libffi/README.md @@ -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. diff --git a/deps/libffi/configure b/deps/libffi/configure index e050ddc8675d..7b8a4cb375d1 100755 --- a/deps/libffi/configure +++ b/deps/libffi/configure @@ -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 . # @@ -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\\" diff --git a/deps/libffi/configure.ac b/deps/libffi/configure.ac index 3370acc3a396..826b453b8353 100644 --- a/deps/libffi/configure.ac +++ b/deps/libffi/configure.ac @@ -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 diff --git a/deps/libffi/doc/libffi.info b/deps/libffi/doc/libffi.info index b43246f8ed4e..4c1abc85f777 100644 --- a/deps/libffi/doc/libffi.info +++ b/deps/libffi/doc/libffi.info @@ -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 diff --git a/deps/libffi/doc/libffi.pdf b/deps/libffi/doc/libffi.pdf index 250d34faf711..75458a8f7d5d 100644 Binary files a/deps/libffi/doc/libffi.pdf and b/deps/libffi/doc/libffi.pdf differ diff --git a/deps/libffi/doc/libffi.texi b/deps/libffi/doc/libffi.texi index 251214f890d3..4d2802c5bd97 100644 --- a/deps/libffi/doc/libffi.texi +++ b/deps/libffi/doc/libffi.texi @@ -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 diff --git a/deps/libffi/doc/stamp-vti b/deps/libffi/doc/stamp-vti index e755454e9c82..dc281e38e0f6 100644 --- a/deps/libffi/doc/stamp-vti +++ b/deps/libffi/doc/stamp-vti @@ -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 diff --git a/deps/libffi/doc/version.texi b/deps/libffi/doc/version.texi index e755454e9c82..dc281e38e0f6 100644 --- a/deps/libffi/doc/version.texi +++ b/deps/libffi/doc/version.texi @@ -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 diff --git a/deps/libffi/generate-headers.py b/deps/libffi/generate-headers.py index e2d2942deffb..fb58edf17b66 100644 --- a/deps/libffi/generate-headers.py +++ b/deps/libffi/generate-headers.py @@ -7,8 +7,8 @@ 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 = { diff --git a/deps/libffi/include/ffi.h.in b/deps/libffi/include/ffi.h.in index 35f09cf43315..cb0a7dbcaaa3 100644 --- a/deps/libffi/include/ffi.h.in +++ b/deps/libffi/include/ffi.h.in @@ -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 @@ -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); diff --git a/deps/libffi/libffi.map.in b/deps/libffi/libffi.map.in index f4e366eb60bc..6151f10cbdce 100644 --- a/deps/libffi/libffi.map.in +++ b/deps/libffi/libffi.map.in @@ -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: diff --git a/deps/libffi/libtool-version b/deps/libffi/libtool-version index c5545eab8bf6..814c6e21d925 100644 --- a/deps/libffi/libtool-version +++ b/deps/libffi/libtool-version @@ -26,4 +26,4 @@ # release, then set age to 0. # # CURRENT:REVISION:AGE -12:1:4 +13:0:5 diff --git a/deps/libffi/src/aarch64/ffi.c b/deps/libffi/src/aarch64/ffi.c index 2e6a2ad2624c..1eb90dd565ad 100644 --- a/deps/libffi/src/aarch64/ffi.c +++ b/deps/libffi/src/aarch64/ffi.c @@ -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) diff --git a/deps/libffi/src/aarch64/ffitarget.h b/deps/libffi/src/aarch64/ffitarget.h index 46e2687ae7fe..8ba86799e113 100644 --- a/deps/libffi/src/aarch64/ffitarget.h +++ b/deps/libffi/src/aarch64/ffitarget.h @@ -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 diff --git a/deps/libffi/src/debug.c b/deps/libffi/src/debug.c index 63321dc013cc..cf847f3b1107 100644 --- a/deps/libffi/src/debug.c +++ b/deps/libffi/src/debug.c @@ -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 diff --git a/deps/libffi/src/ia64/ia64_flags.h b/deps/libffi/src/ia64/ia64_flags.h index 9d652cef14ce..bfe102c7d86f 100644 --- a/deps/libffi/src/ia64/ia64_flags.h +++ b/deps/libffi/src/ia64/ia64_flags.h @@ -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 diff --git a/deps/libffi/src/ia64/unix.S b/deps/libffi/src/ia64/unix.S index 04908368c3e2..b8e347169e2e 100644 --- a/deps/libffi/src/ia64/unix.S +++ b/deps/libffi/src/ia64/unix.S @@ -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 diff --git a/deps/libffi/src/java_raw_api.c b/deps/libffi/src/java_raw_api.c index 114d3e47fcde..e0a02ef27432 100644 --- a/deps/libffi/src/java_raw_api.c +++ b/deps/libffi/src/java_raw_api.c @@ -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. */ diff --git a/deps/libffi/src/pa/ffitarget.h b/deps/libffi/src/pa/ffitarget.h index f6f09975cfac..aeaacc167ef2 100644 --- a/deps/libffi/src/pa/ffitarget.h +++ b/deps/libffi/src/pa/ffitarget.h @@ -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 diff --git a/deps/libffi/src/powerpc/darwin_closure.S b/deps/libffi/src/powerpc/darwin_closure.S index 3121e6ac26d3..08cbe4bc1389 100644 --- a/deps/libffi/src/powerpc/darwin_closure.S +++ b/deps/libffi/src/powerpc/darwin_closure.S @@ -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 - - /* 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. */ + /* 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. */ + + 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) - b Lfinish - nop -#else - lwz r3,0(r5) - lwz r4,4(r5) + lwa r3,4(r5) b Lfinish -#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 diff --git a/deps/libffi/src/powerpc/ffi_darwin.c b/deps/libffi/src/powerpc/ffi_darwin.c index 01e2a43701d7..64449c38e156 100644 --- a/deps/libffi/src/powerpc/ffi_darwin.c +++ b/deps/libffi/src/powerpc/ffi_darwin.c @@ -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; diff --git a/deps/libffi/src/powerpc/ffi_linux64.c b/deps/libffi/src/powerpc/ffi_linux64.c index b1f1468ed5f8..e92f88c46973 100644 --- a/deps/libffi/src/powerpc/ffi_linux64.c +++ b/deps/libffi/src/powerpc/ffi_linux64.c @@ -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 @@ ffi_prep_cif_linux64_core (ffi_cif *cif) 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 @@ ffi_prep_cif_linux64_core (ffi_cif *cif) 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; diff --git a/deps/libffi/src/powerpc/linux64_closure.S b/deps/libffi/src/powerpc/linux64_closure.S index 405b2cfc47a1..3071bec0d22a 100644 --- a/deps/libffi/src/powerpc/linux64_closure.S +++ b/deps/libffi/src/powerpc/linux64_closure.S @@ -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: diff --git a/deps/libffi/src/prep_cif.c b/deps/libffi/src/prep_cif.c index 1836270d1a9c..8a448ebbf81f 100644 --- a/deps/libffi/src/prep_cif.c +++ b/deps/libffi/src/prep_cif.c @@ -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 */ diff --git a/deps/libffi/src/raw_api.c b/deps/libffi/src/raw_api.c index be156116cb0d..670d56d948a3 100644 --- a/deps/libffi/src/raw_api.c +++ b/deps/libffi/src/raw_api.c @@ -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 diff --git a/deps/libffi/src/tramp.c b/deps/libffi/src/tramp.c index 525f81547156..a04188858af6 100644 --- a/deps/libffi/src/tramp.c +++ b/deps/libffi/src/tramp.c @@ -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 ()) { diff --git a/deps/libffi/src/x86/ffi.c b/deps/libffi/src/x86/ffi.c index 27f17b0c8849..a953891362d5 100644 --- a/deps/libffi/src/x86/ffi.c +++ b/deps/libffi/src/x86/ffi.c @@ -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; diff --git a/deps/libffi/src/x86/ffi64.c b/deps/libffi/src/x86/ffi64.c index c24db38c4364..c2c78f3fbbfb 100644 --- a/deps/libffi/src/x86/ffi64.c +++ b/deps/libffi/src/x86/ffi64.c @@ -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 diff --git a/deps/libffi/src/x86/ffitarget.h b/deps/libffi/src/x86/ffitarget.h index d702235f90fe..eaf6a910a4f5 100644 --- a/deps/libffi/src/x86/ffitarget.h +++ b/deps/libffi/src/x86/ffitarget.h @@ -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 diff --git a/deps/libffi/src/x86/win64.S b/deps/libffi/src/x86/win64.S index 185f0a3048fb..f23a5fa29e8e 100644 --- a/deps/libffi/src/x86/win64.S +++ b/deps/libffi/src/x86/win64.S @@ -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 diff --git a/deps/libffi/src/x86/win64_intel.S b/deps/libffi/src/x86/win64_intel.S index e9eff00da3ce..807f5b3e98f7 100644 --- a/deps/libffi/src/x86/win64_intel.S +++ b/deps/libffi/src/x86/win64_intel.S @@ -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 diff --git a/deps/libffi/testsuite/Makefile.am b/deps/libffi/testsuite/Makefile.am index c14a880959d8..702461d02418 100644 --- a/deps/libffi/testsuite/Makefile.am +++ b/deps/libffi/testsuite/Makefile.am @@ -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 diff --git a/deps/libffi/testsuite/Makefile.in b/deps/libffi/testsuite/Makefile.in index 1b29b90d3633..30b417735d5e 100644 --- a/deps/libffi/testsuite/Makefile.in +++ b/deps/libffi/testsuite/Makefile.in @@ -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 diff --git a/deps/libffi/testsuite/libffi.call/closure_thiscall_fastcall_pop.c b/deps/libffi/testsuite/libffi.call/closure_thiscall_fastcall_pop.c new file mode 100644 index 000000000000..9cc0b091943f --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/closure_thiscall_fastcall_pop.c @@ -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 diff --git a/deps/libffi/testsuite/libffi.call/many_large_structs.c b/deps/libffi/testsuite/libffi.call/many_large_structs.c new file mode 100644 index 000000000000..9f766a9113c6 --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/many_large_structs.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.call/plan_size.c b/deps/libffi/testsuite/libffi.call/plan_size.c new file mode 100644 index 000000000000..b8398fbee991 --- /dev/null +++ b/deps/libffi/testsuite/libffi.call/plan_size.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.vector/cls_vector.c b/deps/libffi/testsuite/libffi.vector/cls_vector.c new file mode 100644 index 000000000000..18d8806b51b5 --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/cls_vector.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.vector/ffitest.h b/deps/libffi/testsuite/libffi.vector/ffitest.h new file mode 100644 index 000000000000..d27d362d6a6e --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/ffitest.h @@ -0,0 +1 @@ +#include "../libffi.call/ffitest.h" diff --git a/deps/libffi/testsuite/libffi.vector/vector.exp b/deps/libffi/testsuite/libffi.vector/vector.exp new file mode 100644 index 000000000000..a76957ee4d33 --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector.exp @@ -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 +# . + +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: diff --git a/deps/libffi/testsuite/libffi.vector/vector.h b/deps/libffi/testsuite/libffi.vector/vector.h new file mode 100644 index 000000000000..7baf832d37e4 --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector.h @@ -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 */ diff --git a/deps/libffi/testsuite/libffi.vector/vector_args_spill.c b/deps/libffi/testsuite/libffi.vector/vector_args_spill.c new file mode 100644 index 000000000000..dec6ab2e62af --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector_args_spill.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.vector/vector_double2.c b/deps/libffi/testsuite/libffi.vector/vector_double2.c new file mode 100644 index 000000000000..dd45878b5afe --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector_double2.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.vector/vector_double4.c b/deps/libffi/testsuite/libffi.vector/vector_double4.c new file mode 100644 index 000000000000..9f4473935929 --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector_double4.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.vector/vector_float32x2.c b/deps/libffi/testsuite/libffi.vector/vector_float32x2.c new file mode 100644 index 000000000000..f613687a2988 --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector_float32x2.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.vector/vector_float32x4.c b/deps/libffi/testsuite/libffi.vector/vector_float32x4.c new file mode 100644 index 000000000000..971814aaf66c --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector_float32x4.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.vector/vector_hva.c b/deps/libffi/testsuite/libffi.vector/vector_hva.c new file mode 100644 index 000000000000..5f80da07dfde --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector_hva.c @@ -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] = ∈ + + 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); +} diff --git a/deps/libffi/testsuite/libffi.vector/vector_int32x4.c b/deps/libffi/testsuite/libffi.vector/vector_int32x4.c new file mode 100644 index 000000000000..eaa6b802bab5 --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector_int32x4.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.vector/vector_validate.c b/deps/libffi/testsuite/libffi.vector/vector_validate.c new file mode 100644 index 000000000000..d923ab465fe4 --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector_validate.c @@ -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); +} diff --git a/deps/libffi/testsuite/libffi.vector/vector_vec3.c b/deps/libffi/testsuite/libffi.vector/vector_vec3.c new file mode 100644 index 000000000000..5a0367283023 --- /dev/null +++ b/deps/libffi/testsuite/libffi.vector/vector_vec3.c @@ -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