Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 54 additions & 5 deletions clang/lib/Basic/FileManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,40 @@ FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
isVolatile);
}

// [HACK] Walk a path component-by-component, resolving each level with a
// case-insensitive directory scan. Returns the real-cased path on success.
static std::optional<std::string>
findPathCaseInsensitive(StringRef CaseInsensitivePath) {
using llvm::sys::fs::directory_iterator;

SmallVector<StringRef> Components;
CaseInsensitivePath.split(Components, "/", -1, /*KeepEmpty=*/false);
if (Components.empty())
return std::nullopt;

// Start from "/" for absolute paths, "" (cwd) for relative ones.
SmallString<256> Resolved;
if (CaseInsensitivePath.starts_with("/"))
Resolved = "/";

for (StringRef Component : Components) {
std::error_code EC;
bool Found = false;
for (directory_iterator It(Resolved, EC), End; It != End && !EC;
It.increment(EC)) {
StringRef EntryName = llvm::sys::path::filename(It->path());
if (EntryName.equals_insensitive(Component)) {
llvm::sys::path::append(Resolved, EntryName);
Found = true;
break;
}
}
if (EC || !Found)
return std::nullopt;
}
return Resolved.str().str();
}

/// getStatValue - Get the 'stat' information for the specified path,
/// using the cache to accelerate it if possible. This returns true
/// if the path points to a virtual file or does not exist, or returns
Expand All @@ -587,15 +621,30 @@ FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status,
bool isFile, std::unique_ptr<llvm::vfs::File> *F) {
// FIXME: FileSystemOpts shouldn't be passed in here, all paths should be
// absolute!
if (FileSystemOpts.WorkingDir.empty())
return FileSystemStatCache::get(Path, Status, isFile, F,
StatCache.get(), *FS);
if (FileSystemOpts.WorkingDir.empty()) {
auto EC = FileSystemStatCache::get(Path, Status, isFile, F,
StatCache.get(), *FS);
if (!EC)
return EC;
// [HACK] Case-insensitive fallback for Windows SDK headers on Linux.
if (auto Resolved = findPathCaseInsensitive(Path))
return FileSystemStatCache::get(*Resolved, Status, isFile, F,
StatCache.get(), *FS);
return EC;
}

SmallString<128> FilePath(Path);
FixupRelativePath(FilePath);

return FileSystemStatCache::get(FilePath.c_str(), Status, isFile, F,
StatCache.get(), *FS);
auto EC = FileSystemStatCache::get(FilePath.c_str(), Status, isFile, F,
StatCache.get(), *FS);
if (!EC)
return EC;
// [HACK] Case-insensitive fallback.
if (auto Resolved = findPathCaseInsensitive(FilePath))
return FileSystemStatCache::get(*Resolved, Status, isFile, F,
StatCache.get(), *FS);
return EC;
}

std::error_code
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4243,6 +4243,10 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,

if (IsDeclForCallSite)
Fn->setSubprogram(SP);
else
// [HACK] Retain declaration-only subprograms so they survive into the
// PDB / DWARF output even when the function is never defined or called.
DBuilder.retainType(SP);

DBuilder.finalizeSubprogram(SP);
}
Expand Down
73 changes: 68 additions & 5 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/X86TargetParser.h"
#include "llvm/Support/xxhash.h"
#include <optional>
Expand Down Expand Up @@ -6276,6 +6279,46 @@ void CodeGenModule::EmitDeclContext(const DeclContext *DC) {
}
}

// [HACK] Lazily load the PDB_TRAVERSE_FILE whitelist.
struct TraverseWhitelist {
bool Loaded = false;
bool HasFilter = false;
llvm::StringSet<> Files;

void ensureLoaded() {
if (Loaded)
return;
Loaded = true;
if (auto V = llvm::sys::Process::GetEnv("PDB_TRAVERSE_FILE"))
if (auto B = llvm::MemoryBuffer::getFile(*V)) {
llvm::SmallVector<llvm::StringRef, 64> Lines;
(*B)->getBuffer().split(Lines, '\n', -1, false);
for (auto L : Lines)
if (!(L = L.trim()).empty())
Files.insert(L.lower());
HasFilter = true;
}
}

bool hasFilter() { ensureLoaded(); return HasFilter; }

bool contains(const Decl *D, ASTContext &Ctx) {
ensureLoaded();
if (!HasFilter)
return true;
auto Loc = Ctx.getSourceManager().getExpansionLoc(D->getLocation());
auto F = Ctx.getSourceManager().getFilename(Loc);
return !F.empty() && Files.count(F.lower());
}
};
static TraverseWhitelist TheTraverseWhitelist;

// [HACK] Check whether a Decl comes from a file listed in the
// PDB_TRAVERSE_FILE whitelist. If no whitelist is set, allow everything.
static bool isDeclInTraverseWhitelist(const Decl *D, ASTContext &Ctx) {
return TheTraverseWhitelist.contains(D, Ctx);
}

/// EmitTopLevelDecl - Emit code for a single top level declaration.
void CodeGenModule::EmitTopLevelDecl(Decl *D) {
// Ignore dependent declarations.
Expand All @@ -6295,6 +6338,23 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
// Always provide some coverage mapping
// even for the functions that aren't emitted.
AddDeferredUnusedCoverageMapping(D);
// [HACK] Emit debug info for extern function declarations from
// whitelisted headers so their prototypes appear in the PDB.
// Only active when PDB_TRAVERSE_FILE is set: EmitFunctionDecl with
// Fn=nullptr can crash for certain C++ declarations whose debug
// context is not yet materialized.
if (TheTraverseWhitelist.hasFilter()) {
if (CGDebugInfo *DI = getModuleDebugInfo()) {
auto *FD = cast<FunctionDecl>(D);
if (!isa<CXXMethodDecl>(FD) &&
!FD->doesThisDeclarationHaveABody() &&
!FD->isDependentContext() && !FD->isImplicit() &&
isDeclInTraverseWhitelist(D, getContext()))
if (auto *FPT = FD->getType()->getAs<FunctionProtoType>())
DI->EmitFunctionDecl(GlobalDecl(FD), FD->getLocation(),
QualType(FPT, 0));
}
}
break;

case Decl::CXXDeductionGuide:
Expand Down Expand Up @@ -6331,7 +6391,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
case Decl::CXXRecord: {
CXXRecordDecl *CRD = cast<CXXRecordDecl>(D);
if (CGDebugInfo *DI = getModuleDebugInfo()) {
if (CRD->hasDefinition())
if (CRD->hasDefinition() && isDeclInTraverseWhitelist(D, getContext()))
DI->EmitAndRetainType(getContext().getRecordType(cast<RecordDecl>(D)));
if (auto *ES = D->getASTContext().getExternalSource())
if (ES->hasExternalDefinitions(D) == ExternalASTSource::EK_Never)
Expand Down Expand Up @@ -6553,19 +6613,22 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
case Decl::Typedef:
case Decl::TypeAlias: // using foo = bar; [C++11]
if (CGDebugInfo *DI = getModuleDebugInfo())
DI->EmitAndRetainType(
getContext().getTypedefType(cast<TypedefNameDecl>(D)));
if (isDeclInTraverseWhitelist(D, getContext()))
DI->EmitAndRetainType(
getContext().getTypedefType(cast<TypedefNameDecl>(D)));
break;

case Decl::Record:
if (CGDebugInfo *DI = getModuleDebugInfo())
if (cast<RecordDecl>(D)->getDefinition())
if (cast<RecordDecl>(D)->getDefinition() &&
isDeclInTraverseWhitelist(D, getContext()))
DI->EmitAndRetainType(getContext().getRecordType(cast<RecordDecl>(D)));
break;

case Decl::Enum:
if (CGDebugInfo *DI = getModuleDebugInfo())
if (cast<EnumDecl>(D)->getDefinition())
if (cast<EnumDecl>(D)->getDefinition() &&
isDeclInTraverseWhitelist(D, getContext()))
DI->EmitAndRetainType(getContext().getEnumType(cast<EnumDecl>(D)));
break;

Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3298,6 +3298,11 @@ void CodeViewDebug::emitDebugInfoForRetainedTypes() {
if (DIType *RT = dyn_cast<DIType>(Ty)) {
getTypeIndex(RT);
// FIXME: Add to global/local DTU list.
} else if (auto *SP = dyn_cast<DISubprogram>(Ty)) {
// [HACK] Emit LF_FUNC_ID for declaration-only subprograms so that
// function prototypes from headers appear in the PDB.
if (!SP->isDefinition())
getFuncIdForSubprogram(SP);
}
}
}
Expand Down