Skip to content

Commit 4b1c413

Browse files
Merge pull request #59344 from aschwaighofer/single_module_in_num_threads
IRGen: Add a frontend option to force single LLVM module emission in multithreaded mode
2 parents ddf9268 + 821ba47 commit 4b1c413

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)