Skip to content

Commit 22592d2

Browse files
authored
Merge pull request #69470 from rintaro/swift-parse-test
[swift-parse-test] A parser performance check utility
2 parents bd372c2 + 66b8946 commit 22592d2

File tree

11 files changed

+362
-73
lines changed

11 files changed

+362
-73
lines changed

include/swift/Bridging/ASTGen.h

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//===--- ASTGen.h -----------------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/AST/CASTBridging.h"
14+
15+
#ifdef __cplusplus
16+
extern "C" {
17+
#endif
18+
19+
void *_Nonnull swift_ASTGen_createQueuedDiagnostics();
20+
void swift_ASTGen_destroyQueuedDiagnostics(void *_Nonnull queued);
21+
void swift_ASTGen_addQueuedSourceFile(
22+
void *_Nonnull queuedDiagnostics, ssize_t bufferID,
23+
void *_Nonnull sourceFile, const uint8_t *_Nonnull displayNamePtr,
24+
intptr_t displayNameLength, ssize_t parentID, ssize_t positionInParent);
25+
void swift_ASTGen_addQueuedDiagnostic(
26+
void *_Nonnull queued, const char *_Nonnull text, ptrdiff_t textLength,
27+
BridgedDiagnosticSeverity severity, const void *_Nullable sourceLoc,
28+
const void *_Nullable *_Nullable highlightRanges,
29+
ptrdiff_t numHighlightRanges);
30+
void swift_ASTGen_renderQueuedDiagnostics(
31+
void *_Nonnull queued, ssize_t contextSize, ssize_t colorize,
32+
BridgedString *_Nonnull renderedString);
33+
34+
// FIXME: Hack because we cannot easily get to the already-parsed source
35+
// file from here. Fix this egregious oversight!
36+
void *_Nullable swift_ASTGen_parseSourceFile(const char *_Nonnull buffer,
37+
size_t bufferLength,
38+
const char *_Nonnull moduleName,
39+
const char *_Nonnull filename,
40+
void *_Nullable ctx);
41+
void swift_ASTGen_destroySourceFile(void *_Nonnull sourceFile);
42+
43+
void *_Nullable swift_ASTGen_resolveMacroType(const void *_Nonnull macroType);
44+
void swift_ASTGen_destroyMacro(void *_Nonnull macro);
45+
46+
void swift_ASTGen_freeBridgedString(BridgedString);
47+
48+
void *_Nonnull swift_ASTGen_resolveExecutableMacro(
49+
const char *_Nonnull moduleName, const char *_Nonnull typeName,
50+
void *_Nonnull opaquePluginHandle);
51+
void swift_ASTGen_destroyExecutableMacro(void *_Nonnull macro);
52+
53+
ptrdiff_t swift_ASTGen_checkMacroDefinition(
54+
void *_Nonnull diagEngine, void *_Nonnull sourceFile,
55+
const void *_Nonnull macroSourceLocation,
56+
BridgedString *_Nonnull expansionSourceOutPtr,
57+
ptrdiff_t *_Nullable *_Nonnull replacementsPtr,
58+
ptrdiff_t *_Nonnull numReplacements);
59+
void swift_ASTGen_freeExpansionReplacements(
60+
ptrdiff_t *_Nullable replacementsPtr, ptrdiff_t numReplacements);
61+
62+
ptrdiff_t swift_ASTGen_expandFreestandingMacro(
63+
void *_Nonnull diagEngine, const void *_Nonnull macro, uint8_t externalKind,
64+
const char *_Nonnull discriminator, uint8_t rawMacroRole,
65+
void *_Nonnull sourceFile, const void *_Nullable sourceLocation,
66+
BridgedString *_Nonnull evaluatedSourceOut);
67+
68+
ptrdiff_t swift_ASTGen_expandAttachedMacro(
69+
void *_Nonnull diagEngine, const void *_Nonnull macro, uint8_t externalKind,
70+
const char *_Nonnull discriminator, const char *_Nonnull qualifiedType,
71+
const char *_Nonnull conformances, uint8_t rawMacroRole,
72+
void *_Nonnull customAttrSourceFile,
73+
const void *_Nullable customAttrSourceLocation,
74+
void *_Nonnull declarationSourceFile,
75+
const void *_Nullable declarationSourceLocation,
76+
void *_Nullable parentDeclSourceFile,
77+
const void *_Nullable parentDeclSourceLocation,
78+
BridgedString *_Nonnull evaluatedSourceOut);
79+
80+
bool swift_ASTGen_initializePlugin(void *_Nonnull handle,
81+
void *_Nullable diagEngine);
82+
void swift_ASTGen_deinitializePlugin(void *_Nonnull handle);
83+
bool swift_ASTGen_pluginServerLoadLibraryPlugin(
84+
void *_Nonnull handle, const char *_Nonnull libraryPath,
85+
const char *_Nonnull moduleName, BridgedString *_Nullable errorOut);
86+
87+
#ifdef __cplusplus
88+
}
89+
#endif

include/swift/Driver/Driver.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ class Driver {
181181
SymbolGraph, // swift-symbolgraph
182182
APIExtract, // swift-api-extract
183183
APIDigester, // swift-api-digester
184-
CacheTool // swift-cache-tool
184+
CacheTool, // swift-cache-tool
185+
ParseTest, // swift-parse-test
185186
};
186187

187188
class InputInfoMap;

lib/Driver/Driver.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ void Driver::parseDriverKind(ArrayRef<const char *> Args) {
112112
.Case("swift-api-extract", DriverKind::APIExtract)
113113
.Case("swift-api-digester", DriverKind::APIDigester)
114114
.Case("swift-cache-tool", DriverKind::CacheTool)
115+
.Case("swift-parse-test", DriverKind::ParseTest)
115116
.Default(llvm::None);
116117

117118
if (Kind.has_value())
@@ -3587,6 +3588,7 @@ void Driver::printHelp(bool ShowHidden) const {
35873588
case DriverKind::APIExtract:
35883589
case DriverKind::APIDigester:
35893590
case DriverKind::CacheTool:
3591+
case DriverKind::ParseTest:
35903592
ExcludedFlagsBitmask |= options::NoBatchOption;
35913593
break;
35923594
}

lib/DriverTool/CMakeLists.txt

+9-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ set(driver_sources_and_options
1313
swift_cache_tool_main.cpp
1414
swift_indent_main.cpp
1515
swift_symbolgraph_extract_main.cpp
16-
swift_api_extract_main.cpp)
16+
swift_api_extract_main.cpp
17+
swift_parse_test_main.cpp)
1718

1819
set(driver_common_libs
1920
swiftAPIDigester
@@ -34,6 +35,13 @@ if(NOT SWIFT_BUILT_STANDALONE)
3435
add_dependencies(swiftDriverTool clang-resource-headers)
3536
endif()
3637

38+
if (SWIFT_BUILD_SWIFT_SYNTAX)
39+
target_compile_definitions(swiftDriverTool
40+
PRIVATE
41+
SWIFT_BUILD_SWIFT_SYNTAX
42+
)
43+
endif()
44+
3745
set_swift_llvm_is_available(swiftDriverTool)
3846

3947
set(LLVM_TARGET_DEFINITIONS SwiftCacheToolOptions.td)

lib/DriverTool/driver.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ extern int swift_api_extract_main(ArrayRef<const char *> Args,
111111
extern int swift_cache_tool_main(ArrayRef<const char *> Args, const char *Argv0,
112112
void *MainAddr);
113113

114+
/// Run 'swift-parse-test'
115+
extern int swift_parse_test_main(ArrayRef<const char *> Args, const char *Argv0,
116+
void *MainAddr);
117+
114118
/// Determine if the given invocation should run as a "subcommand".
115119
///
116120
/// Examples of "subcommands" are 'swift build' or 'swift test', which are
@@ -379,6 +383,9 @@ static int run_driver(StringRef ExecName,
379383
return swift_cache_tool_main(
380384
TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0],
381385
(void *)(intptr_t)getExecutablePath);
386+
case Driver::DriverKind::ParseTest:
387+
return swift_parse_test_main(argv, argv[0],
388+
(void *)(intptr_t)getExecutablePath);
382389
default:
383390
break;
384391
}
+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
//===--- swift_parse_test_main.cpp ----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// A utility tool to measure the parser performance.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/AST/ASTContext.h"
18+
#include "swift/Basic/LLVM.h"
19+
#include "swift/Basic/LangOptions.h"
20+
#include "swift/Bridging/ASTGen.h"
21+
#include "swift/Parse/Parser.h"
22+
#include "llvm/Support/CommandLine.h"
23+
24+
#include <chrono>
25+
#include <ctime>
26+
27+
using namespace swift;
28+
29+
namespace {
30+
31+
enum class Executor {
32+
SwiftParser,
33+
LibParse,
34+
};
35+
36+
enum class ExecuteOptionFlag {
37+
/// Enable body skipping
38+
SkipBodies = 1 << 0,
39+
};
40+
using ExecuteOptions = OptionSet<ExecuteOptionFlag>;
41+
42+
struct SwiftParseTestOptions {
43+
llvm::cl::list<Executor> Executors = llvm::cl::list<Executor>(
44+
llvm::cl::desc("Available parsers"),
45+
llvm::cl::values(
46+
clEnumValN(Executor::SwiftParser, "swift-parser", "SwiftParser"),
47+
clEnumValN(Executor::LibParse, "lib-parse", "libParse")));
48+
49+
llvm::cl::opt<unsigned int> Iteration = llvm::cl::opt<unsigned int>(
50+
"n", llvm::cl::desc("iteration"), llvm::cl::init(1));
51+
52+
llvm::cl::opt<bool> SkipBodies = llvm::cl::opt<bool>(
53+
"skip-bodies",
54+
llvm::cl::desc("skip function bodies and type members if possible"));
55+
56+
llvm::cl::list<std::string> InputPaths = llvm::cl::list<std::string>(
57+
llvm::cl::Positional, llvm::cl::desc("input paths"));
58+
};
59+
60+
struct LibParseExecutor {
61+
constexpr static StringRef name = "libParse";
62+
63+
static void performParse(llvm::MemoryBufferRef buffer, ExecuteOptions opts) {
64+
SourceManager SM;
65+
unsigned bufferID =
66+
SM.addNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(buffer));
67+
DiagnosticEngine diagEngine(SM);
68+
LangOptions langOpts;
69+
TypeCheckerOptions typeckOpts;
70+
SILOptions silOpts;
71+
SearchPathOptions searchPathOpts;
72+
ClangImporterOptions clangOpts;
73+
symbolgraphgen::SymbolGraphOptions symbolOpts;
74+
std::unique_ptr<ASTContext> ctx(
75+
ASTContext::get(langOpts, typeckOpts, silOpts, searchPathOpts,
76+
clangOpts, symbolOpts, SM, diagEngine));
77+
78+
SourceFile::ParsingOptions parseOpts;
79+
parseOpts |= SourceFile::ParsingFlags::DisablePoundIfEvaluation;
80+
if (!opts.contains(ExecuteOptionFlag::SkipBodies))
81+
parseOpts |= SourceFile::ParsingFlags::DisableDelayedBodies;
82+
83+
ModuleDecl *M = ModuleDecl::create(Identifier(), *ctx);
84+
SourceFile *SF =
85+
new (*ctx) SourceFile(*M, SourceFileKind::Library, bufferID, parseOpts);
86+
87+
Parser parser(bufferID, *SF, /*SILParserState=*/nullptr);
88+
SmallVector<ASTNode> items;
89+
parser.parseTopLevelItems(items);
90+
}
91+
};
92+
93+
struct SwiftParserExecutor {
94+
constexpr static StringRef name = "SwiftParser";
95+
96+
static void performParse(llvm::MemoryBufferRef buffer, ExecuteOptions opts) {
97+
#if SWIFT_BUILD_SWIFT_SYNTAX
98+
// TODO: Implement 'ExecuteOptionFlag::SkipBodies'
99+
auto sourceFile = swift_ASTGen_parseSourceFile(
100+
buffer.getBufferStart(), buffer.getBufferSize(), /*moduleName=*/"",
101+
buffer.getBufferIdentifier().data(), /*ASTContext=*/nullptr);
102+
swift_ASTGen_destroySourceFile(sourceFile);
103+
#endif
104+
}
105+
};
106+
107+
static void _loadSwiftFilesRecursively(
108+
StringRef path,
109+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers) {
110+
if (llvm::sys::fs::is_directory(path)) {
111+
std::error_code err;
112+
for (auto I = llvm::sys::fs::directory_iterator(path, err),
113+
E = llvm::sys::fs::directory_iterator();
114+
I != E; I.increment(err)) {
115+
_loadSwiftFilesRecursively(I->path(), buffers);
116+
}
117+
} else if (path.endswith(".swift")) {
118+
if (auto buffer = llvm::MemoryBuffer::getFile(path)) {
119+
buffers.push_back(std::move(*buffer));
120+
}
121+
}
122+
}
123+
124+
/// Load all '.swift' files in the specified \p filePaths into \p buffers.
125+
/// If the path is a directory, this recursively collects the files in it.
126+
static void
127+
loadSources(ArrayRef<std::string> filePaths,
128+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers) {
129+
for (auto path : filePaths) {
130+
_loadSwiftFilesRecursively(path, buffers);
131+
}
132+
}
133+
134+
/// Measure the duration of \p body execution.
135+
template <typename Duration>
136+
static std::pair<Duration, Duration> measure(llvm::function_ref<void()> body) {
137+
auto cStart = std::clock();
138+
auto tStart = std::chrono::steady_clock::now();
139+
body();
140+
auto cEnd = std::clock();
141+
auto tEnd = std::chrono::steady_clock::now();
142+
143+
auto clockMultiply =
144+
Duration::period::den / CLOCKS_PER_SEC / Duration::period::num;
145+
146+
Duration cDuration((cEnd - cStart) * clockMultiply);
147+
return {std::chrono::duration_cast<Duration>(tEnd - tStart),
148+
std::chrono::duration_cast<Duration>(cDuration)};
149+
}
150+
151+
/// Perform the performance measurement using \c Executor .
152+
/// Parse all \p buffers using \c Executor , \p iteration times, and print out
153+
/// the measurement to the \c stdout.
154+
template <typename Executor>
155+
static void
156+
perform(const SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers,
157+
ExecuteOptions opts, unsigned iteration, uintmax_t totalBytes,
158+
uintmax_t totalLines) {
159+
160+
llvm::outs() << "----\n";
161+
llvm::outs() << "parser: " << Executor::name << "\n";
162+
163+
using duration_t = std::chrono::nanoseconds;
164+
auto tDuration = duration_t::zero();
165+
auto cDuration = duration_t::zero();
166+
167+
for (unsigned i = 0; i < iteration; i++) {
168+
for (auto &buffer : buffers) {
169+
std::pair<duration_t, duration_t> elapsed = measure<duration_t>(
170+
[&] { Executor::performParse(buffer->getMemBufferRef(), opts); });
171+
tDuration += elapsed.first;
172+
cDuration += elapsed.second;
173+
}
174+
}
175+
176+
auto tDisplay =
177+
std::chrono::duration_cast<std::chrono::milliseconds>(tDuration).count();
178+
auto cDisplay =
179+
std::chrono::duration_cast<std::chrono::milliseconds>(cDuration).count();
180+
181+
auto byteTPS = totalBytes * duration_t::period::den / cDuration.count();
182+
auto lineTPS = totalLines * duration_t::period::den / cDuration.count();
183+
184+
llvm::outs() << llvm::format("wall clock time (ms): %8d\n", tDisplay)
185+
<< llvm::format("cpu time (ms): %8d\n", cDisplay)
186+
<< llvm::format("throughput (byte/s): %8d\n", byteTPS)
187+
<< llvm::format("throughput (line/s): %8d\n", lineTPS);
188+
}
189+
190+
} // namespace
191+
192+
int swift_parse_test_main(ArrayRef<const char *> args, const char *argv0,
193+
void *mainAddr) {
194+
SwiftParseTestOptions options;
195+
llvm::cl::ParseCommandLineOptions(args.size(), args.data(),
196+
"Swift parse test\n");
197+
198+
unsigned iteration = options.Iteration;
199+
ExecuteOptions execOptions;
200+
if (options.SkipBodies)
201+
execOptions |= ExecuteOptionFlag::SkipBodies;
202+
203+
SmallVector<std::unique_ptr<llvm::MemoryBuffer>> buffers;
204+
loadSources(options.InputPaths, buffers);
205+
unsigned int byteCount = 0;
206+
unsigned int lineCount = 0;
207+
for (auto &buffer : buffers) {
208+
byteCount += buffer->getBufferSize();
209+
lineCount += buffer->getBuffer().count('\n');
210+
}
211+
212+
llvm::outs() << llvm::format("file count: %8d\n", buffers.size())
213+
<< llvm::format("total bytes: %8d\n", byteCount)
214+
<< llvm::format("total lines: %8d\n", lineCount)
215+
<< llvm::format("iterations: %8d\n", iteration);
216+
for (auto mode : options.Executors) {
217+
switch (mode) {
218+
case Executor::SwiftParser:
219+
#if SWIFT_BUILD_SWIFT_SYNTAX
220+
perform<SwiftParserExecutor>(buffers, execOptions, iteration, byteCount,
221+
lineCount);
222+
break;
223+
#else
224+
llvm::errs() << "error: SwiftParser is not enabled\n";
225+
return 1;
226+
#endif
227+
case Executor::LibParse:
228+
perform<LibParseExecutor>(buffers, execOptions, iteration, byteCount,
229+
lineCount);
230+
break;
231+
}
232+
}
233+
234+
return 0;
235+
}

0 commit comments

Comments
 (0)