brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.8 KiB · 9cc2556 Raw
382 lines · c
1//===----------------------------------------------------------------------===//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 support the generation of CIR for cleanups, initially based10// on LLVM IR cleanup handling, but ought to change as CIR evolves.11//12//===----------------------------------------------------------------------===//13 14#ifndef CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H15#define CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H16 17#include "Address.h"18#include "CIRGenModule.h"19#include "EHScopeStack.h"20#include "mlir/IR/Value.h"21#include "clang/AST/StmtCXX.h"22 23namespace clang::CIRGen {24 25/// The MS C++ ABI needs a pointer to RTTI data plus some flags to describe the26/// type of a catch handler, so we use this wrapper.27struct CatchTypeInfo {28  mlir::TypedAttr rtti;29  unsigned flags;30};31 32/// A protected scope for zero-cost EH handling.33class EHScope {34  EHScopeStack::stable_iterator enclosingEHScope;35 36  class CommonBitFields {37    friend class EHScope;38    unsigned kind : 3;39  };40  enum { NumCommonBits = 3 };41 42  bool scopeMayThrow;43 44protected:45  class CatchBitFields {46    friend class EHCatchScope;47    unsigned : NumCommonBits;48    unsigned numHandlers : 32 - NumCommonBits;49  };50 51  class CleanupBitFields {52    friend class EHCleanupScope;53    unsigned : NumCommonBits;54 55    /// Whether this cleanup needs to be run along normal edges.56    unsigned isNormalCleanup : 1;57 58    /// Whether this cleanup needs to be run along exception edges.59    unsigned isEHCleanup : 1;60 61    /// Whether this cleanup is currently active.62    unsigned isActive : 1;63 64    /// Whether this cleanup is a lifetime marker65    unsigned isLifetimeMarker : 1;66 67    /// Whether the normal cleanup should test the activation flag.68    unsigned testFlagInNormalCleanup : 1;69 70    /// Whether the EH cleanup should test the activation flag.71    unsigned testFlagInEHCleanup : 1;72 73    /// The amount of extra storage needed by the Cleanup.74    /// Always a multiple of the scope-stack alignment.75    unsigned cleanupSize : 12;76  };77 78  union {79    CommonBitFields commonBits;80    CatchBitFields catchBits;81    CleanupBitFields cleanupBits;82  };83 84public:85  enum Kind { Cleanup, Catch, Terminate, Filter };86 87  EHScope(Kind kind, EHScopeStack::stable_iterator enclosingEHScope)88      : enclosingEHScope(enclosingEHScope) {89    commonBits.kind = kind;90  }91 92  Kind getKind() const { return static_cast<Kind>(commonBits.kind); }93 94  bool mayThrow() const {95    // Traditional LLVM codegen also checks for `!block->use_empty()`, but96    // in CIRGen the block content is not important, just used as a way to97    // signal `hasEHBranches`.98    return scopeMayThrow;99  }100 101  void setMayThrow(bool mayThrow) { scopeMayThrow = mayThrow; }102 103  EHScopeStack::stable_iterator getEnclosingEHScope() const {104    return enclosingEHScope;105  }106};107 108/// A scope which attempts to handle some, possibly all, types of109/// exceptions.110///111/// Objective C \@finally blocks are represented using a cleanup scope112/// after the catch scope.113 114class EHCatchScope : public EHScope {115  // In effect, we have a flexible array member116  //   Handler Handlers[0];117  // But that's only standard in C99, not C++, so we have to do118  // annoying pointer arithmetic instead.119 120public:121  struct Handler {122    /// A type info value, or null MLIR attribute for a catch-all123    CatchTypeInfo type;124 125    /// The catch handler for this type.126    mlir::Region *region;127 128    /// The catch handler stmt.129    const CXXCatchStmt *stmt;130 131    bool isCatchAll() const { return type.rtti == nullptr; }132  };133 134private:135  friend class EHScopeStack;136 137  Handler *getHandlers() { return reinterpret_cast<Handler *>(this + 1); }138 139  const Handler *getHandlers() const {140    return reinterpret_cast<const Handler *>(this + 1);141  }142 143public:144  static size_t getSizeForNumHandlers(unsigned n) {145    return sizeof(EHCatchScope) + n * sizeof(Handler);146  }147 148  EHCatchScope(unsigned numHandlers,149               EHScopeStack::stable_iterator enclosingEHScope)150      : EHScope(Catch, enclosingEHScope) {151    catchBits.numHandlers = numHandlers;152    assert(catchBits.numHandlers == numHandlers && "NumHandlers overflow?");153  }154 155  unsigned getNumHandlers() const { return catchBits.numHandlers; }156 157  void setHandler(unsigned i, CatchTypeInfo type, mlir::Region *region,158                  const CXXCatchStmt *stmt) {159    assert(i < getNumHandlers());160    Handler *handler = &getHandlers()[i];161    handler->type = type;162    handler->region = region;163    handler->stmt = stmt;164  }165 166  const Handler &getHandler(unsigned i) const {167    assert(i < getNumHandlers());168    return getHandlers()[i];169  }170 171  // Clear all handler blocks.172  // FIXME: it's better to always call clearHandlerBlocks in DTOR and have a173  // 'takeHandler' or some such function which removes ownership from the174  // EHCatchScope object if the handlers should live longer than EHCatchScope.175  void clearHandlerBlocks() {176    // The blocks are owned by TryOp, nothing to delete.177  }178 179  using iterator = const Handler *;180  iterator begin() const { return getHandlers(); }181  iterator end() const { return getHandlers() + getNumHandlers(); }182 183  static bool classof(const EHScope *scope) {184    return scope->getKind() == Catch;185  }186};187 188/// A cleanup scope which generates the cleanup blocks lazily.189class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope190    : public EHScope {191  /// The nearest normal cleanup scope enclosing this one.192  EHScopeStack::stable_iterator enclosingNormal;193 194  /// The dual entry/exit block along the normal edge.  This is lazily195  /// created if needed before the cleanup is popped.196  mlir::Block *normalBlock = nullptr;197 198  /// The number of fixups required by enclosing scopes (not including199  /// this one).  If this is the top cleanup scope, all the fixups200  /// from this index onwards belong to this scope.201  unsigned fixupDepth = 0;202 203public:204  /// Gets the size required for a lazy cleanup scope with the given205  /// cleanup-data requirements.206  static size_t getSizeForCleanupSize(size_t size) {207    return sizeof(EHCleanupScope) + size;208  }209 210  size_t getAllocatedSize() const {211    return sizeof(EHCleanupScope) + cleanupBits.cleanupSize;212  }213 214  EHCleanupScope(unsigned cleanupSize, unsigned fixupDepth,215                 EHScopeStack::stable_iterator enclosingNormal,216                 EHScopeStack::stable_iterator enclosingEH)217      : EHScope(EHScope::Cleanup, enclosingEH),218        enclosingNormal(enclosingNormal), fixupDepth(fixupDepth) {219    // TODO(cir): When exception handling is upstreamed, isNormalCleanup and220    // isEHCleanup will be arguments to the constructor.221    cleanupBits.isNormalCleanup = true;222    cleanupBits.isEHCleanup = false;223    cleanupBits.isActive = true;224    cleanupBits.isLifetimeMarker = false;225    cleanupBits.testFlagInNormalCleanup = false;226    cleanupBits.testFlagInEHCleanup = false;227    cleanupBits.cleanupSize = cleanupSize;228 229    assert(cleanupBits.cleanupSize == cleanupSize && "cleanup size overflow");230  }231 232  void destroy() {}233  // Objects of EHCleanupScope are not destructed. Use destroy().234  ~EHCleanupScope() = delete;235 236  mlir::Block *getNormalBlock() const { return normalBlock; }237  void setNormalBlock(mlir::Block *bb) { normalBlock = bb; }238 239  bool isNormalCleanup() const { return cleanupBits.isNormalCleanup; }240 241  bool isActive() const { return cleanupBits.isActive; }242  void setActive(bool isActive) { cleanupBits.isActive = isActive; }243 244  bool isLifetimeMarker() const { return cleanupBits.isLifetimeMarker; }245 246  unsigned getFixupDepth() const { return fixupDepth; }247  EHScopeStack::stable_iterator getEnclosingNormalCleanup() const {248    return enclosingNormal;249  }250 251  size_t getCleanupSize() const { return cleanupBits.cleanupSize; }252  void *getCleanupBuffer() { return this + 1; }253 254  EHScopeStack::Cleanup *getCleanup() {255    return reinterpret_cast<EHScopeStack::Cleanup *>(getCleanupBuffer());256  }257 258  static bool classof(const EHScope *scope) {259    return (scope->getKind() == Cleanup);260  }261 262  void markEmitted() {}263};264 265/// A non-stable pointer into the scope stack.266class EHScopeStack::iterator {267  char *ptr = nullptr;268 269  friend class EHScopeStack;270  explicit iterator(char *ptr) : ptr(ptr) {}271 272public:273  iterator() = default;274 275  EHScope *get() const { return reinterpret_cast<EHScope *>(ptr); }276 277  EHScope *operator->() const { return get(); }278  EHScope &operator*() const { return *get(); }279 280  iterator &operator++() {281    size_t size;282    switch (get()->getKind()) {283    case EHScope::Catch:284      size = EHCatchScope::getSizeForNumHandlers(285          static_cast<const EHCatchScope *>(get())->getNumHandlers());286      break;287 288    case EHScope::Filter:289      llvm_unreachable("EHScopeStack::iterator Filter");290      break;291 292    case EHScope::Cleanup:293      llvm_unreachable("EHScopeStack::iterator Cleanup");294      break;295 296    case EHScope::Terminate:297      llvm_unreachable("EHScopeStack::iterator Terminate");298      break;299    }300    ptr += llvm::alignTo(size, ScopeStackAlignment);301    return *this;302  }303 304  bool operator==(iterator other) const { return ptr == other.ptr; }305  bool operator!=(iterator other) const { return ptr != other.ptr; }306};307 308inline EHScopeStack::iterator EHScopeStack::begin() const {309  return iterator(startOfData);310}311 312inline EHScopeStack::iterator EHScopeStack::end() const {313  return iterator(endOfBuffer);314}315 316inline EHScopeStack::iterator317EHScopeStack::find(stable_iterator savePoint) const {318  assert(savePoint.isValid() && "finding invalid savepoint");319  assert(savePoint.size <= stable_begin().size &&320         "finding savepoint after pop");321  return iterator(endOfBuffer - savePoint.size);322}323 324inline void EHScopeStack::popCatch() {325  assert(!empty() && "popping exception stack when not empty");326 327  EHCatchScope &scope = llvm::cast<EHCatchScope>(*begin());328  innermostEHScope = scope.getEnclosingEHScope();329  deallocate(EHCatchScope::getSizeForNumHandlers(scope.getNumHandlers()));330}331 332/// The exceptions personality for a function.333struct EHPersonality {334  const char *personalityFn = nullptr;335 336  // If this is non-null, this personality requires a non-standard337  // function for rethrowing an exception after a catchall cleanup.338  // This function must have prototype void(void*).339  const char *catchallRethrowFn = nullptr;340 341  static const EHPersonality &get(CIRGenModule &cgm,342                                  const clang::FunctionDecl *fd);343  static const EHPersonality &get(CIRGenFunction &cgf);344 345  static const EHPersonality GNU_C;346  static const EHPersonality GNU_C_SJLJ;347  static const EHPersonality GNU_C_SEH;348  static const EHPersonality GNU_ObjC;349  static const EHPersonality GNU_ObjC_SJLJ;350  static const EHPersonality GNU_ObjC_SEH;351  static const EHPersonality GNUstep_ObjC;352  static const EHPersonality GNU_ObjCXX;353  static const EHPersonality NeXT_ObjC;354  static const EHPersonality GNU_CPlusPlus;355  static const EHPersonality GNU_CPlusPlus_SJLJ;356  static const EHPersonality GNU_CPlusPlus_SEH;357  static const EHPersonality MSVC_except_handler;358  static const EHPersonality MSVC_C_specific_handler;359  static const EHPersonality MSVC_CxxFrameHandler3;360  static const EHPersonality GNU_Wasm_CPlusPlus;361  static const EHPersonality XL_CPlusPlus;362  static const EHPersonality ZOS_CPlusPlus;363 364  /// Does this personality use landingpads or the family of pad instructions365  /// designed to form funclets?366  bool usesFuncletPads() const {367    return isMSVCPersonality() || isWasmPersonality();368  }369 370  bool isMSVCPersonality() const {371    return this == &MSVC_except_handler || this == &MSVC_C_specific_handler ||372           this == &MSVC_CxxFrameHandler3;373  }374 375  bool isWasmPersonality() const { return this == &GNU_Wasm_CPlusPlus; }376 377  bool isMSVCXXPersonality() const { return this == &MSVC_CxxFrameHandler3; }378};379 380} // namespace clang::CIRGen381#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H382