mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-27 10:20:11 +01:00
This is (far) from robust -- particularly with repeated compiles in the same address space. But it appears to be sufficient in the short term, and we already have the relevant TODOs to factor this upstream into something that we can use here. This also somehow uncovered a bug in how we were logging failed commands -- the failed commands are destroyed when we destroy the driver object (and its diagnostics object), so we simply cannot do the logging _after_ flushing diagnostics. That's probably ok, and just doing this in the other order makes the code simpler. Assisted-by: Antigravity with Gemini
183 lines
7.2 KiB
C++
183 lines
7.2 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "third_party/llvm/clang_cc1.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "clang/Basic/DiagnosticDriver.h"
|
|
#include "clang/Basic/DiagnosticIDs.h"
|
|
#include "clang/CodeGen/ObjectFilePCHContainerWriter.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/CompilerInvocation.h"
|
|
#include "clang/Frontend/TextDiagnosticBuffer.h"
|
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
|
#include "clang/FrontendTool/Utils.h"
|
|
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
|
|
#include "clang/Serialization/PCHContainerOperations.h"
|
|
#include "common/check.h"
|
|
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/Support/Allocator.h"
|
|
#include "llvm/Support/BuryPointer.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/TimeProfiler.h"
|
|
#include "llvm/Support/Timer.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "toolchain/base/install_paths.h"
|
|
|
|
namespace Carbon {
|
|
|
|
auto RunClangCC1Main(const InstallPaths& installation,
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
|
llvm::SmallVectorImpl<const char*>& cc1_args,
|
|
bool enable_leaking) -> int {
|
|
llvm::BumpPtrAllocator allocator;
|
|
llvm::cl::ExpansionContext expansion_context(
|
|
allocator, llvm::cl::TokenizeGNUCommandLine);
|
|
if (llvm::Error error = expansion_context.expandResponseFiles(cc1_args)) {
|
|
llvm::errs() << toString(std::move(error)) << '\n';
|
|
return 1;
|
|
}
|
|
CARBON_CHECK(cc1_args[1] == llvm::StringRef("-cc1"));
|
|
|
|
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> diag_ids =
|
|
clang::DiagnosticIDs::create();
|
|
|
|
// Register the support for object-file-wrapped Clang modules.
|
|
auto pch_ops = std::make_shared<clang::PCHContainerOperations>();
|
|
pch_ops->registerWriter(
|
|
std::make_unique<clang::ObjectFilePCHContainerWriter>());
|
|
pch_ops->registerReader(
|
|
std::make_unique<clang::ObjectFilePCHContainerReader>());
|
|
|
|
// Buffer diagnostics from argument parsing so that we can output them using a
|
|
// well formed diagnostic object.
|
|
clang::DiagnosticOptions diag_opts;
|
|
clang::TextDiagnosticBuffer diag_buffer;
|
|
clang::DiagnosticsEngine diags(diag_ids, diag_opts, &diag_buffer,
|
|
/*ShouldOwnClient=*/false);
|
|
|
|
// Setup round-trip remarks for the DiagnosticsEngine used in CreateFromArgs.
|
|
if (llvm::find(cc1_args, llvm::StringRef("-Rround-trip-cc1-args")) !=
|
|
cc1_args.end()) {
|
|
diags.setSeverity(clang::diag::remark_cc1_round_trip_generated,
|
|
clang::diag::Severity::Remark, {});
|
|
}
|
|
|
|
auto invocation = std::make_shared<clang::CompilerInvocation>();
|
|
bool success = clang::CompilerInvocation::CreateFromArgs(
|
|
*invocation, llvm::ArrayRef(cc1_args).slice(1), diags, cc1_args[0]);
|
|
|
|
// Heap allocate the compiler instance so that if we disable freeing we can
|
|
// discard the pointer without destroying or deallocating it.
|
|
auto clang_instance = std::make_unique<clang::CompilerInstance>(
|
|
std::move(invocation), std::move(pch_ops));
|
|
|
|
// Override the disabling of free when we don't want to leak memory.
|
|
if (!enable_leaking) {
|
|
clang_instance->getFrontendOpts().DisableFree = false;
|
|
clang_instance->getCodeGenOpts().DisableFree = false;
|
|
}
|
|
|
|
if (!clang_instance->getFrontendOpts().TimeTracePath.empty()) {
|
|
llvm::timeTraceProfilerInitialize(
|
|
clang_instance->getFrontendOpts().TimeTraceGranularity, cc1_args[0],
|
|
clang_instance->getFrontendOpts().TimeTraceVerbose);
|
|
}
|
|
|
|
// TODO: These options should take priority over the actual compilation.
|
|
// However, their implementation is currently not accessible from a library.
|
|
// We should factor the implementation into a reusable location and then use
|
|
// that here.
|
|
CARBON_CHECK(!clang_instance->getFrontendOpts().PrintSupportedCPUs &&
|
|
!clang_instance->getFrontendOpts().PrintSupportedExtensions &&
|
|
!clang_instance->getFrontendOpts().PrintEnabledExtensions);
|
|
|
|
// Infer the builtin include path if unspecified.
|
|
if (clang_instance->getHeaderSearchOpts().UseBuiltinIncludes &&
|
|
clang_instance->getHeaderSearchOpts().ResourceDir.empty()) {
|
|
clang_instance->getHeaderSearchOpts().ResourceDir =
|
|
installation.clang_resource_path();
|
|
}
|
|
|
|
// Create the filesystem.
|
|
clang_instance->createVirtualFileSystem(std::move(fs), &diag_buffer);
|
|
|
|
// Create the actual diagnostics engine.
|
|
clang_instance->createDiagnostics();
|
|
if (!clang_instance->hasDiagnostics()) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Now flush the buffered diagnostics into the Clang instance's diagnostic
|
|
// engine. If we've already hit an error, we can exit early once that's done.
|
|
diag_buffer.FlushDiagnostics(clang_instance->getDiagnostics());
|
|
if (!success) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Execute the frontend actions.
|
|
{
|
|
llvm::TimeTraceScope time_scope("ExecuteCompiler");
|
|
bool time_passes = clang_instance->getCodeGenOpts().TimePasses;
|
|
if (time_passes) {
|
|
clang_instance->createFrontendTimer();
|
|
}
|
|
llvm::TimeRegion timer(time_passes ? &clang_instance->getFrontendTimer()
|
|
: nullptr);
|
|
success = clang::ExecuteCompilerInvocation(clang_instance.get());
|
|
}
|
|
|
|
// If any timers were active but haven't been destroyed yet, print their
|
|
// results now. This happens in -disable-free mode.
|
|
std::unique_ptr<llvm::raw_ostream> io_file = llvm::CreateInfoOutputFile();
|
|
if (clang_instance->getCodeGenOpts().TimePassesJson) {
|
|
*io_file << "{\n";
|
|
llvm::TimerGroup::printAllJSONValues(*io_file, "");
|
|
*io_file << "\n}\n";
|
|
} else if (!clang_instance->getCodeGenOpts().TimePassesStatsFile) {
|
|
llvm::TimerGroup::printAll(*io_file);
|
|
}
|
|
llvm::TimerGroup::clearAll();
|
|
|
|
if (llvm::timeTraceProfilerEnabled()) {
|
|
// It is possible that the compiler instance doesn't own a file manager here
|
|
// if we're compiling a module unit, since the file manager is owned by the
|
|
// AST when we're compiling a module unit. So the file manager may be
|
|
// invalid here.
|
|
//
|
|
// It should be fine to create file manager here since the file system
|
|
// options are stored in the compiler invocation and we can recreate the VFS
|
|
// from the compiler invocation.
|
|
if (!clang_instance->hasFileManager()) {
|
|
clang_instance->createFileManager();
|
|
}
|
|
|
|
if (auto profiler_output = clang_instance->createOutputFile(
|
|
clang_instance->getFrontendOpts().TimeTracePath, /*Binary=*/false,
|
|
/*RemoveFileOnSignal=*/false,
|
|
/*useTemporary=*/false)) {
|
|
llvm::timeTraceProfilerWrite(*profiler_output);
|
|
profiler_output.reset();
|
|
llvm::timeTraceProfilerCleanup();
|
|
clang_instance->clearOutputFiles(false);
|
|
}
|
|
}
|
|
|
|
// When running with -disable-free, don't do any destruction or shutdown.
|
|
if (clang_instance->getFrontendOpts().DisableFree) {
|
|
llvm::BuryPointer(std::move(clang_instance));
|
|
}
|
|
return success ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|
|
|
|
} // namespace Carbon
|