Experience while trying to compile Octave for mingw32 with LTO

This is just to summarize my experience while trying to compile Octave for mingw32 with LTO (link time optimization) using MXE Octave.
To state the most important at the beginning: I wasn’t successful.
But still this might help someone else that tries to do something similar. (Maybe at least, it’ll save them some time.)

If I understand GCC’s documentation correctly, using LTO should be possible in principle for a mingw target:
LTO Overview (GNU Compiler Collection (GCC) Internals)

Currently, LTO support is enabled in most ELF-based systems, as well as darwin, cygwin and mingw systems.

I wanted to see if there is any measurable performance difference when using LTO compared to a “regular” build.

I used this patch for the build rule of default Octave: mxe-octave-lto.patch (1.6 KB)

Compilation went smoothly. But I got the following errors when linking liboctinterp:

/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-pager.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD1Ev[_ZThn8_N11oprocstreamD1Ev]+0x0): multiple definition of `oprocstream::~oprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD1Ev[_ZTv0_n24_N11oprocstreamD1Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-pager.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD1Ev[_ZThn8_N11oprocstreamD1Ev]+0x0): multiple definition of `non-virtual thunk to oprocstream::~oprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD1Ev[_ZTv0_n24_N11oprocstreamD1Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-pager.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD1Ev[_ZThn8_N11oprocstreamD1Ev]+0x0): multiple definition of `virtual thunk to oprocstream::~oprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD1Ev[_ZTv0_n24_N11oprocstreamD1Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-pager.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD0Ev[_ZThn8_N11oprocstreamD0Ev]+0x0): multiple definition of `oprocstream::~oprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD0Ev[_ZTv0_n24_N11oprocstreamD0Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-pager.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD0Ev[_ZThn8_N11oprocstreamD0Ev]+0x0): multiple definition of `non-virtual thunk to oprocstream::~oprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD0Ev[_ZTv0_n24_N11oprocstreamD0Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-pager.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD0Ev[_ZThn8_N11oprocstreamD0Ev]+0x0): multiple definition of `virtual thunk to oprocstream::~oprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11oprocstreamD0Ev[_ZTv0_n24_N11oprocstreamD0Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-toplev.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD1Ev[_ZThn16_N11iprocstreamD1Ev]+0x0): multiple definition of `iprocstream::~iprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD1Ev[_ZTv0_n24_N11iprocstreamD1Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-toplev.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD1Ev[_ZThn16_N11iprocstreamD1Ev]+0x0): multiple definition of `non-virtual thunk to iprocstream::~iprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD1Ev[_ZTv0_n24_N11iprocstreamD1Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-toplev.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD1Ev[_ZThn16_N11iprocstreamD1Ev]+0x0): multiple definition of `virtual thunk to iprocstream::~iprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD1Ev[_ZTv0_n24_N11iprocstreamD1Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-toplev.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD0Ev[_ZThn16_N11iprocstreamD0Ev]+0x0): multiple definition of `iprocstream::~iprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD0Ev[_ZTv0_n24_N11iprocstreamD0Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-toplev.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD0Ev[_ZThn16_N11iprocstreamD0Ev]+0x0): multiple definition of `non-virtual thunk to iprocstream::~iprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD0Ev[_ZTv0_n24_N11iprocstreamD0Ev]+0x0): first defined here
/home/osboxes/Documents/Repositories/Octave/mxe-octave/usr/bin/x86_64-w64-mingw32-ld: libcorefcn_la-toplev.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD0Ev[_ZThn16_N11iprocstreamD0Ev]+0x0): multiple definition of `virtual thunk to iprocstream::~iprocstream()'; libcorefcn_la-dirfns.o (symbol from plugin):(.gnu.linkonce.t._ZN11iprocstreamD0Ev[_ZTv0_n24_N11iprocstreamD0Ev]+0x0): first defined here
collect2: error: ld returned 1 exit status

It is “only” complaining about two classes: iprocstream and oprocstream.

This might be caused by the issue described in this bug report against gcc:
94156 – Multiple definition of destructor and non-virtual thunk for classes that use multiple inheritance when building static library (gnu.org)

The inheritance of those two classes is

class
OCTINTERP_API
iprocstream : public std::istream, public procstreambase

and

class
OCTINTERP_API
oprocstream : public std::ostream, public procstreambase

They also inherit from multiple classes like in the linked bug report.

It is interesting that it doesn’t complain about procstream which has very similar inheritance:

class
OCTINTERP_API
procstream : public std::iostream, public procstreambase

AFAICT, this is not a bug in Octave. But more likely a compiler/linker issue.

That’s all I got. :smiley:

On linux I do not see much difference between “-O2” and “-O3 -mtune=native -flto”

Dmitri.

Also on linux (see e.g. buildbot’s configs) we had to set “NM=gcc-nm AR=gcc-ar RANLIB=gcc-ranlib”

Dmitri.

I played with benchmarks a little longe and while most are slightly (1 to 5%) faster for (-O3 -flto),
the one that surprisingly slow is “test integral2” (also use “test integral3” for the reference):

default:
octave:4> tic; test integral2; toc
PASSES 50 out of 50 tests
Elapsed time is 3.00025 seconds.
octave:5> tic; test integral3; toc
PASSES 42 out of 42 tests
Elapsed time is 2.5638 seconds.

-O3 -mtune=native -mavx2 -flto:
octave:4> tic; test integral2 ; toc
PASSES 50 out of 50 tests
Elapsed time is 11.8607 seconds
octave:5> tic; test integral3 ; toc
PASSES 42 out of 42 tests
Elapsed time is 2.4521 seconds.

Not sure what to make out of it. The compiler gcc8.3.1 might be a little too old for the cpu (Ryzen 9).

Dmitri.