
This relands D84013 but with a test that relies on less shell features to hopefully make the test pass on Fuchsia (where the test from the previous patch version strangely failed with a plain "Exit code 1"). Original summary: D81347 changes the ASTFileSignature to be an array of 20 uint8_t instead of 5 uint32_t. However, it didn't update the code in ObjectFilePCHContainerOperations that creates the dwoID in the module from the ASTFileSignature (`Buffer->Signature` being the array subclass that is now `std::array<uint8_t, 20>` instead of `std::array<uint32_t, 5>`). ``` uint64_t Signature = [..] (uint64_t)Buffer->Signature[1] << 32 | Buffer->Signature[0] ``` This code works with the old ASTFileSignature (where two uint32_t are enough to fill the uint64_t), but after the patch this only took two bytes from the ASTFileSignature and only partly filled the Signature uint64_t. This caused that the dwoID in the module ref and the dwoID in the actual module no longer match (which in turns causes that LLDB keeps warning about the dwoID's not matching when debugging -gmodules-compiled binaries). This patch just unifies the logic for turning the ASTFileSignature into an uint64_t which makes the dwoID match again (and should prevent issues like that in the future). Reviewed By: aprantl, dang Differential Revision: https://reviews.llvm.org/D84013
365 lines
13 KiB
C++
365 lines
13 KiB
C++
//===--- ObjectFilePCHContainerOperations.cpp -----------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
|
|
#include "CGDebugInfo.h"
|
|
#include "CodeGenModule.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/Basic/CodeGenOptions.h"
|
|
#include "clang/Basic/Diagnostic.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
#include "clang/CodeGen/BackendUtil.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Lex/HeaderSearch.h"
|
|
#include "clang/Lex/Preprocessor.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Bitstream/BitstreamReader.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Object/COFF.h"
|
|
#include "llvm/Object/ObjectFile.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
using namespace clang;
|
|
|
|
#define DEBUG_TYPE "pchcontainer"
|
|
|
|
namespace {
|
|
class PCHContainerGenerator : public ASTConsumer {
|
|
DiagnosticsEngine &Diags;
|
|
const std::string MainFileName;
|
|
const std::string OutputFileName;
|
|
ASTContext *Ctx;
|
|
ModuleMap &MMap;
|
|
const HeaderSearchOptions &HeaderSearchOpts;
|
|
const PreprocessorOptions &PreprocessorOpts;
|
|
CodeGenOptions CodeGenOpts;
|
|
const TargetOptions TargetOpts;
|
|
const LangOptions LangOpts;
|
|
std::unique_ptr<llvm::LLVMContext> VMContext;
|
|
std::unique_ptr<llvm::Module> M;
|
|
std::unique_ptr<CodeGen::CodeGenModule> Builder;
|
|
std::unique_ptr<raw_pwrite_stream> OS;
|
|
std::shared_ptr<PCHBuffer> Buffer;
|
|
|
|
/// Visit every type and emit debug info for it.
|
|
struct DebugTypeVisitor : public RecursiveASTVisitor<DebugTypeVisitor> {
|
|
clang::CodeGen::CGDebugInfo &DI;
|
|
ASTContext &Ctx;
|
|
DebugTypeVisitor(clang::CodeGen::CGDebugInfo &DI, ASTContext &Ctx)
|
|
: DI(DI), Ctx(Ctx) {}
|
|
|
|
/// Determine whether this type can be represented in DWARF.
|
|
static bool CanRepresent(const Type *Ty) {
|
|
return !Ty->isDependentType() && !Ty->isUndeducedType();
|
|
}
|
|
|
|
bool VisitImportDecl(ImportDecl *D) {
|
|
if (!D->getImportedOwningModule())
|
|
DI.EmitImportDecl(*D);
|
|
return true;
|
|
}
|
|
|
|
bool VisitTypeDecl(TypeDecl *D) {
|
|
// TagDecls may be deferred until after all decls have been merged and we
|
|
// know the complete type. Pure forward declarations will be skipped, but
|
|
// they don't need to be emitted into the module anyway.
|
|
if (auto *TD = dyn_cast<TagDecl>(D))
|
|
if (!TD->isCompleteDefinition())
|
|
return true;
|
|
|
|
QualType QualTy = Ctx.getTypeDeclType(D);
|
|
if (!QualTy.isNull() && CanRepresent(QualTy.getTypePtr()))
|
|
DI.getOrCreateStandaloneType(QualTy, D->getLocation());
|
|
return true;
|
|
}
|
|
|
|
bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) {
|
|
QualType QualTy(D->getTypeForDecl(), 0);
|
|
if (!QualTy.isNull() && CanRepresent(QualTy.getTypePtr()))
|
|
DI.getOrCreateStandaloneType(QualTy, D->getLocation());
|
|
return true;
|
|
}
|
|
|
|
bool VisitFunctionDecl(FunctionDecl *D) {
|
|
if (isa<CXXMethodDecl>(D))
|
|
// This is not yet supported. Constructing the `this' argument
|
|
// mandates a CodeGenFunction.
|
|
return true;
|
|
|
|
SmallVector<QualType, 16> ArgTypes;
|
|
for (auto i : D->parameters())
|
|
ArgTypes.push_back(i->getType());
|
|
QualType RetTy = D->getReturnType();
|
|
QualType FnTy = Ctx.getFunctionType(RetTy, ArgTypes,
|
|
FunctionProtoType::ExtProtoInfo());
|
|
if (CanRepresent(FnTy.getTypePtr()))
|
|
DI.EmitFunctionDecl(D, D->getLocation(), FnTy);
|
|
return true;
|
|
}
|
|
|
|
bool VisitObjCMethodDecl(ObjCMethodDecl *D) {
|
|
if (!D->getClassInterface())
|
|
return true;
|
|
|
|
bool selfIsPseudoStrong, selfIsConsumed;
|
|
SmallVector<QualType, 16> ArgTypes;
|
|
ArgTypes.push_back(D->getSelfType(Ctx, D->getClassInterface(),
|
|
selfIsPseudoStrong, selfIsConsumed));
|
|
ArgTypes.push_back(Ctx.getObjCSelType());
|
|
for (auto i : D->parameters())
|
|
ArgTypes.push_back(i->getType());
|
|
QualType RetTy = D->getReturnType();
|
|
QualType FnTy = Ctx.getFunctionType(RetTy, ArgTypes,
|
|
FunctionProtoType::ExtProtoInfo());
|
|
if (CanRepresent(FnTy.getTypePtr()))
|
|
DI.EmitFunctionDecl(D, D->getLocation(), FnTy);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
public:
|
|
PCHContainerGenerator(CompilerInstance &CI, const std::string &MainFileName,
|
|
const std::string &OutputFileName,
|
|
std::unique_ptr<raw_pwrite_stream> OS,
|
|
std::shared_ptr<PCHBuffer> Buffer)
|
|
: Diags(CI.getDiagnostics()), MainFileName(MainFileName),
|
|
OutputFileName(OutputFileName), Ctx(nullptr),
|
|
MMap(CI.getPreprocessor().getHeaderSearchInfo().getModuleMap()),
|
|
HeaderSearchOpts(CI.getHeaderSearchOpts()),
|
|
PreprocessorOpts(CI.getPreprocessorOpts()),
|
|
TargetOpts(CI.getTargetOpts()), LangOpts(CI.getLangOpts()),
|
|
OS(std::move(OS)), Buffer(std::move(Buffer)) {
|
|
// The debug info output isn't affected by CodeModel and
|
|
// ThreadModel, but the backend expects them to be nonempty.
|
|
CodeGenOpts.CodeModel = "default";
|
|
CodeGenOpts.ThreadModel = "single";
|
|
CodeGenOpts.DebugTypeExtRefs = true;
|
|
// When building a module MainFileName is the name of the modulemap file.
|
|
CodeGenOpts.MainFileName =
|
|
LangOpts.CurrentModule.empty() ? MainFileName : LangOpts.CurrentModule;
|
|
CodeGenOpts.setDebugInfo(codegenoptions::FullDebugInfo);
|
|
CodeGenOpts.setDebuggerTuning(CI.getCodeGenOpts().getDebuggerTuning());
|
|
CodeGenOpts.DebugPrefixMap =
|
|
CI.getInvocation().getCodeGenOpts().DebugPrefixMap;
|
|
}
|
|
|
|
~PCHContainerGenerator() override = default;
|
|
|
|
void Initialize(ASTContext &Context) override {
|
|
assert(!Ctx && "initialized multiple times");
|
|
|
|
Ctx = &Context;
|
|
VMContext.reset(new llvm::LLVMContext());
|
|
M.reset(new llvm::Module(MainFileName, *VMContext));
|
|
M->setDataLayout(Ctx->getTargetInfo().getDataLayout());
|
|
Builder.reset(new CodeGen::CodeGenModule(
|
|
*Ctx, HeaderSearchOpts, PreprocessorOpts, CodeGenOpts, *M, Diags));
|
|
|
|
// Prepare CGDebugInfo to emit debug info for a clang module.
|
|
auto *DI = Builder->getModuleDebugInfo();
|
|
StringRef ModuleName = llvm::sys::path::filename(MainFileName);
|
|
DI->setPCHDescriptor(
|
|
{ModuleName, "", OutputFileName, ASTFileSignature::createDISentinel()});
|
|
DI->setModuleMap(MMap);
|
|
}
|
|
|
|
bool HandleTopLevelDecl(DeclGroupRef D) override {
|
|
if (Diags.hasErrorOccurred())
|
|
return true;
|
|
|
|
// Collect debug info for all decls in this group.
|
|
for (auto *I : D)
|
|
if (!I->isFromASTFile()) {
|
|
DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx);
|
|
DTV.TraverseDecl(I);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override {
|
|
HandleTopLevelDecl(D);
|
|
}
|
|
|
|
void HandleTagDeclDefinition(TagDecl *D) override {
|
|
if (Diags.hasErrorOccurred())
|
|
return;
|
|
|
|
if (D->isFromASTFile())
|
|
return;
|
|
|
|
// Anonymous tag decls are deferred until we are building their declcontext.
|
|
if (D->getName().empty())
|
|
return;
|
|
|
|
// Defer tag decls until their declcontext is complete.
|
|
auto *DeclCtx = D->getDeclContext();
|
|
while (DeclCtx) {
|
|
if (auto *D = dyn_cast<TagDecl>(DeclCtx))
|
|
if (!D->isCompleteDefinition())
|
|
return;
|
|
DeclCtx = DeclCtx->getParent();
|
|
}
|
|
|
|
DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx);
|
|
DTV.TraverseDecl(D);
|
|
Builder->UpdateCompletedType(D);
|
|
}
|
|
|
|
void HandleTagDeclRequiredDefinition(const TagDecl *D) override {
|
|
if (Diags.hasErrorOccurred())
|
|
return;
|
|
|
|
if (const RecordDecl *RD = dyn_cast<RecordDecl>(D))
|
|
Builder->getModuleDebugInfo()->completeRequiredType(RD);
|
|
}
|
|
|
|
void HandleImplicitImportDecl(ImportDecl *D) override {
|
|
if (!D->getImportedOwningModule())
|
|
Builder->getModuleDebugInfo()->EmitImportDecl(*D);
|
|
}
|
|
|
|
/// Emit a container holding the serialized AST.
|
|
void HandleTranslationUnit(ASTContext &Ctx) override {
|
|
assert(M && VMContext && Builder);
|
|
// Delete these on function exit.
|
|
std::unique_ptr<llvm::LLVMContext> VMContext = std::move(this->VMContext);
|
|
std::unique_ptr<llvm::Module> M = std::move(this->M);
|
|
std::unique_ptr<CodeGen::CodeGenModule> Builder = std::move(this->Builder);
|
|
|
|
if (Diags.hasErrorOccurred())
|
|
return;
|
|
|
|
M->setTargetTriple(Ctx.getTargetInfo().getTriple().getTriple());
|
|
M->setDataLayout(Ctx.getTargetInfo().getDataLayout());
|
|
|
|
// PCH files don't have a signature field in the control block,
|
|
// but LLVM detects DWO CUs by looking for a non-zero DWO id.
|
|
// We use the lower 64 bits for debug info.
|
|
|
|
uint64_t Signature =
|
|
Buffer->Signature ? Buffer->Signature.truncatedValue() : ~1ULL;
|
|
|
|
Builder->getModuleDebugInfo()->setDwoId(Signature);
|
|
|
|
// Finalize the Builder.
|
|
if (Builder)
|
|
Builder->Release();
|
|
|
|
// Ensure the target exists.
|
|
std::string Error;
|
|
auto Triple = Ctx.getTargetInfo().getTriple();
|
|
if (!llvm::TargetRegistry::lookupTarget(Triple.getTriple(), Error))
|
|
llvm::report_fatal_error(Error);
|
|
|
|
// Emit the serialized Clang AST into its own section.
|
|
assert(Buffer->IsComplete && "serialization did not complete");
|
|
auto &SerializedAST = Buffer->Data;
|
|
auto Size = SerializedAST.size();
|
|
auto Int8Ty = llvm::Type::getInt8Ty(*VMContext);
|
|
auto *Ty = llvm::ArrayType::get(Int8Ty, Size);
|
|
auto *Data = llvm::ConstantDataArray::getString(
|
|
*VMContext, StringRef(SerializedAST.data(), Size),
|
|
/*AddNull=*/false);
|
|
auto *ASTSym = new llvm::GlobalVariable(
|
|
*M, Ty, /*constant*/ true, llvm::GlobalVariable::InternalLinkage, Data,
|
|
"__clang_ast");
|
|
// The on-disk hashtable needs to be aligned.
|
|
ASTSym->setAlignment(llvm::Align(8));
|
|
|
|
// Mach-O also needs a segment name.
|
|
if (Triple.isOSBinFormatMachO())
|
|
ASTSym->setSection("__CLANG,__clangast");
|
|
// COFF has an eight character length limit.
|
|
else if (Triple.isOSBinFormatCOFF())
|
|
ASTSym->setSection("clangast");
|
|
else
|
|
ASTSym->setSection("__clangast");
|
|
|
|
LLVM_DEBUG({
|
|
// Print the IR for the PCH container to the debug output.
|
|
llvm::SmallString<0> Buffer;
|
|
clang::EmitBackendOutput(
|
|
Diags, HeaderSearchOpts, CodeGenOpts, TargetOpts, LangOpts,
|
|
Ctx.getTargetInfo().getDataLayout(), M.get(),
|
|
BackendAction::Backend_EmitLL,
|
|
std::make_unique<llvm::raw_svector_ostream>(Buffer));
|
|
llvm::dbgs() << Buffer;
|
|
});
|
|
|
|
// Use the LLVM backend to emit the pch container.
|
|
clang::EmitBackendOutput(Diags, HeaderSearchOpts, CodeGenOpts, TargetOpts,
|
|
LangOpts, Ctx.getTargetInfo().getDataLayout(),
|
|
M.get(), BackendAction::Backend_EmitObj,
|
|
std::move(OS));
|
|
|
|
// Free the memory for the temporary buffer.
|
|
llvm::SmallVector<char, 0> Empty;
|
|
SerializedAST = std::move(Empty);
|
|
}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
std::unique_ptr<ASTConsumer>
|
|
ObjectFilePCHContainerWriter::CreatePCHContainerGenerator(
|
|
CompilerInstance &CI, const std::string &MainFileName,
|
|
const std::string &OutputFileName,
|
|
std::unique_ptr<llvm::raw_pwrite_stream> OS,
|
|
std::shared_ptr<PCHBuffer> Buffer) const {
|
|
return std::make_unique<PCHContainerGenerator>(
|
|
CI, MainFileName, OutputFileName, std::move(OS), Buffer);
|
|
}
|
|
|
|
StringRef
|
|
ObjectFilePCHContainerReader::ExtractPCH(llvm::MemoryBufferRef Buffer) const {
|
|
StringRef PCH;
|
|
auto OFOrErr = llvm::object::ObjectFile::createObjectFile(Buffer);
|
|
if (OFOrErr) {
|
|
auto &OF = OFOrErr.get();
|
|
bool IsCOFF = isa<llvm::object::COFFObjectFile>(*OF);
|
|
// Find the clang AST section in the container.
|
|
for (auto &Section : OF->sections()) {
|
|
StringRef Name;
|
|
if (Expected<StringRef> NameOrErr = Section.getName())
|
|
Name = *NameOrErr;
|
|
else
|
|
consumeError(NameOrErr.takeError());
|
|
|
|
if ((!IsCOFF && Name == "__clangast") || (IsCOFF && Name == "clangast")) {
|
|
if (Expected<StringRef> E = Section.getContents())
|
|
return *E;
|
|
else {
|
|
handleAllErrors(E.takeError(), [&](const llvm::ErrorInfoBase &EIB) {
|
|
EIB.log(llvm::errs());
|
|
});
|
|
return "";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
handleAllErrors(OFOrErr.takeError(), [&](const llvm::ErrorInfoBase &EIB) {
|
|
if (EIB.convertToErrorCode() ==
|
|
llvm::object::object_error::invalid_file_type)
|
|
// As a fallback, treat the buffer as a raw AST.
|
|
PCH = Buffer.getBuffer();
|
|
else
|
|
EIB.log(llvm::errs());
|
|
});
|
|
return PCH;
|
|
}
|