================ @@ -2397,6 +2397,1262 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler { }; } // namespace +// ============================================================================= + +namespace FXAnalysis { + +enum class DiagnosticID : uint8_t { + None = 0, // sentinel for an empty Diagnostic + Throws, + Catches, + CallsObjC, + AllocatesMemory, + HasStaticLocal, + AccessesThreadLocal, + + // These only apply to callees, where the analysis stops at the Decl + DeclDisallowsInference, + + CallsDeclWithoutEffect, + CallsExprWithoutEffect, +}; + +// Holds an effect diagnosis, potentially for the entire duration of the +// analysis phase, in order to refer to it when explaining why a caller has been +// made unsafe by a callee. +struct Diagnostic { + FunctionEffect Effect; + DiagnosticID ID = DiagnosticID::None; + SourceLocation Loc; + const Decl *Callee = nullptr; // only valid for Calls* + + Diagnostic() = default; + + Diagnostic(const FunctionEffect &Effect, DiagnosticID ID, SourceLocation Loc, + const Decl *Callee = nullptr) + : Effect(Effect), ID(ID), Loc(Loc), Callee(Callee) {} +}; + +enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete }; +enum class CallType { + // unknown: probably function pointer + Unknown, + Function, + Virtual, + Block +}; + +// Return whether a function's effects CAN be verified. +// The question of whether it SHOULD be verified is independent. +static bool functionIsVerifiable(const FunctionDecl *FD) { + if (!(FD->hasBody() || FD->isInlined())) { + // externally defined; we couldn't verify if we wanted to. + return false; + } + if (FD->isTrivial()) { + // Otherwise `struct x { int a; };` would have an unverifiable default + // constructor. + return true; + } + return true; +} + +/// A mutable set of FunctionEffect, for use in places where any conditions +/// have been resolved or can be ignored. +class EffectSet { + // This implementation optimizes footprint, since we hold one of these for + // every function visited, which, due to inference, can be many more functions + // than have declared effects. + + template <typename T, typename SizeT, SizeT Capacity> struct FixedVector { + SizeT Count = 0; + T Items[Capacity] = {}; + + using value_type = T; + + using iterator = T *; + using const_iterator = const T *; + iterator begin() { return &Items[0]; } + iterator end() { return &Items[Count]; } + const_iterator begin() const { return &Items[0]; } + const_iterator end() const { return &Items[Count]; } + const_iterator cbegin() const { return &Items[0]; } + const_iterator cend() const { return &Items[Count]; } + + void insert(iterator I, const T &Value) { + assert(Count < Capacity); + iterator E = end(); + if (I != E) + std::copy_backward(I, E, E + 1); + *I = Value; + ++Count; + } + + void push_back(const T &Value) { + assert(Count < Capacity); + Items[Count++] = Value; + } + }; + + // As long as FunctionEffect is only 1 byte, and there are only 2 verifiable + // effects, this fixed-size vector with a capacity of 7 is more than + // sufficient and is only 8 bytes. + FixedVector<FunctionEffect, uint8_t, 7> Impl; ---------------- dougsonos wrote:
To summarize the multiple data structures and explore alternatives: `FunctionEffectsRef` is a pair of `ArrayRef`s, one of `FunctionEffect`, another of `EffectConditionExpr`. These are convenient and normalized forms of what is held in `FunctionProtoType`'s trailing objects. `FunctionEffectSet` is a pair of small vectors, also `FunctionEffect` and `EffectConditionExpr`. To interoperate with `FunctionEffectsRef`, the vectors are not "interleaved". Once we get to this secondary analysis, none of the effects have conditions any more, and we need to hold potentially thousands of little sets to indicate which effects are either declared on, or inferred on, every visited `Decl`. Here are some possible representations for these sets of effects without conditions: 1. Use `FunctionEffectSet` and don't worry that it's 32 bytes where 8 (or less) would have sufficed. 2. Use low-level layout tricks to make `FunctionEffectSet` 8 bytes in the overwhelmingly common case where no effects have conditions. (e.g. it's either a pointer to two SmallVectors, or a uint8_t count and an array of 7 FunctionEffects, determined by one bit.) 3. Make a `TinyFunctionEffectSet` which holds only effects, no conditions. Using a bit vector (of effect kinds) would be possible but make it difficult to build the `ArrayRef<FunctionEffect>` needed by some methods of `FunctionEffect`. Thus the current array-like implementation here in the PR. Options 1 and 2 are appealing in that FunctionEffectSet already implements the iteration, insertion and set difference options -- albeit with some extra business about conflicts that shouldn't happen in the 2nd phase analysis. The more I tinker with this third class, the more I appreciate your sense that it's redundant. Then it becomes a tradeoff between 24 bytes of overhead times possibly thousands of functions in a TU (1), vs. the low-level ugliness / space-saving elegance of 2. https://github.com/llvm/llvm-project/pull/99656 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits