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> |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 32 | #include <clang/Basic/VirtualFileSystem.h> |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 33 | #include <clang/Driver/Compilation.h> |
| 34 | #include <clang/Driver/Driver.h> |
| 35 | #include <clang/Frontend/CompilerInstance.h> |
| 36 | #include <clang/Frontend/CompilerInvocation.h> |
| 37 | #include <clang/Frontend/FrontendAction.h> |
| 38 | #include <clang/Frontend/FrontendActions.h> |
| 39 | #include <clang/Frontend/TextDiagnosticPrinter.h> |
| 40 | #include <clang/Frontend/Utils.h> |
| 41 | #include <clang/FrontendTool/Utils.h> |
| 42 | #include <llvm/ADT/IntrusiveRefCntPtr.h> |
| 43 | #include <llvm/ADT/SmallVector.h> |
| 44 | #include <llvm/ADT/StringRef.h> |
Yi Kong | 06be345 | 2017-04-20 14:27:28 -0700 | [diff] [blame] | 45 | #include <llvm/Config/config.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__"; |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 97 | static void generateTargetCC1Flags(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs, |
| 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" }; |
| 101 | cmd.push_back("-std=c11"); |
| 102 | cmd.push_back("-x"); |
| 103 | cmd.push_back("c-header"); |
| 104 | cmd.push_back("-fsyntax-only"); |
| 105 | |
| 106 | cmd.push_back("-Wall"); |
| 107 | cmd.push_back("-Wextra"); |
| 108 | cmd.push_back("-Werror"); |
| 109 | cmd.push_back("-Wundef"); |
| 110 | cmd.push_back("-Wno-unused-macros"); |
| 111 | cmd.push_back("-Wno-unused-function"); |
| 112 | cmd.push_back("-Wno-unused-variable"); |
| 113 | cmd.push_back("-Wno-unknown-attributes"); |
| 114 | cmd.push_back("-Wno-pragma-once-outside-header"); |
| 115 | |
| 116 | cmd.push_back("-target"); |
| 117 | cmd.push_back(arch_targets[type.arch]); |
| 118 | |
| 119 | cmd.push_back("-DANDROID"); |
| 120 | cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level)); |
| 121 | cmd.push_back("-D_FORTIFY_SOURCE=2"); |
| 122 | cmd.push_back("-D_GNU_SOURCE"); |
| 123 | cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits)); |
| 124 | |
| 125 | cmd.push_back("-nostdinc"); |
| 126 | |
| 127 | if (add_include) { |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 128 | cmd.push_back("-include"); |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 129 | cmd.push_back("android/versioning.h"); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | for (const auto& dir : include_dirs) { |
| 133 | cmd.push_back("-isystem"); |
| 134 | cmd.push_back(dir); |
| 135 | } |
| 136 | |
| 137 | cmd.push_back(filename_placeholder); |
| 138 | |
| 139 | auto diags = constructDiags(); |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 140 | driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags, vfs); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 141 | driver.setCheckInputsExist(false); |
| 142 | |
| 143 | llvm::SmallVector<const char*, 32> driver_args; |
| 144 | for (const std::string& str : cmd) { |
| 145 | driver_args.push_back(str.c_str()); |
| 146 | } |
| 147 | |
| 148 | std::unique_ptr<driver::Compilation> Compilation(driver.BuildCompilation(driver_args)); |
| 149 | const driver::JobList& jobs = Compilation->getJobs(); |
| 150 | if (jobs.size() != 1) { |
| 151 | errx(1, "driver returned %zu jobs for %s", jobs.size(), to_string(type).c_str()); |
| 152 | } |
| 153 | |
| 154 | const driver::Command& driver_cmd = llvm::cast<driver::Command>(*jobs.begin()); |
| 155 | const driver::ArgStringList& cc_args = driver_cmd.getArguments(); |
| 156 | |
| 157 | if (cc_args.size() == 0) { |
| 158 | errx(1, "driver returned empty command for %s", to_string(type).c_str()); |
| 159 | } |
| 160 | |
| 161 | std::vector<std::string> result(cc_args.begin(), cc_args.end()); |
| 162 | |
| 163 | { |
| 164 | static std::mutex cc1_init_mutex; |
| 165 | std::unique_lock<std::mutex> lock(cc1_init_mutex); |
| 166 | if (cc1_flags.count(type) > 0) { |
| 167 | errx(1, "attemped to generate cc1 flags for existing CompilationType %s", |
| 168 | to_string(type).c_str()); |
| 169 | } |
| 170 | |
| 171 | cc1_flags.emplace(std::make_pair(type, std::move(result))); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | static std::vector<const char*> getCC1Command(CompilationType type, const std::string& filename) { |
| 176 | const auto& target_flag_it = cc1_flags.find(type); |
| 177 | if (target_flag_it == cc1_flags.end()) { |
| 178 | errx(1, "failed to find target flags for CompilationType %s", to_string(type).c_str()); |
| 179 | } |
| 180 | |
| 181 | std::vector<const char*> result; |
| 182 | for (const std::string& flag : target_flag_it->second) { |
| 183 | if (flag == "-disable-free") { |
| 184 | continue; |
| 185 | } else if (flag == filename_placeholder) { |
| 186 | result.push_back(filename.c_str()); |
| 187 | } else { |
| 188 | result.push_back(flag.c_str()); |
| 189 | } |
| 190 | } |
| 191 | return result; |
| 192 | } |
| 193 | |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 194 | void initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs, |
| 195 | const std::set<CompilationType>& types, |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 196 | const std::unordered_map<Arch, CompilationRequirements>& reqs) { |
| 197 | if (!cc1_flags.empty()) { |
| 198 | errx(1, "reinitializing target CC1 flag cache?"); |
| 199 | } |
| 200 | |
| 201 | auto start = std::chrono::high_resolution_clock::now(); |
| 202 | std::vector<std::thread> threads; |
| 203 | for (const CompilationType type : types) { |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 204 | threads.emplace_back([type, &vfs, &reqs]() { |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 205 | const auto& arch_req_it = reqs.find(type.arch); |
| 206 | if (arch_req_it == reqs.end()) { |
| 207 | errx(1, "CompilationRequirement map missing entry for CompilationType %s", |
| 208 | to_string(type).c_str()); |
| 209 | } |
| 210 | |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 211 | generateTargetCC1Flags(vfs, type, arch_req_it->second.dependencies); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 212 | }); |
| 213 | } |
| 214 | for (auto& thread : threads) { |
| 215 | thread.join(); |
| 216 | } |
| 217 | auto end = std::chrono::high_resolution_clock::now(); |
| 218 | |
| 219 | if (verbose) { |
| 220 | auto diff = (end - start) / 1.0ms; |
| 221 | printf("Generated compiler flags for %zu targets in %0.2Lfms\n", types.size(), diff); |
| 222 | } |
| 223 | |
| 224 | if (cc1_flags.empty()) { |
| 225 | errx(1, "failed to initialize target CC1 flag cache"); |
| 226 | } |
| 227 | } |
| 228 | |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 229 | void compileHeader(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs, |
| 230 | HeaderDatabase* header_database, CompilationType type, |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 231 | const std::string& filename) { |
| 232 | auto diags = constructDiags(); |
| 233 | std::vector<const char*> cc1_flags = getCC1Command(type, filename); |
| 234 | auto invocation = std::make_unique<CompilerInvocation>(); |
| 235 | if (!CompilerInvocation::CreateFromArgs(*invocation.get(), &cc1_flags.front(), |
| 236 | &cc1_flags.front() + cc1_flags.size(), *diags)) { |
| 237 | errx(1, "failed to create CompilerInvocation"); |
| 238 | } |
| 239 | |
| 240 | clang::CompilerInstance Compiler; |
Yi Kong | 06be345 | 2017-04-20 14:27:28 -0700 | [diff] [blame] | 241 | |
| 242 | // Remove the workaround once b/35936936 is fixed. |
| 243 | #if LLVM_VERSION_MAJOR >= 5 |
| 244 | Compiler.setInvocation(std::move(invocation)); |
| 245 | #else |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 246 | Compiler.setInvocation(invocation.release()); |
Yi Kong | 06be345 | 2017-04-20 14:27:28 -0700 | [diff] [blame] | 247 | #endif |
| 248 | |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 249 | Compiler.setDiagnostics(diags.get()); |
Josh Gao | 78b8a14 | 2016-11-09 01:00:41 -0800 | [diff] [blame] | 250 | Compiler.setVirtualFileSystem(vfs); |
Josh Gao | b5c4963 | 2016-11-08 22:21:31 -0800 | [diff] [blame] | 251 | |
| 252 | VersionerASTAction versioner_action(header_database, type); |
| 253 | if (!Compiler.ExecuteAction(versioner_action)) { |
| 254 | errx(1, "compilation generated warnings or errors"); |
| 255 | } |
| 256 | |
| 257 | if (diags->getNumWarnings() || diags->hasErrorOccurred()) { |
| 258 | errx(1, "compilation generated warnings or errors"); |
| 259 | } |
| 260 | } |