| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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> | 
| Logan Chien | aef762a | 2018-12-04 17:17:51 +0800 | [diff] [blame] | 44 | #include <llvm/Option/Option.h> | 
| Logan Chien | 6c148d1 | 2018-11-07 10:35:38 +0800 | [diff] [blame] | 45 | #include <llvm/Support/VirtualFileSystem.h> | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 46 |  | 
 | 47 | #include "Arch.h" | 
 | 48 | #include "DeclarationDatabase.h" | 
 | 49 | #include "versioner.h" | 
 | 50 |  | 
 | 51 | using namespace std::chrono_literals; | 
 | 52 | using namespace std::string_literals; | 
 | 53 |  | 
 | 54 | using namespace clang; | 
 | 55 |  | 
 | 56 | class VersionerASTConsumer : public clang::ASTConsumer { | 
 | 57 |  public: | 
 | 58 |   HeaderDatabase* header_database; | 
 | 59 |   CompilationType type; | 
 | 60 |  | 
 | 61 |   VersionerASTConsumer(HeaderDatabase* header_database, CompilationType type) | 
 | 62 |       : header_database(header_database), type(type) { | 
 | 63 |   } | 
 | 64 |  | 
 | 65 |   virtual void HandleTranslationUnit(ASTContext& ctx) override { | 
 | 66 |     header_database->parseAST(type, ctx); | 
 | 67 |   } | 
 | 68 | }; | 
 | 69 |  | 
 | 70 | class VersionerASTAction : public clang::ASTFrontendAction { | 
 | 71 |  public: | 
 | 72 |   HeaderDatabase* header_database; | 
 | 73 |   CompilationType type; | 
 | 74 |  | 
 | 75 |   VersionerASTAction(HeaderDatabase* header_database, CompilationType type) | 
 | 76 |       : header_database(header_database), type(type) { | 
 | 77 |   } | 
 | 78 |  | 
 | 79 |   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance&, llvm::StringRef) override { | 
 | 80 |     return std::make_unique<VersionerASTConsumer>(header_database, type); | 
 | 81 |   } | 
 | 82 | }; | 
 | 83 |  | 
 | 84 | static IntrusiveRefCntPtr<DiagnosticsEngine> constructDiags() { | 
 | 85 |   IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions()); | 
 | 86 |   auto diag_printer = std::make_unique<TextDiagnosticPrinter>(llvm::errs(), diag_opts.get()); | 
 | 87 |   IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs()); | 
 | 88 |   IntrusiveRefCntPtr<DiagnosticsEngine> diags( | 
 | 89 |       new DiagnosticsEngine(diag_ids.get(), diag_opts.get(), diag_printer.release())); | 
 | 90 |   return diags; | 
 | 91 | } | 
 | 92 |  | 
 | 93 | // clang's driver is slow compared to the work it performs to compile our headers. | 
 | 94 | // Run it once to generate flags for each target, and memoize the results. | 
 | 95 | static std::unordered_map<CompilationType, std::vector<std::string>> cc1_flags; | 
 | 96 | static const char* filename_placeholder = "__VERSIONER_PLACEHOLDER__"; | 
| Logan Chien | 6c148d1 | 2018-11-07 10:35:38 +0800 | [diff] [blame] | 97 | static void generateTargetCC1Flags(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs, | 
| Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 98 |                                    CompilationType type, | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 99 |                                    const std::vector<std::string>& include_dirs) { | 
 | 100 |   std::vector<std::string> cmd = { "versioner" }; | 
| Josh Gao | ab25d0b | 2017-08-10 10:50:33 -0700 | [diff] [blame] | 101 |   if (type.cpp) { | 
 | 102 |     cmd.push_back("-std=gnu++11"); | 
 | 103 |     cmd.push_back("-x"); | 
 | 104 |     cmd.push_back("c++"); | 
 | 105 |   } else { | 
 | 106 |     cmd.push_back("-std=gnu11"); | 
 | 107 |     cmd.push_back("-x"); | 
 | 108 |     cmd.push_back("c"); | 
 | 109 |   } | 
 | 110 |  | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 111 |   cmd.push_back("-fsyntax-only"); | 
 | 112 |  | 
 | 113 |   cmd.push_back("-Wall"); | 
 | 114 |   cmd.push_back("-Wextra"); | 
| Josh Gao | d2ab9ff | 2017-07-28 12:53:36 -0700 | [diff] [blame] | 115 |   cmd.push_back("-Weverything"); | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 116 |   cmd.push_back("-Werror"); | 
 | 117 |   cmd.push_back("-Wundef"); | 
 | 118 |   cmd.push_back("-Wno-unused-macros"); | 
 | 119 |   cmd.push_back("-Wno-unused-function"); | 
 | 120 |   cmd.push_back("-Wno-unused-variable"); | 
 | 121 |   cmd.push_back("-Wno-unknown-attributes"); | 
 | 122 |   cmd.push_back("-Wno-pragma-once-outside-header"); | 
 | 123 |  | 
 | 124 |   cmd.push_back("-target"); | 
 | 125 |   cmd.push_back(arch_targets[type.arch]); | 
 | 126 |  | 
 | 127 |   cmd.push_back("-DANDROID"); | 
 | 128 |   cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level)); | 
| George Burgess IV | 1de2e35 | 2017-08-15 11:03:07 -0700 | [diff] [blame] | 129 |   // FIXME: Re-enable FORTIFY properly once our clang in external/ is new enough | 
 | 130 |   // to support diagnose_if without giving us syntax errors. | 
 | 131 | #if 0 | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 132 |   cmd.push_back("-D_FORTIFY_SOURCE=2"); | 
| George Burgess IV | 1de2e35 | 2017-08-15 11:03:07 -0700 | [diff] [blame] | 133 | #else | 
 | 134 |   cmd.push_back("-D_FORTIFY_SOURCE=0"); | 
 | 135 |   cmd.push_back("-D__BIONIC_DECLARE_FORTIFY_HELPERS"); | 
 | 136 | #endif | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 137 |   cmd.push_back("-D_GNU_SOURCE"); | 
 | 138 |   cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits)); | 
 | 139 |  | 
 | 140 |   cmd.push_back("-nostdinc"); | 
 | 141 |  | 
 | 142 |   if (add_include) { | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 143 |     cmd.push_back("-include"); | 
| Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 144 |     cmd.push_back("android/versioning.h"); | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 145 |   } | 
 | 146 |  | 
 | 147 |   for (const auto& dir : include_dirs) { | 
 | 148 |     cmd.push_back("-isystem"); | 
 | 149 |     cmd.push_back(dir); | 
 | 150 |   } | 
 | 151 |  | 
| Josh Gao | d2ab9ff | 2017-07-28 12:53:36 -0700 | [diff] [blame] | 152 |   cmd.push_back("-include"); | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 153 |   cmd.push_back(filename_placeholder); | 
| Josh Gao | d2ab9ff | 2017-07-28 12:53:36 -0700 | [diff] [blame] | 154 |   cmd.push_back("-"); | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 155 |  | 
 | 156 |   auto diags = constructDiags(); | 
| Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 157 |   driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags, vfs); | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 158 |   driver.setCheckInputsExist(false); | 
 | 159 |  | 
 | 160 |   llvm::SmallVector<const char*, 32> driver_args; | 
 | 161 |   for (const std::string& str : cmd) { | 
 | 162 |     driver_args.push_back(str.c_str()); | 
 | 163 |   } | 
 | 164 |  | 
 | 165 |   std::unique_ptr<driver::Compilation> Compilation(driver.BuildCompilation(driver_args)); | 
 | 166 |   const driver::JobList& jobs = Compilation->getJobs(); | 
 | 167 |   if (jobs.size() != 1) { | 
 | 168 |     errx(1, "driver returned %zu jobs for %s", jobs.size(), to_string(type).c_str()); | 
 | 169 |   } | 
 | 170 |  | 
 | 171 |   const driver::Command& driver_cmd = llvm::cast<driver::Command>(*jobs.begin()); | 
| Logan Chien | aef762a | 2018-12-04 17:17:51 +0800 | [diff] [blame] | 172 |   const llvm::opt::ArgStringList& cc_args = driver_cmd.getArguments(); | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 173 |  | 
 | 174 |   if (cc_args.size() == 0) { | 
 | 175 |     errx(1, "driver returned empty command for %s", to_string(type).c_str()); | 
 | 176 |   } | 
 | 177 |  | 
 | 178 |   std::vector<std::string> result(cc_args.begin(), cc_args.end()); | 
 | 179 |  | 
 | 180 |   { | 
 | 181 |     static std::mutex cc1_init_mutex; | 
 | 182 |     std::unique_lock<std::mutex> lock(cc1_init_mutex); | 
 | 183 |     if (cc1_flags.count(type) > 0) { | 
 | 184 |       errx(1, "attemped to generate cc1 flags for existing CompilationType %s", | 
 | 185 |            to_string(type).c_str()); | 
 | 186 |     } | 
 | 187 |  | 
 | 188 |     cc1_flags.emplace(std::make_pair(type, std::move(result))); | 
 | 189 |   } | 
 | 190 | } | 
 | 191 |  | 
 | 192 | static std::vector<const char*> getCC1Command(CompilationType type, const std::string& filename) { | 
 | 193 |   const auto& target_flag_it = cc1_flags.find(type); | 
 | 194 |   if (target_flag_it == cc1_flags.end()) { | 
 | 195 |     errx(1, "failed to find target flags for CompilationType %s", to_string(type).c_str()); | 
 | 196 |   } | 
 | 197 |  | 
 | 198 |   std::vector<const char*> result; | 
 | 199 |   for (const std::string& flag : target_flag_it->second) { | 
 | 200 |     if (flag == "-disable-free") { | 
 | 201 |       continue; | 
 | 202 |     } else if (flag == filename_placeholder) { | 
 | 203 |       result.push_back(filename.c_str()); | 
 | 204 |     } else { | 
 | 205 |       result.push_back(flag.c_str()); | 
 | 206 |     } | 
 | 207 |   } | 
 | 208 |   return result; | 
 | 209 | } | 
 | 210 |  | 
| Logan Chien | 6c148d1 | 2018-11-07 10:35:38 +0800 | [diff] [blame] | 211 | void initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs, | 
| Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 212 |                                   const std::set<CompilationType>& types, | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 213 |                                   const std::unordered_map<Arch, CompilationRequirements>& reqs) { | 
 | 214 |   if (!cc1_flags.empty()) { | 
 | 215 |     errx(1, "reinitializing target CC1 flag cache?"); | 
 | 216 |   } | 
 | 217 |  | 
 | 218 |   auto start = std::chrono::high_resolution_clock::now(); | 
 | 219 |   std::vector<std::thread> threads; | 
 | 220 |   for (const CompilationType type : types) { | 
| Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 221 |     threads.emplace_back([type, &vfs, &reqs]() { | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 222 |       const auto& arch_req_it = reqs.find(type.arch); | 
 | 223 |       if (arch_req_it == reqs.end()) { | 
 | 224 |         errx(1, "CompilationRequirement map missing entry for CompilationType %s", | 
 | 225 |              to_string(type).c_str()); | 
 | 226 |       } | 
 | 227 |  | 
| Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 228 |       generateTargetCC1Flags(vfs, type, arch_req_it->second.dependencies); | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 229 |     }); | 
 | 230 |   } | 
 | 231 |   for (auto& thread : threads) { | 
 | 232 |     thread.join(); | 
 | 233 |   } | 
 | 234 |   auto end = std::chrono::high_resolution_clock::now(); | 
 | 235 |  | 
 | 236 |   if (verbose) { | 
 | 237 |     auto diff = (end - start) / 1.0ms; | 
 | 238 |     printf("Generated compiler flags for %zu targets in %0.2Lfms\n", types.size(), diff); | 
 | 239 |   } | 
 | 240 |  | 
 | 241 |   if (cc1_flags.empty()) { | 
 | 242 |     errx(1, "failed to initialize target CC1 flag cache"); | 
 | 243 |   } | 
 | 244 | } | 
 | 245 |  | 
| Logan Chien | 6c148d1 | 2018-11-07 10:35:38 +0800 | [diff] [blame] | 246 | void compileHeader(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs, | 
| Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 247 |                    HeaderDatabase* header_database, CompilationType type, | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 248 |                    const std::string& filename) { | 
 | 249 |   auto diags = constructDiags(); | 
 | 250 |   std::vector<const char*> cc1_flags = getCC1Command(type, filename); | 
 | 251 |   auto invocation = std::make_unique<CompilerInvocation>(); | 
 | 252 |   if (!CompilerInvocation::CreateFromArgs(*invocation.get(), &cc1_flags.front(), | 
 | 253 |                                           &cc1_flags.front() + cc1_flags.size(), *diags)) { | 
 | 254 |     errx(1, "failed to create CompilerInvocation"); | 
 | 255 |   } | 
 | 256 |  | 
 | 257 |   clang::CompilerInstance Compiler; | 
| Yi Kong | 06be345 | 2017-04-20 14:27:28 -0700 | [diff] [blame] | 258 |  | 
| Yi Kong | 06be345 | 2017-04-20 14:27:28 -0700 | [diff] [blame] | 259 |   Compiler.setInvocation(std::move(invocation)); | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 260 |   Compiler.setDiagnostics(diags.get()); | 
| Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 261 |   Compiler.setVirtualFileSystem(vfs); | 
| Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 262 |  | 
 | 263 |   VersionerASTAction versioner_action(header_database, type); | 
 | 264 |   if (!Compiler.ExecuteAction(versioner_action)) { | 
 | 265 |     errx(1, "compilation generated warnings or errors"); | 
 | 266 |   } | 
 | 267 |  | 
 | 268 |   if (diags->getNumWarnings() || diags->hasErrorOccurred()) { | 
 | 269 |     errx(1, "compilation generated warnings or errors"); | 
 | 270 |   } | 
 | 271 | } |