Skip to content

Commit 09b8af8

Browse files
Virtualize swift compiler outputs (#63206)
Using a virutal output backend to capture all the outputs from swift-frontend invocation. This allows redirecting and/or mirroring compiler outputs to multiple location using different OutputBackend. As an example usage for the virtual outputs, teach swift compiler to check its output determinism by running the compiler invocation twice and compare the hash of all its outputs. Virtual output will be used to enable caching in the future.
1 parent 829491b commit 09b8af8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+690
-327
lines changed

include/swift/APIDigester/ModuleAnalyzerNodes.h

+7-6
Original file line numberDiff line numberDiff line change
@@ -784,8 +784,8 @@ class SwiftDeclCollector: public VisibleDeclConsumer {
784784
void deSerialize(StringRef Filename);
785785

786786
// Serialize the content of all roots to a given file using JSON format.
787-
void serialize(StringRef Filename);
788-
static void serialize(StringRef Filename, SDKNode *Root, PayLoad otherInfo);
787+
void serialize(llvm::raw_ostream &os);
788+
static void serialize(llvm::raw_ostream &os, SDKNode *Root, PayLoad otherInfo);
789789

790790
// After collecting decls, either from imported modules or from a previously
791791
// serialized JSON file, using this function to get the root of the SDK.
@@ -835,14 +835,15 @@ SDKNodeRoot *getSDKNodeRoot(SDKContext &SDKCtx,
835835

836836
SDKNodeRoot *getEmptySDKNodeRoot(SDKContext &SDKCtx);
837837

838-
void dumpSDKRoot(SDKNodeRoot *Root, PayLoad load, StringRef OutputFile);
839-
void dumpSDKRoot(SDKNodeRoot *Root, StringRef OutputFile);
838+
void dumpSDKRoot(SDKNodeRoot *Root, PayLoad load, llvm::raw_ostream &os);
839+
void dumpSDKRoot(SDKNodeRoot *Root, llvm::raw_ostream &os);
840840

841841
int dumpSDKContent(const CompilerInvocation &InitInvoke,
842842
const llvm::StringSet<> &ModuleNames,
843-
StringRef OutputFile, CheckerOptions Opts);
843+
llvm::raw_ostream &os, CheckerOptions Opts);
844844

845-
void dumpModuleContent(ModuleDecl *MD, StringRef OutputFile, bool ABI, bool Empty);
845+
void dumpModuleContent(ModuleDecl *MD, llvm::raw_ostream &os, bool ABI,
846+
bool Empty);
846847

847848
/// Mostly for testing purposes, this function de-serializes the SDK dump in
848849
/// dumpPath and re-serialize them to OutputPath. If the tool performs correctly,

include/swift/AST/ASTContext.h

+18
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "llvm/ADT/TinyPtrVector.h"
4545
#include "llvm/Support/Allocator.h"
4646
#include "llvm/Support/DataTypes.h"
47+
#include "llvm/Support/VirtualOutputBackend.h"
4748
#include <functional>
4849
#include <memory>
4950
#include <utility>
@@ -231,6 +232,7 @@ class ASTContext final {
231232
ClangImporterOptions &ClangImporterOpts,
232233
symbolgraphgen::SymbolGraphOptions &SymbolGraphOpts,
233234
SourceManager &SourceMgr, DiagnosticEngine &Diags,
235+
llvm::IntrusiveRefCntPtr<llvm::vfs::OutputBackend> OutBackend = nullptr,
234236
std::function<bool(llvm::StringRef, bool)> PreModuleImportCallback = {});
235237

236238
public:
@@ -248,6 +250,7 @@ class ASTContext final {
248250
ClangImporterOptions &ClangImporterOpts,
249251
symbolgraphgen::SymbolGraphOptions &SymbolGraphOpts,
250252
SourceManager &SourceMgr, DiagnosticEngine &Diags,
253+
llvm::IntrusiveRefCntPtr<llvm::vfs::OutputBackend> OutBackend = nullptr,
251254
std::function<bool(llvm::StringRef, bool)> PreModuleImportCallback = {});
252255
~ASTContext();
253256

@@ -281,6 +284,9 @@ class ASTContext final {
281284
/// Diags - The diagnostics engine.
282285
DiagnosticEngine &Diags;
283286

287+
/// OutputBackend for writing outputs.
288+
llvm::IntrusiveRefCntPtr<llvm::vfs::OutputBackend> OutputBackend;
289+
284290
/// If the shared pointer is not a \c nullptr and the pointee is \c true,
285291
/// all operations working on this ASTContext should be aborted at the next
286292
/// possible opportunity.
@@ -1518,6 +1524,18 @@ class ASTContext final {
15181524

15191525
const llvm::StringSet<> &getLoadedPluginLibraryPaths() const;
15201526

1527+
/// Get the output backend. The output backend needs to be initialized via
1528+
/// constructor or `setOutputBackend`.
1529+
llvm::vfs::OutputBackend &getOutputBackend() const {
1530+
assert(OutputBackend && "OutputBackend is not setup");
1531+
return *OutputBackend;
1532+
}
1533+
/// Set output backend for virtualized outputs.
1534+
void setOutputBackend(
1535+
llvm::IntrusiveRefCntPtr<llvm::vfs::OutputBackend> OutBackend) {
1536+
OutputBackend = std::move(OutBackend);
1537+
}
1538+
15211539
private:
15221540
friend Decl;
15231541

include/swift/AST/AbstractSourceFileDepGraphFactory.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "swift/AST/Decl.h"
1717
#include "swift/AST/DeclContext.h"
1818
#include "swift/AST/FineGrainedDependencies.h"
19+
#include "llvm/Support/VirtualOutputBackend.h"
1920

2021
namespace swift {
2122
class DiagnosticEngine;
@@ -39,6 +40,9 @@ class AbstractSourceFileDepGraphFactory {
3940

4041
DiagnosticEngine &diags;
4142

43+
/// OutputBackend.
44+
llvm::vfs::OutputBackend &backend;
45+
4246
/// Graph under construction
4347
SourceFileDepGraph g;
4448

@@ -49,7 +53,8 @@ class AbstractSourceFileDepGraphFactory {
4953
StringRef swiftDeps,
5054
Fingerprint fileFingerprint,
5155
bool emitDotFileAfterConstruction,
52-
DiagnosticEngine &diags);
56+
DiagnosticEngine &diags,
57+
llvm::vfs::OutputBackend &outputBackend);
5358

5459
virtual ~AbstractSourceFileDepGraphFactory() = default;
5560

include/swift/AST/DiagnosticsCommon.def

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ ERROR(not_implemented,none,
2929
ERROR(error_opening_output,none,
3030
"error opening '%0' for output: %1", (StringRef, StringRef))
3131

32+
ERROR(error_closing_output,none,
33+
"error closing '%0' for output: %1", (StringRef, StringRef))
34+
3235
ERROR(cannot_find_group_info_file,none,
3336
"cannot find group info file at path: '%0'", (StringRef))
3437

include/swift/AST/DiagnosticsFrontend.def

+10
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,16 @@ REMARK(interface_file_backup_used,none,
475475

476476
WARNING(warn_flag_deprecated,none, "flag '%0' is deprecated", (StringRef))
477477

478+
// Output deterministic check
479+
ERROR(error_nondeterministic_output,none,
480+
"output file '%0' is not deterministic: hash value '%1' vs '%2' between two compilations",
481+
(StringRef, StringRef, StringRef))
482+
ERROR(error_output_missing,none,
483+
"output file '%0' is missing from %select{first|second}1 compilation for deterministic check",
484+
(StringRef, /*SecondRun=*/bool))
485+
REMARK(matching_output_produced,none,
486+
"produced matching output file '%0' for deterministic check: hash '%1'", (StringRef, StringRef))
487+
478488
// Dependency Verifier Diagnostics
479489
ERROR(missing_member_dependency,none,
480490
"expected "

include/swift/AST/FileSystem.h

+24-11
Original file line numberDiff line numberDiff line change
@@ -16,29 +16,42 @@
1616
#include "swift/Basic/FileSystem.h"
1717
#include "swift/AST/DiagnosticEngine.h"
1818
#include "swift/AST/DiagnosticsCommon.h"
19+
#include "llvm/Support/VirtualOutputBackend.h"
20+
#include "llvm/Support/VirtualOutputConfig.h"
1921

2022
namespace swift {
21-
22-
/// A wrapper around swift::atomicallyWritingToFile that handles diagnosing any
23-
/// filesystem errors and asserts the output path is nonempty.
23+
/// A wrapper around llvm::vfs::OutputBackend to handle diagnosing any file
24+
/// system errors during output creation.
2425
///
2526
/// \returns true if there were any errors, either from the filesystem
2627
/// operations or from \p action returning true.
2728
inline bool
28-
withOutputFile(DiagnosticEngine &diags, StringRef outputPath,
29+
withOutputPath(DiagnosticEngine &diags, llvm::vfs::OutputBackend &Backend,
30+
StringRef outputPath,
2931
llvm::function_ref<bool(llvm::raw_pwrite_stream &)> action) {
3032
assert(!outputPath.empty());
33+
llvm::vfs::OutputConfig config;
34+
config.setAtomicWrite().setOnlyIfDifferent();
3135

32-
bool actionFailed = false;
33-
std::error_code EC = swift::atomicallyWritingToFile(
34-
outputPath,
35-
[&](llvm::raw_pwrite_stream &out) { actionFailed = action(out); });
36-
if (EC) {
36+
auto outputFile = Backend.createFile(outputPath, config);
37+
if (!outputFile) {
3738
diags.diagnose(SourceLoc(), diag::error_opening_output, outputPath,
38-
EC.message());
39+
toString(outputFile.takeError()));
40+
return true;
41+
}
42+
43+
bool failed = action(*outputFile);
44+
// If there is an error, discard output. Otherwise keep the output file.
45+
if (auto error = failed ? outputFile->discard() : outputFile->keep()) {
46+
// Don't diagnose discard error.
47+
if (failed)
48+
consumeError(std::move(error));
49+
else
50+
diags.diagnose(SourceLoc(), diag::error_closing_output, outputPath,
51+
toString(std::move(error)));
3952
return true;
4053
}
41-
return actionFailed;
54+
return failed;
4255
}
4356
} // end namespace swift
4457

include/swift/AST/FineGrainedDependencies.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/ADT/SetVector.h"
2525
#include "llvm/Support/MD5.h"
2626
#include "llvm/Support/MemoryBuffer.h"
27+
#include "llvm/Support/VirtualOutputBackend.h"
2728
#include "llvm/Support/YAMLParser.h"
2829
#include "llvm/Support/YAMLTraits.h"
2930
#include "llvm/Support/raw_ostream.h"
@@ -357,8 +358,9 @@ class BiIndexedTwoStageMap {
357358
/// \Note The returned graph should not be escaped from the callback.
358359
bool withReferenceDependencies(
359360
llvm::PointerUnion<const ModuleDecl *, const SourceFile *> MSF,
360-
const DependencyTracker &depTracker, StringRef outputPath,
361-
bool alsoEmitDotFile, llvm::function_ref<bool(SourceFileDepGraph &&)>);
361+
const DependencyTracker &depTracker, llvm::vfs::OutputBackend &backend,
362+
StringRef outputPath, bool alsoEmitDotFile,
363+
llvm::function_ref<bool(SourceFileDepGraph &&)>);
362364

363365
//==============================================================================
364366
// MARK: Enums
@@ -895,7 +897,8 @@ class SourceFileDepGraph {
895897

896898
bool verifySequenceNumber() const;
897899

898-
void emitDotFile(StringRef outputPath, DiagnosticEngine &diags);
900+
void emitDotFile(llvm::vfs::OutputBackend &outputBackend,
901+
StringRef outputPath, DiagnosticEngine &diags);
899902

900903
void addNode(SourceFileDepGraphNode *n) {
901904
n->setSequenceNumber(allNodes.size());

include/swift/AST/FineGrainedDependencyFormat.h

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
namespace llvm {
2020
class MemoryBuffer;
21+
namespace vfs {
22+
class OutputBackend;
23+
}
2124
}
2225

2326
namespace swift {
@@ -132,6 +135,7 @@ bool readFineGrainedDependencyGraph(llvm::StringRef path,
132135
/// Tries to write the dependency graph to the given path name.
133136
/// Returns true if there was an error.
134137
bool writeFineGrainedDependencyGraphToPath(DiagnosticEngine &diags,
138+
llvm::vfs::OutputBackend &backend,
135139
llvm::StringRef path,
136140
const SourceFileDepGraph &g);
137141

include/swift/AST/IRGenOptions.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ class IRGenOptions {
318318
/// Print the LLVM inline tree at the end of the LLVM pass pipeline.
319319
unsigned PrintInlineTree : 1;
320320

321+
/// Always recompile the output even if the module hash might match.
322+
unsigned AlwaysCompile : 1;
323+
321324
/// Whether we should embed the bitcode file.
322325
IRGenEmbedMode EmbedMode : 2;
323326

@@ -481,7 +484,7 @@ class IRGenOptions {
481484
DisableLLVMOptzns(false), DisableSwiftSpecificLLVMOptzns(false),
482485
Playground(false),
483486
EmitStackPromotionChecks(false), UseSingleModuleLLVMEmission(false),
484-
FunctionSections(false), PrintInlineTree(false),
487+
FunctionSections(false), PrintInlineTree(false), AlwaysCompile(false),
485488
EmbedMode(IRGenEmbedMode::None), LLVMLTOKind(IRGenLLVMLTOKind::None),
486489
SwiftAsyncFramePointer(SwiftAsyncFramePointerKind::Auto),
487490
HasValueNamesSetting(false), ValueNames(false),

include/swift/AST/ModuleLoader.h

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232

3333
namespace llvm {
3434
class FileCollectorBase;
35+
namespace vfs {
36+
class OutputBackend;
37+
}
3538
}
3639

3740
namespace clang {
@@ -156,6 +159,7 @@ class ModuleInterfaceChecker {
156159
virtual bool tryEmitForwardingModule(StringRef moduleName,
157160
StringRef interfacePath,
158161
ArrayRef<std::string> candidates,
162+
llvm::vfs::OutputBackend &backend,
159163
StringRef outPath) = 0;
160164
virtual ~ModuleInterfaceChecker() = default;
161165
};

include/swift/ClangImporter/ClangImporter.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace llvm {
2828
template<typename Fn> class function_ref;
2929
namespace vfs {
3030
class FileSystem;
31+
class OutputBackend;
3132
}
3233
}
3334

@@ -394,8 +395,7 @@ class ClangImporter final : public ClangModuleLoader {
394395
/// replica.
395396
///
396397
/// \sa clang::GeneratePCHAction
397-
bool emitBridgingPCH(StringRef headerPath,
398-
StringRef outputPCHPath);
398+
bool emitBridgingPCH(StringRef headerPath, StringRef outputPCHPath);
399399

400400
/// Returns true if a clang CompilerInstance can successfully read in a PCH,
401401
/// assuming it exists, with the current options. This can be used to find out

include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
namespace llvm {
2020
class MemoryBuffer;
21+
namespace vfs{
22+
class OutputBackend;
23+
}
2124
}
2225

2326
namespace swift {
@@ -187,6 +190,7 @@ bool readInterModuleDependenciesCache(llvm::StringRef path,
187190
/// Tries to write the dependency graph to the given path name.
188191
/// Returns true if there was an error.
189192
bool writeInterModuleDependenciesCache(DiagnosticEngine &diags,
193+
llvm::vfs::OutputBackend &backend,
190194
llvm::StringRef path,
191195
const SwiftDependencyScanningService &cache);
192196

include/swift/Driver/FineGrainedDependencyDriverGraph.h

+10
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@
2020
#include "swift/Basic/OptionSet.h"
2121
#include "swift/Driver/Job.h"
2222
#include "llvm/ADT/ArrayRef.h"
23+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
2324
#include "llvm/ADT/STLExtras.h"
2425
#include "llvm/ADT/StringRef.h"
2526
#include "llvm/ADT/iterator_range.h"
2627
#include "llvm/Support/Path.h"
2728
#include "llvm/Support/PointerLikeTypeTraits.h"
29+
#include "llvm/Support/VirtualOutputBackend.h"
30+
#include "llvm/Support/VirtualOutputBackends.h"
2831
#include <string>
2932
#include <unordered_map>
3033
#include <unordered_set>
@@ -201,6 +204,9 @@ class ModuleDepGraph {
201204
std::vector<const ModuleDepGraphNode *>>
202205
dependencyPathsToJobs;
203206

207+
/// VirtualOutputBackend for emitting graphs.
208+
llvm::IntrusiveRefCntPtr<llvm::vfs::OutputBackend> backend;
209+
204210
/// For helping with performance tuning, may be null:
205211
UnifiedStatsReporter *stats;
206212

@@ -306,6 +312,10 @@ class ModuleDepGraph {
306312
: None),
307313
stats(stats) {
308314
assert(verify() && "ModuleDepGraph should be fine when created");
315+
316+
// Create a OnDiskOutputBackend for emitting graphs. Currently, this is
317+
// only used by driver so the backend is not shared with a CompilerInstance.
318+
backend = llvm::makeIntrusiveRefCnt<llvm::vfs::OnDiskOutputBackend>();
309319
}
310320

311321
/// For unit tests.

0 commit comments

Comments
 (0)