summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/lib/CodeGen/CGExpr.cpp4
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp13
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h9
-rw-r--r--clang/lib/CodeGen/CodeGenTBAA.cpp47
-rw-r--r--clang/lib/CodeGen/CodeGenTBAA.h12
-rw-r--r--clang/test/CodeGen/tbaa.cpp91
6 files changed, 107 insertions, 69 deletions
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index fe0d0701055..4d72dc1c884 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1175,7 +1175,7 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(llvm::Value *Addr, bool Volatile,
if (TBAAInfo) {
llvm::MDNode *TBAAPath = CGM.getTBAAStructTagInfo(TBAABaseType, TBAAInfo,
TBAAOffset);
- CGM.DecorateInstruction(Load, TBAAPath);
+ CGM.DecorateInstruction(Load, TBAAPath, false/*ConvertTypeToTag*/);
}
if ((SanOpts->Bool && hasBooleanRepresentation(Ty)) ||
@@ -1289,7 +1289,7 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, llvm::Value *Addr,
if (TBAAInfo) {
llvm::MDNode *TBAAPath = CGM.getTBAAStructTagInfo(TBAABaseType, TBAAInfo,
TBAAOffset);
- CGM.DecorateInstruction(Store, TBAAPath);
+ CGM.DecorateInstruction(Store, TBAAPath, false/*ConvertTypeToTag*/);
}
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e8e3d1ae76e..4126bea295b 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -242,9 +242,18 @@ llvm::MDNode *CodeGenModule::getTBAAStructTagInfo(QualType BaseTy,
return TBAA->getTBAAStructTagInfo(BaseTy, AccessN, O);
}
+/// Decorate the instruction with a TBAA tag. For scalar TBAA, the tag
+/// is the same as the type. For struct-path aware TBAA, the tag
+/// is different from the type: base type, access type and offset.
+/// When ConvertTypeToTag is true, we create a tag based on the scalar type.
void CodeGenModule::DecorateInstruction(llvm::Instruction *Inst,
- llvm::MDNode *TBAAInfo) {
- Inst->setMetadata(llvm::LLVMContext::MD_tbaa, TBAAInfo);
+ llvm::MDNode *TBAAInfo,
+ bool ConvertTypeToTag) {
+ if (ConvertTypeToTag && TBAA && CodeGenOpts.StructPathTBAA)
+ Inst->setMetadata(llvm::LLVMContext::MD_tbaa,
+ TBAA->getTBAAScalarTagInfo(TBAAInfo));
+ else
+ Inst->setMetadata(llvm::LLVMContext::MD_tbaa, TBAAInfo);
}
bool CodeGenModule::isTargetDarwin() const {
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 6bc44563fae..f8b09711f78 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -519,8 +519,13 @@ public:
bool isPaddedAtomicType(QualType type);
bool isPaddedAtomicType(const AtomicType *type);
- static void DecorateInstruction(llvm::Instruction *Inst,
- llvm::MDNode *TBAAInfo);
+ /// Decorate the instruction with a TBAA tag. For scalar TBAA, the tag
+ /// is the same as the type. For struct-path aware TBAA, the tag
+ /// is different from the type: base type, access type and offset.
+ /// When ConvertTypeToTag is true, we create a tag based on the scalar type.
+ void DecorateInstruction(llvm::Instruction *Inst,
+ llvm::MDNode *TBAAInfo,
+ bool ConvertTypeToTag = true);
/// getSize - Emit the given number of characters as a value of type size_t.
llvm::ConstantInt *getSize(CharUnits numChars);
diff --git a/clang/lib/CodeGen/CodeGenTBAA.cpp b/clang/lib/CodeGen/CodeGenTBAA.cpp
index 7e4d34ab898..bb1ebda9084 100644
--- a/clang/lib/CodeGen/CodeGenTBAA.cpp
+++ b/clang/lib/CodeGen/CodeGenTBAA.cpp
@@ -50,13 +50,25 @@ llvm::MDNode *CodeGenTBAA::getRoot() {
return Root;
}
+// For struct-path aware TBAA, the scalar type has the same format as
+// the struct type: name, offset, pointer to another node in the type DAG.
+// For scalar TBAA, the scalar type is the same as the scalar tag:
+// name and a parent pointer.
+llvm::MDNode *CodeGenTBAA::createTBAAScalarType(StringRef Name,
+ llvm::MDNode *Parent) {
+ if (CodeGenOpts.StructPathTBAA)
+ return MDHelper.createTBAAScalarTypeNode(Name, 0, Parent);
+ else
+ return MDHelper.createTBAANode(Name, Parent);
+}
+
llvm::MDNode *CodeGenTBAA::getChar() {
// Define the root of the tree for user-accessible memory. C and C++
// give special powers to char and certain similar types. However,
// these special powers only cover user-accessible memory, and doesn't
// include things like vtables.
if (!Char)
- Char = MDHelper.createTBAANode("omnipotent char", getRoot());
+ Char = createTBAAScalarType("omnipotent char", getRoot());
return Char;
}
@@ -124,7 +136,7 @@ CodeGenTBAA::getTBAAInfo(QualType QTy) {
// "underlying types".
default:
return MetadataCache[Ty] =
- MDHelper.createTBAANode(BTy->getName(Features), getChar());
+ createTBAAScalarType(BTy->getName(Features), getChar());
}
}
@@ -132,8 +144,8 @@ CodeGenTBAA::getTBAAInfo(QualType QTy) {
// TODO: Implement C++'s type "similarity" and consider dis-"similar"
// pointers distinct.
if (Ty->isPointerType())
- return MetadataCache[Ty] = MDHelper.createTBAANode("any pointer",
- getChar());
+ return MetadataCache[Ty] = createTBAAScalarType("any pointer",
+ getChar());
// Enum types are distinct types. In C++ they have "underlying types",
// however they aren't related for TBAA.
@@ -160,7 +172,7 @@ CodeGenTBAA::getTBAAInfo(QualType QTy) {
llvm::raw_svector_ostream Out(OutName);
MContext.mangleCXXRTTIName(QualType(ETy, 0), Out);
Out.flush();
- return MetadataCache[Ty] = MDHelper.createTBAANode(OutName, getChar());
+ return MetadataCache[Ty] = createTBAAScalarType(OutName, getChar());
}
// For now, handle any other kind of type conservatively.
@@ -168,7 +180,7 @@ CodeGenTBAA::getTBAAInfo(QualType QTy) {
}
llvm::MDNode *CodeGenTBAA::getTBAAInfoForVTablePtr() {
- return MDHelper.createTBAANode("vtable pointer", getRoot());
+ return createTBAAScalarType("vtable pointer", getRoot());
}
bool
@@ -252,10 +264,6 @@ CodeGenTBAA::getTBAAStructTypeInfo(QualType QTy) {
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
SmallVector <std::pair<uint64_t, llvm::MDNode*>, 4> Fields;
- // To reduce the size of MDNode for a given struct type, we only output
- // once for all the fields with the same scalar types.
- // Offsets for scalar fields in the type DAG are not used.
- llvm::SmallSet <llvm::MDNode*, 4> ScalarFieldTypes;
unsigned idx = 0;
for (RecordDecl::field_iterator i = RD->field_begin(),
e = RD->field_end(); i != e; ++i, ++idx) {
@@ -263,13 +271,8 @@ CodeGenTBAA::getTBAAStructTypeInfo(QualType QTy) {
llvm::MDNode *FieldNode;
if (isTBAAPathStruct(FieldQTy))
FieldNode = getTBAAStructTypeInfo(FieldQTy);
- else {
+ else
FieldNode = getTBAAInfo(FieldQTy);
- // Ignore this field if the type already exists.
- if (ScalarFieldTypes.count(FieldNode))
- continue;
- ScalarFieldTypes.insert(FieldNode);
- }
if (!FieldNode)
return StructTypeMetadataCache[Ty] = NULL;
Fields.push_back(std::make_pair(
@@ -305,8 +308,18 @@ CodeGenTBAA::getTBAAStructTagInfo(QualType BaseQTy, llvm::MDNode *AccessNode,
if (isTBAAPathStruct(BaseQTy))
BNode = getTBAAStructTypeInfo(BaseQTy);
if (!BNode)
- return StructTagMetadataCache[PathTag] = AccessNode;
+ return StructTagMetadataCache[PathTag] =
+ MDHelper.createTBAAStructTagNode(AccessNode, AccessNode, 0);
return StructTagMetadataCache[PathTag] =
MDHelper.createTBAAStructTagNode(BNode, AccessNode, Offset);
}
+
+llvm::MDNode *
+CodeGenTBAA::getTBAAScalarTagInfo(llvm::MDNode *AccessNode) {
+ if (llvm::MDNode *N = ScalarTagMetadataCache[AccessNode])
+ return N;
+
+ return ScalarTagMetadataCache[AccessNode] =
+ MDHelper.createTBAAStructTagNode(AccessNode, AccessNode, 0);
+}
diff --git a/clang/lib/CodeGen/CodeGenTBAA.h b/clang/lib/CodeGen/CodeGenTBAA.h
index 9ddc3aa9700..f0c9e06f02b 100644
--- a/clang/lib/CodeGen/CodeGenTBAA.h
+++ b/clang/lib/CodeGen/CodeGenTBAA.h
@@ -61,6 +61,8 @@ class CodeGenTBAA {
llvm::DenseMap<const Type *, llvm::MDNode *> StructTypeMetadataCache;
/// This maps TBAAPathTags to a tag node.
llvm::DenseMap<TBAAPathTag, llvm::MDNode *> StructTagMetadataCache;
+ /// This maps a scalar type to a scalar tag node.
+ llvm::DenseMap<const llvm::MDNode *, llvm::MDNode *> ScalarTagMetadataCache;
/// StructMetadataCache - This maps clang::Types to llvm::MDNodes describing
/// them for struct assignments.
@@ -84,6 +86,11 @@ class CodeGenTBAA {
SmallVectorImpl<llvm::MDBuilder::TBAAStructField> &Fields,
bool MayAlias);
+ /// A wrapper function to create a scalar type. For struct-path aware TBAA,
+ /// the scalar type has the same format as the struct type: name, offset,
+ /// pointer to another node in the type DAG.
+ llvm::MDNode *createTBAAScalarType(StringRef Name, llvm::MDNode *Parent);
+
public:
CodeGenTBAA(ASTContext &Ctx, llvm::LLVMContext &VMContext,
const CodeGenOptions &CGO,
@@ -105,10 +112,13 @@ public:
/// Get the MDNode in the type DAG for given struct type QType.
llvm::MDNode *getTBAAStructTypeInfo(QualType QType);
- /// Get the tag MDNode for a given base type, the actual sclar access MDNode
+ /// Get the tag MDNode for a given base type, the actual scalar access MDNode
/// and offset into the base type.
llvm::MDNode *getTBAAStructTagInfo(QualType BaseQType,
llvm::MDNode *AccessNode, uint64_t Offset);
+
+ /// Get the sclar tag MDNode for a given scalar type.
+ llvm::MDNode *getTBAAScalarTagInfo(llvm::MDNode *AccessNode);
};
} // end namespace CodeGen
diff --git a/clang/test/CodeGen/tbaa.cpp b/clang/test/CodeGen/tbaa.cpp
index b7d02fd80a3..c256b4af809 100644
--- a/clang/test/CodeGen/tbaa.cpp
+++ b/clang/test/CodeGen/tbaa.cpp
@@ -49,8 +49,8 @@ uint32_t g(uint32_t *s, StructA *A, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
-// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !5
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_i32:!.*]]
+// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32:!.*]]
*s = 1;
A->f32 = 4;
return *s;
@@ -61,8 +61,8 @@ uint32_t g2(uint32_t *s, StructA *A, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
-// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa !8
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_i32]]
+// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_A_f16:!.*]]
*s = 1;
A->f16 = 4;
return *s;
@@ -73,8 +73,8 @@ uint32_t g3(StructA *A, StructB *B, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5
-// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !9
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]]
+// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32:!.*]]
A->f32 = 1;
B->a.f32 = 4;
return A->f32;
@@ -85,8 +85,8 @@ uint32_t g4(StructA *A, StructB *B, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5
-// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa !11
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]]
+// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_B_a_f16:!.*]]
A->f32 = 1;
B->a.f16 = 4;
return A->f32;
@@ -97,8 +97,8 @@ uint32_t g5(StructA *A, StructB *B, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5
-// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !12
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]]
+// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_f32:!.*]]
A->f32 = 1;
B->f32 = 4;
return A->f32;
@@ -109,8 +109,8 @@ uint32_t g6(StructA *A, StructB *B, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5
-// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !13
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]]
+// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32_2:!.*]]
A->f32 = 1;
B->a.f32_2 = 4;
return A->f32;
@@ -121,8 +121,8 @@ uint32_t g7(StructA *A, StructS *S, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5
-// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !14
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]]
+// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32:!.*]]
A->f32 = 1;
S->f32 = 4;
return A->f32;
@@ -133,8 +133,8 @@ uint32_t g8(StructA *A, StructS *S, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !5
-// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa !16
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_A_f32]]
+// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_S_f16:!.*]]
A->f32 = 1;
S->f16 = 4;
return A->f32;
@@ -145,8 +145,8 @@ uint32_t g9(StructS *S, StructS2 *S2, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !14
-// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !17
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32]]
+// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_S2_f32:!.*]]
S->f32 = 1;
S2->f32 = 4;
return S->f32;
@@ -157,8 +157,8 @@ uint32_t g10(StructS *S, StructS2 *S2, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i16 4, i16* %{{.*}}, align 2, !tbaa !5
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !14
-// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa !19
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_S_f32]]
+// PATH: store i16 4, i16* %{{.*}}, align 2, !tbaa [[TAG_S2_f16:!.*]]
S->f32 = 1;
S2->f16 = 4;
return S->f32;
@@ -169,8 +169,8 @@ uint32_t g11(StructC *C, StructD *D, uint64_t count) {
// CHECK: store i32 1, i32* %{{.*}}, align 4, !tbaa !4
// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !20
-// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !22
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_C_b_a_f32:!.*]]
+// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_D_b_a_f32:!.*]]
C->b.a.f32 = 1;
D->b.a.f32 = 4;
return C->b.a.f32;
@@ -182,8 +182,8 @@ uint32_t g12(StructC *C, StructD *D, uint64_t count) {
// CHECK: store i32 4, i32* %{{.*}}, align 4, !tbaa !4
// TODO: differentiate the two accesses.
// PATH: define i32 @{{.*}}(
-// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa !9
-// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa !9
+// PATH: store i32 1, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32]]
+// PATH: store i32 4, i32* %{{.*}}, align 4, !tbaa [[TAG_B_a_f32]]
StructB *b1 = &(C->b);
StructB *b2 = &(D->b);
// b1, b2 have different context.
@@ -197,24 +197,25 @@ uint32_t g12(StructC *C, StructD *D, uint64_t count) {
// CHECK: !4 = metadata !{metadata !"int", metadata !1}
// CHECK: !5 = metadata !{metadata !"short", metadata !1}
-// PATH: !1 = metadata !{metadata !"omnipotent char", metadata !2}
-// PATH: !4 = metadata !{metadata !"int", metadata !1}
-// PATH: !5 = metadata !{metadata !6, metadata !4, i64 4}
-// PATH: !6 = metadata !{metadata !"_ZTS7StructA", i64 0, metadata !7, i64 4, metadata !4}
-// PATH: !7 = metadata !{metadata !"short", metadata !1}
-// PATH: !8 = metadata !{metadata !6, metadata !7, i64 0}
-// PATH: !9 = metadata !{metadata !10, metadata !4, i64 8}
-// PATH: !10 = metadata !{metadata !"_ZTS7StructB", i64 0, metadata !7, i64 4, metadata !6, i64 20, metadata !4}
-// PATH: !11 = metadata !{metadata !10, metadata !7, i64 4}
-// PATH: !12 = metadata !{metadata !10, metadata !4, i64 20}
-// PATH: !13 = metadata !{metadata !10, metadata !4, i64 16}
-// PATH: !14 = metadata !{metadata !15, metadata !4, i64 4}
-// PATH: !15 = metadata !{metadata !"_ZTS7StructS", i64 0, metadata !7, i64 4, metadata !4}
-// PATH: !16 = metadata !{metadata !15, metadata !7, i64 0}
-// PATH: !17 = metadata !{metadata !18, metadata !4, i64 4}
-// PATH: !18 = metadata !{metadata !"_ZTS8StructS2", i64 0, metadata !7, i64 4, metadata !4}
-// PATH: !19 = metadata !{metadata !18, metadata !7, i64 0}
-// PATH: !20 = metadata !{metadata !21, metadata !4, i64 12}
-// PATH: !21 = metadata !{metadata !"_ZTS7StructC", i64 0, metadata !7, i64 4, metadata !10, i64 28, metadata !4}
-// PATH: !22 = metadata !{metadata !23, metadata !4, i64 12}
-// PATH: !23 = metadata !{metadata !"_ZTS7StructD", i64 0, metadata !7, i64 4, metadata !10, i64 28, metadata !4, i64 32, metadata !1}
+// PATH: [[TYPE_CHAR:!.*]] = metadata !{metadata !"omnipotent char", i64 0, metadata !3}
+// PATH: [[TAG_i32]] = metadata !{metadata [[TYPE_INT:!.*]], metadata [[TYPE_INT]], i64 0}
+// PATH: [[TYPE_INT]] = metadata !{metadata !"int", i64 0, metadata [[TYPE_CHAR]]}
+// PATH: [[TAG_A_f32]] = metadata !{metadata [[TYPE_A:!.*]], metadata [[TYPE_INT]], i64 4}
+// PATH: [[TYPE_A]] = metadata !{metadata !"_ZTS7StructA", i64 0, metadata [[TYPE_SHORT:!.*]], i64 4, metadata [[TYPE_INT]], i64 8, metadata [[TYPE_SHORT]], i64 12, metadata [[TYPE_INT]]}
+// PATH: [[TYPE_SHORT:!.*]] = metadata !{metadata !"short", i64 0, metadata [[TYPE_CHAR]]}
+// PATH: [[TAG_A_f16]] = metadata !{metadata [[TYPE_A]], metadata [[TYPE_SHORT]], i64 0}
+// PATH: [[TAG_B_a_f32]] = metadata !{metadata [[TYPE_B:!.*]], metadata [[TYPE_INT]], i64 8}
+// PATH: [[TYPE_B]] = metadata !{metadata !"_ZTS7StructB", i64 0, metadata [[TYPE_SHORT]], i64 4, metadata [[TYPE_A]], i64 20, metadata [[TYPE_INT]]}
+// PATH: [[TAG_B_a_f16]] = metadata !{metadata [[TYPE_B]], metadata [[TYPE_SHORT]], i64 4}
+// PATH: [[TAG_B_f32]] = metadata !{metadata [[TYPE_B]], metadata [[TYPE_INT]], i64 20}
+// PATH: [[TAG_B_a_f32_2]] = metadata !{metadata [[TYPE_B]], metadata [[TYPE_INT]], i64 16}
+// PATH: [[TAG_S_f32]] = metadata !{metadata [[TYPE_S:!.*]], metadata [[TYPE_INT]], i64 4}
+// PATH: [[TYPE_S]] = metadata !{metadata !"_ZTS7StructS", i64 0, metadata [[TYPE_SHORT]], i64 4, metadata [[TYPE_INT]]}
+// PATH: [[TAG_S_f16]] = metadata !{metadata [[TYPE_S]], metadata [[TYPE_SHORT]], i64 0}
+// PATH: [[TAG_S2_f32]] = metadata !{metadata [[TYPE_S2:!.*]], metadata [[TYPE_INT]], i64 4}
+// PATH: [[TYPE_S2]] = metadata !{metadata !"_ZTS8StructS2", i64 0, metadata [[TYPE_SHORT]], i64 4, metadata [[TYPE_INT]]}
+// PATH: [[TAG_S2_f16]] = metadata !{metadata [[TYPE_S2]], metadata [[TYPE_SHORT]], i64 0}
+// PATH: [[TAG_C_b_a_f32]] = metadata !{metadata [[TYPE_C:!.*]], metadata [[TYPE_INT]], i64 12}
+// PATH: [[TYPE_C]] = metadata !{metadata !"_ZTS7StructC", i64 0, metadata [[TYPE_SHORT]], i64 4, metadata [[TYPE_B]], i64 28, metadata [[TYPE_INT]]}
+// PATH: [[TAG_D_b_a_f32]] = metadata !{metadata [[TYPE_D:!.*]], metadata [[TYPE_INT]], i64 12}
+// PATH: [[TYPE_D]] = metadata !{metadata !"_ZTS7StructD", i64 0, metadata [[TYPE_SHORT]], i64 4, metadata [[TYPE_B]], i64 28, metadata [[TYPE_INT]], i64 32, metadata [[TYPE_CHAR]]}
OpenPOWER on IntegriCloud