================ @@ -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 { ---------------- Sirraide wrote:
Right, my bad, I didn’t phrase that too well; they’re stripped by default in release builds, and `LLVM_DUMP_METHOD` is to prevent them from getting stripped if you build w/ `LLVM_ENABLE_DUMP=ON`. https://github.com/llvm/llvm-project/pull/84983 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits