1 //===--- Benchmark.cpp -  clang pseudoparser benchmarks ---------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Benchmark for the overall pseudoparser performance, it also includes other
10 // important pieces of the pseudoparser (grammar compliation, LR table build
11 // etc).
12 //
13 // Note: make sure we build it in Relase mode.
14 //
15 // Usage:
16 //   tools/clang/tools/extra/pseudo/benchmarks/ClangPseudoBenchmark \
17 //      --grammar=/path/to/cxx.bnf --source=/patch/to/source-to-parse.cpp \
18 //      --benchmark_filter=runParseOverall
19 //
20 //===----------------------------------------------------------------------===//
21 
22 #include "benchmark/benchmark.h"
23 #include "clang-pseudo/DirectiveTree.h"
24 #include "clang-pseudo/Forest.h"
25 #include "clang-pseudo/GLR.h"
26 #include "clang-pseudo/Grammar.h"
27 #include "clang-pseudo/LRTable.h"
28 #include "clang-pseudo/Token.h"
29 #include "clang/Basic/LangOptions.h"
30 #include "llvm/ADT/StringRef.h"
31 #include "llvm/Support/CommandLine.h"
32 #include "llvm/Support/ErrorOr.h"
33 #include "llvm/Support/MemoryBuffer.h"
34 #include "llvm/Support/raw_ostream.h"
35 #include <string>
36 
37 using llvm::cl::desc;
38 using llvm::cl::init;
39 using llvm::cl::opt;
40 
41 static opt<std::string> GrammarFile("grammar",
42                                     desc("Parse and check a BNF grammar file."),
43                                     init(""));
44 static opt<std::string> Source("source", desc("Source file"));
45 
46 namespace clang {
47 namespace pseudo {
48 namespace {
49 
50 const std::string *GrammarText = nullptr;
51 const std::string *SourceText = nullptr;
52 const Grammar *G = nullptr;
53 
54 void setupGrammarAndSource() {
55   auto ReadFile = [](llvm::StringRef FilePath) -> std::string {
56     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> GrammarText =
57         llvm::MemoryBuffer::getFile(FilePath);
58     if (std::error_code EC = GrammarText.getError()) {
59       llvm::errs() << "Error: can't read file '" << FilePath
60                    << "': " << EC.message() << "\n";
61       std::exit(1);
62     }
63     return GrammarText.get()->getBuffer().str();
64   };
65   GrammarText = new std::string(ReadFile(GrammarFile));
66   SourceText = new std::string(ReadFile(Source));
67   std::vector<std::string> Diags;
68   G = Grammar::parseBNF(*GrammarText, Diags).release();
69 }
70 
71 static void runParseBNFGrammar(benchmark::State &State) {
72   std::vector<std::string> Diags;
73   for (auto _ : State)
74     Grammar::parseBNF(*GrammarText, Diags);
75 }
76 BENCHMARK(runParseBNFGrammar);
77 
78 static void runBuildLR(benchmark::State &State) {
79   for (auto _ : State)
80     clang::pseudo::LRTable::buildSLR(*G);
81 }
82 BENCHMARK(runBuildLR);
83 
84 TokenStream parseableTokenStream() {
85   clang::LangOptions LangOpts = genericLangOpts();
86   TokenStream RawStream = clang::pseudo::lex(*SourceText, LangOpts);
87   auto DirectiveStructure = DirectiveTree::parse(RawStream);
88   clang::pseudo::chooseConditionalBranches(DirectiveStructure, RawStream);
89   TokenStream Cook =
90       cook(DirectiveStructure.stripDirectives(RawStream), LangOpts);
91   return clang::pseudo::stripComments(Cook);
92 }
93 
94 static void runPreprocessTokens(benchmark::State &State) {
95   for (auto _ : State)
96     parseableTokenStream();
97   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
98                           SourceText->size());
99 }
100 BENCHMARK(runPreprocessTokens);
101 
102 static void runGLRParse(benchmark::State &State) {
103   clang::LangOptions LangOpts = genericLangOpts();
104   LRTable Table = clang::pseudo::LRTable::buildSLR(*G);
105   TokenStream ParseableStream = parseableTokenStream();
106   SymbolID StartSymbol = *G->findNonterminal("translation-unit");
107   for (auto _ : State) {
108     pseudo::ForestArena Forest;
109     pseudo::GSS GSS;
110     glrParse(ParseableStream, ParseParams{*G, Table, Forest, GSS}, StartSymbol);
111   }
112   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
113                           SourceText->size());
114 }
115 BENCHMARK(runGLRParse);
116 
117 static void runParseOverall(benchmark::State &State) {
118   clang::LangOptions LangOpts = genericLangOpts();
119   LRTable Table = clang::pseudo::LRTable::buildSLR(*G);
120   SymbolID StartSymbol = *G->findNonterminal("translation-unit");
121   for (auto _ : State) {
122     pseudo::ForestArena Forest;
123     pseudo::GSS GSS;
124     glrParse(parseableTokenStream(), ParseParams{*G, Table, Forest, GSS},
125              StartSymbol);
126   }
127   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
128                           SourceText->size());
129 }
130 BENCHMARK(runParseOverall);
131 
132 } // namespace
133 } // namespace pseudo
134 } // namespace clang
135 
136 int main(int argc, char *argv[]) {
137   benchmark::Initialize(&argc, argv);
138   llvm::cl::ParseCommandLineOptions(argc, argv);
139   clang::pseudo::setupGrammarAndSource();
140   benchmark::RunSpecifiedBenchmarks();
141   return 0;
142 }
143