1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "Driver.h"
18 
19 #include <err.h>
20 #include <string.h>
21 
22 #include <chrono>
23 #include <mutex>
24 #include <string>
25 #include <thread>
26 #include <unordered_map>
27 #include <vector>
28 
29 #include <clang/AST/ASTConsumer.h>
30 #include <clang/Basic/Diagnostic.h>
31 #include <clang/Basic/TargetInfo.h>
32 #include <clang/Driver/Compilation.h>
33 #include <clang/Driver/Driver.h>
34 #include <clang/Frontend/CompilerInstance.h>
35 #include <clang/Frontend/CompilerInvocation.h>
36 #include <clang/Frontend/FrontendAction.h>
37 #include <clang/Frontend/FrontendActions.h>
38 #include <clang/Frontend/TextDiagnosticPrinter.h>
39 #include <clang/Frontend/Utils.h>
40 #include <clang/FrontendTool/Utils.h>
41 #include <llvm/ADT/IntrusiveRefCntPtr.h>
42 #include <llvm/ADT/SmallVector.h>
43 #include <llvm/ADT/StringRef.h>
44 #include <llvm/Option/Option.h>
45 #include <llvm/Support/Host.h>
46 #include <llvm/Support/VirtualFileSystem.h>
47 
48 #include "Arch.h"
49 #include "DeclarationDatabase.h"
50 #include "versioner.h"
51 
52 using namespace std::chrono_literals;
53 using namespace std::string_literals;
54 
55 using namespace clang;
56 
57 class VersionerASTConsumer : public clang::ASTConsumer {
58  public:
59   HeaderDatabase* header_database;
60   CompilationType type;
61 
VersionerASTConsumer(HeaderDatabase * header_database,CompilationType type)62   VersionerASTConsumer(HeaderDatabase* header_database, CompilationType type)
63       : header_database(header_database), type(type) {
64   }
65 
HandleTranslationUnit(ASTContext & ctx)66   void HandleTranslationUnit(ASTContext& ctx) override {
67     header_database->parseAST(type, ctx);
68   }
69 };
70 
71 class VersionerASTAction : public clang::ASTFrontendAction {
72  public:
73   HeaderDatabase* header_database;
74   CompilationType type;
75 
VersionerASTAction(HeaderDatabase * header_database,CompilationType type)76   VersionerASTAction(HeaderDatabase* header_database, CompilationType type)
77       : header_database(header_database), type(type) {
78   }
79 
CreateASTConsumer(CompilerInstance &,llvm::StringRef)80   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance&, llvm::StringRef) override {
81     return std::make_unique<VersionerASTConsumer>(header_database, type);
82   }
83 };
84 
constructDiags()85 static IntrusiveRefCntPtr<DiagnosticsEngine> constructDiags() {
86   IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions());
87   auto diag_printer = std::make_unique<TextDiagnosticPrinter>(llvm::errs(), diag_opts.get());
88   IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
89   IntrusiveRefCntPtr<DiagnosticsEngine> diags(
90       new DiagnosticsEngine(diag_ids.get(), diag_opts.get(), diag_printer.release()));
91   return diags;
92 }
93 
94 // clang's driver is slow compared to the work it performs to compile our headers.
95 // Run it once to generate flags for each target, and memoize the results.
96 static std::unordered_map<CompilationType, std::vector<std::string>> cc1_flags;
97 static const char* filename_placeholder = "__VERSIONER_PLACEHOLDER__";
generateTargetCC1Flags(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,CompilationType type,const std::vector<std::string> & include_dirs)98 static void generateTargetCC1Flags(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,
99                                    CompilationType type,
100                                    const std::vector<std::string>& include_dirs) {
101   std::vector<std::string> cmd = { "versioner" };
102   if (type.cpp) {
103     cmd.push_back("-std=gnu++11");
104     cmd.push_back("-x");
105     cmd.push_back("c++");
106   } else {
107     cmd.push_back("-std=gnu11");
108     cmd.push_back("-x");
109     cmd.push_back("c");
110   }
111 
112   cmd.push_back("-fsyntax-only");
113 
114   cmd.push_back("-Wall");
115   cmd.push_back("-Wextra");
116   cmd.push_back("-Weverything");
117   cmd.push_back("-Werror");
118   cmd.push_back("-Wundef");
119   cmd.push_back("-Wno-unused-macros");
120   cmd.push_back("-Wno-unused-function");
121   cmd.push_back("-Wno-unused-variable");
122   cmd.push_back("-Wno-unknown-attributes");
123   cmd.push_back("-Wno-pragma-once-outside-header");
124 
125   cmd.push_back("-target");
126   cmd.push_back(arch_targets[type.arch]);
127 
128   cmd.push_back("-DANDROID");
129   cmd.push_back("-D__BIONIC_VERSIONER=1");
130   cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level));
131   cmd.push_back("-D_FORTIFY_SOURCE=2");
132   cmd.push_back("-D_GNU_SOURCE");
133   cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits));
134 
135   cmd.push_back("-nostdinc");
136 
137   if (add_include) {
138     cmd.push_back("-include");
139     cmd.push_back("android/versioning.h");
140   }
141 
142   for (const auto& dir : include_dirs) {
143     cmd.push_back("-isystem");
144     cmd.push_back(dir);
145   }
146 
147   cmd.push_back("-include");
148   cmd.push_back(filename_placeholder);
149   cmd.push_back("-");
150 
151   auto diags = constructDiags();
152   driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags, "versioner", vfs);
153   driver.setCheckInputsExist(false);
154 
155   llvm::SmallVector<const char*, 32> driver_args;
156   for (const std::string& str : cmd) {
157     driver_args.push_back(str.c_str());
158   }
159 
160   std::unique_ptr<driver::Compilation> Compilation(driver.BuildCompilation(driver_args));
161   const driver::JobList& jobs = Compilation->getJobs();
162   if (jobs.size() != 1) {
163     errx(1, "driver returned %zu jobs for %s", jobs.size(), to_string(type).c_str());
164   }
165 
166   const driver::Command& driver_cmd = llvm::cast<driver::Command>(*jobs.begin());
167   const llvm::opt::ArgStringList& cc_args = driver_cmd.getArguments();
168 
169   if (cc_args.size() == 0) {
170     errx(1, "driver returned empty command for %s", to_string(type).c_str());
171   }
172 
173   std::vector<std::string> result(cc_args.begin(), cc_args.end());
174 
175   {
176     static std::mutex cc1_init_mutex;
177     std::unique_lock<std::mutex> lock(cc1_init_mutex);
178     if (cc1_flags.count(type) > 0) {
179       errx(1, "attemped to generate cc1 flags for existing CompilationType %s",
180            to_string(type).c_str());
181     }
182 
183     cc1_flags.emplace(std::make_pair(type, std::move(result)));
184   }
185 }
186 
getCC1Command(CompilationType type,const std::string & filename)187 static std::vector<const char*> getCC1Command(CompilationType type, const std::string& filename) {
188   const auto& target_flag_it = cc1_flags.find(type);
189   if (target_flag_it == cc1_flags.end()) {
190     errx(1, "failed to find target flags for CompilationType %s", to_string(type).c_str());
191   }
192 
193   std::vector<const char*> result;
194   for (const std::string& flag : target_flag_it->second) {
195     if (flag == "-disable-free") {
196       continue;
197     } else if (flag == filename_placeholder) {
198       result.push_back(filename.c_str());
199     } else {
200       result.push_back(flag.c_str());
201     }
202   }
203   return result;
204 }
205 
initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,const std::set<CompilationType> & types,const std::unordered_map<Arch,CompilationRequirements> & reqs)206 void initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,
207                                   const std::set<CompilationType>& types,
208                                   const std::unordered_map<Arch, CompilationRequirements>& reqs) {
209   if (!cc1_flags.empty()) {
210     errx(1, "reinitializing target CC1 flag cache?");
211   }
212 
213   auto start = std::chrono::high_resolution_clock::now();
214   std::vector<std::thread> threads;
215   for (const CompilationType type : types) {
216     threads.emplace_back([type, &vfs, &reqs]() {
217       const auto& arch_req_it = reqs.find(type.arch);
218       if (arch_req_it == reqs.end()) {
219         errx(1, "CompilationRequirement map missing entry for CompilationType %s",
220              to_string(type).c_str());
221       }
222 
223       generateTargetCC1Flags(vfs, type, arch_req_it->second.dependencies);
224     });
225   }
226   for (auto& thread : threads) {
227     thread.join();
228   }
229   auto end = std::chrono::high_resolution_clock::now();
230 
231   if (verbose) {
232     auto diff = (end - start) / 1.0ms;
233     printf("Generated compiler flags for %zu targets in %0.2Lfms\n", types.size(), diff);
234   }
235 
236   if (cc1_flags.empty()) {
237     errx(1, "failed to initialize target CC1 flag cache");
238   }
239 }
240 
compileHeader(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,HeaderDatabase * header_database,CompilationType type,const std::string & filename)241 void compileHeader(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,
242                    HeaderDatabase* header_database, CompilationType type,
243                    const std::string& filename) {
244   auto diags = constructDiags();
245   std::vector<const char*> cc1_flags = getCC1Command(type, filename);
246   auto invocation = std::make_unique<CompilerInvocation>();
247   if (!CompilerInvocation::CreateFromArgs(*invocation.get(), cc1_flags, *diags)) {
248     errx(1, "failed to create CompilerInvocation");
249   }
250 
251   clang::CompilerInstance Compiler;
252 
253   Compiler.setInvocation(std::move(invocation));
254   Compiler.setDiagnostics(diags.get());
255   Compiler.createFileManager(vfs);
256 
257   VersionerASTAction versioner_action(header_database, type);
258   if (!Compiler.ExecuteAction(versioner_action)) {
259     errx(1, "compilation generated warnings or errors");
260   }
261 
262   if (diags->getNumWarnings() || diags->hasErrorOccurred()) {
263     errx(1, "compilation generated warnings or errors");
264   }
265 }
266