mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
48ab4b0595
Some Darwin platforms like DriverKit use a system prefix on all of their search paths. Even though DriverKit isn't supported, add support to get the system prefix from SDKSettings when constructing the default search paths. This requires the DarwinSDKInfo to be gotten earlier in CompilerInvocation, pass that down to ASTContext through CompilerInstance. -platform-availability-inheritance-map-path is no longer needed to support visionOS in tests, remove that and its supporting code that gets an alternative DarwinSDKInfo. rdar://166277280
351 lines
12 KiB
C++
351 lines
12 KiB
C++
//===--- swift_parse_test_main.cpp ----------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2023 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// A utility tool to measure the parser performance.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/LangOptions.h"
|
|
#include "swift/Bridging/ASTGen.h"
|
|
#include "swift/Subsystems.h"
|
|
#include "clang/Basic/DarwinSDKInfo.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Error.h"
|
|
|
|
#include <chrono>
|
|
#include <ctime>
|
|
|
|
using namespace swift;
|
|
|
|
namespace {
|
|
|
|
enum class Executor {
|
|
SwiftParser,
|
|
LibParse,
|
|
ASTGen,
|
|
};
|
|
|
|
enum class ExecuteOptionFlag {
|
|
/// Enable body skipping
|
|
SkipBodies = 1 << 0,
|
|
/// Dump result
|
|
Dump = 1 << 1,
|
|
};
|
|
using ExecuteOptions = OptionSet<ExecuteOptionFlag>;
|
|
|
|
struct SwiftParseTestOptions {
|
|
llvm::cl::list<Executor> Executors = llvm::cl::list<Executor>(
|
|
llvm::cl::desc("Available parsers"),
|
|
llvm::cl::values(
|
|
clEnumValN(Executor::SwiftParser, "swift-parser", "SwiftParser"),
|
|
clEnumValN(Executor::ASTGen, "ast-gen", "ASTGen with SwiftParser"),
|
|
clEnumValN(Executor::LibParse, "lib-parse", "libParse")));
|
|
|
|
llvm::cl::opt<unsigned int> Iterations = llvm::cl::opt<unsigned int>(
|
|
"n", llvm::cl::desc("iteration"), llvm::cl::init(1));
|
|
|
|
llvm::cl::opt<bool> SkipBodies = llvm::cl::opt<bool>(
|
|
"skip-bodies",
|
|
llvm::cl::desc("skip function bodies and type members if possible"));
|
|
|
|
llvm::cl::opt<bool> Dump = llvm::cl::opt<bool>(
|
|
"dump", llvm::cl::desc("dump result for each iteration"));
|
|
|
|
llvm::cl::list<std::string> InputPaths = llvm::cl::list<std::string>(
|
|
llvm::cl::Positional, llvm::cl::desc("input paths"));
|
|
};
|
|
|
|
struct LibParseExecutor {
|
|
constexpr static StringRef name = "libParse";
|
|
|
|
static llvm::Error performParse(llvm::MemoryBufferRef buffer,
|
|
ExecuteOptions opts) {
|
|
SourceManager SM;
|
|
unsigned bufferID =
|
|
SM.addNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(buffer));
|
|
DiagnosticEngine diagEngine(SM);
|
|
LangOptions langOpts;
|
|
TypeCheckerOptions typeckOpts;
|
|
SILOptions silOpts;
|
|
SearchPathOptions searchPathOpts;
|
|
ClangImporterOptions clangOpts;
|
|
symbolgraphgen::SymbolGraphOptions symbolOpts;
|
|
CASOptions casOpts;
|
|
SerializationOptions serializationOpts;
|
|
std::optional<clang::DarwinSDKInfo> SDKInfo;
|
|
std::unique_ptr<ASTContext> ctx(ASTContext::get(
|
|
langOpts, typeckOpts, silOpts, searchPathOpts, clangOpts, symbolOpts,
|
|
casOpts, serializationOpts, SM, diagEngine, SDKInfo));
|
|
auto &eval = ctx->evaluator;
|
|
registerParseRequestFunctions(eval);
|
|
registerTypeCheckerRequestFunctions(eval);
|
|
|
|
SourceFile::ParsingOptions parseOpts;
|
|
parseOpts |= SourceFile::ParsingFlags::DisablePoundIfEvaluation;
|
|
if (!opts.contains(ExecuteOptionFlag::SkipBodies))
|
|
parseOpts |= SourceFile::ParsingFlags::DisableDelayedBodies;
|
|
|
|
ModuleDecl *M = ModuleDecl::createEmpty(Identifier(), *ctx);
|
|
SourceFile *SF =
|
|
new (*ctx) SourceFile(*M, SourceFileKind::Library, bufferID, parseOpts);
|
|
|
|
auto items = evaluateOrDefault(eval, ParseSourceFileRequest{SF}, {}).TopLevelItems;
|
|
|
|
if (opts.contains(ExecuteOptionFlag::Dump)) {
|
|
for (auto &item : items) {
|
|
item.dump(llvm::outs());
|
|
}
|
|
}
|
|
|
|
return llvm::Error::success();
|
|
}
|
|
};
|
|
|
|
struct SwiftParserExecutor {
|
|
constexpr static StringRef name = "SwiftParser";
|
|
|
|
static llvm::Error performParse(llvm::MemoryBufferRef buffer,
|
|
ExecuteOptions opts) {
|
|
#if SWIFT_BUILD_SWIFT_SYNTAX
|
|
// TODO: Implement 'ExecuteOptionFlag::SkipBodies'
|
|
auto sourceFile = swift_ASTGen_parseSourceFile(
|
|
buffer.getBuffer(),
|
|
/*moduleName=*/StringRef(), buffer.getBufferIdentifier(),
|
|
/*declContextPtr=*/nullptr, BridgedGeneratedSourceFileKindNone);
|
|
swift_ASTGen_destroySourceFile(sourceFile);
|
|
|
|
if (opts.contains(ExecuteOptionFlag::Dump)) {
|
|
// TODO: Implement.
|
|
}
|
|
|
|
return llvm::Error::success();
|
|
#else
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"SwiftParser is not supported");
|
|
#endif
|
|
}
|
|
};
|
|
|
|
struct ASTGenExecutor {
|
|
constexpr static StringRef name = "ASTGen with SwiftParser";
|
|
|
|
static llvm::Error performParse(llvm::MemoryBufferRef buffer,
|
|
ExecuteOptions opts) {
|
|
#if SWIFT_BUILD_SWIFT_SYNTAX
|
|
|
|
SourceManager SM;
|
|
unsigned bufferID =
|
|
SM.addNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(buffer));
|
|
DiagnosticEngine diagEngine(SM);
|
|
LangOptions langOpts;
|
|
TypeCheckerOptions typeckOpts;
|
|
SILOptions silOpts;
|
|
SearchPathOptions searchPathOpts;
|
|
ClangImporterOptions clangOpts;
|
|
CASOptions casOpts;
|
|
symbolgraphgen::SymbolGraphOptions symbolOpts;
|
|
SerializationOptions serializationOpts;
|
|
std::optional<clang::DarwinSDKInfo> SDKInfo;
|
|
|
|
// Enable ASTGen.
|
|
langOpts.enableFeature(Feature::ParserASTGen);
|
|
|
|
std::unique_ptr<ASTContext> ctx(ASTContext::get(
|
|
langOpts, typeckOpts, silOpts, searchPathOpts, clangOpts, symbolOpts,
|
|
casOpts, serializationOpts, SM, diagEngine, SDKInfo));
|
|
auto &eval = ctx->evaluator;
|
|
registerParseRequestFunctions(eval);
|
|
registerTypeCheckerRequestFunctions(eval);
|
|
|
|
SourceFile::ParsingOptions parseOpts;
|
|
parseOpts |= SourceFile::ParsingFlags::DisablePoundIfEvaluation;
|
|
if (!opts.contains(ExecuteOptionFlag::SkipBodies))
|
|
parseOpts |= SourceFile::ParsingFlags::DisableDelayedBodies;
|
|
|
|
ModuleDecl *M = ModuleDecl::createEmpty(Identifier(), *ctx);
|
|
SourceFile *SF =
|
|
new (*ctx) SourceFile(*M, SourceFileKind::Library, bufferID, parseOpts);
|
|
|
|
auto items = evaluateOrDefault(eval, ParseSourceFileRequest{SF}, {}).TopLevelItems;
|
|
|
|
if (opts.contains(ExecuteOptionFlag::Dump)) {
|
|
for (auto &item : items) {
|
|
item.dump(llvm::outs());
|
|
}
|
|
}
|
|
|
|
return llvm::Error::success();
|
|
#else
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"ASTGen/SwiftParser is not supported");
|
|
#endif
|
|
}
|
|
};
|
|
|
|
static void _loadSwiftFilesRecursively(
|
|
StringRef path,
|
|
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers) {
|
|
if (llvm::sys::fs::is_directory(path)) {
|
|
std::error_code err;
|
|
for (auto I = llvm::sys::fs::directory_iterator(path, err),
|
|
E = llvm::sys::fs::directory_iterator();
|
|
I != E; I.increment(err)) {
|
|
_loadSwiftFilesRecursively(I->path(), buffers);
|
|
}
|
|
} else if (path.ends_with(".swift")) {
|
|
if (auto buffer = llvm::MemoryBuffer::getFile(path)) {
|
|
buffers.push_back(std::move(*buffer));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Load all '.swift' files in the specified \p filePaths into \p buffers.
|
|
/// If the path is a directory, this recursively collects the files in it.
|
|
static void
|
|
loadSources(ArrayRef<std::string> filePaths,
|
|
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers) {
|
|
for (auto path : filePaths) {
|
|
_loadSwiftFilesRecursively(path, buffers);
|
|
}
|
|
}
|
|
|
|
/// Measure the duration of \p body execution. Returns a pair of "wall clock
|
|
/// time" and "CPU time".
|
|
template <typename Duration>
|
|
static std::pair<Duration, Duration> measure(llvm::function_ref<void()> body) {
|
|
auto cStart = std::clock();
|
|
auto tStart = std::chrono::steady_clock::now();
|
|
body();
|
|
auto cEnd = std::clock();
|
|
auto tEnd = std::chrono::steady_clock::now();
|
|
|
|
auto clockMultiply =
|
|
CLOCKS_PER_SEC > 0
|
|
? (Duration::period::den / CLOCKS_PER_SEC / Duration::period::num)
|
|
: 0;
|
|
|
|
Duration cDuration((cEnd - cStart) * clockMultiply);
|
|
return {std::chrono::duration_cast<Duration>(tEnd - tStart),
|
|
std::chrono::duration_cast<Duration>(cDuration)};
|
|
}
|
|
|
|
/// Perform the performance measurement using \c Executor .
|
|
/// Parse all \p buffers using \c Executor , \p iteration times, and print out
|
|
/// the measurement to the \c stdout.
|
|
template <typename Executor>
|
|
static llvm::Error
|
|
perform(const SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers,
|
|
ExecuteOptions opts, unsigned iteration, uintmax_t totalBytes,
|
|
uintmax_t totalLines) {
|
|
|
|
llvm::outs() << "----\n";
|
|
llvm::outs() << "parser: " << Executor::name << "\n";
|
|
|
|
using duration_t = std::chrono::nanoseconds;
|
|
// Wall clock time.
|
|
auto tDuration = duration_t::zero();
|
|
// CPU time.
|
|
auto cDuration = duration_t::zero();
|
|
|
|
llvm::Error err = llvm::Error::success();
|
|
(void)bool(err);
|
|
|
|
for (unsigned i = 0; i < iteration; i++) {
|
|
for (auto &buffer : buffers) {
|
|
std::pair<duration_t, duration_t> elapsed = measure<duration_t>([&] {
|
|
err = Executor::performParse(buffer->getMemBufferRef(), opts);
|
|
});
|
|
if (err)
|
|
return err;
|
|
tDuration += elapsed.first;
|
|
cDuration += elapsed.second;
|
|
}
|
|
}
|
|
|
|
auto tDisplay =
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(tDuration).count();
|
|
auto cDisplay =
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(cDuration).count();
|
|
llvm::outs() << llvm::format("wall clock time (ms): %8d\n", tDisplay)
|
|
<< llvm::format("cpu time (ms): %8d\n", cDisplay);
|
|
|
|
if (cDuration.count() > 0) {
|
|
// Throughputs are based on CPU time.
|
|
auto byteTPS = totalBytes * duration_t::period::den / cDuration.count();
|
|
auto lineTPS = totalLines * duration_t::period::den / cDuration.count();
|
|
|
|
llvm::outs() << llvm::format("throughput (byte/s): %8d\n", byteTPS)
|
|
<< llvm::format("throughput (line/s): %8d\n", lineTPS);
|
|
}
|
|
|
|
return llvm::Error::success();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int swift_parse_test_main(ArrayRef<const char *> args, const char *argv0,
|
|
void *mainAddr) {
|
|
SwiftParseTestOptions options;
|
|
llvm::cl::ParseCommandLineOptions(args.size(), args.data(),
|
|
"Swift parse test\n");
|
|
|
|
unsigned iterations = options.Iterations;
|
|
ExecuteOptions execOptions;
|
|
if (options.SkipBodies)
|
|
execOptions |= ExecuteOptionFlag::SkipBodies;
|
|
if (options.Dump)
|
|
execOptions |= ExecuteOptionFlag::Dump;
|
|
|
|
SmallVector<std::unique_ptr<llvm::MemoryBuffer>> buffers;
|
|
loadSources(options.InputPaths, buffers);
|
|
unsigned int byteCount = 0;
|
|
unsigned int lineCount = 0;
|
|
for (auto &buffer : buffers) {
|
|
byteCount += buffer->getBufferSize();
|
|
lineCount += buffer->getBuffer().count('\n');
|
|
}
|
|
|
|
llvm::outs() << llvm::format("file count: %8d\n", buffers.size())
|
|
<< llvm::format("total bytes: %8d\n", byteCount)
|
|
<< llvm::format("total lines: %8d\n", lineCount)
|
|
<< llvm::format("iterations: %8d\n", iterations);
|
|
|
|
llvm::Error err = llvm::Error::success();
|
|
(void)bool(err);
|
|
|
|
for (auto mode : options.Executors) {
|
|
switch (mode) {
|
|
#define CASE(NAME, EXECUTOR) \
|
|
case Executor::NAME: \
|
|
err = perform<EXECUTOR>(buffers, execOptions, iterations, byteCount, \
|
|
lineCount); \
|
|
break;
|
|
CASE(LibParse, LibParseExecutor)
|
|
CASE(SwiftParser, SwiftParserExecutor)
|
|
CASE(ASTGen, ASTGenExecutor)
|
|
}
|
|
if (err)
|
|
break;
|
|
}
|
|
|
|
if (err) {
|
|
llvm::handleAllErrors(std::move(err), [](llvm::ErrorInfoBase &info) {
|
|
llvm::errs() << "error: " << info.message() << "\n";
|
|
});
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|