brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.5 KiB · ba78e54 Raw
709 lines · c
1//===-- CGCleanup.h - Classes for cleanups 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 support the generation of LLVM IR for cleanups.10//11//===----------------------------------------------------------------------===//12 13#ifndef LLVM_CLANG_LIB_CODEGEN_CGCLEANUP_H14#define LLVM_CLANG_LIB_CODEGEN_CGCLEANUP_H15 16#include "EHScopeStack.h"17 18#include "Address.h"19#include "llvm/ADT/STLExtras.h"20#include "llvm/ADT/SetVector.h"21#include "llvm/ADT/SmallPtrSet.h"22#include "llvm/ADT/SmallVector.h"23#include "llvm/IR/Instruction.h"24 25namespace llvm {26class BasicBlock;27class Value;28class ConstantInt;29}30 31namespace clang {32class FunctionDecl;33namespace CodeGen {34class CodeGenModule;35class CodeGenFunction;36 37/// The MS C++ ABI needs a pointer to RTTI data plus some flags to describe the38/// type of a catch handler, so we use this wrapper.39struct CatchTypeInfo {40  llvm::Constant *RTTI;41  unsigned Flags;42};43 44/// A protected scope for zero-cost EH handling.45class EHScope {46public:47  enum Kind { Cleanup, Catch, Terminate, Filter };48 49private:50  llvm::BasicBlock *CachedLandingPad;51  llvm::BasicBlock *CachedEHDispatchBlock;52 53  EHScopeStack::stable_iterator EnclosingEHScope;54 55  class CommonBitFields {56    friend class EHScope;57    LLVM_PREFERRED_TYPE(Kind)58    unsigned Kind : 3;59  };60  enum { NumCommonBits = 3 };61 62protected:63  class CatchBitFields {64    friend class EHCatchScope;65    unsigned : NumCommonBits;66 67    unsigned NumHandlers : 32 - NumCommonBits;68  };69 70  class CleanupBitFields {71    friend class EHCleanupScope;72    unsigned : NumCommonBits;73 74    /// Whether this cleanup needs to be run along normal edges.75    LLVM_PREFERRED_TYPE(bool)76    unsigned IsNormalCleanup : 1;77 78    /// Whether this cleanup needs to be run along exception edges.79    LLVM_PREFERRED_TYPE(bool)80    unsigned IsEHCleanup : 1;81 82    /// Whether this cleanup is currently active.83    LLVM_PREFERRED_TYPE(bool)84    unsigned IsActive : 1;85 86    /// Whether this cleanup is a lifetime marker87    LLVM_PREFERRED_TYPE(bool)88    unsigned IsLifetimeMarker : 1;89 90    /// Whether this cleanup is a fake use91    LLVM_PREFERRED_TYPE(bool)92    unsigned IsFakeUse : 1;93 94    /// Whether the normal cleanup should test the activation flag.95    LLVM_PREFERRED_TYPE(bool)96    unsigned TestFlagInNormalCleanup : 1;97 98    /// Whether the EH cleanup should test the activation flag.99    LLVM_PREFERRED_TYPE(bool)100    unsigned TestFlagInEHCleanup : 1;101 102    /// The amount of extra storage needed by the Cleanup.103    /// Always a multiple of the scope-stack alignment.104    unsigned CleanupSize : 12;105  };106 107  class FilterBitFields {108    friend class EHFilterScope;109    unsigned : NumCommonBits;110 111    unsigned NumFilters : 32 - NumCommonBits;112  };113 114  union {115    CommonBitFields CommonBits;116    CatchBitFields CatchBits;117    CleanupBitFields CleanupBits;118    FilterBitFields FilterBits;119  };120 121public:122  EHScope(Kind kind, EHScopeStack::stable_iterator enclosingEHScope)123    : CachedLandingPad(nullptr), CachedEHDispatchBlock(nullptr),124      EnclosingEHScope(enclosingEHScope) {125    CommonBits.Kind = kind;126  }127 128  Kind getKind() const { return static_cast<Kind>(CommonBits.Kind); }129 130  llvm::BasicBlock *getCachedLandingPad() const {131    return CachedLandingPad;132  }133 134  void setCachedLandingPad(llvm::BasicBlock *block) {135    CachedLandingPad = block;136  }137 138  llvm::BasicBlock *getCachedEHDispatchBlock() const {139    return CachedEHDispatchBlock;140  }141 142  void setCachedEHDispatchBlock(llvm::BasicBlock *block) {143    CachedEHDispatchBlock = block;144  }145 146  bool hasEHBranches() const {147    if (llvm::BasicBlock *block = getCachedEHDispatchBlock())148      return !block->use_empty();149    return false;150  }151 152  EHScopeStack::stable_iterator getEnclosingEHScope() const {153    return EnclosingEHScope;154  }155};156 157/// A scope which attempts to handle some, possibly all, types of158/// exceptions.159///160/// Objective C \@finally blocks are represented using a cleanup scope161/// after the catch scope.162class EHCatchScope : public EHScope {163  // In effect, we have a flexible array member164  //   Handler Handlers[0];165  // But that's only standard in C99, not C++, so we have to do166  // annoying pointer arithmetic instead.167 168public:169  struct Handler {170    /// A type info value, or null (C++ null, not an LLVM null pointer)171    /// for a catch-all.172    CatchTypeInfo Type;173 174    /// The catch handler for this type.175    llvm::BasicBlock *Block;176 177    bool isCatchAll() const { return Type.RTTI == nullptr; }178  };179 180private:181  friend class EHScopeStack;182 183  Handler *getHandlers() {184    return reinterpret_cast<Handler*>(this+1);185  }186 187  const Handler *getHandlers() const {188    return reinterpret_cast<const Handler*>(this+1);189  }190 191public:192  static size_t getSizeForNumHandlers(unsigned N) {193    return sizeof(EHCatchScope) + N * sizeof(Handler);194  }195 196  EHCatchScope(unsigned numHandlers,197               EHScopeStack::stable_iterator enclosingEHScope)198    : EHScope(Catch, enclosingEHScope) {199    CatchBits.NumHandlers = numHandlers;200    assert(CatchBits.NumHandlers == numHandlers && "NumHandlers overflow?");201  }202 203  unsigned getNumHandlers() const {204    return CatchBits.NumHandlers;205  }206 207  void setCatchAllHandler(unsigned I, llvm::BasicBlock *Block) {208    setHandler(I, CatchTypeInfo{nullptr, 0}, Block);209  }210 211  void setHandler(unsigned I, llvm::Constant *Type, llvm::BasicBlock *Block) {212    assert(I < getNumHandlers());213    getHandlers()[I].Type = CatchTypeInfo{Type, 0};214    getHandlers()[I].Block = Block;215  }216 217  void setHandler(unsigned I, CatchTypeInfo Type, llvm::BasicBlock *Block) {218    assert(I < getNumHandlers());219    getHandlers()[I].Type = Type;220    getHandlers()[I].Block = Block;221  }222 223  const Handler &getHandler(unsigned I) const {224    assert(I < getNumHandlers());225    return getHandlers()[I];226  }227 228  // Clear all handler blocks.229  // FIXME: it's better to always call clearHandlerBlocks in DTOR and have a230  // 'takeHandler' or some such function which removes ownership from the231  // EHCatchScope object if the handlers should live longer than EHCatchScope.232  void clearHandlerBlocks() {233    for (unsigned I = 0, N = getNumHandlers(); I != N; ++I)234      delete getHandler(I).Block;235  }236 237  typedef const Handler *iterator;238  iterator begin() const { return getHandlers(); }239  iterator end() const { return getHandlers() + getNumHandlers(); }240 241  static bool classof(const EHScope *Scope) {242    return Scope->getKind() == Catch;243  }244};245 246/// A cleanup scope which generates the cleanup blocks lazily.247class alignas(8) EHCleanupScope : public EHScope {248  /// The nearest normal cleanup scope enclosing this one.249  EHScopeStack::stable_iterator EnclosingNormal;250 251  /// The nearest EH scope enclosing this one.252  EHScopeStack::stable_iterator EnclosingEH;253 254  /// The dual entry/exit block along the normal edge.  This is lazily255  /// created if needed before the cleanup is popped.256  llvm::BasicBlock *NormalBlock;257 258  /// An optional i1 variable indicating whether this cleanup has been259  /// activated yet.260  Address ActiveFlag;261 262  /// Extra information required for cleanups that have resolved263  /// branches through them.  This has to be allocated on the side264  /// because everything on the cleanup stack has be trivially265  /// movable.266  struct ExtInfo {267    /// The destinations of normal branch-afters and branch-throughs.268    llvm::SmallPtrSet<llvm::BasicBlock*, 4> Branches;269 270    /// Normal branch-afters.271    SmallVector<std::pair<llvm::BasicBlock*,llvm::ConstantInt*>, 4>272      BranchAfters;273  };274  mutable struct ExtInfo *ExtInfo;275 276  /// Erases auxillary allocas and their usages for an unused cleanup.277  /// Cleanups should mark these allocas as 'used' if the cleanup is278  /// emitted, otherwise these instructions would be erased.279  struct AuxillaryAllocas {280    SmallVector<llvm::Instruction *, 1> AuxAllocas;281    bool used = false;282 283    // Records a potentially unused instruction to be erased later.284    void Add(llvm::AllocaInst *Alloca) { AuxAllocas.push_back(Alloca); }285 286    // Mark all recorded instructions as used. These will not be erased later.287    void MarkUsed() {288      used = true;289      AuxAllocas.clear();290    }291 292    ~AuxillaryAllocas() {293      if (used)294        return;295      llvm::SetVector<llvm::Instruction *> Uses;296      for (auto *Inst : llvm::reverse(AuxAllocas))297        CollectUses(Inst, Uses);298      // Delete uses in the reverse order of insertion.299      for (auto *I : llvm::reverse(Uses))300        I->eraseFromParent();301    }302 303  private:304    void CollectUses(llvm::Instruction *I,305                     llvm::SetVector<llvm::Instruction *> &Uses) {306      if (!I || !Uses.insert(I))307        return;308      for (auto *User : I->users())309        CollectUses(cast<llvm::Instruction>(User), Uses);310    }311  };312  mutable struct AuxillaryAllocas *AuxAllocas;313 314  AuxillaryAllocas &getAuxillaryAllocas() {315    if (!AuxAllocas) {316      AuxAllocas = new struct AuxillaryAllocas();317    }318    return *AuxAllocas;319  }320 321  /// The number of fixups required by enclosing scopes (not including322  /// this one).  If this is the top cleanup scope, all the fixups323  /// from this index onwards belong to this scope.324  unsigned FixupDepth;325 326  struct ExtInfo &getExtInfo() {327    if (!ExtInfo) ExtInfo = new struct ExtInfo();328    return *ExtInfo;329  }330 331  const struct ExtInfo &getExtInfo() const {332    if (!ExtInfo) ExtInfo = new struct ExtInfo();333    return *ExtInfo;334  }335 336public:337  /// Gets the size required for a lazy cleanup scope with the given338  /// cleanup-data requirements.339  static size_t getSizeForCleanupSize(size_t Size) {340    return sizeof(EHCleanupScope) + Size;341  }342 343  size_t getAllocatedSize() const {344    return sizeof(EHCleanupScope) + CleanupBits.CleanupSize;345  }346 347  EHCleanupScope(bool isNormal, bool isEH, unsigned cleanupSize,348                 unsigned fixupDepth,349                 EHScopeStack::stable_iterator enclosingNormal,350                 EHScopeStack::stable_iterator enclosingEH)351      : EHScope(EHScope::Cleanup, enclosingEH),352        EnclosingNormal(enclosingNormal), NormalBlock(nullptr),353        ActiveFlag(Address::invalid()), ExtInfo(nullptr), AuxAllocas(nullptr),354        FixupDepth(fixupDepth) {355    CleanupBits.IsNormalCleanup = isNormal;356    CleanupBits.IsEHCleanup = isEH;357    CleanupBits.IsActive = true;358    CleanupBits.IsLifetimeMarker = false;359    CleanupBits.IsFakeUse = false;360    CleanupBits.TestFlagInNormalCleanup = false;361    CleanupBits.TestFlagInEHCleanup = false;362    CleanupBits.CleanupSize = cleanupSize;363 364    assert(CleanupBits.CleanupSize == cleanupSize && "cleanup size overflow");365  }366 367  void Destroy() {368    if (AuxAllocas)369      delete AuxAllocas;370    delete ExtInfo;371  }372  void AddAuxAllocas(llvm::SmallVector<llvm::AllocaInst *> Allocas) {373    for (auto *Alloca : Allocas)374      getAuxillaryAllocas().Add(Alloca);375  }376  void MarkEmitted() { getAuxillaryAllocas().MarkUsed(); }377  // Objects of EHCleanupScope are not destructed. Use Destroy().378  ~EHCleanupScope() = delete;379 380  bool isNormalCleanup() const { return CleanupBits.IsNormalCleanup; }381  llvm::BasicBlock *getNormalBlock() const { return NormalBlock; }382  void setNormalBlock(llvm::BasicBlock *BB) { NormalBlock = BB; }383 384  bool isEHCleanup() const { return CleanupBits.IsEHCleanup; }385 386  bool isActive() const { return CleanupBits.IsActive; }387  void setActive(bool A) { CleanupBits.IsActive = A; }388 389  bool isLifetimeMarker() const { return CleanupBits.IsLifetimeMarker; }390  void setLifetimeMarker() { CleanupBits.IsLifetimeMarker = true; }391 392  bool isFakeUse() const { return CleanupBits.IsFakeUse; }393  void setFakeUse() { CleanupBits.IsFakeUse = true; }394 395  bool hasActiveFlag() const { return ActiveFlag.isValid(); }396  Address getActiveFlag() const {397    return ActiveFlag;398  }399  void setActiveFlag(RawAddress Var) {400    assert(Var.getAlignment().isOne());401    ActiveFlag = Var;402  }403 404  void setTestFlagInNormalCleanup() {405    CleanupBits.TestFlagInNormalCleanup = true;406  }407  bool shouldTestFlagInNormalCleanup() const {408    return CleanupBits.TestFlagInNormalCleanup;409  }410 411  void setTestFlagInEHCleanup() {412    CleanupBits.TestFlagInEHCleanup = true;413  }414  bool shouldTestFlagInEHCleanup() const {415    return CleanupBits.TestFlagInEHCleanup;416  }417 418  unsigned getFixupDepth() const { return FixupDepth; }419  EHScopeStack::stable_iterator getEnclosingNormalCleanup() const {420    return EnclosingNormal;421  }422 423  size_t getCleanupSize() const { return CleanupBits.CleanupSize; }424  void *getCleanupBuffer() { return this + 1; }425 426  EHScopeStack::Cleanup *getCleanup() {427    return reinterpret_cast<EHScopeStack::Cleanup*>(getCleanupBuffer());428  }429 430  /// True if this cleanup scope has any branch-afters or branch-throughs.431  bool hasBranches() const { return ExtInfo && !ExtInfo->Branches.empty(); }432 433  /// Add a branch-after to this cleanup scope.  A branch-after is a434  /// branch from a point protected by this (normal) cleanup to a435  /// point in the normal cleanup scope immediately containing it.436  /// For example,437  ///   for (;;) { A a; break; }438  /// contains a branch-after.439  ///440  /// Branch-afters each have their own destination out of the441  /// cleanup, guaranteed distinct from anything else threaded through442  /// it.  Therefore branch-afters usually force a switch after the443  /// cleanup.444  void addBranchAfter(llvm::ConstantInt *Index,445                      llvm::BasicBlock *Block) {446    struct ExtInfo &ExtInfo = getExtInfo();447    if (ExtInfo.Branches.insert(Block).second)448      ExtInfo.BranchAfters.push_back(std::make_pair(Block, Index));449  }450 451  /// Return the number of unique branch-afters on this scope.452  unsigned getNumBranchAfters() const {453    return ExtInfo ? ExtInfo->BranchAfters.size() : 0;454  }455 456  llvm::BasicBlock *getBranchAfterBlock(unsigned I) const {457    assert(I < getNumBranchAfters());458    return ExtInfo->BranchAfters[I].first;459  }460 461  llvm::ConstantInt *getBranchAfterIndex(unsigned I) const {462    assert(I < getNumBranchAfters());463    return ExtInfo->BranchAfters[I].second;464  }465 466  /// Add a branch-through to this cleanup scope.  A branch-through is467  /// a branch from a scope protected by this (normal) cleanup to an468  /// enclosing scope other than the immediately-enclosing normal469  /// cleanup scope.470  ///471  /// In the following example, the branch through B's scope is a472  /// branch-through, while the branch through A's scope is a473  /// branch-after:474  ///   for (;;) { A a; B b; break; }475  ///476  /// All branch-throughs have a common destination out of the477  /// cleanup, one possibly shared with the fall-through.  Therefore478  /// branch-throughs usually don't force a switch after the cleanup.479  ///480  /// \return true if the branch-through was new to this scope481  bool addBranchThrough(llvm::BasicBlock *Block) {482    return getExtInfo().Branches.insert(Block).second;483  }484 485  /// Determines if this cleanup scope has any branch throughs.486  bool hasBranchThroughs() const {487    if (!ExtInfo) return false;488    return (ExtInfo->BranchAfters.size() != ExtInfo->Branches.size());489  }490 491  static bool classof(const EHScope *Scope) {492    return (Scope->getKind() == Cleanup);493  }494};495// NOTE: there's a bunch of different data classes tacked on after an496// EHCleanupScope. It is asserted (in EHScopeStack::pushCleanup*) that497// they don't require greater alignment than ScopeStackAlignment. So,498// EHCleanupScope ought to have alignment equal to that -- not more499// (would be misaligned by the stack allocator), and not less (would500// break the appended classes).501static_assert(alignof(EHCleanupScope) == EHScopeStack::ScopeStackAlignment,502              "EHCleanupScope expected alignment");503 504/// An exceptions scope which filters exceptions thrown through it.505/// Only exceptions matching the filter types will be permitted to be506/// thrown.507///508/// This is used to implement C++ exception specifications.509class EHFilterScope : public EHScope {510  // Essentially ends in a flexible array member:511  // llvm::Value *FilterTypes[0];512 513  llvm::Value **getFilters() {514    return reinterpret_cast<llvm::Value**>(this+1);515  }516 517  llvm::Value * const *getFilters() const {518    return reinterpret_cast<llvm::Value* const *>(this+1);519  }520 521public:522  EHFilterScope(unsigned numFilters)523    : EHScope(Filter, EHScopeStack::stable_end()) {524    FilterBits.NumFilters = numFilters;525    assert(FilterBits.NumFilters == numFilters && "NumFilters overflow");526  }527 528  static size_t getSizeForNumFilters(unsigned numFilters) {529    return sizeof(EHFilterScope) + numFilters * sizeof(llvm::Value*);530  }531 532  unsigned getNumFilters() const { return FilterBits.NumFilters; }533 534  void setFilter(unsigned i, llvm::Value *filterValue) {535    assert(i < getNumFilters());536    getFilters()[i] = filterValue;537  }538 539  llvm::Value *getFilter(unsigned i) const {540    assert(i < getNumFilters());541    return getFilters()[i];542  }543 544  static bool classof(const EHScope *scope) {545    return scope->getKind() == Filter;546  }547};548 549/// An exceptions scope which calls std::terminate if any exception550/// reaches it.551class EHTerminateScope : public EHScope {552public:553  EHTerminateScope(EHScopeStack::stable_iterator enclosingEHScope)554    : EHScope(Terminate, enclosingEHScope) {}555  static size_t getSize() { return sizeof(EHTerminateScope); }556 557  static bool classof(const EHScope *scope) {558    return scope->getKind() == Terminate;559  }560};561 562/// A non-stable pointer into the scope stack.563class EHScopeStack::iterator {564  char *Ptr;565 566  friend class EHScopeStack;567  explicit iterator(char *Ptr) : Ptr(Ptr) {}568 569public:570  iterator() : Ptr(nullptr) {}571 572  EHScope *get() const {573    return reinterpret_cast<EHScope*>(Ptr);574  }575 576  EHScope *operator->() const { return get(); }577  EHScope &operator*() const { return *get(); }578 579  iterator &operator++() {580    size_t Size;581    switch (get()->getKind()) {582    case EHScope::Catch:583      Size = EHCatchScope::getSizeForNumHandlers(584          static_cast<const EHCatchScope *>(get())->getNumHandlers());585      break;586 587    case EHScope::Filter:588      Size = EHFilterScope::getSizeForNumFilters(589          static_cast<const EHFilterScope *>(get())->getNumFilters());590      break;591 592    case EHScope::Cleanup:593      Size = static_cast<const EHCleanupScope *>(get())->getAllocatedSize();594      break;595 596    case EHScope::Terminate:597      Size = EHTerminateScope::getSize();598      break;599    }600    Ptr += llvm::alignTo(Size, ScopeStackAlignment);601    return *this;602  }603 604  iterator next() {605    iterator copy = *this;606    ++copy;607    return copy;608  }609 610  iterator operator++(int) {611    iterator copy = *this;612    operator++();613    return copy;614  }615 616  bool encloses(iterator other) const { return Ptr >= other.Ptr; }617  bool strictlyEncloses(iterator other) const { return Ptr > other.Ptr; }618 619  bool operator==(iterator other) const { return Ptr == other.Ptr; }620  bool operator!=(iterator other) const { return Ptr != other.Ptr; }621};622 623inline EHScopeStack::iterator EHScopeStack::begin() const {624  return iterator(StartOfData);625}626 627inline EHScopeStack::iterator EHScopeStack::end() const {628  return iterator(EndOfBuffer);629}630 631inline void EHScopeStack::popCatch() {632  assert(!empty() && "popping exception stack when not empty");633 634  EHCatchScope &scope = cast<EHCatchScope>(*begin());635  InnermostEHScope = scope.getEnclosingEHScope();636  deallocate(EHCatchScope::getSizeForNumHandlers(scope.getNumHandlers()));637}638 639inline void EHScopeStack::popTerminate() {640  assert(!empty() && "popping exception stack when not empty");641 642  EHTerminateScope &scope = cast<EHTerminateScope>(*begin());643  InnermostEHScope = scope.getEnclosingEHScope();644  deallocate(EHTerminateScope::getSize());645}646 647inline EHScopeStack::iterator EHScopeStack::find(stable_iterator sp) const {648  assert(sp.isValid() && "finding invalid savepoint");649  assert(sp.Size <= stable_begin().Size && "finding savepoint after pop");650  return iterator(EndOfBuffer - sp.Size);651}652 653inline EHScopeStack::stable_iterator654EHScopeStack::stabilize(iterator ir) const {655  assert(StartOfData <= ir.Ptr && ir.Ptr <= EndOfBuffer);656  return stable_iterator(EndOfBuffer - ir.Ptr);657}658 659/// The exceptions personality for a function.660struct EHPersonality {661  const char *PersonalityFn;662 663  // If this is non-null, this personality requires a non-standard664  // function for rethrowing an exception after a catchall cleanup.665  // This function must have prototype void(void*).666  const char *CatchallRethrowFn;667 668  static const EHPersonality &get(CodeGenModule &CGM, const FunctionDecl *FD);669  static const EHPersonality &get(CodeGenFunction &CGF);670 671  static const EHPersonality GNU_C;672  static const EHPersonality GNU_C_SJLJ;673  static const EHPersonality GNU_C_SEH;674  static const EHPersonality GNU_ObjC;675  static const EHPersonality GNU_ObjC_SJLJ;676  static const EHPersonality GNU_ObjC_SEH;677  static const EHPersonality GNUstep_ObjC;678  static const EHPersonality GNU_ObjCXX;679  static const EHPersonality NeXT_ObjC;680  static const EHPersonality GNU_CPlusPlus;681  static const EHPersonality GNU_CPlusPlus_SJLJ;682  static const EHPersonality GNU_CPlusPlus_SEH;683  static const EHPersonality MSVC_except_handler;684  static const EHPersonality MSVC_C_specific_handler;685  static const EHPersonality MSVC_CxxFrameHandler3;686  static const EHPersonality GNU_Wasm_CPlusPlus;687  static const EHPersonality XL_CPlusPlus;688  static const EHPersonality ZOS_CPlusPlus;689 690  /// Does this personality use landingpads or the family of pad instructions691  /// designed to form funclets?692  bool usesFuncletPads() const {693    return isMSVCPersonality() || isWasmPersonality();694  }695 696  bool isMSVCPersonality() const {697    return this == &MSVC_except_handler || this == &MSVC_C_specific_handler ||698           this == &MSVC_CxxFrameHandler3;699  }700 701  bool isWasmPersonality() const { return this == &GNU_Wasm_CPlusPlus; }702 703  bool isMSVCXXPersonality() const { return this == &MSVC_CxxFrameHandler3; }704};705}706}707 708#endif709