blob: 0941fd65950d0277cc62c866956168be7fda29ae [file] [edit]
//===--- DXILDebugInfo.cpp - analysis&lowering for Debug info -*- C++ -*- ---=//
//
// 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 "DXILDebugInfo.h"
#include "../DirectX.h"
#include "DXILAttributes.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/IR/AttributeMask.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
#include "llvm/Support/Casting.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#define DEBUG_TYPE "dx-debug-info"
using namespace llvm;
using namespace llvm::dxil;
static bool lowerDXILDebugInfo(Module &M) {
// Convert debug markers back to dbg.value intrinsics. Record whether any
// changes were made.
bool Modified = M.convertFromNewDbgValues();
DebugInfoFinder DIF;
DIF.processModule(M);
{
Function *DVDecl = nullptr;
// Logically these should be variables in the
// for (BasicBlock &BB : F) loop.
// They are defined here and cleared at the start of the loop body to avoid
// the cost of deconstruction and reconstruction.
DenseMap<DILocalVariable *, std::pair<Instruction *, DbgValueInst *>>
DbgValues;
DenseMap<std::pair<DILocalVariable *, DIExpression *>,
std::pair<Instruction *, DbgValueInst *>>
DbgValueFragments;
// Likewise, logically, this should be a variable in the
// for (Function &F : M) loop.
DenseSet<DILocalVariable *> DbgVariablesSeen;
const AttributeMask &AttrMask = getNonDXILAttributeMask();
for (Function &F : M) {
F.removeFnAttrs(AttrMask);
F.removeRetAttrs(AttrMask);
for (unsigned ArgNo = 0; ArgNo != F.arg_size(); ++ArgNo)
F.removeParamAttrs(ArgNo, AttrMask);
bool IsEntryBlock = true;
DbgVariablesSeen.clear();
for (BasicBlock &BB : F) {
Instruction *NextNonDebugInst = nullptr;
DbgValues.clear();
DbgValueFragments.clear();
for (Instruction &I : make_early_inc_range(reverse(BB))) {
I.eraseMetadataIf([](unsigned KindID, MDNode *) {
return KindID == LLVMContext::MD_DIAssignID;
});
if (!isa<DbgInfoIntrinsic>(I)) {
NextNonDebugInst = &I;
continue;
}
if (auto *DL = dyn_cast<DbgLabelInst>(&I)) {
DL->eraseFromParent();
Modified = true;
continue;
}
// Process both llvm.dbg.value and llvm.dbg.assign here. We convert
// llvm.dbg.assign to llvm.dbg.value by dropping the last arguments,
// and remove redundant llvm.dbg.values.
if (auto *DV = dyn_cast<DbgValueInst>(&I)) {
// Keep track of the last location where we saw any debug value for
// a variable.
DILocalVariable *V = DV->getVariable();
DIExpression *E = DV->getExpression();
// If this is already an llvm.dbg.value instruction that we can
// keep, just do that, otherwise convert it.
auto *Val = cast<MetadataAsValue>(DV->getArgOperand(0));
auto *Var = cast<MetadataAsValue>(DV->getArgOperand(1));
auto *Expr = cast<MetadataAsValue>(DV->getArgOperand(2));
bool Replace = DV->getIntrinsicID() != Intrinsic::dbg_value;
if (!isa<ValueAsMetadata>(Val->getMetadata())) {
// This may be a DIArgList which is not supported in LLVM 3.7. If
// it is, we cannot record the new value, but we still need to
// kill any old value. Do this by poison. We do not know the
// correct type to use here and arbitrarily use i1.
// This should never be anything other than ValueAsMetadata or
// DIArgList, but in manually constructed LLVM IR, it can be.
// Handle this gracefully by also replacing it with poison.
Val = MetadataAsValue::get(
M.getContext(), ConstantAsMetadata::get(PoisonValue::get(
Type::getInt1Ty(M.getContext()))));
E = DIExpression::get(M.getContext(), {});
Expr = MetadataAsValue::get(M.getContext(), E);
Replace = true;
}
std::pair<Instruction *, DbgValueInst *> &DbgValue = DbgValues[V];
std::pair<Instruction *, DbgValueInst *> &DbgValueFragment =
DbgValueFragments[{V, E}];
if (DbgValue.second) {
// If there is a later value of the same fragment at the same
// location, this value is redundant.
if (DbgValueFragment.first == NextNonDebugInst) {
DV->eraseFromParent();
Modified = true;
continue;
}
// If there is a later identical value of the same fragment at a
// later point, and there have been no intervening values of
// different possibly overlapping fragments, that later value is
// redundant.
if (DbgValueFragment.second &&
DbgValueFragment.second == DbgValue.second &&
DbgValueFragment.second->getValue() == DV->getValue()) {
DbgValue.second->eraseFromParent();
Modified = true;
}
}
DbgValueInst *NewDV;
if (Replace) {
if (!DVDecl) {
DVDecl =
Intrinsic::getOrInsertDeclaration(&M, Intrinsic::dbg_value);
AttributeMask AM;
for (Attribute A : DVDecl->getAttributes().getFnAttrs())
if (A.isStringAttribute() ||
(A.getKindAsEnum() != Attribute::NoUnwind &&
A.getKindAsEnum() != Attribute::Memory))
AM.addAttribute(A);
DVDecl->removeFnAttrs(AM);
}
NewDV = cast<DbgValueInst>(
CallInst::Create(DVDecl, {Val, Var, Expr}, {}, "",
std::next(DV->getIterator())));
NewDV->setTailCall();
NewDV->setDebugLoc(DV->getDebugLoc());
DV->eraseFromParent();
Modified = true;
} else {
NewDV = DV;
}
DbgValue = DbgValueFragment = {NextNonDebugInst, NewDV};
continue;
}
}
// If this is the entry block, if the first value we see for each debug
// value is undef, it is redundant.
if (IsEntryBlock) {
for (Instruction &I : make_early_inc_range(BB)) {
auto *DV = dyn_cast<DbgValueInst>(&I);
if (!DV || DbgVariablesSeen.contains(DV->getVariable()))
continue;
if (isa<UndefValue>(DV->getValue())) {
DV->eraseFromParent();
Modified = true;
continue;
}
DbgVariablesSeen.insert(DV->getVariable());
}
}
IsEntryBlock = false;
}
}
}
for (DISubprogram *SP : DIF.subprograms()) {
if (MDTuple *RN = cast_or_null<MDTuple>(SP->getRawRetainedNodes())) {
SmallVector<Metadata *> MDs(RN->operands());
MDs.erase(std::remove_if(MDs.begin(), MDs.end(),
[](Metadata *M) { return isa<DILabel>(M); }),
MDs.end());
SP->replaceRetainedNodes(MDTuple::get(M.getContext(), MDs));
Modified = true;
}
}
return Modified;
}
PreservedAnalyses DXILDebugInfo::run(Module &M, ModuleAnalysisManager &) {
return lowerDXILDebugInfo(M) ? PreservedAnalyses::none()
: PreservedAnalyses::all();
}
namespace {
class DXILDebugInfoLegacy : public ModulePass {
public:
static char ID;
DXILDebugInfoLegacy() : ModulePass(ID) {}
bool runOnModule(Module &M) override { return lowerDXILDebugInfo(M); }
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
};
} // namespace
char DXILDebugInfoLegacy::ID = 0;
INITIALIZE_PASS(DXILDebugInfoLegacy, DEBUG_TYPE, "DXIL Debug Info", false,
false)
ModulePass *llvm::createDXILDebugInfoLegacyPass() {
return new DXILDebugInfoLegacy();
}