[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