Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perl 5.40.0: Crash while trying to compile perl on i686 with quadmath and SSE. #22577

Closed
abatyiev opened this issue Sep 6, 2024 · 14 comments · Fixed by #22609
Closed

perl 5.40.0: Crash while trying to compile perl on i686 with quadmath and SSE. #22577

abatyiev opened this issue Sep 6, 2024 · 14 comments · Fixed by #22609
Assignees

Comments

@abatyiev
Copy link

abatyiev commented Sep 6, 2024

Module: core

Description
I'm trying to build perl on 32bit Gentoo Linux box with SSE enabled

Steps to Reproduce
I'm trying to compile perl via following configure:
sh ./Configure -des -Dusequadmath -Accflags="-O2 -fwrapv -pipe -msse" -Doptimize="-O2 -fwrapv -pipe -msse"
However, miniperl binary crashes during build.

Investigation
Crash in Perl_init_constants (sv.c:16516) is due to misaligned access to PL_sv_no of __float128 type, because it requires 16 bytes alignment while perl's allocator (new_XPVNV) provides only 8 byte alignment.
Compiler uses movaps instruction that is part of SSE instruction set that triggers the crash.

Perl's allocator seem to allocate memory in blocks of 40 bytes on i686. Crash does not reproduce on x64 due to bigger allocated block (64 bytes) that provides correct alignment.

@jkeenan
Copy link
Contributor

jkeenan commented Sep 6, 2024

Were you able to build older versions of Perl (e.g., perl-5.38.*) on this platform with the same configuration arguments?

@abatyiev
Copy link
Author

abatyiev commented Sep 8, 2024

I've done git bisect and the first bad commit is 9c75d91 from 2014.

@jkeenan
Copy link
Contributor

jkeenan commented Sep 8, 2024

I've done git bisect and the first bad commit is 9c75d91 from 2014.

For reference:

$ gitshowf 9c75d918805f7766855958e1eff74f6379d8b069
commit 9c75d918805f7766855958e1eff74f6379d8b069
Author:     Jarkko Hietaniemi <[email protected]>
AuthorDate: Fri Aug 22 11:55:33 2014 -0400
Commit:     Jarkko Hietaniemi <[email protected]>
CommitDate: Fri Sep 19 09:26:49 2014 -0400

    quadmath __float128 as NVTYPE.

diff --git a/Configure b/Configure
index 14a1b012ab..2c820f984e 100755
--- a/Configure
+++ b/Configure
@@ -16305,6 +16305,9 @@ define:define)
        nvsize=$doublesize
        ;;
 esac
+case "$usequadmath:$i_quadmath" in
+define:define) nvtype="__float128" ;;
+esac
 
 $echo "(IV will be "$ivtype", $ivsize bytes)"
 $echo "(UV will be "$uvtype", $uvsize bytes)"

@jkeenan
Copy link
Contributor

jkeenan commented Sep 8, 2024

I've done git bisect and the first bad commit is 9c75d91 from 2014.

An artifact from ancient times! Let's hope that @jhi spots this.

In the meantime, would it be possible for you to (1) either (a) download and unpack a perl-5.40.0 tarball or (b) do a checkout of tag v5.40.0 from the git repository, and then (2) run ./Configure like this:

$ sh ./Configure -des -Dusedevel -Dusequadmath -Accflags="-O2 -fwrapv -pipe -msse" -Doptimize="-O2 -fwrapv -pipe -msse"

... and then (3) paste the content of config.sh in this ticket?

Thanks.

@sisyphus
Copy link
Contributor

sisyphus commented Sep 9, 2024

In perl.h, we deal with a __float128 alignment issue (that affects only gcc builds on Windows) with the following:

/* On MS Windows,with 64-bit mingw-w64 compilers, we
   need to attend to a __float128 alignment issue if
   USE_QUADMATH is defined. Otherwise we simply:
   typedef NVTYPE NV
   32-bit mingw.org compilers might also require
   aligned(32) - at least that's what I found with my
   Math::Foat128 module. But this is as yet untested
   here, so no allowance is being made for mingw.org
   compilers at this stage. -- sisyphus January 2021
*/
#if (defined(USE_LONG_DOUBLE) || defined(USE_QUADMATH)) && defined(__MINGW64__)
   /* 64-bit build, mingw-w64 compiler only */
   typedef NVTYPE NV __attribute__ ((aligned(8)));
#else
   typedef NVTYPE NV;
#endif

@abatyiev, perhaps that same technique could be useful to you ?

@tonycoz tonycoz self-assigned this Sep 9, 2024
@tonycoz
Copy link
Contributor

tonycoz commented Sep 10, 2024

Reproduced with blead on i686 debian.

It looks like alignbytes is being set correctly.

I expect bodies_by_type[SVt_PVNV] needs a fix but it's been a while since I looked at this code.

tonycoz added a commit to tonycoz/perl5 that referenced this issue Sep 18, 2024
On i686 systems with -msse __float128 requires 16 byte alignment, but
for XPVNV bodies the hack used by new_body_allocated() to avoid
allocating the unused xmg_stash and xmg_u fields means that the base
of the XPVNV body ends up mis-aligned on 32-bit systems.

One 64-bit systems the combined size of those fields is 16-bytes so
the modified pointer is still properly aligned.

Fixes Perl#22577
@tonycoz
Copy link
Contributor

tonycoz commented Sep 23, 2024

It may be that the PR fixes the ming64 misalignments too, though you might need to set alignbytes=16 in the config file.

@sisyphus
Copy link
Contributor

It may be that the PR fixes the ming64 misalignments too,

Windows 32-bit quadmath builds are already trouble-free.
The only alignment fiddling we do (in perl.h and win32/vmem.h) is for 64-bit builds only.

Nevertheless, for the 32-bit quadmath builds, I did try setting MEM_ALIGNBYTES and $Config{alignbytes} to 16.
The MSWin32-x86-multi-thread-quadmath build still worked fine, irrespective of whether this PR is included or not.
(I didn't check any other 32-bit build configurations.)

With the alignment values set to 16, and the current alignment fiddling removed from perl.h and win32/vmem.h, I found no trouble in running gmake for MSWin32-x64-multi-thread-quadmath, but the perl.exe that is built crashes silently upon execution.
And the same problem persists even when I change the condition specified by this PR from:

#if NVSIZE > 8 && PTRSIZE < 8 && MEM_ALIGNBYTES > 8
to
#if NVSIZE > 8 && PTRSIZE == 8 && MEM_ALIGNBYTES > 8

It seems that doing away with our current alignment fiddling on the 64-bit builds might be difficult, and the 32-bit builds work so easily and simply that there's not much point in introducing any changes to them.

I don't mind poking around some more if you have some advice on what else I can try to get MSWin32-x64-multi-thread-quadmath to build successfully when the alignment values are 16.
I'm not really a big fan of our current method of dealing with the 64-bit alignment as it has always felt a bit "random" to me.
But it works.

@tonycoz
Copy link
Contributor

tonycoz commented Sep 24, 2024

With that aligned(8) disabled, I didn't see a crash with:

gmake -j2 CCHOME=c:\mingw64 USE_QUADMATH=define I_QUADMATH=define test

gcc is:

gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders) 13.1.0

tonycoz added a commit to tonycoz/perl5 that referenced this issue Sep 24, 2024
Perl SV body structures include xmg_stash and xmg_u fields at the
front, which are only valid for type SVt_PVMG and higher.

This allows those fields to be at a constant offset from the start
of the body.

To save memory perl generally allocates the bodies where
type < SVt_PVMG without the space needed for these two fields,
offsetting the body pointer back by the size of the two fields.  At
least for the first body in an arena this is technically
undefined behaviour, but we've done it forever.

With -msse __float128 requires 16 byte alignment, but for XPVNV
bodies the hack used here means that the base of the XPVNV
body ends up mis-aligned on 32-bit systems.

On 64-bit systems the combined size of those fields is 16-bytes so
the modified pointer is still properly aligned.

To fix this allocate the full XPVNV structure when 16 byte alignment
is required for NV, NV is more than 8 bytes and pointers are small
enough that the NV would have been mis-aligned.

Fixes Perl#22577
@sisyphus
Copy link
Contributor

sisyphus commented Sep 24, 2024

The aligned(16) occurrences in win32/vmem.h (which I assume you've left in place) were introduced some time after the aligned(8) modification in perl.h - ie when gcc-12 arrived on the scene.
That win32/vmem.h alteration apparently now makes (and perhaps always did make) the modification to perl.h superfluous.
AFAICT, that perl.h code could be removed. (I've checked with a -Duselongdouble build, too.)

UPDATE: Today, gcc-12 is working fine. I'll do some more testing using older (but not too old) versions of gcc and submit a PR that removes the "aligned(8)" stuff in perl.h once I'm satisfied that it can be done safely.

When I try to build a standard (nvtype of "double") x64 perl-5.41.4 using gcc-12.2.0, the gmake step won't even complete - owing to undefined references in win32.o.
On the basis that gcc-12 (and probably earlier) is therefore unable to build 5.41.4 we could also remove the __GNUC__ > 11 condition from the aligned(16) modifications in win32/vmem.h, as that condition can also be deemed superfluous.

@tonycoz
Copy link
Contributor

tonycoz commented Sep 25, 2024

It depends on which symbols are missing, it may be fixable.

I did test my fix against i686 builds on Windows with -msse, which crashes in similar ways to the report here.

Unfortunately I don't think this is fixable on Windows, since the underlying problem is that malloc() returns 8 byte aligned memory, not 16-byte.

With UCRT we could use _aligned_malloc but that works by calling malloc() with some extra space and returning a pointer offset from the allocation to align, which is wasteful for general use (see ucrt/heap/align.cpp in the UCRT source).

@sisyphus
Copy link
Contributor

As I've mentioned in the update to my previous post, I'll provide a PR that removes the aligned(8) stuff from perl.h if some more testing reveals that can be done safely.

I couldn't get anywhere with -msse builds - probably something that can be looked at separately.

sisyphus added a commit to sisyphus/perl5 that referenced this issue Sep 26, 2024
Remove a Win32-specific memory alignment instruction as
discussed in Perl#22577.
khwilliamson pushed a commit that referenced this issue Sep 30, 2024
Remove a Win32-specific memory alignment instruction as
discussed in #22577.
@thesamesam
Copy link

I found the review comments in #22609 a bit hard to follow. Is it pending another reviewer or needs more testing? (I'm just asking as OP here linked it over in the Gentoo bug and wondering if I should backport it now or wait).

tonycoz added a commit that referenced this issue Nov 17, 2024
Perl SV body structures include xmg_stash and xmg_u fields at the
front, which are only valid for type SVt_PVMG and higher.

This allows those fields to be at a constant offset from the start
of the body.

To save memory perl generally allocates the bodies where
type < SVt_PVMG without the space needed for these two fields,
offsetting the body pointer back by the size of the two fields.  At
least for the first body in an arena this is technically
undefined behaviour, but we've done it forever.

With -msse __float128 requires 16 byte alignment, but for XPVNV
bodies the hack used here means that the base of the XPVNV
body ends up mis-aligned on 32-bit systems.

On 64-bit systems the combined size of those fields is 16-bytes so
the modified pointer is still properly aligned.

To fix this allocate the full XPVNV structure when 16 byte alignment
is required for NV, NV is more than 8 bytes and pointers are small
enough that the NV would have been mis-aligned.

Fixes #22577
@tonycoz
Copy link
Contributor

tonycoz commented Nov 17, 2024

I was hoping for an approval, but it's pretty esoteric, I've applied it anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants