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/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::opt;
39 using llvm::cl::Required;
40 
41 static opt<std::string> GrammarFile("grammar",
42                                     desc("Parse and check a BNF grammar file."),
43                                     Required);
44 static opt<std::string> Source("source", desc("Source file"), Required);
45 
46 namespace clang {
47 namespace pseudo {
48 namespace bench {
49 namespace {
50 
51 const std::string *GrammarText = nullptr;
52 const std::string *SourceText = nullptr;
53 const Grammar *G = nullptr;
54 
55 void setupGrammarAndSource() {
56   auto ReadFile = [](llvm::StringRef FilePath) -> std::string {
57     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> GrammarText =
58         llvm::MemoryBuffer::getFile(FilePath);
59     if (std::error_code EC = GrammarText.getError()) {
60       llvm::errs() << "Error: can't read file '" << FilePath
61                    << "': " << EC.message() << "\n";
62       std::exit(1);
63     }
64     return GrammarText.get()->getBuffer().str();
65   };
66   GrammarText = new std::string(ReadFile(GrammarFile));
67   SourceText = new std::string(ReadFile(Source));
68   std::vector<std::string> Diags;
69   G = Grammar::parseBNF(*GrammarText, Diags).release();
70 }
71 
72 static void parseBNF(benchmark::State &State) {
73   std::vector<std::string> Diags;
74   for (auto _ : State)
75     Grammar::parseBNF(*GrammarText, Diags);
76 }
77 BENCHMARK(parseBNF);
78 
79 static void buildSLR(benchmark::State &State) {
80   for (auto _ : State)
81     LRTable::buildSLR(*G);
82 }
83 BENCHMARK(buildSLR);
84 
85 TokenStream lexAndPreprocess() {
86   clang::LangOptions LangOpts = genericLangOpts();
87   TokenStream RawStream = pseudo::lex(*SourceText, LangOpts);
88   auto DirectiveStructure = DirectiveTree::parse(RawStream);
89   chooseConditionalBranches(DirectiveStructure, RawStream);
90   TokenStream Cook =
91       cook(DirectiveStructure.stripDirectives(RawStream), LangOpts);
92   return stripComments(Cook);
93 }
94 
95 static void lex(benchmark::State &State) {
96   clang::LangOptions LangOpts = genericLangOpts();
97   for (auto _ : State)
98     clang::pseudo::lex(*SourceText, LangOpts);
99   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
100                           SourceText->size());
101 }
102 BENCHMARK(lex);
103 
104 static void preprocess(benchmark::State &State) {
105   clang::LangOptions LangOpts = genericLangOpts();
106   TokenStream RawStream = clang::pseudo::lex(*SourceText, LangOpts);
107   for (auto _ : State) {
108     auto DirectiveStructure = DirectiveTree::parse(RawStream);
109     chooseConditionalBranches(DirectiveStructure, RawStream);
110     stripComments(
111         cook(DirectiveStructure.stripDirectives(RawStream), LangOpts));
112   }
113   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
114                           SourceText->size());
115 }
116 BENCHMARK(preprocess);
117 
118 static void glrParse(benchmark::State &State) {
119   LRTable Table = clang::pseudo::LRTable::buildSLR(*G);
120   SymbolID StartSymbol = *G->findNonterminal("translation-unit");
121   TokenStream Stream = lexAndPreprocess();
122   for (auto _ : State) {
123     pseudo::ForestArena Forest;
124     pseudo::GSS GSS;
125     pseudo::glrParse(Stream, ParseParams{*G, Table, Forest, GSS}, StartSymbol);
126   }
127   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
128                           SourceText->size());
129 }
130 BENCHMARK(glrParse);
131 
132 static void full(benchmark::State &State) {
133   LRTable Table = clang::pseudo::LRTable::buildSLR(*G);
134   SymbolID StartSymbol = *G->findNonterminal("translation-unit");
135   for (auto _ : State) {
136     TokenStream Stream = lexAndPreprocess();
137     pseudo::ForestArena Forest;
138     pseudo::GSS GSS;
139     pseudo::glrParse(lexAndPreprocess(), ParseParams{*G, Table, Forest, GSS},
140                      StartSymbol);
141   }
142   State.SetBytesProcessed(static_cast<uint64_t>(State.iterations()) *
143                           SourceText->size());
144 }
145 BENCHMARK(full);
146 
147 } // namespace
148 } // namespace bench
149 } // namespace pseudo
150 } // namespace clang
151 
152 int main(int argc, char *argv[]) {
153   benchmark::Initialize(&argc, argv);
154   llvm::cl::ParseCommandLineOptions(argc, argv);
155   clang::pseudo::bench::setupGrammarAndSource();
156   benchmark::RunSpecifiedBenchmarks();
157   return 0;
158 }
159