sympow: do not mess with fpu control word if possible

Also: add basic tests to detect fpu miscompilation.

The program sets the fpu to 53 bit mode to get consistent results. It
turns out that doing this on musl breaks fmt_fp (-> printf) which uses
long double to format floats. Note this is an issue even on 64 bit musl,
although setting fpu to 53 bits is unnecessary on 64 bits because of the
default -mfpmath=sse.

The way Configure works: it tries different combinations of flags which
exercise different methods to change the fpu control word until one
works, meaning doubles are effectively 53 bits.  The fix here is to try
first NOT touching the fpu control word. On x86_64 using the default
-mfpmath=sse this will succeed bypassing all fpucw modification.

For i686 using -mfpmath=387 (always if sse2 not available) the code will
fall back to changing the fpu control word as before. Since this is not
a problem for glibc, everything still works. I expect i686-musl to still
be broken, but we don't support that arch.

A simple way to test the bug on musl is to run

    $ sympow -curve '[0,0,0,0,1]' -analrank
    ...
    Analytic Rank is 0 : L-value 7.01091e-01

When the fpucw is changed, on musl this prints "7.01092e-01" instead.

The actual value computed to 128 bits with pari is:

    $ echo 'elllseries(ellinit([0,0,0,0,1]),1)' | gp -q
    0.70109105266272713058750953952514706773

so the glibc output is the correct one.

Again: what is broken is not computing but printing, as fmt_fp in musl
uses long double, which means messing with fpu control word breaks it.
This is important since sagemath parses sympow output.

This affects sagemath doctests as in

    $ sage -t src/sage/lfunctions/sympow.py
    ...
    (3 failures)

After this commit, the doctest in question passes on the 3 supported
archs.
This commit is contained in:
Gonzalo Tornaría 2021-11-22 19:24:50 -03:00 committed by Leah Neukirchen
parent 5276d248be
commit bd67413b3c
4 changed files with 128 additions and 1 deletions

51
srcpkgs/sympow/files/test Normal file
View file

@ -0,0 +1,51 @@
#! /bin/sh
# Quick minimal test for sympow
#
# Compute modular degree and analytic rank for some curves so we check
# that it works and also that fpu arithmetic and output are ok
# setup sympow to run from cwd
export SYMPOW_PKGDATADIR=.
export SYMPOW_PKGLIBDIR=.
mkdir -p sympow_cache
export SYMPOW_CACHEDIR=sympow_cache
E11a=[0,-1,1,-10,-20]
E37a=[0,0,1,-1,0]
E389a=[0,1,1,-2,0]
E5077a=[0,0,1,-7,6]
E4=[1,-1,0,-79,289]
E5=[0,0,1,-79,342]
E6=[1,1,0,-2582,48720]
E7=[0,0,0,-10012,346900]
echo "### Compute modular degree for some curves"
echo "# 11a"
./sympow -quiet -curve $E11a -moddeg
echo "# 37a"
./sympow -quiet -curve $E37a -moddeg
echo "# 389a"
./sympow -quiet -curve $E389a -moddeg
echo "# 5077a"
./sympow -quiet -curve $E5077a -moddeg
echo "# 234446a"
./sympow -quiet -curve $E4 -moddeg
echo "### Compute analytic rank for some curves"
echo "# 11a"
./sympow -quiet -curve $E11a -analrank
echo "# 37a"
./sympow -quiet -curve $E37a -analrank
echo "# 389a"
./sympow -quiet -curve $E389a -analrank
echo "# 5077a"
./sympow -quiet -curve $E5077a -analrank
echo "# 234446a"
./sympow -quiet -curve $E4 -analrank
echo "# curve of rank 5"
./sympow -quiet -curve $E5 -analrank
echo "# curve of rank 6"
./sympow -quiet -curve $E6 -analrank
echo "# curve of rank 7"
./sympow -quiet -curve $E7 -analrank

View file

@ -0,0 +1,54 @@
### Compute modular degree for some curves
# 11a
Minimal model of curve is [0,-1,1,-10,-20]
Maximal number of terms is 14
Modular Degree is 1
# 37a
Minimal model of curve is [0,0,1,-1,0]
Maximal number of terms is 48
Modular Degree is 2
# 389a
Minimal model of curve is [0,1,1,-2,0]
Maximal number of terms is 512
Modular Degree is 40
# 5077a
Minimal model of curve is [0,0,1,-7,6]
Maximal number of terms is 8130
Modular Degree is 1984
# 234446a
Minimal model of curve is [1,-1,0,-79,289]
Maximal number of terms is 518272
Modular Degree is 334976
### Compute analytic rank for some curves
# 11a
Minimal model of curve is [0,-1,1,-10,-20]
Maximal number of terms is 7
Analytic Rank is 0 : L-value 2.53842e-01
# 37a
Minimal model of curve is [0,0,1,-1,0]
Maximal number of terms is 10
Analytic Rank is 1 : L'-value 3.06000e-01
# 389a
Minimal model of curve is [0,1,1,-2,0]
Maximal number of terms is 43
Analytic Rank is 2 : leading L-term 7.59317e-01
# 5077a
Minimal model of curve is [0,0,1,-7,6]
Maximal number of terms is 128
Analytic Rank is 3 : leading L-term 1.73185e+00
# 234446a
Minimal model of curve is [1,-1,0,-79,289]
Maximal number of terms is 1064
Analytic Rank is 4 : leading L-term 8.94385e+00
# curve of rank 5
Minimal model of curve is [0,0,1,-79,342]
Maximal number of terms is 7858
Analytic Rank is 5 : leading L-term 3.02857e+01
# curve of rank 6
Minimal model of curve is [1,1,0,-2582,48720]
Maximal number of terms is 184767
Analytic Rank is 6 : leading L-term 3.20781e+02
# curve of rank 7
Minimal model of curve is [0,0,0,-10012,346900]
Maximal number of terms is 1324544
Analytic Rank is 7 : leading L-term 1.32517e+03

View file

@ -0,0 +1,14 @@
--- a/Configure 2020-04-16 14:12:56.000000000 -0300
+++ b/Configure 2021-11-24 16:04:43.647041822 -0300
@@ -154,10 +154,10 @@
try_add_CFLAG $FLAG bypass && break
done
# Select the most appropriate FPU control scheme
-for FLAG in '-DISOC99_FENV' '-DFPUCONTROLH' '-Dx86'; do
+for FLAG in '' '-DISOC99_FENV' '-DFPUCONTROLH' '-Dx86'; do
try_add_CFLAG $FLAG && break
done
# Some flags to try as last resort. These hurt performance, so only add
# them if needed.

View file

@ -1,7 +1,7 @@
# Template file for 'sympow'
pkgname=sympow
version=2.023.6
revision=2
revision=3
wrksrc=${pkgname}-v${version}
build_style=configure
make_build_target=all
@ -15,6 +15,8 @@ distfiles="https://gitlab.com/rezozer/forks/sympow/-/archive/v${version}/sympow-
checksum=d153530dfdd46da05c954121640e50771064536fedc22c7fef24fb11083172ef
nocross=yes # runs binaries built for target
CFLAGS="-Wno-unused-result"
do_configure() {
PREFIX=/usr ./Configure
}
@ -22,3 +24,9 @@ do_configure() {
post_install() {
vlicense COPYING
}
do_check() {
echo "Testing sympow ..."
sh "${FILESDIR}"/test > test.log
diff "${FILESDIR}"/test.out test.log && echo PASS
}