Undo Force Load + Incremental Ban on Darwin Platforms

Gather 'round to hear tell of the saga of autolinking in incremental
mode.

In the beginning, there was Swift code, and there was Objective-C code.
To make one import bind two languages, a twinned Swift module named the same as an
Objective-C module could be imported as an overlay. But all was not
well, for an overlay could be created which had no Swift content, yet
required Swift symbols. And there was much wailing and gnashing of teeth
as loaders everywhere disregarded loading these content-less Swift
libraries.

So, a solution was found - a magical symbol _swift_FORCE_LOAD_$_<MODULE>
that forced the loaders to heed the dependency on a Swift library
regardless of its content. It was a constant with common linkage, and it
was good. But, along came COFF which needed to support autolinking but
had no support for such matters. It did, however, have support for
COMDAT sections into which we placed the symbol. Immediately, a darkness
fell across the land as the windows linker loudly proclaimed it had
discovered a contradiction: "_swift_FORCE_LOAD_$_<MODULE> cannot be
a constant!", it said, gratingly, "for this value requires rebasing."
Undeterred, we switched to a function instead, and the windows linker
happily added a level of indirection to its symbol resolution procedure
and all was right with the world.

But this definition was not all right. In order to support multiple
translation units emitting it, and to prevent the linker from dead
stripping it, Weak ODR linkage was used. Weak ODR linkage has the nasty
side effect of pessimizing load times since the dynamic linker must
assume that loading a later library could produce a more definitive
definition for the symbol.

A compromise was drawn up: To keep load times low, external linkage was
used. To keep the linker from complaining about multiple strong
definitions for the same symbol, the first translation unit in the
module was nominated to recieve the magic symbol. But one final problem
remained:

Incremental builds allow for files to be added or removed during the
build procedure. The placement of the symbol was therefore dependent
entirely upon the order of files passed at the command line. This was no
good, so a decree was set forth that using -autolink-force-load and
-incremental together was a criminal offense.

So we must compromise once more: Return to a symbol with common linkage,
but only on Mach-O targets. Preserve the existing COMDAT-friendly
approach everywhere else.

This concludes our tale.

rdar://77803299
This commit is contained in:
Robert Widmann
2021-05-10 19:18:22 -07:00
parent 99c837b413
commit f6b2294d5a
18 changed files with 125 additions and 89 deletions

View File

@@ -1300,18 +1300,6 @@ static bool replaceModuleFlagsEntry(llvm::LLVMContext &Ctx,
llvm_unreachable("Could not replace old linker options entry?");
}
/// Returns true if the object file generated by \p IGM will be the "first"
/// object file in the module. This lets us determine where to put a symbol
/// that must be unique.
static bool isFirstObjectFileInModule(IRGenModule &IGM) {
if (IGM.getSILModule().isWholeModule())
return IGM.IRGen.getPrimaryIGM() == &IGM;
auto *file = cast<FileUnit>(IGM.getSILModule().getAssociatedContext());
auto *containingModule = file->getParentModule();
return containingModule->getFiles().front() == file;
}
static bool
doesTargetAutolinkUsingAutolinkExtract(const SwiftTargetInfo &TargetInfo,
const llvm::Triple &Triple) {
@@ -1486,6 +1474,47 @@ AutolinkKind AutolinkKind::create(const SwiftTargetInfo &TargetInfo,
return AutolinkKind::LLVMLinkerOptions;
}
static llvm::GlobalObject *createForceImportThunk(IRGenModule &IGM) {
llvm::SmallString<64> buf;
encodeForceLoadSymbolName(buf, IGM.IRGen.Opts.ForceLoadSymbolName);
if (IGM.Triple.isOSBinFormatMachO()) {
// On Mach-O targets, emit a common force-load symbol that resolves to
// a global variable so it will be coalesced at static link time into a
// single external symbol.
//
// Looks like C's tentative definitions are good for something after all.
auto ForceImportThunk =
new llvm::GlobalVariable(IGM.Module,
IGM.Int1Ty,
/*isConstant=*/false,
llvm::GlobalValue::CommonLinkage,
llvm::Constant::getNullValue(IGM.Int1Ty),
buf.str());
ApplyIRLinkage(IRLinkage::ExternalCommon).to(ForceImportThunk);
IGM.addUsedGlobal(ForceImportThunk);
return ForceImportThunk;
} else {
// On all other targets, emit an external symbol that resolves to a
// function definition. On Windows, linkonce_odr is basically a no-op and
// the COMDAT we set by applying linkage gives us the desired coalescing
// behavior.
auto ForceImportThunk =
llvm::Function::Create(llvm::FunctionType::get(IGM.VoidTy, false),
llvm::GlobalValue::LinkOnceODRLinkage, buf,
&IGM.Module);
ForceImportThunk->setAttributes(IGM.constructInitialAttributes());
ApplyIRLinkage(IRLinkage::ExternalExport).to(ForceImportThunk);
if (IGM.Triple.supportsCOMDAT())
if (auto *GO = cast<llvm::GlobalObject>(ForceImportThunk))
GO->setComdat(IGM.Module.getOrInsertComdat(ForceImportThunk->getName()));
auto BB = llvm::BasicBlock::Create(IGM.getLLVMContext(), "", ForceImportThunk);
llvm::IRBuilder<> IRB(BB);
IRB.CreateRetVoid();
return ForceImportThunk;
}
}
void IRGenModule::emitAutolinkInfo() {
auto Autolink =
AutolinkKind::create(TargetInfo, Triple, IRGen.Opts.LLVMLTOKind);
@@ -1504,23 +1533,8 @@ void IRGenModule::emitAutolinkInfo() {
Autolink.writeEntries(Entries, Metadata, *this);
if (!IRGen.Opts.ForceLoadSymbolName.empty() &&
(Triple.supportsCOMDAT() || isFirstObjectFileInModule(*this))) {
llvm::SmallString<64> buf;
encodeForceLoadSymbolName(buf, IRGen.Opts.ForceLoadSymbolName);
auto ForceImportThunk =
llvm::Function::Create(llvm::FunctionType::get(VoidTy, false),
llvm::GlobalValue::ExternalLinkage, buf,
&Module);
ForceImportThunk->setAttributes(constructInitialAttributes());
ApplyIRLinkage(IRLinkage::ExternalExport).to(ForceImportThunk);
if (Triple.supportsCOMDAT())
if (auto *GO = cast<llvm::GlobalObject>(ForceImportThunk))
GO->setComdat(Module.getOrInsertComdat(ForceImportThunk->getName()));
auto BB = llvm::BasicBlock::Create(getLLVMContext(), "", ForceImportThunk);
llvm::IRBuilder<> IRB(BB);
IRB.CreateRetVoid();
if (!IRGen.Opts.ForceLoadSymbolName.empty()) {
(void) createForceImportThunk(*this);
}
}