On Sat, Apr 06, 2013 at 04:36:31AM -0700, Brian Fraser wrote:
> $ opencc --version
> Open64 Compiler Suite: Version 4.5.2.1
> Built on: 2013-04-06 06:04:55 -0300
> Thread model: posix
> GNU gcc version 4.2.0 (Open64 4.5.2.1 driver)
Which version *is* that?
As best I can work out, there has never been a 4.5 release. 5.0 is current
(Nov 2011), and 4.2.4 was the previous branch:
http://www.open64.net/news.html
> My configure line is just this:
>
> ./Configure -des -Dusedevel -DDEBUGGING -Dcc=opencc
> -Accflags='-OPT:Olimit=0'
> not ok 1012
> # For list
(-2147483648, -1, 0, 1,
2147483647) (total -1) packed with i
> unpack '%53i' gave
8589934591, expected 9007199254740991
> not ok 1015
> # For list
(-2147483648, -1, 0, 1,
2147483647) (total -1) packed with i
> unpack '%54i' gave
8589934591, expected 18014398509481983
> not ok 1018
Also, this isn't the perl -V from the compiler that you were building with:
> Summary of my perl5 (revision 5 version 16 subversion 2) configuration:
>
> Platform:
> osname=linux, osvers=3.5.0-18-generic,
> archname=x86_64-linux-thread-multi
> uname='linux naw 3.5.0-18-generic #29-ubuntu smp fri oct 19 10:26:51
> utc 2012 x86_64 x86_64 x86_64 gnulinux '
> config_args='-de
> -Dprefix=/home/hugmeir/perl5/perlbrew/perls/perl-5.16.2 -DDEBUGGING
> -Dusethreads -Doptimize=-g -O0 -ggdb3 -Uversiononly -Accflags=-Wall -Wextra
> -Aeval:scriptdir=/home/hugmeir/perl5/perlbrew/perls/perl-5.16.2/bin'
> hint=recommended, useposix=true, d_sigaction=define
> useithreads=define, usemultiplicity=define
> useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
> use64bitint=define, use64bitall=define, uselongdouble=undef
> usemymalloc=n, bincompat5005=undef
> Compiler:
> cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -Wall -Wextra -DDEBUGGING
> -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include
> -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
> optimize='-g -O0 -ggdb3',
> cppflags='-D_REENTRANT -D_GNU_SOURCE -Wall -Wextra -DDEBUGGING
> -fno-strict-aliasing -pipe -fstack-protector -I/usr/local/include'
> ccversion='', gccversion='4.7.2', gccosandvers=''
which makes it harder to work out what is going on.
However, I can (eventually) get Open64 to build. I am going to document
this, because the summary conclusion becomes relevant to what to do next.
I first tried to build the current release, 5.0.0, from source from the
tarball, on a 6 month old Ubuntu install on x86_64 linux.
To do this, it seems
1) It insists on building 32 bit, so I have to install the gcc multilibs
package so that gcc -m32 works
2) As a chunk of it is in C++, I need to install the g++ multilibs packages
3) This *still* doesn't work, as it fails to compile some of the C++ code.
The default system version of gcc/g++ is 4.7. I've installed 4.8 in
/usr/local
The release notes say that they've dropped gcc 3 support, and recommend
upgrading from that to 4.3. They mention 4.4
g++-4.4 is the *earliest* version I can install, so I install that.
This at least gets past that compile problem
4) However, it is still spewing warnings from one g++ supplied header that
this functionality is deprecated and will be removed without warning
5) It bails out later due to a bad path in a Makefile. I fix this. Why?
$ diff -u osprey-gcc-4.2.0/host-x86_64-redhat-linux/gcc/Makefile~ osprey-gcc-4.2.0/host-x86_64-redhat-linux/gcc/Makefile
--- osprey-gcc-4.2.0/host-x86_64-redhat-linux/gcc/Makefile~ 2013-04-25 20:15:50.000000000 +0200
+++ osprey-gcc-4.2.0/host-x86_64-redhat-linux/gcc/Makefile 2013-04-25 20:28:24.000000000 +0200
@@ -848,7 +848,7 @@
#LIBGSPIN = ../../../libspin/libgspin42.a
#This is a symbol link, create by the CONFIGURE script
#LIBGSPIN = ../../../libspin/libgspin42.a
-LIBGSPIN = ../../osprey/targdir/libspin_4_2_0/libgspin42.a
+LIBGSPIN = ../../../osprey/targdir/libspin_4_2_0/libgspin42.a
# Build and host support libraries.
LIBIBERTY = ../libiberty/libiberty.a
6) There are still other errors. I spot part of the instructions on the web
page:
On some Ubuntu distributions, you need to change the symbol link
'/bin/sh' to '/bin/bash' to make the scripts work.
http://www.open64.net/?id=254
I do this. It's a good job the power didn't go out in the middle, as
not having /bin/sh would render the machine unbootable.
I'd also like to express my utter *contempt* for anything which feels that
the right "fix" for #! lines that read "/bin/sh" instead of "/bin/bash" is
to keep the #! lines the same and instead futz with the root filesystem as
root.
7) This still didn't fix the build.
So I got trunk from subversion. That does build.
With this, I can replicate your problem.
It seems to be a code generation bug.
With the optimisation level you gave:
$ ./perl -le 'print unpack "%33i", pack "i", -
2147483648
2147483648
One would expect this:
$ perl -le 'print unpack "%33i", pack "i", -
2147483648'
6442450944
If I turn the optimiser down, it goes away.
I wondered if it was bug in our code from abusing C aliasing, but having
tweaked a function to take this didn't seem to be it.
For the example above, it's this bit of pp_pack.c:
case 'i' | TYPE_IS_SHRIEKING:
while (len-- > 0) {
int aint;
SHIFT_VAR(utf8, s, strend, aint, datumtype);
DO_BO_UNPACK(aint, i);
if (!checksum)
mPUSHi(aint);
else if (checksum > bits_in_uv)
cdouble += (NV)aint;
else
cuv += aint;
}
break;
Sticking gdb on there to look at aint produces garbage:
(gdb)
1822 SHIFT_VAR(utf8, s, strend, aint, datumtype);
(gdb)
1823 DO_BO_UNPACK(aint, i);
(gdb) p aint
$1 = 7123920
(gdb) s
1824 if (!checksum)
(gdb)
1826 else if (checksum > bits_in_uv)
(gdb)
1829 cuv += aint;
(gdb) p aint
$2 = 7123920
(gdb) s
1820 while (len-- > 0) {
(gdb) p cuv
$3 =
2147483648
However, as it's compiled with -O2 as well as -g, it's not clear whether this
is just an artefact of buggy debugging metadata generated by the compiler.
So I stuck a printf() in there, and the bug goes away.
Interesting.
Question - is this because printf() is varargs, and so it forces that variable
aint to be stored on the stack? Or is it something else?
After a bit of experimentation, it seemed that adding any *reference* to
aint between SHIFT_VAR() and DO_BO_UNPACK() makes the bug go away. So,
there is no bug with this:
diff --git a/pp_pack.c b/pp_pack.c
index eaeec89..33a65b6 100644
--- a/pp_pack.c
+++ b/pp_pack.c
@@ -1820,6 +1820,8 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, const char *s, const
while (len-- > 0) {
int aint;
SHIFT_VAR(utf8, s, strend, aint, datumtype);
+ if (aint)
+ getpid();
DO_BO_UNPACK(aint, i);
if (!checksum)
mPUSHi(aint);
but the bug is still visible with this:
diff --git a/pp_pack.c b/pp_pack.c
index eaeec89..3366c59 100644
--- a/pp_pack.c
+++ b/pp_pack.c
@@ -1820,6 +1820,8 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, const char *s, const
while (len-- > 0) {
int aint;
SHIFT_VAR(utf8, s, strend, aint, datumtype);
+ if (checksum)
+ getpid();
DO_BO_UNPACK(aint, i);
if (!checksum)
mPUSHi(aint);
The generated assembly code for the two is very close:
$ diff -u pp_pack.s.bad pp_pack.s.good | diffstat
pp_pack.s.good | 37 +++++++++++++++++++------------------
1 file changed, 19 insertions(+), 18 deletions(-)
I don't know x86_64 assembler (and there isn't space in my head for it)
but the diff is small enough to experiment with by trial and error to find
the solution. This appears to be the fix needed to the assembler:
$ diff -u pp_pack.s.bad pp_pack.s
--- pp_pack.s.bad 2013-04-26 16:49:40.000000000 +0200
+++ pp_pack.s 2013-04-26 16:51:29.000000000 +0200
@@ -9416,7 +9416,8 @@
#<loop> Part of loop body line 1822, head labeled .Lt_68_360194
.loc 1 1831 0
movq -464(%rbp),%rax # [0] cuv
- addq %rsi,%rax # [3]
+ movslq %esi,%rdi # [2]
+ addq %rdi,%rax # [3]
movq %rax,-464(%rbp) # [4] cuv
jmp .Lt_68_425730 # [4]
.LBB426_S_unpack_rec:
With that there is no bug (for 'i'. I assume that the others are the same)
So, yes, I think that this is a compiler bug. In theory we could try to
work round this.
In practise, I don't think that we should bother
1) It's not obvious how to to do it cleanly
2) I don't think that it's worth it. The Open64 compiler doesn't look like
it has much future. However good its codegen might be, it doesn't look
like it will survive against gcc and clang.
There aren't many commits since 2011, and if they aren't careful they will
die when their C++ code no longer compiles on supported, installable g++
releases.
3) That code in pp_pack.c is a lot more complex than it could be. In
particular, I think that the jobs of SHIFT_VAR() and DO_BO_UNPACK() should
be merged and reworked. At which point the code will be considerably
different (and simpler), which might mean that the bug is never hit.
SHIFT_VAR() is from Ton Hospel's "encoding neutral unpack" work. It either
uses Copy() to copy bytes to the target variable, or calls out to a function
to decode UTF-8 if the source string is in UTF-8.
DO_BO_UNPACK() is from Marcus Holland Moritz's byte-order changes. It flips
the endianness if a suffix is used.
I think that the byte-order stuff was done first.
Right now, the code carefully copies (logical) characters in-order into an
integer variable aint, and then (if necessary) carefully uses bit-shifting
to invert that integer variable.
I think that a simpler solution would be to migrate that inversion into the
"copy" routine. Reverse the bytes while copying them, if an endian-swap is
needed. Keep the inline Copy() for the case of source is octets, no endian
swap, and push the other 3 cases into the helper function. That way the
default path remains fast and small, and a lot of inline code is
de-duplicated.
However, I can see that that refactoring is going to be a little
involved.
I guess that this bug is sort of a "wontfix".
Nicholas Clark