1 | //===- DebugTypeODRUniquingTest.cpp - Debug type ODR uniquing tests -------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/BinaryFormat/Dwarf.h" |
10 | #include "llvm/IR/DebugInfoMetadata.h" |
11 | #include "llvm/IR/LLVMContext.h" |
12 | #include "gtest/gtest.h" |
13 | using namespace llvm; |
14 | |
15 | namespace { |
16 | |
17 | TEST(DebugTypeODRUniquingTest, enableDebugTypeODRUniquing) { |
18 | LLVMContext Context; |
19 | EXPECT_FALSE(Context.isODRUniquingDebugTypes()); |
20 | Context.enableDebugTypeODRUniquing(); |
21 | EXPECT_TRUE(Context.isODRUniquingDebugTypes()); |
22 | Context.disableDebugTypeODRUniquing(); |
23 | EXPECT_FALSE(Context.isODRUniquingDebugTypes()); |
24 | } |
25 | |
26 | TEST(DebugTypeODRUniquingTest, getODRType) { |
27 | LLVMContext Context; |
28 | MDString &UUID = *MDString::get(Context, Str: "string" ); |
29 | |
30 | // Without a type map, this should return null. |
31 | EXPECT_FALSE(DICompositeType::getODRType( |
32 | Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, nullptr, |
33 | nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr, nullptr, nullptr, |
34 | nullptr, nullptr, nullptr, nullptr, nullptr)); |
35 | |
36 | // Enable the mapping. There still shouldn't be a type. |
37 | Context.enableDebugTypeODRUniquing(); |
38 | EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID)); |
39 | |
40 | // Create some ODR-uniqued type. |
41 | auto &CT = *DICompositeType::getODRType( |
42 | Context, Identifier&: UUID, Tag: dwarf::DW_TAG_class_type, Name: nullptr, File: nullptr, Line: 0, Scope: nullptr, |
43 | BaseType: nullptr, SizeInBits: 0, AlignInBits: 0, OffsetInBits: 0, Flags: DINode::FlagZero, Elements: nullptr, RuntimeLang: 0, VTableHolder: nullptr, TemplateParams: nullptr, Discriminator: nullptr, |
44 | DataLocation: nullptr, Associated: nullptr, Allocated: nullptr, Rank: nullptr, Annotations: nullptr); |
45 | EXPECT_EQ(UUID.getString(), CT.getIdentifier()); |
46 | |
47 | // Check that we get it back, even if we change a field. |
48 | EXPECT_EQ(&CT, DICompositeType::getODRTypeIfExists(Context, UUID)); |
49 | EXPECT_EQ(&CT, |
50 | DICompositeType::getODRType( |
51 | Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, 0, |
52 | nullptr, nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, |
53 | nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
54 | nullptr)); |
55 | EXPECT_EQ(&CT, DICompositeType::getODRType( |
56 | Context, UUID, dwarf::DW_TAG_class_type, |
57 | MDString::get(Context, "name" ), nullptr, 0, nullptr, |
58 | nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, nullptr, |
59 | nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
60 | nullptr)); |
61 | |
62 | // Check that it's discarded with the type map. |
63 | Context.disableDebugTypeODRUniquing(); |
64 | EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID)); |
65 | |
66 | // And it shouldn't magically reappear... |
67 | Context.enableDebugTypeODRUniquing(); |
68 | EXPECT_FALSE(DICompositeType::getODRTypeIfExists(Context, UUID)); |
69 | } |
70 | |
71 | TEST(DebugTypeODRUniquingTest, buildODRType) { |
72 | LLVMContext Context; |
73 | Context.enableDebugTypeODRUniquing(); |
74 | |
75 | // Build an ODR type that's a forward decl. |
76 | MDString &UUID = *MDString::get(Context, Str: "Type" ); |
77 | auto &CT = *DICompositeType::buildODRType( |
78 | Context, Identifier&: UUID, Tag: dwarf::DW_TAG_class_type, Name: nullptr, File: nullptr, Line: 0, Scope: nullptr, |
79 | BaseType: nullptr, SizeInBits: 0, AlignInBits: 0, OffsetInBits: 0, Flags: DINode::FlagFwdDecl, Elements: nullptr, RuntimeLang: 0, VTableHolder: nullptr, TemplateParams: nullptr, |
80 | Discriminator: nullptr, DataLocation: nullptr, Associated: nullptr, Allocated: nullptr, Rank: nullptr, Annotations: nullptr); |
81 | EXPECT_EQ(&CT, DICompositeType::getODRTypeIfExists(Context, UUID)); |
82 | EXPECT_EQ(dwarf::DW_TAG_class_type, CT.getTag()); |
83 | |
84 | // Update with another forward decl. This should be a no-op. |
85 | EXPECT_EQ(&CT, DICompositeType::buildODRType( |
86 | Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, |
87 | 0, nullptr, nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, |
88 | 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
89 | nullptr, nullptr)); |
90 | |
91 | EXPECT_FALSE(DICompositeType::buildODRType( |
92 | Context, UUID, dwarf::DW_TAG_structure_type, nullptr, nullptr, 0, nullptr, |
93 | nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, 0, nullptr, nullptr, |
94 | nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)); |
95 | |
96 | // Update with a definition. This time we should see a change. |
97 | EXPECT_EQ(&CT, DICompositeType::buildODRType( |
98 | Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, |
99 | 0, nullptr, nullptr, 0, 0, 0, DINode::FlagZero, nullptr, 0, |
100 | nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
101 | nullptr, nullptr)); |
102 | EXPECT_FALSE(CT.isForwardDecl()); |
103 | |
104 | // Further updates should be ignored. |
105 | EXPECT_EQ(&CT, DICompositeType::buildODRType( |
106 | Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, |
107 | 0, nullptr, nullptr, 0, 0, 0, DINode::FlagFwdDecl, nullptr, |
108 | 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
109 | nullptr, nullptr)); |
110 | EXPECT_FALSE(CT.isForwardDecl()); |
111 | EXPECT_EQ(&CT, DICompositeType::buildODRType( |
112 | Context, UUID, dwarf::DW_TAG_class_type, nullptr, nullptr, |
113 | 111u, nullptr, nullptr, 0, 0, 0, DINode::FlagZero, nullptr, |
114 | 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
115 | nullptr, nullptr)); |
116 | EXPECT_NE(111u, CT.getLine()); |
117 | } |
118 | |
119 | TEST(DebugTypeODRUniquingTest, buildODRTypeFields) { |
120 | LLVMContext Context; |
121 | Context.enableDebugTypeODRUniquing(); |
122 | |
123 | // Build an ODR type that's a forward decl with no other fields set. |
124 | MDString &UUID = *MDString::get(Context, Str: "UUID" ); |
125 | auto &CT = *DICompositeType::buildODRType( |
126 | Context, Identifier&: UUID, Tag: 0, Name: nullptr, File: nullptr, Line: 0, Scope: nullptr, BaseType: nullptr, SizeInBits: 0, AlignInBits: 0, OffsetInBits: 0, |
127 | Flags: DINode::FlagFwdDecl, Elements: nullptr, RuntimeLang: 0, VTableHolder: nullptr, TemplateParams: nullptr, Discriminator: nullptr, DataLocation: nullptr, |
128 | Associated: nullptr, Allocated: nullptr, Rank: nullptr, Annotations: nullptr); |
129 | |
130 | // Create macros for running through all the fields except Identifier and Flags. |
131 | #define FOR_EACH_MDFIELD() \ |
132 | DO_FOR_FIELD(Name) \ |
133 | DO_FOR_FIELD(File) \ |
134 | DO_FOR_FIELD(Scope) \ |
135 | DO_FOR_FIELD(BaseType) \ |
136 | DO_FOR_FIELD(Elements) \ |
137 | DO_FOR_FIELD(VTableHolder) \ |
138 | DO_FOR_FIELD(TemplateParams) |
139 | #define FOR_EACH_INLINEFIELD() \ |
140 | DO_FOR_FIELD(Line) \ |
141 | DO_FOR_FIELD(SizeInBits) \ |
142 | DO_FOR_FIELD(AlignInBits) \ |
143 | DO_FOR_FIELD(OffsetInBits) \ |
144 | DO_FOR_FIELD(RuntimeLang) |
145 | |
146 | // Create all the fields. |
147 | #define DO_FOR_FIELD(X) auto *X = MDString::get(Context, #X); |
148 | FOR_EACH_MDFIELD(); |
149 | #undef DO_FOR_FIELD |
150 | unsigned NonZeroInit = 0; |
151 | #define DO_FOR_FIELD(X) auto X = ++NonZeroInit; |
152 | FOR_EACH_INLINEFIELD(); |
153 | #undef DO_FOR_FIELD |
154 | |
155 | // Replace all the fields with new values that are distinct from each other. |
156 | EXPECT_EQ(&CT, |
157 | DICompositeType::buildODRType( |
158 | Context, UUID, 0, Name, File, Line, Scope, BaseType, SizeInBits, |
159 | AlignInBits, OffsetInBits, DINode::FlagArtificial, Elements, |
160 | RuntimeLang, VTableHolder, TemplateParams, nullptr, nullptr, |
161 | nullptr, nullptr, nullptr, nullptr)); |
162 | |
163 | // Confirm that all the right fields got updated. |
164 | #define DO_FOR_FIELD(X) EXPECT_EQ(X, CT.getRaw##X()); |
165 | FOR_EACH_MDFIELD(); |
166 | #undef DO_FOR_FIELD |
167 | #undef FOR_EACH_MDFIELD |
168 | #define DO_FOR_FIELD(X) EXPECT_EQ(X, CT.get##X()); |
169 | FOR_EACH_INLINEFIELD(); |
170 | #undef DO_FOR_FIELD |
171 | #undef FOR_EACH_INLINEFIELD |
172 | EXPECT_EQ(DINode::FlagArtificial, CT.getFlags()); |
173 | EXPECT_EQ(&UUID, CT.getRawIdentifier()); |
174 | } |
175 | |
176 | } // end namespace |
177 | |