Skip to content

Commit 91473cc

Browse files
committed
[5.7] 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 (cherry picked from commit 821ba47)
1 parent 9692a83 commit 91473cc

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

@@ -524,8 +525,8 @@ class IRGenOptions {
524525
return llvm::hash_value(0);
525526
}
526527

527-
bool hasMultipleIRGenThreads() const { return NumThreads > 1; }
528-
bool shouldPerformIRGenerationInParallel() const { return NumThreads != 0; }
528+
bool hasMultipleIRGenThreads() const { return !UseSingleModuleLLVMEmission && NumThreads > 1; }
529+
bool shouldPerformIRGenerationInParallel() const { return !UseSingleModuleLLVMEmission && NumThreads != 0; }
529530
bool hasMultipleIGMs() const { return hasMultipleIRGenThreads(); }
530531
};
531532

include/swift/Option/FrontendOptions.td

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

502+
def enable_single_module_llvm_emission: Flag<["-"], "enable-single-module-llvm-emission">,
503+
Flags<[FrontendOption, NoInteractiveOption]>,
504+
HelpText<"Emit LLVM IR into a single LLVM module in multithreaded mode.">;
505+
502506
def stack_promotion_checks : Flag<["-"], "emit-stack-promotion-checks">,
503507
HelpText<"Emit runtime checks for correct stack promotion of objects.">;
504508

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
@@ -2326,6 +2326,9 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
23262326
"-num-threads");
23272327
}
23282328
}
2329+
Opts.UseSingleModuleLLVMEmission =
2330+
Opts.NumThreads != 0 &&
2331+
Args.hasArg(OPT_enable_single_module_llvm_emission);
23292332

23302333
if (SWIFT_ENABLE_GLOBAL_ISEL_ARM64 &&
23312334
Triple.getArch() == llvm::Triple::aarch64 &&

lib/FrontendTool/FrontendTool.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,13 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
17241724
publicCMOSymbols))
17251725
return true;
17261726

1727+
if (IRGenOpts.UseSingleModuleLLVMEmission) {
1728+
// Pretend the other files that drivers/build systems expect exist by
1729+
// creating empty files.
1730+
if (writeEmptyOutputFilesFor(Context, ParallelOutputFilenames, IRGenOpts))
1731+
return true;
1732+
}
1733+
17271734
return generateCode(Instance, OutputFilename, IRModule.getModule(),
17281735
HashGlobal);
17291736
}

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"
@@ -1614,12 +1615,7 @@ void IRGenModule::emitAutolinkInfo() {
16141615
}
16151616
}
16161617

1617-
void IRGenModule::cleanupClangCodeGenMetadata() {
1618-
// Remove llvm.ident that ClangCodeGen might have left in the module.
1619-
auto *LLVMIdent = Module.getNamedMetadata("llvm.ident");
1620-
if (LLVMIdent)
1621-
Module.eraseNamedMetadata(LLVMIdent);
1622-
1618+
void emitSwiftVersionNumberIntoModule(llvm::Module *Module) {
16231619
// LLVM's object-file emission collects a fixed set of keys for the
16241620
// image info.
16251621
// Using "Objective-C Garbage Collection" as the key here is a hack,
@@ -1629,21 +1625,29 @@ void IRGenModule::cleanupClangCodeGenMetadata() {
16291625
const char *ObjectiveCGarbageCollection = "Objective-C Garbage Collection";
16301626
uint8_t Major, Minor;
16311627
std::tie(Major, Minor) = version::getSwiftNumericVersion();
1632-
uint32_t Value = (Major << 24) | (Minor << 16) | (swiftVersion << 8);
1633-
1634-
if (Module.getModuleFlag(ObjectiveCGarbageCollection)) {
1628+
uint32_t Value =
1629+
(Major << 24) | (Minor << 16) | (IRGenModule::swiftVersion << 8);
1630+
auto &llvmContext = Module->getContext();
1631+
if (Module->getModuleFlag(ObjectiveCGarbageCollection)) {
16351632
bool FoundOldEntry = replaceModuleFlagsEntry(
1636-
Module.getContext(), Module, ObjectiveCGarbageCollection,
1633+
llvmContext, *Module, ObjectiveCGarbageCollection,
16371634
llvm::Module::Override,
1638-
llvm::ConstantAsMetadata::get(
1639-
llvm::ConstantInt::get(Int32Ty, Value)));
1635+
llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
1636+
llvm::Type::getInt32Ty(llvmContext), Value)));
16401637

16411638
(void)FoundOldEntry;
16421639
assert(FoundOldEntry && "Could not replace old module flag entry?");
16431640
} else
1644-
Module.addModuleFlag(llvm::Module::Override,
1645-
ObjectiveCGarbageCollection,
1646-
Value);
1641+
Module->addModuleFlag(llvm::Module::Override, ObjectiveCGarbageCollection,
1642+
Value);
1643+
}
1644+
1645+
void IRGenModule::cleanupClangCodeGenMetadata() {
1646+
// Remove llvm.ident that ClangCodeGen might have left in the module.
1647+
auto *LLVMIdent = Module.getNamedMetadata("llvm.ident");
1648+
if (LLVMIdent)
1649+
Module.eraseNamedMetadata(LLVMIdent);
1650+
emitSwiftVersionNumberIntoModule(&Module);
16471651
}
16481652

16491653
bool IRGenModule::finalize() {
@@ -1862,3 +1866,40 @@ bool IRGenModule::isConcurrencyAvailable() {
18621866
AvailabilityContext::forDeploymentTarget(ctx);
18631867
return deploymentAvailability.isContainedIn(ctx.getConcurrencyAvailability());
18641868
}
1869+
1870+
/// Pretend the other files that drivers/build systems expect exist by
1871+
/// creating empty files. Used by UseSingleModuleLLVMEmission when
1872+
/// num-threads > 0.
1873+
bool swift::writeEmptyOutputFilesFor(
1874+
const ASTContext &Context,
1875+
std::vector<std::string>& ParallelOutputFilenames,
1876+
const IRGenOptions &IRGenOpts) {
1877+
1878+
for (auto fileName : ParallelOutputFilenames) {
1879+
// The first output file, was use for genuine output.
1880+
if (fileName == ParallelOutputFilenames[0])
1881+
continue;
1882+
1883+
std::unique_ptr<llvm::LLVMContext> llvmContext(new llvm::LLVMContext());
1884+
std::unique_ptr<clang::CodeGenerator> clangCodeGen(
1885+
createClangCodeGenerator(const_cast<ASTContext&>(Context),
1886+
*llvmContext, IRGenOpts, fileName, ""));
1887+
auto *llvmModule = clangCodeGen->GetModule();
1888+
1889+
auto *clangImporter = static_cast<ClangImporter *>(
1890+
Context.getClangModuleLoader());
1891+
llvmModule->setTargetTriple(
1892+
clangImporter->getTargetInfo().getTargetOpts().Triple);
1893+
1894+
// Add LLVM module flags.
1895+
auto &clangASTContext = clangImporter->getClangASTContext();
1896+
clangCodeGen->HandleTranslationUnit(
1897+
const_cast<clang::ASTContext &>(clangASTContext));
1898+
1899+
emitSwiftVersionNumberIntoModule(llvmModule);
1900+
1901+
swift::performLLVM(IRGenOpts, const_cast<ASTContext&>(Context),
1902+
llvmModule, fileName);
1903+
}
1904+
return false;
1905+
}
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)