1 | //===- llvm/unittest/IR/AttributesTest.cpp - Attributes unit 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/IR/Attributes.h" |
10 | #include "llvm/AsmParser/Parser.h" |
11 | #include "llvm/IR/AttributeMask.h" |
12 | #include "llvm/IR/DerivedTypes.h" |
13 | #include "llvm/IR/InstrTypes.h" |
14 | #include "llvm/IR/LLVMContext.h" |
15 | #include "llvm/IR/Module.h" |
16 | #include "llvm/Support/SourceMgr.h" |
17 | #include "gtest/gtest.h" |
18 | using namespace llvm; |
19 | |
20 | namespace { |
21 | |
22 | TEST(Attributes, Uniquing) { |
23 | LLVMContext C; |
24 | |
25 | Attribute AttrA = Attribute::get(C, Attribute::AlwaysInline); |
26 | Attribute AttrB = Attribute::get(C, Attribute::AlwaysInline); |
27 | EXPECT_EQ(AttrA, AttrB); |
28 | |
29 | AttributeList ASs[] = {AttributeList::get(C, 1, Attribute::ZExt), |
30 | AttributeList::get(C, 2, Attribute::SExt)}; |
31 | |
32 | AttributeList SetA = AttributeList::get(C, ASs); |
33 | AttributeList SetB = AttributeList::get(C, ASs); |
34 | EXPECT_EQ(SetA, SetB); |
35 | } |
36 | |
37 | TEST(Attributes, Ordering) { |
38 | LLVMContext C; |
39 | |
40 | Attribute Align4 = Attribute::get(C, Attribute::Alignment, 4); |
41 | Attribute Align5 = Attribute::get(C, Attribute::Alignment, 5); |
42 | Attribute Deref4 = Attribute::get(C, Attribute::Dereferenceable, 4); |
43 | Attribute Deref5 = Attribute::get(C, Attribute::Dereferenceable, 5); |
44 | EXPECT_TRUE(Align4 < Align5); |
45 | EXPECT_TRUE(Align4 < Deref4); |
46 | EXPECT_TRUE(Align4 < Deref5); |
47 | EXPECT_TRUE(Align5 < Deref4); |
48 | |
49 | Attribute ByVal = Attribute::get(C, Attribute::ByVal, Type::getInt32Ty(C)); |
50 | EXPECT_FALSE(ByVal < Attribute::get(C, Attribute::ZExt)); |
51 | EXPECT_TRUE(ByVal < Align4); |
52 | EXPECT_FALSE(ByVal < ByVal); |
53 | |
54 | AttributeList ASs[] = {AttributeList::get(C, 2, Attribute::ZExt), |
55 | AttributeList::get(C, 1, Attribute::SExt)}; |
56 | |
57 | AttributeList SetA = AttributeList::get(C, ASs); |
58 | AttributeList SetB = |
59 | SetA.removeParamAttributes(C, ArgNo: 0, AttrsToRemove: ASs[1].getParamAttrs(ArgNo: 0)); |
60 | EXPECT_NE(SetA, SetB); |
61 | } |
62 | |
63 | TEST(Attributes, AddAttributes) { |
64 | LLVMContext C; |
65 | AttributeList AL; |
66 | AttrBuilder B(C); |
67 | B.addAttribute(Attribute::NoReturn); |
68 | AL = AL.addFnAttributes(C, B: AttrBuilder(C, AttributeSet::get(C, B))); |
69 | EXPECT_TRUE(AL.hasFnAttr(Attribute::NoReturn)); |
70 | B.clear(); |
71 | B.addAttribute(Attribute::SExt); |
72 | AL = AL.addRetAttributes(C, B); |
73 | EXPECT_TRUE(AL.hasRetAttr(Attribute::SExt)); |
74 | EXPECT_TRUE(AL.hasFnAttr(Attribute::NoReturn)); |
75 | } |
76 | |
77 | TEST(Attributes, RemoveAlign) { |
78 | LLVMContext C; |
79 | |
80 | Attribute AlignAttr = Attribute::getWithAlignment(Context&: C, Alignment: Align(8)); |
81 | Attribute StackAlignAttr = Attribute::getWithStackAlignment(Context&: C, Alignment: Align(32)); |
82 | AttrBuilder B_align_readonly(C); |
83 | B_align_readonly.addAttribute(A: AlignAttr); |
84 | B_align_readonly.addAttribute(Attribute::ReadOnly); |
85 | AttributeMask B_align; |
86 | B_align.addAttribute(A: AlignAttr); |
87 | AttrBuilder B_stackalign_optnone(C); |
88 | B_stackalign_optnone.addAttribute(A: StackAlignAttr); |
89 | B_stackalign_optnone.addAttribute(Attribute::OptimizeNone); |
90 | AttributeMask B_stackalign; |
91 | B_stackalign.addAttribute(A: StackAlignAttr); |
92 | |
93 | AttributeSet AS = AttributeSet::get(C, B: B_align_readonly); |
94 | EXPECT_TRUE(AS.getAlignment() == MaybeAlign(8)); |
95 | EXPECT_TRUE(AS.hasAttribute(Attribute::ReadOnly)); |
96 | AS = AS.removeAttribute(C, Attribute::Alignment); |
97 | EXPECT_FALSE(AS.hasAttribute(Attribute::Alignment)); |
98 | EXPECT_TRUE(AS.hasAttribute(Attribute::ReadOnly)); |
99 | AS = AttributeSet::get(C, B: B_align_readonly); |
100 | AS = AS.removeAttributes(C, AttrsToRemove: B_align); |
101 | EXPECT_TRUE(AS.getAlignment() == std::nullopt); |
102 | EXPECT_TRUE(AS.hasAttribute(Attribute::ReadOnly)); |
103 | |
104 | AttributeList AL; |
105 | AL = AL.addParamAttributes(C, ArgNo: 0, B: B_align_readonly); |
106 | AL = AL.addRetAttributes(C, B: B_stackalign_optnone); |
107 | EXPECT_TRUE(AL.hasRetAttrs()); |
108 | EXPECT_TRUE(AL.hasRetAttr(Attribute::StackAlignment)); |
109 | EXPECT_TRUE(AL.hasRetAttr(Attribute::OptimizeNone)); |
110 | EXPECT_TRUE(AL.getRetStackAlignment() == MaybeAlign(32)); |
111 | EXPECT_TRUE(AL.hasParamAttrs(0)); |
112 | EXPECT_TRUE(AL.hasParamAttr(0, Attribute::Alignment)); |
113 | EXPECT_TRUE(AL.hasParamAttr(0, Attribute::ReadOnly)); |
114 | EXPECT_TRUE(AL.getParamAlignment(0) == MaybeAlign(8)); |
115 | |
116 | AL = AL.removeParamAttribute(C, 0, Attribute::Alignment); |
117 | EXPECT_FALSE(AL.hasParamAttr(0, Attribute::Alignment)); |
118 | EXPECT_TRUE(AL.hasParamAttr(0, Attribute::ReadOnly)); |
119 | EXPECT_TRUE(AL.hasRetAttr(Attribute::StackAlignment)); |
120 | EXPECT_TRUE(AL.hasRetAttr(Attribute::OptimizeNone)); |
121 | EXPECT_TRUE(AL.getRetStackAlignment() == MaybeAlign(32)); |
122 | |
123 | AL = AL.removeRetAttribute(C, Attribute::StackAlignment); |
124 | EXPECT_FALSE(AL.hasParamAttr(0, Attribute::Alignment)); |
125 | EXPECT_TRUE(AL.hasParamAttr(0, Attribute::ReadOnly)); |
126 | EXPECT_FALSE(AL.hasRetAttr(Attribute::StackAlignment)); |
127 | EXPECT_TRUE(AL.hasRetAttr(Attribute::OptimizeNone)); |
128 | |
129 | AttributeList AL2; |
130 | AL2 = AL2.addParamAttributes(C, ArgNo: 0, B: B_align_readonly); |
131 | AL2 = AL2.addRetAttributes(C, B: B_stackalign_optnone); |
132 | |
133 | AL2 = AL2.removeParamAttributes(C, ArgNo: 0, AttrsToRemove: B_align); |
134 | EXPECT_FALSE(AL2.hasParamAttr(0, Attribute::Alignment)); |
135 | EXPECT_TRUE(AL2.hasParamAttr(0, Attribute::ReadOnly)); |
136 | EXPECT_TRUE(AL2.hasRetAttr(Attribute::StackAlignment)); |
137 | EXPECT_TRUE(AL2.hasRetAttr(Attribute::OptimizeNone)); |
138 | EXPECT_TRUE(AL2.getRetStackAlignment() == MaybeAlign(32)); |
139 | |
140 | AL2 = AL2.removeRetAttributes(C, AttrsToRemove: B_stackalign); |
141 | EXPECT_FALSE(AL2.hasParamAttr(0, Attribute::Alignment)); |
142 | EXPECT_TRUE(AL2.hasParamAttr(0, Attribute::ReadOnly)); |
143 | EXPECT_FALSE(AL2.hasRetAttr(Attribute::StackAlignment)); |
144 | EXPECT_TRUE(AL2.hasRetAttr(Attribute::OptimizeNone)); |
145 | } |
146 | |
147 | TEST(Attributes, AddMatchingAlignAttr) { |
148 | LLVMContext C; |
149 | AttributeList AL; |
150 | AL = AL.addParamAttribute(C, ArgNos: 0, A: Attribute::getWithAlignment(Context&: C, Alignment: Align(8))); |
151 | AL = AL.addParamAttribute(C, ArgNos: 1, A: Attribute::getWithAlignment(Context&: C, Alignment: Align(32))); |
152 | EXPECT_EQ(Align(8), AL.getParamAlignment(0)); |
153 | EXPECT_EQ(Align(32), AL.getParamAlignment(1)); |
154 | |
155 | AttrBuilder B(C); |
156 | B.addAttribute(Attribute::NonNull); |
157 | B.addAlignmentAttr(Align: 8); |
158 | AL = AL.addParamAttributes(C, ArgNo: 0, B); |
159 | EXPECT_EQ(Align(8), AL.getParamAlignment(0)); |
160 | EXPECT_EQ(Align(32), AL.getParamAlignment(1)); |
161 | EXPECT_TRUE(AL.hasParamAttr(0, Attribute::NonNull)); |
162 | } |
163 | |
164 | TEST(Attributes, EmptyGet) { |
165 | LLVMContext C; |
166 | AttributeList EmptyLists[] = {AttributeList(), AttributeList()}; |
167 | AttributeList AL = AttributeList::get(C, Attrs: EmptyLists); |
168 | EXPECT_TRUE(AL.isEmpty()); |
169 | } |
170 | |
171 | TEST(Attributes, OverflowGet) { |
172 | LLVMContext C; |
173 | std::pair<unsigned, Attribute> Attrs[] = { { AttributeList::ReturnIndex, Attribute::get(C, Attribute::SExt) }, |
174 | { AttributeList::FunctionIndex, Attribute::get(C, Attribute::ReadOnly) } }; |
175 | AttributeList AL = AttributeList::get(C, Attrs); |
176 | EXPECT_EQ(2U, AL.getNumAttrSets()); |
177 | } |
178 | |
179 | TEST(Attributes, StringRepresentation) { |
180 | LLVMContext C; |
181 | StructType *Ty = StructType::create(Elements: Type::getInt32Ty(C), Name: "mystruct" ); |
182 | |
183 | // Insufficiently careful printing can result in byval(%mystruct = { i32 }) |
184 | Attribute A = Attribute::getWithByValType(Context&: C, Ty); |
185 | EXPECT_EQ(A.getAsString(), "byval(%mystruct)" ); |
186 | |
187 | A = Attribute::getWithByValType(Context&: C, Ty: Type::getInt32Ty(C)); |
188 | EXPECT_EQ(A.getAsString(), "byval(i32)" ); |
189 | } |
190 | |
191 | TEST(Attributes, HasParentContext) { |
192 | LLVMContext C1, C2; |
193 | |
194 | { |
195 | Attribute Attr1 = Attribute::get(C1, Attribute::AlwaysInline); |
196 | Attribute Attr2 = Attribute::get(C2, Attribute::AlwaysInline); |
197 | EXPECT_TRUE(Attr1.hasParentContext(C1)); |
198 | EXPECT_FALSE(Attr1.hasParentContext(C2)); |
199 | EXPECT_FALSE(Attr2.hasParentContext(C1)); |
200 | EXPECT_TRUE(Attr2.hasParentContext(C2)); |
201 | } |
202 | |
203 | { |
204 | AttributeSet AS1 = AttributeSet::get( |
205 | C1, ArrayRef(Attribute::get(C1, Attribute::NoReturn))); |
206 | AttributeSet AS2 = AttributeSet::get( |
207 | C2, ArrayRef(Attribute::get(C2, Attribute::NoReturn))); |
208 | EXPECT_TRUE(AS1.hasParentContext(C1)); |
209 | EXPECT_FALSE(AS1.hasParentContext(C2)); |
210 | EXPECT_FALSE(AS2.hasParentContext(C1)); |
211 | EXPECT_TRUE(AS2.hasParentContext(C2)); |
212 | } |
213 | |
214 | { |
215 | AttributeList AL1 = AttributeList::get(C1, 1, Attribute::ZExt); |
216 | AttributeList AL2 = AttributeList::get(C2, 1, Attribute::ZExt); |
217 | EXPECT_TRUE(AL1.hasParentContext(C1)); |
218 | EXPECT_FALSE(AL1.hasParentContext(C2)); |
219 | EXPECT_FALSE(AL2.hasParentContext(C1)); |
220 | EXPECT_TRUE(AL2.hasParentContext(C2)); |
221 | } |
222 | } |
223 | |
224 | TEST(Attributes, AttributeListPrinting) { |
225 | LLVMContext C; |
226 | |
227 | { |
228 | std::string S; |
229 | raw_string_ostream OS(S); |
230 | AttributeList AL; |
231 | AL.addFnAttribute(C, Attribute::AlwaysInline).print(OS); |
232 | EXPECT_EQ(S, "AttributeList[\n" |
233 | " { function => alwaysinline }\n" |
234 | "]\n" ); |
235 | } |
236 | |
237 | { |
238 | std::string S; |
239 | raw_string_ostream OS(S); |
240 | AttributeList AL; |
241 | AL.addRetAttribute(C, Attribute::SExt).print(OS); |
242 | EXPECT_EQ(S, "AttributeList[\n" |
243 | " { return => signext }\n" |
244 | "]\n" ); |
245 | } |
246 | |
247 | { |
248 | std::string S; |
249 | raw_string_ostream OS(S); |
250 | AttributeList AL; |
251 | AL.addParamAttribute(C, 5, Attribute::ZExt).print(OS); |
252 | EXPECT_EQ(S, "AttributeList[\n" |
253 | " { arg(5) => zeroext }\n" |
254 | "]\n" ); |
255 | } |
256 | } |
257 | |
258 | TEST(Attributes, MismatchedABIAttrs) { |
259 | const char *IRString = R"IR( |
260 | declare void @f1(i32* byval(i32)) |
261 | define void @g() { |
262 | call void @f1(i32* null) |
263 | ret void |
264 | } |
265 | declare void @f2(i32* preallocated(i32)) |
266 | define void @h() { |
267 | call void @f2(i32* null) |
268 | ret void |
269 | } |
270 | declare void @f3(i32* inalloca(i32)) |
271 | define void @i() { |
272 | call void @f3(i32* null) |
273 | ret void |
274 | } |
275 | )IR" ; |
276 | |
277 | SMDiagnostic Err; |
278 | LLVMContext Context; |
279 | std::unique_ptr<Module> M = parseAssemblyString(AsmString: IRString, Err, Context); |
280 | ASSERT_TRUE(M); |
281 | |
282 | { |
283 | auto *I = cast<CallBase>(Val: &M->getFunction(Name: "g" )->getEntryBlock().front()); |
284 | ASSERT_TRUE(I->isByValArgument(0)); |
285 | ASSERT_TRUE(I->getParamByValType(0)); |
286 | } |
287 | { |
288 | auto *I = cast<CallBase>(Val: &M->getFunction(Name: "h" )->getEntryBlock().front()); |
289 | ASSERT_TRUE(I->getParamPreallocatedType(0)); |
290 | } |
291 | { |
292 | auto *I = cast<CallBase>(Val: &M->getFunction(Name: "i" )->getEntryBlock().front()); |
293 | ASSERT_TRUE(I->isInAllocaArgument(0)); |
294 | ASSERT_TRUE(I->getParamInAllocaType(0)); |
295 | } |
296 | } |
297 | |
298 | TEST(Attributes, RemoveParamAttributes) { |
299 | LLVMContext C; |
300 | AttributeList AL; |
301 | AL = AL.addParamAttribute(C, 1, Attribute::NoUndef); |
302 | EXPECT_EQ(AL.getNumAttrSets(), 4U); |
303 | AL = AL.addParamAttribute(C, 3, Attribute::NonNull); |
304 | EXPECT_EQ(AL.getNumAttrSets(), 6U); |
305 | AL = AL.removeParamAttributes(C, ArgNo: 3); |
306 | EXPECT_EQ(AL.getNumAttrSets(), 4U); |
307 | AL = AL.removeParamAttribute(C, 1, Attribute::NoUndef); |
308 | EXPECT_EQ(AL.getNumAttrSets(), 0U); |
309 | } |
310 | |
311 | } // end anonymous namespace |
312 | |