brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.7 KiB · 6455bd8 Raw
282 lines · c
1//===-- EHScopeStack.h - Stack for cleanup CIR 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// CIR CodeGen to emit cleanups.  The implementation is in CIRGenCleanup.cpp and11// other implemenentation details that are not widely needed are in12// CIRGenCleanup.h.13//14// TODO(cir): this header should be shared between LLVM and CIR codegen.15//16//===----------------------------------------------------------------------===//17 18#ifndef CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H19#define CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H20 21#include "clang/CIR/Dialect/IR/CIRDialect.h"22#include "llvm/ADT/SmallVector.h"23 24namespace clang::CIRGen {25 26class CIRGenFunction;27 28/// A branch fixup.  These are required when emitting a goto to a29/// label which hasn't been emitted yet.  The goto is optimistically30/// emitted as a branch to the basic block for the label, and (if it31/// occurs in a scope with non-trivial cleanups) a fixup is added to32/// the innermost cleanup.  When a (normal) cleanup is popped, any33/// unresolved fixups in that scope are threaded through the cleanup.34struct BranchFixup {35  /// The block containing the terminator which needs to be modified36  /// into a switch if this fixup is resolved into the current scope.37  /// If null, LatestBranch points directly to the destination.38  mlir::Block *optimisticBranchBlock = nullptr;39 40  /// The ultimate destination of the branch.41  ///42  /// This can be set to null to indicate that this fixup was43  /// successfully resolved.44  mlir::Block *destination = nullptr;45 46  /// The destination index value.47  unsigned destinationIndex = 0;48 49  /// The initial branch of the fixup.50  cir::BrOp initialBranch = {};51};52 53enum CleanupKind : unsigned {54  /// Denotes a cleanup that should run when a scope is exited using exceptional55  /// control flow (a throw statement leading to stack unwinding, ).56  EHCleanup = 0x1,57 58  /// Denotes a cleanup that should run when a scope is exited using normal59  /// control flow (falling off the end of the scope, return, goto, ...).60  NormalCleanup = 0x2,61 62  NormalAndEHCleanup = EHCleanup | NormalCleanup,63 64  LifetimeMarker = 0x8,65  NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup,66};67 68/// A stack of scopes which respond to exceptions, including cleanups69/// and catch blocks.70class EHScopeStack {71  friend class CIRGenFunction;72 73public:74  // TODO(ogcg): Switch to alignof(uint64_t) instead of 875  enum { ScopeStackAlignment = 8 };76 77  /// A saved depth on the scope stack.  This is necessary because78  /// pushing scopes onto the stack invalidates iterators.79  class stable_iterator {80    friend class EHScopeStack;81 82    /// Offset from startOfData to endOfBuffer.83    ptrdiff_t size = -1;84 85    explicit stable_iterator(ptrdiff_t size) : size(size) {}86 87  public:88    static stable_iterator invalid() { return stable_iterator(-1); }89    stable_iterator() = default;90 91    bool isValid() const { return size >= 0; }92 93    /// Returns true if this scope encloses I.94    /// Returns false if I is invalid.95    /// This scope must be valid.96    bool encloses(stable_iterator other) const { return size <= other.size; }97 98    /// Returns true if this scope strictly encloses I: that is,99    /// if it encloses I and is not I.100    /// Returns false is I is invalid.101    /// This scope must be valid.102    bool strictlyEncloses(stable_iterator I) const { return size < I.size; }103 104    friend bool operator==(stable_iterator A, stable_iterator B) {105      return A.size == B.size;106    }107    friend bool operator!=(stable_iterator A, stable_iterator B) {108      return A.size != B.size;109    }110  };111 112  /// Information for lazily generating a cleanup.  Subclasses must be113  /// POD-like: cleanups will not be destructed, and they will be114  /// allocated on the cleanup stack and freely copied and moved115  /// around.116  ///117  /// Cleanup implementations should generally be declared in an118  /// anonymous namespace.119  class LLVM_MOVABLE_POLYMORPHIC_TYPE Cleanup {120    // Anchor the construction vtable.121    virtual void anchor();122 123  public:124    Cleanup(const Cleanup &) = default;125    Cleanup(Cleanup &&) {}126    Cleanup() = default;127 128    virtual ~Cleanup() = default;129 130    /// Emit the cleanup.  For normal cleanups, this is run in the131    /// same EH context as when the cleanup was pushed, i.e. the132    /// immediately-enclosing context of the cleanup scope.  For133    /// EH cleanups, this is run in a terminate context.134    ///135    // \param flags cleanup kind.136    virtual void emit(CIRGenFunction &cgf) = 0;137  };138 139private:140  // The implementation for this class is in CIRGenCleanup.h and141  // CIRGenCleanup.cpp; the definition is here because it's used as a142  // member of CIRGenFunction.143 144  /// The start of the scope-stack buffer, i.e. the allocated pointer145  /// for the buffer.  All of these pointers are either simultaneously146  /// null or simultaneously valid.147  std::unique_ptr<char[]> startOfBuffer;148 149  /// The end of the buffer.150  char *endOfBuffer = nullptr;151 152  /// The first valid entry in the buffer.153  char *startOfData = nullptr;154 155  /// The innermost normal cleanup on the stack.156  stable_iterator innermostNormalCleanup = stable_end();157 158  /// The innermost EH scope on the stack.159  stable_iterator innermostEHScope = stable_end();160 161  /// The CGF this Stack belong to162  CIRGenFunction *cgf = nullptr;163 164  /// The current set of branch fixups.  A branch fixup is a jump to165  /// an as-yet unemitted label, i.e. a label for which we don't yet166  /// know the EH stack depth.  Whenever we pop a cleanup, we have167  /// to thread all the current branch fixups through it.168  ///169  /// Fixups are recorded as the Use of the respective branch or170  /// switch statement.  The use points to the final destination.171  /// When popping out of a cleanup, these uses are threaded through172  /// the cleanup and adjusted to point to the new cleanup.173  ///174  /// Note that branches are allowed to jump into protected scopes175  /// in certain situations;  e.g. the following code is legal:176  ///     struct A { ~A(); }; // trivial ctor, non-trivial dtor177  ///     goto foo;178  ///     A a;179  ///    foo:180  ///     bar();181  llvm::SmallVector<BranchFixup> branchFixups;182 183  // This class uses a custom allocator for maximum efficiency because cleanups184  // are allocated and freed very frequently. It's basically a bump pointer185  // allocator, but we can't use LLVM's BumpPtrAllocator because we use offsets186  // into the buffer as stable iterators.187  char *allocate(size_t size);188  void deallocate(size_t size);189 190  void *pushCleanup(CleanupKind kind, size_t dataSize);191 192public:193  EHScopeStack() = default;194  ~EHScopeStack() = default;195 196  /// Push a lazily-created cleanup on the stack.197  template <class T, class... As> void pushCleanup(CleanupKind kind, As... a) {198    static_assert(alignof(T) <= ScopeStackAlignment,199                  "Cleanup's alignment is too large.");200    void *buffer = pushCleanup(kind, sizeof(T));201    [[maybe_unused]] Cleanup *obj = new (buffer) T(a...);202  }203 204  void setCGF(CIRGenFunction *inCGF) { cgf = inCGF; }205 206  /// Pops a cleanup scope off the stack.  This is private to CIRGenCleanup.cpp.207  void popCleanup();208 209  /// Push a set of catch handlers on the stack.  The catch is210  /// uninitialized and will need to have the given number of handlers211  /// set on it.212  class EHCatchScope *pushCatch(unsigned numHandlers);213 214  /// Pops a catch scope off the stack. This is private to CIRGenException.cpp.215  void popCatch();216 217  /// Determines whether the exception-scopes stack is empty.218  bool empty() const { return startOfData == endOfBuffer; }219 220  bool requiresCatchOrCleanup() const;221 222  /// Determines whether there are any normal cleanups on the stack.223  bool hasNormalCleanups() const {224    return innermostNormalCleanup != stable_end();225  }226 227  /// Returns the innermost normal cleanup on the stack, or228  /// stable_end() if there are no normal cleanups.229  stable_iterator getInnermostNormalCleanup() const {230    return innermostNormalCleanup;231  }232  stable_iterator getInnermostActiveNormalCleanup() const;233 234  stable_iterator getInnermostEHScope() const { return innermostEHScope; }235 236  /// An unstable reference to a scope-stack depth.  Invalidated by237  /// pushes but not pops.238  class iterator;239 240  /// Returns an iterator pointing to the innermost EH scope.241  iterator begin() const;242 243  /// Returns an iterator pointing to the outermost EH scope.244  iterator end() const;245 246  /// Create a stable reference to the top of the EH stack.  The247  /// returned reference is valid until that scope is popped off the248  /// stack.249  stable_iterator stable_begin() const {250    return stable_iterator(endOfBuffer - startOfData);251  }252 253  /// Create a stable reference to the bottom of the EH stack.254  static stable_iterator stable_end() { return stable_iterator(0); }255 256  /// Turn a stable reference to a scope depth into a unstable pointer257  /// to the EH stack.258  iterator find(stable_iterator savePoint) const;259 260  /// Add a branch fixup to the current cleanup scope.261  BranchFixup &addBranchFixup() {262    assert(hasNormalCleanups() && "adding fixup in scope without cleanups");263    branchFixups.push_back(BranchFixup());264    return branchFixups.back();265  }266 267  unsigned getNumBranchFixups() const { return branchFixups.size(); }268  BranchFixup &getBranchFixup(unsigned i) {269    assert(i < getNumBranchFixups());270    return branchFixups[i];271  }272 273  /// Pops lazily-removed fixups from the end of the list.  This274  /// should only be called by procedures which have just popped a275  /// cleanup or resolved one or more fixups.276  void popNullFixups();277};278 279} // namespace clang::CIRGen280 281#endif // CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H282