227 changes: 189 additions & 38 deletions llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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());
Expand All @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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");
}
Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -642,6 +648,13 @@ class DwarfDebug : public AsmPrinterHandler {
/// \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);
Expand Down
66 changes: 51 additions & 15 deletions llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -511,26 +514,42 @@ 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 {
addUInt(TheDie, dwarf::DW_FORM_data1, dwarf::DW_OP_regx);
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);
}
}
}
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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());

Expand All @@ -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");
}
Expand Down Expand Up @@ -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());

Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ class DwarfUnit {
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);
Expand Down
22 changes: 22 additions & 0 deletions llvm/lib/IR/DIBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
48 changes: 48 additions & 0 deletions llvm/lib/IR/DebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down
76 changes: 76 additions & 0 deletions llvm/test/DebugInfo/X86/pieces-1.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 92 additions & 0 deletions llvm/test/DebugInfo/X86/pieces-2.ll
Original file line number Diff line number Diff line change
@@ -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 changes: 105 additions & 0 deletions llvm/test/DebugInfo/X86/pieces-3.ll
Original file line number Diff line number Diff line change
@@ -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}