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 to build the benchmark in Release mode.
14 //
15 // Usage:
16 //   tools/clang/tools/extra/pseudo/benchmarks/ClangPseudoBenchmark \
17 //      --grammar=../clang-tools-extra/pseudo/lib/cxx.bnf \
18 //      --source=../clang/lib/Sema/SemaDecl.cpp
19 //
20 //===----------------------------------------------------------------------===//
21 
22 #include "benchmark/benchmark.h"
23 #include "clang-pseudo/Bracket.h"
24 #include "clang-pseudo/DirectiveTree.h"
25 #include "clang-pseudo/Forest.h"
26 #include "clang-pseudo/GLR.h"
27 #include "clang-pseudo/Token.h"
28 #include "clang-pseudo/grammar/Grammar.h"
29 #include "clang-pseudo/grammar/LRTable.h"
30 #include "clang/Basic/LangOptions.h"
31 #include "llvm/ADT/StringRef.h"
32 #include "llvm/Support/CommandLine.h"
33 #include "llvm/Support/ErrorOr.h"
34 #include "llvm/Support/MemoryBuffer.h"
35 #include "llvm/Support/raw_ostream.h"
36 #include <string>
37 
38 using llvm::cl::desc;
39 using llvm::cl::opt;
40 using llvm::cl::Required;
41 
42 static opt<std::string> GrammarFile("grammar",
43                                     desc("Parse and check a BNF grammar file."),
44                                     Required);
45 static opt<std::string> Source("source", desc("Source file"), Required);
46 
47 namespace clang {
48 namespace pseudo {
49 namespace bench {
50 namespace {
51 
52 const std::string *GrammarText = nullptr;
53 const std::string *SourceText = nullptr;
54 const Grammar *G = nullptr;
55 
56 void setupGrammarAndSource() {
57   auto ReadFile = [](llvm::StringRef FilePath) -> std::string {
58     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> GrammarText =
59         llvm::MemoryBuffer::getFile(FilePath);
60     if (std::error_code EC = GrammarText.getError()) {
61       llvm::errs() << "Error: can't read file '" << FilePath
62                    << "': " << EC.message() << "\n";
63       std::exit(1);
64     }
65     return GrammarText.get()->getBuffer().str();
66   };
67   GrammarText = new std::string(ReadFile(GrammarFile));
68   SourceText = new std::string(ReadFile(Source));
69   std::vector<std::string> Diags;
70   G = Grammar::parseBNF(*GrammarText, Diags).release();
71 }
72 
73 static void parseBNF(benchmark::State &State) {
74   std::vector<std::string> Diags;
75   for (auto _ : State)
76     Grammar::parseBNF(*GrammarText, Diags);
77 }
78 BENCHMARK(parseBNF);
79 
80 static void buildSLR(benchmark::State &State) {
81   for (auto _ : State)
82     LRTable::buildSLR(*G);
83 }
84 BENCHMARK(buildSLR);
85 
86 TokenStream lexAndPreprocess() {
87   clang::LangOptions LangOpts = genericLangOpts();
88   TokenStream RawStream = pseudo::lex(*SourceText, LangOpts);
89   auto DirectiveStructure = DirectiveTree::parse(RawStream);
90   chooseConditionalBranches(DirectiveStructure, RawStream);
91   TokenStream Cook =
92       cook(DirectiveStructure.stripDirectives(RawStream), LangOpts);
93   auto Stream = stripComments(Cook);
94   pairBrackets(Stream);
95   return Stream;
96 }
97 
98 static void lex(benchmark::State &State) {
99   clang::LangOptions LangOpts = genericLangOpts();
100   for (auto _ : State)
101     clang::pseudo::lex(*SourceText, LangOpts);
102   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
103                           SourceText->size());
104 }
105 BENCHMARK(lex);
106 
107 static void pairBrackets(benchmark::State &State) {
108   clang::LangOptions LangOpts = genericLangOpts();
109   auto Stream = clang::pseudo::lex(*SourceText, LangOpts);
110   for (auto _ : State)
111     pairBrackets(Stream);
112   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
113                           SourceText->size());
114 }
115 BENCHMARK(pairBrackets);
116 
117 static void preprocess(benchmark::State &State) {
118   clang::LangOptions LangOpts = genericLangOpts();
119   TokenStream RawStream = clang::pseudo::lex(*SourceText, LangOpts);
120   for (auto _ : State) {
121     auto DirectiveStructure = DirectiveTree::parse(RawStream);
122     chooseConditionalBranches(DirectiveStructure, RawStream);
123     stripComments(
124         cook(DirectiveStructure.stripDirectives(RawStream), LangOpts));
125   }
126   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
127                           SourceText->size());
128 }
129 BENCHMARK(preprocess);
130 
131 static void glrParse(benchmark::State &State) {
132   LRTable Table = clang::pseudo::LRTable::buildSLR(*G);
133   SymbolID StartSymbol = *G->findNonterminal("translation-unit");
134   TokenStream Stream = lexAndPreprocess();
135   for (auto _ : State) {
136     pseudo::ForestArena Forest;
137     pseudo::GSS GSS;
138     pseudo::glrParse(Stream, ParseParams{*G, Table, Forest, GSS}, StartSymbol);
139   }
140   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
141                           SourceText->size());
142 }
143 BENCHMARK(glrParse);
144 
145 static void full(benchmark::State &State) {
146   LRTable Table = clang::pseudo::LRTable::buildSLR(*G);
147   SymbolID StartSymbol = *G->findNonterminal("translation-unit");
148   for (auto _ : State) {
149     TokenStream Stream = lexAndPreprocess();
150     pseudo::ForestArena Forest;
151     pseudo::GSS GSS;
152     pseudo::glrParse(lexAndPreprocess(), ParseParams{*G, Table, Forest, GSS},
153                      StartSymbol);
154   }
155   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
156                           SourceText->size());
157 }
158 BENCHMARK(full);
159 
160 } // namespace
161 } // namespace bench
162 } // namespace pseudo
163 } // namespace clang
164 
165 int main(int argc, char *argv[]) {
166   benchmark::Initialize(&argc, argv);
167   llvm::cl::ParseCommandLineOptions(argc, argv);
168   clang::pseudo::bench::setupGrammarAndSource();
169   benchmark::RunSpecifiedBenchmarks();
170   return 0;
171 }
172