Skip to content

Commit 821ba47

Browse files
committed
IRGen: Add a frontend option to force single LLVM module emission in multithreaded mode
This allows to experiment with single module LLVM emission without having to change drivers that expect multiple output files. rdar://94744623
1 parent 5ed21c6 commit 821ba47

File tree

9 files changed

+105
-24
lines changed

9 files changed

+105
-24
lines changed

include/swift/AST/IRGenOptions.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ class IRGenOptions {
284284
/// objects.
285285
unsigned EmitStackPromotionChecks : 1;
286286

287+
unsigned UseSingleModuleLLVMEmission : 1;
288+
287289
/// Emit functions to separate sections.
288290
unsigned FunctionSections : 1;
289291

@@ -447,13 +449,13 @@ class IRGenOptions {
447449
DebugInfoFormat(IRGenDebugInfoFormat::None),
448450
DisableClangModuleSkeletonCUs(false), UseJIT(false),
449451
DisableLLVMOptzns(false), DisableSwiftSpecificLLVMOptzns(false),
450-
Playground(false),
451-
EmitStackPromotionChecks(false), FunctionSections(false),
452+
Playground(false), EmitStackPromotionChecks(false),
453+
UseSingleModuleLLVMEmission(false), FunctionSections(false),
452454
PrintInlineTree(false), EmbedMode(IRGenEmbedMode::None),
453455
LLVMLTOKind(IRGenLLVMLTOKind::None),
454456
SwiftAsyncFramePointer(SwiftAsyncFramePointerKind::Auto),
455-
HasValueNamesSetting(false),
456-
ValueNames(false), ReflectionMetadata(ReflectionMetadataMode::Runtime),
457+
HasValueNamesSetting(false), ValueNames(false),
458+
ReflectionMetadata(ReflectionMetadataMode::Runtime),
457459
EnableReflectionNames(true), EnableAnonymousContextMangledNames(false),
458460
ForcePublicLinkage(false), LazyInitializeClassMetadata(false),
459461
LazyInitializeProtocolConformances(false),
@@ -468,8 +470,7 @@ class IRGenOptions {
468470
EnableGlobalISel(false), VirtualFunctionElimination(false),
469471
WitnessMethodElimination(false), ConditionalRuntimeRecords(false),
470472
InternalizeAtLink(false), InternalizeSymbols(false),
471-
NoPreallocatedInstantiationCaches(false),
472-
CmdArgs(),
473+
NoPreallocatedInstantiationCaches(false), CmdArgs(),
473474
SanitizeCoverage(llvm::SanitizerCoverageOptions()),
474475
TypeInfoFilter(TypeInfoDumpFilter::All) {
475476
#ifndef NDEBUG
@@ -530,8 +531,8 @@ class IRGenOptions {
530531
return llvm::hash_value(0);
531532
}
532533

533-
bool hasMultipleIRGenThreads() const { return NumThreads > 1; }
534-
bool shouldPerformIRGenerationInParallel() const { return NumThreads != 0; }
534+
bool hasMultipleIRGenThreads() const { return !UseSingleModuleLLVMEmission && NumThreads > 1; }
535+
bool shouldPerformIRGenerationInParallel() const { return !UseSingleModuleLLVMEmission && NumThreads != 0; }
535536
bool hasMultipleIGMs() const { return hasMultipleIRGenThreads(); }
536537
};
537538

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,10 @@ def function_sections: Flag<["-"], "function-sections">,
505505
Flags<[FrontendOption, NoInteractiveOption]>,
506506
HelpText<"Emit functions to separate sections.">;
507507

508+
def enable_single_module_llvm_emission: Flag<["-"], "enable-single-module-llvm-emission">,
509+
Flags<[FrontendOption, NoInteractiveOption]>,
510+
HelpText<"Emit LLVM IR into a single LLVM module in multithreaded mode.">;
511+
508512
def stack_promotion_checks : Flag<["-"], "emit-stack-promotion-checks">,
509513
HelpText<"Emit runtime checks for correct stack promotion of objects.">;
510514

include/swift/Subsystems.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ namespace swift {
258258
llvm::Module *Module,
259259
StringRef OutputFilename);
260260

261+
bool writeEmptyOutputFilesFor(
262+
const ASTContext &Context,
263+
std::vector<std::string> &ParallelOutputFilenames,
264+
const IRGenOptions &IRGenOpts);
265+
261266
/// Run the LLVM passes. In multi-threaded compilation this will be done for
262267
/// multiple LLVM modules in parallel.
263268
/// \param Diags The Diagnostic Engine.

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,6 +2299,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
22992299
"-num-threads");
23002300
}
23012301
}
2302+
Opts.UseSingleModuleLLVMEmission =
2303+
Opts.NumThreads != 0 &&
2304+
Args.hasArg(OPT_enable_single_module_llvm_emission);
23022305

23032306
if (SWIFT_ENABLE_GLOBAL_ISEL_ARM64 &&
23042307
Triple.getArch() == llvm::Triple::aarch64 &&

lib/FrontendTool/FrontendTool.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,13 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
17271727
if (validateTBDIfNeeded(Invocation, MSF, *IRModule.getModule()))
17281728
return true;
17291729

1730+
if (IRGenOpts.UseSingleModuleLLVMEmission) {
1731+
// Pretend the other files that drivers/build systems expect exist by
1732+
// creating empty files.
1733+
if (writeEmptyOutputFilesFor(Context, ParallelOutputFilenames, IRGenOpts))
1734+
return true;
1735+
}
1736+
17301737
return generateCode(Instance, OutputFilename, IRModule.getModule(),
17311738
HashGlobal);
17321739
}

lib/IRGen/IRGen.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,8 @@ GeneratedModule swift::performIRGeneration(
14851485
outModuleHash);
14861486

14871487
if (Opts.shouldPerformIRGenerationInParallel() &&
1488-
!parallelOutputFilenames.empty()) {
1488+
!parallelOutputFilenames.empty() &&
1489+
!Opts.UseSingleModuleLLVMEmission) {
14891490
::performParallelIRGeneration(desc);
14901491
// TODO: Parallel LLVM compilation cannot be used if a (single) module is
14911492
// needed as return value.

lib/IRGen/IRGenModule.cpp

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/IRGen/Linking.h"
2929
#include "swift/Runtime/RuntimeFnWrappersGen.h"
3030
#include "swift/Runtime/Config.h"
31+
#include "swift/Subsystems.h"
3132
#include "clang/AST/ASTContext.h"
3233
#include "clang/Basic/CharInfo.h"
3334
#include "clang/Basic/TargetInfo.h"
@@ -1636,12 +1637,7 @@ void IRGenModule::emitAutolinkInfo() {
16361637
}
16371638
}
16381639

1639-
void IRGenModule::cleanupClangCodeGenMetadata() {
1640-
// Remove llvm.ident that ClangCodeGen might have left in the module.
1641-
auto *LLVMIdent = Module.getNamedMetadata("llvm.ident");
1642-
if (LLVMIdent)
1643-
Module.eraseNamedMetadata(LLVMIdent);
1644-
1640+
void emitSwiftVersionNumberIntoModule(llvm::Module *Module) {
16451641
// LLVM's object-file emission collects a fixed set of keys for the
16461642
// image info.
16471643
// Using "Objective-C Garbage Collection" as the key here is a hack,
@@ -1651,21 +1647,29 @@ void IRGenModule::cleanupClangCodeGenMetadata() {
16511647
const char *ObjectiveCGarbageCollection = "Objective-C Garbage Collection";
16521648
uint8_t Major, Minor;
16531649
std::tie(Major, Minor) = version::getSwiftNumericVersion();
1654-
uint32_t Value = (Major << 24) | (Minor << 16) | (swiftVersion << 8);
1655-
1656-
if (Module.getModuleFlag(ObjectiveCGarbageCollection)) {
1650+
uint32_t Value =
1651+
(Major << 24) | (Minor << 16) | (IRGenModule::swiftVersion << 8);
1652+
auto &llvmContext = Module->getContext();
1653+
if (Module->getModuleFlag(ObjectiveCGarbageCollection)) {
16571654
bool FoundOldEntry = replaceModuleFlagsEntry(
1658-
Module.getContext(), Module, ObjectiveCGarbageCollection,
1655+
llvmContext, *Module, ObjectiveCGarbageCollection,
16591656
llvm::Module::Override,
1660-
llvm::ConstantAsMetadata::get(
1661-
llvm::ConstantInt::get(Int32Ty, Value)));
1657+
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
1658+
llvm::Type::getInt32Ty(llvmContext), Value)));
16621659

16631660
(void)FoundOldEntry;
16641661
assert(FoundOldEntry && "Could not replace old module flag entry?");
16651662
} else
1666-
Module.addModuleFlag(llvm::Module::Override,
1667-
ObjectiveCGarbageCollection,
1668-
Value);
1663+
Module->addModuleFlag(llvm::Module::Override, ObjectiveCGarbageCollection,
1664+
Value);
1665+
}
1666+
1667+
void IRGenModule::cleanupClangCodeGenMetadata() {
1668+
// Remove llvm.ident that ClangCodeGen might have left in the module.
1669+
auto *LLVMIdent = Module.getNamedMetadata("llvm.ident");
1670+
if (LLVMIdent)
1671+
Module.eraseNamedMetadata(LLVMIdent);
1672+
emitSwiftVersionNumberIntoModule(&Module);
16691673
}
16701674

16711675
bool IRGenModule::finalize() {
@@ -1884,3 +1888,40 @@ bool IRGenModule::isConcurrencyAvailable() {
18841888
AvailabilityContext::forDeploymentTarget(ctx);
18851889
return deploymentAvailability.isContainedIn(ctx.getConcurrencyAvailability());
18861890
}
1891+
1892+
/// Pretend the other files that drivers/build systems expect exist by
1893+
/// creating empty files. Used by UseSingleModuleLLVMEmission when
1894+
/// num-threads > 0.
1895+
bool swift::writeEmptyOutputFilesFor(
1896+
const ASTContext &Context,
1897+
std::vector<std::string>& ParallelOutputFilenames,
1898+
const IRGenOptions &IRGenOpts) {
1899+
1900+
for (auto fileName : ParallelOutputFilenames) {
1901+
// The first output file, was use for genuine output.
1902+
if (fileName == ParallelOutputFilenames[0])
1903+
continue;
1904+
1905+
std::unique_ptr<llvm::LLVMContext> llvmContext(new llvm::LLVMContext());
1906+
std::unique_ptr<clang::CodeGenerator> clangCodeGen(
1907+
createClangCodeGenerator(const_cast<ASTContext&>(Context),
1908+
*llvmContext, IRGenOpts, fileName, ""));
1909+
auto *llvmModule = clangCodeGen->GetModule();
1910+
1911+
auto *clangImporter = static_cast<ClangImporter *>(
1912+
Context.getClangModuleLoader());
1913+
llvmModule->setTargetTriple(
1914+
clangImporter->getTargetInfo().getTargetOpts().Triple);
1915+
1916+
// Add LLVM module flags.
1917+
auto &clangASTContext = clangImporter->getClangASTContext();
1918+
clangCodeGen->HandleTranslationUnit(
1919+
const_cast<clang::ASTContext &>(clangASTContext));
1920+
1921+
emitSwiftVersionNumberIntoModule(llvmModule);
1922+
1923+
swift::performLLVM(IRGenOpts, const_cast<ASTContext&>(Context),
1924+
llvmModule, fileName);
1925+
}
1926+
return false;
1927+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public func B() {
2+
print("B")
3+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -module-name X -num-threads 1 -O -enable-single-module-llvm-emission -emit-ir %s %S/Inputs/single-module-num-threads-2.swift -o %t/single-module-num-threads.ll -o %t/single-module-num-threads-2.ll
3+
// RUN: %FileCheck %s < %t/single-module-num-threads.ll
4+
// RUN: %FileCheck %s --check-prefix=EMPTY < %t/single-module-num-threads-2.ll
5+
6+
// CHECK: define{{.*}} swiftcc void @"$s1X1AyyF"()
7+
// CHECK: define{{.*}} swiftcc void @"$s1X1ByyF"()
8+
9+
// EMPTY-NOT: s1X1AyyF
10+
// EMPTY-NOT: s1X1ByyF
11+
12+
// Make sure that with enable-single-module-llvm-emission we emit all code into
13+
// one llvm module.
14+
public func A() {
15+
print("A")
16+
}

0 commit comments

Comments
 (0)