1 | //===- llvm/unittest/IR/VerifierTest.cpp - Verifier unit tests --*- C++ -*-===// |
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/Verifier.h" |
10 | #include "llvm/IR/Constants.h" |
11 | #include "llvm/IR/DIBuilder.h" |
12 | #include "llvm/IR/DerivedTypes.h" |
13 | #include "llvm/IR/Function.h" |
14 | #include "llvm/IR/GlobalAlias.h" |
15 | #include "llvm/IR/GlobalVariable.h" |
16 | #include "llvm/IR/IRBuilder.h" |
17 | #include "llvm/IR/Instructions.h" |
18 | #include "llvm/IR/LLVMContext.h" |
19 | #include "llvm/IR/Module.h" |
20 | #include "gtest/gtest.h" |
21 | |
22 | namespace llvm { |
23 | namespace { |
24 | |
25 | TEST(VerifierTest, Branch_i1) { |
26 | LLVMContext C; |
27 | Module M("M" , C); |
28 | FunctionType *FTy = FunctionType::get(Result: Type::getVoidTy(C), /*isVarArg=*/false); |
29 | Function *F = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo" , M); |
30 | BasicBlock *Entry = BasicBlock::Create(Context&: C, Name: "entry" , Parent: F); |
31 | BasicBlock *Exit = BasicBlock::Create(Context&: C, Name: "exit" , Parent: F); |
32 | ReturnInst::Create(C, InsertAtEnd: Exit); |
33 | |
34 | // To avoid triggering an assertion in BranchInst::Create, we first create |
35 | // a branch with an 'i1' condition ... |
36 | |
37 | Constant *False = ConstantInt::getFalse(Context&: C); |
38 | BranchInst *BI = BranchInst::Create(IfTrue: Exit, IfFalse: Exit, Cond: False, InsertAtEnd: Entry); |
39 | |
40 | // ... then use setOperand to redirect it to a value of different type. |
41 | |
42 | Constant *Zero32 = ConstantInt::get(Ty: IntegerType::get(C, NumBits: 32), V: 0); |
43 | BI->setOperand(i_nocapture: 0, Val_nocapture: Zero32); |
44 | |
45 | EXPECT_TRUE(verifyFunction(*F)); |
46 | } |
47 | |
48 | TEST(VerifierTest, Freeze) { |
49 | LLVMContext C; |
50 | Module M("M" , C); |
51 | FunctionType *FTy = FunctionType::get(Result: Type::getVoidTy(C), /*isVarArg=*/false); |
52 | Function *F = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo" , M); |
53 | BasicBlock *Entry = BasicBlock::Create(Context&: C, Name: "entry" , Parent: F); |
54 | ReturnInst *RI = ReturnInst::Create(C, InsertAtEnd: Entry); |
55 | |
56 | IntegerType *ITy = IntegerType::get(C, NumBits: 32); |
57 | ConstantInt *CI = ConstantInt::get(Ty: ITy, V: 0); |
58 | |
59 | // Valid type : freeze(<2 x i32>) |
60 | Constant *CV = ConstantVector::getSplat(EC: ElementCount::getFixed(MinVal: 2), Elt: CI); |
61 | FreezeInst *FI_vec = new FreezeInst(CV); |
62 | FI_vec->insertBefore(InsertPos: RI); |
63 | |
64 | EXPECT_FALSE(verifyFunction(*F)); |
65 | |
66 | FI_vec->eraseFromParent(); |
67 | |
68 | // Valid type : freeze(float) |
69 | Constant *CFP = ConstantFP::get(Ty: Type::getDoubleTy(C), V: 0.0); |
70 | FreezeInst *FI_dbl = new FreezeInst(CFP); |
71 | FI_dbl->insertBefore(InsertPos: RI); |
72 | |
73 | EXPECT_FALSE(verifyFunction(*F)); |
74 | |
75 | FI_dbl->eraseFromParent(); |
76 | |
77 | // Valid type : freeze(i32*) |
78 | PointerType *PT = PointerType::get(ElementType: ITy, AddressSpace: 0); |
79 | ConstantPointerNull *CPN = ConstantPointerNull::get(T: PT); |
80 | FreezeInst *FI_ptr = new FreezeInst(CPN); |
81 | FI_ptr->insertBefore(InsertPos: RI); |
82 | |
83 | EXPECT_FALSE(verifyFunction(*F)); |
84 | |
85 | FI_ptr->eraseFromParent(); |
86 | |
87 | // Valid type : freeze(int) |
88 | FreezeInst *FI = new FreezeInst(CI); |
89 | FI->insertBefore(InsertPos: RI); |
90 | |
91 | EXPECT_FALSE(verifyFunction(*F)); |
92 | |
93 | FI->eraseFromParent(); |
94 | } |
95 | |
96 | TEST(VerifierTest, InvalidRetAttribute) { |
97 | LLVMContext C; |
98 | Module M("M" , C); |
99 | FunctionType *FTy = FunctionType::get(Result: Type::getInt32Ty(C), /*isVarArg=*/false); |
100 | Function *F = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo" , M); |
101 | AttributeList AS = F->getAttributes(); |
102 | F->setAttributes(AS.addRetAttribute( |
103 | C, Attr: Attribute::getWithUWTableKind(Context&: C, Kind: UWTableKind::Default))); |
104 | |
105 | std::string Error; |
106 | raw_string_ostream ErrorOS(Error); |
107 | EXPECT_TRUE(verifyModule(M, &ErrorOS)); |
108 | EXPECT_TRUE( |
109 | StringRef(ErrorOS.str()) |
110 | .starts_with( |
111 | "Attribute 'uwtable' does not apply to function return values" )); |
112 | } |
113 | |
114 | /// Test the verifier rejects invalid nofpclass values that the assembler may |
115 | /// also choose to reject. |
116 | TEST(VerifierTest, InvalidNoFPClassAttribute) { |
117 | LLVMContext C; |
118 | |
119 | const unsigned InvalidMasks[] = {0, fcAllFlags + 1}; |
120 | |
121 | for (unsigned InvalidMask : InvalidMasks) { |
122 | Module M("M" , C); |
123 | FunctionType *FTy = |
124 | FunctionType::get(Result: Type::getFloatTy(C), /*isVarArg=*/false); |
125 | Function *F = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo" , M); |
126 | AttributeList AS = F->getAttributes(); |
127 | |
128 | // Don't use getWithNoFPClass to avoid using out of bounds enum values here. |
129 | F->setAttributes(AS.addRetAttribute( |
130 | C, Attribute::get(C, Attribute::NoFPClass, InvalidMask))); |
131 | |
132 | std::string Error; |
133 | raw_string_ostream ErrorOS(Error); |
134 | EXPECT_TRUE(verifyModule(M, &ErrorOS)); |
135 | |
136 | StringRef ErrMsg(ErrorOS.str()); |
137 | |
138 | if (InvalidMask == 0) { |
139 | EXPECT_TRUE(ErrMsg.starts_with( |
140 | "Attribute 'nofpclass' must have at least one test bit set" )) |
141 | << ErrMsg; |
142 | } else { |
143 | EXPECT_TRUE(ErrMsg.starts_with("Invalid value for 'nofpclass' test mask" )) |
144 | << ErrMsg; |
145 | } |
146 | } |
147 | } |
148 | |
149 | TEST(VerifierTest, CrossModuleRef) { |
150 | LLVMContext C; |
151 | Module M1("M1" , C); |
152 | Module M2("M2" , C); |
153 | Module M3("M3" , C); |
154 | FunctionType *FTy = FunctionType::get(Result: Type::getInt32Ty(C), /*isVarArg=*/false); |
155 | Function *F1 = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo1" , M&: M1); |
156 | Function *F2 = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo2" , M&: M2); |
157 | Function *F3 = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo3" , M&: M3); |
158 | |
159 | BasicBlock *Entry1 = BasicBlock::Create(Context&: C, Name: "entry" , Parent: F1); |
160 | BasicBlock *Entry3 = BasicBlock::Create(Context&: C, Name: "entry" , Parent: F3); |
161 | |
162 | // BAD: Referencing function in another module |
163 | CallInst::Create(Func: F2,NameStr: "call" ,InsertAtEnd: Entry1); |
164 | |
165 | // BAD: Referencing personality routine in another module |
166 | F3->setPersonalityFn(F2); |
167 | |
168 | // Fill in the body |
169 | Constant *ConstZero = ConstantInt::get(Ty: Type::getInt32Ty(C), V: 0); |
170 | ReturnInst::Create(C, retVal: ConstZero, InsertAtEnd: Entry1); |
171 | ReturnInst::Create(C, retVal: ConstZero, InsertAtEnd: Entry3); |
172 | |
173 | std::string Error; |
174 | raw_string_ostream ErrorOS(Error); |
175 | EXPECT_TRUE(verifyModule(M2, &ErrorOS)); |
176 | EXPECT_TRUE(StringRef(ErrorOS.str()) |
177 | .equals("Global is referenced in a different module!\n" |
178 | "ptr @foo2\n" |
179 | "; ModuleID = 'M2'\n" |
180 | " %call = call i32 @foo2()\n" |
181 | "ptr @foo1\n" |
182 | "; ModuleID = 'M1'\n" |
183 | "Global is used by function in a different module\n" |
184 | "ptr @foo2\n" |
185 | "; ModuleID = 'M2'\n" |
186 | "ptr @foo3\n" |
187 | "; ModuleID = 'M3'\n" )); |
188 | |
189 | Error.clear(); |
190 | EXPECT_TRUE(verifyModule(M1, &ErrorOS)); |
191 | EXPECT_TRUE(StringRef(ErrorOS.str()).equals( |
192 | "Referencing function in another module!\n" |
193 | " %call = call i32 @foo2()\n" |
194 | "; ModuleID = 'M1'\n" |
195 | "ptr @foo2\n" |
196 | "; ModuleID = 'M2'\n" )); |
197 | |
198 | Error.clear(); |
199 | EXPECT_TRUE(verifyModule(M3, &ErrorOS)); |
200 | EXPECT_TRUE( |
201 | StringRef(ErrorOS.str()) |
202 | .starts_with("Referencing personality function in another module!" )); |
203 | |
204 | // Erase bad methods to avoid triggering an assertion failure on destruction |
205 | F1->eraseFromParent(); |
206 | F3->eraseFromParent(); |
207 | } |
208 | |
209 | TEST(VerifierTest, InvalidVariableLinkage) { |
210 | LLVMContext C; |
211 | Module M("M" , C); |
212 | new GlobalVariable(M, Type::getInt8Ty(C), false, |
213 | GlobalValue::LinkOnceODRLinkage, nullptr, "Some Global" ); |
214 | std::string Error; |
215 | raw_string_ostream ErrorOS(Error); |
216 | EXPECT_TRUE(verifyModule(M, &ErrorOS)); |
217 | EXPECT_TRUE(StringRef(ErrorOS.str()) |
218 | .starts_with("Global is external, but doesn't " |
219 | "have external or weak linkage!" )); |
220 | } |
221 | |
222 | TEST(VerifierTest, InvalidFunctionLinkage) { |
223 | LLVMContext C; |
224 | Module M("M" , C); |
225 | |
226 | FunctionType *FTy = FunctionType::get(Result: Type::getVoidTy(C), /*isVarArg=*/false); |
227 | Function::Create(Ty: FTy, Linkage: GlobalValue::LinkOnceODRLinkage, N: "foo" , M: &M); |
228 | std::string Error; |
229 | raw_string_ostream ErrorOS(Error); |
230 | EXPECT_TRUE(verifyModule(M, &ErrorOS)); |
231 | EXPECT_TRUE(StringRef(ErrorOS.str()) |
232 | .starts_with("Global is external, but doesn't " |
233 | "have external or weak linkage!" )); |
234 | } |
235 | |
236 | TEST(VerifierTest, DetectInvalidDebugInfo) { |
237 | { |
238 | LLVMContext C; |
239 | Module M("M" , C); |
240 | DIBuilder DIB(M); |
241 | DIB.createCompileUnit(Lang: dwarf::DW_LANG_C89, File: DIB.createFile(Filename: "broken.c" , Directory: "/" ), |
242 | Producer: "unittest" , isOptimized: false, Flags: "" , RV: 0); |
243 | DIB.finalize(); |
244 | EXPECT_FALSE(verifyModule(M)); |
245 | |
246 | // Now break it by inserting non-CU node to the list of CUs. |
247 | auto *File = DIB.createFile(Filename: "not-a-CU.f" , Directory: "." ); |
248 | NamedMDNode *NMD = M.getOrInsertNamedMetadata(Name: "llvm.dbg.cu" ); |
249 | NMD->addOperand(M: File); |
250 | EXPECT_TRUE(verifyModule(M)); |
251 | } |
252 | { |
253 | LLVMContext C; |
254 | Module M("M" , C); |
255 | DIBuilder DIB(M); |
256 | auto *CU = DIB.createCompileUnit(Lang: dwarf::DW_LANG_C89, |
257 | File: DIB.createFile(Filename: "broken.c" , Directory: "/" ), |
258 | Producer: "unittest" , isOptimized: false, Flags: "" , RV: 0); |
259 | new GlobalVariable(M, Type::getInt8Ty(C), false, |
260 | GlobalValue::ExternalLinkage, nullptr, "g" ); |
261 | |
262 | auto *F = Function::Create(Ty: FunctionType::get(Result: Type::getVoidTy(C), isVarArg: false), |
263 | Linkage: Function::ExternalLinkage, N: "f" , M); |
264 | IRBuilder<> Builder(BasicBlock::Create(Context&: C, Name: "" , Parent: F)); |
265 | Builder.CreateUnreachable(); |
266 | F->setSubprogram(DIB.createFunction( |
267 | Scope: CU, Name: "f" , LinkageName: "f" , File: DIB.createFile(Filename: "broken.c" , Directory: "/" ), LineNo: 1, Ty: nullptr, ScopeLine: 1, |
268 | Flags: DINode::FlagZero, |
269 | SPFlags: DISubprogram::SPFlagLocalToUnit | DISubprogram::SPFlagDefinition)); |
270 | DIB.finalize(); |
271 | EXPECT_FALSE(verifyModule(M)); |
272 | |
273 | // Now break it by not listing the CU at all. |
274 | M.eraseNamedMetadata(NMD: M.getOrInsertNamedMetadata(Name: "llvm.dbg.cu" )); |
275 | EXPECT_TRUE(verifyModule(M)); |
276 | } |
277 | } |
278 | |
279 | TEST(VerifierTest, MDNodeWrongContext) { |
280 | LLVMContext C1, C2; |
281 | auto *Node = MDNode::get(Context&: C1, MDs: std::nullopt); |
282 | |
283 | Module M("M" , C2); |
284 | auto *NamedNode = M.getOrInsertNamedMetadata(Name: "test" ); |
285 | NamedNode->addOperand(M: Node); |
286 | |
287 | std::string Error; |
288 | raw_string_ostream ErrorOS(Error); |
289 | EXPECT_TRUE(verifyModule(M, &ErrorOS)); |
290 | EXPECT_TRUE( |
291 | StringRef(ErrorOS.str()) |
292 | .starts_with("MDNode context does not match Module context!" )); |
293 | } |
294 | |
295 | TEST(VerifierTest, AttributesWrongContext) { |
296 | LLVMContext C1, C2; |
297 | Module M1("M" , C1); |
298 | FunctionType *FTy1 = |
299 | FunctionType::get(Result: Type::getVoidTy(C&: C1), /*isVarArg=*/false); |
300 | Function *F1 = Function::Create(Ty: FTy1, Linkage: Function::ExternalLinkage, N: "foo" , M&: M1); |
301 | F1->setDoesNotReturn(); |
302 | |
303 | Module M2("M" , C2); |
304 | FunctionType *FTy2 = |
305 | FunctionType::get(Result: Type::getVoidTy(C&: C2), /*isVarArg=*/false); |
306 | Function *F2 = Function::Create(Ty: FTy2, Linkage: Function::ExternalLinkage, N: "foo" , M&: M2); |
307 | F2->copyAttributesFrom(Src: F1); |
308 | |
309 | EXPECT_TRUE(verifyFunction(*F2)); |
310 | } |
311 | |
312 | TEST(VerifierTest, SwitchInst) { |
313 | LLVMContext C; |
314 | Module M("M" , C); |
315 | IntegerType *Int32Ty = Type::getInt32Ty(C); |
316 | FunctionType *FTy = FunctionType::get(Result: Type::getVoidTy(C), Params: {Int32Ty, Int32Ty}, |
317 | /*isVarArg=*/false); |
318 | Function *F = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo" , M); |
319 | BasicBlock *Entry = BasicBlock::Create(Context&: C, Name: "entry" , Parent: F); |
320 | BasicBlock *Default = BasicBlock::Create(Context&: C, Name: "default" , Parent: F); |
321 | BasicBlock *OnOne = BasicBlock::Create(Context&: C, Name: "on_one" , Parent: F); |
322 | BasicBlock *OnTwo = BasicBlock::Create(Context&: C, Name: "on_two" , Parent: F); |
323 | |
324 | BasicBlock *Exit = BasicBlock::Create(Context&: C, Name: "exit" , Parent: F); |
325 | |
326 | BranchInst::Create(IfTrue: Exit, InsertAtEnd: Default); |
327 | BranchInst::Create(IfTrue: Exit, InsertAtEnd: OnTwo); |
328 | BranchInst::Create(IfTrue: Exit, InsertAtEnd: OnOne); |
329 | ReturnInst::Create(C, InsertAtEnd: Exit); |
330 | |
331 | Value *Cond = F->getArg(i: 0); |
332 | SwitchInst *Switch = SwitchInst::Create(Value: Cond, Default, NumCases: 2, InsertAtEnd: Entry); |
333 | Switch->addCase(OnVal: ConstantInt::get(Ty: Int32Ty, V: 1), Dest: OnOne); |
334 | Switch->addCase(OnVal: ConstantInt::get(Ty: Int32Ty, V: 2), Dest: OnTwo); |
335 | |
336 | EXPECT_FALSE(verifyFunction(*F)); |
337 | // set one case value to function argument. |
338 | Switch->setOperand(i_nocapture: 2, Val_nocapture: F->getArg(i: 1)); |
339 | EXPECT_TRUE(verifyFunction(*F)); |
340 | } |
341 | |
342 | TEST(VerifierTest, CrossFunctionRef) { |
343 | LLVMContext C; |
344 | Module M("M" , C); |
345 | FunctionType *FTy = FunctionType::get(Result: Type::getVoidTy(C), /*isVarArg=*/false); |
346 | Function *F1 = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo1" , M); |
347 | Function *F2 = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo2" , M); |
348 | BasicBlock *Entry1 = BasicBlock::Create(Context&: C, Name: "entry" , Parent: F1); |
349 | BasicBlock *Entry2 = BasicBlock::Create(Context&: C, Name: "entry" , Parent: F2); |
350 | Type *I32 = Type::getInt32Ty(C); |
351 | |
352 | Value *Alloca = new AllocaInst(I32, 0, "alloca" , Entry1); |
353 | ReturnInst::Create(C, InsertAtEnd: Entry1); |
354 | |
355 | Instruction *Store = new StoreInst(ConstantInt::get(Ty: I32, V: 0), Alloca, Entry2); |
356 | ReturnInst::Create(C, InsertAtEnd: Entry2); |
357 | |
358 | std::string Error; |
359 | raw_string_ostream ErrorOS(Error); |
360 | EXPECT_TRUE(verifyModule(M, &ErrorOS)); |
361 | EXPECT_TRUE( |
362 | StringRef(ErrorOS.str()) |
363 | .starts_with("Referring to an instruction in another function!" )); |
364 | |
365 | // Explicitly erase the store to avoid a use-after-free when the module is |
366 | // destroyed. |
367 | Store->eraseFromParent(); |
368 | } |
369 | |
370 | TEST(VerifierTest, AtomicRMW) { |
371 | LLVMContext C; |
372 | Module M("M" , C); |
373 | FunctionType *FTy = FunctionType::get(Result: Type::getVoidTy(C), /*isVarArg=*/false); |
374 | Function *F = Function::Create(Ty: FTy, Linkage: Function::ExternalLinkage, N: "foo" , M); |
375 | BasicBlock *Entry = BasicBlock::Create(Context&: C, Name: "entry" , Parent: F); |
376 | Value *Ptr = PoisonValue::get(T: PointerType::get(C, AddressSpace: 0)); |
377 | |
378 | Type *FPTy = Type::getFloatTy(C); |
379 | Constant *CF = ConstantFP::getZero(Ty: FPTy); |
380 | |
381 | // Invalid scalable type : atomicrmw (<vscale x 2 x float>) |
382 | Constant *CV = ConstantVector::getSplat(EC: ElementCount::getScalable(MinVal: 2), Elt: CF); |
383 | new AtomicRMWInst(AtomicRMWInst::FAdd, Ptr, CV, Align(8), |
384 | AtomicOrdering::SequentiallyConsistent, SyncScope::System, |
385 | Entry); |
386 | ReturnInst::Create(C, InsertAtEnd: Entry); |
387 | |
388 | std::string Error; |
389 | raw_string_ostream ErrorOS(Error); |
390 | EXPECT_TRUE(verifyFunction(*F, &ErrorOS)); |
391 | EXPECT_TRUE( |
392 | StringRef(ErrorOS.str()) |
393 | .starts_with("atomicrmw fadd operand must have floating-point or " |
394 | "fixed vector of floating-point type!" )) |
395 | << ErrorOS.str(); |
396 | } |
397 | |
398 | } // end anonymous namespace |
399 | } // end namespace llvm |
400 | |