Debug Info: Retreive the layout information of exploded values from the

explosion schema rather than from the debug type information and retire
a bunch of heuristics that are no longer necessary.

To enable this, this commit also includes a bugfix to debug info for
opaque data structures with the correct size and alignment information.

<rdar://problem/21470869+22707597>
This commit is contained in:
Adrian Prantl
2016-03-09 13:29:27 -08:00
parent ad4f065ad6
commit 7e489db91b
3 changed files with 102 additions and 125 deletions

View File

@@ -885,74 +885,6 @@ llvm::DIFile *IRGenDebugInfo::getFile(llvm::DIScope *Scope) {
return cast<llvm::DIFile>(Scope); return cast<llvm::DIFile>(Scope);
} }
/// Return the storage size of an explosion value.
static uint64_t getSizeFromExplosionValue(const clang::TargetInfo &TI,
llvm::Value *V) {
llvm::Type *Ty = V->getType();
if (unsigned PrimitiveSize = Ty->getPrimitiveSizeInBits())
return PrimitiveSize;
else if (Ty->isPointerTy())
return TI.getPointerWidth(0);
else
llvm_unreachable("unhandled type of explosion value");
}
/// A generator that recursively returns the size of each element of a
/// composite type.
class ElementSizes {
const TrackingDIRefMap &DIRefMap;
llvm::SmallPtrSetImpl<const llvm::DIType *> &IndirectEnums;
llvm::SmallVector<const llvm::DIType *, 12> Stack;
public:
ElementSizes(const llvm::DIType *DITy, const TrackingDIRefMap &DIRefMap,
llvm::SmallPtrSetImpl<const llvm::DIType *> &IndirectEnums)
: DIRefMap(DIRefMap), IndirectEnums(IndirectEnums), Stack(1, DITy) {}
struct SizeAlign {
uint64_t SizeInBits, AlignInBits;
};
struct SizeAlign getNext() {
if (Stack.empty())
return {0, 0};
auto *Cur = Stack.pop_back_val();
if (isa<llvm::DICompositeType>(Cur) &&
Cur->getTag() != llvm::dwarf::DW_TAG_subroutine_type) {
auto *CTy = cast<llvm::DICompositeType>(Cur);
auto Elts = CTy->getElements();
unsigned N = Cur->getTag() == llvm::dwarf::DW_TAG_union_type
? std::min(1U, Elts.size()) // For unions, pick any one.
: Elts.size();
if (N) {
// Push all elements in reverse order.
// FIXME: With a little more state we don't need to actually
// store them on the Stack.
for (unsigned I = N; I > 0; --I)
Stack.push_back(cast<llvm::DIType>(Elts[I - 1]));
return getNext();
}
}
switch (Cur->getTag()) {
case llvm::dwarf::DW_TAG_member:
// FIXME: Correctly handle the explosion value for enum types
// with indirect members.
if (IndirectEnums.count(Cur))
return {0, 0};
[[clang::fallthrough]];
case llvm::dwarf::DW_TAG_typedef: {
// Replace top of stack.
auto *DTy = cast<llvm::DIDerivedType>(Cur);
Stack.push_back(DTy->getBaseType().resolve(DIRefMap));
return getNext();
}
default:
return {Cur->getSizeInBits(), Cur->getAlignInBits()};
}
}
};
static Size static Size
getStorageSize(const llvm::DataLayout &DL, ArrayRef<llvm::Value *> Storage) { getStorageSize(const llvm::DataLayout &DL, ArrayRef<llvm::Value *> Storage) {
unsigned size = 0; unsigned size = 0;
@@ -1012,13 +944,16 @@ void IRGenDebugInfo::emitVariableDeclaration(
Optimized, Flags); Optimized, Flags);
// Insert a debug intrinsic into the current block. // Insert a debug intrinsic into the current block.
unsigned OffsetInBits = 0;
auto *BB = Builder.GetInsertBlock(); auto *BB = Builder.GetInsertBlock();
bool IsPiece = Storage.size() > 1; bool IsPiece = Storage.size() > 1;
uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth(); uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth();
unsigned VarSizeInBits = getSizeInBits(Var, DIRefMap); unsigned VarSizeInBits = getSizeInBits(Var, DIRefMap);
ElementSizes EltSizes(DITy, DIRefMap, IndirectEnumCases);
auto Dim = EltSizes.getNext(); // Running variables for the current/previous piece.
unsigned SizeInBits = 0;
unsigned AlignInBits = SizeOfByte;
unsigned OffsetInBits = 0;
for (llvm::Value *Piece : Storage) { for (llvm::Value *Piece : Storage) {
SmallVector<uint64_t, 3> Operands; SmallVector<uint64_t, 3> Operands;
if (Indirection) if (Indirection)
@@ -1030,32 +965,22 @@ void IRGenDebugInfo::emitVariableDeclaration(
Piece = llvm::ConstantInt::get(llvm::Type::getInt64Ty(M.getContext()), 0); Piece = llvm::ConstantInt::get(llvm::Type::getInt64Ty(M.getContext()), 0);
if (IsPiece) { if (IsPiece) {
// Try to get the size from the type if possible. // Advance the offset and align it for the next piece.
auto StorageSize = getSizeFromExplosionValue(CI.getTargetInfo(), Piece); OffsetInBits += llvm::alignTo(SizeInBits, AlignInBits);
assert((Dim.SizeInBits != 0 || StorageSize != 0) && SizeInBits = IGM.DataLayout.getTypeSizeInBits(Piece->getType());
"zero-sized variable with nonzero storage size"); AlignInBits = IGM.DataLayout.getABITypeAlignment(Piece->getType());
if (!AlignInBits)
AlignInBits = SizeOfByte;
// FIXME: Occasionally we miss out that the Storage is actually a // Sanity checks.
// refcount wrapper. Silently skip these for now. assert(SizeInBits && "zero-sized piece");
if (OffsetInBits+Dim.SizeInBits > VarSizeInBits) assert(SizeInBits < VarSizeInBits && "piece covers entire var");
break; assert(OffsetInBits+SizeInBits <= VarSizeInBits && "pars > totum");
if (OffsetInBits == 0 && Dim.SizeInBits == VarSizeInBits)
break;
if (Dim.SizeInBits == 0)
break;
assert(Dim.SizeInBits < VarSizeInBits // Add the piece DWARF expression.
&& "piece covers entire var");
assert(OffsetInBits+Dim.SizeInBits <= VarSizeInBits && "pars > totum");
Operands.push_back(llvm::dwarf::DW_OP_bit_piece); Operands.push_back(llvm::dwarf::DW_OP_bit_piece);
Operands.push_back(OffsetInBits); Operands.push_back(OffsetInBits);
Operands.push_back(Dim.SizeInBits); Operands.push_back(SizeInBits);
auto Size = Dim.SizeInBits;
Dim = EltSizes.getNext();
OffsetInBits +=
llvm::alignTo(Size, Dim.AlignInBits ? Dim.AlignInBits
: SizeOfByte);
} }
emitDbgIntrinsic(BB, Piece, Var, DBuilder.createExpression(Operands), Line, emitDbgIntrinsic(BB, Piece, Var, DBuilder.createExpression(Operands), Line,
Loc.Column, Scope, DS); Loc.Column, Scope, DS);
@@ -1334,14 +1259,46 @@ llvm::DIType *IRGenDebugInfo::createPointerSizedStruct(
unsigned PtrAlign = CI.getTargetInfo().getPointerAlign(0); unsigned PtrAlign = CI.getTargetInfo().getPointerAlign(0);
auto PtrTy = DBuilder.createPointerType(PointeeTy, PtrSize, PtrAlign); auto PtrTy = DBuilder.createPointerType(PointeeTy, PtrSize, PtrAlign);
llvm::Metadata *Elements[] = { llvm::Metadata *Elements[] = {
DBuilder.createMemberType(Scope, "pointer", File, 0, DBuilder.createMemberType(Scope, "ptr", File, 0,
PtrSize, PtrAlign, 0, Flags, PtrTy) PtrSize, PtrAlign, 0, Flags, PtrTy)
}; };
return DBuilder.createStructType( return DBuilder.createStructType(
Scope, Name, File, Line, PtrSize, PtrAlign, Flags, Scope, Name, File, Line, PtrSize, PtrAlign, Flags,
nullptr, // DerivedFrom /* DerivedFrom */ nullptr, DBuilder.getOrCreateArray(Elements),
DBuilder.getOrCreateArray(Elements), llvm::dwarf::DW_LANG_Swift, llvm::dwarf::DW_LANG_Swift, nullptr, MangledName);
nullptr, MangledName); }
/// Create a 2*pointer-sized struct with a mangled name and a single
/// member of PointeeTy.
llvm::DIType *IRGenDebugInfo::createDoublePointerSizedStruct(
llvm::DIScope *Scope, StringRef Name, llvm::DIType *PointeeTy,
llvm::DIFile *File, unsigned Line, unsigned Flags, StringRef MangledName) {
unsigned PtrSize = CI.getTargetInfo().getPointerWidth(0);
unsigned PtrAlign = CI.getTargetInfo().getPointerAlign(0);
llvm::Metadata *Elements[] = {
DBuilder.createMemberType(
Scope, "ptr", File, 0, PtrSize, PtrAlign, 0, Flags,
DBuilder.createPointerType(PointeeTy, PtrSize, PtrAlign)),
DBuilder.createMemberType(
Scope, "_", File, 0, PtrSize, PtrAlign, 0, Flags,
DBuilder.createPointerType(nullptr, PtrSize, PtrAlign))};
return DBuilder.createStructType(
Scope, Name, File, Line, 2*PtrSize, PtrAlign, Flags,
/* DerivedFrom */ nullptr, DBuilder.getOrCreateArray(Elements),
llvm::dwarf::DW_LANG_Swift, nullptr, MangledName);
}
/// Create an opaque struct with a mangled name.
llvm::DIType *
IRGenDebugInfo::createOpaqueStruct(llvm::DIScope *Scope, StringRef Name,
llvm::DIFile *File, unsigned Line,
unsigned SizeInBits, unsigned AlignInBits,
unsigned Flags, StringRef MangledName) {
return DBuilder.createStructType(
Scope, Name, File, Line, SizeInBits, AlignInBits, Flags,
/* DerivedFrom */ nullptr,
DBuilder.getOrCreateArray(ArrayRef<llvm::Metadata *>()),
llvm::dwarf::DW_LANG_Swift, nullptr, MangledName);
} }
/// Construct a DIType from a DebugTypeInfo object. /// Construct a DIType from a DebugTypeInfo object.
@@ -1497,6 +1454,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
} }
Scope = getOrCreateModule(ModuleName, TheCU, ModuleName, ModulePath); Scope = getOrCreateModule(ModuleName, TheCU, ModuleName, ModulePath);
} }
assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0));
return createPointerSizedStruct(Scope, Decl->getNameStr(), return createPointerSizedStruct(Scope, Decl->getNameStr(),
getOrCreateFile(L.Filename), L.Line, Flags, getOrCreateFile(L.Filename), L.Line, Flags,
MangledName); MangledName);
@@ -1508,9 +1466,9 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
// FIXME: (LLVM branch) This should probably be a DW_TAG_interface_type. // FIXME: (LLVM branch) This should probably be a DW_TAG_interface_type.
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(SM, Decl);
auto File = getOrCreateFile(L.Filename); auto File = getOrCreateFile(L.Filename);
return createPointerSizedStruct(Scope, return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName,
Decl ? Decl->getNameStr() : MangledName, File, L.Line, SizeInBits, AlignInBits, Flags,
File, L.Line, Flags, MangledName); MangledName);
} }
case TypeKind::ProtocolComposition: { case TypeKind::ProtocolComposition: {
@@ -1520,15 +1478,16 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
// FIXME: emit types // FIXME: emit types
// auto ProtocolCompositionTy = BaseTy->castTo<ProtocolCompositionType>(); // auto ProtocolCompositionTy = BaseTy->castTo<ProtocolCompositionType>();
return createPointerSizedStruct(Scope, return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName,
Decl ? Decl->getNameStr() : MangledName, File, L.Line, SizeInBits, AlignInBits, Flags,
File, L.Line, Flags, MangledName); MangledName);
} }
case TypeKind::UnboundGeneric: { case TypeKind::UnboundGeneric: {
auto *UnboundTy = BaseTy->castTo<UnboundGenericType>(); auto *UnboundTy = BaseTy->castTo<UnboundGenericType>();
auto *Decl = UnboundTy->getDecl(); auto *Decl = UnboundTy->getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(SM, Decl);
assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0));
return createPointerSizedStruct(Scope, return createPointerSizedStruct(Scope,
Decl ? Decl->getNameStr() : MangledName, Decl ? Decl->getNameStr() : MangledName,
File, L.Line, Flags, MangledName); File, L.Line, Flags, MangledName);
@@ -1538,9 +1497,9 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
auto *StructTy = BaseTy->castTo<BoundGenericStructType>(); auto *StructTy = BaseTy->castTo<BoundGenericStructType>();
auto *Decl = StructTy->getDecl(); auto *Decl = StructTy->getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(SM, Decl);
return createPointerSizedStruct(Scope, return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName,
Decl ? Decl->getNameStr() : MangledName, File, L.Line, SizeInBits, AlignInBits, Flags,
File, L.Line, Flags, MangledName); MangledName);
} }
case TypeKind::BoundGenericClass: { case TypeKind::BoundGenericClass: {
@@ -1549,6 +1508,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(SM, Decl);
// TODO: We may want to peek at Decl->isObjC() and set this // TODO: We may want to peek at Decl->isObjC() and set this
// attribute accordingly. // attribute accordingly.
assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0));
return createPointerSizedStruct(Scope, return createPointerSizedStruct(Scope,
Decl ? Decl->getNameStr() : MangledName, Decl ? Decl->getNameStr() : MangledName,
File, L.Line, Flags, MangledName); File, L.Line, Flags, MangledName);
@@ -1640,18 +1600,17 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::Function: case TypeKind::Function:
case TypeKind::PolymorphicFunction: case TypeKind::PolymorphicFunction:
case TypeKind::GenericFunction: { case TypeKind::GenericFunction: {
auto FwdDecl = llvm::TempDINode( auto FwdDecl = llvm::TempDINode(DBuilder.createReplaceableCompositeType(
DBuilder.createReplaceableCompositeType(
llvm::dwarf::DW_TAG_subroutine_type, MangledName, Scope, File, 0, llvm::dwarf::DW_TAG_subroutine_type, MangledName, Scope, File, 0,
llvm::dwarf::DW_LANG_Swift, SizeInBits, AlignInBits, Flags, llvm::dwarf::DW_LANG_Swift, SizeInBits, AlignInBits, Flags,
MangledName)); MangledName));
auto TH = llvm::TrackingMDNodeRef(FwdDecl.get()); auto TH = llvm::TrackingMDNodeRef(FwdDecl.get());
DITypeCache[DbgTy.getType()] = TH; DITypeCache[DbgTy.getType()] = TH;
CanSILFunctionType FunctionTy; CanSILFunctionType FunTy;
if (auto *SILFnTy = dyn_cast<SILFunctionType>(BaseTy)) if (auto *SILFnTy = dyn_cast<SILFunctionType>(BaseTy))
FunctionTy = CanSILFunctionType(SILFnTy); FunTy = CanSILFunctionType(SILFnTy);
// FIXME: Handling of generic parameters in SIL type lowering is in flux. // FIXME: Handling of generic parameters in SIL type lowering is in flux.
// DebugInfo doesn't appear to care about the generic context, so just // DebugInfo doesn't appear to care about the generic context, so just
// throw it away before lowering. // throw it away before lowering.
@@ -1659,20 +1618,31 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
isa<PolymorphicFunctionType>(BaseTy)) { isa<PolymorphicFunctionType>(BaseTy)) {
auto *fTy = cast<AnyFunctionType>(BaseTy); auto *fTy = cast<AnyFunctionType>(BaseTy);
auto *nongenericTy = FunctionType::get(fTy->getInput(), fTy->getResult(), auto *nongenericTy = FunctionType::get(fTy->getInput(), fTy->getResult(),
fTy->getExtInfo()); fTy->getExtInfo());
FunctionTy = IGM.SILMod->Types.getLoweredType(nongenericTy) FunTy = IGM.SILMod->Types.getLoweredType(nongenericTy)
.castTo<SILFunctionType>(); .castTo<SILFunctionType>();
} else } else
FunctionTy = FunTy =
IGM.SILMod->Types.getLoweredType(BaseTy).castTo<SILFunctionType>(); IGM.SILMod->Types.getLoweredType(BaseTy).castTo<SILFunctionType>();
auto Params = createParameterTypes(FunctionTy, DbgTy.getDeclContext()); auto Params = createParameterTypes(FunTy, DbgTy.getDeclContext());
// Functions are actually stored as a Pointer or a FunctionPairTy:
// { i8*, %swift.refcounted* }
auto FnTy = DBuilder.createSubroutineType(Params, Flags); auto FnTy = DBuilder.createSubroutineType(Params, Flags);
auto DITy = createPointerSizedStruct(Scope, MangledName, FnTy, llvm::DIType *DITy;
MainFile, 0, Flags, MangledName); if (FunTy->getRepresentation() == SILFunctionType::Representation::Thick) {
if (SizeInBits == 2 * CI.getTargetInfo().getPointerWidth(0))
// This is a FunctionPairTy: { i8*, %swift.refcounted* }.
DITy = createDoublePointerSizedStruct(Scope, MangledName, FnTy,
MainFile, 0, Flags, MangledName);
else
// This is a generic function as noted above.
DITy = createOpaqueStruct(Scope, MangledName, MainFile, 0, SizeInBits,
AlignInBits, Flags, MangledName);
} else {
assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0));
DITy = createPointerSizedStruct(Scope, MangledName, FnTy, MainFile, 0,
Flags, MangledName);
}
DBuilder.replaceTemporary(std::move(FwdDecl), DITy); DBuilder.replaceTemporary(std::move(FwdDecl), DITy);
return DITy; return DITy;
} }

View File

@@ -264,6 +264,13 @@ private:
llvm::DIType *PointeeTy, llvm::DIType *PointeeTy,
llvm::DIFile *File, unsigned Line, llvm::DIFile *File, unsigned Line,
unsigned Flags, StringRef MangledName); unsigned Flags, StringRef MangledName);
llvm::DIType *createDoublePointerSizedStruct(
llvm::DIScope *Scope, StringRef Name, llvm::DIType *PointeeTy,
llvm::DIFile *File, unsigned Line, unsigned Flags, StringRef MangledName);
llvm::DIType *createOpaqueStruct(llvm::DIScope *Scope, StringRef Name,
llvm::DIFile *File, unsigned Line,
unsigned SizeInBits, unsigned AlignInBits,
unsigned Flags, StringRef MangledName);
uint64_t getSizeOfBasicType(DebugTypeInfo DbgTy); uint64_t getSizeOfBasicType(DebugTypeInfo DbgTy);
TypeAliasDecl *getMetadataType(); TypeAliasDecl *getMetadataType();
}; };

View File

@@ -13,7 +13,7 @@ func main() -> Int64 {
// CHECK-DAG: !DILocalVariable(name: "bar_function_pointer",{{.*}} line: [[@LINE+1]],{{.*}} type: !"[[BARPT:[^,]+]]" // CHECK-DAG: !DILocalVariable(name: "bar_function_pointer",{{.*}} line: [[@LINE+1]],{{.*}} type: !"[[BARPT:[^,]+]]"
var bar_function_pointer = bar var bar_function_pointer = bar
// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "[[BARPT]]",{{.*}} elements: ![[BARMEMBERS:[0-9]+]] // CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "[[BARPT]]",{{.*}} elements: ![[BARMEMBERS:[0-9]+]]
// CHECK-DAG: ![[BARMEMBERS]] = !{![[BARMEMBER:.*]]} // CHECK-DAG: ![[BARMEMBERS]] = !{![[BARMEMBER:.*]], {{.*}}}
// CHECK-DAG: ![[BARMEMBER]] = !DIDerivedType(tag: DW_TAG_member,{{.*}} baseType: ![[BARPTR:[0-9]+]] // CHECK-DAG: ![[BARMEMBER]] = !DIDerivedType(tag: DW_TAG_member,{{.*}} baseType: ![[BARPTR:[0-9]+]]
// CHECK-DAG: ![[BARPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BART:[0-9]+]] // CHECK-DAG: ![[BARPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BART:[0-9]+]]
// CHECK-DAG: ![[BART]] = !DISubroutineType(types: ![[BARARGS:[0-9]+]]) // CHECK-DAG: ![[BART]] = !DISubroutineType(types: ![[BARARGS:[0-9]+]])
@@ -22,7 +22,7 @@ func main() -> Int64 {
// CHECK-DAG: !DILocalVariable(name: "baz_function_pointer",{{.*}} type: !"[[BAZPT:[^,]+]]" // CHECK-DAG: !DILocalVariable(name: "baz_function_pointer",{{.*}} type: !"[[BAZPT:[^,]+]]"
// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "[[BAZPT]]",{{.*}} elements: ![[BAZMEMBERS:[0-9]+]] // CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "[[BAZPT]]",{{.*}} elements: ![[BAZMEMBERS:[0-9]+]]
// CHECK-DAG: ![[BAZMEMBERS]] = !{![[BAZMEMBER:.*]]} // CHECK-DAG: ![[BAZMEMBERS]] = !{![[BAZMEMBER:.*]], {{.*}}}
// CHECK-DAG: ![[BAZMEMBER]] = !DIDerivedType(tag: DW_TAG_member,{{.*}} baseType: ![[BAZPTR:[0-9]+]] // CHECK-DAG: ![[BAZMEMBER]] = !DIDerivedType(tag: DW_TAG_member,{{.*}} baseType: ![[BAZPTR:[0-9]+]]
// CHECK-DAG: ![[BAZPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BAZT:[0-9]+]] // CHECK-DAG: ![[BAZPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BAZT:[0-9]+]]
// CHECK-DAG: ![[BAZT]] = !DISubroutineType(types: ![[BAZARGS:.*]]) // CHECK-DAG: ![[BAZT]] = !DISubroutineType(types: ![[BAZARGS:.*]])
@@ -32,7 +32,7 @@ func main() -> Int64 {
// CHECK-DAG: !DILocalVariable(name: "barz_function_pointer",{{.*}} type: !"[[BARZPT:[^,]+]]" // CHECK-DAG: !DILocalVariable(name: "barz_function_pointer",{{.*}} type: !"[[BARZPT:[^,]+]]"
// CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "[[BARZPT]]",{{.*}} elements: ![[BARZMEMBERS:[0-9]+]] // CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "[[BARZPT]]",{{.*}} elements: ![[BARZMEMBERS:[0-9]+]]
// CHECK-DAG: ![[BARZMEMBERS]] = !{![[BARZMEMBER:.*]]} // CHECK-DAG: ![[BARZMEMBERS]] = !{![[BARZMEMBER:.*]], {{.*}}}
// CHECK-DAG: ![[BARZMEMBER]] = !DIDerivedType(tag: DW_TAG_member,{{.*}} baseType: ![[BARZPTR:[0-9]+]] // CHECK-DAG: ![[BARZMEMBER]] = !DIDerivedType(tag: DW_TAG_member,{{.*}} baseType: ![[BARZPTR:[0-9]+]]
// CHECK-DAG: ![[BARZPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BARZT:[0-9]+]] // CHECK-DAG: ![[BARZPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BARZT:[0-9]+]]
// CHECK-DAG: ![[BARZT]] = !DISubroutineType(types: ![[BARZARGS:.*]]) // CHECK-DAG: ![[BARZT]] = !DISubroutineType(types: ![[BARZARGS:.*]])