commit 132fb82ff4b43c9b8eaeeb533f38c95ae925131c
Author: Anthony Green <green@moxielogic.com>
Date:   Wed Jul 8 07:40:34 2026 -0400

    Update libtool version to 12:0:4 for 3.7.0
    
    3.7.0 adds three new public interfaces (ffi_call_plan_alloc,
    ffi_call_plan_invoke, ffi_call_plan_free) additively, with no
    existing interface removed or changed.  Per the libtool rules:
    increment current, reset revision, increment age (11:1:3 -> 12:0:4).
    This keeps the change ABI backward-compatible; the soname stays
    libffi.so.8 (current - age = 8).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 0ad8fd46c0a32cbbf18260c72cc60679da66c89f
Author: Anthony Green <green@moxielogic.com>
Date:   Wed Jul 8 06:43:03 2026 -0400

    Prepare for 3.7.0

commit c9275302baf0d109ec474972d4679a6afe8fe64d
Author: Anthony Green <green@moxielogic.com>
Date:   Wed Jul 8 06:29:29 2026 -0400

    Bump version to 3.7.0
    
    Set AC_INIT to 3.7.0 and update the manual's edition/version/date
    stamps to match.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 4f7ef24565eb60e8a093bfeba0bb77d5c576c311
Author: Anthony Green <green@moxielogic.com>
Date:   Wed Jul 8 06:28:30 2026 -0400

    Update copyright year
    
    Bump the LICENSE year range to 2026.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 5ab7b328c3adde14105b1f36ae30adc9cdefae0a
Author: Anthony Green <green@moxielogic.com>
Date:   Wed Jul 8 06:24:26 2026 -0400

    Add Anthony Green copyright line for 2026 authorship
    
    Attribute this year's work in the wasm and PA backends and the darwin
    PowerPC closure code:
      - wasm/ffi.c: widen narrow/unboxed returns to ffi_arg; NULL rvalue.
      - pa/ffi.c: bump year (5-8 byte struct slot-sizing fix + avalue copy).
      - powerpc/ffi_darwin.c: PPC_LD_* jump-table indexes for the aix/darwin
        closure build fix.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 5e334c6aa70b937b5c0d30ba10a373e34d7526b6
Author: Anthony Green <green@moxielogic.com>
Date:   Wed Jul 8 06:17:58 2026 -0400

    x86-64: add copyright line for the 2026 unix64.S plan trampolines
    
    The fast-path plan trampoline (ffi_plan_fast_call) and the pure-GP64
    direct thunks (ffi_plan_gp0..6) were added to unix64.S this year, so
    add an Anthony Green copyright line alongside the original authors'.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 70106b98d0a333a13b509b4bdc554018785be50c
Author: Anthony Green <green@moxielogic.com>
Date:   Wed Jul 8 06:15:53 2026 -0400

    Update copyright year to 2026
    
    Add 2026 to the copyright line on the source files, headers and the
    manual touched during this year's development.  Following existing
    practice, only the maintainer's own copyright line is bumped (the
    Anthony Green line, plus the Red Hat line in types.c); the years on
    third-party-held backends are left as their original authors set them.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 621874d6726007a6470b9c0c90b5a27c3c3ed932
Author: Anthony Green <green@moxielogic.com>
Date:   Wed Jul 8 05:43:18 2026 -0400

    configure: derive FFI_VERSION_STRING/NUMBER from AC_INIT
    
    The 3.6.0 release bumped AC_INIT but left FFI_VERSION_STRING and
    FFI_VERSION_NUMBER at 3.5.2/30502, so the released headers report the
    wrong version.  Compute both from AC_PACKAGE_VERSION instead of
    duplicating the version by hand: the string is the package version
    verbatim, and the number encodes X.Y.Z as X*10000 + Y*100 + Z with any
    non-numeric suffix (e.g. -rc0) ignored.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit b8c60b06597cc77cbe66bd208083acc20251fae0
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 12:57:06 2026 -0400

    README: note the ffi_call fixes in the 3.7.0 history
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit ed5da041796cd6e38d2f30b948eadf63cf96139a
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 12:30:56 2026 -0400

    wasm: don't widen unboxed small-struct returns
    
    The previous commit widened narrow integral returns to ffi_arg, but
    unbox_small_structs had already collapsed single-member struct returns
    into those same scalar type ids, and a struct return is stored at its
    natural size -- the caller's buffer is exactly the struct.  This broke
    cls_1_1byte and the bhaible small-struct return tests.
    
    Remember whether the original return type was a struct and widen only
    genuine scalar returns.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 59a9e68e59205da9c4db854dd1f1bce4e8b58dad
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 12:02:20 2026 -0400

    wasm: widen narrow integral returns to ffi_arg
    
    The return store wrote only the natural width for integer returns
    narrower than a register (one byte for U8/S8, two for U16/S16, four on
    wasm64 for 32-bit types), leaving the rest of the caller's ffi_arg
    buffer as stack garbage.  The documented API widens such returns to a
    full ffi_arg, and every native port stores the full register width.
    This is what plan_mixed.c caught: two identical calls disagreed in the
    garbage bytes of their ffi_arg results.
    
    Mask or sign-extend the JS result explicitly and store a full ffi_arg
    (32 bits on wasm32, 64 on wasm64).
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 6954bd1715b8a5895adfad7c2196d4eb41094dc7
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 11:30:55 2026 -0400

    testsuite: use ffi_arg for the integral return in plan.c
    
    The struct-argument block stored an ffi_type_slong return into a plain
    long.  Integral returns narrower than a register are widened to
    ffi_arg, so on LLP64 targets libffi writes 8 bytes into the 4-byte
    variable and clobbers its neighbor; this is what broke plan.c on MSVC
    Win64 while mingw survived by stack-layout luck.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 822d2e555fd97271c7c0ba8b6325cf4b541e3e9c
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 11:30:55 2026 -0400

    Allow a NULL rvalue in ffi_call; handle it in the wasm port
    
    Most ports already quietly supported calling with rvalue == NULL by
    substituting scratch space for in-memory returns, and the new plan
    tests rely on it, but the manual never promised it and the wasm port
    passed NULL straight to the callee as the return-by-argument pointer,
    writing the result through address zero.
    
    Document NULL rvalue as supported for any return type, and in the wasm
    port allocate stack scratch for return-by-argument callees and skip the
    store-back for scalar returns when rvalue is NULL.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 110156e1cceab76b4440ced9d95b5abaa84f30f7
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 11:30:55 2026 -0400

    ffi_call: never modify the caller's avalue array
    
    Eight ports made large struct arguments pass-by-value by alloca'ing a
    copy inside ffi_call and writing the copy's address back into the
    caller's avalue[].  Those pointers dangle as soon as ffi_call returns,
    so any caller that legally reuses its avalue array for a second call
    reads dead stack.  The new ffi_call_plan equivalence tests do exactly
    that and exposed it as plan_struct.c failures on hppa, MSVC Win64 and
    macOS x86_64 under Rosetta; other targets passed only because the dead
    copy usually survives between back-to-back calls.
    
    Clone the pointer array once, on the first argument that needs a copy,
    and redirect the clone instead.  Document that ffi_call leaves the
    caller's vector untouched.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit e3dae4657710a9ddd4eb4bdbefbcc73722aad9fe
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 09:18:31 2026 -0400

    testsuite: add ffi_call_plan equivalence tests; fix EXTRA_DIST gaps
    
    Add plan.c, plan_mixed.c, plan_spill.c, plan_struct.c and plan_var.c.
    Each builds a reusable call plan (ffi_call_plan_alloc/invoke/free) and
    checks it against ffi_call across the x86-64 argument-placement matrix:
    the pure-GP64 direct thunks, the mixed GP+SSE fast path, stack-spilled
    arguments, struct returns (in-memory including a NULL rvalue, and the
    register pair), a struct-by-value argument that falls back to ffi_call,
    and variadic calls.  On targets without the accelerated backend the
    generic plan simply wraps ffi_call, so the tests still pass there.
    
    While adding these to EXTRA_DIST, register four existing tests that were
    never listed (i128-1.c, large_struct_by_value.c, many_small_structs.c
    and complex_i128.c).  They ran via the .exp globs but were missing from
    distribution tarballs.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit a40ab3a7e85f8c9b193b8a442297bb20de2b4293
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 08:19:33 2026 -0400

    README: update 3.7.0 history entry
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 50baa0458db6b2d617e0d661292bbf12354b22ce
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 08:10:29 2026 -0400

    x86_64: size closure argument buffer to match copy loop
    
    ffi_closure_unix64_inner allocated a fixed 16 bytes when gathering a
    register-passed argument's eightbytes, but the copy loop writes n * 8
    bytes and examine_argument can classify up to MAX_CLASSES (4)
    eightbytes for vector-classified types.  Allocate n * 8 bytes so the
    buffer always matches the loop bound.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit ff30d37615f82ef7bf1130b34528e01e1479b63d
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 08:10:29 2026 -0400

    tramp.c: bound sscanf path field when parsing /proc/self/maps
    
    The %s conversion into file[PATH_MAX] was unbounded while the input
    line buffer is PATH_MAX+100 bytes, so a mapping with a very long
    pathname could overflow the destination.  Give the conversion an
    explicit PATH_MAX field width and size the buffer PATH_MAX+1 to
    accommodate the terminating NUL.
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit ed1844e6b50d0498d2b88d1b3ef31cc39b6cf117
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 08:10:29 2026 -0400

    .gitignore: ignore local build and scan artifact directories
    
    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

commit 854df63dbbda802985facbfab7a0e7c4169aefd1
Merge: 2a595a18 731ec08f
Author: Anthony Green <green@moxielogic.com>
Date:   Tue Jul 7 08:06:25 2026 -0400

    Merge pull request #991 from rorth/freebsd-libffi-note-gnu-stack
    
    Include .note.GNU-stack on FreeBSD/x86

commit 731ec08f3c3bbec883d823a71f1b0f86731cf312
Author: Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
Date:   Tue Jul 7 11:14:09 2026 +0200

    Fix typo.

commit 19cc1424e924767c7ca43f9e4d4c27fbae27d1d9
Author: Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
Date:   Tue Jul 7 10:27:55 2026 +0200

    Include .note.GNU-stack on FreeBSD/x86
    
    When building libffi on FreeBSD/x86 with --disable-shared as is done inside
    the GCC tree, all tests FAIL when using GNU ld:
    
    gld-2.46: warning: win64.o: missing .note.GNU-stack section implies executable stack
    gld-2.46: NOTE: This behaviour is deprecated and will be removed in a future version of the linker
    
    The emission of .note.GNU-stack in src/x86/*.S is currently guarded by
    __ELF__ && __linux__, but FreeBSD/x86 uses that NOTE section, too.  This
    doesn't happen with libffi.so which lacks that NOTE.
    
    Therefore this patch extends the emission accordingly.
    
    Tested on amd64-pc-freebsd15.1 with and without --disable-shared.

commit 2a595a183be146974bdf55716969e7a3e860f112
Merge: c93f9428 f7b36640
Author: Anthony Green <green@moxielogic.com>
Date:   Thu Jul 2 20:03:30 2026 -0400

    Merge pull request #989 from rvandermeulen/dlmalloc-msvc-lock-cast
    
    dlmalloc: cast Win32 spin-lock word to LONG volatile* in CAS_LOCK/CLEAR_LOCK

commit f7b36640544a59813ee372ed1a763affe7a67562
Author: Ryan VanderMeulen <rvandermeulen@mozilla.com>
Date:   Thu Jul 2 19:32:00 2026 -0400

    dlmalloc: cast Win32 spin-lock word to LONG volatile* in CAS_LOCK/CLEAR_LOCK
    
    MLOCK_T is int, but on the Win32 MSC path CAS_LOCK/CLEAR_LOCK map to
    _InterlockedExchange(LONG volatile *, LONG), so the int* lock word is
    passed to a LONG volatile * parameter. clang 22 promotes
    -Wincompatible-pointer-types to an error by default, which breaks the
    clang-cl build (32- and 64-bit) with:
    
      error: incompatible pointer types passing 'int *' to parameter of type
      'volatile long *' [-Wincompatible-pointer-types]
    
    Cast to (LONG volatile *), matching what the ffi_spin_peek macro just
    below already does in its _MSC_VER branch. LONG and int are both 32-bit
    on Windows, so this is a type-correctness fix with no behavioral change.

commit c93f9428d17cde4eb35517b58feeae6fb43aba5b
Author: Anthony Green <green@moxielogic.com>
Date:   Mon Jun 22 17:33:33 2026 -0400

    testsuite: link threads tests against libpthread on NetBSD
    
    The libffi.threads/tsan.c test calls pthread_create but the harness
    only appended -lpthread for OpenBSD, FreeBSD, and some Linux targets.
    On NetBSD this caused an undefined reference to pthread_create.
    
    Fixes #988
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 9ca53a19833dabaf80b73763638ab988bdbc42cc
Author: Anthony Green <green@moxielogic.com>
Date:   Sun Jun 21 18:42:37 2026 -0400

    powerpc: fix aix/darwin closure build errors
    
    unbuildable: the PPC_LD_* jump-table indexes (defined in ffi_powerpc.h,
    which this file cannot include) were missing, and the closure helper
    functions still declared an ffi_type* return type inconsistent with the
    PPC_LD_* constants they now return.
    
    Define the PPC_LD_* constants locally and change the closure helper
    return types to int.
    
    Fixes #987
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 807a93115f927229f6e7cf5675384e8dc2bf76d5
Author: Anthony Green <green@moxielogic.com>
Date:   Sun Jun 21 18:31:12 2026 -0400

    doc: mark the 3.7.0 release date as TBD
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit c876d4663183c40edbe5218603a26290900e7794
Author: Anthony Green <green@moxielogic.com>
Date:   Sun Jun 21 18:28:12 2026 -0400

    doc: add 3.7.0 history entry
    
    Record the next release (3.7.0, a minor bump for the new reusable call-plan API
    on master) and the two PA-RISC fixes merged in #986.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit c3e054afebb2d9121ff4d12a56a803eecc31cc41
Merge: 3cc6beb7 1f04d458
Author: Anthony Green <green@moxielogic.com>
Date:   Sun Jun 21 18:13:59 2026 -0400

    Merge pull request #986 from libffi/ci-enable-parisc-regression
    
    Add PA32/ARCompact struct-by-value regression tests; enable hppa CI

commit 1f04d458126bd79c58196073477515c88de8163d
Author: Anthony Green <green@moxielogic.com>
Date:   Sun Jun 21 17:37:26 2026 -0400

    pa: fix stack sizing for 5-8 byte structs passed by value (FFI_PA32)
    
    ffi_size_stack_pa32 reserved a single stack slot for every struct argument,
    but ffi_prep_args_pa32 passes a 5-8 byte struct inline in two even-aligned
    slots (like a 64-bit value).  With enough such arguments the marshaller's slot
    count outgrew cif->bytes and it wrote past the allocated frame -- below sp and
    over ffi_call_pa32's saved return pointer -- corrupting the call.
    
    Make the sizing mirror the marshalling exactly: 1-4 byte structs take one slot,
    5-8 byte structs take "2 + (z & 1)" slots (z stays offset from the marshaller's
    slot by the odd FIRST_ARG_SLOT, so the parity matches), and larger structs are
    passed by pointer in one slot.  Split the PA_HPUX long double case out since it
    is passed by pointer.
    
    Fixes the failure exercised by testsuite/libffi.call/many_small_structs.c.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit cf45dfc368ea7373fdd946603845782dbcc1549a
Author: Anthony Green <green@moxielogic.com>
Date:   Sun Jun 21 16:57:56 2026 -0400

    testsuite: make many_small_structs actually trigger the PA32 overflow
    
    With only 16 structs the PA-RISC slot under-count overflows ~32 bytes below
    sp, landing in the 64-byte register save area where it clobbers nothing live,
    so the call still returned correctly and the test passed under qemu.
    
    Raise the count to 40 so the marshaller's writes sweep past the save area and
    over ffi_call_pa32's saved return pointer (sp-20), corrupting the return path.
    On a correct backend the arguments still marshal within the allocated frame and
    the call returns the expected sums; the test now fails only on an unfixed
    FFI_PA32.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit d55de2637abd9518d550d18a3e1d89a37bd32f47
Author: Anthony Green <green@moxielogic.com>
Date:   Sun Jun 21 16:39:30 2026 -0400

    pa: fix build broken by the int128 generic types
    
    FFI_TYPE_UINT128/FFI_TYPE_SINT128 were added after FFI_TYPE_COMPLEX, moving
    FFI_TYPE_LAST to FFI_TYPE_SINT128.  The PA-RISC tripwire still pinned
    FFI_PA_TYPE_LAST to FFI_TYPE_COMPLEX, so "#error You likely have broken jump
    tables" fired and PA-RISC stopped building once int128 support landed.
    
    ffi_prep_cif_machdep maps every return type it does not handle explicitly --
    including FFI_TYPE_COMPLEX and the 128-bit integer types -- to FFI_TYPE_INT via
    its default case, so cif->flags never exceeds FFI_TYPE_COMPLEX and the linux.S /
    hpux32.S return-value jump tables remain sufficient.  Bump FFI_PA_TYPE_LAST to
    FFI_TYPE_SINT128 to clear the tripwire, with a comment recording why no table
    entries were needed.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit d7177f2db574c962b4366b1948b7f7e1aaf896c2
Author: Anthony Green <green@moxielogic.com>
Date:   Sun Jun 21 16:26:53 2026 -0400

    ci: run the testsuite on PA-RISC 32-bit (hppa) under qemu-user
    
    Add an hppa-linux-gnu entry to the build-cross-qemu matrix so the testsuite is
    cross-compiled with the Debian gcc-hppa-linux-gnu toolchain and run under
    qemu-user (binfmt).  hppa-linux uses FFI_PA32, so this exercises the
    ffi_prep_args_pa32 / ffi_size_stack_pa32 marshalling path and will surface the
    many_small_structs.c regression until the backend is fixed.
    
    Also drop the stale g++-5-hppa-linux-gnu pin from install.sh; the toolchain and
    qemu-user-static now come from the CROSS_QEMU block.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit a384cba94f3935ac54a38f7c6489b6fbe0488c8c
Author: Anthony Green <green@moxielogic.com>
Date:   Sun Jun 21 16:26:21 2026 -0400

    testsuite: add struct-by-value regression tests for PA32 and ARCompact
    
    Two ffi_call tests covering argument-marshalling defects found by a security
    review of the backends:
    
    * many_small_structs.c -- passes sixteen 8-byte structs by value.  On
      PA-RISC 32-bit (FFI_PA32), ffi_size_stack_pa32 reserves one stack slot per
      struct while ffi_prep_args_pa32 consumes two slots for a 5-8 byte struct
      passed inline, so enough such args write past the asm-allocated frame.
    
    * large_struct_by_value.c -- passes one 64-byte struct by value.  On
      ARCompact (FFI_ARCOMPACT), ffi_call_int sizes the stack arg area at two
      words per argument, but a by-value struct is marshalled one word at a time
      and words beyond the eight argument registers are written to that
      under-sized area without bound.
    
    Both are written as ordinary correctness tests (valid CIFs, asserts on the
    expected sums); they pass on unaffected targets and fail on PA32/ARCompact
    until the backends are fixed.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 3cc6beb7d404e12d3458461d76557b79795816ff
Author: Anthony Green <anthony@atgreen.org>
Date:   Sun Jun 21 09:47:10 2026 -0400

    x86-64: replace the transparent plan cache with an explicit ffi_call_plan API
    
    The transparent plan cache sped up ffi_call and closures by caching a
    precomputed argument-placement plan per signature, but it charged every
    call a cache lookup and content fingerprint, and it slightly regressed
    signatures with no fast path (struct-by-value) that paid the key
    computation only to find there was nothing to do.
    
    Replace it with an opt-in API.  ffi_call_plan_alloc builds a plan from a
    prepared cif, ffi_call_plan_invoke applies it with no per-call lookup,
    and ffi_call_plan_free releases it.  The plan is opaque and caller-owned,
    immutable once built (so it may be shared across threads), and falls back
    to ffi_call for signatures with no fast path.  ffi_call and the closure
    path return to their original behaviour, so callers that don't adopt the
    API pay nothing.
    
    The x86-64 backend provides an accelerated plan; every other target gets
    a portable fallback in prep_cif.c that wraps ffi_call, so the symbols are
    defined on all platforms.  The per-thread cache, content fingerprint,
    thread-exit cleanup and closure demarshal layout are removed.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 0c8f175eb650bb204b679996696b3e7706505d8f
Author: Anthony Green <anthony@atgreen.org>
Date:   Sun Jun 21 08:18:14 2026 -0400

    x86-64: trim the plan-cache fingerprint to the arg_types contents
    
    abi, nargs and rtype are stored in the slot and compared exactly on every
    hit, so folding them into the fingerprint is redundant work on the hot
    path.  Hashing only the arg_types element pointers keeps the same collision
    behaviour and the same protection against a reused arg_types address, while
    shortening the per-call hash and speeding up the all-pointer thunk path.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 7b8c3283351298c55d09f580414ab937f358156c
Merge: d4995b5b f1e21329
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 19:48:22 2026 -0400

    Merge pull request #985 from libffi/plan-cache-fingerprint
    
    x86-64: content-based plan-cache key and thread-exit cleanup

commit f1e2132940880128e26b885ee956e76fe3cad2d8
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 19:46:50 2026 -0400

    x86-64: content-based plan-cache key and thread-exit cleanup
    
    Follow-up to the plan cache (PR #984), addressing two issues.
    
    Content key: the cache keyed slots on the abi/nargs/rtype plus the arg_types
    *array pointer*.  If a cif's arg_types array was freed and the address later
    reused for a different signature with the same abi/nargs/rtype, the keys
    collided and a stale plan could be returned.  Key on a 64-bit fingerprint that
    folds in the element ffi_type pointers, so distinct argument types always get
    distinct keys.  Lookup is now O(nargs) cheap pointer reads -- still far less
    than re-classifying the call.
    
    Thread-exit cleanup: the per-thread cache leaked its malloc'd plans when a
    thread exited.  Register a pthread key whose destructor frees the thread's
    plans.  pthread is referenced via weak symbols, so a program that never links
    pthread simply skips the registration (the cache is already bounded per thread).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit d4995b5bff6e5ecefb46eda218c40717f895baaf
Merge: 918ca8ed de3826a8
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 18:43:14 2026 -0400

    Merge pull request #984 from libffi/fast-libffi-plan
    
    x86-64: transparent plan cache for ffi_call and closures

commit de3826a8063fde113ee8d1e455d459038d5814cc
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 18:14:08 2026 -0400

    x86-64: limit the plan cache to the SysV ABI
    
    The Win64/EFI64 plan path failed an MSVC execution test (libffi.call/float3)
    that can't be reproduced or validated without a Windows toolchain.  Revert
    ffiw64.c to the proven upstream implementation and build plans only for
    FFI_UNIX64, leaving Win64/EFI64/GNUW64 on the existing path.  The SysV fast
    path -- the bulk of the speedup -- is unaffected.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit fdbf8deaf297b308731385f91868ea8b5e7dbd21
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 17:59:54 2026 -0400

    x86-64: fix Windows build of the plan cache
    
    Native Windows targets build only ffiw64.c + win64.S for the x86 backend;
    ffi64.c (which instantiates the plan-cache storage and ffi_build_plan_arch)
    is not compiled, so ffiw64.c's ffi_plan_get left ffi_plan_miss / ffi_plan_cache
    unresolved at link time.  Instantiate the cache in ffiw64.c when X86_WIN64 is
    defined (i.e. when ffi64.c is absent); on Unix x86-64 ffi64.c still does it and
    X86_WIN64 is undefined, so there is no duplicate definition.
    
    Also use a portable thread-local qualifier (FFI_TLS: __declspec(thread) on
    MSVC, __thread elsewhere).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 872365e143c521b4a84f712aa60f4e638a8b8053
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 17:25:17 2026 -0400

    x86-64: reword plan-cache comments to be self-contained
    
    Drop the in-development "Mechanism A" label and the stale references to the
    earlier opt-in arena/slab storage, so the comments describe the cache as it
    now stands.  No functional change.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit c4c9750c37868f26d1ce914592ca826e4ffc0b35
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 13:10:18 2026 -0400

    x86-64: transparent plan cache for ffi_call and closures
    
    Precompute each signature's argument placement once and cache it, keyed on
    (abi, nargs, rtype, arg_types) in a per-thread direct-mapped cache, so ffi_call
    and closures skip the per-call ABI classification.  No API or ABI change --
    every caller benefits transparently on a relink.
    
      - Cache: src/plan-cache.h (inline O(1) probe) + src/plan-cache-impl.h
        (per-thread storage, evict-and-free, FFI_PLAN_NONE for non-plan-able sigs).
      - SysV (ffi64.c, unix64.S): pure-GP64 direct thunks (avalue -> arg regs), a
        lean trampoline for register-only calls, and a plan executor reusing
        ffi_call_unix64; closures use a precomputed demarshal layout.  CFI on the
        thunks/trampoline so exceptions unwind through them.
      - Win64/EFI64 (ffiw64.c): plan executor + demarshal.
      - W^X preserved: no runtime codegen; thunks shipped in .text.
    
    Scalar/pointer/int128/float/double args are accelerated; struct/complex/x87
    args fall back to the existing path.  Pointer-heavy calls ~5x faster.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 918ca8edddbf542d9034d8e58931edfea036ecca
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 12:34:46 2026 -0400

    ci: actually zip the MSVC binaries before the release upload
    
    The MSVC 'Create binary distribution' step builds a *directory*
    (libffi-<ver>-x86-<width>bit-msvc-binaries/), but 'Upload to GitHub Release'
    pointed softprops/action-gh-release at a .zip file that was never created.  With
    no matching file the action just warns and succeeds, so on the v3.6.0 tag the
    step reported success while attaching nothing.
    
    Add a pwsh Compress-Archive step to produce the .zip (contents at top level, like
    the upload-artifact layout), and set fail_on_unmatched_files: true so a missing
    file fails the step loudly instead of silently passing.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit f6303b8b0dd6353c6a5bb4de2e855a13b86f22cf
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 09:54:12 2026 -0400

    ci: publish a release (source tarball + MSVC binaries) on version tags
    
    Add release.yml: on a v* tag it verifies the tag matches configure.ac, builds
    the source distribution via 'make dist', extracts the matching section of
    README.md as the release notes, and publishes a GitHub release with the tarball.
    
    Also add a 'tags: v[0-9]*' trigger to build.yml so the full matrix runs at the
    tagged commit and the existing MSVC 'Upload to GitHub Release' step (gated on
    github.ref_type == 'tag') attaches the 32-bit and 64-bit Windows MSVC binaries
    to the same release.
    
    No signing yet; mingw/cygwin binaries are not published.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 4e537ca4a37d1401598834b40aa1406dcf4997b4
Merge: ce9904d6 78417bf2
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 09:45:29 2026 -0400

    Merge pull request #981 from libffi/dlmalloc-builtin-clz-all-arches
    
    dlmalloc: use __builtin_clz/ctz bit-index on all GNU compilers (#754)

commit ce9904d6aec8dc6bb536a901a71c1e445eae7ec6
Merge: b32ddaf7 2f61bf22
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 09:44:59 2026 -0400

    Merge pull request #982 from emiltayl/msvc-atomics
    
    Add atomic intrinsics for MSVC to dlmalloc.c

commit b32ddaf73a16f0ddb8055908b829682eb5c73eeb
Merge: 0bff58c1 ad506b70
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 09:43:53 2026 -0400

    Merge pull request #983 from libffi/ci-drop-mingw-ansi-stdio
    
    ci: drop ineffective __USE_MINGW_ANSI_STDIO from Win64 mingw test run

commit ad506b7055ce76f7254c821a68149e4e38807da0
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 08:36:41 2026 -0400

    ci: drop ineffective __USE_MINGW_ANSI_STDIO from the Win64 mingw test run
    
    The RUNTESTFLAGS="TOOL_OPTIONS=-D__USE_MINGW_ANSI_STDIO=1" added to silence the
    long double %Lf/%Lg -Wformat warnings on x86_64-w64-mingw32 does not actually
    work: the flag reaches the compile line but gcc's -Wformat still uses the MS
    printf archetype with the mingw headers on the runner, so the ~10 long double
    '(test for excess errors)' failures persist regardless.  The rlgl policy already
    XFAILs those as the environmental printf-format issue they are, so the
    ineffective flag is just noise -- remove it and let the baseline handle them.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 78417bf2e6ca007fe4c9ff9cb74b31a55a2f3cae
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 08:16:29 2026 -0400

    dlmalloc: use __builtin_clz/ctz bit-index on all GNU compilers (#754)
    
    Upstream 2.8.6 already replaced the old x86 'bsrl' inline asm in
    compute_tree_index/compute_bit2idx with __builtin_clz/__builtin_ctz, but gated
    the intrinsic path to __i386__/__x86_64__; every other GNU-compatible target
    fell back to the generic C bit-twiddling (or ffs()).  __builtin_clz/__builtin_ctz
    are target-independent, so enable the intrinsic path for any __GNUC__ compiler.
    This lowers to a hardware bit-count instruction on aarch64 (clz / rbit+clz),
    riscv, power, s390x, etc. instead of the open-coded idiom.
    
    Also drop the now-unused USE_BUILTIN_FFS define in closures.c (and thus the
    <strings.h> include for ffs), since the intrinsic path no longer needs it.
    
    Implements the optimization from #754 (whose diff predated the 2.8.6 update).
    Verified: x86_64 closures suite 588/0 (unchanged); the macros compile and emit
    clz/ctz instructions for aarch64/riscv64/ppc64le/s390x/arm/mips64/sparc64.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 2f61bf2213bbda7b348f13c1d58947c5fa33f858
Author: Emil Taylor Bye <phptph@gmail.com>
Date:   Sat Jun 20 14:05:16 2026 +0200

    Add atomic intrinsics for MSVC to dlmalloc.c

commit 0bff58c1043064d7cb0e825107ba79946c2bae51
Merge: 2388f6dc ad447103
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 08:06:46 2026 -0400

    Merge pull request #978 from libffi/prep-3.6.0-release
    
    Prepare libffi 3.6.0 release

commit 2388f6dcfac3c434472668d32c805ec34a08ce2d
Merge: 68be2150 72ce9313
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 08:02:26 2026 -0400

    Merge pull request #980 from libffi/doc-closure-free-safety
    
    doc: note that freeing an in-use closure is unsafe (#835)

commit 68be21507f1008e069e572b585dee75d48249528
Merge: bc0ebb2e f554e4ce
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 08:01:59 2026 -0400

    Merge pull request #979 from libffi/portability-c99-clang-asm
    
    Portability: strict-C99 and clang-assembler fixes (#795, #851, #947)

commit 72ce931309653640ddd4e687a30a8293b95f909c
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 04:35:17 2026 -0400

    doc: note that freeing a closure in use is unsafe (#835)
    
    Clarify in the ffi_closure_free documentation that a closure must not be freed
    while it may still be invoked -- including from within its own callback -- since
    that frees the executable trampoline still in use.  (It may appear to work by
    luck on some targets, but is not safe in general.)
    
    Fixes #835
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit f554e4ce0cb10ace600819e08beefd6593cdedc0
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 04:33:36 2026 -0400

    x86: use pc-relative label difference for clang's assembler (#947)
    
    When HAVE_AS_X86_PCREL is not defined (e.g. builds that don't run libffi's
    autoconf checks, such as the meson port), the unwind tables fall back to the
    'X@rel' relocation variant, which clang's integrated assembler rejects with
    "invalid variant 'rel'".  The pc-relative label difference 'X - .' assembles
    on clang and GNU as alike, so prefer it for clang.  The gcc/other path (which
    may target Sun as on Solaris) is left on @rel.  Verified end-to-end with clang.
    
    Fixes #947
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 2760a1e2c828a9d7ab2169ff31c0c495a0c5f2d4
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 04:33:36 2026 -0400

    use __asm__ instead of the asm keyword for strict C99 (#851)
    
    Clang (e.g. the Android NDK) only provides the 'asm' keyword in GNU modes; under
    a strict -std=c99 it errors with "use of undeclared identifier 'asm'".  Replace
    the bare 'asm' keyword with the always-available __asm__ spelling across the
    affected backends (aarch64, alpha, frv, ia64, moxie, or1k, pa, riscv, sh64,
    sparc).  This is a pure spelling change with identical semantics on GNU
    compilers; verified the __asm__/__asm__ volatile/register-__asm__ forms compile
    under gcc and clang -std=c99 -pedantic.
    
    Fixes #851
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 08487a6214d4419430b0e8e7dcb3ec967caf8f6f
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 04:33:36 2026 -0400

    ffi.h: avoid -Wpedantic warning on the anonymous union under C99 (#795)
    
    The ffi_closure struct uses an anonymous union, which is C11 (a GNU extension
    in C99).  Including <ffi.h> with -std=c99 -pedantic warned (and broke -Werror
    users).  Prefix the union with __extension__ on GNU compilers so the field
    names are kept without the pedantic diagnostic; MSVC accepts anonymous unions
    natively.  Verified with gcc and clang under -std=c99 -pedantic -Werror.
    
    Fixes #795
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit ad4471039fa55c7e001aef031da297df30c952ae
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 03:47:51 2026 -0400

    Prepare libffi 3.6.0 release
    
    Bump version to 3.6.0, libtool version-info to 11:1:3 (revision++; no public
    interface change since 3.5.2), set the release date, and record the dlmalloc
    2.8.6 update and the #873 ThreadSanitizer fix in the history.
    
    NOTE: the release date (June 20, 2026) is a placeholder -- adjust to the actual
    tag date before merging.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit bc0ebb2e04c39fa750c3c6ebd93755f9b4762d77
Merge: d3f96bee fc67b31a
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 03:36:07 2026 -0400

    Merge pull request #977 from libffi/dlmalloc-tsan-clean
    
    dlmalloc: make mparams init and spin-lock peek TSAN-clean (fix #873)

commit fc67b31ab8c0871dd94385f018fc55413bc034b5
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 03:30:59 2026 -0400

    dlmalloc: make mparams init and spin-lock peek TSAN-clean
    
    dlmalloc reads two lock-free locations on every allocation that race with
    writes elsewhere; ThreadSanitizer flags both (libffi issue #873).  The 2.8.6
    update did not fix #873 -- confirmed with a -fsanitize=thread build of the
    libffi.threads/tsan.c 20-thread test, which reported two data races on global
    'mparams', plus (newly, since 2.8.6 uses spin locks instead of 2.8.3's pthread
    mutexes) a race on the spin-lock word.
    
    1. mparams init (#873): ensure_initialization() reads mparams.magic without a
       lock to decide if one-time init already ran, racing the write in
       init_mparams().  Upstream writes magic through a `volatile` cast, which is
       not a synchronizing operation.  Use an acquire load for the read and a
       release store for the write, so a thread that observes magic != 0 is
       guaranteed to see the other init writes (page_size, mflags, ...) too.
    
    2. spin-lock peek: spin_acquire_lock()'s `*(volatile int *)sl != 0` lock-free
       peek races CLEAR_LOCK's atomic release.  Read it with a relaxed atomic load
       instead -- still just a hint (CAS_LOCK provides the acquire on success).
    
    Both use __atomic_* builtins (work in any -std, no _Atomic field churn) with the
    original volatile access as a fallback when they are unavailable (e.g. MSVC),
    and are marked as libffi-local patches so they survive the next dlmalloc sync.
    
    After the fix the tsan.c test reports zero data races across 20 runs; the normal
    testsuite is unaffected (the atomic accesses read/write the same values).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit d3f96bee061158b0934a08ca2355e4e92fcc4b98
Merge: 5e3eeaff 97006e1a
Author: Anthony Green <green@moxielogic.com>
Date:   Sat Jun 20 03:03:43 2026 -0400

    Merge pull request #975 from libffi/update-dlmalloc-2.8.6
    
    dlmalloc: update bundled allocator 2.8.3 → 2.8.6

commit 97006e1a2d7f846b02e19411672c09a0f4aae56b
Merge: 26a1c8cd 5e3eeaff
Author: Anthony Green <green@moxielogic.com>
Date:   Fri Jun 19 21:55:11 2026 -0400

    Merge remote-tracking branch 'origin/master' into update-dlmalloc-2.8.6

commit 5e3eeaffbf7cf98e6991fe3995ab0826a461545e
Merge: 17bce72a 2aabe7eb
Author: Anthony Green <green@moxielogic.com>
Date:   Fri Jun 19 20:08:41 2026 -0400

    Merge pull request #976 from libffi/x86_64-android-binary128-longdouble
    
    x86: support IEEE binary128 long double on x86_64 (Android)

commit 2aabe7eb739a16197ed0fcaacb45eeba76149c19
Author: Anthony Green <green@moxielogic.com>
Date:   Fri Jun 19 20:04:09 2026 -0400

    x86: support IEEE binary128 long double on x86_64 (e.g. Android)
    
    On most x86_64 targets `long double` is the 80-bit x87 type, which the SysV
    psABI classifies X87/X87UP, passes in memory, and returns in st(0).  But some
    x86_64 targets -- notably Android (bionic), and anything built with
    -mlong-double-128 -- make `long double` the IEEE binary128 quad type, which the
    psABI passes and returns in SSE registers exactly like __float128 (class
    SSE/SSEUP, i.e. one %xmm register; _Complex binary128 is 32 bytes -> memory).
    
    libffi's x86_64 backend hardcoded the x87 path, so every long double call/return
    on x86_64 Android produced garbage (sizeof(long double)==16 for both formats, so
    the configure check could not tell them apart).  This was misdiagnosed as a QEMU
    or bionic-on-host issue; it is a real ABI bug -- the binaries run natively, the
    ABI is simply different.  Confirmed via the compiler: x86_64-linux-android sets
    __LDBL_MANT_DIG__=113 (binary128) and emits __addtf3/%xmm, vs 64/fldt on glibc.
    
    Detect binary128 long double at compile time (__LDBL_MANT_DIG__ == 113) and:
      - classify scalar long double as SSE/SSEUP, returned in %xmm0 (new
        UNIX64_RET_XMM128 store/load path: movups %xmm0);
      - classify _Complex long double as memory (passed/returned via hidden pointer);
      - copy the SSEUP eightbyte (the high half of the same %xmm register) in both
        the call and closure argument marshalling, which previously dropped it.
    
    Everything is gated on the compile-time check, so x87 targets (glibc, *BSD,
    macOS) are byte-for-byte unchanged.
    
    Validated by building with `-mlong-double-128` on x86_64 glibc (which reproduces
    the Android ABI: __addtf3/%xmm) and round-tripping a full-precision binary128
    value through both ffi_call and a closure -- both preserve all 113 mantissa bits.
    x87 long double regression is clean (full testsuite, 0 unexpected failures).
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 26a1c8cddcbdbbd1b5bae43e89c9deb20477d99e
Author: Anthony Green <green@moxielogic.com>
Date:   Fri Jun 19 19:16:38 2026 -0400

    dlmalloc: drop FORCEINLINE on win32 mmap helpers (fix mingw build)
    
    The Win64 mingw build failed:
    
      dlmalloc.c: error: multiple storage classes in declaration specifiers
      (at static FORCEINLINE win32mmap / win32direct_mmap / win32munmap)
    
    mingw-w64's <windows.h> (included in dlmalloc's WIN32 block) defines
    FORCEINLINE with an 'extern' storage class, so the upstream-2.8.6
    'static FORCEINLINE' on these helpers expands to 'static extern' -- rejected by
    gcc. MSVC's FORCEINLINE is __forceinline (no storage class), so MSVC was fine.
    
    libffi's previous 2.8.3 dlmalloc used plain 'static' for these win32 helpers;
    restore that (keeping the PAGE_EXECUTE_READWRITE change). They are trivial
    wrappers, so dropping the forced-inline hint has no practical effect.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

commit 17bce72aa5e2c396983b3c4ffa83a15e1cf453fa
Merge: a00d2dd3 7e93373f
Author: Anthony Green <green@moxielogic.com>
Date:   Fri Jun 19 18:36:28 2026 -0400

    Merge pull request #973 from libffi/ci-win64-mingw-real-tests
    
    ci: make Win64 mingw job actually run the testsuite

commit a00d2dd3b650e4d7d1b19008e1879179fa7dd315
Merge: 22c1a3e6 9db3ee0b
Author: Anthony Green <green@moxielogic.com>
Date:   Fri Jun 19 18:35:56 2026 -0400

    Merge pull request #974 from libffi/ci-fix-dist-hook-readme
    
    build: fix make dist failure from missing README "was released on" line

commit 326fff39c41a3c076c63f18b216fb0a44104edf4
