╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ zig_clang_driver.cpp ─────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
36 #include "llvm/Support/BuryPointer.h" 37 #include "llvm/Support/CommandLine.h" 38 #include "llvm/Support/CrashRecoveryContext.h" 39 #include "llvm/Support/ErrorHandling.h" 40 #include "llvm/Support/FileSystem.h" 41 #include "llvm/Support/IOSandbox.h" 42 #include "llvm/Support/LLVMDriver.h" 43 #include "llvm/Support/Path.h" 44 #include "llvm/Support/PrettyStackTrace.h" 45 #include "llvm/Support/Process.h" 46 #include "llvm/Support/Program.h" 47 #include "llvm/Support/Signals.h" 48 #include "llvm/Support/StringSaver.h" 49 #include "llvm/Support/TargetSelect.h" 50 #include "llvm/Support/Timer.h" 51 #include "llvm/Support/VirtualFileSystem.h" 52 #include "llvm/Support/raw_ostream.h" 53 #include "llvm/TargetParser/Host.h" 54 #include <memory> 55 #include <optional> 56 #include <set> 57 #include <system_error> 58 59 using namespace clang; 60 using namespace clang::driver; 61 using namespace llvm::opt; 62 63 std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) { 64 if (!CanonicalPrefixes) { 65 SmallString<128> ExecutablePath(Argv0); 66 // Do a PATH lookup if Argv0 isn't a valid path. 67 if (!llvm::sys::fs::exists(ExecutablePath)) 68 if (llvm::ErrorOr<std::string> P = 69 llvm::sys::findProgramByName(ExecutablePath)) 70 ExecutablePath = *P; 71 return std::string(ExecutablePath); 72 } 73 74 // This just needs to be some symbol in the binary; C++ doesn't 75 // allow taking the address of ::main however. 76 void *P = (void*) (intptr_t) GetExecutablePath; 77 return llvm::sys::fs::getMainExecutable(Argv0, P); 78 } 79 80 static const char *GetStableCStr(llvm::StringSet<> &SavedStrings, StringRef S) { 81 return SavedStrings.insert(S).first->getKeyData(); 82 } 83 84 extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, 85 void *MainAddr); 86 extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, 87 void *MainAddr); 88 89 static void insertTargetAndModeArgs(const ParsedClangName &NameParts, 90 SmallVectorImpl<const char *> &ArgVector, 91 llvm::StringSet<> &SavedStrings) { 92 // Put target and mode arguments at the start of argument list so that 93 // arguments specified in command line could override them. Avoid putting 94 // them at index 0, as an option like '-cc1' must remain the first. 95 int InsertionPoint = 0; 96 if (ArgVector.size() > 0) 97 ++InsertionPoint; 98 99 if (NameParts.DriverMode) { 100 // Add the mode flag to the arguments. 101 ArgVector.insert(ArgVector.begin() + InsertionPoint, 102 GetStableCStr(SavedStrings, NameParts.DriverMode)); 103 } 104 105 if (NameParts.TargetIsValid) { 106 const char *arr[] = {"-target", GetStableCStr(SavedStrings, 107 NameParts.TargetPrefix)}; 108 ArgVector.insert(ArgVector.begin() + InsertionPoint, 109 std::begin(arr), std::end(arr)); 110 } 111 } 112 113 static void getCLEnvVarOptions(std::string &EnvValue, llvm::StringSaver &Saver, 114 SmallVectorImpl<const char *> &Opts) { 115 llvm::cl::TokenizeWindowsCommandLine(EnvValue, Saver, Opts); 116 // The first instance of '#' should be replaced with '=' in each option. 117 for (const char *Opt : Opts) 118 if (char *NumberSignPtr = const_cast<char *>(::strchr(Opt, '#'))) 119 *NumberSignPtr = '='; 120 } 121 122 template <class T> 123 static T checkEnvVar(const char *EnvOptSet, const char *EnvOptFile, 124 std::string &OptFile) { 125 const char *Str = ::getenv(EnvOptSet); 126 if (!Str) 127 return T{}; 128 129 T OptVal = Str; 130 if (const char *Var = ::getenv(EnvOptFile)) 131 OptFile = Var; 132 return OptVal; 133 } 134 135 static bool SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) { 136 TheDriver.CCPrintOptions = 137 checkEnvVar<bool>("CC_PRINT_OPTIONS", "CC_PRINT_OPTIONS_FILE", 138 TheDriver.CCPrintOptionsFilename); 139 if (checkEnvVar<bool>("CC_PRINT_HEADERS", "CC_PRINT_HEADERS_FILE", 140 TheDriver.CCPrintHeadersFilename)) { 141 TheDriver.CCPrintHeadersFormat = HIFMT_Textual; 142 TheDriver.CCPrintHeadersFiltering = HIFIL_None; 143 } else { 144 std::string EnvVar = checkEnvVar<std::string>( 145 "CC_PRINT_HEADERS_FORMAT", "CC_PRINT_HEADERS_FILE", 146 TheDriver.CCPrintHeadersFilename); 147 if (!EnvVar.empty()) { 148 TheDriver.CCPrintHeadersFormat = 149 stringToHeaderIncludeFormatKind(EnvVar.c_str()); 150 if (!TheDriver.CCPrintHeadersFormat) { 151 TheDriver.Diag(clang::diag::err_drv_print_header_env_var) 152 << 0 << EnvVar; 153 return false; 154 } 155 156 const char *FilteringStr = ::getenv("CC_PRINT_HEADERS_FILTERING"); 157 if (!FilteringStr) { 158 TheDriver.Diag(clang::diag::err_drv_print_header_env_var_invalid_format) 159 << EnvVar; 160 return false; 161 } 162 HeaderIncludeFilteringKind Filtering; 163 if (!stringToHeaderIncludeFiltering(FilteringStr, Filtering)) { 164 TheDriver.Diag(clang::diag::err_drv_print_header_env_var) 165 << 1 << FilteringStr; 166 return false; 167 } 168 169 if ((TheDriver.CCPrintHeadersFormat == HIFMT_Textual && 170 Filtering != HIFIL_None) || 171 (TheDriver.CCPrintHeadersFormat == HIFMT_JSON && 172 Filtering == HIFIL_None)) { 173 TheDriver.Diag(clang::diag::err_drv_print_header_env_var_combination) 174 << EnvVar << FilteringStr; 175 return false; 176 } 177 TheDriver.CCPrintHeadersFiltering = Filtering; 178 } 179 } 180 181 TheDriver.CCLogDiagnostics = 182 checkEnvVar<bool>("CC_LOG_DIAGNOSTICS", "CC_LOG_DIAGNOSTICS_FILE", 183 TheDriver.CCLogDiagnosticsFilename); 184 TheDriver.CCPrintProcessStats = 185 checkEnvVar<bool>("CC_PRINT_PROC_STAT", "CC_PRINT_PROC_STAT_FILE", 186 TheDriver.CCPrintStatReportFilename); 187 TheDriver.CCPrintInternalStats = 188 checkEnvVar<bool>("CC_PRINT_INTERNAL_STAT", "CC_PRINT_INTERNAL_STAT_FILE", 189 TheDriver.CCPrintInternalStatReportFilename); 190 191 return true; 192 } 193 194 static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient, 195 const std::string &Path) { 196 // If the clang binary happens to be named cl.exe for compatibility reasons, 197 // use clang-cl.exe as the prefix to avoid confusion between clang and MSVC. 198 StringRef ExeBasename(llvm::sys::path::stem(Path)); 199 if (ExeBasename.equals_insensitive("cl")) 200 ExeBasename = "clang-cl"; 201 DiagClient->setPrefix(std::string(ExeBasename)); 202 } 203 204 static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV, 205 const llvm::ToolContext &ToolContext, 206 IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { 207 // If we call the cc1 tool from the clangDriver library (through 208 // Driver::CC1Main), we need to clean up the options usage count. The options 209 // are currently global, and they might have been used previously by the 210 // driver. 211 llvm::cl::ResetAllOptionOccurrences(); 212 213 llvm::BumpPtrAllocator A; 214 llvm::cl::ExpansionContext ECtx(A, llvm::cl::TokenizeGNUCommandLine, 215 VFS.get()); 216 if (llvm::Error Err = ECtx.expandResponseFiles(ArgV)) { 217 llvm::errs() << toString(std::move(Err)) << '\n'; 218 return 1; 219 } 220 StringRef Tool = ArgV[1]; 221 void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath; 222 if (Tool == "-cc1") 223 return cc1_main(ArrayRef(ArgV).slice(1), ArgV[0], GetExecutablePathVP); 224 if (Tool == "-cc1as") 225 return cc1as_main(ArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP); 226 // Reject unknown tools. 227 llvm::errs() 228 << "error: unknown integrated tool '" << Tool << "'. " 229 << "Valid tools include '-cc1' and '-cc1as'.\n"; 230 return 1; 231 } 232 233 static int clang_main(int Argc, char **Argv, const llvm::ToolContext &ToolContext) { 234 noteBottomOfStack(); 235 llvm::setBugReportMsg("PLEASE submit a bug report to " BUG_REPORT_URL 236 " and include the crash backtrace, preprocessed " 237 "source, and associated run script.\n"); 238 size_t argv_offset = (strcmp(Argv[1], "-cc1") == 0 || strcmp(Argv[1], "-cc1as") == 0) ? 0 : 1; 239 SmallVector<const char *, 256> Args(Argv + argv_offset, Argv + Argc); 240 241 if (llvm::sys::Process::FixupStandardFileDescriptors()) 242 return 1; 243 244 llvm::InitializeAllTargets(); 245 246 llvm::BumpPtrAllocator A; 247 llvm::StringSaver Saver(A); 248 249 const char *ProgName = 250 ToolContext.NeedsPrependArg ? ToolContext.PrependArg : ToolContext.Path; 251 252 bool ClangCLMode = 253 IsClangCL(getDriverMode(ProgName, llvm::ArrayRef(Args).slice(1))); 254 255 auto VFS = llvm::vfs::getRealFileSystem(); 256 257 if (llvm::Error Err = expandResponseFiles(Args, ClangCLMode, A, VFS.get())) { 258 llvm::errs() << toString(std::move(Err)) << '\n'; 259 return 1; 260 } 261 262 // Handle -cc1 integrated tools. 263 if (Args.size() >= 2 && StringRef(Args[1]).starts_with("-cc1")) { 264 // Note that this only enables the sandbox for direct -cc1 invocations and 265 // out-of-process -cc1 invocations launched by the driver. For in-process 266 // -cc1 invocations launched by the driver, the sandbox is enabled in 267 // CC1Command::Execute() for better crash recovery. 268 auto EnableSandbox = llvm::sys::sandbox::scopedEnable(); 269 return ExecuteCC1Tool(Args, ToolContext, VFS); 270 } 271 272 // Handle options that need handling before the real command line parsing in 273 // Driver::BuildCompilation() 274 bool CanonicalPrefixes = true; 275 for (int i = 1, size = Args.size(); i < size; ++i) { 276 // Skip end-of-line response file markers 277 if (Args[i] == nullptr) 278 continue; 279 if (StringRef(Args[i]) == "-canonical-prefixes") 280 CanonicalPrefixes = true; 281 else if (StringRef(Args[i]) == "-no-canonical-prefixes") 282 CanonicalPrefixes = false; 283 } 284 285 // Handle CL and _CL_ which permits additional command line options to be 286 // prepended or appended. 287 if (ClangCLMode) { 288 // Arguments in "CL" are prepended. 289 std::optional<std::string> OptCL = llvm::sys::Process::GetEnv("CL"); 290 if (OptCL) { 291 SmallVector<const char *, 8> PrependedOpts; 292 getCLEnvVarOptions(*OptCL, Saver, PrependedOpts); 293 294 // Insert right after the program name to prepend to the argument list. 295 Args.insert(Args.begin() + 1, PrependedOpts.begin(), PrependedOpts.end()); 296 } 297 // Arguments in "_CL_" are appended. 298 std::optional<std::string> Opt_CL_ = llvm::sys::Process::GetEnv("_CL_"); 299 if (Opt_CL_) { 300 SmallVector<const char *, 8> AppendedOpts; 301 getCLEnvVarOptions(*Opt_CL_, Saver, AppendedOpts); 302 303 // Insert at the end of the argument list to append. 304 Args.append(AppendedOpts.begin(), AppendedOpts.end()); 305 } 306 } 307 308 llvm::StringSet<> SavedStrings; 309 // Handle CCC_OVERRIDE_OPTIONS, used for editing a command line behind the 310 // scenes. 311 if (const char *OverrideStr = ::getenv("CCC_OVERRIDE_OPTIONS")) { 312 // FIXME: Driver shouldn't take extra initial argument. 313 driver::applyOverrideOptions(Args, OverrideStr, SavedStrings, 314 "CCC_OVERRIDE_OPTIONS", &llvm::errs()); 315 } 316 317 std::string Path = GetExecutablePath(ToolContext.Path, CanonicalPrefixes); 318 319 // Whether the cc1 tool should be called inside the current process, or if we 320 // should spawn a new clang subprocess (old behavior). 321 // Not having an additional process saves some execution time of Windows, 322 // and makes debugging and profiling easier. 323 bool UseNewCC1Process = CLANG_SPAWN_CC1; 324 for (const char *Arg : Args) 325 UseNewCC1Process = llvm::StringSwitch<bool>(Arg) 326 .Case("-fno-integrated-cc1", true) 327 .Case("-fintegrated-cc1", false) 328 .Default(UseNewCC1Process); 329 330 std::unique_ptr<DiagnosticOptions> DiagOpts = CreateAndPopulateDiagOpts(Args); 331 // Driver's diagnostics don't use suppression mappings, so don't bother 332 // parsing them. CC1 still receives full args, so this doesn't impact other 333 // actions. 334 DiagOpts->DiagnosticSuppressionMappingsFile.clear(); 335 336 TextDiagnosticPrinter *DiagClient = 337 new TextDiagnosticPrinter(llvm::errs(), *DiagOpts); 338 FixupDiagPrefixExeName(DiagClient, ProgName); 339 340 DiagnosticsEngine Diags(DiagnosticIDs::create(), *DiagOpts, DiagClient); 341 342 if (!DiagOpts->DiagnosticSerializationFile.empty()) { 343 auto SerializedConsumer = 344 clang::serialized_diags::create(DiagOpts->DiagnosticSerializationFile, 345 *DiagOpts, /*MergeChildRecords=*/true); 346 Diags.setClient(new ChainedDiagnosticConsumer( 347 Diags.takeClient(), std::move(SerializedConsumer))); 348 } 349 350 ProcessWarningOptions(Diags, *DiagOpts, *VFS, /*ReportDiags=*/false); 351 352 Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags, 353 /*Title=*/"clang LLVM compiler", VFS); 354 auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(ProgName); 355 TheDriver.setTargetAndMode(TargetAndMode); 356 // If -canonical-prefixes is set, GetExecutablePath will have resolved Path 357 // to the llvm driver binary, not clang. In this case, we need to use 358 // PrependArg which should be clang-*. Checking just CanonicalPrefixes is 359 // safe even in the normal case because PrependArg will be null so 360 // setPrependArg will be a no-op. 361 if (ToolContext.NeedsPrependArg || CanonicalPrefixes) 362 TheDriver.setPrependArg(ToolContext.PrependArg); 363 364 insertTargetAndModeArgs(TargetAndMode, Args, SavedStrings); 365 366 if (!SetBackdoorDriverOutputsFromEnvVars(TheDriver)) 367 return 1; 368 369 auto ExecuteCC1WithContext = [&ToolContext, 370 &VFS](SmallVectorImpl<const char *> &ArgV) { 371 return ExecuteCC1Tool(ArgV, ToolContext, VFS); 372 }; 373 if (!UseNewCC1Process) { 374 TheDriver.CC1Main = ExecuteCC1WithContext; 375 // Ensure the CC1Command actually catches cc1 crashes 376 llvm::CrashRecoveryContext::Enable(); 377 } 378 379 std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Args)); 380 381 Driver::ReproLevel ReproLevel = Driver::ReproLevel::OnCrash; 382 if (Arg *A = C->getArgs().getLastArg(options::OPT_gen_reproducer_eq)) { 383 auto Level = 384 llvm::StringSwitch<std::optional<Driver::ReproLevel>>(A->getValue()) 385 .Case("off", Driver::ReproLevel::Off) 386 .Case("crash", Driver::ReproLevel::OnCrash) 387 .Case("error", Driver::ReproLevel::OnError) 388 .Case("always", Driver::ReproLevel::Always) 389 .Default(std::nullopt); 390 if (!Level) { 391 llvm::errs() << "Unknown value for " << A->getSpelling() << ": '" 392 << A->getValue() << "'\n"; 393 return 1; 394 } 395 ReproLevel = *Level; 396 } 397 if (!!::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH")) 398 ReproLevel = Driver::ReproLevel::Always; 399 400 int Res = 1; 401 bool IsCrash = false; 402 Driver::CommandStatus CommandStatus = Driver::CommandStatus::Ok; 403 // Pretend the first command failed if ReproStatus is Always. 404 const Command *FailingCommand = nullptr; 405 if (!C->getJobs().empty()) 406 FailingCommand = &*C->getJobs().begin(); 407 if (C && !C->containsError()) { 408 SmallVector<std::pair<int, const Command *>, 4> FailingCommands; 409 Res = TheDriver.ExecuteCompilation(*C, FailingCommands); 410 411 for (const auto &P : FailingCommands) { 412 int CommandRes = P.first; 413 FailingCommand = P.second; 414 if (!Res) 415 Res = CommandRes; 416 417 // If result status is < 0, then the driver command signalled an error. 418 // If result status is 70, then the driver command reported a fatal error. 419 // On Windows, abort will return an exit code of 3. In these cases, 420 // generate additional diagnostic information if possible. 421 IsCrash = CommandRes < 0 || CommandRes == 70; 422 #ifdef _WIN32 423 IsCrash |= CommandRes == 3; 424 #endif 425 #if LLVM_ON_UNIX 426 // When running in integrated-cc1 mode, the CrashRecoveryContext returns 427 // the same codes as if the program crashed. See section "Exit Status for 428 // Commands": 429 // https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html 430 IsCrash |= CommandRes > 128; 431 #endif 432 CommandStatus = 433 IsCrash ? Driver::CommandStatus::Crash : Driver::CommandStatus::Error; 434 if (IsCrash) 435 break; 436 } 437 } 438 439 // Print the bug report message that would be printed if we did actually 440 // crash, but only if we're crashing due to FORCE_CLANG_DIAGNOSTICS_CRASH. 441 if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH")) 442 llvm::dbgs() << llvm::getBugReportMsg(); 443 if (FailingCommand != nullptr && 444 TheDriver.maybeGenerateCompilationDiagnostics(CommandStatus, ReproLevel, 445 *C, *FailingCommand)) 446 Res = 1; 447 448 Diags.getClient()->finish(); 449 450 if (!UseNewCC1Process && IsCrash) { 451 // When crashing in -fintegrated-cc1 mode, bury the timer pointers, because 452 // the internal linked list might point to already released stack frames. 453 llvm::BuryPointer(llvm::TimerGroup::acquireTimerGlobals()); 454 } else { 455 // If any timers were active but haven't been destroyed yet, print their 456 // results now. This happens in -disable-free mode. 457 llvm::TimerGroup::printAll(llvm::errs()); 458 llvm::TimerGroup::clearAll(); 459 } 460 461 #ifdef _WIN32 462 // Exit status should not be negative on Win32, unless abnormal termination. 463 // Once abnormal termination was caught, negative status should not be 464 // propagated. 465 if (Res < 0) 466 Res = 1; 467 #endif 468 469 // If we have multiple failing commands, we return the result of the first 470 // failing command. 471 return Res; 472 } 473 474 extern "C" int ZigClang_main(int, char **); 475 int ZigClang_main(int argc, char **argv) { 476 return clang_main(argc, argv, {argv[0], nullptr, false}); 477 } 478