1 | //===- llvm/unittest/AsmParser/AsmParserTest.cpp - asm parser unittests ---===// |
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/ADT/StringRef.h" |
10 | #include "llvm/AsmParser/Parser.h" |
11 | #include "llvm/AsmParser/SlotMapping.h" |
12 | #include "llvm/IR/Constants.h" |
13 | #include "llvm/IR/DataLayout.h" |
14 | #include "llvm/IR/LLVMContext.h" |
15 | #include "llvm/IR/Module.h" |
16 | #include "llvm/Support/Error.h" |
17 | #include "llvm/Support/SourceMgr.h" |
18 | #include "gtest/gtest.h" |
19 | |
20 | using namespace llvm; |
21 | |
22 | namespace { |
23 | |
24 | TEST(AsmParserTest, NullTerminatedInput) { |
25 | LLVMContext Ctx; |
26 | StringRef Source = "; Empty module \n" ; |
27 | SMDiagnostic Error; |
28 | auto Mod = parseAssemblyString(AsmString: Source, Err&: Error, Context&: Ctx); |
29 | |
30 | EXPECT_TRUE(Mod != nullptr); |
31 | EXPECT_TRUE(Error.getMessage().empty()); |
32 | } |
33 | |
34 | #ifdef GTEST_HAS_DEATH_TEST |
35 | #ifndef NDEBUG |
36 | |
37 | TEST(AsmParserTest, NonNullTerminatedInput) { |
38 | LLVMContext Ctx; |
39 | StringRef Source = "; Empty module \n\1\2" ; |
40 | SMDiagnostic Error; |
41 | std::unique_ptr<Module> Mod; |
42 | EXPECT_DEATH(Mod = parseAssemblyString(Source.substr(0, Source.size() - 2), |
43 | Error, Ctx), |
44 | "Buffer is not null terminated!" ); |
45 | } |
46 | |
47 | #endif |
48 | #endif |
49 | |
50 | TEST(AsmParserTest, SlotMappingTest) { |
51 | LLVMContext Ctx; |
52 | StringRef Source = "@0 = global i32 0\n !0 = !{}\n !42 = !{i32 42}" ; |
53 | SMDiagnostic Error; |
54 | SlotMapping Mapping; |
55 | auto Mod = parseAssemblyString(AsmString: Source, Err&: Error, Context&: Ctx, Slots: &Mapping); |
56 | |
57 | EXPECT_TRUE(Mod != nullptr); |
58 | EXPECT_TRUE(Error.getMessage().empty()); |
59 | |
60 | ASSERT_EQ(Mapping.GlobalValues.getNext(), 1u); |
61 | EXPECT_TRUE(isa<GlobalVariable>(Mapping.GlobalValues.get(0))); |
62 | |
63 | EXPECT_EQ(Mapping.MetadataNodes.size(), 2u); |
64 | EXPECT_EQ(Mapping.MetadataNodes.count(0), 1u); |
65 | EXPECT_EQ(Mapping.MetadataNodes.count(42), 1u); |
66 | EXPECT_EQ(Mapping.MetadataNodes.count(1), 0u); |
67 | } |
68 | |
69 | TEST(AsmParserTest, TypeAndConstantValueParsing) { |
70 | LLVMContext Ctx; |
71 | SMDiagnostic Error; |
72 | StringRef Source = "define void @test() {\n entry:\n ret void\n}" ; |
73 | auto Mod = parseAssemblyString(AsmString: Source, Err&: Error, Context&: Ctx); |
74 | ASSERT_TRUE(Mod != nullptr); |
75 | auto &M = *Mod; |
76 | |
77 | const Value *V; |
78 | V = parseConstantValue(Asm: "double 3.5" , Err&: Error, M); |
79 | ASSERT_TRUE(V); |
80 | EXPECT_TRUE(V->getType()->isDoubleTy()); |
81 | ASSERT_TRUE(isa<ConstantFP>(V)); |
82 | EXPECT_TRUE(cast<ConstantFP>(V)->isExactlyValue(3.5)); |
83 | |
84 | V = parseConstantValue(Asm: "i32 42" , Err&: Error, M); |
85 | ASSERT_TRUE(V); |
86 | EXPECT_TRUE(V->getType()->isIntegerTy()); |
87 | ASSERT_TRUE(isa<ConstantInt>(V)); |
88 | EXPECT_TRUE(cast<ConstantInt>(V)->equalsInt(42)); |
89 | |
90 | V = parseConstantValue(Asm: "<4 x i32> <i32 0, i32 1, i32 2, i32 3>" , Err&: Error, M); |
91 | ASSERT_TRUE(V); |
92 | EXPECT_TRUE(V->getType()->isVectorTy()); |
93 | ASSERT_TRUE(isa<ConstantDataVector>(V)); |
94 | |
95 | V = parseConstantValue(Asm: "i32 add (i32 1, i32 2)" , Err&: Error, M); |
96 | ASSERT_TRUE(V); |
97 | ASSERT_TRUE(isa<ConstantInt>(V)); |
98 | |
99 | V = parseConstantValue(Asm: "ptr blockaddress(@test, %entry)" , Err&: Error, M); |
100 | ASSERT_TRUE(V); |
101 | ASSERT_TRUE(isa<BlockAddress>(V)); |
102 | |
103 | V = parseConstantValue(Asm: "ptr undef" , Err&: Error, M); |
104 | ASSERT_TRUE(V); |
105 | ASSERT_TRUE(isa<UndefValue>(V)); |
106 | |
107 | EXPECT_FALSE(parseConstantValue("duble 3.25" , Error, M)); |
108 | EXPECT_EQ(Error.getMessage(), "expected type" ); |
109 | |
110 | EXPECT_FALSE(parseConstantValue("i32 3.25" , Error, M)); |
111 | EXPECT_EQ(Error.getMessage(), "floating point constant invalid for type" ); |
112 | |
113 | EXPECT_FALSE(parseConstantValue("ptr @foo" , Error, M)); |
114 | EXPECT_EQ(Error.getMessage(), "expected a constant value" ); |
115 | |
116 | EXPECT_FALSE(parseConstantValue("i32 3, " , Error, M)); |
117 | EXPECT_EQ(Error.getMessage(), "expected end of string" ); |
118 | } |
119 | |
120 | TEST(AsmParserTest, TypeAndConstantValueWithSlotMappingParsing) { |
121 | LLVMContext Ctx; |
122 | SMDiagnostic Error; |
123 | StringRef Source = |
124 | "%st = type { i32, i32 }\n" |
125 | "@v = common global [50 x %st] zeroinitializer, align 16\n" |
126 | "%0 = type { i32, i32, i32, i32 }\n" |
127 | "@g = common global [50 x %0] zeroinitializer, align 16\n" |
128 | "define void @marker4(i64 %d) {\n" |
129 | "entry:\n" |
130 | " %conv = trunc i64 %d to i32\n" |
131 | " store i32 %conv, ptr getelementptr inbounds " |
132 | " ([50 x %st], ptr @v, i64 0, i64 1, i32 0), align 16\n" |
133 | " store i32 %conv, ptr getelementptr inbounds " |
134 | " ([50 x %0], ptr @g, i64 0, i64 1, i32 0), align 16\n" |
135 | " ret void\n" |
136 | "}" ; |
137 | SlotMapping Mapping; |
138 | auto Mod = parseAssemblyString(AsmString: Source, Err&: Error, Context&: Ctx, Slots: &Mapping); |
139 | ASSERT_TRUE(Mod != nullptr); |
140 | auto &M = *Mod; |
141 | |
142 | const Value *V; |
143 | V = parseConstantValue(Asm: "ptr getelementptr inbounds ([50 x %st], ptr " |
144 | "@v, i64 0, i64 1, i32 0)" , |
145 | Err&: Error, M, Slots: &Mapping); |
146 | ASSERT_TRUE(V); |
147 | ASSERT_TRUE(isa<ConstantExpr>(V)); |
148 | |
149 | V = parseConstantValue(Asm: "ptr getelementptr inbounds ([50 x %0], ptr " |
150 | "@g, i64 0, i64 1, i32 0)" , |
151 | Err&: Error, M, Slots: &Mapping); |
152 | ASSERT_TRUE(V); |
153 | ASSERT_TRUE(isa<ConstantExpr>(V)); |
154 | } |
155 | |
156 | TEST(AsmParserTest, TypeWithSlotMappingParsing) { |
157 | LLVMContext Ctx; |
158 | SMDiagnostic Error; |
159 | StringRef Source = |
160 | "%st = type { i32, i32 }\n" |
161 | "@v = common global [50 x %st] zeroinitializer, align 16\n" |
162 | "%0 = type { i32, i32, i32, i32 }\n" |
163 | "@g = common global [50 x %0] zeroinitializer, align 16\n" |
164 | "define void @marker4(i64 %d) {\n" |
165 | "entry:\n" |
166 | " %conv = trunc i64 %d to i32\n" |
167 | " store i32 %conv, ptr getelementptr inbounds " |
168 | " ([50 x %st], ptr @v, i64 0, i64 0, i32 0), align 16\n" |
169 | " store i32 %conv, ptr getelementptr inbounds " |
170 | " ([50 x %0], ptr @g, i64 0, i64 0, i32 0), align 16\n" |
171 | " ret void\n" |
172 | "}" ; |
173 | SlotMapping Mapping; |
174 | auto Mod = parseAssemblyString(AsmString: Source, Err&: Error, Context&: Ctx, Slots: &Mapping); |
175 | ASSERT_TRUE(Mod != nullptr); |
176 | auto &M = *Mod; |
177 | |
178 | // Check we properly parse integer types. |
179 | Type *Ty; |
180 | Ty = parseType(Asm: "i32" , Err&: Error, M, Slots: &Mapping); |
181 | ASSERT_TRUE(Ty); |
182 | ASSERT_TRUE(Ty->isIntegerTy()); |
183 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); |
184 | |
185 | // Check we properly parse integer types with exotic size. |
186 | Ty = parseType(Asm: "i13" , Err&: Error, M, Slots: &Mapping); |
187 | ASSERT_TRUE(Ty); |
188 | ASSERT_TRUE(Ty->isIntegerTy()); |
189 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 13); |
190 | |
191 | // Check we properly parse floating point types. |
192 | Ty = parseType(Asm: "float" , Err&: Error, M, Slots: &Mapping); |
193 | ASSERT_TRUE(Ty); |
194 | ASSERT_TRUE(Ty->isFloatTy()); |
195 | |
196 | Ty = parseType(Asm: "double" , Err&: Error, M, Slots: &Mapping); |
197 | ASSERT_TRUE(Ty); |
198 | ASSERT_TRUE(Ty->isDoubleTy()); |
199 | |
200 | // Check we properly parse struct types. |
201 | // Named struct. |
202 | Ty = parseType(Asm: "%st" , Err&: Error, M, Slots: &Mapping); |
203 | ASSERT_TRUE(Ty); |
204 | ASSERT_TRUE(Ty->isStructTy()); |
205 | |
206 | // Check the details of the struct. |
207 | StructType *ST = cast<StructType>(Val: Ty); |
208 | ASSERT_TRUE(ST->getNumElements() == 2); |
209 | for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) { |
210 | Ty = ST->getElementType(N: i); |
211 | ASSERT_TRUE(Ty->isIntegerTy()); |
212 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); |
213 | } |
214 | |
215 | // Anonymous struct. |
216 | Ty = parseType(Asm: "%0" , Err&: Error, M, Slots: &Mapping); |
217 | ASSERT_TRUE(Ty); |
218 | ASSERT_TRUE(Ty->isStructTy()); |
219 | |
220 | // Check the details of the struct. |
221 | ST = cast<StructType>(Val: Ty); |
222 | ASSERT_TRUE(ST->getNumElements() == 4); |
223 | for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) { |
224 | Ty = ST->getElementType(N: i); |
225 | ASSERT_TRUE(Ty->isIntegerTy()); |
226 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); |
227 | } |
228 | |
229 | // Check we properly parse vector types. |
230 | Ty = parseType(Asm: "<5 x i32>" , Err&: Error, M, Slots: &Mapping); |
231 | ASSERT_TRUE(Ty); |
232 | ASSERT_TRUE(Ty->isVectorTy()); |
233 | |
234 | // Check the details of the vector. |
235 | auto *VT = cast<FixedVectorType>(Val: Ty); |
236 | ASSERT_TRUE(VT->getNumElements() == 5); |
237 | ASSERT_TRUE(VT->getPrimitiveSizeInBits().getFixedValue() == 160); |
238 | Ty = VT->getElementType(); |
239 | ASSERT_TRUE(Ty->isIntegerTy()); |
240 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); |
241 | |
242 | // Opaque struct. |
243 | Ty = parseType(Asm: "%opaque" , Err&: Error, M, Slots: &Mapping); |
244 | ASSERT_TRUE(Ty); |
245 | ASSERT_TRUE(Ty->isStructTy()); |
246 | |
247 | ST = cast<StructType>(Val: Ty); |
248 | ASSERT_TRUE(ST->isOpaque()); |
249 | |
250 | // Check we properly parse pointer types. |
251 | Ty = parseType(Asm: "ptr" , Err&: Error, M, Slots: &Mapping); |
252 | ASSERT_TRUE(Ty); |
253 | ASSERT_TRUE(Ty->isPointerTy()); |
254 | |
255 | // Check that we reject types with garbage. |
256 | Ty = parseType(Asm: "i32 garbage" , Err&: Error, M, Slots: &Mapping); |
257 | ASSERT_TRUE(!Ty); |
258 | } |
259 | |
260 | TEST(AsmParserTest, TypeAtBeginningWithSlotMappingParsing) { |
261 | LLVMContext Ctx; |
262 | SMDiagnostic Error; |
263 | StringRef Source = |
264 | "%st = type { i32, i32 }\n" |
265 | "@v = common global [50 x %st] zeroinitializer, align 16\n" |
266 | "%0 = type { i32, i32, i32, i32 }\n" |
267 | "@g = common global [50 x %0] zeroinitializer, align 16\n" |
268 | "define void @marker4(i64 %d) {\n" |
269 | "entry:\n" |
270 | " %conv = trunc i64 %d to i32\n" |
271 | " store i32 %conv, ptr getelementptr inbounds " |
272 | " ([50 x %st], ptr @v, i64 0, i64 0, i32 0), align 16\n" |
273 | " store i32 %conv, ptr getelementptr inbounds " |
274 | " ([50 x %0], ptr @g, i64 0, i64 0, i32 0), align 16\n" |
275 | " ret void\n" |
276 | "}" ; |
277 | SlotMapping Mapping; |
278 | auto Mod = parseAssemblyString(AsmString: Source, Err&: Error, Context&: Ctx, Slots: &Mapping); |
279 | ASSERT_TRUE(Mod != nullptr); |
280 | auto &M = *Mod; |
281 | unsigned Read; |
282 | |
283 | // Check we properly parse integer types. |
284 | Type *Ty; |
285 | Ty = parseTypeAtBeginning(Asm: "i32" , Read, Err&: Error, M, Slots: &Mapping); |
286 | ASSERT_TRUE(Ty); |
287 | ASSERT_TRUE(Ty->isIntegerTy()); |
288 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); |
289 | ASSERT_TRUE(Read == 3); |
290 | |
291 | // Check we properly parse integer types with exotic size. |
292 | Ty = parseTypeAtBeginning(Asm: "i13" , Read, Err&: Error, M, Slots: &Mapping); |
293 | ASSERT_TRUE(Ty); |
294 | ASSERT_TRUE(Ty->isIntegerTy()); |
295 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 13); |
296 | ASSERT_TRUE(Read == 3); |
297 | |
298 | // Check we properly parse floating point types. |
299 | Ty = parseTypeAtBeginning(Asm: "float" , Read, Err&: Error, M, Slots: &Mapping); |
300 | ASSERT_TRUE(Ty); |
301 | ASSERT_TRUE(Ty->isFloatTy()); |
302 | ASSERT_TRUE(Read == 5); |
303 | |
304 | Ty = parseTypeAtBeginning(Asm: "double" , Read, Err&: Error, M, Slots: &Mapping); |
305 | ASSERT_TRUE(Ty); |
306 | ASSERT_TRUE(Ty->isDoubleTy()); |
307 | ASSERT_TRUE(Read == 6); |
308 | |
309 | // Check we properly parse struct types. |
310 | // Named struct. |
311 | Ty = parseTypeAtBeginning(Asm: "%st" , Read, Err&: Error, M, Slots: &Mapping); |
312 | ASSERT_TRUE(Ty); |
313 | ASSERT_TRUE(Ty->isStructTy()); |
314 | ASSERT_TRUE(Read == 3); |
315 | |
316 | // Check the details of the struct. |
317 | StructType *ST = cast<StructType>(Val: Ty); |
318 | ASSERT_TRUE(ST->getNumElements() == 2); |
319 | for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) { |
320 | Ty = ST->getElementType(N: i); |
321 | ASSERT_TRUE(Ty->isIntegerTy()); |
322 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); |
323 | } |
324 | |
325 | // Anonymous struct. |
326 | Ty = parseTypeAtBeginning(Asm: "%0" , Read, Err&: Error, M, Slots: &Mapping); |
327 | ASSERT_TRUE(Ty); |
328 | ASSERT_TRUE(Ty->isStructTy()); |
329 | ASSERT_TRUE(Read == 2); |
330 | |
331 | // Check the details of the struct. |
332 | ST = cast<StructType>(Val: Ty); |
333 | ASSERT_TRUE(ST->getNumElements() == 4); |
334 | for (unsigned i = 0, e = ST->getNumElements(); i != e; ++i) { |
335 | Ty = ST->getElementType(N: i); |
336 | ASSERT_TRUE(Ty->isIntegerTy()); |
337 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); |
338 | } |
339 | |
340 | // Check we properly parse vector types. |
341 | Ty = parseTypeAtBeginning(Asm: "<5 x i32>" , Read, Err&: Error, M, Slots: &Mapping); |
342 | ASSERT_TRUE(Ty); |
343 | ASSERT_TRUE(Ty->isVectorTy()); |
344 | ASSERT_TRUE(Read == 9); |
345 | |
346 | // Check the details of the vector. |
347 | auto *VT = cast<FixedVectorType>(Val: Ty); |
348 | ASSERT_TRUE(VT->getNumElements() == 5); |
349 | ASSERT_TRUE(VT->getPrimitiveSizeInBits().getFixedValue() == 160); |
350 | Ty = VT->getElementType(); |
351 | ASSERT_TRUE(Ty->isIntegerTy()); |
352 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); |
353 | |
354 | // Opaque struct. |
355 | Ty = parseTypeAtBeginning(Asm: "%opaque" , Read, Err&: Error, M, Slots: &Mapping); |
356 | ASSERT_TRUE(Ty); |
357 | ASSERT_TRUE(Ty->isStructTy()); |
358 | ASSERT_TRUE(Read == 7); |
359 | |
360 | ST = cast<StructType>(Val: Ty); |
361 | ASSERT_TRUE(ST->isOpaque()); |
362 | |
363 | // Check we properly parse pointer types. |
364 | // One indirection. |
365 | Ty = parseTypeAtBeginning(Asm: "ptr" , Read, Err&: Error, M, Slots: &Mapping); |
366 | ASSERT_TRUE(Ty); |
367 | ASSERT_TRUE(Ty->isPointerTy()); |
368 | ASSERT_TRUE(Read == 3); |
369 | |
370 | // Check that we reject types with garbage. |
371 | Ty = parseTypeAtBeginning(Asm: "i32 garbage" , Read, Err&: Error, M, Slots: &Mapping); |
372 | ASSERT_TRUE(Ty); |
373 | ASSERT_TRUE(Ty->isIntegerTy()); |
374 | ASSERT_TRUE(Ty->getPrimitiveSizeInBits() == 32); |
375 | // We go to the next token, i.e., we read "i32" + ' '. |
376 | ASSERT_TRUE(Read == 4); |
377 | } |
378 | |
379 | TEST(AsmParserTest, InvalidDataLayoutStringCallback) { |
380 | LLVMContext Ctx; |
381 | SMDiagnostic Error; |
382 | // Note the invalid i8:7 part |
383 | // Overalign i32 as marker so we can check that indeed this DL was used, |
384 | // and not some default. |
385 | StringRef InvalidDLStr = |
386 | "e-m:e-p:64:64-i8:7-i16:16-i32:64-i64:64-f80:128-n8:16:32:64" ; |
387 | StringRef FixedDLStr = |
388 | "e-m:e-p:64:64-i8:8-i16:16-i32:64-i64:64-f80:128-n8:16:32:64" ; |
389 | Expected<DataLayout> ExpectedFixedDL = DataLayout::parse(LayoutDescription: FixedDLStr); |
390 | ASSERT_TRUE(!ExpectedFixedDL.takeError()); |
391 | DataLayout FixedDL = ExpectedFixedDL.get(); |
392 | std::string Source = ("target datalayout = \"" + InvalidDLStr + "\"\n" ).str(); |
393 | MemoryBufferRef SourceBuffer(Source, "<string>" ); |
394 | |
395 | // Check that we reject the source without a DL override. |
396 | SlotMapping Mapping1; |
397 | auto Mod1 = parseAssembly(F: SourceBuffer, Err&: Error, Context&: Ctx, Slots: &Mapping1); |
398 | EXPECT_TRUE(Mod1 == nullptr); |
399 | |
400 | // Check that we pass the correct DL str to the callback, |
401 | // that fixing the DL str from the callback works, |
402 | // and that the resulting module has the correct DL. |
403 | SlotMapping Mapping2; |
404 | auto Mod2 = parseAssembly( |
405 | F: SourceBuffer, Err&: Error, Context&: Ctx, Slots: &Mapping2, |
406 | DataLayoutCallback: [&](StringRef Triple, StringRef DLStr) -> std::optional<std::string> { |
407 | EXPECT_EQ(DLStr, InvalidDLStr); |
408 | return std::string{FixedDLStr}; |
409 | }); |
410 | ASSERT_TRUE(Mod2 != nullptr); |
411 | EXPECT_EQ(Mod2->getDataLayout(), FixedDL); |
412 | } |
413 | |
414 | } // end anonymous namespace |
415 | |