Skip to content

IRGen: ensure that helper functions are COMDAT'ed #20059

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

Merged
merged 1 commit into from
Nov 6, 2018

Conversation

compnerd
Copy link
Member

The linkonce_odr semantics in LLVM have changed. They no longer create
a COMDAT group. This means that the helper functions that we are
generating inline globally are not collected and uniqued. This results
in multiple definitions, which is a hard error in PE/COFF. ELF would
get away with it due to the weak linkage semantics that it provides.
Explicitly mark the helpers for COMDAT. This should enable building the
swift SDK overlay for libdispatch on Windows again.

Replace this paragraph with a description of your changes and rationale. Provide links to external references/discussions if appropriate.

Resolves SR-NNNN.

@compnerd
Copy link
Member Author

CC: @aschwaighofer
@eeckstein, @gottesmm you might care about this as this impacts size (on non-MachO though)

Yes, I know, I should go clean up the other cases, but this is sufficient to unblock me. I'll try to get to the other cases in a follow up.

@compnerd
Copy link
Member Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 1cc2e25f7028a4aea487969c5165da9f8c03fa30

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 1cc2e25f7028a4aea487969c5165da9f8c03fa30

@eeckstein
Copy link
Contributor

Fine with me

// CHECK: define linkonce_odr hidden i8* @__swift_memcpy4097_8(i8*, i8*, %swift.type*)
// CHECK-COMDAT: define linkonce_odr hidden i8* @__swift_memcpy4097_8(i8*, i8*, %swift.type*)
// CHECK-COMDAT-SAME: comdat
// CHECK-NOCOMDAT-NOT:comdat
Copy link
Contributor

Choose a reason for hiding this comment

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

This will only check that "comdat" isn't written here, not that it isn't written at all. Did you mean to leave the first line as CHECK instead of CHECK-COMDAT?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops, yeah, the first line should be CHECK.

@compnerd
Copy link
Member Author

This uncovered a bug in LLVM (dwarfdump was being too aggressive about the verification on object files, I've fixed that in LLVM and will pull that into the stable branch).

@compnerd compnerd force-pushed the comdat-helpers branch 2 times, most recently from c72ea22 to 649f6d3 Compare October 28, 2018 10:21
@compnerd
Copy link
Member Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 1cc2e25f7028a4aea487969c5165da9f8c03fa30

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 1cc2e25f7028a4aea487969c5165da9f8c03fa30

@compnerd
Copy link
Member Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 649f6d38cb052f10c402e064a6a51c981704c3ad

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 649f6d38cb052f10c402e064a6a51c981704c3ad

@compnerd
Copy link
Member Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 2556e2ccd4e578af8f8d54aff3b04d2ba027742a

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 2556e2ccd4e578af8f8d54aff3b04d2ba027742a

@rjmccall
Copy link
Contributor

@compnerd Can you point out where this change happened in LLVM? It wasn't well-communicated.

@jckarter
Copy link
Contributor

Are you sure this was an intentional LLVM change? It seems like a pretty severe break if linkonce_odr doesn't guarantee coalescing by itself anymore.

@compnerd
Copy link
Member Author

@rjmccall, @jckarter - yes, this was intentional :-( ... I just have it on what Reid Klecker mentioned to me. @jckarter - it still can coalesce on ELF - because of weak symbols. This definitely does not work on COFF where you do not have weak symbols. By creating the COMDAT group we ensure that there is a single definition.

@compnerd
Copy link
Member Author

CC: @rnk

@gottesmm
Copy link
Contributor

@rnk Do you remember what changed here?

@rnk
Copy link

rnk commented Oct 29, 2018

Yeah, like everything. You gotta use comdats now if you want comdats. That happened back in 2014. It looks like the final change was r226467, so I guess 2015. Unfortunately, I have not been able to find a good llvm-dev discussion summarizing the change, but I remember there was a lot of discussion.

You know, the plans to bulldoze linkonce_odr semantics were on display down at the planning office for months. =P

@compnerd
Copy link
Member Author

😢

Failing Tests (5):
Swift(linux-x86_64) :: Reflection/typeref_decoding_asan.swift
Swift(linux-x86_64) :: Reflection/typeref_decoding.swift
Swift(linux-x86_64) :: Reflection/typeref_lowering.swift
Swift(linux-x86_64) :: Reflection/typeref_decoding_imported.swift
Swift(linux-x86_64) :: DebugInfo/local-vars.swift.gyb

The DebugInfo failure is due to the DIE check being invalid. That was relaxed in LLVM (by me). I'm not sure why the Reflection tests are failing after this change.

@rjmccall
Copy link
Contributor

I feel like the old semantics were a useful common core across all targets, which is all that was necessary for the vast majority of frontend use cases. But PE/COFF does some other things in IR pointlessly differently, so I guess this is of a piece.

@compnerd
Copy link
Member Author

compnerd commented Nov 2, 2018

CC: @slavapestov

@compnerd
Copy link
Member Author

compnerd commented Nov 4, 2018

sigh okay, binaries built with lld work but fail with gold? This is starting to smell like a linker issue :-(. I am amused at the number of linker issues we are finding with swift code.

@jckarter
Copy link
Contributor

jckarter commented Nov 4, 2018

Alas, the more you deviate from what C compilers do, the more linker breakage you overturn. Can we stick to using COMDAT only for COFF, and continue to rely on weak definitions on ELF?

@compnerd
Copy link
Member Author

compnerd commented Nov 4, 2018

@jckarter that definitely is what I have in mind, but this does come at a cost, we should definitely consider this. We already know that the BFD linker doesn't work for swift. Perhaps it is time to switch from gold to lld.

With COMDAT:
text data bss dec hex filename
5252178 328781 128424 5709383 571e47 libswiftCore.so

Weak:
text data bss dec hex filename
5405803 175220 128424 5709447 571e87 libswiftCore.so

@jckarter
Copy link
Contributor

jckarter commented Nov 4, 2018

We dropped BFD support because they broke protected linkage in favor of copy relocations, and don't seem interested in fixing it (https://www.sourceware.org/ml/binutils/2016-03/msg00312.html). gold would probably be receptive to fixing bugs in their comdat handling. The code size win is nice, but I think we should fix the immediate issue with Windows support first without regressing Linux support in the meantime.

In order to handle LinkOnceODR semantics correctly across various object
formats, introduce a new helper ApplyIRLinkage.  This abstracts the need
to create a COMDAT group and set it on the GlobalValue.  Adjust all
sites where we set the IR linkage attributes to use this mechanism
instead to avoid having to track down symbols not being added to a
COMDAT group.
@compnerd
Copy link
Member Author

compnerd commented Nov 4, 2018

@swift-ci please test

@swift-ci
Copy link
Contributor

swift-ci commented Nov 4, 2018

Build failed
Swift Test OS X Platform
Git Sha - c4e6514e17fbb5eedd7c3b259f545a61f2aa0f71

@swift-ci
Copy link
Contributor

swift-ci commented Nov 4, 2018

Build failed
Swift Test Linux Platform
Git Sha - c4e6514e17fbb5eedd7c3b259f545a61f2aa0f71

@rnk
Copy link

rnk commented Nov 5, 2018

Alas, the more you deviate from what C compilers do, the more linker breakage you overturn. Can we stick to using COMDAT only for COFF, and continue to rely on weak definitions on ELF?

Uh, no, C++ compilers use comdat and weak on ELF. I would suggest setting both comdat and linkonce_odr linkage everywhere to more closely match what clang does.

I feel like the old semantics were a useful common core across all targets, which is all that was necessary for the vast majority of frontend use cases. But PE/COFF does some other things in IR pointlessly differently, so I guess this is of a piece.

There were nice things about the old way of doing things, but as usual, people wanted to access lower-level features that LLVM was hiding from them. This isn't PE/COFF being weirdly different for its own sake, though. ELF has comdats too, and you need to use them if you want to generate vague linkage things the same way that clang does.

Here's what the main object file formats support (ignoring wasm):
macho: has weak
elf: has weak, has comdats
pe/coff: has only comdats, but GCC implements attribute((weak)) with stubs if you're game (not appropriate for the use case of vague linkage)

All of the weak, linkonce, and ODR linkages used to enable weak and comdat, depending on what the object file format supported. For ELF, weak and comdat are kind of redundant. If all symbol definitions are weak, the first one will prevail, otherwise the one and only strong one prevails. Adding a comdat group basically does a first pass to drop duplicate symbols before doing that weak vs. strong resolution.

@jckarter
Copy link
Contributor

jckarter commented Nov 5, 2018

Alas, the more you deviate from what C compilers do, the more linker breakage you overturn. Can we stick to using COMDAT only for COFF, and continue to rely on weak definitions on ELF?

Uh, no, C++ compilers use comdat and weak on ELF. I would suggest setting both comdat and linkonce_odr linkage everywhere to more closely match what clang does.

I'm aware of that, but there could be interactions with comdat and other things Swift does that C doesn't that gold doesn't handle correctly. (Particularly, we've had persistent problems with ld64 on Darwin not handling subtractions for relative references correctly with symbols in coalesced sections; doesn't seem totally out of the question gold might have similar latent phase-order bugs.)

@compnerd
Copy link
Member Author

compnerd commented Nov 6, 2018

@swift-ci please clean test macOS platform

@compnerd
Copy link
Member Author

compnerd commented Nov 6, 2018

I've filed SR-9197 to track the ELF side of things. This currently is limited to COFF for the COMDAT group construction. We should work out the bug in gold and enable this for ELF too.

@compnerd
Copy link
Member Author

compnerd commented Nov 6, 2018

@jckarter @rjmccall - okay to merge?

alias->setVisibility(link.getVisibility());
alias->setDLLStorageClass(link.getDLLStorage());
ApplyIRLinkage({link.getLinkage(), link.getVisibility(), link.getDLLStorage()})
.to(alias);
Copy link
Contributor

Choose a reason for hiding this comment

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

This pattern — repeated over and over again — where we create a declaration and then immediately override half the values from it with the values of an IRLinkage is quite unfortunate. I like the idea of encapsulating IRLinkage; perhaps it ought to have functions on it to actually create these globals?

Copy link
Member Author

Choose a reason for hiding this comment

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

I disliked that we were setting the properties for the linkage everywhere but didn't manage to do it very uniformly. So, the application of the IRLinkage at least ensures we set the properties more uniformly. But, I can try to sink this into createVariable and createFunction. I'm happy to try to do that in a follow up.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm fine with doing it in multiple stages. Some other suggestions:

  • As mentioned, please add functions to create variables and functions.
  • Please look around IRGen for other places that create variables and functions that could use this. LLVM's API for creating globals is really a mess, so it shouldn't be hard to create something that's friendlier and therefore encourages people to use it (and thus implicitly get the correctness benefits).
  • Please add static factories (like shared() or internal()) for the common patterns.
  • Consider incorporating unnamed_addr into this, and maybe an optional section name. But constant probably ought to be a mandatory argument to the function to create a global, not part of the linkage.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, unnamed_addr and dso_local are other attributes that I think that we should grow into this. But, this unblocks Windows builds and improves things.

@compnerd compnerd merged commit 24b86d8 into swiftlang:master Nov 6, 2018
@compnerd compnerd deleted the comdat-helpers branch November 6, 2018 16:53
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.

8 participants