Files
revng-revng/lib/EarlyFunctionAnalysis/AttachDebugInfo.cpp
Djordje Todorovic fc88a52fae AttachDebugInfo: Fix for MLIR
Up until now, we did not need different !dbg/DISubprogram
attached to each isolated llvm::Function, but for the
purpose of MLIR lowering, we need it.

On the LLVM IR level, we used to reuse root's DISubpgram,
and we have not being attaching !dbg/DISubprogram to the
isolated functions at all (just to llvm::Instructions),
but that was an hack in order to avoid some LLVM verify
checks, since LLVM IR expects !dbg attachments to calls of
functions that could be inlined, but by avoid !dbg on the
Function itself, it won't verify !dbg attachments on the
instructions, since it will assume it has no debug info in
it. When it comes to MLIR, it expects different !dbg
attached to each function, in order to process debug info
in it, so that is the reason we cannot rely on the root
DISubprogram only anymore.
2023-09-21 08:05:49 +02:00

160 lines
6.3 KiB
C++

/// \file AttachDebugInfo.cpp
/// A simple pass to attach debug metadata.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Metadata.h"
#include "revng/EarlyFunctionAnalysis/AttachDebugInfo.h"
#include "revng/Pipeline/Location.h"
#include "revng/Pipes/Ranks.h"
#include "revng/Support/FunctionTags.h"
using namespace llvm;
char AttachDebugInfo::ID = 0;
using Register = RegisterPass<AttachDebugInfo>;
static Register X("attach-debug-info", "Attach debug info metadata.");
static Logger<> Log("attach-debug-info");
static bool isTrue(const llvm::Value *V) {
return getLimitedValue(V) != 0;
}
static void handleFunction(DIBuilder &DIB,
llvm::Function &F,
DISubprogram *TheSubprogram,
efa::FunctionMetadata &FM,
GeneratedCodeBasicInfo &GCBI) {
namespace ranks = revng::ranks;
LLVMContext &Context = F.getParent()->getContext();
DILocation *CurrentDebugLocation = nullptr;
BasicBlockID LastJumpTarget;
for (auto *BB : ReversePostOrderTraversal(&F)) {
if (not GCBI.isTranslated(BB))
continue;
for (auto &I : *BB) {
if (auto *Call = getCallTo(&I, "newpc")) {
BasicBlockID Address = blockIDFromNewPC(Call);
if (isTrue(Call->getArgOperand(NewPCArguments::IsJumpTarget))) {
const auto &CFG = FM.ControlFlowGraph();
if (CFG.contains(Address)) {
LastJumpTarget = Address;
} else {
revng_assert(CFG.at(LastJumpTarget).contains(Address));
}
}
revng_assert(LastJumpTarget.isValid());
revng_assert(Address.inliningIndex() == LastJumpTarget.inliningIndex());
auto SPFlags = DISubprogram::toSPFlags(false, /* isLocalToUnit */
true, /* isDefinition*/
false /* isOptimized */);
auto Type = DIB.createSubroutineType(DIB.getOrCreateTypeArray({}));
// Let's make the debug location that points back to the binary.
std::string NewDebugLocation = serializedLocation(ranks::Instruction,
FM.Entry(),
LastJumpTarget,
Address.start());
auto Subprogram = DIB.createFunction(TheSubprogram->getFile(), // Scope
NewDebugLocation, // Name
StringRef(), // LinkageName
TheSubprogram->getFile(), // File
1, // LineNo
Type, // Ty (subroutine type)
1, // ScopeLine
DINode::FlagPrototyped, // Flags
SPFlags);
DIB.finalizeSubprogram(Subprogram);
auto InlineLocationForMetaAddress = DILocation::get(Context,
0,
0,
TheSubprogram,
nullptr);
// Represent debug info for all the isolated functions as if they were
// inlined in the root.
auto InlineLoc = DILocation::get(Context,
0,
0,
Subprogram,
InlineLocationForMetaAddress);
CurrentDebugLocation = InlineLoc;
}
I.setDebugLoc(CurrentDebugLocation);
}
}
}
bool AttachDebugInfo::runOnModule(llvm::Module &M) {
DIBuilder DIB(M);
FunctionMetadataCache *Cache = &getAnalysis<FunctionMetadataCachePass>()
.get();
auto &GCBI = getAnalysis<GeneratedCodeBasicInfoWrapperPass>().getGCBI();
// This will be used for attaching the !dbg to instructions.
// TODO: Document how are we going to abuse DILocation fields.
DIFile *File = DIB.createFile(M.getSourceFileName(), "./");
// Also add dummy CU.
DICompileUnit *CU = DIB.createCompileUnit(dwarf::DW_LANG_C,
File,
"revng", // Producer
true, // isOptimized
"", // Flags
0 // RV
);
for (auto &F : M) {
// Skip non-isolated functions (e.g. helpers from QEMU).
if (not FunctionTags::Isolated.isTagOf(&F))
continue;
// Skip functions with debug-info.
if (F.getSubprogram())
continue;
// Skip declarations
if (F.isDeclaration())
continue;
auto FM = Cache->getFunctionMetadata(&F);
revng_log(Log,
"Metadata for Function " << F.getName() << ":"
<< FM.Entry().toString());
// Create debug info for the function.
auto SPFlags = DISubprogram::toSPFlags(false, // isLocalToUnit
true, // isDefinition
false // isOptimized
);
auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray({}));
DISubprogram
*TheSubprogram = DIB.createFunction(CU->getFile(), // Scope
F.getName(), // Name
StringRef(), // LinkageName
CU->getFile(), // File
1, // LineNo
SPType, // Ty (subroutine type)
1, // ScopeLine
DINode::FlagPrototyped, // Flags
SPFlags);
DIB.finalizeSubprogram(TheSubprogram);
handleFunction(DIB, F, TheSubprogram, FM, GCBI);
}
return true;
}