brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.1 KiB · 2dcb755 Raw
441 lines · c
1//===-- EHScopeStack.h - Stack for cleanup IR generation --------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// These classes should be the minimum interface required for other parts of10// CodeGen to emit cleanups.  The implementation is in CGCleanup.cpp and other11// implemenentation details that are not widely needed are in CGCleanup.h.12//13//===----------------------------------------------------------------------===//14 15#ifndef LLVM_CLANG_LIB_CODEGEN_EHSCOPESTACK_H16#define LLVM_CLANG_LIB_CODEGEN_EHSCOPESTACK_H17 18#include "clang/Basic/LLVM.h"19#include "llvm/ADT/STLExtras.h"20#include "llvm/ADT/SmallVector.h"21#include "llvm/IR/BasicBlock.h"22#include "llvm/IR/Instructions.h"23#include "llvm/IR/Value.h"24 25namespace clang {26namespace CodeGen {27 28class CodeGenFunction;29 30/// A branch fixup.  These are required when emitting a goto to a31/// label which hasn't been emitted yet.  The goto is optimistically32/// emitted as a branch to the basic block for the label, and (if it33/// occurs in a scope with non-trivial cleanups) a fixup is added to34/// the innermost cleanup.  When a (normal) cleanup is popped, any35/// unresolved fixups in that scope are threaded through the cleanup.36struct BranchFixup {37  /// The block containing the terminator which needs to be modified38  /// into a switch if this fixup is resolved into the current scope.39  /// If null, LatestBranch points directly to the destination.40  llvm::BasicBlock *OptimisticBranchBlock;41 42  /// The ultimate destination of the branch.43  ///44  /// This can be set to null to indicate that this fixup was45  /// successfully resolved.46  llvm::BasicBlock *Destination;47 48  /// The destination index value.49  unsigned DestinationIndex;50 51  /// The initial branch of the fixup.52  llvm::BranchInst *InitialBranch;53};54 55template <class T> struct InvariantValue {56  typedef T type;57  typedef T saved_type;58  static bool needsSaving(type value) { return false; }59  static saved_type save(CodeGenFunction &CGF, type value) { return value; }60  static type restore(CodeGenFunction &CGF, saved_type value) { return value; }61};62 63/// A metaprogramming class for ensuring that a value will dominate an64/// arbitrary position in a function.65template <class T> struct DominatingValue : InvariantValue<T> {};66 67template <class T, bool mightBeInstruction =68            std::is_base_of<llvm::Value, T>::value &&69            !std::is_base_of<llvm::Constant, T>::value &&70            !std::is_base_of<llvm::BasicBlock, T>::value>71struct DominatingPointer;72template <class T> struct DominatingPointer<T,false> : InvariantValue<T*> {};73// template <class T> struct DominatingPointer<T,true> at end of file74 75template <class T> struct DominatingValue<T*> : DominatingPointer<T> {};76 77enum CleanupKind : unsigned {78  /// Denotes a cleanup that should run when a scope is exited using exceptional79  /// control flow (a throw statement leading to stack unwinding, ).80  EHCleanup = 0x1,81 82  /// Denotes a cleanup that should run when a scope is exited using normal83  /// control flow (falling off the end of the scope, return, goto, ...).84  NormalCleanup = 0x2,85 86  NormalAndEHCleanup = EHCleanup | NormalCleanup,87 88  LifetimeMarker = 0x8,89  NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup,90 91  // FakeUse needs to be recognized as a special cleanup similar to lifetime92  // markers chiefly to be ignored in most contexts.93  FakeUse = 0x10,94  NormalFakeUse = FakeUse | NormalCleanup,95};96 97/// A stack of scopes which respond to exceptions, including cleanups98/// and catch blocks.99class EHScopeStack {100public:101  /* Should switch to alignof(uint64_t) instead of 8, when EHCleanupScope can */102  enum { ScopeStackAlignment = 8 };103 104  /// A saved depth on the scope stack.  This is necessary because105  /// pushing scopes onto the stack invalidates iterators.106  class stable_iterator {107    friend class EHScopeStack;108 109    /// Offset from StartOfData to EndOfBuffer.110    ptrdiff_t Size;111 112    stable_iterator(ptrdiff_t Size) : Size(Size) {}113 114  public:115    static stable_iterator invalid() { return stable_iterator(-1); }116    stable_iterator() : Size(-1) {}117 118    bool isValid() const { return Size >= 0; }119 120    /// Returns true if this scope encloses I.121    /// Returns false if I is invalid.122    /// This scope must be valid.123    bool encloses(stable_iterator I) const { return Size <= I.Size; }124 125    /// Returns true if this scope strictly encloses I: that is,126    /// if it encloses I and is not I.127    /// Returns false is I is invalid.128    /// This scope must be valid.129    bool strictlyEncloses(stable_iterator I) const { return Size < I.Size; }130 131    friend bool operator==(stable_iterator A, stable_iterator B) {132      return A.Size == B.Size;133    }134    friend bool operator!=(stable_iterator A, stable_iterator B) {135      return A.Size != B.Size;136    }137  };138 139  /// Information for lazily generating a cleanup.  Subclasses must be140  /// POD-like: cleanups will not be destructed, and they will be141  /// allocated on the cleanup stack and freely copied and moved142  /// around.143  ///144  /// Cleanup implementations should generally be declared in an145  /// anonymous namespace.146  class LLVM_MOVABLE_POLYMORPHIC_TYPE alignas(uint64_t) Cleanup {147    // Anchor the construction vtable.148    virtual void anchor();149 150  protected:151    ~Cleanup() = default;152 153  public:154    Cleanup(const Cleanup &) = default;155    Cleanup(Cleanup &&) {}156 157    // The copy and move assignment operator is defined as deleted pending158    // further motivation.159    Cleanup &operator=(const Cleanup &) = delete;160    Cleanup &operator=(Cleanup &&) = delete;161 162    Cleanup() = default;163 164    virtual bool isRedundantBeforeReturn() { return false; }165 166    /// Generation flags.167    class Flags {168      enum {169        F_IsForEH = 0x1,170        F_IsNormalCleanupKind = 0x2,171        F_IsEHCleanupKind = 0x4,172        F_HasExitSwitch = 0x8,173      };174      unsigned flags = 0;175 176    public:177      Flags() = default;178 179      /// isForEH - true if the current emission is for an EH cleanup.180      bool isForEHCleanup() const { return flags & F_IsForEH; }181      bool isForNormalCleanup() const { return !isForEHCleanup(); }182      void setIsForEHCleanup() { flags |= F_IsForEH; }183 184      bool isNormalCleanupKind() const { return flags & F_IsNormalCleanupKind; }185      void setIsNormalCleanupKind() { flags |= F_IsNormalCleanupKind; }186 187      /// isEHCleanupKind - true if the cleanup was pushed as an EH188      /// cleanup.189      bool isEHCleanupKind() const { return flags & F_IsEHCleanupKind; }190      void setIsEHCleanupKind() { flags |= F_IsEHCleanupKind; }191 192      bool hasExitSwitch() const { return flags & F_HasExitSwitch; }193      void setHasExitSwitch() { flags |= F_HasExitSwitch; }194    };195 196    /// Emit the cleanup.  For normal cleanups, this is run in the197    /// same EH context as when the cleanup was pushed, i.e. the198    /// immediately-enclosing context of the cleanup scope.  For199    /// EH cleanups, this is run in a terminate context.200    ///201    // \param flags cleanup kind.202    virtual void Emit(CodeGenFunction &CGF, Flags flags) = 0;203  };204 205  /// ConditionalCleanup stores the saved form of its parameters,206  /// then restores them and performs the cleanup.207  template <class T, class... As>208  class ConditionalCleanup final : public Cleanup {209    typedef std::tuple<typename DominatingValue<As>::saved_type...> SavedTuple;210    SavedTuple Saved;211 212    template <std::size_t... Is>213    T restore(CodeGenFunction &CGF, std::index_sequence<Is...>) {214      // It's important that the restores are emitted in order. The braced init215      // list guarantees that.216      return T{DominatingValue<As>::restore(CGF, std::get<Is>(Saved))...};217    }218 219    void Emit(CodeGenFunction &CGF, Flags flags) override {220      restore(CGF, std::index_sequence_for<As...>()).Emit(CGF, flags);221    }222 223  public:224    ConditionalCleanup(typename DominatingValue<As>::saved_type... A)225        : Saved(A...) {}226 227    ConditionalCleanup(SavedTuple Tuple) : Saved(std::move(Tuple)) {}228  };229 230private:231  // The implementation for this class is in CGException.h and232  // CGException.cpp; the definition is here because it's used as a233  // member of CodeGenFunction.234 235  /// The start of the scope-stack buffer, i.e. the allocated pointer236  /// for the buffer.  All of these pointers are either simultaneously237  /// null or simultaneously valid.238  char *StartOfBuffer;239 240  /// The end of the buffer.241  char *EndOfBuffer;242 243  /// The first valid entry in the buffer.244  char *StartOfData;245 246  /// The innermost normal cleanup on the stack.247  stable_iterator InnermostNormalCleanup;248 249  /// The innermost EH scope on the stack.250  stable_iterator InnermostEHScope;251 252  /// The CGF this Stack belong to253  CodeGenFunction* CGF;254 255  /// The current set of branch fixups.  A branch fixup is a jump to256  /// an as-yet unemitted label, i.e. a label for which we don't yet257  /// know the EH stack depth.  Whenever we pop a cleanup, we have258  /// to thread all the current branch fixups through it.259  ///260  /// Fixups are recorded as the Use of the respective branch or261  /// switch statement.  The use points to the final destination.262  /// When popping out of a cleanup, these uses are threaded through263  /// the cleanup and adjusted to point to the new cleanup.264  ///265  /// Note that branches are allowed to jump into protected scopes266  /// in certain situations;  e.g. the following code is legal:267  ///     struct A { ~A(); }; // trivial ctor, non-trivial dtor268  ///     goto foo;269  ///     A a;270  ///    foo:271  ///     bar();272  SmallVector<BranchFixup, 8> BranchFixups;273 274  char *allocate(size_t Size);275  void deallocate(size_t Size);276 277  void *pushCleanup(CleanupKind K, size_t DataSize);278 279public:280  EHScopeStack()281    : StartOfBuffer(nullptr), EndOfBuffer(nullptr), StartOfData(nullptr),282      InnermostNormalCleanup(stable_end()), InnermostEHScope(stable_end()),283      CGF(nullptr) {}284  ~EHScopeStack() { delete[] StartOfBuffer; }285 286  EHScopeStack(const EHScopeStack &) = delete;287  EHScopeStack &operator=(const EHScopeStack &) = delete;288 289  /// Push a lazily-created cleanup on the stack.290  template <class T, class... As> void pushCleanup(CleanupKind Kind, As... A) {291    static_assert(alignof(T) <= ScopeStackAlignment,292                  "Cleanup's alignment is too large.");293    void *Buffer = pushCleanup(Kind, sizeof(T));294    Cleanup *Obj = new (Buffer) T(A...);295    (void) Obj;296  }297 298  /// Push a lazily-created cleanup on the stack. Tuple version.299  template <class T, class... As>300  void pushCleanupTuple(CleanupKind Kind, std::tuple<As...> A) {301    static_assert(alignof(T) <= ScopeStackAlignment,302                  "Cleanup's alignment is too large.");303    void *Buffer = pushCleanup(Kind, sizeof(T));304    Cleanup *Obj = new (Buffer) T(std::move(A));305    (void) Obj;306  }307 308  // Feel free to add more variants of the following:309 310  /// Push a cleanup with non-constant storage requirements on the311  /// stack.  The cleanup type must provide an additional static method:312  ///   static size_t getExtraSize(size_t);313  /// The argument to this method will be the value N, which will also314  /// be passed as the first argument to the constructor.315  ///316  /// The data stored in the extra storage must obey the same317  /// restrictions as normal cleanup member data.318  ///319  /// The pointer returned from this method is valid until the cleanup320  /// stack is modified.321  template <class T, class... As>322  T *pushCleanupWithExtra(CleanupKind Kind, size_t N, As... A) {323    static_assert(alignof(T) <= ScopeStackAlignment,324                  "Cleanup's alignment is too large.");325    void *Buffer = pushCleanup(Kind, sizeof(T) + T::getExtraSize(N));326    return new (Buffer) T(N, A...);327  }328 329  void pushCopyOfCleanup(CleanupKind Kind, const void *Cleanup, size_t Size) {330    void *Buffer = pushCleanup(Kind, Size);331    std::memcpy(Buffer, Cleanup, Size);332  }333 334  void setCGF(CodeGenFunction *inCGF) { CGF = inCGF; }335 336  /// Pops a cleanup scope off the stack.  This is private to CGCleanup.cpp.337  void popCleanup();338 339  /// Push a set of catch handlers on the stack.  The catch is340  /// uninitialized and will need to have the given number of handlers341  /// set on it.342  class EHCatchScope *pushCatch(unsigned NumHandlers);343 344  /// Pops a catch scope off the stack.  This is private to CGException.cpp.345  void popCatch();346 347  /// Push an exceptions filter on the stack.348  class EHFilterScope *pushFilter(unsigned NumFilters);349 350  /// Pops an exceptions filter off the stack.351  void popFilter();352 353  /// Push a terminate handler on the stack.354  void pushTerminate();355 356  /// Pops a terminate handler off the stack.357  void popTerminate();358 359  // Returns true iff the current scope is either empty or contains only360  // noop cleanups, i.e. lifetime markers and fake uses.361  bool containsOnlyNoopCleanups(stable_iterator Old) const;362 363  /// Determines whether the exception-scopes stack is empty.364  bool empty() const { return StartOfData == EndOfBuffer; }365 366  bool requiresLandingPad() const;367 368  /// Determines whether there are any normal cleanups on the stack.369  bool hasNormalCleanups() const {370    return InnermostNormalCleanup != stable_end();371  }372 373  /// Returns the innermost normal cleanup on the stack, or374  /// stable_end() if there are no normal cleanups.375  stable_iterator getInnermostNormalCleanup() const {376    return InnermostNormalCleanup;377  }378  stable_iterator getInnermostActiveNormalCleanup() const;379 380  stable_iterator getInnermostEHScope() const {381    return InnermostEHScope;382  }383 384 385  /// An unstable reference to a scope-stack depth.  Invalidated by386  /// pushes but not pops.387  class iterator;388 389  /// Returns an iterator pointing to the innermost EH scope.390  iterator begin() const;391 392  /// Returns an iterator pointing to the outermost EH scope.393  iterator end() const;394 395  /// Create a stable reference to the top of the EH stack.  The396  /// returned reference is valid until that scope is popped off the397  /// stack.398  stable_iterator stable_begin() const {399    return stable_iterator(EndOfBuffer - StartOfData);400  }401 402  /// Create a stable reference to the bottom of the EH stack.403  static stable_iterator stable_end() {404    return stable_iterator(0);405  }406 407  /// Translates an iterator into a stable_iterator.408  stable_iterator stabilize(iterator it) const;409 410  /// Turn a stable reference to a scope depth into a unstable pointer411  /// to the EH stack.412  iterator find(stable_iterator save) const;413 414  /// Add a branch fixup to the current cleanup scope.415  BranchFixup &addBranchFixup() {416    assert(hasNormalCleanups() && "adding fixup in scope without cleanups");417    BranchFixups.push_back(BranchFixup());418    return BranchFixups.back();419  }420 421  unsigned getNumBranchFixups() const { return BranchFixups.size(); }422  BranchFixup &getBranchFixup(unsigned I) {423    assert(I < getNumBranchFixups());424    return BranchFixups[I];425  }426 427  /// Pops lazily-removed fixups from the end of the list.  This428  /// should only be called by procedures which have just popped a429  /// cleanup or resolved one or more fixups.430  void popNullFixups();431 432  /// Clears the branch-fixups list.  This should only be called by433  /// ResolveAllBranchFixups.434  void clearFixups() { BranchFixups.clear(); }435};436 437} // namespace CodeGen438} // namespace clang439 440#endif441