[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
Doug Wyatt via cfe-commits
cfe-commits at lists.llvm.org
Wed May 22 07:51:04 PDT 2024
================
@@ -4912,3 +4922,279 @@ void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(),
getTypeConstraintConcept(), getTypeConstraintArguments());
}
+
+FunctionEffect::~FunctionEffect() = default;
+
+bool FunctionEffect::diagnoseConversion(bool Adding, QualType OldType,
+ FunctionEffectSet OldFX,
+ QualType NewType,
+ FunctionEffectSet NewFX) const {
+ return false;
+}
+
+bool FunctionEffect::diagnoseRedeclaration(bool Adding,
+ const FunctionDecl &OldFunction,
+ FunctionEffectSet OldFX,
+ const FunctionDecl &NewFunction,
+ FunctionEffectSet NewFX) const {
+ return false;
+}
+
+bool FunctionEffect::diagnoseMethodOverride(bool Adding,
+ const CXXMethodDecl &OldMethod,
+ FunctionEffectSet OldFX,
+ const CXXMethodDecl &NewMethod,
+ FunctionEffectSet NewFX) const {
+ return false;
+}
+
+bool FunctionEffect::canInferOnDecl(const Decl *Caller,
+ FunctionEffectSet CallerFX) const {
+ return false;
+}
+
+bool FunctionEffect::diagnoseFunctionCall(bool Direct, const Decl *Caller,
+ FunctionEffectSet CallerFX,
+ CalleeDeclOrType Callee,
+ FunctionEffectSet CalleeFX) const {
+ return false;
+}
+
+const NoLockNoAllocEffect &NoLockNoAllocEffect::nolock_instance() {
+ static NoLockNoAllocEffect global(kNoLockTrue, "nolock");
+ return global;
+}
+
+const NoLockNoAllocEffect &NoLockNoAllocEffect::noalloc_instance() {
+ static NoLockNoAllocEffect global(kNoAllocTrue, "noalloc");
+ return global;
+}
+
+// TODO: Separate flags for noalloc
+NoLockNoAllocEffect::NoLockNoAllocEffect(EffectType Ty, const char *Name)
+ : FunctionEffect(Ty,
+ kRequiresVerification | kVerifyCalls |
+ kInferrableOnCallees | kExcludeThrow | kExcludeCatch |
+ kExcludeObjCMessageSend | kExcludeStaticLocalVars |
+ kExcludeThreadLocalVars,
+ Name) {}
+
+NoLockNoAllocEffect::~NoLockNoAllocEffect() = default;
+
+std::string NoLockNoAllocEffect::attribute() const {
+ return std::string{"__attribute__((clang_"} + name().str() + "))";
+}
+
+bool NoLockNoAllocEffect::diagnoseConversion(bool Adding, QualType OldType,
+ FunctionEffectSet OldFX,
+ QualType NewType,
+ FunctionEffectSet NewFX) const {
+ // noalloc can't be added (spoofed) during a conversion, unless we have nolock
+ if (Adding) {
+ if (!isNoLock()) {
+ for (const auto *Effect : OldFX) {
+ if (Effect->type() == kNoLockTrue)
+ return false;
+ }
+ }
+ // nolock can't be added (spoofed) during a conversion.
+ return true;
+ }
+ return false;
+}
+
+bool NoLockNoAllocEffect::diagnoseRedeclaration(bool Adding,
+ const FunctionDecl &OldFunction,
+ FunctionEffectSet OldFX,
+ const FunctionDecl &NewFunction,
+ FunctionEffectSet NewFX) const {
+ // nolock/noalloc can't be removed in a redeclaration
+ // adding -> false, removing -> true (diagnose)
+ return !Adding;
+}
+
+bool NoLockNoAllocEffect::diagnoseMethodOverride(
+ bool Adding, const CXXMethodDecl &OldMethod, FunctionEffectSet OldFX,
+ const CXXMethodDecl &NewMethod, FunctionEffectSet NewFX) const {
+ // nolock/noalloc can't be removed from an override
+ return !Adding;
+}
+
+bool NoLockNoAllocEffect::canInferOnDecl(const Decl *Caller,
+ FunctionEffectSet CallerFX) const {
+ // Does the Decl have nolock(false) / noalloc(false) ?
+ QualType QT;
+ if (isa<BlockDecl>(Caller)) {
+ const auto *TSI = cast<BlockDecl>(Caller)->getSignatureAsWritten();
+ QT = TSI->getType();
+ } else if (isa<ValueDecl>(Caller)) {
+ QT = cast<ValueDecl>(Caller)->getType();
+ } else {
+ return false;
+ }
+ if (QT->hasAttr(isNoLock() ? attr::Kind::NoLock : attr::Kind::NoAlloc)) {
+ return false;
+ }
+
+ return true;
+}
+
+// TODO: Notice that we don't care about some of the parameters. Is the
+// interface overly general?
+bool NoLockNoAllocEffect::diagnoseFunctionCall(
+ bool Direct, const Decl *Caller, FunctionEffectSet CallerFX,
+ CalleeDeclOrType Callee, FunctionEffectSet CalleeFX) const {
+ const EffectType CallerType = type();
+ for (const auto *Effect : CalleeFX) {
+ const EffectType ET = Effect->type();
+ if (ET == CallerType || (CallerType == kNoAllocTrue && ET == kNoLockTrue)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// =====
+
+void MutableFunctionEffectSet::insert(const FunctionEffect *Effect) {
+ auto Iter = std::lower_bound(begin(), end(), Effect);
+ if (*Iter != Effect) {
+ insert(Iter, Effect);
+ }
+}
+
+MutableFunctionEffectSet &
+MutableFunctionEffectSet::operator|=(FunctionEffectSet RHS) {
+ // TODO: For large RHS sets, use set_union or a custom insert-in-place
+ for (const auto *Effect : RHS) {
+ insert(Effect);
+ }
+ return *this;
+}
+
+// This could be simpler if there were a simple set container that could be
+// queried by ArrayRef but which stored something else. Possibly a DenseMap with
+// void values?
+FunctionEffectSet
+FunctionEffectSet::create(llvm::ArrayRef<const FunctionEffect *> Items) {
+ if (Items.empty()) {
+ return FunctionEffectSet{};
+ }
+ if (Items.size() == 1) {
+ return FunctionEffectSet{Items[0]};
+ }
+
+ UniquedAndSortedFX NewSet(Items); // just copies the ArrayRef
+
+ // SmallSet only has contains(), so it provides no way to obtain the uniqued
+ // value.
+ static std::set<UniquedAndSortedFX> uniquedFXSets;
+
+ // See if we already have this set.
+ const auto Iter = uniquedFXSets.find(NewSet);
+ if (Iter != uniquedFXSets.end()) {
+ return FunctionEffectSet{&*Iter};
+ }
+
+ // Copy the incoming array to permanent storage.
+ auto *Storage = new const FunctionEffect *[Items.size()];
+ std::copy(Items.begin(), Items.end(), Storage);
+
+ // Make a new wrapper and insert it into the set.
+ NewSet = UniquedAndSortedFX(Storage, Items.size());
+ auto [InsIter, _] = uniquedFXSets.insert(NewSet);
+ return FunctionEffectSet(&*InsIter);
+}
+
+FunctionEffectSet
+FunctionEffectSet::operator|(const FunctionEffectSet &RHS) const {
+ // Optimize for one of the two sets being empty
+ const FunctionEffectSet &LHS = *this;
+ if (LHS.empty())
+ return RHS;
+ if (RHS.empty())
+ return LHS;
+
+ // Optimize the case where the two sets are identical
+ if (LHS == RHS)
+ return LHS;
+
+ MutableFunctionEffectSet Vec;
+ Vec.reserve(LHS.size() + RHS.size());
+ std::set_union(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(),
+ std::back_inserter(Vec));
+ // The result of a set operation is an ordered/unique set.
+ return FunctionEffectSet::create(Vec);
+}
+
+MutableFunctionEffectSet
+FunctionEffectSet::operator&(const FunctionEffectSet &RHS) const {
+ const FunctionEffectSet &LHS = *this;
+ if (LHS.empty() || RHS.empty()) {
+ return {};
+ }
+
+ MutableFunctionEffectSet Vec;
+ std::set_intersection(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(),
+ std::back_inserter(Vec));
+ // The result of a set operation is an ordered/unique set.
+ return Vec;
+}
+
+// TODO: inline?
+FunctionEffectSet FunctionEffectSet::get(const Type &TyRef) {
+ const Type *Ty = &TyRef;
+ if (Ty->isPointerType())
+ Ty = Ty->getPointeeType().getTypePtr();
+ if (const auto *FPT = Ty->getAs<FunctionProtoType>())
+ return FPT->getFunctionEffects();
+ return {};
+}
+
+FunctionEffectSet::Differences
+FunctionEffectSet::differences(const FunctionEffectSet &Old,
+ const FunctionEffectSet &New) {
+ // TODO: Could be a one-pass algorithm.
+ Differences Result;
+ for (const auto *Effect : (New - Old)) {
+ Result.emplace_back(Effect, true);
+ }
+ for (const auto *Effect : (Old - New)) {
+ Result.emplace_back(Effect, false);
+ }
+ return Result;
+}
+
+MutableFunctionEffectSet
+FunctionEffectSet::operator-(const FunctionEffectSet &RHS) const {
+ const FunctionEffectSet &LHS = *this;
+ MutableFunctionEffectSet Result;
+
+ std::set_difference(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(),
+ std::back_inserter(Result));
+ return Result;
+}
+
+bool FunctionEffectSet::operator<(const FunctionEffectSet &RHS) const {
+ const FunctionEffectSet &LHS = *this;
+ return std::lexicographical_compare(LHS.begin(), LHS.end(), RHS.begin(),
+ RHS.end());
+}
+
+bool FunctionEffectSet::UniquedAndSortedFX::operator<(
+ const UniquedAndSortedFX &RHS) const {
+ return this < &RHS;
+}
+
+void FunctionEffectSet::dump(llvm::raw_ostream &OS) const {
----------------
dougsonos wrote:
This has been done for awhile
https://github.com/llvm/llvm-project/pull/84983
More information about the cfe-commits
mailing list