Index: llvm/lib/IR/Globals.cpp =================================================================== --- llvm/lib/IR/Globals.cpp +++ llvm/lib/IR/Globals.cpp @@ -381,6 +381,24 @@ getParent()->getGlobalList().erase(getIterator()); } +static void assertThatAllGlobalsAreIn(Constant *Value, + SmallPtrSetImpl &Visited, + Module *Correct) { +#ifndef NDEBUG + if (Visited.count(Value)) + return; + + Visited.insert(Value); + if (ConstantAggregate *V = dyn_cast(Value)) { + for (auto &O : V->operands()) + assertThatAllGlobalsAreIn(cast(O.getUser()), Visited, Correct); + } else if (GlobalValue *V = dyn_cast(Value)) { + assert((!V->getParent() || V->getParent() == Correct) && + "A Constant and its initializer cannot be in different modules"); + } +#endif +} + void GlobalVariable::setInitializer(Constant *InitVal) { if (!InitVal) { if (hasInitializer()) { @@ -399,6 +417,8 @@ if (!hasInitializer()) setGlobalVariableNumOperands(1); Op<0>().set(InitVal); + SmallPtrSet RecursionGuard; + assertThatAllGlobalsAreIn(InitVal, RecursionGuard, getParent()); } } Index: llvm/unittests/IR/VerifierTest.cpp =================================================================== --- llvm/unittests/IR/VerifierTest.cpp +++ llvm/unittests/IR/VerifierTest.cpp @@ -190,5 +190,62 @@ } } +TEST(VerifierTest, CrossModuleInitialization) { + // Setup: provide a module containing a global variable that can be + // referenced... + LLVMContext C; + Module M1("M1", C); + + StructType *ST = StructType::create(C, "selfref"); + ST->setBody({ST->getPointerTo()}, false); + GlobalVariable *GV1 = new GlobalVariable( + M1, ST, true, GlobalVariable::ExternalLinkage, nullptr); + + // ... and a module, that absolutely cannot refer to GV1 as part of any + // initializer. + Module M2("M2", C); + + // No test is complete without a little plumbing. + std::string Error; + raw_string_ostream ErrorOS(Error); + + // Test 1. Is it okay for another variable in M1 to refer to GV1? It should + // be. + new GlobalVariable(M1, ST, true, GlobalVariable::ExternalLinkage, + ConstantStruct::get(ST, {GV1})); + EXPECT_FALSE(verifyModule(M1, &ErrorOS)); + + // Test 2. Is it okay for a variable in M2 to refer to GV1? It should not be. + new GlobalVariable(M2, ST, true, GlobalVariable::ExternalLinkage, + ConstantStruct::get(ST, {GV1})); + Error.clear(); + EXPECT_TRUE(verifyModule(M2, &ErrorOS)); + EXPECT_TRUE(ErrorOS.str().substr(0, 36) == + "Referencing global in another module"); + + // Test 3. Like 2, except that here a ConstantExpr is used to mask the + // reference. + PointerType *I1p = IntegerType::get(C, 1)->getPointerTo(); + auto GV1again = ConstantExpr::getPointerCast(GV1, I1p); + StructType *STagain = StructType::create(C, "masked-selfref"); + STagain->setBody({I1p}, false); + new GlobalVariable(M2, STagain, true, GlobalVariable::ExternalLinkage, + ConstantStruct::get(STagain, {GV1again})); + Error.clear(); + EXPECT_TRUE(verifyModule(M2, &ErrorOS)); + EXPECT_TRUE(ErrorOS.str().substr(0, 36) == + "Referencing global in another module"); + + // Minor addendum: M1 isn't made bad by the references from M2, right? + EXPECT_FALSE(verifyModule(M1, &ErrorOS)); + + // Test 4: Is it okay for GV1 to refer to itself? It should be, and that + // definitely should not lead to any infinite loop anywhere. + GV1->setInitializer(ConstantStruct::get(ST, {GV1})); + EXPECT_FALSE(verifyModule(M1, &ErrorOS)); + // LLVM is built without exceptions, so if this test fails, you won't get a + // nice error message. Instead the stack blows up and the test segfaults. +} + } // end anonymous namespace } // end namespace llvm