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,24 @@ InstVisitor::visit(I); } +// Helper to recursively iterate over indirect users that are +// associated to a given module (i.e. we pass through constants, etc. +// but stop at Instructions and Functions) +static void forEachInstructionAndFunctionUser( + const Value *User, SmallPtrSet &Visited, + llvm::function_ref InstCallback, + llvm::function_ref FuncCallback) { + if (!Visited.insert(User).second) + return; + if (const Instruction *I = dyn_cast(User)) + InstCallback(I); + else if (const Function *F = dyn_cast(User)) + FuncCallback(F); + else + for (const Value *TheNextUser : User->materialized_users()) + forEachInstructionAndFunctionUser(TheNextUser, Visited, InstCallback, + FuncCallback); +} void Verifier::visitGlobalValue(const GlobalValue &GV) { Assert(!GV.isDeclaration() || GV.hasExternalLinkage() || @@ -467,6 +485,29 @@ 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; + for (const Value *TheUser : GV.materialized_users()) + forEachInstructionAndFunctionUser( + TheUser, Visited, + [&](const Instruction *I) { + Assert(I->getParent() && I->getParent()->getParent(), + "Global is referenced by parentless instruction!", &GV, + GV.getParent(), I); + Assert(I->getParent()->getParent()->getParent() == GV.getParent(), + "Global is referenced in a different module!", &GV, + GV.getParent(), I, I->getParent()->getParent(), + I->getParent()->getParent()->getParent()); + }, + [&](const Function *F) { + Assert(F->getParent() == GV.getParent(), + "Global is used by function in a different module", &GV, + GV.getParent(), F, F->getParent()); + }); } 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"