Skip to content

[RemoveDIs] Handle DPValues in FastISel #76952

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 5, 2024
Merged
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
14 changes: 14 additions & 0 deletions llvm/include/llvm/CodeGen/FastISel.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,10 @@ class FastISel {
/// Reset InsertPt to the given old insert position.
void leaveLocalValueArea(SavePoint Old);

/// Target-independent lowering of non-instruction debug info associated with
/// this instruction.
void handleDbgInfo(const Instruction *II);

Comment on lines +324 to +325
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docu-comment desired

protected:
explicit FastISel(FunctionLoweringInfo &FuncInfo,
const TargetLibraryInfo *LibInfo,
Expand Down Expand Up @@ -518,6 +522,16 @@ class FastISel {
return MF->getFunction().hasOptSize();
}

/// Target-independent lowering of debug information. Returns false if the
/// debug information couldn't be lowered and was instead discarded.
virtual bool lowerDbgValue(const Value *V, DIExpression *Expr,
DILocalVariable *Var, const DebugLoc &DL);

/// Target-independent lowering of debug information. Returns false if the
/// debug information couldn't be lowered and was instead discarded.
virtual bool lowerDbgDeclare(const Value *V, DIExpression *Expr,
DILocalVariable *Var, const DebugLoc &DL);

private:
/// Handle PHI nodes in successor blocks.
///
Expand Down
323 changes: 188 additions & 135 deletions llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,184 @@ bool FastISel::selectCall(const User *I) {
return lowerCall(Call);
}

void FastISel::handleDbgInfo(const Instruction *II) {
if (!II->hasDbgValues())
return;

// Clear any metadata.
MIMD = MIMetadata();

// Reverse order of debug records, because fast-isel walks through backwards.
for (DPValue &DPV : llvm::reverse(II->getDbgValueRange())) {
flushLocalValueMap();
recomputeInsertPt();

Value *V = nullptr;
if (!DPV.hasArgList())
V = DPV.getVariableLocationOp(0);

bool Res = false;
if (DPV.getType() == DPValue::LocationType::Value) {
Res = lowerDbgValue(V, DPV.getExpression(), DPV.getVariable(),
DPV.getDebugLoc());
} else {
assert(DPV.getType() == DPValue::LocationType::Declare);
if (FuncInfo.PreprocessedDPVDeclares.contains(&DPV))
continue;
Res = lowerDbgDeclare(V, DPV.getExpression(), DPV.getVariable(),
DPV.getDebugLoc());
}

if (!Res)
LLVM_DEBUG(dbgs() << "Dropping debug-info for " << DPV << "\n";);
}
}

bool FastISel::lowerDbgValue(const Value *V, DIExpression *Expr,
DILocalVariable *Var, const DebugLoc &DL) {
// This form of DBG_VALUE is target-independent.
const MCInstrDesc &II = TII.get(TargetOpcode::DBG_VALUE);
if (!V || isa<UndefValue>(V)) {
// DI is either undef or cannot produce a valid DBG_VALUE, so produce an
// undef DBG_VALUE to terminate any prior location.
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL, II, false, 0U, Var, Expr);
return true;
}
if (const auto *CI = dyn_cast<ConstantInt>(V)) {
// See if there's an expression to constant-fold.
if (Expr)
std::tie(Expr, CI) = Expr->constantFold(CI);
if (CI->getBitWidth() > 64)
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL, II)
.addCImm(CI)
.addImm(0U)
.addMetadata(Var)
.addMetadata(Expr);
else
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL, II)
.addImm(CI->getZExtValue())
.addImm(0U)
.addMetadata(Var)
.addMetadata(Expr);
return true;
}
if (const auto *CF = dyn_cast<ConstantFP>(V)) {
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL, II)
.addFPImm(CF)
.addImm(0U)
.addMetadata(Var)
.addMetadata(Expr);
return true;
}
if (const auto *Arg = dyn_cast<Argument>(V);
Arg && Expr && Expr->isEntryValue()) {
// As per the Verifier, this case is only valid for swift async Args.
assert(Arg->hasAttribute(Attribute::AttrKind::SwiftAsync));

Register Reg = getRegForValue(Arg);
for (auto [PhysReg, VirtReg] : FuncInfo.RegInfo->liveins())
if (Reg == VirtReg || Reg == PhysReg) {
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL, II, false /*IsIndirect*/,
PhysReg, Var, Expr);
return true;
}

LLVM_DEBUG(dbgs() << "Dropping dbg.value: expression is entry_value but "
"couldn't find a physical register\n");
return false;
}
if (auto SI = FuncInfo.StaticAllocaMap.find(dyn_cast<AllocaInst>(V));
SI != FuncInfo.StaticAllocaMap.end()) {
MachineOperand FrameIndexOp = MachineOperand::CreateFI(SI->second);
bool IsIndirect = false;
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL, II, IsIndirect, FrameIndexOp,
Var, Expr);
return true;
}
if (Register Reg = lookUpRegForValue(V)) {
// FIXME: This does not handle register-indirect values at offset 0.
if (!FuncInfo.MF->useDebugInstrRef()) {
bool IsIndirect = false;
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL, II, IsIndirect, Reg, Var,
Expr);
return true;
}
// If using instruction referencing, produce this as a DBG_INSTR_REF,
// to be later patched up by finalizeDebugInstrRefs.
SmallVector<MachineOperand, 1> MOs({MachineOperand::CreateReg(
/* Reg */ Reg, /* isDef */ false, /* isImp */ false,
/* isKill */ false, /* isDead */ false,
/* isUndef */ false, /* isEarlyClobber */ false,
/* SubReg */ 0, /* isDebug */ true)});
SmallVector<uint64_t, 2> Ops({dwarf::DW_OP_LLVM_arg, 0});
auto *NewExpr = DIExpression::prependOpcodes(Expr, Ops);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL,
TII.get(TargetOpcode::DBG_INSTR_REF), /*IsIndirect*/ false, MOs,
Var, NewExpr);
return true;
}
return false;
}

bool FastISel::lowerDbgDeclare(const Value *Address, DIExpression *Expr,
DILocalVariable *Var, const DebugLoc &DL) {
if (!Address || isa<UndefValue>(Address)) {
LLVM_DEBUG(dbgs() << "Dropping debug info (bad/undef address)\n");
return false;
}

std::optional<MachineOperand> Op;
if (Register Reg = lookUpRegForValue(Address))
Op = MachineOperand::CreateReg(Reg, false);

// If we have a VLA that has a "use" in a metadata node that's then used
// here but it has no other uses, then we have a problem. E.g.,
//
// int foo (const int *x) {
// char a[*x];
// return 0;
// }
//
// If we assign 'a' a vreg and fast isel later on has to use the selection
// DAG isel, it will want to copy the value to the vreg. However, there are
// no uses, which goes counter to what selection DAG isel expects.
if (!Op && !Address->use_empty() && isa<Instruction>(Address) &&
(!isa<AllocaInst>(Address) ||
!FuncInfo.StaticAllocaMap.count(cast<AllocaInst>(Address))))
Op = MachineOperand::CreateReg(FuncInfo.InitializeRegForValue(Address),
false);

if (Op) {
assert(Var->isValidLocationForIntrinsic(DL) &&
"Expected inlined-at fields to agree");
if (FuncInfo.MF->useDebugInstrRef() && Op->isReg()) {
// If using instruction referencing, produce this as a DBG_INSTR_REF,
// to be later patched up by finalizeDebugInstrRefs. Tack a deref onto
// the expression, we don't have an "indirect" flag in DBG_INSTR_REF.
SmallVector<uint64_t, 3> Ops(
{dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_deref});
auto *NewExpr = DIExpression::prependOpcodes(Expr, Ops);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL,
TII.get(TargetOpcode::DBG_INSTR_REF), /*IsIndirect*/ false, *Op,
Var, NewExpr);
return true;
}

// A dbg.declare describes the address of a source variable, so lower it
// into an indirect DBG_VALUE.
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL,
TII.get(TargetOpcode::DBG_VALUE), /*IsIndirect*/ true, *Op, Var,
Expr);
return true;
}

// We can't yet handle anything else here because it would require
// generating code, thus altering codegen because of debug info.
LLVM_DEBUG(
dbgs() << "Dropping debug info (no materialized reg for address)\n");
return false;
}

bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
switch (II->getIntrinsicID()) {
default:
Expand Down Expand Up @@ -1211,153 +1389,28 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
return true;

const Value *Address = DI->getAddress();
if (!Address || isa<UndefValue>(Address)) {
LLVM_DEBUG(dbgs() << "Dropping debug info for " << *DI
<< " (bad/undef address)\n");
return true;
}
if (!lowerDbgDeclare(Address, DI->getExpression(), DI->getVariable(),
MIMD.getDL()))
LLVM_DEBUG(dbgs() << "Dropping debug info for " << *DI);

std::optional<MachineOperand> Op;
if (Register Reg = lookUpRegForValue(Address))
Op = MachineOperand::CreateReg(Reg, false);

// If we have a VLA that has a "use" in a metadata node that's then used
// here but it has no other uses, then we have a problem. E.g.,
//
// int foo (const int *x) {
// char a[*x];
// return 0;
// }
//
// If we assign 'a' a vreg and fast isel later on has to use the selection
// DAG isel, it will want to copy the value to the vreg. However, there are
// no uses, which goes counter to what selection DAG isel expects.
if (!Op && !Address->use_empty() && isa<Instruction>(Address) &&
(!isa<AllocaInst>(Address) ||
!FuncInfo.StaticAllocaMap.count(cast<AllocaInst>(Address))))
Op = MachineOperand::CreateReg(FuncInfo.InitializeRegForValue(Address),
false);

if (Op) {
assert(DI->getVariable()->isValidLocationForIntrinsic(MIMD.getDL()) &&
"Expected inlined-at fields to agree");
if (FuncInfo.MF->useDebugInstrRef() && Op->isReg()) {
// If using instruction referencing, produce this as a DBG_INSTR_REF,
// to be later patched up by finalizeDebugInstrRefs. Tack a deref onto
// the expression, we don't have an "indirect" flag in DBG_INSTR_REF.
SmallVector<uint64_t, 3> Ops(
{dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_deref});
auto *NewExpr = DIExpression::prependOpcodes(DI->getExpression(), Ops);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD.getDL(),
TII.get(TargetOpcode::DBG_INSTR_REF), /*IsIndirect*/ false, *Op,
DI->getVariable(), NewExpr);
} else {
// A dbg.declare describes the address of a source variable, so lower it
// into an indirect DBG_VALUE.
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD.getDL(),
TII.get(TargetOpcode::DBG_VALUE), /*IsIndirect*/ true, *Op,
DI->getVariable(), DI->getExpression());
}
} else {
// We can't yet handle anything else here because it would require
// generating code, thus altering codegen because of debug info.
LLVM_DEBUG(dbgs() << "Dropping debug info for " << *DI
<< " (no materialized reg for address)\n");
}
return true;
}
case Intrinsic::dbg_value: {
// This form of DBG_VALUE is target-independent.
const DbgValueInst *DI = cast<DbgValueInst>(II);
const MCInstrDesc &II = TII.get(TargetOpcode::DBG_VALUE);
const Value *V = DI->getValue();
DIExpression *Expr = DI->getExpression();
DILocalVariable *Var = DI->getVariable();
if (DI->hasArgList())
// Signal that we don't have a location for this.
V = nullptr;

assert(Var->isValidLocationForIntrinsic(MIMD.getDL()) &&
"Expected inlined-at fields to agree");
if (!V || isa<UndefValue>(V) || DI->hasArgList()) {
// DI is either undef or cannot produce a valid DBG_VALUE, so produce an
// undef DBG_VALUE to terminate any prior location.
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD.getDL(), II, false, 0U,
Var, Expr);
return true;
}
if (const auto *CI = dyn_cast<ConstantInt>(V)) {
// See if there's an expression to constant-fold.
if (Expr)
std::tie(Expr, CI) = Expr->constantFold(CI);
if (CI->getBitWidth() > 64)
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II)
.addCImm(CI)
.addImm(0U)
.addMetadata(Var)
.addMetadata(Expr);
else
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II)
.addImm(CI->getZExtValue())
.addImm(0U)
.addMetadata(Var)
.addMetadata(Expr);
return true;
}
if (const auto *CF = dyn_cast<ConstantFP>(V)) {
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, II)
.addFPImm(CF)
.addImm(0U)
.addMetadata(Var)
.addMetadata(Expr);
return true;
}
if (const auto *Arg = dyn_cast<Argument>(V);
Arg && Expr && Expr->isEntryValue()) {
// As per the Verifier, this case is only valid for swift async Args.
assert(Arg->hasAttribute(Attribute::AttrKind::SwiftAsync));

Register Reg = getRegForValue(Arg);
for (auto [PhysReg, VirtReg] : FuncInfo.RegInfo->liveins())
if (Reg == VirtReg || Reg == PhysReg) {
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD.getDL(), II,
false /*IsIndirect*/, PhysReg, Var, Expr);
return true;
}

LLVM_DEBUG(dbgs() << "Dropping dbg.value: expression is entry_value but "
"couldn't find a physical register\n"
<< *DI << "\n");
return true;
}
if (auto SI = FuncInfo.StaticAllocaMap.find(dyn_cast<AllocaInst>(V));
SI != FuncInfo.StaticAllocaMap.end()) {
MachineOperand FrameIndexOp = MachineOperand::CreateFI(SI->second);
bool IsIndirect = false;
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD.getDL(), II, IsIndirect,
FrameIndexOp, Var, Expr);
return true;
}
if (Register Reg = lookUpRegForValue(V)) {
// FIXME: This does not handle register-indirect values at offset 0.
if (!FuncInfo.MF->useDebugInstrRef()) {
bool IsIndirect = false;
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD.getDL(), II, IsIndirect,
Reg, Var, Expr);
return true;
}
// If using instruction referencing, produce this as a DBG_INSTR_REF,
// to be later patched up by finalizeDebugInstrRefs.
SmallVector<MachineOperand, 1> MOs({MachineOperand::CreateReg(
/* Reg */ Reg, /* isDef */ false, /* isImp */ false,
/* isKill */ false, /* isDead */ false,
/* isUndef */ false, /* isEarlyClobber */ false,
/* SubReg */ 0, /* isDebug */ true)});
SmallVector<uint64_t, 2> Ops({dwarf::DW_OP_LLVM_arg, 0});
auto *NewExpr = DIExpression::prependOpcodes(Expr, Ops);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD.getDL(),
TII.get(TargetOpcode::DBG_INSTR_REF), /*IsIndirect*/ false, MOs,
Var, NewExpr);
return true;
}
// We don't know how to handle other cases, so we drop.
LLVM_DEBUG(dbgs() << "Dropping debug info for " << *DI << "\n");
if (!lowerDbgValue(V, Expr, Var, MIMD.getDL()))
LLVM_DEBUG(dbgs() << "Dropping debug info for " << *DI << "\n");

return true;
}
case Intrinsic::dbg_label: {
Expand Down
Loading