mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
deps: update libffi to 3.8.0
PR-URL: https://github.com/nodejs/node/pull/65154 Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
1 parent
2f65b902d8
commit
01f201888e
50 files changed
+2565
-159
No files matched your search
Vendored
+607
@@ -1,3 +1,610 @@
|
||||
commit 12ffd1f9dc56fcea79d2f742f424301ae668d663
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sat Aug 8 18:08:29 2026 -0400
|
||||
|
||||
README: order 3.8.0 notes by decreasing importance
|
||||
|
||||
Lead with new capabilities (VECTOR types, ffi_call_plan_size, ppc64
|
||||
_Complex long double), then correctness fixes by severity, then the
|
||||
trampoline caching optimization.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit 8f2a41d9d89dd8ee2c2438f1e2f9cf04aa9a53d4
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sat Aug 8 18:05:33 2026 -0400
|
||||
|
||||
Release 3.8.0
|
||||
|
||||
Bump version to 3.8.0, soname to libffi.so.8.5.0 (libtool 13:0:5) for the
|
||||
new public interfaces added this cycle (FFI_TYPE_VECTOR, ffi_call_plan_size),
|
||||
date the README history section, and update doc/version.texi.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit d956fe177ccfd4bb91ae7cb3ccaa0f8935a76522
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sat Aug 8 17:38:40 2026 -0400
|
||||
|
||||
testsuite: distribute plan_size.c
|
||||
|
||||
The ffi_call_plan_size test added in #1006 was not listed in EXTRA_DIST,
|
||||
so it would be omitted from release tarballs (it still runs from a git
|
||||
checkout, where dejagnu globs *.c). Add it alongside the other plan_*.c
|
||||
tests.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit 670a0327b7d1576712a3cad7b9297f59f23d5430
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sat Aug 8 17:09:22 2026 -0400
|
||||
|
||||
README: note i386 BSD small-struct register return
|
||||
|
||||
Follow-up to #1010, which returns small structs in registers on i386
|
||||
FreeBSD/OpenBSD but did not update the History section.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit f744bc303fa4c69f1202ce283b866ebc768e0432
|
||||
Merge: abc18be0 5b8fa3fe
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sat Aug 8 17:09:02 2026 -0400
|
||||
|
||||
Merge pull request #1010 from DTW-Thalion/x86-bsd-small-struct-return
|
||||
|
||||
x86: return small structs in registers on the BSD i386 targets
|
||||
|
||||
commit abc18be0d9ba9cc37c955b317e63cd52fd0d90ee
|
||||
Merge: ed742112 5f24e6a0
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sat Aug 8 16:57:36 2026 -0400
|
||||
|
||||
Merge pull request #1006 from rvandermeulen/call-plan-size
|
||||
|
||||
call_plan: add ffi_call_plan_size to report a plan's allocation
|
||||
|
||||
commit ed7421122880e4daff87f1c8623d508a2e2c5c9a
|
||||
Merge: e43f2548 e6db2d38
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sat Aug 8 16:41:24 2026 -0400
|
||||
|
||||
Merge pull request #1009 from libffi/fix-jumptable-desync-family
|
||||
|
||||
Fix FFI_TYPE_LAST/vector jump-table desyncs on ia64, ppc64 (BE ELFv2), and aarch64
|
||||
|
||||
commit e6db2d38decee8bf6321472dff5147ad311e639a
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Fri Aug 7 17:00:49 2026 -0400
|
||||
|
||||
aarch64: reject sub-4-byte vector lanes in HVA classification
|
||||
|
||||
is_vfp_type() maps a homogeneous vector aggregate's lane width onto the
|
||||
S/D/Q register classes via FFI_TYPE_FLOAT + intlog2(reg_size) - 2, and
|
||||
encodes the result as an AARCH64_RET_* code. A lane narrower than 4 bytes
|
||||
(e.g. a struct of two 2-byte vectors, which libffi's own initialize_vector
|
||||
accepts) yields intlog2(reg_size) < 2, producing a code below
|
||||
AARCH64_RET_S4. extend_hfa_type() then computes a negative jump-table
|
||||
offset (h - AARCH64_RET_S4) and branches before its table -- a wild
|
||||
computed branch during ffi_call argument marshalling.
|
||||
|
||||
Such a type has no short-vector register class under AAPCS64, so reject it
|
||||
in is_vfp_type() (returning 0 routes it through the generic aggregate
|
||||
path). Fixing it at the source covers both the argument path
|
||||
(extend_hfa_type) and the return path. Verified on aarch64 (Fedora under
|
||||
qemu-aarch64): a call passing such an HVA segfaults before the fix and
|
||||
returns correctly after it.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit 159268174ece06f6854c6d1bca1a9b95961f6ae9
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Fri Aug 7 08:18:51 2026 -0400
|
||||
|
||||
powerpc64: fix big-endian ELFv2 closure returns of 5/6/7-byte structs
|
||||
|
||||
On big-endian ppc64 ELFv2, ffi_closure_helper_LINUX64 returns the load
|
||||
codes PPC64_LD_STRUCT_5/6/7 (17/18/19) for closures returning a 5-, 6-,
|
||||
or 7-byte struct, but linux64_closure.S only defined return jump-table
|
||||
entries through PPC64_LD_STRUCT_3 (16). The E() macro places each 16-byte
|
||||
slot with .align 4 (no .org), so codes 17/18/19 fell through into the
|
||||
.Lmoredouble continuation: the closure loaded FP registers and returned
|
||||
without writing r3, so the ELFv2 caller read back the computed jump
|
||||
target -- a libffi code address -- as the struct value (wrong result plus
|
||||
a code-pointer disclosure). Little-endian ELFv2 is unaffected (those
|
||||
codes alias PPC_LD_R3/I64); big-endian ELFv1 returns structs by reference
|
||||
and never emits the codes.
|
||||
|
||||
Add the three missing handlers, loading the struct right-justified into
|
||||
r3 per the ELFv2 convention. Verified on big-endian ppc64 ELFv2 (Adélie
|
||||
Linux under qemu-ppc64): testsuite/libffi.closures/cls_{5,6,7}_1_byte.c
|
||||
abort before the fix and pass after it.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit 3fdd99b5d2fb4c8f940d82fc4b1e530e743c685b
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Fri Aug 7 06:11:00 2026 -0400
|
||||
|
||||
ia64: fix return jump-table desync after FFI_TYPE_LAST bump
|
||||
|
||||
The .Lst_table/.Lld_table return-value dispatch tables in unix.S are
|
||||
indexed by the FFI_IA64_TYPE_SMALL_STRUCT/HFA_* codes, which are
|
||||
FFI_TYPE_LAST-relative, but the tables hardcoded 20 entries assuming
|
||||
FFI_TYPE_LAST == FFI_TYPE_COMPLEX (15). The conditional __int128
|
||||
support added in 3.6.0 advanced FFI_TYPE_LAST to SINT128 (17), and
|
||||
FFI_TYPE_VECTOR advanced it to 18, shifting SMALL_STRUCT to 19 -- so a
|
||||
small-struct return dispatched to the HFA-ldouble handler's 16-byte
|
||||
stfe store, an out-of-bounds write past rvalue, and HFA returns indexed
|
||||
off the end of the table entirely.
|
||||
|
||||
Add the missing UINT128/SINT128/VECTOR slots to both tables (pointing at
|
||||
the existing not-implemented void handler, matching FFI_TYPE_COMPLEX)
|
||||
and a FFI_TYPE_LAST tripwire, mirroring the pa and win64 guards.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit 5b8fa3fed84ce17768eeb379f5b81663172482e9
|
||||
Author: Todd White <todd.white@thalion.global>
|
||||
Date: Fri Aug 7 19:40:05 2026 -0400
|
||||
|
||||
x86: return small structs in registers on the BSD i386 targets
|
||||
|
||||
i386 FreeBSD and OpenBSD return a struct of 1, 2, 4 or 8 bytes in eax and
|
||||
edx, as Darwin and win32 do. ffi_prep_cif_machdep applied the size test
|
||||
only under X86_WIN32 and X86_DARWIN, so on these targets it recorded
|
||||
X86_RET_STRUCTPOP and allocated a return pointer the callee never writes,
|
||||
and a struct return through ffi_call or through a closure read a value
|
||||
that was never stored.
|
||||
|
||||
configure.host already maps i?86-*-freebsd* and i?86-*-openbsd* to
|
||||
TARGET=X86_FREEBSD, and include/ffi.h.in defines that name, so extend the
|
||||
condition to it. Sizes 3, 6 and 12 continue to be returned in memory.
|
||||
|
||||
commit 5f24e6a05574b1aa74cca77b1ecd6413a8105f62
|
||||
Author: Ryan VanderMeulen <rvandermeulen@mozilla.com>
|
||||
Date: Wed Aug 5 11:09:51 2026 -0400
|
||||
|
||||
call_plan: add ffi_call_plan_size to report a plan's allocation
|
||||
|
||||
ffi_call_plan is opaque, so an embedder that tracks the memory a long-lived
|
||||
plan holds has no way to ask how big it is. The only options are to hardcode
|
||||
a guess or to hardcode knowledge of the private struct layout, and both go
|
||||
stale silently on the next release.
|
||||
|
||||
The x86-64 backend records the byte count in ffi_plan at the point it is
|
||||
passed to malloc, so the reported value cannot drift from the allocation it
|
||||
describes; ffi_call_plan_size adds that to the handle and treats a signature
|
||||
with no fast path as owning nothing beyond it. The generic backend's plan is
|
||||
a bare handle, so it reports sizeof (struct ffi_call_plan). The counter lives
|
||||
in ffi_plan rather than in the handle so that only plans that actually own a
|
||||
move-list pay for it, and plans without one pay nothing.
|
||||
|
||||
Computing the size in the query from cif->nargs instead would duplicate
|
||||
build_plan's allocation formula in a second place, and would report the wrong
|
||||
number if the cif were re-prepared with a different argument count after the
|
||||
plan was built.
|
||||
|
||||
The new symbol gets its own version node rather than joining
|
||||
LIBFFI_CALL_PLAN_8.4, which shipped in 3.7.0: adding to a released node would
|
||||
let a binary that needs ffi_call_plan_size look satisfiable against a 3.7.x
|
||||
library that exports the node without the symbol, turning a clean link error
|
||||
into a runtime failure. libtool-version is left alone, since rule 2 in that
|
||||
file defers version updates to immediately before a release.
|
||||
|
||||
commit e43f254881f9010a26c48f595928461c0432c7b4
|
||||
Merge: 2fd434cd 04d721cc
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Thu Aug 6 00:40:32 2026 -0400
|
||||
|
||||
Merge pull request #1008 from libffi/fix-win64-vector-small-struct-flags
|
||||
|
||||
x86: fix Win64 small-struct returns broken by FFI_TYPE_VECTOR
|
||||
|
||||
commit 04d721cc448316dbba5af506be46315efabd80e3
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Wed Aug 5 22:59:29 2026 -0400
|
||||
|
||||
x86: fix Win64 small-struct returns broken by FFI_TYPE_VECTOR
|
||||
|
||||
Adding FFI_TYPE_VECTOR (#1000) moved FFI_TYPE_LAST from FFI_TYPE_SINT128
|
||||
(17) to FFI_TYPE_VECTOR (18). The Win64 return pseudo-types
|
||||
|
||||
FFI_TYPE_SMALL_STRUCT_1B/2B/4B = FFI_TYPE_LAST + 1..3
|
||||
|
||||
are FFI_TYPE_LAST-relative, so they shifted from 18/19/20 to 19/20/21.
|
||||
The win64.S / win64_intel.S return-value dispatch is a computed jump
|
||||
table indexed by cif->flags (base + flags*8) whose handlers are emitted
|
||||
contiguously right after FFI_TYPE_SINT128, with no slot for value 18.
|
||||
Under the sequential E() variant used by the MSVC/ml64 build, the
|
||||
size-1/2/4 small-struct handlers therefore sat one 8-byte slot below the
|
||||
flag values ffiw64.c now emits, so small structs returned by value were
|
||||
written with the wrong width (or fell off the table into abort). This
|
||||
showed up as 14 execution failures in the "Windows 64-bit Visual C++" CI
|
||||
job (s55, struct3, struct_by_value_small, struct_return_2H, the small
|
||||
cls_* / single_entry_structs closures, and bhaible DGTEST 47/53/55).
|
||||
|
||||
Add an FFI_TYPE_VECTOR abort stub between SINT128 and SMALL_STRUCT_1B in
|
||||
both tables so the jump table stays contiguous and the small-struct
|
||||
entries realign with their (shifted) code values. Win64 does not marshal
|
||||
vectors -- ffi_prep_cif_core rejects them since FFI_TARGET_HAS_VECTOR_TYPE
|
||||
is undefined there -- so the slot is never reached at runtime.
|
||||
|
||||
Also add a pa-style compile-time tripwire to src/x86/ffitarget.h so the
|
||||
next generic type added bumps FFI_TYPE_LAST and #errors until the win64
|
||||
tables are updated in step. 32-bit x86 is unaffected: sysv.S indexes its
|
||||
store table by the independent X86_RET_* enum, not FFI_TYPE_LAST.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit 2fd434cd9ada4d3d97b355e62c3ce3a969682230
|
||||
Merge: a00279c2 2aa33761
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sun Aug 2 10:18:45 2026 -0400
|
||||
|
||||
Merge pull request #1005 from libffi/tramp-cache-unsupported-verdict
|
||||
|
||||
tramp: cache the static trampoline "unsupported" verdict
|
||||
|
||||
commit 2aa33761c0536339f9f322902b9bb3a981114724
|
||||
Merge: 76883c62 a00279c2
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sun Aug 2 10:18:33 2026 -0400
|
||||
|
||||
Merge branch 'master' into tramp-cache-unsupported-verdict
|
||||
|
||||
commit 76883c628a5273f71fb025e75bf1076adae3bb4b
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sun Aug 2 10:05:22 2026 -0400
|
||||
|
||||
tramp: cache the static trampoline "unsupported" verdict
|
||||
|
||||
ffi_tramp_init() bailed out with a plain `return 0` when the system page
|
||||
size exceeds the trampoline code table mapping, without recording the
|
||||
outcome in tramp_globals.status. Because that early return was the only
|
||||
failure exit that left status as UNINITIALIZED, every subsequent
|
||||
ffi_tramp_alloc()/ffi_tramp_is_supported() call re-ran the full
|
||||
initialization (ffi_tramp_arch(), sysconf(), etc.) instead of
|
||||
short-circuiting on the cached verdict like the other two failure paths.
|
||||
|
||||
The comparison is between two process-lifetime invariants -- map_size is
|
||||
a compile-time constant from ffi_tramp_arch(), and page_size is fixed for
|
||||
the life of the process (and only checked when sysconf() returned a valid
|
||||
value) -- so it can never flip. Caching FAILED is therefore safe and
|
||||
matches the intent of the status field.
|
||||
|
||||
Affects hosts with pages larger than the 16K table, in practice 64K-page
|
||||
aarch64 kernels, where static trampolines are correctly declined but the
|
||||
decline was recomputed on every closure allocation. No functional change
|
||||
on 4K/16K-page hosts.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit a00279c2dc8e191ae5136b46bf6ae0e7a8da5b7a
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sat Aug 1 07:17:23 2026 -0400
|
||||
|
||||
Note unreleased development changes in README history
|
||||
|
||||
Add a "Development source only" History block for changes on master
|
||||
since 3.7.1: FFI_TYPE_VECTOR SIMD support (#1000), powerpc64 _Complex
|
||||
long double (#1003), and the powerpc Darwin closure fix (#1002).
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit ce77ca107a5cb0d10d5525c9422f0207e6c79ebf
|
||||
Merge: d257b084 19dbdb53
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sat Aug 1 07:09:17 2026 -0400
|
||||
|
||||
Merge pull request #1000 from edusperoni/feat/vector-types
|
||||
|
||||
Add FFI_TYPE_VECTOR: vector (SIMD) type support with libffi-computed layout
|
||||
|
||||
commit d257b08495e95248f66b7bd50dd106ea19124df9
|
||||
Merge: 333d87cf b2170647
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Tue Jul 28 00:47:29 2026 -0400
|
||||
|
||||
Merge pull request #1004 from libffi/fix-1002-ppc-darwin-closure
|
||||
|
||||
powerpc: fix Darwin closure returns broken by #951
|
||||
|
||||
commit b2170647583461f42dc2d9f201211fcafda2429f
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Mon Jul 27 20:08:15 2026 -0400
|
||||
|
||||
powerpc: fix Darwin closure returns broken by #951
|
||||
|
||||
PR #951 (840add3b) changed the shared PowerPC closure helper,
|
||||
ffi_closure_helper_common, to return a small PPC_LD_* jump-table index
|
||||
instead of the ffi_type*, and rewrote aix_closure.S to consume it -- but
|
||||
left darwin_closure.S expecting the old ffi_type* and dereferencing it.
|
||||
With the helper now returning a small integer, ffi_closure_ASM
|
||||
dereferenced e.g. 0 (PPC_LD_NONE, a void return) as a pointer, faulting
|
||||
on a load from address 0. This crashed essentially every closure call
|
||||
-- including every gobject-introspection signal handler -- on 32- and
|
||||
64-bit PowerPC Darwin (SIGBUS at ffi_closure_ASM, dar=0; issue #1002).
|
||||
|
||||
Convert darwin_closure.S to the PPC_LD_* convention, mirroring
|
||||
aix_closure.S: drop the ffi_type* dereference, use the returned index
|
||||
directly, and reorder the return-value jump table into PPC_LD_* order
|
||||
(NONE, R3, R3R4, F32, F64, F128, U8, S8, U16, S16, and on ppc64 U32, S32).
|
||||
|
||||
Darwin, unlike AIX, returns small structs by value in registers, which
|
||||
the existing assembly handles (Lsmallstruct/Lfour/Lstructend). The
|
||||
helper's return code is a single small integer with no room for
|
||||
cif->rtype, which that assembly needs, so for a by-value struct return
|
||||
the helper now stashes cif->rtype in the first parameter-save slot (dead
|
||||
by return time) and returns a new PPC_LD_STRUCT code; the PPC_LD_STRUCT
|
||||
fragment recovers it and drives the unchanged struct machinery. By-
|
||||
reference struct returns still return PPC_LD_NONE.
|
||||
|
||||
Based on the approach in a patch by Sergey Fedorov (@barracuda156); the
|
||||
jump table here is reordered to the PPC_LD_* layout so that float,
|
||||
double, long double, sub-word and 64-bit returns also dispatch correctly.
|
||||
|
||||
I have no PowerPC Darwin hardware; the jump-table fragment offsets were
|
||||
checked by assembling for powerpc and powerpc64, but runtime
|
||||
confirmation on 10.5/10.6 is still needed.
|
||||
|
||||
Fixes #1002.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit 333d87cf201ee279c9870fb5ad3e48e3a08aa6e5
|
||||
Merge: 46cb2e38 d7cd3a61
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Mon Jul 27 08:52:34 2026 -0400
|
||||
|
||||
Merge pull request #1003 from libffi/fix-ppc64le-complex-longdouble
|
||||
|
||||
powerpc64: implement _Complex long double for both IBM-128 and IEEE-128
|
||||
|
||||
commit d7cd3a6194885c85255c77782a05a153a42b29a5
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Mon Jul 27 07:06:39 2026 -0400
|
||||
|
||||
powerpc64: implement _Complex long double for both IBM-128 and IEEE-128
|
||||
|
||||
Complex support for POWERPC64 ELFv2 (f0ca157, #970) defined
|
||||
FFI_TARGET_HAS_COMPLEX_TYPE, which flips complex.exp from marking the
|
||||
libffi.complex suite UNSUPPORTED to running it. _Complex long double
|
||||
was deliberately deferred with FFI_BAD_TYPEDEF, so ffi_prep_cif failed
|
||||
and every libffi.complex/*longdouble* test aborted. This was not caught
|
||||
upstream because an XFAIL entry in the rlgl CI policy masked the FAILs.
|
||||
|
||||
Implement both long double formats:
|
||||
|
||||
- IBM-128 (double-double): each _Complex long double is passed and
|
||||
returned as four doubles (real hi/lo, imag hi/lo) in f1-f4, with a
|
||||
GPR shadow doubleword per FPR, and returned as a double homogeneous
|
||||
aggregate.
|
||||
|
||||
- IEEE binary128: real in v2, imag in v3; each half occupies a vector
|
||||
register (or a 16-byte-aligned parameter save slot with two GPR
|
||||
shadow doublewords) and is returned via the vector-homogeneous
|
||||
small-struct path.
|
||||
|
||||
discover_homogeneous_aggregate now accepts FFI_TYPE_LONGDOUBLE as a
|
||||
_Complex inner type so struct-of-complex-longdouble is treated as an HFA.
|
||||
Covers ffi_prep_cif, ffi_prep_args64, and the closure decode/return
|
||||
paths.
|
||||
|
||||
Fixes #1001.
|
||||
|
||||
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||||
|
||||
commit debc00a0114d8530d6d691862028c607aa17dd6a
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Sun Jul 26 07:28:36 2026 -0400
|
||||
|
||||
Update doc version
|
||||
|
||||
commit 19dbdb53e869e07fbff05c86d634e8c08c9a7f61
|
||||
Author: Eduardo Speroni <edusperoni@gmail.com>
|
||||
Date: Tue Jul 21 20:32:24 2026 -0300
|
||||
|
||||
testsuite: fix vector suite CI failures on gcc and MSVC
|
||||
|
||||
Two fixes for the libffi.vector suite:
|
||||
|
||||
- vector_double4.c: the non-aarch64 branch built its own void_args
|
||||
array and never read the already-populated args, tripping gcc's
|
||||
-Wunused-but-set-variable (an excess-errors FAIL on Linux x86-64
|
||||
with gcc; clang does not emit this warning). Use args for the
|
||||
negative argument-passing check instead.
|
||||
|
||||
- vector.exp: the suite only probed FFI_TARGET_HAS_VECTOR_TYPE, but
|
||||
the tests are written with the GCC/Clang vector extension. On
|
||||
Windows ARM64 the aarch64 port enables the feature while MSVC
|
||||
cannot compile __attribute__ ((vector_size)), so every test failed
|
||||
to build. Add a compile probe and mark the suite unsupported when
|
||||
the compiler lacks the syntax.
|
||||
|
||||
commit 71a95a2cd433b151b3fcf83a0a830eb9aa38fa3a
|
||||
Author: Eduardo Speroni <edusperoni@gmail.com>
|
||||
Date: Tue Jul 21 16:44:26 2026 -0300
|
||||
|
||||
testsuite: add libffi.vector suite for vector (SIMD) types
|
||||
|
||||
Model a new testsuite/libffi.vector/ directory on testsuite/libffi.complex:
|
||||
vector.exp reuses the same dg/run-many-tests driver and skips every test as
|
||||
"unsupported" on ports whose headers do not define
|
||||
FFI_TARGET_HAS_VECTOR_TYPE (libffi_feature_test), so unsupported targets
|
||||
still compile the gating cleanly.
|
||||
|
||||
Vector types are built with the portable __attribute__((vector_size)) via a
|
||||
small make_vector_type() helper (vector.h); each test cross-checks the value
|
||||
returned through ffi against a direct native call. Coverage:
|
||||
|
||||
- vector_float32x4 / vector_float32x2 / vector_double2 / vector_int32x4:
|
||||
pass and return 8- and 16-byte float, double and integer vectors
|
||||
(float32x4 is the vec4 shape of libffi/libffi#773);
|
||||
- vector_args_spill: ten vectors interleaved with int/double scalars,
|
||||
exhausting the vector argument registers and spilling to the stack;
|
||||
- vector_vec3: Clang-only ext_vector_type(3), verifying the 12->16 byte
|
||||
power-of-two padding matches a natively compiled callee (a no-op on
|
||||
other compilers);
|
||||
- vector_double4: on AArch64 a 32-byte vector round-trips (by reference /
|
||||
in memory); elsewhere ffi_prep_cif must return FFI_BAD_TYPEDEF, checked
|
||||
for both return and argument;
|
||||
- vector_hva: a struct of two identical vectors (HVA) passes and returns
|
||||
on both AArch64 (Q-register pair) and x86-64 (SSE struct classification);
|
||||
- cls_vector: a closure receiving vector arguments and returning a vector;
|
||||
- vector_validate: heterogeneous lanes, an empty vector, and a non-scalar
|
||||
lane are each rejected with FFI_BAD_TYPEDEF, and a well-formed vector is
|
||||
accepted with the computed power-of-two size and min(size,16) alignment.
|
||||
|
||||
The files are added to testsuite/Makefile.am EXTRA_DIST, matching how
|
||||
libffi.complex is distributed.
|
||||
|
||||
References: libffi/libffi#414, libffi/libffi#773.
|
||||
|
||||
commit 93b274cec912ac2395575a8bd4dfcb527f61599c
|
||||
Author: Eduardo Speroni <edusperoni@gmail.com>
|
||||
Date: Tue Jul 21 16:32:04 2026 -0300
|
||||
|
||||
x86-64: marshal vector (SIMD) types per the System V psABI
|
||||
|
||||
Define FFI_TARGET_HAS_VECTOR_TYPE for the SysV x86-64 backend (ffi64.c;
|
||||
32-bit x86 and the Windows ffiw64.c backend are excluded) and integrate
|
||||
FFI_TYPE_VECTOR into the existing psABI classifier without restructuring
|
||||
it:
|
||||
|
||||
- classify_argument gains a FFI_TYPE_VECTOR case: an 8-byte vector is
|
||||
one SSE eightbyte (X86_64_SSE_CLASS); a 16-byte vector is one %xmm
|
||||
register (X86_64_SSE_CLASS + X86_64_SSEUP_CLASS). The existing
|
||||
INTEGERSI/SSESF/SSEDF/UINT128 handling is untouched, and the SSE+SSEUP
|
||||
argument marshalling already merges both eightbytes into one %xmm.
|
||||
- ffi_prep_cif_machdep classifies vector returns symmetrically: 8 bytes
|
||||
in %xmm0 (UNIX64_RET_XMM64), 16 bytes in %xmm0 (UNIX64_RET_XMM128).
|
||||
- Vectors wider than 16 bytes return FFI_BAD_TYPEDEF from
|
||||
ffi_prep_cif_machdep, for both returns and arguments. Correct
|
||||
%ymm/%zmm passing needs unix64.S register-save changes and is left as
|
||||
a v1 limitation rather than silently passing them in memory.
|
||||
|
||||
Closures need no separate change: the closure paths reuse
|
||||
classify_argument for arguments and cif->flags for the return.
|
||||
|
||||
References: libffi/libffi#414.
|
||||
|
||||
commit 5eaa8a389de61fc3b056f62c48ceade1931b5413
|
||||
Author: Eduardo Speroni <edusperoni@gmail.com>
|
||||
Date: Tue Jul 21 16:30:12 2026 -0300
|
||||
|
||||
aarch64: marshal vector (SIMD) types per AAPCS64
|
||||
|
||||
Define FFI_TARGET_HAS_VECTOR_TYPE for AArch64 and teach is_vfp_type to
|
||||
classify FFI_TYPE_VECTOR, so ffi_call and closures pass and return
|
||||
vectors the way AAPCS64 (and current GCC/Clang) do:
|
||||
|
||||
- 8- and 16-byte vectors travel in a single V/Q register (a Short
|
||||
Vector), for float, double and integer lane types alike;
|
||||
- homogeneous vector aggregates -- a struct of up to four identical
|
||||
8- or 16-byte vectors -- travel in that many consecutive V/Q
|
||||
registers (an HVA), e.g. struct{float32x4 a,b} in {q0,q1};
|
||||
- a bare vector wider than 16 bytes (e.g. a 32-byte double4) has no
|
||||
short-vector register class, so is_vfp_type returns 0 and the
|
||||
existing composite path passes it by invisible reference and returns
|
||||
it in memory -- exactly what a natively compiled callee expects.
|
||||
|
||||
is_simd() reports the width of one Neon register slot (a bare vector's
|
||||
whole size, or one lane vector of an HVA); is_vfp_type() encodes
|
||||
num_registers slots of that width onto the existing AARCH64_RET_{D,Q}*
|
||||
codes via intlog2. is_hfa0/is_hfa1 recurse through FFI_TYPE_VECTOR so
|
||||
HVA homogeneity is checked, and the three fundamental-type dispatch
|
||||
switches (machdep return, ffi_call_int, ffi_closure_SYSV_inner) route
|
||||
FFI_TYPE_VECTOR through is_vfp_type alongside FFI_TYPE_STRUCT.
|
||||
|
||||
Ported from the battle-tested NativeScript aarch64 vector marshaller,
|
||||
adapted to the FFI_TYPE_VECTOR API and extended so that integer-lane
|
||||
vectors (e.g. int32x4) are classified into V registers too -- the
|
||||
original only handled floating-point lanes.
|
||||
|
||||
References: libffi/libffi#414, libffi/libffi#773 (aarch64 vec4 return).
|
||||
|
||||
commit b6b8be54acc90f7db1dcf3d1c91238a5a9bca185
|
||||
Author: Eduardo Speroni <edusperoni@gmail.com>
|
||||
Date: Tue Jul 21 16:26:33 2026 -0300
|
||||
|
||||
core: add FFI_TYPE_VECTOR fundamental type with computed layout
|
||||
|
||||
Introduce a portable API for marshalling vector (SIMD) types -- the
|
||||
values produced by GCC's __attribute__((vector_size)) and Clang's
|
||||
ext_vector_type. This answers the stalled PR #414 and the maintainer's
|
||||
2018 design questions
|
||||
(https://sourceware.org/legacy-ml/libffi-discuss/2018/msg00020.html):
|
||||
rather than requiring callers to hand-compute a vector's size and
|
||||
alignment (and gating the feature behind configure), libffi now derives
|
||||
the layout itself and the type code is defined unconditionally.
|
||||
|
||||
A vector is described exactly like a struct: type == FFI_TYPE_VECTOR and
|
||||
a NULL-terminated elements[] array, except every element must point to
|
||||
the SAME fundamental scalar (float, double, or a fixed-width integer
|
||||
UINT8..SINT64) and the count is the number of lanes. The caller leaves
|
||||
size and alignment at zero; ffi_prep_cif computes:
|
||||
|
||||
size = lane_size * count, rounded up to the next power of two
|
||||
(matches Clang ext_vector_type storage: 3 x float -> 16,
|
||||
3 x double -> 32; GCC vector_size already requires pow2
|
||||
totals so it is identical there);
|
||||
alignment = min(size, 16).
|
||||
|
||||
Validation (identical scalar lanes, count >= 1, scalar-only) yields
|
||||
FFI_BAD_TYPEDEF otherwise.
|
||||
|
||||
- include/ffi.h.in: FFI_TYPE_VECTOR = 18 (after SINT128 = 17),
|
||||
FFI_TYPE_LAST bumped. Defined unconditionally, no configure gating.
|
||||
- src/prep_cif.c: initialize_vector() computes the layout in
|
||||
initialize_aggregate; ffi_type_contains_vector() rejects vectors
|
||||
(including nested in structs, argument or return) with
|
||||
FFI_BAD_TYPEDEF on any port that does not define
|
||||
FFI_TARGET_HAS_VECTOR_TYPE -- no aborts. Vector returns reserve the
|
||||
hidden return-pointer slot like structs.
|
||||
- src/raw_api.c, src/java_raw_api.c: plumb FFI_TYPE_VECTOR alongside
|
||||
FFI_TYPE_STRUCT, mirroring how FFI_TYPE_COMPLEX is handled.
|
||||
- src/debug.c: ffi_type_test requires elements != NULL for vectors.
|
||||
- src/pa/ffitarget.h: bump the FFI_PA_TYPE_LAST tripwire; PA gates
|
||||
vectors out in prep_cif so its jump tables are never reached.
|
||||
- doc/libffi.texi: new "Vector Types" node documenting the API, the
|
||||
computed-layout rule, the psABI framing, and the per-port support
|
||||
table.
|
||||
|
||||
No port defines FFI_TARGET_HAS_VECTOR_TYPE yet, so this commit rejects
|
||||
every vector signature; the per-architecture ports follow.
|
||||
|
||||
References: libffi/libffi#414, libffi/libffi#773.
|
||||
|
||||
commit 46cb2e3871059f7f5113329ddcca818de3a8cfae
|
||||
Merge: ca86812c 8cd11a77
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Fri Jul 10 16:51:18 2026 -0400
|
||||
|
||||
Merge pull request #998 from bgilbert/tests
|
||||
|
||||
testsuite: Remember to distribute tests added for 3.7.1
|
||||
|
||||
commit 8cd11a772d8a0b687f43390697baa002ae6504d5
|
||||
Author: Benjamin Gilbert <bgilbert@backtick.net>
|
||||
Date: Fri Jul 10 11:56:55 2026 -0700
|
||||
|
||||
testsuite: Remember to distribute tests added for 3.7.1
|
||||
|
||||
commit ca86812cd430cff3018e491ba75a4f3c9ea969d2
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Fri Jul 10 10:56:47 2026 -0400
|
||||
|
||||
ci: Don't publish rlgl reports on tag pushes
|
||||
|
||||
A tag and its commit fire two CI runs at the same SHA. Both run the
|
||||
publish-reports job, which deploys a fixed-name github-pages artifact
|
||||
via actions/deploy-pages; the two deployments collide and one fails
|
||||
with BlobNotFound (seen on the v3.7.1 tag run). The same-SHA branch
|
||||
push already publishes the reports, so gate the job off tag pushes.
|
||||
|
||||
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||||
|
||||
commit 5c1c43091ed611fdea774374355eb938c73a9157
|
||||
Author: Anthony Green <green@moxielogic.com>
|
||||
Date: Fri Jul 10 09:50:53 2026 -0400
|
||||
|
||||
Vendored
+22
@@ -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.
|
||||
|
||||
Vendored
+14
-14
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.71 for libffi 3.7.1.
|
||||
# Generated by GNU Autoconf 2.71 for libffi 3.8.0.
|
||||
#
|
||||
# Report bugs to <http://github.com/libffi/libffi/issues>.
|
||||
#
|
||||
@@ -621,8 +621,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='libffi'
|
||||
PACKAGE_TARNAME='libffi'
|
||||
PACKAGE_VERSION='3.7.1'
|
||||
PACKAGE_STRING='libffi 3.7.1'
|
||||
PACKAGE_VERSION='3.8.0'
|
||||
PACKAGE_STRING='libffi 3.8.0'
|
||||
PACKAGE_BUGREPORT='http://github.com/libffi/libffi/issues'
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1417,7 +1417,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures libffi 3.7.1 to adapt to many kinds of systems.
|
||||
\`configure' configures libffi 3.8.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1489,7 +1489,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of libffi 3.7.1:";;
|
||||
short | recursive ) echo "Configuration of libffi 3.8.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1628,7 +1628,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
libffi configure 3.7.1
|
||||
libffi configure 3.8.0
|
||||
generated by GNU Autoconf 2.71
|
||||
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
@@ -2259,7 +2259,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by libffi $as_me 3.7.1, which was
|
||||
It was created by libffi $as_me 3.8.0, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@@ -3233,10 +3233,10 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||
ac_config_headers="$ac_config_headers fficonfig.h"
|
||||
|
||||
|
||||
FFI_VERSION_STRING="3.7.1"
|
||||
ffi_version_major=`echo "3.7.1" | cut -d. -f1`
|
||||
ffi_version_minor=`echo "3.7.1" | cut -d. -f2 | sed 's/[^0-9].*//'`
|
||||
ffi_version_micro=`echo "3.7.1" | cut -d. -f3 | sed 's/[^0-9].*//'`
|
||||
FFI_VERSION_STRING="3.8.0"
|
||||
ffi_version_major=`echo "3.8.0" | cut -d. -f1`
|
||||
ffi_version_minor=`echo "3.8.0" | cut -d. -f2 | sed 's/[^0-9].*//'`
|
||||
ffi_version_micro=`echo "3.8.0" | cut -d. -f3 | sed 's/[^0-9].*//'`
|
||||
FFI_VERSION_NUMBER=`expr ${ffi_version_major:-0} \* 10000 + ${ffi_version_minor:-0} \* 100 + ${ffi_version_micro:-0}`
|
||||
|
||||
|
||||
@@ -3986,7 +3986,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE='libffi'
|
||||
VERSION='3.7.1'
|
||||
VERSION='3.8.0'
|
||||
|
||||
|
||||
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
|
||||
@@ -20661,7 +20661,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by libffi $as_me 3.7.1, which was
|
||||
This file was extended by libffi $as_me 3.8.0, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -20729,7 +20729,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
libffi config.status 3.7.1
|
||||
libffi config.status 3.8.0
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
Vendored
+116
-16
@@ -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
|
||||
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
+98
@@ -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
|
||||
|
||||
|
||||
Vendored
+4
-4
@@ -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
|
||||
Vendored
+4
-4
@@ -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
|
||||
Vendored
+2
-2
@@ -7,8 +7,8 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
LIBFFI_VERSION = '3.7.1'
|
||||
LIBFFI_VERSION_NUMBER = '30701'
|
||||
LIBFFI_VERSION = '3.8.0'
|
||||
LIBFFI_VERSION_NUMBER = '30800'
|
||||
|
||||
def normalize_arch(target_arch):
|
||||
aliases = {
|
||||
|
||||
Vendored
+10
-2
@@ -79,9 +79,10 @@ extern "C" {
|
||||
#define FFI_TYPE_COMPLEX 15
|
||||
#define FFI_TYPE_UINT128 16
|
||||
#define FFI_TYPE_SINT128 17
|
||||
#define FFI_TYPE_VECTOR 18
|
||||
|
||||
/* This should always refer to the last type code (for sanity checks). */
|
||||
#define FFI_TYPE_LAST FFI_TYPE_SINT128
|
||||
#define FFI_TYPE_LAST FFI_TYPE_VECTOR
|
||||
|
||||
#include <ffitarget.h>
|
||||
|
||||
@@ -535,7 +536,11 @@ void ffi_call(ffi_cif *cif,
|
||||
ffi_call_plan_alloc returns NULL only on allocation failure; a signature
|
||||
with no fast path is still valid and ffi_call_plan_invoke falls back to
|
||||
ffi_call for it. A plan is immutable once built, so it may be shared and
|
||||
invoked concurrently from multiple threads. */
|
||||
invoked concurrently from multiple threads.
|
||||
|
||||
ffi_call_plan_size reports the total number of bytes libffi allocated for a
|
||||
plan, so that callers tracking the footprint of long-lived plans do not have
|
||||
to guess at the size of an opaque type. */
|
||||
typedef struct ffi_call_plan ffi_call_plan;
|
||||
|
||||
FFI_API
|
||||
@@ -550,6 +555,9 @@ void ffi_call_plan_invoke (ffi_call_plan *plan,
|
||||
FFI_API
|
||||
void ffi_call_plan_free (ffi_call_plan *plan);
|
||||
|
||||
FFI_API
|
||||
size_t ffi_call_plan_size (ffi_call_plan *plan);
|
||||
|
||||
FFI_API
|
||||
ffi_status ffi_get_struct_offsets (ffi_abi abi, ffi_type *struct_type,
|
||||
size_t *offsets);
|
||||
|
||||
Vendored
+9
@@ -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:
|
||||
|
||||
Vendored
+1
-1
@@ -26,4 +26,4 @@
|
||||
# release, then set age to 0.
|
||||
#
|
||||
# CURRENT:REVISION:AGE
|
||||
12:1:4
|
||||
13:0:5
|
||||
Vendored
+114
-6
@@ -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)
|
||||
|
||||
Vendored
+4
@@ -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
|
||||
Vendored
+2
-1
@@ -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
|
||||
|
||||
Vendored
+11
@@ -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
|
||||
Vendored
+6
@@ -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
|
||||
|
||||
Vendored
+2
-1
@@ -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. */
|
||||
|
||||
Vendored
+7
-2
@@ -89,8 +89,13 @@ typedef enum ffi_abi {
|
||||
to the default case and is mapped to FFI_TYPE_INT, so cif->flags never
|
||||
exceeds FFI_TYPE_COMPLEX and the existing tables remain sufficient. Bump
|
||||
FFI_PA_TYPE_LAST to the current FFI_TYPE_LAST once you have confirmed any
|
||||
newly added generic type is likewise handled (or the tables extended). */
|
||||
#define FFI_PA_TYPE_LAST FFI_TYPE_SINT128
|
||||
newly added generic type is likewise handled (or the tables extended).
|
||||
|
||||
FFI_TYPE_VECTOR (18) is likewise not reached here: PA does not define
|
||||
FFI_TARGET_HAS_VECTOR_TYPE, so ffi_prep_cif_core rejects any vector
|
||||
signature with FFI_BAD_TYPEDEF before machdep runs. Bumping the tripwire
|
||||
past it is therefore safe. */
|
||||
#define FFI_PA_TYPE_LAST FFI_TYPE_VECTOR
|
||||
|
||||
/* Tripwire: when a new generic type is added FFI_TYPE_LAST changes and this
|
||||
fires, forcing a review of ffi_prep_cif_machdep and the linux.S / hpux32.S
|
||||
|
||||
+56
-75
@@ -186,19 +186,17 @@ LCFI1:
|
||||
/* Make the call. */
|
||||
bl BLCLS_HELP
|
||||
|
||||
/* r3 contains the rtype pointer... save it since we will need
|
||||
it later. */
|
||||
sg r3,LINKAGE_SIZE(r1) ; ffi_type * result_type
|
||||
lg r0,0(r3) ; size => r0
|
||||
lhz r3,FFI_TYPE_TYPE(r3) ; type => r3
|
||||
/* r3 now holds a small PPC_LD_* jump-table index (see the PPC_LD_*
|
||||
defines in ffi_darwin.c), not an ffi_type* as this file previously
|
||||
assumed: ffi_closure_helper_common cannot return both an ffi_type*
|
||||
and the dispatch index through r3, so it returns the index. The
|
||||
helper has already intercepted by-reference struct returns (writing
|
||||
the result to the caller`s buffer and returning PPC_LD_NONE); for a
|
||||
by-value struct return it returns PPC_LD_STRUCT and stashes cif->rtype
|
||||
in the first parameter-save slot, which the PPC_LD_STRUCT fragment
|
||||
below recovers. */
|
||||
|
||||
/* The helper will have intercepted structure returns and inserted
|
||||
the caller`s destination address for structs returned by ref. */
|
||||
|
||||
/* r3 contains the return type so use it to look up in a table
|
||||
so we know how to deal with each type. */
|
||||
|
||||
addi r5,r1,(SAVE_SIZE-RESULT_BYTES) /* Otherwise, our return is here. */
|
||||
addi r5,r1,(SAVE_SIZE-RESULT_BYTES) /* Our return value is here. */
|
||||
bl Lget_ret_type0_addr /* Get pointer to Lret_type0 into LR. */
|
||||
mflr r4 /* Move to r4. */
|
||||
slwi r3,r3,4 /* Now multiply return type by 16. */
|
||||
@@ -218,43 +216,60 @@ LFE1:
|
||||
Lget_ret_type0_addr:
|
||||
blrl
|
||||
|
||||
/* case FFI_TYPE_VOID */
|
||||
/* The fragments below are indexed by the PPC_LD_* return code that
|
||||
ffi_closure_helper_common handed back in r3, so their order must match the
|
||||
PPC_LD_* values in ffi_darwin.c. Each is exactly 16 bytes (four
|
||||
instructions), except the final PPC_LD_STRUCT fragment. */
|
||||
|
||||
/* case PPC_LD_NONE (void, or a struct returned by reference) */
|
||||
Lret_type0:
|
||||
b Lfinish
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_INT */
|
||||
/* case PPC_LD_R3 (one GPR: int, pointer, and on ppc64 also 64-bit ints) */
|
||||
Lret_type1:
|
||||
lg r3,0(r5)
|
||||
b Lfinish
|
||||
nop
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_FLOAT */
|
||||
/* case PPC_LD_R3R4 (two GPRs: the 32-bit ABI`s 64-bit integer) */
|
||||
Lret_type2:
|
||||
#if defined(__ppc64__)
|
||||
lg r3,0(r5)
|
||||
lg r4,8(r5)
|
||||
#else
|
||||
lwz r3,0(r5)
|
||||
lwz r4,4(r5)
|
||||
#endif
|
||||
b Lfinish
|
||||
nop
|
||||
|
||||
/* case PPC_LD_F32 */
|
||||
Lret_type3:
|
||||
lfs f1,0(r5)
|
||||
b Lfinish
|
||||
nop
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_DOUBLE */
|
||||
Lret_type3:
|
||||
/* case PPC_LD_F64 */
|
||||
Lret_type4:
|
||||
lfd f1,0(r5)
|
||||
b Lfinish
|
||||
nop
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_LONGDOUBLE */
|
||||
Lret_type4:
|
||||
/* case PPC_LD_F128 (128-bit long double: two doubles) */
|
||||
Lret_type5:
|
||||
lfd f1,0(r5)
|
||||
lfd f2,8(r5)
|
||||
b Lfinish
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_UINT8 */
|
||||
Lret_type5:
|
||||
/* case PPC_LD_U8 */
|
||||
Lret_type6:
|
||||
#if defined(__ppc64__)
|
||||
lbz r3,7(r5)
|
||||
#else
|
||||
@@ -264,8 +279,8 @@ Lret_type5:
|
||||
nop
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_SINT8 */
|
||||
Lret_type6:
|
||||
/* case PPC_LD_S8 */
|
||||
Lret_type7:
|
||||
#if defined(__ppc64__)
|
||||
lbz r3,7(r5)
|
||||
#else
|
||||
@@ -275,8 +290,8 @@ Lret_type6:
|
||||
b Lfinish
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_UINT16 */
|
||||
Lret_type7:
|
||||
/* case PPC_LD_U16 */
|
||||
Lret_type8:
|
||||
#if defined(__ppc64__)
|
||||
lhz r3,6(r5)
|
||||
#else
|
||||
@@ -286,8 +301,8 @@ Lret_type7:
|
||||
nop
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_SINT16 */
|
||||
Lret_type8:
|
||||
/* case PPC_LD_S16 */
|
||||
Lret_type9:
|
||||
#if defined(__ppc64__)
|
||||
lha r3,6(r5)
|
||||
#else
|
||||
@@ -297,77 +312,43 @@ Lret_type8:
|
||||
nop
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_UINT32 */
|
||||
Lret_type9:
|
||||
#if defined(__ppc64__)
|
||||
lwz r3,4(r5)
|
||||
#else
|
||||
lwz r3,0(r5)
|
||||
#endif
|
||||
b Lfinish
|
||||
nop
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_SINT32 */
|
||||
/* case PPC_LD_U32 (ppc64 only; the 32-bit ABI aliases U32 to PPC_LD_R3) */
|
||||
Lret_type10:
|
||||
#if defined(__ppc64__)
|
||||
lwz r3,4(r5)
|
||||
#else
|
||||
lwz r3,0(r5)
|
||||
#endif
|
||||
b Lfinish
|
||||
nop
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_UINT64 */
|
||||
/* case PPC_LD_S32 (ppc64 only; the 32-bit ABI aliases S32 to PPC_LD_R3) */
|
||||
Lret_type11:
|
||||
#if defined(__ppc64__)
|
||||
lg r3,0(r5)
|
||||
lwa r3,4(r5)
|
||||
b Lfinish
|
||||
nop
|
||||
#else
|
||||
lwz r3,0(r5)
|
||||
lwz r4,4(r5)
|
||||
b Lfinish
|
||||
nop
|
||||
#endif
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_SINT64 */
|
||||
Lret_type12:
|
||||
#if defined(__ppc64__)
|
||||
lg r3,0(r5)
|
||||
b Lfinish
|
||||
nop
|
||||
#else
|
||||
lwz r3,0(r5)
|
||||
lwz r4,4(r5)
|
||||
b Lfinish
|
||||
#endif
|
||||
nop
|
||||
|
||||
/* case FFI_TYPE_STRUCT */
|
||||
Lret_type13:
|
||||
/* case PPC_LD_STRUCT (a by-value struct return). This is the final,
|
||||
variable-length fragment, so it need not be padded to 16 bytes. The helper
|
||||
stashed cif->rtype in the first parameter-save slot (see ffi_darwin.c),
|
||||
because the small dispatch index in r3 left no room for it. */
|
||||
Lret_type_struct:
|
||||
lg r6,PARENT_PARM_BASE(r1) ; cif->rtype
|
||||
sg r6,LINKAGE_SIZE(r1) ; where the struct code below expects it
|
||||
lg r0,0(r6) ; size => r0
|
||||
#if defined(__ppc64__)
|
||||
lg r3,0(r5) ; we need at least this...
|
||||
cmpi 0,r0,4
|
||||
bgt Lstructend ; not a special small case
|
||||
b Lsmallstruct ; see if we need more.
|
||||
#else
|
||||
cmpwi 0,r0,4
|
||||
bgt Lfinish ; not by value
|
||||
lg r3,0(r5)
|
||||
lg r3,0(r5) ; a <=4-byte struct, returned in r3
|
||||
b Lfinish
|
||||
#endif
|
||||
/* case FFI_TYPE_POINTER */
|
||||
Lret_type14:
|
||||
lg r3,0(r5)
|
||||
b Lfinish
|
||||
nop
|
||||
nop
|
||||
|
||||
#if defined(__ppc64__)
|
||||
Lsmallstruct:
|
||||
beq Lfour ; continuation of Lret13.
|
||||
beq Lfour ; continuation of Lret_type_struct.
|
||||
cmpi 0,r0,3
|
||||
beq Lfinish ; don`t adjust this - can`t be any floats here...
|
||||
srdi r3,r3,48
|
||||
|
||||
Vendored
+23
@@ -60,11 +60,13 @@ struct ffi_aix_trampoline_struct {
|
||||
# define PPC_LD_S32 PPC_LD_R3
|
||||
# define PPC_LD_PTR PPC_LD_R3
|
||||
# define PPC_LD_I64 PPC_LD_R3R4
|
||||
# define PPC_LD_STRUCT 10
|
||||
#else
|
||||
# define PPC_LD_U32 10
|
||||
# define PPC_LD_S32 11
|
||||
# define PPC_LD_PTR PPC_LD_R3
|
||||
# define PPC_LD_I64 PPC_LD_R3
|
||||
# define PPC_LD_STRUCT 12
|
||||
#endif
|
||||
|
||||
extern void ffi_closure_ASM (void);
|
||||
@@ -1260,6 +1262,13 @@ ffi_closure_helper_common (ffi_cif* cif,
|
||||
long i, avn;
|
||||
ffi_dblfl * end_pfr = pfr + NUM_FPR_ARG_REGISTERS;
|
||||
unsigned size_al;
|
||||
int struct_ret_by_value = 0;
|
||||
/* When a struct is returned by value, ffi_closure_ASM's jump-table
|
||||
dispatch carries only a small integer return code (see PPC_LD_* above),
|
||||
with no room for cif->rtype. We hand cif->rtype back in the first
|
||||
parameter-save slot -- which is dead by the time we return -- for the
|
||||
PPC_LD_STRUCT fragment in darwin_closure.S to recover. */
|
||||
unsigned long * pgr0 = pgr;
|
||||
#if defined(POWERPC_DARWIN64)
|
||||
unsigned fpsused = 0;
|
||||
#endif
|
||||
@@ -1275,12 +1284,16 @@ ffi_closure_helper_common (ffi_cif* cif,
|
||||
rvalue = (void *) *pgr;
|
||||
pgr++;
|
||||
}
|
||||
else
|
||||
struct_ret_by_value = 1;
|
||||
#elif defined(DARWIN_PPC)
|
||||
if (cif->rtype->size > 4)
|
||||
{
|
||||
rvalue = (void *) *pgr;
|
||||
pgr++;
|
||||
}
|
||||
else
|
||||
struct_ret_by_value = 1;
|
||||
#else /* assume we return by ref. */
|
||||
rvalue = (void *) *pgr;
|
||||
pgr++;
|
||||
@@ -1480,7 +1493,17 @@ ffi_closure_helper_common (ffi_cif* cif,
|
||||
switch (cif->rtype->type)
|
||||
{
|
||||
case FFI_TYPE_VOID:
|
||||
return PPC_LD_NONE;
|
||||
case FFI_TYPE_STRUCT:
|
||||
/* A by-reference struct return needs nothing further here: the result
|
||||
was written straight to the caller's buffer. A by-value struct
|
||||
return is loaded into registers by darwin_closure.S, which needs
|
||||
cif->rtype -- hand it back in the first parameter-save slot. */
|
||||
if (struct_ret_by_value)
|
||||
{
|
||||
*pgr0 = (unsigned long) cif->rtype;
|
||||
return PPC_LD_STRUCT;
|
||||
}
|
||||
return PPC_LD_NONE;
|
||||
case FFI_TYPE_FLOAT:
|
||||
return PPC_LD_F32;
|
||||
|
||||
+121
-16
@@ -107,8 +107,13 @@ discover_homogeneous_aggregate (ffi_abi abi,
|
||||
unsigned int inner_elnum = 0;
|
||||
unsigned int inner
|
||||
= discover_homogeneous_aggregate (abi, t->elements[0], &inner_elnum);
|
||||
if (inner == FFI_TYPE_FLOAT || inner == FFI_TYPE_DOUBLE)
|
||||
if (inner == FFI_TYPE_FLOAT || inner == FFI_TYPE_DOUBLE
|
||||
|| inner == FFI_TYPE_LONGDOUBLE)
|
||||
{
|
||||
/* A _Complex of an FP base counts as two of that base: an
|
||||
FP-HFA struct member. For IBM-128 long double each half is
|
||||
itself two FPRs (inner_elnum == 2), so a _Complex long double
|
||||
contributes four FPRs. */
|
||||
*elnum = 2 * inner_elnum;
|
||||
return inner;
|
||||
}
|
||||
@@ -257,11 +262,17 @@ homogeneous:
|
||||
goto homogeneous;
|
||||
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
|
||||
case FFI_TYPE_LONGDOUBLE:
|
||||
/* Only the 64-bit long double case is wired up; IBM-128 and
|
||||
IEEE-binary128 _Complex are left as a follow-up. */
|
||||
if ((cif->abi & (FFI_LINUX_LONG_DOUBLE_128
|
||||
| FFI_LINUX_LONG_DOUBLE_IEEE128)) != 0)
|
||||
return FFI_BAD_TYPEDEF;
|
||||
if ((cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
|
||||
{
|
||||
/* IEEE-128 _Complex long double: real in v2, imag in v3.
|
||||
Return via the vector-homogeneous small-struct path. */
|
||||
flags |= FLAG_RETURNS_SMST | FLAG_RETURNS_VEC;
|
||||
break;
|
||||
}
|
||||
/* IBM-128 _Complex long double is returned like a homogeneous
|
||||
aggregate of doubles: real in f1:f2, imag in f3:f4. (For a
|
||||
64-bit long double this reduces to the FFI_TYPE_DOUBLE case,
|
||||
real in f1 and imag in f2.) */
|
||||
flags |= FLAG_RETURNS_SMST;
|
||||
rtype = FFI_TYPE_DOUBLE;
|
||||
goto homogeneous;
|
||||
@@ -393,11 +404,21 @@ homogeneous:
|
||||
break;
|
||||
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
|
||||
case FFI_TYPE_LONGDOUBLE:
|
||||
if ((cif->abi & (FFI_LINUX_LONG_DOUBLE_128
|
||||
| FFI_LINUX_LONG_DOUBLE_IEEE128)) != 0)
|
||||
return FFI_BAD_TYPEDEF;
|
||||
fparg_count += 2;
|
||||
intarg_count += 2;
|
||||
if ((cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
|
||||
{
|
||||
/* Two IEEE-128 halves: each occupies a vector register plus
|
||||
two GPR shadow doublewords, the pair 16-byte aligned. */
|
||||
vecarg_count += 2;
|
||||
intarg_count = (intarg_count + 1) & ~0x1;
|
||||
intarg_count += 4;
|
||||
if (vecarg_count > NUM_VEC_ARG_REGISTERS64)
|
||||
flags |= FLAG_ARG_NEEDS_PSAVE;
|
||||
break;
|
||||
}
|
||||
/* IBM-128: each half is a pair of FPRs, and each FPR half
|
||||
consumes a GPR shadow doubleword -- four of each in total. */
|
||||
fparg_count += 4;
|
||||
intarg_count += 4;
|
||||
if (fparg_count > NUM_FPR_ARG_REGISTERS64)
|
||||
flags |= FLAG_ARG_NEEDS_PSAVE;
|
||||
break;
|
||||
@@ -755,10 +776,51 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
|
||||
case FFI_TYPE_COMPLEX:
|
||||
elt = (*ptr)->elements[0]->type;
|
||||
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
|
||||
/* 64-bit long double is equivalent to double; the IBM-128 and
|
||||
IEEE-binary128 variants were rejected in prep_cif. */
|
||||
if (elt == FFI_TYPE_LONGDOUBLE
|
||||
&& (ecif->cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
|
||||
{
|
||||
/* IEEE-128 _Complex long double: each half goes in its own
|
||||
vector register (or the parameter save area), 16-byte
|
||||
aligned, consuming two GPR shadow doublewords. */
|
||||
float128 *cval = (float128 *) *p_argv.v;
|
||||
unsigned int j;
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
next_arg.p = FFI_ALIGN (next_arg.p, 16);
|
||||
if (next_arg.ul == gpr_end.ul)
|
||||
next_arg.ul = rest.ul;
|
||||
if (vecarg_count < NUM_VEC_ARG_REGISTERS64 && i < nfixedargs)
|
||||
memcpy (vec_base.f128++, cval + j, sizeof (float128));
|
||||
else
|
||||
memcpy (next_arg.f128, cval + j, sizeof (float128));
|
||||
if (++next_arg.f128 == gpr_end.f128)
|
||||
next_arg.f128 = rest.f128;
|
||||
vecarg_count++;
|
||||
}
|
||||
FFI_ASSERT (flags & FLAG_VEC_ARGUMENTS);
|
||||
break;
|
||||
}
|
||||
if (elt == FFI_TYPE_LONGDOUBLE)
|
||||
elt = FFI_TYPE_DOUBLE;
|
||||
{
|
||||
/* IBM-128 _Complex long double: four doubles (real hi/lo,
|
||||
imag hi/lo) into consecutive FPRs, each with a GPR shadow
|
||||
doubleword. */
|
||||
double *cval = (double *) *p_argv.v;
|
||||
unsigned int j;
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
double_tmp = cval[j];
|
||||
if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
|
||||
*fpr_base.d++ = double_tmp;
|
||||
else
|
||||
*next_arg.d = double_tmp;
|
||||
if (++next_arg.ul == gpr_end.ul)
|
||||
next_arg.ul = rest.ul;
|
||||
fparg_count++;
|
||||
}
|
||||
FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (elt == FFI_TYPE_FLOAT)
|
||||
{
|
||||
@@ -1336,8 +1398,45 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
|
||||
unsigned int j;
|
||||
elt = arg_types[i]->elements[0]->type;
|
||||
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
|
||||
if (elt == FFI_TYPE_LONGDOUBLE
|
||||
&& (cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
|
||||
{
|
||||
/* IEEE-128: each half arrives in a vector register (or the
|
||||
16-byte-aligned parameter save area) with two GPR shadow
|
||||
doublewords. */
|
||||
float128 *cval = alloca (2 * sizeof (float128));
|
||||
if (((unsigned long) pst & 0xF) != 0)
|
||||
++pst;
|
||||
for (j = 0; j < 2; j++)
|
||||
{
|
||||
if (pvec < end_pvec && i < nfixedargs)
|
||||
memcpy (&cval[j], pvec++, sizeof (float128));
|
||||
else
|
||||
memcpy (&cval[j], pst, sizeof (float128));
|
||||
pst += 2;
|
||||
}
|
||||
avalue[i] = cval;
|
||||
break;
|
||||
}
|
||||
if (elt == FFI_TYPE_LONGDOUBLE)
|
||||
elt = FFI_TYPE_DOUBLE;
|
||||
{
|
||||
/* IBM-128: four doubles, each in an FPR (or one GPR shadow
|
||||
doubleword) -- real hi/lo then imag hi/lo. */
|
||||
double *cval = alloca (4 * sizeof (double));
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
if (pfr < end_pfr && i < nfixedargs)
|
||||
{
|
||||
cval[j] = pfr->d;
|
||||
pfr++;
|
||||
}
|
||||
else
|
||||
cval[j] = *(double *) pst;
|
||||
pst++;
|
||||
}
|
||||
avalue[i] = cval;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (elt == FFI_TYPE_FLOAT)
|
||||
{
|
||||
@@ -1448,7 +1547,13 @@ ffi_closure_helper_LINUX64 (ffi_cif *cif,
|
||||
int inner = cif->rtype->elements[0]->type;
|
||||
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
|
||||
if (inner == FFI_TYPE_LONGDOUBLE)
|
||||
inner = FFI_TYPE_DOUBLE;
|
||||
{
|
||||
/* IEEE-128 _Complex long double returns in v2:v3; IBM-128 in
|
||||
f1:f2 (real) and f3:f4 (imag), i.e. as a double HFA. */
|
||||
if ((cif->abi & FFI_LINUX_LONG_DOUBLE_IEEE128) != 0)
|
||||
return PPC64_LD_VECTOR_HOMOG;
|
||||
inner = FFI_TYPE_DOUBLE;
|
||||
}
|
||||
#endif
|
||||
if (inner == FFI_TYPE_FLOAT)
|
||||
return PPC64_LD_FLOAT_HOMOG;
|
||||
|
||||
+15
@@ -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:
|
||||
|
||||
Vendored
+109
-1
@@ -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 */
|
||||
Vendored
+5
-3
@@ -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
|
||||
|
||||
Vendored
+11
-1
@@ -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 ())
|
||||
{
|
||||
|
||||
Vendored
+1
-1
@@ -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;
|
||||
|
||||
Vendored
+54
-2
@@ -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
|
||||
|
||||
Vendored
+19
@@ -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
|
||||
|
||||
Vendored
+5
@@ -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
|
||||
|
||||
Vendored
+5
@@ -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
|
||||
|
||||
Vendored
+11
-3
@@ -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
|
||||
Vendored
+11
-3
@@ -301,15 +301,17 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \
|
||||
libffi.bhaible/alignof.h libffi.bhaible/bhaible.exp libffi.bhaible/test-call.c \
|
||||
libffi.bhaible/test-callback.c libffi.bhaible/testcases.c libffi.call/align_mixed.c \
|
||||
libffi.call/align_stdcall.c libffi.call/bpo_38748.c libffi.call/call.exp \
|
||||
libffi.call/closure_thiscall_fastcall_pop.c \
|
||||
libffi.call/err_bad_typedef.c libffi.call/ffitest.h libffi.call/float.c \
|
||||
libffi.call/float1.c libffi.call/float2.c libffi.call/float3.c \
|
||||
libffi.call/float4.c libffi.call/float_va.c libffi.call/i128-1.c \
|
||||
libffi.call/large_struct_by_value.c libffi.call/many.c \
|
||||
libffi.call/many2.c libffi.call/many_double.c libffi.call/many_mixed.c \
|
||||
libffi.call/many2.c libffi.call/many_double.c \
|
||||
libffi.call/many_large_structs.c libffi.call/many_mixed.c \
|
||||
libffi.call/many_small_structs.c \
|
||||
libffi.call/negint.c libffi.call/offsets.c libffi.call/overread.c \
|
||||
libffi.call/plan.c libffi.call/plan_mixed.c libffi.call/plan_spill.c \
|
||||
libffi.call/plan_struct.c libffi.call/plan_var.c \
|
||||
libffi.call/plan_struct.c libffi.call/plan_size.c libffi.call/plan_var.c \
|
||||
libffi.call/pr1172638.c libffi.call/promotion.c libffi.call/pyobjc_tc.c libffi.call/return_dbl.c \
|
||||
libffi.call/return_dbl1.c libffi.call/return_dbl2.c libffi.call/return_fl.c \
|
||||
libffi.call/return_fl1.c libffi.call/return_fl2.c libffi.call/return_fl3.c \
|
||||
@@ -378,7 +380,13 @@ EXTRA_DIST = config/default.exp emscripten/build.sh emscripten/conftest.py \
|
||||
libffi.complex/return_complex_float.c libffi.complex/return_complex_longdouble.c libffi.go/aa-direct.c \
|
||||
libffi.go/closure1.c libffi.go/ffitest.h libffi.go/go.exp \
|
||||
libffi.go/static-chain.h Makefile.am Makefile.in \
|
||||
libffi.threads/ffitest.h libffi.threads/threads.exp libffi.threads/tsan.c
|
||||
libffi.threads/ffitest.h libffi.threads/threads.exp libffi.threads/tsan.c \
|
||||
libffi.vector/vector.exp libffi.vector/ffitest.h libffi.vector/vector.h \
|
||||
libffi.vector/vector_float32x4.c libffi.vector/vector_float32x2.c \
|
||||
libffi.vector/vector_double2.c libffi.vector/vector_int32x4.c \
|
||||
libffi.vector/vector_args_spill.c libffi.vector/vector_vec3.c \
|
||||
libffi.vector/vector_double4.c libffi.vector/vector_hva.c \
|
||||
libffi.vector/cls_vector.c libffi.vector/vector_validate.c
|
||||
|
||||
all: all-am
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/* Area: closure, ffi_prep_closure_loc
|
||||
Purpose: Check i386 THISCALL/FASTCALL closures pop the stack correctly.
|
||||
Limitations: i386 + GNU inline asm only; a no-op elsewhere.
|
||||
PR: none.
|
||||
Originator: i386 closure stack-pop accounting regression.
|
||||
|
||||
THISCALL and FASTCALL are callee-clean: the closure must remove its
|
||||
stack-resident arguments on return (ret $n). When a 64-bit integer or
|
||||
a struct argument is placed on the stack, the closure return path used
|
||||
to compute the pop as cif->bytes - narg_reg*4 with narg_reg force-bumped
|
||||
to 2, discounting register slots that were never used and under-popping
|
||||
the stack. A caller that relies on callee cleanup is then left with the
|
||||
argument bytes where its return address should be.
|
||||
|
||||
This test invokes the generated closure through a minimal callee-clean
|
||||
call site and checks that ESP is balanced across the call (delta 0).
|
||||
Without the fix the delta is 8 (FASTCALL uint64) or 4 (THISCALL). */
|
||||
|
||||
/* { dg-do run } */
|
||||
#include "ffitest.h"
|
||||
|
||||
#if defined(__i386__) && defined(__GNUC__) && !defined(__APPLE__)
|
||||
|
||||
static uint64_t received;
|
||||
static int ran;
|
||||
|
||||
static void
|
||||
cb (ffi_cif *cif, void *resp, void **args, void *userdata)
|
||||
{
|
||||
(void) cif; (void) resp; (void) userdata;
|
||||
received = *(uint64_t *) args[cif->nargs - 1];
|
||||
ran++;
|
||||
}
|
||||
|
||||
/* Push an 8-byte stack argument, load ECX (the thiscall "this" register,
|
||||
ignored by the fastcall callee), call the closure, and return how many
|
||||
bytes the callee under-popped (0 == it popped exactly what was pushed).
|
||||
|
||||
Every operand is read into a register up front, while ESP is still at
|
||||
its incoming value, so nothing is referenced through an ESP-relative
|
||||
memory operand after we start moving ESP (which would otherwise read a
|
||||
stale slot, on clang at -O2 in particular). The stack is then 16-byte
|
||||
aligned at the call as the i386 psABI requires, so the -O2-built closure
|
||||
body may use aligned SSE without faulting; the alignment cancels out of
|
||||
the delta. ESP is restored to its exact incoming value before the delta
|
||||
is stored, so a wrong pop cannot corrupt our frame. Not using EBX keeps
|
||||
this compatible with -fPIC; the delta is returned via memory so no free
|
||||
register is needed for it. */
|
||||
static int
|
||||
esp_delta (void *code, uint64_t stackarg, unsigned ecxv)
|
||||
{
|
||||
unsigned delta;
|
||||
unsigned lo = (unsigned) stackarg;
|
||||
unsigned hi = (unsigned) (stackarg >> 32);
|
||||
__asm__ volatile (
|
||||
"movl %[lo], %%eax\n\t" /* stash all operands in registers */
|
||||
"movl %[hi], %%edx\n\t" /* before ESP moves */
|
||||
"movl %[code], %%edi\n\t"
|
||||
"movl %[ecxv], %%ecx\n\t" /* thiscall 'this' */
|
||||
"movl %%esp, %%esi\n\t" /* remember the real esp */
|
||||
"andl $-16, %%esp\n\t" /* 16-byte align, then bias by the */
|
||||
"subl $8, %%esp\n\t" /* 8 arg bytes so 'call' is 0 mod 16 */
|
||||
"pushl %%edx\n\t" /* high dword */
|
||||
"pushl %%eax\n\t" /* low dword */
|
||||
"calll *%%edi\n\t"
|
||||
"movl %%esi, %%eax\n\t" /* recompute esp just before the */
|
||||
"andl $-16, %%eax\n\t" /* pushes... */
|
||||
"subl $8, %%eax\n\t"
|
||||
"subl %%esp, %%eax\n\t" /* eax = under-popped byte count */
|
||||
"movl %%esi, %%esp\n\t" /* restore before touching memory */
|
||||
"movl %%eax, %[delta]\n\t"
|
||||
: [delta] "=m" (delta)
|
||||
: [lo] "m" (lo), [hi] "m" (hi), [code] "m" (code), [ecxv] "m" (ecxv)
|
||||
: "memory", "cc", "eax", "ecx", "edx", "esi", "edi");
|
||||
return (int) delta;
|
||||
}
|
||||
|
||||
static int
|
||||
check_abi (ffi_abi abi, unsigned nargs, ffi_type **atypes, unsigned ecx)
|
||||
{
|
||||
ffi_cif cif;
|
||||
ffi_closure *closure;
|
||||
void *code;
|
||||
int delta;
|
||||
|
||||
closure = ffi_closure_alloc (sizeof (ffi_closure), &code);
|
||||
CHECK (closure != NULL);
|
||||
CHECK (ffi_prep_cif (&cif, abi, nargs, &ffi_type_void, atypes) == FFI_OK);
|
||||
CHECK (ffi_prep_closure_loc (closure, &cif, cb, NULL, code) == FFI_OK);
|
||||
|
||||
ran = 0;
|
||||
received = 0;
|
||||
delta = esp_delta (code, 0x1122334455667788ULL, ecx);
|
||||
|
||||
CHECK (ran == 1);
|
||||
CHECK (received == 0x1122334455667788ULL);
|
||||
ffi_closure_free (closure);
|
||||
return delta;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
ffi_type *fastcall_args[1] = { &ffi_type_uint64 };
|
||||
ffi_type *thiscall_args[2] = { &ffi_type_pointer, &ffi_type_uint64 };
|
||||
int d;
|
||||
|
||||
/* FASTCALL void cb(uint64_t): the uint64 is stack-resident; pop must be 8. */
|
||||
d = check_abi (FFI_FASTCALL, 1, fastcall_args, 0);
|
||||
printf ("FASTCALL uint64 esp delta: %d\n", d);
|
||||
CHECK (d == 0);
|
||||
|
||||
/* THISCALL void cb(void*, uint64_t): 'this' in ECX, uint64 on the stack;
|
||||
pop must be 8 (not 4). */
|
||||
d = check_abi (FFI_THISCALL, 2, thiscall_args, 0xdeadbeef);
|
||||
printf ("THISCALL this+uint64 esp delta: %d\n", d);
|
||||
CHECK (d == 0);
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
/* Not an i386 GNU target: nothing to check here. */
|
||||
exit (0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
/* Area: ffi_call
|
||||
Purpose: Pass many large by-value structs on AArch64.
|
||||
Limitations: none.
|
||||
PR: none.
|
||||
Originator: AArch64 large-struct stack accounting regression.
|
||||
|
||||
Regression test: on AArch64, composites larger than 16 bytes are passed
|
||||
by invisible reference. ffi_call copies each payload into the argument
|
||||
slab (growing down from the top) and, once X0-X7 are exhausted, also
|
||||
spills the by-ref pointer into the same slab (the NSAA, growing up).
|
||||
The generic prep_cif budget in cif->bytes only charged the payload copy,
|
||||
not the 8-byte pointer slot, so with enough large structs the two regions
|
||||
collided and a later payload copy overwrote an already-spilled pointer,
|
||||
leaving the callee with a corrupt pointer for a by-value argument.
|
||||
Passing sixteen 32-byte (non-HFA) structs by value -- eight more than the
|
||||
argument registers -- must marshal every argument intact. */
|
||||
|
||||
/* { dg-do run } */
|
||||
#include "ffitest.h"
|
||||
|
||||
#define NARGS 16
|
||||
#define SSIZE 32
|
||||
|
||||
typedef struct { unsigned char b[SSIZE]; } big_struct;
|
||||
|
||||
/* Sum every byte of every argument. A corrupted by-ref pointer makes the
|
||||
callee read the wrong memory, so the sum no longer matches. */
|
||||
static int ABI_ATTR
|
||||
sum_bytes (big_struct s0, big_struct s1, big_struct s2, big_struct s3,
|
||||
big_struct s4, big_struct s5, big_struct s6, big_struct s7,
|
||||
big_struct s8, big_struct s9, big_struct s10, big_struct s11,
|
||||
big_struct s12, big_struct s13, big_struct s14, big_struct s15)
|
||||
{
|
||||
big_struct *all[NARGS];
|
||||
int i, j, sum = 0;
|
||||
|
||||
all[0] = &s0; all[1] = &s1; all[2] = &s2; all[3] = &s3;
|
||||
all[4] = &s4; all[5] = &s5; all[6] = &s6; all[7] = &s7;
|
||||
all[8] = &s8; all[9] = &s9; all[10] = &s10; all[11] = &s11;
|
||||
all[12] = &s12; all[13] = &s13; all[14] = &s14; all[15] = &s15;
|
||||
|
||||
for (i = 0; i < NARGS; i++)
|
||||
for (j = 0; j < SSIZE; j++)
|
||||
sum += all[i]->b[j];
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
ffi_cif cif;
|
||||
ffi_type *args[NARGS];
|
||||
void *values[NARGS];
|
||||
ffi_type bs_type;
|
||||
ffi_type *bs_elements[SSIZE + 1];
|
||||
big_struct in[NARGS];
|
||||
ffi_arg result = 0;
|
||||
int i, j, expected = 0;
|
||||
|
||||
bs_type.size = 0;
|
||||
bs_type.alignment = 0;
|
||||
bs_type.type = FFI_TYPE_STRUCT;
|
||||
for (i = 0; i < SSIZE; i++)
|
||||
bs_elements[i] = &ffi_type_uchar;
|
||||
bs_elements[SSIZE] = NULL;
|
||||
bs_type.elements = bs_elements;
|
||||
|
||||
/* Fill struct i with the distinct byte value (i + 1) so any pointer
|
||||
mix-up between arguments changes the total. */
|
||||
for (i = 0; i < NARGS; i++)
|
||||
{
|
||||
for (j = 0; j < SSIZE; j++)
|
||||
{
|
||||
in[i].b[j] = (unsigned char) (i + 1);
|
||||
expected += (i + 1);
|
||||
}
|
||||
args[i] = &bs_type;
|
||||
values[i] = &in[i];
|
||||
}
|
||||
|
||||
CHECK(ffi_prep_cif(&cif, ABI_NUM, NARGS, &ffi_type_sint, args) == FFI_OK);
|
||||
|
||||
ffi_call(&cif, FFI_FN(sum_bytes), &result, values);
|
||||
|
||||
CHECK((int) result == expected);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
+77
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include "../libffi.call/ffitest.h"
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
# Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; see the file COPYING3. If not see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
|
||||
dg-init
|
||||
libffi-init
|
||||
|
||||
global srcdir subdir
|
||||
|
||||
# The tests are written with the GCC/Clang vector extension
|
||||
# (__attribute__ ((vector_size (N)))). A target port can support
|
||||
# FFI_TYPE_VECTOR at the ABI level while the compiler under test (e.g.
|
||||
# MSVC) cannot compile that syntax, so probe the compiler with an actual
|
||||
# compilation, not just a preprocessor check.
|
||||
proc libffi_vector_syntax_test { } {
|
||||
set src "vecprobe[pid].c"
|
||||
set obj "vecprobe[pid].o"
|
||||
|
||||
set f [open $src "w"]
|
||||
puts $f "typedef float probe_v4 __attribute__ ((vector_size (16)));"
|
||||
puts $f "probe_v4 probe_var;"
|
||||
puts $f "int main (void) { return 0; }"
|
||||
close $f
|
||||
|
||||
set lines [libffi_target_compile $src $obj object ""]
|
||||
file delete $src
|
||||
file delete $obj
|
||||
|
||||
return [string match "" $lines]
|
||||
}
|
||||
|
||||
set tlist [lsort [glob -nocomplain -- $srcdir/$subdir/*.{c,cc}]]
|
||||
|
||||
if { [libffi_feature_test "#ifdef FFI_TARGET_HAS_VECTOR_TYPE"]
|
||||
&& [libffi_vector_syntax_test] } {
|
||||
run-many-tests $tlist ""
|
||||
} else {
|
||||
foreach test $tlist {
|
||||
unsupported "$test"
|
||||
}
|
||||
}
|
||||
|
||||
dg-finish
|
||||
|
||||
# Local Variables:
|
||||
# tcl-indent-level:4
|
||||
# End:
|
||||
+32
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
Reference in new issue
Block a user