diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h --- a/llvm/include/llvm/ADT/FoldingSet.h +++ b/llvm/include/llvm/ADT/FoldingSet.h @@ -435,6 +435,16 @@ return bucket_iterator(Buckets + (hash & (NumBuckets-1)), true); } + using const_bucket_iterator = FoldingSetBucketIterator; + + const_bucket_iterator bucket_begin(unsigned hash) const { + return const_bucket_iterator(Buckets + (hash & (NumBuckets-1))); + } + + const_bucket_iterator bucket_end(unsigned hash) const { + return const_bucket_iterator(Buckets + (hash & (NumBuckets-1)), true); + } + /// reserve - Increase the number of buckets such that adding the /// EltCount-th node won't cause a rebucket operation. reserve is permitted /// to allocate more space than requested by EltCount. @@ -478,6 +488,20 @@ (void)Inserted; assert(Inserted == N && "Node already inserted!"); } + + /// Contains - Check whether the node is contained in this folding set. For + /// performance reasons, this should not be used as a prelude to a mutation, + /// the other methods provide combined operations with better performance. + bool Contains(T *N) const { + FoldingSetNodeID ID; + N->Profile(ID); + auto Hash = ID.ComputeHash(); + for (auto BI = bucket_begin(Hash), BE = bucket_end(Hash); BI != BE; ++BI) { + if (&*BI == N) + return true; + } + return false; + } }; //===----------------------------------------------------------------------===// @@ -652,6 +676,13 @@ Vector.push_back(N); } + /// Contains - Check whether the node is contained in this folding set. For + /// performance reasons, this should not be used as a prelude to a mutation, + /// the other methods provide combined operations with better performance. + bool Contains(T *N) const { + return Set.Contains(N); + } + /// size - Returns the number of nodes in the folding set. unsigned size() const { return Set.size(); } diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -207,6 +207,9 @@ /// is, presumably, for writing out the mnemonics for the assembly writer. std::string getAsString(bool InAttrGrp = false) const; + /// Return true if this attribute belongs to the LLVMContext. + bool hasParentContext(LLVMContext &C) const; + /// Equality and non-equality operators. bool operator==(Attribute A) const { return pImpl == A.pImpl; } bool operator!=(Attribute A) const { return pImpl != A.pImpl; } @@ -329,6 +332,9 @@ std::pair getVScaleRangeArgs() const; std::string getAsString(bool InAttrGrp = false) const; + /// Return true if this attribute set belongs to the LLVMContext. + bool hasParentContext(LLVMContext &C) const; + using iterator = const Attribute *; iterator begin() const; @@ -710,6 +716,9 @@ /// Return the attributes at the index as a string. std::string getAsString(unsigned Index, bool InAttrGrp = false) const; + /// Return true if this attribute list belongs to the LLVMContext. + bool hasParentContext(LLVMContext &C) const; + //===--------------------------------------------------------------------===// // AttributeList Introspection //===--------------------------------------------------------------------===// @@ -737,6 +746,8 @@ /// Return true if there are no attributes. bool isEmpty() const { return pImpl == nullptr; } + void print(raw_ostream &O) const; + void dump() const; }; diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -599,6 +599,11 @@ llvm_unreachable("Unknown attribute"); } +bool Attribute::hasParentContext(LLVMContext &C) const { + assert(isValid() && "invalid Attribute doesn't refer to any context"); + return C.pImpl->AttrsSet.Contains(pImpl); +} + bool Attribute::operator<(Attribute A) const { if (!pImpl && !A.pImpl) return false; if (!pImpl) return true; @@ -823,6 +828,11 @@ return SetNode ? SetNode->getAsString(InAttrGrp) : ""; } +bool AttributeSet::hasParentContext(LLVMContext &C) const { + assert(hasAttributes() && "empty AttributeSet doesn't refer to any context"); + return C.pImpl->AttrsSetNodes.Contains(SetNode); +} + AttributeSet::iterator AttributeSet::begin() const { return SetNode ? SetNode->begin() : nullptr; } @@ -1600,6 +1610,11 @@ return pImpl->begin()[Index]; } +bool AttributeList::hasParentContext(LLVMContext &C) const { + assert(!isEmpty() && "an empty attribute list has no parent context"); + return C.pImpl->AttrsLists.Contains(pImpl); +} + AttributeList::iterator AttributeList::begin() const { return pImpl ? pImpl->begin() : nullptr; } @@ -1616,16 +1631,20 @@ return pImpl ? pImpl->NumAttrSets : 0; } -#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) -LLVM_DUMP_METHOD void AttributeList::dump() const { - dbgs() << "PAL[\n"; +void AttributeList::print(raw_ostream &O) const { + O << "PAL[\n"; for (unsigned i = index_begin(), e = index_end(); i != e; ++i) { if (getAttributes(i).hasAttributes()) - dbgs() << " { " << i << " => " << getAsString(i) << " }\n"; + O << " { " << i << " => " << getAsString(i) << " }\n"; } - dbgs() << "]\n"; + O << "]\n"; +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void AttributeList::dump() const { + print(dbgs()); } #endif 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 @@ -199,6 +199,24 @@ void Write(const unsigned i) { *OS << i << '\n'; } + void Write(const Attribute *A) { + if (!A) + return; + *OS << A->getAsString() << '\n'; + } + + void Write(const AttributeSet *AS) { + if (!AS) + return; + *OS << AS->getAsString() << '\n'; + } + + void Write(const AttributeList *AL) { + if (!AL) + return; + AL->print(*OS); + } + template void Write(ArrayRef Vs) { for (const T &V : Vs) Write(V); @@ -1834,6 +1852,17 @@ if (Attrs.isEmpty()) return; + Assert(Attrs.hasParentContext(Context), + "Attribute list does not match Module context!", &Attrs); + for (const auto &AttrSet : Attrs) { + Assert(!AttrSet.hasAttributes() || AttrSet.hasParentContext(Context), + "Attribute set does not match Module context!", &AttrSet); + for (const auto &A : AttrSet) { + Assert(A.hasParentContext(Context), + "Attribute does not match Module context!", &A); + } + } + bool SawNest = false; bool SawReturned = false; bool SawSRet = false; diff --git a/llvm/unittests/ADT/FoldingSet.cpp b/llvm/unittests/ADT/FoldingSet.cpp --- a/llvm/unittests/ADT/FoldingSet.cpp +++ b/llvm/unittests/ADT/FoldingSet.cpp @@ -188,5 +188,17 @@ EXPECT_EQ(Trivial.capacity(), OldCapacity); } +TEST(FoldingSetTest, Contains) { + FoldingSet Trivial; + TrivialPair T(99, 42); + EXPECT_FALSE(Trivial.Contains(&T)); + Trivial.InsertNode(&T); + EXPECT_TRUE(Trivial.Contains(&T)); + TrivialPair T2(99, 42); + EXPECT_FALSE(Trivial.Contains(&T2)); + Trivial.clear(); + EXPECT_FALSE(Trivial.Contains(&T)); +} + } diff --git a/llvm/unittests/IR/AttributesTest.cpp b/llvm/unittests/IR/AttributesTest.cpp --- a/llvm/unittests/IR/AttributesTest.cpp +++ b/llvm/unittests/IR/AttributesTest.cpp @@ -187,4 +187,37 @@ EXPECT_EQ(A.getAsString(), "byval(i32)"); } +TEST(Attributes, HasParentContext) { + LLVMContext C1, C2; + + { + Attribute Attr1 = Attribute::get(C1, Attribute::AlwaysInline); + Attribute Attr2 = Attribute::get(C2, Attribute::AlwaysInline); + EXPECT_TRUE(Attr1.hasParentContext(C1)); + EXPECT_FALSE(Attr1.hasParentContext(C2)); + EXPECT_FALSE(Attr2.hasParentContext(C1)); + EXPECT_TRUE(Attr2.hasParentContext(C2)); + } + + { + AttributeSet AS1 = AttributeSet::get( + C1, makeArrayRef(Attribute::get(C1, Attribute::NoReturn))); + AttributeSet AS2 = AttributeSet::get( + C2, makeArrayRef(Attribute::get(C2, Attribute::NoReturn))); + EXPECT_TRUE(AS1.hasParentContext(C1)); + EXPECT_FALSE(AS1.hasParentContext(C2)); + EXPECT_FALSE(AS2.hasParentContext(C1)); + EXPECT_TRUE(AS2.hasParentContext(C2)); + } + + { + AttributeList AL1 = AttributeList::get(C1, 1, Attribute::ZExt); + AttributeList AL2 = AttributeList::get(C2, 1, Attribute::ZExt); + EXPECT_TRUE(AL1.hasParentContext(C1)); + EXPECT_FALSE(AL1.hasParentContext(C2)); + EXPECT_FALSE(AL2.hasParentContext(C1)); + EXPECT_TRUE(AL2.hasParentContext(C2)); + } +} + } // end anonymous namespace diff --git a/llvm/unittests/IR/VerifierTest.cpp b/llvm/unittests/IR/VerifierTest.cpp --- a/llvm/unittests/IR/VerifierTest.cpp +++ b/llvm/unittests/IR/VerifierTest.cpp @@ -253,5 +253,22 @@ .startswith("MDNode context does not match Module context!")); } +TEST(VerifierTest, AttributesWrongContext) { + LLVMContext C1, C2; + Module M1("M", C1); + FunctionType *FTy1 = + FunctionType::get(Type::getVoidTy(C1), /*isVarArg=*/false); + Function *F1 = Function::Create(FTy1, Function::ExternalLinkage, "foo", M1); + F1->setDoesNotReturn(); + + Module M2("M", C2); + FunctionType *FTy2 = + FunctionType::get(Type::getVoidTy(C2), /*isVarArg=*/false); + Function *F2 = Function::Create(FTy2, Function::ExternalLinkage, "foo", M2); + F2->copyAttributesFrom(F1); + + EXPECT_TRUE(verifyFunction(*F2)); +} + } // end anonymous namespace } // end namespace llvm