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> |
Stephen Hines | 7606477 | 2020-09-22 13:37:42 -0700 | [diff] [blame] | 45 | #include <llvm/Support/Host.h> |
Logan Chien | 6c148d1 | 2018-11-07 10:35:38 +0800 | [diff] [blame] | 46 | #include <llvm/Support/VirtualFileSystem.h> |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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 | |
| 62 | VersionerASTConsumer(HeaderDatabase* header_database, CompilationType type) |
| 63 | : header_database(header_database), type(type) { |
| 64 | } |
| 65 | |
Yi Kong | 358603a | 2019-03-29 14:25:16 -0700 | [diff] [blame] | 66 | void HandleTranslationUnit(ASTContext& ctx) override { |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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 | |
| 76 | VersionerASTAction(HeaderDatabase* header_database, CompilationType type) |
| 77 | : header_database(header_database), type(type) { |
| 78 | } |
| 79 | |
| 80 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance&, llvm::StringRef) override { |
| 81 | return std::make_unique<VersionerASTConsumer>(header_database, type); |
| 82 | } |
| 83 | }; |
| 84 | |
| 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__"; |
Logan Chien | 6c148d1 | 2018-11-07 10:35:38 +0800 | [diff] [blame] | 98 | static void generateTargetCC1Flags(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs, |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 99 | CompilationType type, |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 100 | const std::vector<std::string>& include_dirs) { |
| 101 | std::vector<std::string> cmd = { "versioner" }; |
Josh Gao | ab25d0b | 2017-08-10 10:50:33 -0700 | [diff] [blame] | 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 | |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 112 | cmd.push_back("-fsyntax-only"); |
| 113 | |
| 114 | cmd.push_back("-Wall"); |
| 115 | cmd.push_back("-Wextra"); |
Josh Gao | d2ab9ff | 2017-07-28 12:53:36 -0700 | [diff] [blame] | 116 | cmd.push_back("-Weverything"); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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"); |
Logan Chien | a6bf7f2 | 2019-11-27 07:24:42 -0800 | [diff] [blame] | 129 | cmd.push_back("-D__BIONIC_VERSIONER=1"); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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) { |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 138 | cmd.push_back("-include"); |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 139 | cmd.push_back("android/versioning.h"); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | for (const auto& dir : include_dirs) { |
| 143 | cmd.push_back("-isystem"); |
| 144 | cmd.push_back(dir); |
| 145 | } |
| 146 | |
Josh Gao | d2ab9ff | 2017-07-28 12:53:36 -0700 | [diff] [blame] | 147 | cmd.push_back("-include"); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 148 | cmd.push_back(filename_placeholder); |
Josh Gao | d2ab9ff | 2017-07-28 12:53:36 -0700 | [diff] [blame] | 149 | cmd.push_back("-"); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 150 | |
| 151 | auto diags = constructDiags(); |
Yabin Cui | 1169bf9 | 2021-01-07 15:57:26 -0800 | [diff] [blame] | 152 | driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags, "versioner", vfs); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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()); |
Logan Chien | aef762a | 2018-12-04 17:17:51 +0800 | [diff] [blame] | 167 | const llvm::opt::ArgStringList& cc_args = driver_cmd.getArguments(); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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 | |
| 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 | |
Logan Chien | 6c148d1 | 2018-11-07 10:35:38 +0800 | [diff] [blame] | 206 | void initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs, |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 207 | const std::set<CompilationType>& types, |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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) { |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 216 | threads.emplace_back([type, &vfs, &reqs]() { |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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 | |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 223 | generateTargetCC1Flags(vfs, type, arch_req_it->second.dependencies); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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 | |
Logan Chien | 6c148d1 | 2018-11-07 10:35:38 +0800 | [diff] [blame] | 241 | void compileHeader(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs, |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 242 | HeaderDatabase* header_database, CompilationType type, |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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>(); |
Logan Chien | 1da9f96 | 2019-12-05 14:00:44 -0800 | [diff] [blame] | 247 | if (!CompilerInvocation::CreateFromArgs(*invocation.get(), cc1_flags, *diags)) { |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 248 | errx(1, "failed to create CompilerInvocation"); |
| 249 | } |
| 250 | |
| 251 | clang::CompilerInstance Compiler; |
Yi Kong | 06be345 | 2017-04-20 14:27:28 -0700 | [diff] [blame] | 252 | |
Yi Kong | 06be345 | 2017-04-20 14:27:28 -0700 | [diff] [blame] | 253 | Compiler.setInvocation(std::move(invocation)); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 254 | Compiler.setDiagnostics(diags.get()); |
Logan Chien | c071fe4 | 2019-08-27 09:53:53 -0700 | [diff] [blame] | 255 | Compiler.createFileManager(vfs); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 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 | } |