www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.ldc - MinGW-w64 runtime library inclusion (Clang-compiled) for proper Win64

reply "kink" <noone nowhere.com> writes:
Hi guys,

first of all, a big fat thank-you to David & Kai for your 
dedication, it's looking good and getting better!

I recently synced the LDC sources, compiled it on my Win8 x64 
system using MSVC 2012 and latest LLVM 3.4 sources and ran a few 
tests (the whole procedure has gotten even less painful, thank 
you very much Kai!). I soon figured out that printing reals still 
results in garbage, e.g.:

<code>
import std.stdio: writefln;
import std.conv:  to;

int main()
{
     real r = 2;
     writefln("%%g (double): %g", cast(double) r);
     writefln("%%g:          %g", r);
     string s = to!string(r);
     writefln("to!string:   %s", s);

     s = "2.0";
     r = to!real(s);
     writefln("%s parsed as %g", s, cast(double) r);

     return 0;
}
</code>

outputs something like:

%g (double): 2
%g:          3.95253e-323
to!string:   4.94066e-324
2.0 parsed as 2

The problem is that the underlying calls to MSVCRT's snprintf() 
pass reals as unsupported 80-bit 'long doubles' (MSVCRT doesn't 
support this type at all), and yes, I know the Wiki page mentions 
broken reals support. ;)

So I was wondering about how to tackle this task, considering 
Phobos' std.math strongly encourages using the real type. It's 
nice that we exploit LLVM intrinsics, LLVM assembly, D assembly 
and D workarounds in LDC, DRuntime and Phobos to circumvent some 
issues caused by Microsoft's shameful missing C99 support in 
their runtime (in general, math.h just being an example). But as 
you know, there are still missing functions (=> linking issues), 
broken implementations and bugs.

My idea was to include the MinGW(-w64) runtime library, since 
afaik MinGW mainly addresses exactly Microsoft's lacking 
compatibility. I really like LDC's ability to output 
MSVC-compatible objects and the usage of the MS linker, i.e. the 
pretty smooth integration into the Visual-Studio-ecosystem, and 
don't want to use LDC based on MinGW(-w64) and its object-file 
format.

I first tried to compile a few modules of the MinGW-w64-CRT 
source using VC 2012. Obviously loads of issues with incompatible 
keywords, assembly syntax etc. arised and the 'long double' type 
simply maps to 'double' (you may very well call that cheating), 
so there's no way to get that to work unless coding everything in 
naked assembly.

Then I tried to compile a few modules with Clang (latest source & 
latest LLVM, compiled painlessly with MSVC 2012 as well). It 
seems to swallow MinGW's code quite well - nice. I succeeded in 
linking a dummy DLL (compiled with Clang and linked with MSVCRT; 
Clang is supposed to have troubles with Microsoft's C++ headers, 
but the C headers should work) to a D executable (compiled with 
LDC, of course). So imho with MinGW's runtime (or a trimmed-down 
version thereof) as additional (default?) static/dynamic library 
we'd have a nice fallback for missing functions.

Unfortunately, there are ABI issues, even a minimalistic

__declspec(dllexport) int foo(int a) { return a + 3; }

in the dummy library returns garbage in x64 mode, in both LDC and 
MSVC executables - but works in x86 mode, at least when called 
from a dummy MSVC executable (my LDC is on a strict diet and is 
allowed to spit out x64 code only). So Clang apparently doesn't 
support the Win64 ABI yet, *sigh*.

Anyway, I'd like to know what you think about including a (most 
likely tailored) MinGW runtime library, compiled with Clang, with 
LDC for Windows to obtain max compatibility.
Jun 03 2013
next sibling parent "kink" <noone nowhere.com> writes:
 %g (double): 2
 %g:          3.95253e-323
 to!string:   4.94066e-324
 2.0 parsed as 2
Btw, DMD v2.063 prints similar garbage in x64 mode, just in case you were wondering. The huge amount of work required to circumvent all MSVCRT shortcomings in DRuntime and Phobos is probably why we won't see official Win64 support in DMD anytime soon, if ever.
Jun 03 2013
prev sibling next sibling parent "kink" <noone nowhere.com> writes:
PS: For those who don't know: the MinGW C runtime is just an 
additional layer on top of Microsoft's Visual C Runtime, adding 
missing functionality. What we need would ideally be a clean D 
port of missing and relevant MinGW-runtime parts into 
DRuntime/Phobos, for both DMD and LDC on Win64. Since that's 
quite a task and especially because it is only needed for a 
single, yet the most popular OS, my proposal is to include 
relevant parts of the MinGW runtime layer directly with the Win64 
D compiler, so that we have a solid fallback for the time being.
Jun 03 2013
prev sibling next sibling parent "kink" <noone nowhere.com> writes:
 Unfortunately, there are ABI issues, even a minimalistic

 __declspec(dllexport) int foo(int a) { return a + 3; }

 in the dummy library returns garbage in x64 mode.
Discard that, I was erroneously using a Clang 3.3 build. It works using a very recent 3.4 build. A similar dummy function using "long double" doesn't work though, but that's not really surprising. Once these ABI issues are fixed, I think the main task would consist in emulating all included MinGW C system headers (not present or incomplete in MSVC) by custom ones and getting rid of superfluous modules - I guess that'd be a manageable effort.
Jun 06 2013
prev sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
Hi,

Sorry for my late response, but I haven't really looked at Win64 
support in LDC at all, apart from some playing around with SEH 
quite some while back. Thus, I was hoping that Kai would answer 
first – maybe he has already something up the sleeves in this 
area?

Anyway, I'd strongly argue that any such code should be shared 
between any D compilers targeting Win64/MSCRT, as you mentioned 
as well. And the current stance of pretty much everybody on the 
druntime/Phobos core team is that all code has to be 
Boost-licensed (or similar) to be considered for inclusion in the 
current

I haven't checked what the exact licensing situation of the MinGW 
runtime is (apart from some parts which I know are public domain) 
but I imagine there might be potential problems in this area. 
Thus, I'd encourage you to primarily discuss this on the upstream 
(druntime/Phobos) development lists.

David
Jun 12 2013
parent reply "kink" <noone nowhere.com> writes:
 I haven't checked what the exact licensing situation of the 
 MinGW runtime is (apart from some parts which I know are public 
 domain) but I imagine there might be potential problems in this 
 area.
Hi David, from what I've seen, the MinGW-w64 CRT seems to be entirely public domain. I got the whole thing (latest v2.0.8) to compile; a few linking issues remain, working these out wouldn't be a problem. I chose to use the MinGW-w64 headers exclusively (no MS headers) to keep things simple and basically only had to adapt a few headers in a few places after I let Clang 3.4 run in a GCC-compatible way (cmdline options: -c -nostdinc -target x86_64-pc-win32 -Xclang -cxx-abi -Xclang microsoft -fno-ms-compatibility -fmsc-version=0). The thing is that Clang maps C "long double" to double as well, just like MSVC. I'm not sure if that's generally the case when targeting Windows or only if Clang itself has been compiled with MSVC and not with MinGW. GCC's __float80 does not seem to be supported either. So I think there's probably a good reason for the dropped x87 support; I don't know about how well it is still supported in x86 hardware nowadays and how slow it is compared to scalar SSE code. So maybe the best course of action would be to map D's real type to double as well, either as default setting in LDC for Windows or at least as option. I wouldn't miss the x87 type; I just want working reals support as it's used in Phobos' std.math all over the place.
Jun 14 2013
next sibling parent "kink" <noone nowhere.com> writes:
 Hi David, from what I've seen, the MinGW-w64 CRT seems to be 
 entirely public domain.
Damn, I should have checked first before making such a statement; e.g., there are modules under the Zope Public License, copyrights by Sun, Lucent etc.
Jun 14 2013
prev sibling parent reply "kink" <noone nowhere.com> writes:
 The thing is that Clang maps C "long double" to double as well, 
 just like MSVC. I'm not sure if that's generally the case when 
 targeting Windows or only if Clang itself has been compiled 
 with MSVC and not with MinGW.
I've just looked into the Clang sources; the "long double" mapping depends on the target triple, in this case specifically the OS component. "win32" specifies a Visual Studio target, for which "long double" is remapped to double (doesn't happen for the corresponding MinGW target). I commented out the remapping in the constructor of the VisualStudioWindowsX86_64TargetInfo class; the "long double" x87 type is now supported. A dummy function __declspec(dllexport) long double foo(long double x) { return x + 2.5; } is now compiled to (dumpbin /disasm) push rbp mov rbp,rsp sub rsp,10h fld tbyte ptr [rbp+30h] fld st(0) fstp tbyte ptr [rbp-10h] fld dword ptr [180005698h] faddp st(1),st add rsp,10h pop rbp ret which looks quite okay. Calling it from an LDC-compiled exe still doesn't work though (returns NaN, probably an ABI issue). Still, it's an improvement. ;)
Jun 15 2013
parent reply "kink" <noone nowhere.com> writes:
Of course it is an ABI issue. Since MS doesn't support the x87 
type, it obviously isn't mentioned in the Win64 ABI docs. Clang's 
ABI assumes it is passed by-val on the stack whereas in LDC we 
treat it as a struct > 64 bits and hence pass it by-ref (a 
pointer to a private copy for the callee, allocated by the 
caller; the Win64 way of passing larger structs by-val). Clang 
returns an x87 in the ST(0) x87 register and LDC expects the 
result there - perfect. I guess I'll have to modify Clang/LLVM 
further to pass x87s by-ref, that seems more appropriate to me.
This shouldn't be a problem for Win32 though as x87s should be 
passed ordinarily by-val.
Jun 15 2013
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
kink:

 This shouldn't be a problem for Win32 though as x87s should be 
 passed ordinarily by-val.
See the D ABI: http://dlang.org/abi.html Bye, bearophile
Jun 15 2013
parent reply "David Nadlinger" <code klickverbot.at> writes:
On Saturday, 15 June 2013 at 21:37:51 UTC, bearophile wrote:
 kink:

 This shouldn't be a problem for Win32 though as x87s should be 
 passed ordinarily by-val.
See the D ABI: http://dlang.org/abi.html
… which doesn't fully apply to LDC. ;) David
Jun 15 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
David Nadlinger:

 … which doesn't fully apply to LDC. ;)
What's the point of not following the ABI of D reals in LDC? Bye, bearophile
Jun 15 2013
parent reply "David Nadlinger" <code klickverbot.at> writes:
On Saturday, 15 June 2013 at 22:39:17 UTC, bearophile wrote:
 David Nadlinger:

 … which doesn't fully apply to LDC. ;)
What's the point of not following the ABI of D reals in LDC?
The question should be: What's the point of DMD not following the standard C ABI on Win32? ;) In the case of reals, simple things should work, but DMD packs together 80-bit reals end-to-end (i.e. 10 byte spacing), whereas LLVM chooses 12 byte alignment. David
Jun 15 2013
parent "kink" <noone nowhere.com> writes:
 In the case of reals, simple things should work, but DMD packs 
 together 80-bit reals end-to-end (i.e. 10 byte spacing), 
 whereas LLVM chooses 12 byte alignment.
For 32-bit LLVM targets that is; sizeof(long double) == 16 bytes for the Win64 targets, so that the required 8-bytes alignment can be enforced.
Jun 16 2013
prev sibling parent reply "kink" <noone nowhere.com> writes:
Okay, I finally got the x87 argument passing to work between 
Clang and LDC after inserting a few lines into Clang's 
WinX86_64ABIInfo::classify method. Calling foo(666) in D returns 
668.5, as expected.
As soon as I've fixed the remaining linking issues for MinGW-w64 
CRT (including most modules and most likely all relevant ones), 
I'll post the required patches for Clang and the CRT so that 
others can experiment too.
Jun 16 2013
next sibling parent "kink" <noone nowhere.com> writes:
I've got to say that I'm not keen on working further on this. I 
got rid of the linking issues, but the library is surely not 100% 
functional. My changes to the MinGW-w64 CRT are a messy hack, and 
that's because the codebase itself is a mess, very ugly and 
partially from the 80s and 90s of the last century.

I'd really suggest mapping the real type to the double type for 
Windows targets, ideally in all D compilers of course.

Here's at least the tiny patch for Clang to enable 80-bit long 
double support on Win64:

diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp
index 4968938..f6ac502 100644
--- a/lib/Basic/Targets.cpp
+++ b/lib/Basic/Targets.cpp
   -3229,8 +3229,8    class VisualStudioWindowsX86_64TargetInfo : 
public WindowsX86_64TargetInfo {
  public:
    VisualStudioWindowsX86_64TargetInfo(const std::string& triple)
      : WindowsX86_64TargetInfo(triple) {
-    LongDoubleWidth = LongDoubleAlign = 64;
-    LongDoubleFormat = &llvm::APFloat::IEEEdouble;
+    //LongDoubleWidth = LongDoubleAlign = 64;
+    //LongDoubleFormat = &llvm::APFloat::IEEEdouble;
    }
    virtual void getTargetDefines(const LangOptions &Opts,
                                  MacroBuilder &Builder) const {
diff --git a/lib/CodeGen/TargetInfo.cpp 
b/lib/CodeGen/TargetInfo.cpp
index 4fa0c3b..9fb00a5 100644
--- a/lib/CodeGen/TargetInfo.cpp
+++ b/lib/CodeGen/TargetInfo.cpp
   -2584,6 +2584,11    ABIArgInfo 
WinX86_64ABIInfo::classify(QualType Ty, bool IsReturnType) const {
    if (Ty->isPromotableIntegerType())
      return ABIArgInfo::getExtend();

+  if (!IsReturnType &&                                        // 
regular function parameter
+      Ty->isRealFloatingType() && Size > 64 &&                // 
non-complex x87 type (80 bits)
+      getTarget().getTriple().getOS() == llvm::Triple::Win32) // 
Win64 Visual Studio target
+    return ABIArgInfo::getIndirect(0, /*ByVal=*/false);       // 
TODO: is that the right ByVal setting?
+
    return ABIArgInfo::getDirect();
  }
Jun 22 2013
prev sibling parent "kink" <noone nowhere.com> writes:
Hey guys,

I didn't want to let my work be in vain - here's the tailored 
MinGW-w64 CRT:

https://github.com/kinke/mingw-w64-crt

The Clang patch is included. I cleaned up my changes and the 
whole source tree; I think the result is not extremely ugly and 
most of the code should work. ;)
Jun 29 2013