mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
Debug info: Infrastructure to support debug locations for fragmented
variables (for example, by-value struct arguments passed in registers, or large integer values split across several smaller registers). On the IR level, this adds a new type of complex address operation OpPiece to DIVariable that describes size and offset of a variable fragment. On the DWARF emitter level, all pieces describing the same variable are collected, sorted and emitted as DWARF expressions using the DW_OP_piece and DW_OP_bit_piece operators. http://reviews.llvm.org/D3373 rdar://problem/15928306 What this patch doesn't do / Future work: - This patch only adds the backend machinery to make this work, patches that change SROA and SelectionDAG's type legalizer to actually create such debug info will follow. (http://reviews.llvm.org/D2680) - Making the DIVariable complex expressions into an argument of dbg.value will reduce the memory footprint of the debug metadata. - The sorting/uniquing of pieces should be moved into DebugLocEntry, to facilitate the merging of multi-piece entries. llvm-svn: 214576
This commit is contained in:
parent
697d6af472
commit
bce66c31aa
@ -589,6 +589,12 @@ The context is either the subprogram or block where the variable is defined.
|
||||
Name the source variable name. Context and line indicate where the variable
|
||||
was defined. Type descriptor defines the declared type of the variable.
|
||||
|
||||
The ``OpPiece`` operator is used for (typically larger aggregate)
|
||||
variables that are fragmented across several locations. It takes two
|
||||
i32 arguments, an offset and a size in bytes to describe which piece
|
||||
of the variable is at this location.
|
||||
|
||||
|
||||
.. _format_common_intrinsics:
|
||||
|
||||
Debugger intrinsic functions
|
||||
|
@ -405,6 +405,13 @@ public:
|
||||
/// Get the value for DW_AT_APPLE_isa. Zero if no isa encoding specified.
|
||||
virtual unsigned getISAEncoding() { return 0; }
|
||||
|
||||
/// Emit a dwarf register operation for describing
|
||||
/// - a small value occupying only part of a register or
|
||||
/// - a register representing only part of a value.
|
||||
void EmitDwarfOpPiece(ByteStreamer &Streamer, unsigned SizeInBits,
|
||||
unsigned OffsetInBits = 0) const;
|
||||
|
||||
|
||||
/// \brief Emit a partial DWARF register operation.
|
||||
/// \param MLoc the register
|
||||
/// \param PieceSize size and
|
||||
@ -421,7 +428,7 @@ public:
|
||||
unsigned PieceSize = 0,
|
||||
unsigned PieceOffset = 0) const;
|
||||
|
||||
/// Emit dwarf register operation.
|
||||
/// EmitDwarfRegOp - Emit a dwarf register operation.
|
||||
/// \param Indirect whether this is a register-indirect address
|
||||
virtual void EmitDwarfRegOp(ByteStreamer &BS, const MachineLocation &MLoc,
|
||||
bool Indirect) const;
|
||||
|
@ -85,7 +85,7 @@ namespace llvm {
|
||||
|
||||
public:
|
||||
explicit DIBuilder(Module &M);
|
||||
enum ComplexAddrKind { OpPlus=1, OpDeref };
|
||||
enum ComplexAddrKind { OpPlus=1, OpDeref, OpPiece };
|
||||
enum DebugEmissionKind { FullDebug=1, LineTablesOnly };
|
||||
|
||||
/// finalize - Construct any deferred debug info descriptors.
|
||||
@ -561,6 +561,16 @@ namespace llvm {
|
||||
DITypeRef Ty, ArrayRef<Value *> Addr,
|
||||
unsigned ArgNo = 0);
|
||||
|
||||
/// createVariablePiece - Create a descriptor to describe one part
|
||||
/// of aggregate variable that is fragmented across multiple Values.
|
||||
///
|
||||
/// @param Variable Variable that is partially represented by this.
|
||||
/// @param OffsetInBytes Offset of the piece in bytes.
|
||||
/// @param SizeInBytes Size of the piece in bytes.
|
||||
DIVariable createVariablePiece(DIVariable Variable,
|
||||
unsigned OffsetInBytes,
|
||||
unsigned SizeInBytes);
|
||||
|
||||
/// createFunction - Create a new descriptor for the specified subprogram.
|
||||
/// See comments in DISubprogram for descriptions of these fields.
|
||||
/// @param Scope Function scope.
|
||||
|
@ -725,6 +725,17 @@ public:
|
||||
/// information for an inlined function arguments.
|
||||
bool isInlinedFnArgument(const Function *CurFn);
|
||||
|
||||
/// isVariablePiece - Return whether this is a piece of an aggregate
|
||||
/// variable.
|
||||
bool isVariablePiece() const;
|
||||
/// getPieceOffset - Return the offset of this piece in bytes.
|
||||
uint64_t getPieceOffset() const;
|
||||
/// getPieceSize - Return the size of this piece in bytes.
|
||||
uint64_t getPieceSize() const;
|
||||
|
||||
/// Return the size reported by the variable's type.
|
||||
unsigned getSizeInBits(const DITypeIdentifierMap &Map);
|
||||
|
||||
void printExtendedName(raw_ostream &OS) const;
|
||||
};
|
||||
|
||||
@ -842,6 +853,9 @@ DIVariable createInlinedVariable(MDNode *DV, MDNode *InlinedScope,
|
||||
/// cleanseInlinedVariable - Remove inlined scope from the variable.
|
||||
DIVariable cleanseInlinedVariable(MDNode *DV, LLVMContext &VMContext);
|
||||
|
||||
/// getEntireVariable - Remove OpPiece exprs from the variable.
|
||||
DIVariable getEntireVariable(DIVariable DV);
|
||||
|
||||
/// Construct DITypeIdentifierMap by going through retained types of each CU.
|
||||
DITypeIdentifierMap generateDITypeIdentifierMap(const NamedMDNode *CU_Nodes);
|
||||
|
||||
|
@ -618,7 +618,11 @@ static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) {
|
||||
if (!Name.empty())
|
||||
OS << Name << ":";
|
||||
}
|
||||
OS << V.getName() << " <- ";
|
||||
OS << V.getName();
|
||||
if (V.isVariablePiece())
|
||||
OS << " [piece offset=" << V.getPieceOffset()
|
||||
<< " size="<<V.getPieceSize()<<"]";
|
||||
OS << " <- ";
|
||||
|
||||
// The second operand is only an offset if it's an immediate.
|
||||
bool Deref = MI->getOperand(0).isReg() && MI->getOperand(1).isImm();
|
||||
|
@ -214,13 +214,10 @@ static void emitDwarfRegOpIndirect(ByteStreamer &Streamer, int Reg, int Offset,
|
||||
Streamer.EmitInt8(dwarf::DW_OP_deref, "DW_OP_deref");
|
||||
}
|
||||
|
||||
/// Emit a dwarf register operation for describing
|
||||
/// - a small value occupying only part of a register or
|
||||
/// - a small register representing only part of a value.
|
||||
static void emitDwarfOpPiece(ByteStreamer &Streamer, unsigned SizeInBits,
|
||||
unsigned OffsetInBits) {
|
||||
assert(SizeInBits > 0 && "zero-sized piece");
|
||||
unsigned SizeOfByte = 8;
|
||||
void AsmPrinter::EmitDwarfOpPiece(ByteStreamer &Streamer, unsigned SizeInBits,
|
||||
unsigned OffsetInBits) const {
|
||||
assert(SizeInBits > 0 && "piece has size zero");
|
||||
const unsigned SizeOfByte = 8;
|
||||
if (OffsetInBits > 0 || SizeInBits % SizeOfByte) {
|
||||
Streamer.EmitInt8(dwarf::DW_OP_bit_piece, "DW_OP_bit_piece");
|
||||
Streamer.EmitULEB128(SizeInBits, Twine(SizeInBits));
|
||||
@ -255,7 +252,7 @@ void AsmPrinter::EmitDwarfRegOpPiece(ByteStreamer &Streamer,
|
||||
// If this is a valid register number, emit it.
|
||||
if (Reg >= 0) {
|
||||
emitDwarfRegOp(Streamer, Reg);
|
||||
emitDwarfOpPiece(Streamer, PieceSizeInBits, PieceOffsetInBits);
|
||||
EmitDwarfOpPiece(Streamer, PieceSizeInBits, PieceOffsetInBits);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -266,19 +263,19 @@ void AsmPrinter::EmitDwarfRegOpPiece(ByteStreamer &Streamer,
|
||||
if (Reg >= 0) {
|
||||
unsigned Idx = TRI->getSubRegIndex(*SR, MLoc.getReg());
|
||||
unsigned Size = TRI->getSubRegIdxSize(Idx);
|
||||
unsigned Offset = TRI->getSubRegIdxOffset(Idx);
|
||||
unsigned RegOffset = TRI->getSubRegIdxOffset(Idx);
|
||||
OutStreamer.AddComment("super-register");
|
||||
emitDwarfRegOp(Streamer, Reg);
|
||||
if (PieceOffsetInBits == Offset) {
|
||||
emitDwarfOpPiece(Streamer, Size, Offset);
|
||||
if (PieceOffsetInBits == RegOffset) {
|
||||
EmitDwarfOpPiece(Streamer, Size, RegOffset);
|
||||
} else {
|
||||
// If this is part of a variable in a sub-register at a
|
||||
// non-zero offset, we need to manually shift the value into
|
||||
// place, since the DW_OP_piece describes the part of the
|
||||
// variable, not the position of the subregister.
|
||||
emitDwarfOpPiece(Streamer, Size, PieceOffsetInBits);
|
||||
if (Offset)
|
||||
emitDwarfOpShr(Streamer, Offset);
|
||||
if (RegOffset)
|
||||
emitDwarfOpShr(Streamer, RegOffset);
|
||||
EmitDwarfOpPiece(Streamer, Size, PieceOffsetInBits);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -312,7 +309,7 @@ void AsmPrinter::EmitDwarfRegOpPiece(ByteStreamer &Streamer,
|
||||
if (Reg >= 0 && Intersection.any()) {
|
||||
OutStreamer.AddComment("sub-register");
|
||||
emitDwarfRegOp(Streamer, Reg);
|
||||
emitDwarfOpPiece(Streamer, Size, Offset == CurPos ? 0 : Offset);
|
||||
EmitDwarfOpPiece(Streamer, Size, Offset == CurPos ? 0 : Offset);
|
||||
CurPos = Offset + Size;
|
||||
|
||||
// Mark it as emitted.
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/CodeGen/MachineBasicBlock.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Target/TargetRegisterInfo.h"
|
||||
#include <algorithm>
|
||||
@ -36,7 +37,7 @@ void DbgValueHistoryMap::startInstrRange(const MDNode *Var,
|
||||
const MachineInstr &MI) {
|
||||
// Instruction range should start with a DBG_VALUE instruction for the
|
||||
// variable.
|
||||
assert(MI.isDebugValue() && MI.getDebugVariable() == Var);
|
||||
assert(MI.isDebugValue() && getEntireVariable(MI.getDebugVariable()) == Var);
|
||||
auto &Ranges = VarInstrRanges[Var];
|
||||
if (!Ranges.empty() && Ranges.back().second == nullptr &&
|
||||
Ranges.back().first->isIdenticalTo(&MI)) {
|
||||
@ -182,7 +183,10 @@ void calculateDbgValueHistory(const MachineFunction *MF,
|
||||
}
|
||||
|
||||
assert(MI.getNumOperands() > 1 && "Invalid DBG_VALUE instruction!");
|
||||
const MDNode *Var = MI.getDebugVariable();
|
||||
// Use the base variable (without any DW_OP_piece expressions)
|
||||
// as index into History. The full variables including the
|
||||
// piece expressions are attached to the MI.
|
||||
DIVariable Var = getEntireVariable(MI.getDebugVariable());
|
||||
|
||||
if (unsigned PrevReg = Result.getRegisterForVar(Var))
|
||||
dropRegDescribedVar(RegVars, PrevReg, Var);
|
||||
|
@ -28,9 +28,11 @@ class DbgValueHistoryMap {
|
||||
// range. If end is not specified, location is valid until the start
|
||||
// instruction of the next instruction range, or until the end of the
|
||||
// function.
|
||||
public:
|
||||
typedef std::pair<const MachineInstr *, const MachineInstr *> InstrRange;
|
||||
typedef SmallVector<InstrRange, 4> InstrRanges;
|
||||
typedef MapVector<const MDNode *, InstrRanges> InstrRangesMap;
|
||||
private:
|
||||
InstrRangesMap VarInstrRanges;
|
||||
|
||||
public:
|
||||
|
@ -10,6 +10,7 @@
|
||||
#ifndef CODEGEN_ASMPRINTER_DEBUGLOCENTRY_H__
|
||||
#define CODEGEN_ASMPRINTER_DEBUGLOCENTRY_H__
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/MC/MachineLocation.h"
|
||||
#include "llvm/MC/MCSymbol.h"
|
||||
|
||||
@ -106,17 +107,36 @@ public:
|
||||
/// share the same Loc/Constant and if Next immediately follows this
|
||||
/// Entry.
|
||||
bool Merge(const DebugLocEntry &Next) {
|
||||
// If this and Next are describing different pieces of the same
|
||||
// variable, merge them by appending next's values to the current
|
||||
// list of values.
|
||||
if (Begin == Next.Begin && Values.size() > 0 && Next.Values.size() > 0) {
|
||||
DIVariable Var(Values[0].Variable);
|
||||
DIVariable NextVar(Next.Values[0].Variable);
|
||||
if (Var.getName() == NextVar.getName() &&
|
||||
Var.isVariablePiece() && NextVar.isVariablePiece()) {
|
||||
Values.append(Next.Values.begin(), Next.Values.end());
|
||||
End = Next.End;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// If this and Next are describing the same variable, merge them.
|
||||
if ((End == Next.Begin && Values == Next.Values)) {
|
||||
End = Next.End;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const MCSymbol *getBeginSym() const { return Begin; }
|
||||
const MCSymbol *getEndSym() const { return End; }
|
||||
const DwarfCompileUnit *getCU() const { return Unit; }
|
||||
const ArrayRef<Value> getValues() const { return Values; }
|
||||
void addValue(Value Val) { Values.push_back(Val); }
|
||||
void addValue(Value Val) {
|
||||
assert(DIVariable(Val.Variable).isVariablePiece() &&
|
||||
"multi-value DebugLocEntries must be pieces");
|
||||
Values.push_back(Val);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1194,6 +1194,101 @@ static DebugLocEntry::Value getDebugLocValue(const MachineInstr *MI) {
|
||||
llvm_unreachable("Unexpected 3 operand DBG_VALUE instruction!");
|
||||
}
|
||||
|
||||
/// Determine whether two variable pieces overlap.
|
||||
static bool piecesOverlap(DIVariable P1, DIVariable P2) {
|
||||
if (!P1.isVariablePiece() || !P2.isVariablePiece())
|
||||
return true;
|
||||
unsigned l1 = P1.getPieceOffset();
|
||||
unsigned l2 = P2.getPieceOffset();
|
||||
unsigned r1 = l1 + P1.getPieceSize();
|
||||
unsigned r2 = l2 + P2.getPieceSize();
|
||||
// True where [l1,r1[ and [r1,r2[ overlap.
|
||||
return (l1 < r2) && (l2 < r1);
|
||||
}
|
||||
|
||||
/// Build the location list for all DBG_VALUEs in the function that
|
||||
/// describe the same variable. If the ranges of several independent
|
||||
/// pieces of the same variable overlap partially, split them up and
|
||||
/// combine the ranges. The resulting DebugLocEntries are will have
|
||||
/// strict monotonically increasing begin addresses and will never
|
||||
/// overlap.
|
||||
//
|
||||
// Input:
|
||||
//
|
||||
// Ranges History [var, loc, piece ofs size]
|
||||
// 0 | [x, (reg0, piece 0, 32)]
|
||||
// 1 | | [x, (reg1, piece 32, 32)] <- IsPieceOfPrevEntry
|
||||
// 2 | | ...
|
||||
// 3 | [clobber reg0]
|
||||
// 4 [x, (mem, piece 0, 64)] <- overlapping with both previous pieces of x.
|
||||
//
|
||||
// Output:
|
||||
//
|
||||
// [0-1] [x, (reg0, piece 0, 32)]
|
||||
// [1-3] [x, (reg0, piece 0, 32), (reg1, piece 32, 32)]
|
||||
// [3-4] [x, (reg1, piece 32, 32)]
|
||||
// [4- ] [x, (mem, piece 0, 64)]
|
||||
void DwarfDebug::
|
||||
buildLocationList(SmallVectorImpl<DebugLocEntry> &DebugLoc,
|
||||
const DbgValueHistoryMap::InstrRanges &Ranges,
|
||||
DwarfCompileUnit *TheCU) {
|
||||
typedef std::pair<DIVariable, DebugLocEntry::Value> Range;
|
||||
SmallVector<Range, 4> OpenRanges;
|
||||
|
||||
for (auto I = Ranges.begin(), E = Ranges.end(); I != E; ++I) {
|
||||
const MachineInstr *Begin = I->first;
|
||||
const MachineInstr *End = I->second;
|
||||
assert(Begin->isDebugValue() && "Invalid History entry");
|
||||
|
||||
// Check if a variable is inaccessible in this range.
|
||||
if (!Begin->isDebugValue() ||
|
||||
(Begin->getNumOperands() > 1 && Begin->getOperand(0).isReg() &&
|
||||
!Begin->getOperand(0).getReg())) {
|
||||
OpenRanges.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this piece overlaps with any open ranges, truncate them.
|
||||
DIVariable DIVar = Begin->getDebugVariable();
|
||||
auto Last = std::remove_if(OpenRanges.begin(), OpenRanges.end(), [&](Range R){
|
||||
return piecesOverlap(DIVar, R.first);
|
||||
});
|
||||
OpenRanges.erase(Last, OpenRanges.end());
|
||||
|
||||
const MCSymbol *StartLabel = getLabelBeforeInsn(Begin);
|
||||
assert(StartLabel && "Forgot label before DBG_VALUE starting a range!");
|
||||
|
||||
const MCSymbol *EndLabel;
|
||||
if (End != nullptr)
|
||||
EndLabel = getLabelAfterInsn(End);
|
||||
else if (std::next(I) == Ranges.end())
|
||||
EndLabel = FunctionEndSym;
|
||||
else
|
||||
EndLabel = getLabelBeforeInsn(std::next(I)->first);
|
||||
assert(EndLabel && "Forgot label after instruction ending a range!");
|
||||
|
||||
DEBUG(dbgs() << "DotDebugLoc: " << *Begin << "\n");
|
||||
|
||||
auto Value = getDebugLocValue(Begin);
|
||||
DebugLocEntry Loc(StartLabel, EndLabel, Value, TheCU);
|
||||
if (DebugLoc.empty() || !DebugLoc.back().Merge(Loc)) {
|
||||
// Add all values from still valid non-overlapping pieces.
|
||||
for (auto Range : OpenRanges)
|
||||
Loc.addValue(Range.second);
|
||||
DebugLoc.push_back(std::move(Loc));
|
||||
}
|
||||
// Add this value to the list of open ranges.
|
||||
if (DIVar.isVariablePiece())
|
||||
OpenRanges.push_back({DIVar, Value});
|
||||
|
||||
DEBUG(dbgs() << "Values:\n";
|
||||
for (auto Value : DebugLoc.back().getValues())
|
||||
Value.getVariable()->dump();
|
||||
dbgs() << "-----\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Find variables for each lexical scope.
|
||||
void
|
||||
DwarfDebug::collectVariableInfo(SmallPtrSet<const MDNode *, 16> &Processed) {
|
||||
@ -1227,7 +1322,7 @@ DwarfDebug::collectVariableInfo(SmallPtrSet<const MDNode *, 16> &Processed) {
|
||||
if (!Scope)
|
||||
continue;
|
||||
|
||||
Processed.insert(DV);
|
||||
Processed.insert(getEntireVariable(DV));
|
||||
const MachineInstr *MInsn = Ranges.front().first;
|
||||
assert(MInsn->isDebugValue() && "History must begin with debug value");
|
||||
ensureAbstractVariableIsCreatedIfScoped(DV, Scope->getScopeNode());
|
||||
@ -1246,38 +1341,9 @@ DwarfDebug::collectVariableInfo(SmallPtrSet<const MDNode *, 16> &Processed) {
|
||||
DebugLocList &LocList = DotDebugLocEntries.back();
|
||||
LocList.Label =
|
||||
Asm->GetTempSymbol("debug_loc", DotDebugLocEntries.size() - 1);
|
||||
SmallVector<DebugLocEntry, 4> &DebugLoc = LocList.List;
|
||||
for (auto I = Ranges.begin(), E = Ranges.end(); I != E; ++I) {
|
||||
const MachineInstr *Begin = I->first;
|
||||
const MachineInstr *End = I->second;
|
||||
assert(Begin->isDebugValue() && "Invalid History entry");
|
||||
|
||||
// Check if a variable is unaccessible in this range.
|
||||
if (Begin->getNumOperands() > 1 && Begin->getOperand(0).isReg() &&
|
||||
!Begin->getOperand(0).getReg())
|
||||
continue;
|
||||
DEBUG(dbgs() << "DotDebugLoc Pair:\n" << "\t" << *Begin);
|
||||
if (End != nullptr)
|
||||
DEBUG(dbgs() << "\t" << *End);
|
||||
else
|
||||
DEBUG(dbgs() << "\tNULL\n");
|
||||
|
||||
const MCSymbol *StartLabel = getLabelBeforeInsn(Begin);
|
||||
assert(StartLabel && "Forgot label before DBG_VALUE starting a range!");
|
||||
|
||||
const MCSymbol *EndLabel;
|
||||
if (End != nullptr)
|
||||
EndLabel = getLabelAfterInsn(End);
|
||||
else if (std::next(I) == Ranges.end())
|
||||
EndLabel = FunctionEndSym;
|
||||
else
|
||||
EndLabel = getLabelBeforeInsn(std::next(I)->first);
|
||||
assert(EndLabel && "Forgot label after instruction ending a range!");
|
||||
|
||||
DebugLocEntry Loc(StartLabel, EndLabel, getDebugLocValue(Begin), TheCU);
|
||||
if (DebugLoc.empty() || !DebugLoc.back().Merge(Loc))
|
||||
DebugLoc.push_back(std::move(Loc));
|
||||
}
|
||||
// Build the location list for this variable.
|
||||
buildLocationList(LocList.List, Ranges, TheCU);
|
||||
}
|
||||
|
||||
// Collect info for variables that were optimized out.
|
||||
@ -1476,10 +1542,25 @@ void DwarfDebug::beginFunction(const MachineFunction *MF) {
|
||||
|
||||
// The first mention of a function argument gets the FunctionBeginSym
|
||||
// label, so arguments are visible when breaking at function entry.
|
||||
DIVariable DV(I.first);
|
||||
DIVariable DV(Ranges.front().first->getDebugVariable());
|
||||
if (DV.isVariable() && DV.getTag() == dwarf::DW_TAG_arg_variable &&
|
||||
getDISubprogram(DV.getContext()).describes(MF->getFunction()))
|
||||
LabelsBeforeInsn[Ranges.front().first] = FunctionBeginSym;
|
||||
getDISubprogram(DV.getContext()).describes(MF->getFunction())) {
|
||||
if (!DV.isVariablePiece())
|
||||
LabelsBeforeInsn[Ranges.front().first] = FunctionBeginSym;
|
||||
else {
|
||||
// Mark all non-overlapping initial pieces.
|
||||
for (auto I = Ranges.begin(); I != Ranges.end(); ++I) {
|
||||
DIVariable Piece = I->first->getDebugVariable();
|
||||
if (std::all_of(Ranges.begin(), I,
|
||||
[&](DbgValueHistoryMap::InstrRange Pred){
|
||||
return !piecesOverlap(Piece, Pred.first->getDebugVariable());
|
||||
}))
|
||||
LabelsBeforeInsn[I->first] = FunctionBeginSym;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &Range : Ranges) {
|
||||
requestLabelBeforeInsn(Range.first);
|
||||
@ -1962,12 +2043,79 @@ void DwarfDebug::emitDebugStr() {
|
||||
Holder.emitStrings(Asm->getObjFileLowering().getDwarfStrSection());
|
||||
}
|
||||
|
||||
/// Emits an optimal (=sorted) sequence of DW_OP_pieces.
|
||||
void DwarfDebug::emitLocPieces(ByteStreamer &Streamer,
|
||||
const DITypeIdentifierMap &Map,
|
||||
ArrayRef<DebugLocEntry::Value> Values) {
|
||||
typedef DebugLocEntry::Value Piece;
|
||||
SmallVector<Piece, 4> Pieces(Values.begin(), Values.end());
|
||||
assert(std::all_of(Pieces.begin(), Pieces.end(), [](Piece &P) {
|
||||
return DIVariable(P.getVariable()).isVariablePiece();
|
||||
}) && "all values are expected to be pieces");
|
||||
|
||||
// Sort the pieces so they can be emitted using DW_OP_piece.
|
||||
std::sort(Pieces.begin(), Pieces.end(), [](const Piece &A, const Piece &B) {
|
||||
DIVariable VarA(A.getVariable());
|
||||
DIVariable VarB(B.getVariable());
|
||||
return VarA.getPieceOffset() < VarB.getPieceOffset();
|
||||
});
|
||||
// Remove any duplicate entries by dropping all but the first.
|
||||
Pieces.erase(std::unique(Pieces.begin(), Pieces.end(),
|
||||
[] (const Piece &A,const Piece &B){
|
||||
return A.getVariable() == B.getVariable();
|
||||
}), Pieces.end());
|
||||
|
||||
unsigned Offset = 0;
|
||||
for (auto Piece : Pieces) {
|
||||
DIVariable Var(Piece.getVariable());
|
||||
unsigned PieceOffset = Var.getPieceOffset();
|
||||
unsigned PieceSize = Var.getPieceSize();
|
||||
assert(Offset <= PieceOffset && "overlapping pieces in DebugLocEntry");
|
||||
if (Offset < PieceOffset) {
|
||||
// The DWARF spec seriously mandates pieces with no locations for gaps.
|
||||
Asm->EmitDwarfOpPiece(Streamer, (PieceOffset-Offset)*8);
|
||||
Offset += PieceOffset-Offset;
|
||||
}
|
||||
|
||||
Offset += PieceSize;
|
||||
|
||||
const unsigned SizeOfByte = 8;
|
||||
assert(!Var.isIndirect() && "indirect address for piece");
|
||||
#ifndef NDEBUG
|
||||
unsigned VarSize = Var.getSizeInBits(Map);
|
||||
assert(PieceSize+PieceOffset <= VarSize/SizeOfByte
|
||||
&& "piece is larger than or outside of variable");
|
||||
assert(PieceSize*SizeOfByte != VarSize
|
||||
&& "piece covers entire variable");
|
||||
#endif
|
||||
if (Piece.isLocation() && Piece.getLoc().isReg())
|
||||
Asm->EmitDwarfRegOpPiece(Streamer,
|
||||
Piece.getLoc(),
|
||||
PieceSize*SizeOfByte);
|
||||
else {
|
||||
emitDebugLocValue(Streamer, Piece);
|
||||
Asm->EmitDwarfOpPiece(Streamer, PieceSize*SizeOfByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DwarfDebug::emitDebugLocEntry(ByteStreamer &Streamer,
|
||||
const DebugLocEntry &Entry) {
|
||||
assert(Entry.getValues().size() == 1 &&
|
||||
"multi-value entries are not supported yet.");
|
||||
const DebugLocEntry::Value Value = Entry.getValues()[0];
|
||||
DIVariable DV(Value.getVariable());
|
||||
if (DV.isVariablePiece())
|
||||
// Emit all pieces that belong to the same variable and range.
|
||||
return emitLocPieces(Streamer, TypeIdentifierMap, Entry.getValues());
|
||||
|
||||
assert(Entry.getValues().size() == 1 && "only pieces may have >1 value");
|
||||
emitDebugLocValue(Streamer, Value);
|
||||
}
|
||||
|
||||
void DwarfDebug::emitDebugLocValue(ByteStreamer &Streamer,
|
||||
const DebugLocEntry::Value &Value) {
|
||||
DIVariable DV(Value.getVariable());
|
||||
// Regular entry.
|
||||
if (Value.isInt()) {
|
||||
DIBasicType BTy(resolve(DV.getType()));
|
||||
if (BTy.Verify() && (BTy.getEncoding() == dwarf::DW_ATE_signed ||
|
||||
@ -2014,6 +2162,9 @@ void DwarfDebug::emitDebugLocEntry(ByteStreamer &Streamer,
|
||||
} else if (Element == DIBuilder::OpDeref) {
|
||||
if (!Loc.isReg())
|
||||
Streamer.EmitInt8(dwarf::DW_OP_deref, "DW_OP_deref");
|
||||
} else if (Element == DIBuilder::OpPiece) {
|
||||
i += 3;
|
||||
// handled in emitDebugLocEntry.
|
||||
} else
|
||||
llvm_unreachable("unknown Opcode found in complex address");
|
||||
}
|
||||
|
@ -535,6 +535,12 @@ class DwarfDebug : public AsmPrinterHandler {
|
||||
/// \brief Populate LexicalScope entries with variables' info.
|
||||
void collectVariableInfo(SmallPtrSet<const MDNode *, 16> &ProcessedVars);
|
||||
|
||||
/// \brief Build the location list for all DBG_VALUEs in the
|
||||
/// function that describe the same variable.
|
||||
void buildLocationList(SmallVectorImpl<DebugLocEntry> &DebugLoc,
|
||||
const DbgValueHistoryMap::InstrRanges &Ranges,
|
||||
DwarfCompileUnit *TheCU);
|
||||
|
||||
/// \brief Collect variable information from the side table maintained
|
||||
/// by MMI.
|
||||
void collectVariableInfoFromMMITable(SmallPtrSet<const MDNode *, 16> &P);
|
||||
@ -642,6 +648,13 @@ public:
|
||||
/// \brief Emit an entry for the debug loc section. This can be used to
|
||||
/// handle an entry that's going to be emitted into the debug loc section.
|
||||
void emitDebugLocEntry(ByteStreamer &Streamer, const DebugLocEntry &Entry);
|
||||
/// \brief emit a single value for the debug loc section.
|
||||
void emitDebugLocValue(ByteStreamer &Streamer,
|
||||
const DebugLocEntry::Value &Value);
|
||||
/// Emits an optimal (=sorted) sequence of DW_OP_pieces.
|
||||
void emitLocPieces(ByteStreamer &Streamer,
|
||||
const DITypeIdentifierMap &Map,
|
||||
ArrayRef<DebugLocEntry::Value> Values);
|
||||
|
||||
/// Emit the location for a debug loc entry, including the size header.
|
||||
void emitDebugLocEntryLocation(const DebugLocEntry &Entry);
|
||||
|
@ -491,7 +491,10 @@ void DwarfUnit::addVariableAddress(const DbgVariable &DV, DIE &Die,
|
||||
}
|
||||
|
||||
/// addRegisterOp - Add register operand.
|
||||
void DwarfUnit::addRegisterOp(DIELoc &TheDie, unsigned Reg) {
|
||||
// FIXME: Ideally, this would share the implementation with
|
||||
// AsmPrinter::EmitDwarfRegOpPiece.
|
||||
void DwarfUnit::addRegisterOpPiece(DIELoc &TheDie, unsigned Reg,
|
||||
unsigned SizeInBits, unsigned OffsetInBits) {
|
||||
const TargetRegisterInfo *RI = Asm->TM.getRegisterInfo();
|
||||
int DWReg = RI->getDwarfRegNum(Reg, false);
|
||||
bool isSubRegister = DWReg < 0;
|
||||
@ -511,7 +514,7 @@ void DwarfUnit::addRegisterOp(DIELoc &TheDie, unsigned Reg) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Emit register
|
||||
// Emit register.
|
||||
if (DWReg < 32)
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, dwarf::DW_OP_reg0 + DWReg);
|
||||
else {
|
||||
@ -519,18 +522,34 @@ void DwarfUnit::addRegisterOp(DIELoc &TheDie, unsigned Reg) {
|
||||
addUInt(TheDie, dwarf::DW_FORM_udata, DWReg);
|
||||
}
|
||||
|
||||
// Emit Mask
|
||||
if (isSubRegister) {
|
||||
unsigned Size = RI->getSubRegIdxSize(Idx);
|
||||
unsigned Offset = RI->getSubRegIdxOffset(Idx);
|
||||
if (Offset > 0) {
|
||||
// Emit mask.
|
||||
bool isPiece = SizeInBits > 0;
|
||||
if (isSubRegister || isPiece) {
|
||||
const unsigned SizeOfByte = 8;
|
||||
unsigned RegSizeInBits = RI->getSubRegIdxSize(Idx);
|
||||
unsigned RegOffsetInBits = RI->getSubRegIdxOffset(Idx);
|
||||
unsigned PieceSizeInBits = std::max(SizeInBits, RegSizeInBits);
|
||||
unsigned PieceOffsetInBits = OffsetInBits ? OffsetInBits : RegOffsetInBits;
|
||||
assert(RegSizeInBits >= SizeInBits && "register smaller than value");
|
||||
|
||||
if (RegOffsetInBits != PieceOffsetInBits) {
|
||||
// Manually shift the value into place, since the DW_OP_piece
|
||||
// describes the part of the variable, not the position of the
|
||||
// subregister.
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, dwarf::DW_OP_constu);
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, RegOffsetInBits);
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, dwarf::DW_OP_shr);
|
||||
}
|
||||
|
||||
if (PieceOffsetInBits > 0 || PieceSizeInBits % SizeOfByte) {
|
||||
assert(PieceSizeInBits > 0 && "piece has zero size");
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, dwarf::DW_OP_bit_piece);
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, Size);
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, Offset);
|
||||
} else {
|
||||
unsigned ByteSize = Size / 8; // Assuming 8 bits per byte.
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, PieceSizeInBits);
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, PieceOffsetInBits);
|
||||
} else {
|
||||
assert(PieceSizeInBits > 0 && "piece has zero size");
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, dwarf::DW_OP_piece);
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, ByteSize);
|
||||
addUInt(TheDie, dwarf::DW_FORM_data1, PieceSizeInBits/SizeOfByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -560,7 +579,7 @@ void DwarfUnit::addAddress(DIE &Die, dwarf::Attribute Attribute,
|
||||
DIELoc *Loc = new (DIEValueAllocator) DIELoc();
|
||||
|
||||
if (Location.isReg() && !Indirect)
|
||||
addRegisterOp(*Loc, Location.getReg());
|
||||
addRegisterOpPiece(*Loc, Location.getReg());
|
||||
else {
|
||||
addRegisterOffset(*Loc, Location.getReg(), Location.getOffset());
|
||||
if (Indirect && !Location.isReg()) {
|
||||
@ -589,8 +608,13 @@ void DwarfUnit::addComplexAddress(const DbgVariable &DV, DIE &Die,
|
||||
// DW_OP_breg + Offset instead of DW_OP_reg + Offset.
|
||||
addRegisterOffset(*Loc, Location.getReg(), DV.getAddrElement(1));
|
||||
i = 2;
|
||||
} else if (N >= 2 && DV.getAddrElement(0) == DIBuilder::OpDeref) {
|
||||
addRegisterOpPiece(*Loc, Location.getReg(),
|
||||
DV.getVariable().getPieceSize(),
|
||||
DV.getVariable().getPieceOffset());
|
||||
i = 3;
|
||||
} else
|
||||
addRegisterOp(*Loc, Location.getReg());
|
||||
addRegisterOpPiece(*Loc, Location.getReg());
|
||||
} else
|
||||
addRegisterOffset(*Loc, Location.getReg(), Location.getOffset());
|
||||
|
||||
@ -599,9 +623,21 @@ void DwarfUnit::addComplexAddress(const DbgVariable &DV, DIE &Die,
|
||||
if (Element == DIBuilder::OpPlus) {
|
||||
addUInt(*Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_plus_uconst);
|
||||
addUInt(*Loc, dwarf::DW_FORM_udata, DV.getAddrElement(++i));
|
||||
|
||||
} else if (Element == DIBuilder::OpDeref) {
|
||||
if (!Location.isReg())
|
||||
addUInt(*Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_deref);
|
||||
|
||||
} else if (Element == DIBuilder::OpPiece) {
|
||||
const unsigned SizeOfByte = 8;
|
||||
unsigned PieceOffsetInBits = DV.getAddrElement(++i)*SizeOfByte;
|
||||
unsigned PieceSizeInBits = DV.getAddrElement(++i)*SizeOfByte;
|
||||
// Emit DW_OP_bit_piece Size Offset.
|
||||
assert(PieceSizeInBits > 0 && "piece has zero size");
|
||||
addUInt(*Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_bit_piece);
|
||||
addUInt(*Loc, dwarf::DW_FORM_udata, PieceSizeInBits);
|
||||
addUInt(*Loc, dwarf::DW_FORM_udata, PieceOffsetInBits);
|
||||
|
||||
} else
|
||||
llvm_unreachable("unknown DIBuilder Opcode");
|
||||
}
|
||||
@ -712,7 +748,7 @@ void DwarfUnit::addBlockByrefAddress(const DbgVariable &DV, DIE &Die,
|
||||
DIELoc *Loc = new (DIEValueAllocator) DIELoc();
|
||||
|
||||
if (Location.isReg())
|
||||
addRegisterOp(*Loc, Location.getReg());
|
||||
addRegisterOpPiece(*Loc, Location.getReg());
|
||||
else
|
||||
addRegisterOffset(*Loc, Location.getReg(), Location.getOffset());
|
||||
|
||||
|
@ -359,7 +359,8 @@ public:
|
||||
void addTemplateParams(DIE &Buffer, DIArray TParams);
|
||||
|
||||
/// addRegisterOp - Add register operand.
|
||||
void addRegisterOp(DIELoc &TheDie, unsigned Reg);
|
||||
void addRegisterOpPiece(DIELoc &TheDie, unsigned Reg,
|
||||
unsigned SizeInBits = 0, unsigned OffsetInBits = 0);
|
||||
|
||||
/// addRegisterOffset - Add register offset.
|
||||
void addRegisterOffset(DIELoc &TheDie, unsigned Reg, int64_t Offset);
|
||||
|
@ -1100,6 +1100,28 @@ DIVariable DIBuilder::createComplexVariable(unsigned Tag, DIDescriptor Scope,
|
||||
return DIVariable(MDNode::get(VMContext, Elts));
|
||||
}
|
||||
|
||||
/// createVariablePiece - Create a descriptor to describe one part
|
||||
/// of aggregate variable that is fragmented across multiple Values.
|
||||
DIVariable DIBuilder::createVariablePiece(DIVariable Variable,
|
||||
unsigned OffsetInBytes,
|
||||
unsigned SizeInBytes) {
|
||||
assert(SizeInBytes > 0 && "zero-size piece");
|
||||
Value *Addr[] = {
|
||||
ConstantInt::get(Type::getInt32Ty(VMContext), OpPiece),
|
||||
ConstantInt::get(Type::getInt32Ty(VMContext), OffsetInBytes),
|
||||
ConstantInt::get(Type::getInt32Ty(VMContext), SizeInBytes)
|
||||
};
|
||||
|
||||
assert((Variable->getNumOperands() == 8 || Variable.isVariablePiece()) &&
|
||||
"variable already has a complex address");
|
||||
SmallVector<Value *, 9> Elts;
|
||||
for (unsigned i = 0; i < 8; ++i)
|
||||
Elts.push_back(Variable->getOperand(i));
|
||||
|
||||
Elts.push_back(MDNode::get(VMContext, Addr));
|
||||
return DIVariable(MDNode::get(VMContext, Elts));
|
||||
}
|
||||
|
||||
/// createFunction - Create a new descriptor for the specified function.
|
||||
/// FIXME: this is added for dragonegg. Once we update dragonegg
|
||||
/// to call resolve function, this will be removed.
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DIBuilder.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
@ -150,6 +151,36 @@ uint64_t DIVariable::getAddrElement(unsigned Idx) const {
|
||||
/// getInlinedAt - If this variable is inlined then return inline location.
|
||||
MDNode *DIVariable::getInlinedAt() const { return getNodeField(DbgNode, 7); }
|
||||
|
||||
bool DIVariable::isVariablePiece() const {
|
||||
return hasComplexAddress() && getAddrElement(0) == DIBuilder::OpPiece;
|
||||
}
|
||||
|
||||
uint64_t DIVariable::getPieceOffset() const {
|
||||
assert(isVariablePiece());
|
||||
return getAddrElement(1);
|
||||
}
|
||||
|
||||
uint64_t DIVariable::getPieceSize() const {
|
||||
assert(isVariablePiece());
|
||||
return getAddrElement(2);
|
||||
}
|
||||
|
||||
/// Return the size reported by the variable's type.
|
||||
unsigned DIVariable::getSizeInBits(const DITypeIdentifierMap &Map) {
|
||||
DIType Ty = getType().resolve(Map);
|
||||
// Follow derived types until we reach a type that
|
||||
// reports back a size.
|
||||
while (Ty.isDerivedType() && !Ty.getSizeInBits()) {
|
||||
DIDerivedType DT(&*Ty);
|
||||
Ty = DT.getTypeDerivedFrom().resolve(Map);
|
||||
}
|
||||
assert(Ty.getSizeInBits() && "type with size 0");
|
||||
return Ty.getSizeInBits();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Predicates
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -904,6 +935,19 @@ DIVariable llvm::cleanseInlinedVariable(MDNode *DV, LLVMContext &VMContext) {
|
||||
return DIVariable(MDNode::get(VMContext, Elts));
|
||||
}
|
||||
|
||||
|
||||
/// getEntireVariable - Remove OpPiece exprs from the variable.
|
||||
DIVariable llvm::getEntireVariable(DIVariable DV) {
|
||||
if (!DV.isVariablePiece())
|
||||
return DV;
|
||||
|
||||
SmallVector<Value *, 8> Elts;
|
||||
for (unsigned i = 0; i < 8; ++i)
|
||||
Elts.push_back(DV->getOperand(i));
|
||||
|
||||
return DIVariable(MDNode::get(DV->getContext(), Elts));
|
||||
}
|
||||
|
||||
/// getDISubprogram - Find subprogram that is enclosing this scope.
|
||||
DISubprogram llvm::getDISubprogram(const MDNode *Scope) {
|
||||
DIDescriptor D(Scope);
|
||||
@ -1393,6 +1437,10 @@ void DIVariable::printInternal(raw_ostream &OS) const {
|
||||
OS << " [" << Res << ']';
|
||||
|
||||
OS << " [line " << getLineNumber() << ']';
|
||||
|
||||
if (isVariablePiece())
|
||||
OS << " [piece, size " << getPieceSize()
|
||||
<< ", offset " << getPieceOffset() << ']';
|
||||
}
|
||||
|
||||
void DIObjCProperty::printInternal(raw_ostream &OS) const {
|
||||
|
76
test/DebugInfo/X86/pieces-1.ll
Normal file
76
test/DebugInfo/X86/pieces-1.ll
Normal file
@ -0,0 +1,76 @@
|
||||
; RUN: llc -O0 %s -filetype=obj -o %t.o
|
||||
; RUN: llvm-dwarfdump -debug-dump=loc %t.o | FileCheck --check-prefix=CHECK-DWARF %s
|
||||
;
|
||||
; rdar://problem/15928306
|
||||
;
|
||||
; Test that we can emit debug info for aggregate values that are split
|
||||
; up across multiple registers by SROA.
|
||||
;
|
||||
; // Compile with -O1.
|
||||
; typedef struct { long int a; int b;} S;
|
||||
;
|
||||
; int foo(S s) {
|
||||
; return s.b;
|
||||
; }
|
||||
;
|
||||
;
|
||||
; CHECK-DWARF: .debug_loc contents:
|
||||
;
|
||||
|
||||
; 0x0000000000000000 - 0x0000000000000006: rdi, piece 0x00000008, rsi, piece 0x00000004
|
||||
; CHECK-DWARF: Beginning address offset: 0x0000000000000000
|
||||
; CHECK-DWARF: Ending address offset: [[LTMP3:.*]]
|
||||
; CHECK-DWARF: Location description: 55 93 08 54 93 04
|
||||
; 0x0000000000000006 - 0x0000000000000008: rbp-8, piece 0x00000008, rax, piece 0x00000004 )
|
||||
; CHECK-DWARF: Beginning address offset: [[LTMP3]]
|
||||
; CHECK-DWARF: Ending address offset: [[END:.*]]
|
||||
; CHECK-DWARF: Location description: 76 78 93 08 54 93 04
|
||||
|
||||
; Function Attrs: nounwind ssp uwtable
|
||||
define i32 @foo(i64 %s.coerce0, i32 %s.coerce1) #0 {
|
||||
entry:
|
||||
call void @llvm.dbg.value(metadata !{i64 %s.coerce0}, i64 0, metadata !20), !dbg !21
|
||||
call void @llvm.dbg.value(metadata !{i32 %s.coerce1}, i64 0, metadata !22), !dbg !21
|
||||
ret i32 %s.coerce1, !dbg !23
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare void @llvm.dbg.declare(metadata, metadata) #1
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare void @llvm.dbg.value(metadata, i64, metadata) #1
|
||||
|
||||
attributes #0 = { nounwind ssp uwtable "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" }
|
||||
attributes #1 = { nounwind readnone }
|
||||
|
||||
!llvm.dbg.cu = !{!0}
|
||||
!llvm.module.flags = !{!17, !18}
|
||||
!llvm.ident = !{!19}
|
||||
|
||||
!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5 ", i1 true, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !""}
|
||||
!1 = metadata !{metadata !"pieces.c", metadata !""}
|
||||
!2 = metadata !{}
|
||||
!3 = metadata !{metadata !4}
|
||||
!4 = metadata !{i32 786478, metadata !1, metadata !5, metadata !"foo", metadata !"foo", metadata !"", i32 3, metadata !6, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 true, i32 (i64, i32)* @foo, null, null, metadata !15, i32 3} ; [ DW_TAG_subprogram ] [line 3] [def] [foo]
|
||||
!5 = metadata !{i32 786473, metadata !1} ; [ DW_TAG_file_type ] [/pieces.c]
|
||||
!6 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !7, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ]
|
||||
!7 = metadata !{metadata !8, metadata !9}
|
||||
!8 = metadata !{i32 786468, null, null, metadata !"int", i32 0, i64 32, i64 32, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] [int] [line 0, size 32, align 32, offset 0, enc DW_ATE_signed]
|
||||
!9 = metadata !{i32 786454, metadata !1, null, metadata !"S", i32 1, i64 0, i64 0, i64 0, i32 0, metadata !10} ; [ DW_TAG_typedef ] [S] [line 1, size 0, align 0, offset 0] [from ]
|
||||
!10 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 1, i64 128, i64 64, i32 0, i32 0, null, metadata !11, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 1, size 128, align 64, offset 0] [def] [from ]
|
||||
!11 = metadata !{metadata !12, metadata !14}
|
||||
!12 = metadata !{i32 786445, metadata !1, metadata !10, metadata !"a", i32 1, i64 64, i64 64, i64 0, i32 0, metadata !13} ; [ DW_TAG_member ] [a] [line 1, size 64, align 64, offset 0] [from long int]
|
||||
!13 = metadata !{i32 786468, null, null, metadata !"long int", i32 0, i64 64, i64 64, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] [long int] [line 0, size 64, align 64, offset 0, enc DW_ATE_signed]
|
||||
!14 = metadata !{i32 786445, metadata !1, metadata !10, metadata !"b", i32 1, i64 32, i64 32, i64 64, i32 0, metadata !8} ; [ DW_TAG_member ] [b] [line 1, size 32, align 32, offset 64] [from int]
|
||||
!15 = metadata !{metadata !16}
|
||||
!16 = metadata !{i32 786689, metadata !4, metadata !"s", metadata !5, i32 16777219, metadata !9, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [s] [line 3]
|
||||
!17 = metadata !{i32 2, metadata !"Dwarf Version", i32 4}
|
||||
!18 = metadata !{i32 1, metadata !"Debug Info Version", i32 1}
|
||||
!19 = metadata !{metadata !"clang version 3.5 "}
|
||||
!20 = metadata !{i32 786689, metadata !4, metadata !"s", metadata !5, i32 16777219, metadata !9, i32 0, i32 0, metadata !24} ; [ DW_TAG_arg_variable ] [s] [line 3] [piece, size 8, offset 0]
|
||||
!21 = metadata !{i32 3, i32 0, metadata !4, null}
|
||||
!22 = metadata !{i32 786689, metadata !4, metadata !"s", metadata !5, i32 16777219, metadata !9, i32 0, i32 0, metadata !27} ; [ DW_TAG_arg_variable ] [s] [line 3] [piece, size 4, offset 8]
|
||||
!23 = metadata !{i32 4, i32 0, metadata !4, null}
|
||||
!24 = metadata !{i64 3, i64 0, i64 8}
|
||||
!25 = metadata !{}
|
||||
!27 = metadata !{i64 3, i64 8, i64 4}
|
92
test/DebugInfo/X86/pieces-2.ll
Normal file
92
test/DebugInfo/X86/pieces-2.ll
Normal file
@ -0,0 +1,92 @@
|
||||
; RUN: llc %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s
|
||||
;
|
||||
; // Compile with -O1
|
||||
; typedef struct {
|
||||
; int a;
|
||||
; long int b;
|
||||
; } Inner;
|
||||
;
|
||||
; typedef struct {
|
||||
; Inner inner[2];
|
||||
; } Outer;
|
||||
;
|
||||
; int foo(Outer outer) {
|
||||
; Inner i1 = outer.inner[1];
|
||||
; return i1.a;
|
||||
; }
|
||||
;
|
||||
;
|
||||
; CHECK: DW_TAG_variable [4]
|
||||
; rax, piece 0x00000004
|
||||
; CHECK-NEXT: DW_AT_location [DW_FORM_block1]{{.*}}50 93 04
|
||||
; CHECK-NEXT: DW_AT_name {{.*}}"i1"
|
||||
;
|
||||
; ModuleID = '/Volumes/Data/llvm/test/DebugInfo/X86/sroasplit-1.ll'
|
||||
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-apple-macosx10.9.0"
|
||||
|
||||
%struct.Outer = type { [2 x %struct.Inner] }
|
||||
%struct.Inner = type { i32, i64 }
|
||||
|
||||
; Function Attrs: nounwind ssp uwtable
|
||||
define i32 @foo(%struct.Outer* byval align 8 %outer) #0 {
|
||||
entry:
|
||||
call void @llvm.dbg.declare(metadata !{%struct.Outer* %outer}, metadata !25), !dbg !26
|
||||
%i1.sroa.0.0..sroa_idx = getelementptr inbounds %struct.Outer* %outer, i64 0, i32 0, i64 1, i32 0, !dbg !27
|
||||
%i1.sroa.0.0.copyload = load i32* %i1.sroa.0.0..sroa_idx, align 8, !dbg !27
|
||||
call void @llvm.dbg.value(metadata !{i32 %i1.sroa.0.0.copyload}, i64 0, metadata !28), !dbg !27
|
||||
%i1.sroa.2.0..sroa_raw_cast = bitcast %struct.Outer* %outer to i8*, !dbg !27
|
||||
%i1.sroa.2.0..sroa_raw_idx = getelementptr inbounds i8* %i1.sroa.2.0..sroa_raw_cast, i64 20, !dbg !27
|
||||
ret i32 %i1.sroa.0.0.copyload, !dbg !32
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare void @llvm.dbg.declare(metadata, metadata) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1) #2
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare void @llvm.dbg.value(metadata, i64, metadata) #1
|
||||
|
||||
attributes #0 = { nounwind ssp uwtable }
|
||||
attributes #1 = { nounwind readnone }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
!llvm.dbg.cu = !{!0}
|
||||
!llvm.module.flags = !{!22, !23}
|
||||
!llvm.ident = !{!24}
|
||||
|
||||
!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5.0 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !"", i32 1} ; [ DW_TAG_compile_unit ] [/sroasplit-1.c] [DW_LANG_C99]
|
||||
!1 = metadata !{metadata !"sroasplit-1.c", metadata !""}
|
||||
!2 = metadata !{}
|
||||
!3 = metadata !{metadata !4}
|
||||
!4 = metadata !{i32 786478, metadata !1, metadata !5, metadata !"foo", metadata !"foo", metadata !"", i32 10, metadata !6, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 (%struct.Outer*)* @foo, null, null, metadata !2, i32 10} ; [ DW_TAG_subprogram ] [line 10] [def] [foo]
|
||||
!5 = metadata !{i32 786473, metadata !1} ; [ DW_TAG_file_type ] [/sroasplit-1.c]
|
||||
!6 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !7, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ]
|
||||
!7 = metadata !{metadata !8, metadata !9}
|
||||
!8 = metadata !{i32 786468, null, null, metadata !"int", i32 0, i64 32, i64 32, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] [int] [line 0, size 32, align 32, offset 0, enc DW_ATE_signed]
|
||||
!9 = metadata !{i32 786454, metadata !1, null, metadata !"Outer", i32 8, i64 0, i64 0, i64 0, i32 0, metadata !10} ; [ DW_TAG_typedef ] [Outer] [line 8, size 0, align 0, offset 0] [from ]
|
||||
!10 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 6, i64 256, i64 64, i32 0, i32 0, null, metadata !11, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 6, size 256, align 64, offset 0] [def] [from ]
|
||||
!11 = metadata !{metadata !12}
|
||||
!12 = metadata !{i32 786445, metadata !1, metadata !10, metadata !"inner", i32 7, i64 256, i64 64, i64 0, i32 0, metadata !13} ; [ DW_TAG_member ] [inner] [line 7, size 256, align 64, offset 0] [from ]
|
||||
!13 = metadata !{i32 786433, null, null, metadata !"", i32 0, i64 256, i64 64, i32 0, i32 0, metadata !14, metadata !20, i32 0, null, null, null} ; [ DW_TAG_array_type ] [line 0, size 256, align 64, offset 0] [from Inner]
|
||||
!14 = metadata !{i32 786454, metadata !1, null, metadata !"Inner", i32 4, i64 0, i64 0, i64 0, i32 0, metadata !15} ; [ DW_TAG_typedef ] [Inner] [line 4, size 0, align 0, offset 0] [from ]
|
||||
!15 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 1, i64 128, i64 64, i32 0, i32 0, null, metadata !16, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 1, size 128, align 64, offset 0] [def] [from ]
|
||||
!16 = metadata !{metadata !17, metadata !18}
|
||||
!17 = metadata !{i32 786445, metadata !1, metadata !15, metadata !"a", i32 2, i64 32, i64 32, i64 0, i32 0, metadata !8} ; [ DW_TAG_member ] [a] [line 2, size 32, align 32, offset 0] [from int]
|
||||
!18 = metadata !{i32 786445, metadata !1, metadata !15, metadata !"b", i32 3, i64 64, i64 64, i64 64, i32 0, metadata !19} ; [ DW_TAG_member ] [b] [line 3, size 64, align 64, offset 64] [from long int]
|
||||
!19 = metadata !{i32 786468, null, null, metadata !"long int", i32 0, i64 64, i64 64, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] [long int] [line 0, size 64, align 64, offset 0, enc DW_ATE_signed]
|
||||
!20 = metadata !{metadata !21}
|
||||
!21 = metadata !{i32 786465, i64 0, i64 2} ; [ DW_TAG_subrange_type ] [0, 1]
|
||||
!22 = metadata !{i32 2, metadata !"Dwarf Version", i32 2}
|
||||
!23 = metadata !{i32 1, metadata !"Debug Info Version", i32 1}
|
||||
!24 = metadata !{metadata !"clang version 3.5.0 "}
|
||||
!25 = metadata !{i32 786689, metadata !4, metadata !"outer", metadata !5, i32 16777226, metadata !9, i32 0, i32 0} ; [ DW_TAG_arg_variable ] [outer] [line 10]
|
||||
!26 = metadata !{i32 10, i32 0, metadata !4, null}
|
||||
!27 = metadata !{i32 11, i32 0, metadata !4, null}
|
||||
!28 = metadata !{i32 786688, metadata !4, metadata !"i1", metadata !5, i32 11, metadata !14, i32 0, i32 0, metadata !29} ; [ DW_TAG_auto_variable ] [i1] [line 11] [piece, size 4, offset 0]
|
||||
!29 = metadata !{i32 3, i32 0, i32 4}
|
||||
!30 = metadata !{i32 786688, metadata !4, metadata !"i1", metadata !5, i32 11, metadata !14, i32 0, i32 0, metadata !31} ; [ DW_TAG_auto_variable ] [i1] [line 11] [piece, size 12, offset 0]
|
||||
!31 = metadata !{i32 3, i32 0, i32 12}
|
||||
!32 = metadata !{i32 12, i32 0, metadata !4, null}
|
105
test/DebugInfo/X86/pieces-3.ll
Normal file
105
test/DebugInfo/X86/pieces-3.ll
Normal file
@ -0,0 +1,105 @@
|
||||
; RUN: llc %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s
|
||||
;
|
||||
; // Compile with -O1
|
||||
; typedef struct {
|
||||
; int a;
|
||||
; int b;
|
||||
; } Inner;
|
||||
;
|
||||
; typedef struct {
|
||||
; Inner inner[2];
|
||||
; } Outer;
|
||||
;
|
||||
; int foo(Outer outer) {
|
||||
; Inner i1 = outer.inner[1];
|
||||
; return i1.a;
|
||||
; }
|
||||
;
|
||||
; CHECK: DW_TAG_formal_parameter [3]
|
||||
; CHECK-NEXT: DW_AT_location [DW_FORM_data4] ([[LOC:.*]])
|
||||
; CHECK-NEXT: DW_AT_name {{.*}}"outer"
|
||||
; CHECK: DW_TAG_variable
|
||||
; rsi, piece 0x00000004, bit-piece 32 0
|
||||
; CHECK-NEXT: DW_AT_location [DW_FORM_block1] (<0x06> 54 93 04 9d 20 00 )
|
||||
; CHECK-NEXT: "i1"
|
||||
|
||||
; CHECK: .debug_loc
|
||||
; CHECK: [[LOC]]:
|
||||
; CHECK: Beginning address offset: 0x0000000000000000
|
||||
; CHECK: Ending address offset: 0x0000000000000004
|
||||
; rdi, piece 0x00000008, piece 0x00000004, rsi, piece 0x00000004
|
||||
; CHECK: Location description: 55 93 08 93 04 54 93 04
|
||||
;
|
||||
; ModuleID = '/Volumes/Data/llvm/test/DebugInfo/X86/sroasplit-2.ll'
|
||||
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-apple-macosx10.9.0"
|
||||
|
||||
; Function Attrs: nounwind ssp uwtable
|
||||
define i32 @foo(i64 %outer.coerce0, i64 %outer.coerce1) #0 {
|
||||
call void @llvm.dbg.value(metadata !{i64 %outer.coerce0}, i64 0, metadata !24), !dbg !26
|
||||
call void @llvm.dbg.declare(metadata !{null}, metadata !27), !dbg !26
|
||||
call void @llvm.dbg.value(metadata !{i64 %outer.coerce1}, i64 0, metadata !29), !dbg !26
|
||||
call void @llvm.dbg.declare(metadata !{null}, metadata !31), !dbg !26
|
||||
%outer.sroa.1.8.extract.trunc = trunc i64 %outer.coerce1 to i32, !dbg !33
|
||||
call void @llvm.dbg.value(metadata !{i32 %outer.sroa.1.8.extract.trunc}, i64 0, metadata !34), !dbg !33
|
||||
%outer.sroa.1.12.extract.shift = lshr i64 %outer.coerce1, 32, !dbg !33
|
||||
%outer.sroa.1.12.extract.trunc = trunc i64 %outer.sroa.1.12.extract.shift to i32, !dbg !33
|
||||
call void @llvm.dbg.value(metadata !{i32 %outer.sroa.1.12.extract.trunc}, i64 0, metadata !34), !dbg !33
|
||||
call void @llvm.dbg.declare(metadata !{null}, metadata !34), !dbg !33
|
||||
ret i32 %outer.sroa.1.8.extract.trunc, !dbg !36
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare void @llvm.dbg.declare(metadata, metadata) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1) #2
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare void @llvm.dbg.value(metadata, i64, metadata) #1
|
||||
|
||||
attributes #0 = { nounwind ssp uwtable "no-frame-pointer-elim"="true" }
|
||||
attributes #1 = { nounwind readnone }
|
||||
attributes #2 = { nounwind }
|
||||
|
||||
!llvm.dbg.cu = !{!0}
|
||||
!llvm.module.flags = !{!21, !22}
|
||||
!llvm.ident = !{!23}
|
||||
|
||||
!0 = metadata !{i32 786449, metadata !1, i32 12, metadata !"clang version 3.5.0 ", i1 false, metadata !"", i32 0, metadata !2, metadata !2, metadata !3, metadata !2, metadata !2, metadata !"", i32 1} ; [ DW_TAG_compile_unit ] [/sroasplit-2.c] [DW_LANG_C99]
|
||||
!1 = metadata !{metadata !"sroasplit-2.c", metadata !""}
|
||||
!2 = metadata !{}
|
||||
!3 = metadata !{metadata !4}
|
||||
!4 = metadata !{i32 786478, metadata !1, metadata !5, metadata !"foo", metadata !"foo", metadata !"", i32 10, metadata !6, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 (i64, i64)* @foo, null, null, metadata !2, i32 10} ; [ DW_TAG_subprogram ] [line 10] [def] [foo]
|
||||
!5 = metadata !{i32 786473, metadata !1} ; [ DW_TAG_file_type ] [/sroasplit-2.c]
|
||||
!6 = metadata !{i32 786453, i32 0, null, metadata !"", i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !7, i32 0, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ]
|
||||
!7 = metadata !{metadata !8, metadata !9}
|
||||
!8 = metadata !{i32 786468, null, null, metadata !"int", i32 0, i64 32, i64 32, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] [int] [line 0, size 32, align 32, offset 0, enc DW_ATE_signed]
|
||||
!9 = metadata !{i32 786454, metadata !1, null, metadata !"Outer", i32 8, i64 0, i64 0, i64 0, i32 0, metadata !10} ; [ DW_TAG_typedef ] [Outer] [line 8, size 0, align 0, offset 0] [from ]
|
||||
!10 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 6, i64 128, i64 32, i32 0, i32 0, null, metadata !11, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 6, size 128, align 32, offset 0] [def] [from ]
|
||||
!11 = metadata !{metadata !12}
|
||||
!12 = metadata !{i32 786445, metadata !1, metadata !10, metadata !"inner", i32 7, i64 128, i64 32, i64 0, i32 0, metadata !13} ; [ DW_TAG_member ] [inner] [line 7, size 128, align 32, offset 0] [from ]
|
||||
!13 = metadata !{i32 786433, null, null, metadata !"", i32 0, i64 128, i64 32, i32 0, i32 0, metadata !14, metadata !19, i32 0, null, null, null} ; [ DW_TAG_array_type ] [line 0, size 128, align 32, offset 0] [from Inner]
|
||||
!14 = metadata !{i32 786454, metadata !1, null, metadata !"Inner", i32 4, i64 0, i64 0, i64 0, i32 0, metadata !15} ; [ DW_TAG_typedef ] [Inner] [line 4, size 0, align 0, offset 0] [from ]
|
||||
!15 = metadata !{i32 786451, metadata !1, null, metadata !"", i32 1, i64 64, i64 32, i32 0, i32 0, null, metadata !16, i32 0, null, null, null} ; [ DW_TAG_structure_type ] [line 1, size 64, align 32, offset 0] [def] [from ]
|
||||
!16 = metadata !{metadata !17, metadata !18}
|
||||
!17 = metadata !{i32 786445, metadata !1, metadata !15, metadata !"a", i32 2, i64 32, i64 32, i64 0, i32 0, metadata !8} ; [ DW_TAG_member ] [a] [line 2, size 32, align 32, offset 0] [from int]
|
||||
!18 = metadata !{i32 786445, metadata !1, metadata !15, metadata !"b", i32 3, i64 32, i64 32, i64 32, i32 0, metadata !8} ; [ DW_TAG_member ] [b] [line 3, size 32, align 32, offset 32] [from int]
|
||||
!19 = metadata !{metadata !20}
|
||||
!20 = metadata !{i32 786465, i64 0, i64 2} ; [ DW_TAG_subrange_type ] [0, 1]
|
||||
!21 = metadata !{i32 2, metadata !"Dwarf Version", i32 2}
|
||||
!22 = metadata !{i32 1, metadata !"Debug Info Version", i32 1}
|
||||
!23 = metadata !{metadata !"clang version 3.5.0 "}
|
||||
!24 = metadata !{i32 786689, metadata !4, metadata !"outer", metadata !5, i32 16777226, metadata !9, i32 0, i32 0, metadata !25} ; [ DW_TAG_arg_variable ] [outer] [line 10] [piece, size 8, offset 0]
|
||||
!25 = metadata !{i32 3, i32 0, i32 8}
|
||||
!26 = metadata !{i32 10, i32 0, metadata !4, null}
|
||||
!27 = metadata !{i32 786689, metadata !4, metadata !"outer", metadata !5, i32 16777226, metadata !9, i32 0, i32 0, metadata !28} ; [ DW_TAG_arg_variable ] [outer] [line 10] [piece, size 8, offset 8]
|
||||
!28 = metadata !{i32 3, i32 8, i32 8}
|
||||
!29 = metadata !{i32 786689, metadata !4, metadata !"outer", metadata !5, i32 16777226, metadata !9, i32 0, i32 0, metadata !30} ; [ DW_TAG_arg_variable ] [outer] [line 10] [piece, size 4, offset 12]
|
||||
!30 = metadata !{i32 3, i32 12, i32 4}
|
||||
!31 = metadata !{i32 786689, metadata !4, metadata !"outer", metadata !5, i32 16777226, metadata !9, i32 0, i32 0, metadata !32} ; [ DW_TAG_arg_variable ] [outer] [line 10] [piece, size 4, offset 8]
|
||||
!32 = metadata !{i32 3, i32 8, i32 4}
|
||||
!33 = metadata !{i32 11, i32 0, metadata !4, null}
|
||||
!34 = metadata !{i32 786688, metadata !4, metadata !"i1", metadata !5, i32 11, metadata !14, i32 0, i32 0, metadata !35} ; [ DW_TAG_auto_variable ] [i1] [line 11] [piece, size 4, offset 0]
|
||||
!35 = metadata !{i32 3, i32 0, i32 4}
|
||||
!36 = metadata !{i32 12, i32 0, metadata !4, null}
|
Loading…
Reference in New Issue
Block a user