diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -459,6 +459,7 @@ void visitModuleFlagCGProfileEntry(const MDOperand &MDO); void visitFunction(const Function &F); void visitBasicBlock(BasicBlock &BB); + void visitBlockAddress(const BlockAddress &BA); void visitRangeMetadata(Instruction &I, MDNode *Range, Type *Ty); void visitDereferenceableMetadata(Instruction &I, MDNode *MD); void visitProfMetadata(Instruction &I, MDNode *MD); @@ -2729,8 +2730,31 @@ // Check that all instructions have their parent pointers set up correctly. for (auto &I : BB) - { Assert(I.getParent() == &BB, "Instruction has bogus parent pointer!"); + + if (BlockAddress *BA = BlockAddress::lookup(&BB)) + visitBlockAddress(*BA); +} + +void Verifier::visitBlockAddress(const BlockAddress &BA) { + // Check the users of this BA. + Function *F = BA.getFunction(); + // TODO: what about other linkages? isIterposableLinkage? isWeakForLinker? + if (!F->hasAvailableExternallyLinkage()) + return; + + // maybe use a set, too? + SmallVector Worklist(BA.user_begin(), BA.user_end()); + while (!Worklist.empty()) { + const User *U = Worklist.pop_back_val(); + + Assert(!isa(U), + "BlockAddress refers to BasicBlock that will not be emitted"); + if (isa(U)) + Worklist.append(U->user_begin(), U->user_end()); + if (auto *I = dyn_cast(U)) + Assert(I->getParent()->getParent() == F, + "BlockAddress refers to BasicBlock that will not be emitted"); } } diff --git a/llvm/test/Verifier/blockaddress.ll b/llvm/test/Verifier/blockaddress.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/blockaddress.ll @@ -0,0 +1,55 @@ +; RUN: split-file %s %t +; RUN: cat %t/a.ll %t/b.ll > %t/test1.ll +; RUN: cat %t/a.ll %t/c.ll > %t/test2.ll +; RUN: cat %t/a.ll %t/d.ll > %t/test3.ll +; RUN: cat %t/a.ll %t/e.ll > %t/test4.ll +; RUN: not opt -passes=verify %t/test1.ll 2>&1 | FileCheck %t/test1.ll +; RUN: not opt -passes=verify %t/test2.ll 2>&1 | FileCheck %t/test2.ll +; RUN: not opt -passes=verify %t/test3.ll 2>&1 | FileCheck %t/test3.ll +; RUN: not opt -passes=verify %t/test4.ll 2>&1 | FileCheck %t/test4.ll +; RUN: opt -passes=verify %t/f.ll + +; Things get weird with blockaddresses when the refer to functions outside of +; their use, particularly for various linkages. +; Tests b.ll through e.ll all realy on a.ll. + +;--- a.ll +define available_externally void @foo() { +entry: + br label %hello +hello: + ret void +} + +;--- b.ll +; Test global. +; CHECK: BlockAddress refers to BasicBlock that will not be emitted +@glo = global i8* blockaddress(@foo, %hello) + +;--- c.ll +; Test ConstantExpr in global. +; CHECK: BlockAddress refers to BasicBlock that will not be emitted +@glo2 = global i64* bitcast (i8* blockaddress(@foo, %hello) to i64*) + +;--- d.ll +; Test Instruction. +; CHECK: BlockAddress refers to BasicBlock that will not be emitted +define i8* @bar() { + ret i8* blockaddress(@foo, %hello) +} + +;--- e.ll +; Test Instruction and ConstandExpr. +; CHECK: BlockAddress refers to BasicBlock that will not be emitted +define i64* @baz() { + ret i64* bitcast (i8* blockaddress(@foo, %hello) to i64*) +} + +;--- f.ll +; This should be ok/allowed, since the use refers to the function itself. +define available_externally i64* @quux() { +entry: + br label %hello +hello: + ret i64* bitcast (i8* blockaddress(@quux, %hello) to i64*) +}