Index: include/llvm/IR/Value.h =================================================================== --- include/llvm/IR/Value.h +++ include/llvm/IR/Value.h @@ -351,13 +351,19 @@ assertModuleIsMaterialized(); return *materialized_user_begin(); } + iterator_range materialized_users() { + return make_range(materialized_user_begin(), user_end()); + } + iterator_range materialized_users() const { + return make_range(materialized_user_begin(), user_end()); + } iterator_range users() { assertModuleIsMaterialized(); - return make_range(materialized_user_begin(), user_end()); + return materialized_users(); } iterator_range users() const { assertModuleIsMaterialized(); - return make_range(materialized_user_begin(), user_end()); + return materialized_users(); } /// \brief Return true if there is exactly one user of this value. Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -448,6 +448,18 @@ InstVisitor::visit(I); } +// Helper to recursively iterate over indirect users. By +// returning false, the callback can ask to stop recursing +// further. +static void forEachUser(const Value *User, + SmallPtrSet &Visited, + llvm::function_ref Callback) { + if (!Visited.insert(User).second) + return; + for (const Value *TheNextUser : User->materialized_users()) + if (Callback(TheNextUser)) + forEachUser(TheNextUser, Visited, Callback); +} void Verifier::visitGlobalValue(const GlobalValue &GV) { Assert(!GV.isDeclaration() || GV.hasExternalLinkage() || @@ -467,6 +479,30 @@ if (GV.isDeclarationForLinker()) Assert(!GV.hasComdat(), "Declaration may not be in a Comdat!", &GV); + + // Verify that this GlobalValue is only used in this module. + // This map is used to avoid visiting uses twice. We can arrive at a user + // twice, if they have multiple operands. In particular for very large + // constant expressions, we can arrive at a particular user many times. + SmallPtrSet Visited; + forEachUser(&GV, Visited, [&](const Value *V) -> bool { + if (const Instruction *I = dyn_cast(V)) { + if (!I->getParent() || !I->getParent()->getParent()) + CheckFailed("Global is referenced by parentless instruction!", &GV, + GV.getParent(), I); + else if (I->getParent()->getParent()->getParent() != GV.getParent()) + CheckFailed("Global is referenced in a different module!", &GV, + GV.getParent(), I, I->getParent()->getParent(), + I->getParent()->getParent()->getParent()); + return false; + } else if (const Function *F = dyn_cast(V)) { + if (F->getParent() != GV.getParent()) + CheckFailed("Global is used by function in a different module", &GV, + GV.getParent(), F, F->getParent()); + return false; + } + return true; + }); } void Verifier::visitGlobalVariable(const GlobalVariable &GV) { Index: unittests/IR/VerifierTest.cpp =================================================================== --- unittests/IR/VerifierTest.cpp +++ unittests/IR/VerifierTest.cpp @@ -64,7 +64,7 @@ LLVMContext &C = getGlobalContext(); Module M1("M1", C); Module M2("M2", C); - Module M3("M2", C); + Module M3("M3", C); FunctionType *FTy = FunctionType::get(Type::getInt32Ty(C), /*isVarArg=*/false); Function *F1 = cast(M1.getOrInsertFunction("foo1", FTy)); Function *F2 = cast(M2.getOrInsertFunction("foo2", FTy)); @@ -86,7 +86,21 @@ std::string Error; raw_string_ostream ErrorOS(Error); - EXPECT_FALSE(verifyModule(M2, &ErrorOS)); + EXPECT_TRUE(verifyModule(M2, &ErrorOS)); + EXPECT_TRUE(StringRef(ErrorOS.str()) + .equals("Global is used by function in a different module\n" + "i32 ()* @foo2\n" + "; ModuleID = 'M2'\n" + "i32 ()* @foo3\n" + "; ModuleID = 'M3'\n" + "Global is referenced in a different module!\n" + "i32 ()* @foo2\n" + "; ModuleID = 'M2'\n" + " %call = call i32 @foo2()\n" + "i32 ()* @foo1\n" + "; ModuleID = 'M1'\n")); + + Error.clear(); EXPECT_TRUE(verifyModule(M1, &ErrorOS)); EXPECT_TRUE(StringRef(ErrorOS.str()).equals( "Referencing function in another module!\n"