skeeto / w64devkit

Portable C and C++ Development Kit for x64 (and x86) Windows
The Unlicense
2.66k stars 185 forks source link

How to compile x86 binary on x64 machine #122

Open sergeevabc opened 2 months ago

sergeevabc commented 2 months ago

Dear @skeeto, The following makefile compiles x64 binary on Windows 7 x64 as expected. However, make release-win32 throws an error, because w64devkit has no i686-w64-mingw32-gcc.exe. Is there another way to compile x86 binary in this case?

sergeevabc commented 2 months ago

It seems that the easiest option is to use w64devkit-i686-*.zip

skeeto commented 2 months ago

It seems that the easiest option is to use w64devkit-i686-*.zip

Yup, that's the right answer. The 32-bit and 64-bit toolchains are separate, so there's no, say, -m32 option, and especially not an arch-prefixed toolchain.

sergeevabc commented 2 months ago

Christopher, when I run make under w64devkit-i686's shell, x86 binary is compiled with options for x64. And only with make release-win32 command, I get x86 binary with options for x86. Could you take a look at the mentioned makefile and tell whether it is possible to get full-fledged x86 binary with make under w64devkit-i686?

skeeto commented 2 months ago

In that Makefile the default target uses "gcc" with a generic set of options. For w64devkit that's what you want, so this will just work whether it's 32-bit or 64-bit. So you already have your wish. Just make sure you clean between 32-bit and 64-bit builds.

release-win64 only adds "-march=x86-64" which is redundant. It doesn't change anything from the default target.

release-win32 targets pentium3, but w64devkit already requires and expects at least pentium4 (for SSE2). The pentium3 options will only slow it down, but won't actually make it compatible with pentium3 because runtime bits are compiled for pentium4. It also adds "-mfpmath=sse" which will make it prefer SSE over x87 slightly more often. It should already use SSE where it actually matters, so I expect no practical differences.

(You know, I should make 32-bit w64devkit to default to fpmath=sse…)

sergeevabc commented 2 months ago

In that Makefile the default target uses "gcc" with a generic set of options. For w64devkit that's what you want, so this will just work whether it's 32-bit or 64-bit. So you already have your wish.

Let me shed some light here, Christopher. hmp3 is an MP3 encoder that enjoyed success in the days before the ascent of LAME. In 2005, the original team abandoned the project, and now audio archaeologists from HydrogenAudio forum dust it off and modernize it. At the same time, it is necessary to compile this program not only for more or less modern x64 computers, but for x86 computers as well (e.g. imagine some users from Cuba, Palestine and Tibet). The developers also claim that some assembler code (within sources) is used for x86, whereas its C equivalent is used for x64. Therefore, I am worried that generic make produces x86 suitable for Windows 7 x86, but, say, not for Windows XP. For example, it has no -m32 flag.

The pentium3 options will only slow it down, but won't actually make it compatible with pentium3 because runtime bits are compiled for pentium4.

Do I understand correctly that Pentium3 should be replaced with Pentium4 as the lowest supported platform? Is it impossible to compile for Pentium3 at all, or is this a limitation of the components (GCC?) used in w64devkit?

By the way, why doesn't w64devkit allow us to use -flto optimization flag?

skeeto commented 2 months ago

The binary produced by the x86 toolchain (w64devkit-i686) will likely work on Windows XP. It's on the application to avoid Win32 features unavailable on XP, but otherwise the toolchain won't use such features. After all, the toolchain bootstrapped itself, and it definitely runs well on Windows XP.

However, the host CPU must support SSE2, i.e. Pentium 4 or later, because I've configured the x86 toolchain to assume it's available. That includes nearly all x86 CPUs produced within the last 20 years. Normally GCC is conservative and won't emit SSE instructions unless specifically requested (as the hmp3 Makefile does). But the performance delta between x87 and SSE2 is so enormous, and SSE2 so old, that I've enabled it by default in the x86 toolchain.

The consequence is that in the typical case you can't really turn it "off" and produce binaries that run on a Pentium 3. Parts of the toolchain are statically linked into the binary, and they've already been compiled for SSE2. It's possible to not link these runtime objects, but the program must be written with this in mind — some programs included in w64devkit are written that way — but I'm sure hmp3 is not.

So if you must target Pentium 3, then you cannot use the x86 toolchain I distribute, sorry. If it's that important, then you can build your own w64devkit without this configuration. It's quite easy, and you only need Docker: Edit the lines with "pentium4" in src/variant-i686.patch, then run "./multibuild.sh -4". Tens of minutes later you have a w64devkit-i686.zip that can run on and target older hardware.

If you use w64devkit as I distribute it, then you might just need to tell users that the x86 hmp3.exe requires at least a Pentium 4.

The developers also claim that some assembler code (within sources) is used for x86, whereas its C equivalent is used for x64.

The Makefile makes no use of those assembly files. Nor could it because it's MASM assembly, not GAS, and so requires MSVC (see the .vcxproj file). With GCC, x86 and x64 builds from the same C and C++ sources, which is why it doesn't matter that you use "release-win32". In fact, you can skip the Makefile entirely:

$ cd hmp3/src $ cc -O3 -DIEEE_FLOAT -Ipub test/tomp3.cpp -o hmp3 .c -lstdc++

why doesn't w64devkit allow us to use -flto optimization flag?

I've disabled it because, in my experience, the benefits are miniscule, it's kind of broken, and its mere presence has some negative effects. You can change that, too, with a custom-built w64devkit: In Dockerfile delete "--disable-lto".