Re-reapply "[DebugInfo] Use variadic debug values to salvage BinOps and GEP instrs with non-const operands"

Previous build failures were caused by an error in bitcode reading and
writing for DIArgList metadata, which has been fixed in e5d844b587.
There were also some unnecessary asserts that were being triggered on
certain builds, which have been removed.

This reverts commit dad5caa59e.
This commit is contained in:
Stephen Tozer 2021-04-22 12:06:52 +01:00
parent 151e244fe6
commit 791930d740
18 changed files with 399 additions and 142 deletions

View File

@ -2594,6 +2594,16 @@ public:
// return it's sign information.
llvm::Optional<SignedOrUnsignedConstant> isConstant() const;
/// Return the number of unique location operands referred to (via
/// DW_OP_LLVM_arg) in this expression; this is not necessarily the number of
/// instances of DW_OP_LLVM_arg within the expression.
/// For example, for the expression:
/// (DW_OP_LLVM_arg 0, DW_OP_LLVM_arg 1, DW_OP_plus,
/// DW_OP_LLVM_arg 0, DW_OP_mul)
/// This function would return 2, as there are two unique location operands
/// (0 and 1).
uint64_t getNumLocationOperands() const;
using element_iterator = ArrayRef<uint64_t>::iterator;
element_iterator elements_begin() const { return getElements().begin(); }
@ -2741,6 +2751,10 @@ public:
/// return true with an offset of zero.
bool extractIfOffset(int64_t &Offset) const;
/// Returns true iff this DIExpression contains at least one instance of
/// `DW_OP_LLVM_arg, n` for all n in [0, N).
bool hasAllLocationOps(unsigned N) const;
/// Checks if the last 4 elements of the expression are DW_OP_constu <DWARF
/// Address Space> DW_OP_swap DW_OP_xderef and extracts the <DWARF Address
/// Space>.

View File

@ -1122,7 +1122,9 @@ public:
/// must be at least as wide as the IntPtr type for the address space of
/// the base GEP pointer.
bool accumulateConstantOffset(const DataLayout &DL, APInt &Offset) const;
bool collectOffset(const DataLayout &DL, unsigned BitWidth,
SmallDenseMap<Value *, APInt, 8> &VariableOffsets,
APInt &ConstantOffset) const;
// Methods for support type inquiry through isa, cast, and dyn_cast:
static bool classof(const Instruction *I) {
return (I->getOpcode() == Instruction::GetElementPtr);

View File

@ -204,6 +204,11 @@ public:
void replaceVariableLocationOp(Value *OldValue, Value *NewValue);
void replaceVariableLocationOp(unsigned OpIdx, Value *NewValue);
/// Adding a new location operand will always result in this intrinsic using
/// an ArgList, and must always be accompanied by a new expression that uses
/// the new operand.
void addVariableLocationOps(ArrayRef<Value *> NewValues,
DIExpression *NewExpr);
void setVariable(DILocalVariable *NewVar) {
setArgOperand(1, MetadataAsValue::get(NewVar->getContext(), NewVar));

View File

@ -576,6 +576,12 @@ public:
Type *SourceType, ArrayRef<const Value *> Index, const DataLayout &DL,
APInt &Offset,
function_ref<bool(Value &, APInt &)> ExternalAnalysis = nullptr);
/// Collect the offset of this GEP as a map of Values to their associated
/// APInt multipliers, as well as a total Constant Offset.
bool collectOffset(const DataLayout &DL, unsigned BitWidth,
SmallDenseMap<Value *, APInt, 8> &VariableOffsets,
APInt &ConstantOffset) const;
};
class PtrToIntOperator

View File

@ -298,7 +298,8 @@ void salvageDebugInfoForDbgValues(Instruction &I,
/// appended to the expression. \p LocNo: the index of the location operand to
/// which \p I applies, should be 0 for debug info without a DIArgList.
DIExpression *salvageDebugInfoImpl(Instruction &I, DIExpression *DIExpr,
bool StackVal, unsigned LocNo);
bool StackVal, unsigned LocNo,
SmallVectorImpl<Value *> &AdditionalValues);
/// Point debug users of \p From to \p To or salvage them. Use this function
/// only when replacing all uses of \p From with \p To, with a guarantee that

View File

@ -121,11 +121,6 @@ public:
// Currently, DBG_VALUE_VAR expressions must use stack_value.
assert(Expr && Expr->isValid() &&
is_contained(Locs, dwarf::DW_OP_stack_value));
for (DbgValueLocEntry &Entry : ValueLocEntries) {
assert(!Entry.isConstantFP() && !Entry.isConstantInt() &&
"Constant values should only be present in non-variadic "
"DBG_VALUEs.");
}
#endif
}
@ -142,11 +137,6 @@ public:
// Currently, DBG_VALUE_VAR expressions must use stack_value.
assert(Expr && Expr->isValid() &&
is_contained(Expr->getElements(), dwarf::DW_OP_stack_value));
for (DbgValueLocEntry &Entry : ValueLocEntries) {
assert(!Entry.isConstantFP() && !Entry.isConstantInt() &&
"Constant values should only be present in non-variadic "
"DBG_VALUEs.");
}
}
#endif
}

View File

@ -1238,6 +1238,10 @@ void SelectionDAGBuilder::resolveDanglingDebugInfo(const Value *V,
}
void SelectionDAGBuilder::salvageUnresolvedDbgValue(DanglingDebugInfo &DDI) {
// TODO: For the variadic implementation, instead of only checking the fail
// state of `handleDebugValue`, we need know specifically which values were
// invalid, so that we attempt to salvage only those values when processing
// a DIArgList.
assert(!DDI.getDI()->hasArgList() &&
"Not implemented for variadic dbg_values");
Value *V = DDI.getDI()->getValue(0);
@ -1261,16 +1265,21 @@ void SelectionDAGBuilder::salvageUnresolvedDbgValue(DanglingDebugInfo &DDI) {
while (isa<Instruction>(V)) {
Instruction &VAsInst = *cast<Instruction>(V);
// Temporary "0", awaiting real implementation.
DIExpression *NewExpr = salvageDebugInfoImpl(VAsInst, Expr, StackValue, 0);
SmallVector<Value *, 4> AdditionalValues;
DIExpression *SalvagedExpr =
salvageDebugInfoImpl(VAsInst, Expr, StackValue, 0, AdditionalValues);
// If we cannot salvage any further, and haven't yet found a suitable debug
// expression, bail out.
if (!NewExpr)
// TODO: If AdditionalValues isn't empty, then the salvage can only be
// represented with a DBG_VALUE_LIST, so we give up. When we have support
// here for variadic dbg_values, remove that condition.
if (!SalvagedExpr || !AdditionalValues.empty())
break;
// New value and expr now represent this debuginfo.
V = VAsInst.getOperand(0);
Expr = NewExpr;
Expr = SalvagedExpr;
// Some kind of simplification occurred: check whether the operand of the
// salvaged debug expression can be encoded in this DAG.

View File

@ -1244,6 +1244,17 @@ bool DIExpression::extractIfOffset(int64_t &Offset) const {
return false;
}
bool DIExpression::hasAllLocationOps(unsigned N) const {
SmallDenseSet<uint64_t, 4> SeenOps;
for (auto ExprOp : expr_ops())
if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg)
SeenOps.insert(ExprOp.getArg(0));
for (uint64_t Idx = 0; Idx < N; ++Idx)
if (!is_contained(SeenOps, Idx))
return false;
return true;
}
const DIExpression *DIExpression::extractAddressClass(const DIExpression *Expr,
unsigned &AddrClass) {
// FIXME: This seems fragile. Nothing that verifies that these elements
@ -1458,6 +1469,16 @@ Optional<DIExpression *> DIExpression::createFragmentExpression(
return DIExpression::get(Expr->getContext(), Ops);
}
uint64_t DIExpression::getNumLocationOperands() const {
uint64_t Result = 0;
for (auto ExprOp : expr_ops())
if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg)
Result = std::max(Result, ExprOp.getArg(0) + 1);
assert(hasAllLocationOps(Result) &&
"Expression is missing one or more location operands.");
return Result;
}
llvm::Optional<DIExpression::SignedOrUnsignedConstant>
DIExpression::isConstant() const {

View File

@ -1806,6 +1806,15 @@ bool GetElementPtrInst::accumulateConstantOffset(const DataLayout &DL,
return cast<GEPOperator>(this)->accumulateConstantOffset(DL, Offset);
}
bool GetElementPtrInst::collectOffset(
const DataLayout &DL, unsigned BitWidth,
SmallDenseMap<Value *, APInt, 8> &VariableOffsets,
APInt &ConstantOffset) const {
// Delegate to the generic GEPOperator implementation.
return cast<GEPOperator>(this)->collectOffset(DL, BitWidth, VariableOffsets,
ConstantOffset);
}
//===----------------------------------------------------------------------===//
// ExtractElementInst Implementation
//===----------------------------------------------------------------------===//

View File

@ -118,6 +118,23 @@ void DbgVariableIntrinsic::replaceVariableLocationOp(unsigned OpIdx,
0, MetadataAsValue::get(getContext(), DIArgList::get(getContext(), MDs)));
}
void DbgVariableIntrinsic::addVariableLocationOps(ArrayRef<Value *> NewValues,
DIExpression *NewExpr) {
assert(NewExpr->hasAllLocationOps(getNumVariableLocationOps() +
NewValues.size()) &&
"NewExpr for debug variable intrinsic does not reference every "
"location operand.");
assert(!is_contained(NewValues, nullptr) && "New values must be non-null");
setArgOperand(2, MetadataAsValue::get(getContext(), NewExpr));
SmallVector<ValueAsMetadata *, 4> MDs;
for (auto *VMD : location_ops())
MDs.push_back(getAsMetadata(VMD));
for (auto *VMD : NewValues)
MDs.push_back(getAsMetadata(VMD));
setArgOperand(
0, MetadataAsValue::get(getContext(), DIArgList::get(getContext(), MDs)));
}
Optional<uint64_t> DbgVariableIntrinsic::getFragmentSizeInBits() const {
if (auto Fragment = getExpression()->getFragmentInfo())
return Fragment->SizeInBits;

View File

@ -142,4 +142,61 @@ bool GEPOperator::accumulateConstantOffset(
}
return true;
}
bool GEPOperator::collectOffset(
const DataLayout &DL, unsigned BitWidth,
SmallDenseMap<Value *, APInt, 8> &VariableOffsets,
APInt &ConstantOffset) const {
assert(BitWidth == DL.getIndexSizeInBits(getPointerAddressSpace()) &&
"The offset bit width does not match DL specification.");
auto CollectConstantOffset = [&](APInt Index, uint64_t Size) {
Index = Index.sextOrTrunc(BitWidth);
APInt IndexedSize = APInt(BitWidth, Size);
ConstantOffset += Index * IndexedSize;
};
for (gep_type_iterator GTI = gep_type_begin(this), GTE = gep_type_end(this);
GTI != GTE; ++GTI) {
// Scalable vectors are multiplied by a runtime constant.
bool ScalableType = isa<ScalableVectorType>(GTI.getIndexedType());
Value *V = GTI.getOperand();
StructType *STy = GTI.getStructTypeOrNull();
// Handle ConstantInt if possible.
if (auto ConstOffset = dyn_cast<ConstantInt>(V)) {
if (ConstOffset->isZero())
continue;
// If the type is scalable and the constant is not zero (vscale * n * 0 =
// 0) bailout.
// TODO: If the runtime value is accessible at any point before DWARF
// emission, then we could potentially keep a forward reference to it
// in the debug value to be filled in later.
if (ScalableType)
return false;
// Handle a struct index, which adds its field offset to the pointer.
if (STy) {
unsigned ElementIdx = ConstOffset->getZExtValue();
const StructLayout *SL = DL.getStructLayout(STy);
// Element offset is in bytes.
CollectConstantOffset(APInt(BitWidth, SL->getElementOffset(ElementIdx)),
1);
continue;
}
CollectConstantOffset(ConstOffset->getValue(),
DL.getTypeAllocSize(GTI.getIndexedType()));
continue;
}
if (STy || ScalableType)
return false;
// Insert an initial offset of 0 for V iff none exists already, then
// increment the offset by IndexedSize.
VariableOffsets.try_emplace(V, BitWidth, 0);
APInt IndexedSize =
APInt(BitWidth, DL.getTypeAllocSize(GTI.getIndexedType()));
VariableOffsets[V] += IndexedSize;
}
return true;
}
} // namespace llvm

View File

@ -2101,10 +2101,15 @@ void coro::salvageDebugInfo(
} else if (auto *StInst = dyn_cast<StoreInst>(Storage)) {
Storage = StInst->getOperand(0);
} else if (auto *GEPInst = dyn_cast<GetElementPtrInst>(Storage)) {
Expr = llvm::salvageDebugInfoImpl(*GEPInst, Expr,
/*WithStackValue=*/false, 0);
if (!Expr)
return;
SmallVector<Value *> AdditionalValues;
DIExpression *SalvagedExpr = llvm::salvageDebugInfoImpl(
*GEPInst, Expr,
/*WithStackValue=*/false, 0, AdditionalValues);
// Debug declares cannot currently handle additional location
// operands.
if (!SalvagedExpr || !AdditionalValues.empty())
break;
Expr = SalvagedExpr;
Storage = GEPInst->getOperand(0);
} else if (auto *BCInst = dyn_cast<llvm::BitCastInst>(Storage))
Storage = BCInst->getOperand(0);

View File

@ -1729,17 +1729,26 @@ void llvm::salvageDebugInfoForDbgValues(
is_contained(DIILocation, &I) &&
"DbgVariableIntrinsic must use salvaged instruction as its location");
unsigned LocNo = std::distance(DIILocation.begin(), find(DIILocation, &I));
DIExpression *DIExpr =
salvageDebugInfoImpl(I, DII->getExpression(), StackValue, LocNo);
SmallVector<Value *, 4> AdditionalValues;
DIExpression *SalvagedExpr = salvageDebugInfoImpl(
I, DII->getExpression(), StackValue, LocNo, AdditionalValues);
// salvageDebugInfoImpl should fail on examining the first element of
// DbgUsers, or none of them.
if (!DIExpr)
if (!SalvagedExpr)
break;
DII->replaceVariableLocationOp(&I, I.getOperand(0));
DII->setExpression(DIExpr);
if (AdditionalValues.empty()) {
DII->setExpression(SalvagedExpr);
} else if (isa<DbgValueInst>(DII)) {
DII->addVariableLocationOps(AdditionalValues, SalvagedExpr);
} else {
// Do not salvage using DIArgList for dbg.addr/dbg.declare, as it is
// currently only valid for stack value expressions.
Value *Undef = UndefValue::get(I.getOperand(0)->getType());
DII->replaceVariableLocationOp(I.getOperand(0), Undef);
}
LLVM_DEBUG(dbgs() << "SALVAGE: " << *DII << '\n');
Salvaged = true;
}
@ -1754,12 +1763,27 @@ void llvm::salvageDebugInfoForDbgValues(
}
bool getSalvageOpsForGEP(GetElementPtrInst *GEP, const DataLayout &DL,
SmallVectorImpl<uint64_t> &Opcodes) {
uint64_t CurrentLocOps,
SmallVectorImpl<uint64_t> &Opcodes,
SmallVectorImpl<Value *> &AdditionalValues) {
unsigned BitWidth = DL.getIndexSizeInBits(GEP->getPointerAddressSpace());
// Rewrite a constant GEP into a DIExpression.
// Rewrite a GEP into a DIExpression.
SmallDenseMap<Value *, APInt, 8> VariableOffsets;
APInt ConstantOffset(BitWidth, 0);
if (!GEP->accumulateConstantOffset(DL, ConstantOffset))
if (!GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset))
return false;
if (!VariableOffsets.empty() && !CurrentLocOps) {
Opcodes.insert(Opcodes.begin(), {dwarf::DW_OP_LLVM_arg, 0});
CurrentLocOps = 1;
}
for (auto Offset : VariableOffsets) {
AdditionalValues.push_back(Offset.first);
assert(Offset.second.isStrictlyPositive() &&
"Expected strictly positive multiplier for offset.");
Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps++, dwarf::DW_OP_constu,
Offset.second.getZExtValue(), dwarf::DW_OP_mul,
dwarf::DW_OP_plus});
}
DIExpression::appendOffset(Opcodes, ConstantOffset.getSExtValue());
return true;
}
@ -1794,23 +1818,35 @@ uint64_t getDwarfOpForBinOp(Instruction::BinaryOps Opcode) {
}
}
bool getSalvageOpsForBinOp(BinaryOperator *BI,
SmallVectorImpl<uint64_t> &Opcodes) {
// Rewrite binary operations with constant integer operands.
bool getSalvageOpsForBinOp(BinaryOperator *BI, uint64_t CurrentLocOps,
SmallVectorImpl<uint64_t> &Opcodes,
SmallVectorImpl<Value *> &AdditionalValues) {
// Handle binary operations with constant integer operands as a special case.
auto *ConstInt = dyn_cast<ConstantInt>(BI->getOperand(1));
if (!ConstInt || ConstInt->getBitWidth() > 64)
// Values wider than 64 bits cannot be represented within a DIExpression.
if (ConstInt && ConstInt->getBitWidth() > 64)
return false;
uint64_t Val = ConstInt->getSExtValue();
Instruction::BinaryOps BinOpcode = BI->getOpcode();
// Add or Sub Instructions with a constant operand can potentially be
// simplified.
if (BinOpcode == Instruction::Add || BinOpcode == Instruction::Sub) {
uint64_t Offset = BinOpcode == Instruction::Add ? Val : -int64_t(Val);
DIExpression::appendOffset(Opcodes, Offset);
return true;
// Push any Constant Int operand onto the expression stack.
if (ConstInt) {
uint64_t Val = ConstInt->getSExtValue();
// Add or Sub Instructions with a constant operand can potentially be
// simplified.
if (BinOpcode == Instruction::Add || BinOpcode == Instruction::Sub) {
uint64_t Offset = BinOpcode == Instruction::Add ? Val : -int64_t(Val);
DIExpression::appendOffset(Opcodes, Offset);
return true;
}
Opcodes.append({dwarf::DW_OP_constu, Val});
} else {
if (!CurrentLocOps) {
Opcodes.append({dwarf::DW_OP_LLVM_arg, 0});
CurrentLocOps = 1;
}
Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps});
AdditionalValues.push_back(BI->getOperand(1));
}
// Add constant int operand to expression stack.
Opcodes.append({dwarf::DW_OP_constu, Val});
// Add salvaged binary operator to expression stack, if it has a valid
// representation in a DIExpression.
@ -1822,9 +1858,11 @@ bool getSalvageOpsForBinOp(BinaryOperator *BI,
return true;
}
DIExpression *llvm::salvageDebugInfoImpl(Instruction &I,
DIExpression *SrcDIExpr,
bool WithStackValue, unsigned LocNo) {
DIExpression *
llvm::salvageDebugInfoImpl(Instruction &I, DIExpression *SrcDIExpr,
bool WithStackValue, unsigned LocNo,
SmallVectorImpl<Value *> &AdditionalValues) {
uint64_t CurrentLocOps = SrcDIExpr->getNumLocationOperands();
auto &M = *I.getModule();
auto &DL = M.getDataLayout();
@ -1838,7 +1876,7 @@ DIExpression *llvm::salvageDebugInfoImpl(Instruction &I,
};
// initializer-list helper for applying operators to the source DIExpression.
auto applyOps = [&](ArrayRef<uint64_t> Opcodes) -> DIExpression * {
auto applyOps = [&](ArrayRef<uint64_t> Opcodes) {
SmallVector<uint64_t, 8> Ops(Opcodes.begin(), Opcodes.end());
return doSalvage(Ops);
};
@ -1864,15 +1902,15 @@ DIExpression *llvm::salvageDebugInfoImpl(Instruction &I,
SmallVector<uint64_t, 8> Ops;
if (auto *GEP = dyn_cast<GetElementPtrInst>(&I)) {
if (getSalvageOpsForGEP(GEP, DL, Ops))
if (getSalvageOpsForGEP(GEP, DL, CurrentLocOps, Ops, AdditionalValues))
return doSalvage(Ops);
} else if (auto *BI = dyn_cast<BinaryOperator>(&I)) {
if (getSalvageOpsForBinOp(BI, Ops))
if (getSalvageOpsForBinOp(BI, CurrentLocOps, Ops, AdditionalValues))
return doSalvage(Ops);
}
// *Not* to do: we should not attempt to salvage load instructions,
// because the validity and lifetime of a dbg.value containing
// DW_OP_deref becomes difficult to analyze. See PR40628 for examples.
// *Not* to do: we should not attempt to salvage load instructions,
// because the validity and lifetime of a dbg.value containing
// DW_OP_deref becomes difficult to analyze. See PR40628 for examples.
return nullptr;
}

View File

@ -702,12 +702,12 @@ if.end: ; preds = %if.then, %entry
; CHECK-NEXT: }
; CHECK-NEXT: .section .debug_info
; CHECK-NEXT: {
; CHECK-NEXT:.b32 10029 // Length of Unit
; CHECK-NEXT:.b32 10034 // Length of Unit
; CHECK-NEXT:.b8 2 // DWARF version number
; CHECK-NEXT:.b8 0
; CHECK-NEXT:.b32 .debug_abbrev // Offset Into Abbrev. Section
; CHECK-NEXT:.b8 8 // Address Size (in bytes)
; CHECK-NEXT:.b8 1 // Abbrev [1] 0xb:0x2726 DW_TAG_compile_unit
; CHECK-NEXT:.b8 1 // Abbrev [1] 0xb:0x272b DW_TAG_compile_unit
; CHECK-NEXT:.b8 0 // DW_AT_producer
; CHECK-NEXT:.b8 4 // DW_AT_language
; CHECK-NEXT:.b8 0
@ -8306,7 +8306,7 @@ if.end: ; preds = %if.then, %entry
; CHECK-NEXT:.b8 3 // DW_AT_decl_line
; CHECK-NEXT:.b32 3345 // DW_AT_type
; CHECK-NEXT:.b8 0 // End Of Children Mark
; CHECK-NEXT:.b8 40 // Abbrev [40] 0x2671:0xbf DW_TAG_subprogram
; CHECK-NEXT:.b8 40 // Abbrev [40] 0x2671:0xc4 DW_TAG_subprogram
; CHECK-NEXT:.b64 Lfunc_begin0 // DW_AT_low_pc
; CHECK-NEXT:.b64 Lfunc_end0 // DW_AT_high_pc
; CHECK-NEXT:.b8 1 // DW_AT_frame_base
@ -8386,7 +8386,7 @@ if.end: ; preds = %if.then, %entry
; CHECK-NEXT:.b8 12 // DW_AT_call_file
; CHECK-NEXT:.b8 6 // DW_AT_call_line
; CHECK-NEXT:.b8 37 // DW_AT_call_column
; CHECK-NEXT:.b8 43 // Abbrev [43] 0x2711:0x1e DW_TAG_inlined_subroutine
; CHECK-NEXT:.b8 43 // Abbrev [43] 0x2711:0x23 DW_TAG_inlined_subroutine
; CHECK-NEXT:.b32 9791 // DW_AT_abstract_origin
; CHECK-NEXT:.b64 Ltmp9 // DW_AT_low_pc
; CHECK-NEXT:.b64 Ltmp10 // DW_AT_high_pc
@ -8395,6 +8395,8 @@ if.end: ; preds = %if.then, %entry
; CHECK-NEXT:.b8 5 // DW_AT_call_column
; CHECK-NEXT:.b8 44 // Abbrev [44] 0x2729:0x5 DW_TAG_formal_parameter
; CHECK-NEXT:.b32 9820 // DW_AT_abstract_origin
; CHECK-NEXT:.b8 44 // Abbrev [44] 0x272e:0x5 DW_TAG_formal_parameter
; CHECK-NEXT:.b32 9829 // DW_AT_abstract_origin
; CHECK-NEXT:.b8 0 // End Of Children Mark
; CHECK-NEXT:.b8 0 // End Of Children Mark
; CHECK-NEXT:.b8 0 // End Of Children Mark

View File

@ -0,0 +1,56 @@
; RUN: opt %s -dce -S | FileCheck %s
; Tests the salvaging of GEP instructions, specifically struct indexing and
; non-constant array indexing.
%struct.S = type { i32, i32 }
; CHECK: call void @llvm.dbg.value(metadata !DIArgList(%struct.S* %ptr, i64 %offset),
; CHECK-SAME: ![[VAR_OFFSET_PTR:[0-9]+]],
; CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_constu, 8, DW_OP_mul, DW_OP_plus, DW_OP_plus_uconst, 4, DW_OP_stack_value))
; CHECK: ![[VAR_OFFSET_PTR]] = !DILocalVariable(name: "offset_ptr"
define void @"?foo@@YAXPEAUS@@_J@Z"(%struct.S* %ptr, i64 %offset) !dbg !8 {
entry:
call void @llvm.dbg.value(metadata i64 %offset, metadata !20, metadata !DIExpression()), !dbg !24
call void @llvm.dbg.value(metadata %struct.S* %ptr, metadata !21, metadata !DIExpression()), !dbg !24
%arrayidx = getelementptr inbounds %struct.S, %struct.S* %ptr, i64 %offset, !dbg !25
%b = getelementptr inbounds %struct.S, %struct.S* %arrayidx, i32 0, i32 1, !dbg !25
call void @llvm.dbg.value(metadata i32* %b, metadata !22, metadata !DIExpression()), !dbg !24
ret void, !dbg !26
}
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5, !6}
!llvm.ident = !{!7}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "salvage-gep.cpp", directory: "/")
!2 = !{}
!3 = !{i32 2, !"CodeView", i32 1}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 2}
!6 = !{i32 7, !"PIC Level", i32 2}
!7 = !{!"clang version 11.0.0"}
!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAXPEAUS@@_J@Z", scope: !9, file: !9, line: 7, type: !10, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !19)
!9 = !DIFile(filename: ".\\salvage-gep.cpp", directory: "/")
!10 = !DISubroutineType(types: !11)
!11 = !{null, !12, !18}
!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64)
!13 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !9, line: 2, size: 64, flags: DIFlagTypePassByValue, elements: !14, identifier: ".?AUS@@")
!14 = !{!15, !17}
!15 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !13, file: !9, line: 3, baseType: !16, size: 32)
!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!17 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !13, file: !9, line: 4, baseType: !16, size: 32, offset: 32)
!18 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed)
!19 = !{!20, !21, !22}
!20 = !DILocalVariable(name: "offset", arg: 2, scope: !8, file: !9, line: 7, type: !18)
!21 = !DILocalVariable(name: "ptr", arg: 1, scope: !8, file: !9, line: 7, type: !12)
!22 = !DILocalVariable(name: "offset_ptr", scope: !8, file: !9, line: 8, type: !23)
!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
!24 = !DILocation(line: 0, scope: !8)
!25 = !DILocation(line: 8, scope: !8)
!26 = !DILocation(line: 9, scope: !8)

View File

@ -0,0 +1,45 @@
; RUN: opt %s -dce -S | FileCheck %s
; Tests the salvaging of binary operators that use more than one non-constant
; SSA value.
; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b),
; CHECK-SAME: ![[VAR_C:[0-9]+]],
; CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value))
; CHECK: ![[VAR_C]] = !DILocalVariable(name: "c"
define i32 @"?multiply@@YAHHH@Z"(i32 %a, i32 %b) !dbg !8 {
entry:
call void @llvm.dbg.value(metadata i32 %b, metadata !12, metadata !DIExpression()), !dbg !13
call void @llvm.dbg.value(metadata i32 %a, metadata !14, metadata !DIExpression()), !dbg !13
%add = add nsw i32 %a, %b, !dbg !15
call void @llvm.dbg.value(metadata i32 %add, metadata !16, metadata !DIExpression()), !dbg !13
%mul = mul nsw i32 %a, %b, !dbg !17
ret i32 %mul, !dbg !17
}
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5, !6}
!llvm.ident = !{!7}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "test.cpp", directory: "/")
!2 = !{}
!3 = !{i32 2, !"CodeView", i32 1}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 2}
!6 = !{i32 7, !"PIC Level", i32 2}
!7 = !{!"clang version 11.0.0"}
!8 = distinct !DISubprogram(name: "multiply", linkageName: "?multiply@@YAHHH@Z", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
!9 = !DISubroutineType(types: !10)
!10 = !{!11, !11, !11}
!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!12 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 1, type: !11)
!13 = !DILocation(line: 0, scope: !8)
!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 1, type: !11)
!15 = !DILocation(line: 2, scope: !8)
!16 = !DILocalVariable(name: "c", scope: !8, file: !1, line: 2, type: !11)
!17 = !DILocation(line: 3, scope: !8)

View File

@ -33,23 +33,25 @@ sink1:
; value range.
; CHECK-LABEL: define i32 @bar(
; CHECK: call void @llvm.dbg.value(metadata i32* undef,
; CHECK: call void @llvm.dbg.value(metadata <vscale x 4 x i32>* undef,
; CHECK-NEXT: br label %sink2
define i32 @bar(i32 *%a, i32 %b) !dbg !70 {
define i32 @bar(<vscale x 4 x i32>* %a, i32 %b) !dbg !70 {
entry:
%gep = getelementptr i32, i32 *%a, i32 %b
call void @llvm.dbg.value(metadata i32* %gep, metadata !73, metadata !12), !dbg !74
%gep = getelementptr <vscale x 4 x i32>, <vscale x 4 x i32>* %a, i32 %b
call void @llvm.dbg.value(metadata <vscale x 4 x i32>* %gep, metadata !73, metadata !12), !dbg !74
br label %sink2
sink2:
; CHECK-LABEL: sink2:
; CHECK: call void @llvm.dbg.value(metadata i32* %gep,
; CHECK: call void @llvm.dbg.value(metadata <vscale x 4 x i32>* %gep,
; CHECK-SAME: metadata !{{[0-9]+}}, metadata !DIExpression())
; CHECK-NEXT: load
; CHECK-NEXT: extractelement
; CHECK-NEXT: ret
%0 = load i32, i32* %gep
ret i32 %0
%0 = load <vscale x 4 x i32>, <vscale x 4 x i32>* %gep
%extract = extractelement <vscale x 4 x i32> %0, i32 1
ret i32 %extract
}
; This GEP is sunk, and has multiple debug uses in the same block. Check that

View File

@ -1,95 +1,73 @@
; RUN: opt < %s -reassociate -S | FileCheck %s
; RUN: opt < %s -reassociate -S | FileCheck %s
; Check that reassociate pass now undefs debug intrinsics that reference a value
; that gets dropped and cannot be salvaged.
; CHECK-NOT: %add = fadd fast float %a, %b
; CHECK: call void @llvm.dbg.value(metadata float undef, metadata [[VAR_X:![0-9]+]], metadata !DIExpression())
; CHECK-LABEL: if.then:
; CHECK-NOT: %add1 = fadd fast float %add, %c
; CHECK: call void @llvm.dbg.value(metadata float undef, metadata [[VAR_Y:![0-9]+]], metadata !DIExpression())
; CHECK-LABEL: !0 =
; CHECK-DAG: [[VAR_Y]] = !DILocalVariable(name: "y"
; CHECK-DAG: [[VAR_X]] = !DILocalVariable(name: "x"
define float @"?foo@@YAMMMMM@Z"(float %a, float %b, float %c, float %d) !dbg !8 {
entry:
call void @llvm.dbg.value(metadata float %d, metadata !12, metadata !DIExpression()), !dbg !13
call void @llvm.dbg.value(metadata float %c, metadata !14, metadata !DIExpression()), !dbg !13
call void @llvm.dbg.value(metadata float %b, metadata !15, metadata !DIExpression()), !dbg !13
call void @llvm.dbg.value(metadata float %a, metadata !16, metadata !DIExpression()), !dbg !13
%add = fadd fast float %a, %b, !dbg !17
call void @llvm.dbg.value(metadata float %add, metadata !18, metadata !DIExpression()), !dbg !13
%cmp = fcmp fast oeq float %d, 4.000000e+00, !dbg !19
br i1 %cmp, label %if.then, label %return, !dbg !19
; Check that reassociate pass now undefs debug intrinsics that reference a value
; that gets dropped and cannot be salvaged.
if.then: ; preds = %entry
%add1 = fadd fast float %add, %c, !dbg !20
call void @llvm.dbg.value(metadata float %add1, metadata !23, metadata !DIExpression()), !dbg !24
%sub = fsub fast float %add, 1.200000e+01, !dbg !25
%sub2 = fsub fast float %add1, %sub, !dbg !25
%mul = fmul fast float %sub2, 2.000000e+01, !dbg !25
%div = fdiv fast float %mul, 3.000000e+00, !dbg !25
br label %return, !dbg !25
define hidden i32 @main() local_unnamed_addr {
entry:
%foo = alloca i32, align 4, !dbg !20
%foo.0.foo.0..sroa_cast = bitcast i32* %foo to i8*, !dbg !20
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %foo.0.foo.0..sroa_cast), !dbg !20
store volatile i32 4, i32* %foo, align 4, !dbg !20, !tbaa !21
%foo.0.foo.0. = load volatile i32, i32* %foo, align 4, !dbg !25, !tbaa !21
%foo.0.foo.0.15 = load volatile i32, i32* %foo, align 4, !dbg !27, !tbaa !21
%foo.0.foo.0.16 = load volatile i32, i32* %foo, align 4, !dbg !28, !tbaa !21
; CHECK-NOT: %add = add nsw i32 %foo.0.foo.0., %foo.0.foo.0.15
%add = add nsw i32 %foo.0.foo.0., %foo.0.foo.0.15, !dbg !29
; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata [[VAR_A:![0-9]+]], metadata !DIExpression())
call void @llvm.dbg.value(metadata i32 %add, metadata !19, metadata !DIExpression()), !dbg !26
%foo.0.foo.0.17 = load volatile i32, i32* %foo, align 4, !dbg !30, !tbaa !21
%cmp = icmp eq i32 %foo.0.foo.0.17, 4, !dbg !30
br i1 %cmp, label %if.then, label %if.end, !dbg !32
; CHECK-LABEL: if.then:
if.then:
; CHECK-NOT: %add1 = add nsw i32 %add, %foo.0.foo.0.16
%add1 = add nsw i32 %add, %foo.0.foo.0.16, !dbg !33
; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata [[VAR_A]], metadata !DIExpression())
call void @llvm.dbg.value(metadata i32 %add1, metadata !19, metadata !DIExpression()), !dbg !26
; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata [[VAR_CHEESE:![0-9]+]], metadata !DIExpression())
call void @llvm.dbg.value(metadata i32 %add, metadata !18, metadata !DIExpression()), !dbg !26
%sub = add nsw i32 %add, -12, !dbg !34
%sub3 = sub nsw i32 %add1, %sub, !dbg !34
%mul = mul nsw i32 %sub3, 20, !dbg !36
%div = sdiv i32 %mul, 3, !dbg !37
br label %if.end, !dbg !38
if.end:
%a.0 = phi i32 [ %div, %if.then ], [ 0, %entry ], !dbg !39
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %foo.0.foo.0..sroa_cast), !dbg !40
ret i32 %a.0, !dbg !41
return: ; preds = %entry, %if.then
%retval.0 = phi float [ %div, %if.then ], [ 0.000000e+00, %entry ], !dbg !13
ret float %retval.0, !dbg !26
}
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
declare void @llvm.dbg.declare(metadata, metadata, metadata) #2
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
declare void @llvm.dbg.value(metadata, metadata, metadata) #2
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5, !6}
!llvm.ident = !{!7}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, debugInfoForProfiling: true, nameTableKind: None)
!1 = !DIFile(filename: "test.cpp", directory: "F:\")
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "undef_intrinsics_when_deleting_instructions.cpp", directory: "/")
!2 = !{}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!3 = !{i32 2, !"CodeView", i32 1}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 2}
!6 = !{i32 7, !"PIC Level", i32 2}
!7 = !{!"clang version 10.0.0"}
!8 = distinct !DISubprogram(name: "main", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13)
!9 = !DIFile(filename: "./test.cpp", directory: "F:\")
!10 = !DISubroutineType(types: !11)
!11 = !{!12}
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!13 = !{!14, !16, !17, !18, !19}
!14 = !DILocalVariable(name: "foo", scope: !8, file: !9, line: 2, type: !15)
!15 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !12)
!16 = !DILocalVariable(name: "read1", scope: !8, file: !9, line: 3, type: !12)
!17 = !DILocalVariable(name: "read2", scope: !8, file: !9, line: 4, type: !12)
; CHECK: [[VAR_CHEESE]] = !DILocalVariable(name: "cheese"
!18 = !DILocalVariable(name: "cheese", scope: !8, file: !9, line: 6, type: !12)
; CHECK: [[VAR_A]] = !DILocalVariable(name: "a"
!19 = !DILocalVariable(name: "a", scope: !8, file: !9, line: 7, type: !12)
!20 = !DILocation(line: 2, scope: !8)
!21 = !{!22, !22, i64 0}
!22 = !{!"int", !23, i64 0}
!23 = !{!"omnipotent char", !24, i64 0}
!24 = !{!"Simple C++ TBAA"}
!25 = !DILocation(line: 3, scope: !8)
!26 = !DILocation(line: 0, scope: !8)
!27 = !DILocation(line: 4, scope: !8)
!28 = !DILocation(line: 6, scope: !8)
!29 = !DILocation(line: 7, scope: !8)
!30 = !DILocation(line: 10, scope: !31)
!31 = distinct !DILexicalBlock(scope: !8, file: !9, line: 10)
!32 = !DILocation(line: 10, scope: !8)
!33 = !DILocation(line: 8, scope: !8)
!34 = !DILocation(line: 12, scope: !35)
!35 = distinct !DILexicalBlock(scope: !31, file: !9, line: 10)
!36 = !DILocation(line: 13, scope: !35)
!37 = !DILocation(line: 14, scope: !35)
!38 = !DILocation(line: 15, scope: !35)
!39 = !DILocation(line: 0, scope: !31)
!40 = !DILocation(line: 20, scope: !8)
!41 = !DILocation(line: 19, scope: !8)
!7 = !{!"clang version 11.0.0"}
!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAMMMMM@Z", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
!9 = !DISubroutineType(types: !10)
!10 = !{!11, !11, !11, !11, !11}
!11 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
!12 = !DILocalVariable(name: "d", arg: 4, scope: !8, file: !1, line: 1, type: !11)
!13 = !DILocation(line: 0, scope: !8)
!14 = !DILocalVariable(name: "c", arg: 3, scope: !8, file: !1, line: 1, type: !11)
!15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 1, type: !11)
!16 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 1, type: !11)
!17 = !DILocation(line: 2, scope: !8)
!18 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !11)
!19 = !DILocation(line: 3, scope: !8)
!20 = !DILocation(line: 4, scope: !21)
!21 = distinct !DILexicalBlock(scope: !22, file: !1, line: 3)
!22 = distinct !DILexicalBlock(scope: !8, file: !1, line: 3)
!23 = !DILocalVariable(name: "y", scope: !21, file: !1, line: 4, type: !11)
!24 = !DILocation(line: 0, scope: !21)
!25 = !DILocation(line: 5, scope: !21)
!26 = !DILocation(line: 8, scope: !8)