Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] support building for x86 with MSVC runtime #1168

Closed
wants to merge 19 commits into from

Conversation

rainers
Copy link
Contributor

@rainers rainers commented Oct 24, 2015

Work in progress, but made public to show there is some progress.

Needs LLVM master (3.8) and ldc-developers/druntime#35 ldc-developers/druntime#60
Exceptions work in simple cases, but combinations still cause trouble:

Open for comments...

@redstar
Copy link
Member

redstar commented Oct 24, 2015

Could you please put the LLVM 3.8 related changes in a separate PR? Then I'll merge them asap.

@rainers
Copy link
Contributor Author

rainers commented Oct 24, 2015

These are actually the changes you just merged with the other PRs. I'll rebase this to remove them here.

@@ -352,6 +352,9 @@ llvm::GlobalVariable * IrAggr::getInterfaceVtbl(BaseClass * b, bool new_instance
SET_COMDAT(thunk, gIR->module);
thunk->copyAttributesFrom(irFunc->func);

// thunks don't need exception handling themselves
thunk->setPersonalityFn(nullptr);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes a compile error for LLVM < 3.7. Is this really needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem necessary to compile druntime. Isn't it a valid optimization, though?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabled for LLVM < 3.7 now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the right solution. With LLVM < 3.7, the personality is attached to the landing pad. The thunk does not have a landing pad...

@redstar
Copy link
Member

redstar commented Oct 24, 2015

The build now fails for LLVM 3.1-3.4. You need not care about this - support for these versions is dropped in next release.

@rainers rainers force-pushed the msvc_x86 branch 2 times, most recently from 98db446 to 7b3cdf8 Compare November 8, 2015 10:31
@rainers
Copy link
Contributor Author

rainers commented Nov 8, 2015

Rebasing causes too many conflicts due to changed formatting, so I've reset this to master.

This now builds druntime with unittests (but one with exceptions in fibers), and passes all but about 10.

LLFunctionType *fty = nullptr;
if (global.params.targetTriple.isWindowsMSVCEnvironment()) {
if (!global.params.targetTriple.isArch64Bit())
fname = "__CxxFrameHandler3";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this hard-coded, and you provide that function in druntime? What happens when there is both C++ and D code in the same executable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That function is provided by the MSVC runtime; it's the Visual C++ personality function (for C++ exceptions, not SEH ones).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kinke's right. The D runtime just raises a C++ compatible exception: https://github.com/ldc-developers/druntime/blob/ldc/src/ldc/eh/win32.d

@dnadlinger
Copy link
Member

Only a couple of superficial comments – I didn't have time to look into the new EH IR constructs more closely yet, unfortunately.

llvm::StructType *BaseClassDescriptorType;
llvm::StructType *ClassHierarchyDescriptorType;
llvm::StructType *CompleteObjectLocatorType;
llvm::DenseMap<llvm::Constant*, llvm::GlobalVariable*> TypeDescriptorMap; // classInfoPtr -> typeDescriptor
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ok to have these variables as globals? These are in the context in clang IIRC, but the conext is a global in LDC anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't read through the code in any detail, but since it uses llvm::GlobalVariable, I presume this is specific to each LLVM module? In this case, it should probably go into IRState so it is appropriately reset for compilations that produce multiple object files (there is a 1:1 mapping between IRState and llvm::Module instances).

@rainers
Copy link
Contributor Author

rainers commented Nov 8, 2015

Fixed returning from the catch block. The druntime tests now freeze for 5 unittests, 3 of these also don't pass on Win64.

assert(!"MSVC/x86 exception handling not supported with LLVM < 3.8");
#endif
}
return endbb;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return value is now unneeded, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ehhh, yes.

@kinke
Copy link
Member

kinke commented Nov 8, 2015

3 of these also don't pass on Win64

What? According to AppVeyor (using LLVM 3.7), all druntime unittests pass except for a crash in core.thread (release). So are there regressions with LLVM master?

@kinke
Copy link
Member

kinke commented Nov 8, 2015

This already compiles (native x86 LDC + LLVM master build):

import core.stdc.stdio;

class MyException : Exception {
  int x;
  this(int x)  { super("MyException"); this.x = x; }
}

struct Cleanup {
  int x;
  ~this() {}
}

void may_throw(ref Cleanup c, bool doThrow) {
  printf("may_throw %d\n", c.x);
  if (doThrow) throw new MyException(c.x);
}

void may_throw() {}

void main() {
  Cleanup a = Cleanup(1);
  try {
    //Cleanup b = Cleanup(2);
    //may_throw(b, true);
    may_throw();
  } catch (MyException e) {
    printf("caught e.x %d\n", e.x);
    Cleanup c = Cleanup(3);
    may_throw(c, false);
  } catch (Exception e) {
    assert(0);
  } finally {
    may_throw(a, false);
  }
}

When uncommenting the 2 lines in the try-block, we hit an LLVM assertion (Expected BB to be monochrome!).
I don't get it to link with druntime-debug (using the -link-debuglib switch; the release builds don't get very far when the LLVM optimization passes kick in...) though:

LINK : warning LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs; use /NODEFAULTLIB:library
druntime-ldc-debug.lib(errno.c.obj) : warning LNK4217: locally defined symbol __errno imported in function _getErrno
MSVCRT.lib(_chandler4gs_.obj) : error LNK2019: unresolved external symbol __except_handler4_common referenced in function __except_handler4
eh.exe : fatal error LNK1120: 1 unresolved externals

Maybe it's really just a /MD vs. /MT issue for some C parts.

@rainers
Copy link
Contributor Author

rainers commented Nov 8, 2015

What? According to AppVeyor (using LLVM 3.7), all druntime unittests pass except for a crash in core.thread (release). So are there regressions with LLVM master?

I see two unittests in core.thread that are waiting to join threads forever. The unittest in core.sync.barrier sometimes freezes, sometimes passes.

@rainers
Copy link
Contributor Author

rainers commented Nov 8, 2015

When uncommenting the 2 lines in the try-block, we hit an LLVM assertion (Expected BB to be monochrome!).

I get that same assertion with the mentioned unittest in core.thread: https://github.com/ldc-developers/druntime/blob/ldc/src/core/thread.d#L5349

Maybe it's really just a /MD vs. /MT issue for some C parts.

I build druntime using a Visual D project. C files compile without any special options (just cl -c /EHsc /Z7). Linking is done with "/NODEFAULTLIB:libcmt libcmtd.lib" though.

Sounds like you are linking against the dynamic C runtime library. I've only tried the static library.

@kinke
Copy link
Member

kinke commented Nov 8, 2015

Yeah, I've been using LDC for compiling and linking (and Ninja for druntime-ldc-debug). It's working with

bin\ldc2 -c ..\eh.d
link eh.obj lib\druntime-ldc-debug.lib legacy_stdio_definitions.lib /NODEFAULTLIB:msvcrt /NODEFAULTLIB:libcmt libcmtd.lib

It somehow needs libcmt[d].lib for that __except_handler4_common symbol. I'm using VS 2015 btw.

@kinke
Copy link
Member

kinke commented Nov 8, 2015

Oh and btw - your contributions are very much appreciated. 👍
Hint: you may want to use the clang-format VS plugin (see http://llvm.org/builds/).

@rainers
Copy link
Contributor Author

rainers commented Nov 8, 2015

It somehow needs libcmt[d].lib for that __except_handler4_common symbol. I'm using VS 2015 btw.

It seems you need to link to vcruntime.lib too when linking with msvcrt.lib.

@rainers
Copy link
Contributor Author

rainers commented Nov 8, 2015

Oh and btw - your contributions are very much appreciated. 👍

Thanks :-)

Hint: you may want to use the clang-format VS plugin (see http://llvm.org/builds/).

I see too much clang formated code at work that looks horrible...

@dnadlinger
Copy link
Member

I see too much clang formated code at work that looks horrible...

Sure, but we just settled on using it set to the LLVM style for our code base, sorry. You may be mad at me for making that decision. ;)

I've made very positive experiences with it, by the way, plus the constant style mismatch in the LDC code base (seriously, I now know why pretty much all large projects have a style guide).

We should probably formailze this somewhere soon (LLVM coding standards minus grandfathered-in differences (lower case for variables, occasional use of <iostream> allowed).

@kinke
Copy link
Member

kinke commented Mar 11, 2016

Btw why is your .exe that much larger than mine (665k vs. 529k)? I've linked it against release druntime/Phobos and libcmt.lib (default linking with LDC)...

@kinke
Copy link
Member

kinke commented Mar 11, 2016

On my co-worker's Win10 box, both .exes don't crash, regardless of whether starting them via Windows explorer or command line, and regardless of whether && echo 0 is appended.

@smolt
Copy link
Member

smolt commented Mar 11, 2016

This all seems like a deja vu. What happens when the thread-locals are declared in C and referenced by D code?

@kinke
Copy link
Member

kinke commented Mar 11, 2016

Must be a linker bug too here. This:

#include <stdio.h>

thread_local char b = 42;
thread_local int i = 42;
thread_local char b2;
thread_local alignas(0x40) char data[16];

int main()
{
    printf("&b:   0x%p\n", &b);
    printf("&i:   0x%p\n", &i);
    printf("&b2:  0x%p\n", &b2);
    printf("data: 0x%p\n", data);
    return 0;
}

yields something like this when compiling & linking with MSVC 2015 Update 1, both with and without optimizing with /Ox (x64 and x86):

&b:   0x00000046D47D4310 (0x00CB6870)
&i:   0x00000046D47D4314 (0x00CB6874)
&b2:  0x00000046D47D4311 (0x00CB6871)
data: 0x00000046D47D4350 (0x00CB68B0)

So there is an appropriate padding inbetween i and data, but b would need to be aligned on a 64-bytes boundary too. It isn't for both x64 and x86 executables.
Dumpbin shows that the 64-bytes alignment of the COFF object's TLS section is lost in the final executable:

C:\LDC>dumpbin /section:.tls$ tlsalign.obj
Microsoft (R) COFF/PE Dumper Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file tlsalign.obj

File Type: COFF OBJECT

SECTION HEADER #3
   .tls$ name
       0 physical address
       0 virtual address
      50 size of raw data
     35A file pointer to raw data (0000035A to 000003A9)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0700040 flags
         Initialized Data
         64 byte align
         Read Write

  Summary

          50 .tls$

C:\LDC>dumpbin /section:.tls tlsalign.exe
Microsoft (R) COFF/PE Dumper Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file tlsalign.exe

File Type: EXECUTABLE IMAGE

SECTION HEADER #5
    .tls name
      91 virtual size
   1F000 virtual address (000000014001F000 to 000000014001F090)
     200 size of raw data
   1B800 file pointer to raw data (0001B800 to 0001B9FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0000040 flags
         Initialized Data
         Read Write

  Summary

        1000 .tls

@majnemer
Copy link

What if you manually added the alignment flag to .tls using editbin?

@kinke
Copy link
Member

kinke commented Mar 11, 2016

No luck even after restoring the alignment manually:

C:\LDC>editbin /section:.tls,as tlsalign.exe
Microsoft (R) COFF/PE Editor Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.


C:\LDC>tlsalign
&b:   0x0000002CD7514310
&i:   0x0000002CD7514314
&b2:  0x0000002CD7514311
data: 0x0000002CD7514350

C:\LDC>dumpbin /section:.tls tlsalign.exe
Microsoft (R) COFF/PE Dumper Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file tlsalign.exe

File Type: EXECUTABLE IMAGE

SECTION HEADER #5
    .tls name
      91 virtual size
   1F000 virtual address (000000014001F000 to 000000014001F090)
     200 size of raw data
   1B800 file pointer to raw data (0001B800 to 0001B9FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0700040 flags
         Initialized Data
         RESERVED - UNKNOWN
         RESERVED - UNKNOWN
         RESERVED - UNKNOWN
         Read Write

  Summary

        1000 .tls

Note that dumpbin doesn't display the .exe's TLS section alignment (for no other alignment either! I tried them all...). I hope that's not a general Windows/PE limitation (!).

@rainers
Copy link
Contributor Author

rainers commented Mar 11, 2016

I tried your cpp file with cl Version 19.00.23506 for x86, and link Version 14.00.23506.0, that properly aligns the data for me, too. I might just be lucky, though.

The alignment of the .tls is likely not important, because the data is copied into the TLS area anyway. So it's actually the attribute shown with dumpbin /tls that you should look at (as you did in the beginning).

I stepped through the process initialization (LdrpAllocateTls in ntdll) and it looks as if it correctly supports the alignment property of the TLS directory entry (it writes the unaligned pointer just before the start of the TLS area).

@rainers
Copy link
Contributor Author

rainers commented Mar 11, 2016

Btw why is your .exe that much larger than mine (665k vs. 529k)? I've linked it against release druntime/Phobos and libcmt.lib (default linking with LDC)...

I noticed this, too. Maybe we have slightly different build settings for the runtime lib. My path to LDC is slightly longer, too, but that should not add up to 136 kB.

@majnemer
Copy link

Ah, there is an Alignment field in IMAGE_TLS_DIRECTORY... Is the linker setting it to the right value?

@kinke
Copy link
Member

kinke commented Mar 11, 2016

Thx Rainer. Vanilla build, i.e., not modified via editbin:

C:\LDC>dumpbin /tls tlsalign.exe
Microsoft (R) COFF/PE Dumper Version 14.00.23506.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file tlsalign.exe

File Type: EXECUTABLE IMAGE

  Section contains the following TLS directory:

    000000014001F000 Start of raw data
    000000014001F090 End of raw data
    000000014001C998 Address of index
    00000001400122A0 Address of callbacks
                   0 Size of zero fill
            00700000 Characteristics
                       64 byte align

    TLS Callbacks

          Address
          ----------------
          0000000000000000

  Summary

        2000 .data
        1000 .gfids
        1000 .pdata
        A000 .rdata
        1000 .reloc
       11000 .text
        1000 .tls

Should clearly be aligned. I'll debug it later too.

I might just be lucky, though.

I can't believe I'm that unlucky. On two different machines! ;)

@kinke
Copy link
Member

kinke commented Mar 11, 2016

Do you think that may be due to an unaligned overall size?

@rainers
Copy link
Contributor Author

rainers commented Mar 11, 2016

Ah, there is an Alignment field in IMAGE_TLS_DIRECTORY... Is the linker setting it to the right value?

Looks correct according to the dumpbin output (and works on my system).

@rainers
Copy link
Contributor Author

rainers commented Mar 11, 2016

000000014001F000 Start of raw data

These adresses look unusual large (also the addresses printed at runtime). Do you change the default base address for the exe (/BASE)? It's 0400000h with my x64 builds.

Do you think that may be due to the TLS section having an unaligned overall size?

I don't think so.

Maybe there is something in your syystem hooked up to the allocation/process initialization that actually changes the base address (virus/anti-virus/debug-allocator).
Another option might be that your (outdated?) version of ntdll misbehaves. If you can provide a full process dump, I might be able to compare the TLS allocation in ntdll (it's version 6.3.9600.18202 on my system for x86).

@kinke
Copy link
Member

kinke commented Mar 12, 2016

I've just decided it was time to finally make the move to Windows 10. So here you go, problem solved, everything's aligned now using the identical executables. So it may have really been an outdated ntdll (pre-8.1). My Win7 at work isn't that outdated (lagging a few months behind, company policy...), but has also shown this bug. Pretty lame.

Do you change the default base address for the exe (/BASE)?

Nope. I've just compiled and linked it with cl tlsalign.cpp. ;)

@kinke
Copy link
Member

kinke commented Mar 12, 2016

86f5ecb enables Win32 LDC builds again. This means that adding 32-bit AppVeyor jobs will be a breeze.

@dnadlinger
Copy link
Member

We should make sure these TLS issues are documented somewhere (wiki, etc.) we can easily point users to.

@kinke kinke mentioned this pull request Mar 12, 2016
@rainers
Copy link
Contributor Author

rainers commented Mar 13, 2016

I've just decided it was time to finally make the move to Windows 10. So here you go, problem solved, everything's aligned now using the identical executables.

Makes me wonder whether users will hit that issue, too. D code is likely to make more use of thread local data than C++. I checked the definition of IMAGE_TLS_DIRECTORY in the SDKs, and the characteristics field only got the union definition containing the alignment in the Win8.1 SDK. I wonder if an implementation might have been added just then? Actually I did not fnd a documentation of the characteristics field at all, it surely isn't used by this code: http://www.nynaeve.net/Code/VistaImplicitTls.cpp

@kinke Have you been using Win 8.0 or 8.1?

@smolt
Copy link
Member

smolt commented Mar 13, 2016

D code is likely to make more use of thread local data than C++.

That, and more exceptions than C++, at least in phobos unittests, with some big stack frames. There are a collection of cases with either LLVM or OS X toolchain (ld, dyld) where LDC hit an unreported bug in these areas.

@kinke
Copy link
Member

kinke commented Mar 13, 2016

Have you been using Win 8.0 or 8.1?

8.0.

I wonder if an implementation might have been added just then?

I'm struggling to believe that, but it all points in that direction - it didn't work on a more or less up-to-date Win7 either.

So the general recommendation for MSVC users would be to use bleeding-edge stuff only - Windows, VS, LLVM, LDC. :)

@kinke kinke closed this Mar 13, 2016
@kinke kinke reopened this Mar 13, 2016
@rainers
Copy link
Contributor Author

rainers commented Mar 13, 2016

I'm struggling to believe that, but it all points in that direction - it didn't work on a more or less up-to-date Win7 either.

I got hold of an old Win8.0 and a recent Windows7 version of ntdll.dll: they both don't have the same code as my current win8.1 DLL for aligning TLS memory. While I wouldn't care for Win8.0 too much, Windows 7 is still rather popular. I think this could be a rather troublesome issue for clang, too.

Is there a way to disable optimizations that rely on alignment of TLS? Alternatively, we could verify TLS base address alignment at startup and issue a warning.

@kinke
Copy link
Member

kinke commented Mar 13, 2016

Is there a way to disable optimizations that rely on alignment of TLS? Alternatively, we could verify TLS base address alignment at startup and issue a warning.

The latter seems reasonable to me. Even without compiler optimizations, a TLS global of a type T with explicit alignment should be aligned appropriately, so that manual optimizations (cache-line, SIMD etc.) work as expected.

@dnadlinger
Copy link
Member

Yes, at least issuing a warning seems to be reasonable, given how popular Windows 7 still is. Wouldn't DMD run into this as well? IIRC it only uses emulated TLS on OS X?

@rainers
Copy link
Contributor Author

rainers commented Mar 14, 2016

Yes, at least issuing a warning seems to be reasonable, given how popular Windows 7 still is.

IMO a warning might not be good enough. I'll have a look at fixing it at startup.

Wouldn't DMD run into this as well?

With optlink, it has no idea about aligning TLS at all. For COFF builds, it always sets 16-byte alignment. Obviously this can also fail for 32-bit (default alignment is size of 2 pointers), but dmd doesn't have optimizations that take advantage of alignment. So you don't hit it with the standard lib.

@dnadlinger
Copy link
Member

IMO a warning might not be good enough. I'll have a look at fixing it at startup.

Hmm, are you thinking about manually moving the TLS block and setting the address in the TIB appropriately? That might just work, though I'm not sure where else Windows might store that address.

@rainers
Copy link
Contributor Author

rainers commented Mar 17, 2016

Hmm, are you thinking about manually moving the TLS block and setting the address in the TIB appropriately? That might just work, though I'm not sure where else Windows might store that address.

I don't think it stores it elsewhere, too. This seems to work ldc-developers/druntime#66

@kinke
Copy link
Member

kinke commented Mar 19, 2016

Finally merged as part of #1354 (rebased to fix merge conflicts, that's why this PR doesn't show up as merged).

@kinke kinke closed this Mar 19, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants