brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.0 KiB · 6b381b5 Raw
693 lines · c
1//===-- CGValue.h - LLVM CodeGen wrappers for llvm::Value* ------*- 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 implement wrappers around llvm::Value in order to10// fully represent the range of values for C L- and R- values.11//12//===----------------------------------------------------------------------===//13 14#ifndef LLVM_CLANG_LIB_CODEGEN_CGVALUE_H15#define LLVM_CLANG_LIB_CODEGEN_CGVALUE_H16 17#include "Address.h"18#include "CGPointerAuthInfo.h"19#include "CodeGenTBAA.h"20#include "EHScopeStack.h"21#include "clang/AST/ASTContext.h"22#include "clang/AST/Type.h"23#include "llvm/IR/Type.h"24#include "llvm/IR/Value.h"25 26namespace llvm {27  class Constant;28  class MDNode;29}30 31namespace clang {32namespace CodeGen {33class AggValueSlot;34class CGBuilderTy;35class CodeGenFunction;36struct CGBitFieldInfo;37 38/// RValue - This trivial value class is used to represent the result of an39/// expression that is evaluated.  It can be one of three things: either a40/// simple LLVM SSA value, a pair of SSA values for complex numbers, or the41/// address of an aggregate value in memory.42class RValue {43  friend struct DominatingValue<RValue>;44 45  enum FlavorEnum { Scalar, Complex, Aggregate };46 47  union {48    // Stores first and second value.49    struct {50      llvm::Value *first;51      llvm::Value *second;52    } Vals;53 54    // Stores aggregate address.55    Address AggregateAddr;56  };57 58  unsigned IsVolatile : 1;59  unsigned Flavor : 2;60 61public:62  RValue() : Vals{nullptr, nullptr}, Flavor(Scalar) {}63 64  bool isScalar() const { return Flavor == Scalar; }65  bool isComplex() const { return Flavor == Complex; }66  bool isAggregate() const { return Flavor == Aggregate; }67  bool isIgnored() const { return isScalar() && !getScalarVal(); }68 69  bool isVolatileQualified() const { return IsVolatile; }70 71  /// getScalarVal() - Return the Value* of this scalar value.72  llvm::Value *getScalarVal() const {73    assert(isScalar() && "Not a scalar!");74    return Vals.first;75  }76 77  /// getComplexVal - Return the real/imag components of this complex value.78  ///79  std::pair<llvm::Value *, llvm::Value *> getComplexVal() const {80    return std::make_pair(Vals.first, Vals.second);81  }82 83  /// getAggregateAddr() - Return the Value* of the address of the aggregate.84  Address getAggregateAddress() const {85    assert(isAggregate() && "Not an aggregate!");86    return AggregateAddr;87  }88 89  llvm::Value *getAggregatePointer(QualType PointeeType,90                                   CodeGenFunction &CGF) const {91    return getAggregateAddress().getBasePointer();92  }93 94  static RValue getIgnored() {95    // FIXME: should we make this a more explicit state?96    return get(nullptr);97  }98 99  static RValue get(llvm::Value *V) {100    RValue ER;101    ER.Vals.first = V;102    ER.Flavor = Scalar;103    ER.IsVolatile = false;104    return ER;105  }106  static RValue get(Address Addr, CodeGenFunction &CGF) {107    return RValue::get(Addr.emitRawPointer(CGF));108  }109  static RValue getComplex(llvm::Value *V1, llvm::Value *V2) {110    RValue ER;111    ER.Vals = {V1, V2};112    ER.Flavor = Complex;113    ER.IsVolatile = false;114    return ER;115  }116  static RValue getComplex(const std::pair<llvm::Value *, llvm::Value *> &C) {117    return getComplex(C.first, C.second);118  }119  // FIXME: Aggregate rvalues need to retain information about whether they are120  // volatile or not.  Remove default to find all places that probably get this121  // wrong.122 123  /// Convert an Address to an RValue. If the Address is not124  /// signed, create an RValue using the unsigned address. Otherwise, resign the125  /// address using the provided type.126  static RValue getAggregate(Address addr, bool isVolatile = false) {127    RValue ER;128    ER.AggregateAddr = addr;129    ER.Flavor = Aggregate;130    ER.IsVolatile = isVolatile;131    return ER;132  }133};134 135/// Does an ARC strong l-value have precise lifetime?136enum ARCPreciseLifetime_t {137  ARCImpreciseLifetime, ARCPreciseLifetime138};139 140/// The source of the alignment of an l-value; an expression of141/// confidence in the alignment actually matching the estimate.142enum class AlignmentSource {143  /// The l-value was an access to a declared entity or something144  /// equivalently strong, like the address of an array allocated by a145  /// language runtime.146  Decl,147 148  /// The l-value was considered opaque, so the alignment was149  /// determined from a type, but that type was an explicitly-aligned150  /// typedef.151  AttributedType,152 153  /// The l-value was considered opaque, so the alignment was154  /// determined from a type.155  Type156};157 158/// Given that the base address has the given alignment source, what's159/// our confidence in the alignment of the field?160static inline AlignmentSource getFieldAlignmentSource(AlignmentSource Source) {161  // For now, we don't distinguish fields of opaque pointers from162  // top-level declarations, but maybe we should.163  return AlignmentSource::Decl;164}165 166class LValueBaseInfo {167  AlignmentSource AlignSource;168 169public:170  explicit LValueBaseInfo(AlignmentSource Source = AlignmentSource::Type)171    : AlignSource(Source) {}172  AlignmentSource getAlignmentSource() const { return AlignSource; }173  void setAlignmentSource(AlignmentSource Source) { AlignSource = Source; }174 175  void mergeForCast(const LValueBaseInfo &Info) {176    setAlignmentSource(Info.getAlignmentSource());177  }178};179 180/// LValue - This represents an lvalue references.  Because C/C++ allow181/// bitfields, this is not a simple LLVM pointer, it may be a pointer plus a182/// bitrange.183class LValue {184  enum {185    Simple,       // This is a normal l-value, use getAddress().186    VectorElt,    // This is a vector element l-value (V[i]), use getVector*187    BitField,     // This is a bitfield l-value, use getBitfield*.188    ExtVectorElt, // This is an extended vector subset, use getExtVectorComp189    GlobalReg,    // This is a register l-value, use getGlobalReg()190    MatrixElt     // This is a matrix element, use getVector*191  } LVType;192 193  union {194    Address Addr = Address::invalid();195    llvm::Value *V;196  };197 198  union {199    // Index into a vector subscript: V[i]200    llvm::Value *VectorIdx;201 202    // ExtVector element subset: V.xyx203    llvm::Constant *VectorElts;204 205    // BitField start bit and size206    const CGBitFieldInfo *BitFieldInfo;207  };208 209  QualType Type;210 211  // 'const' is unused here212  Qualifiers Quals;213 214  // objective-c's ivar215  bool Ivar:1;216 217  // objective-c's ivar is an array218  bool ObjIsArray:1;219 220  // LValue is non-gc'able for any reason, including being a parameter or local221  // variable.222  bool NonGC: 1;223 224  // Lvalue is a global reference of an objective-c object225  bool GlobalObjCRef : 1;226 227  // Lvalue is a thread local reference228  bool ThreadLocalRef : 1;229 230  // Lvalue has ARC imprecise lifetime.  We store this inverted to try231  // to make the default bitfield pattern all-zeroes.232  bool ImpreciseLifetime : 1;233 234  // This flag shows if a nontemporal load/stores should be used when accessing235  // this lvalue.236  bool Nontemporal : 1;237 238  LValueBaseInfo BaseInfo;239  TBAAAccessInfo TBAAInfo;240 241  Expr *BaseIvarExp;242 243private:244  void Initialize(QualType Type, Qualifiers Quals, Address Addr,245                  LValueBaseInfo BaseInfo, TBAAAccessInfo TBAAInfo) {246    this->Type = Type;247    this->Quals = Quals;248    const unsigned MaxAlign = 1U << 31;249    CharUnits Alignment = Addr.getAlignment();250    assert((isGlobalReg() || !Alignment.isZero() || Type->isIncompleteType()) &&251           "initializing l-value with zero alignment!");252    if (Alignment.getQuantity() > MaxAlign) {253      assert(false && "Alignment exceeds allowed max!");254      Alignment = CharUnits::fromQuantity(MaxAlign);255    }256    this->Addr = Addr;257    this->BaseInfo = BaseInfo;258    this->TBAAInfo = TBAAInfo;259 260    // Initialize Objective-C flags.261    this->Ivar = this->ObjIsArray = this->NonGC = this->GlobalObjCRef = false;262    this->ImpreciseLifetime = false;263    this->Nontemporal = false;264    this->ThreadLocalRef = false;265    this->BaseIvarExp = nullptr;266  }267 268  void initializeSimpleLValue(Address Addr, QualType Type,269                              LValueBaseInfo BaseInfo, TBAAAccessInfo TBAAInfo,270                              ASTContext &Context) {271    Qualifiers QS = Type.getQualifiers();272    QS.setObjCGCAttr(Context.getObjCGCAttrKind(Type));273    LVType = Simple;274    Initialize(Type, QS, Addr, BaseInfo, TBAAInfo);275    assert(Addr.getBasePointer()->getType()->isPointerTy());276  }277 278public:279  bool isSimple() const { return LVType == Simple; }280  bool isVectorElt() const { return LVType == VectorElt; }281  bool isBitField() const { return LVType == BitField; }282  bool isExtVectorElt() const { return LVType == ExtVectorElt; }283  bool isGlobalReg() const { return LVType == GlobalReg; }284  bool isMatrixElt() const { return LVType == MatrixElt; }285 286  bool isVolatileQualified() const { return Quals.hasVolatile(); }287  bool isRestrictQualified() const { return Quals.hasRestrict(); }288  unsigned getVRQualifiers() const {289    return Quals.getCVRQualifiers() & ~Qualifiers::Const;290  }291 292  QualType getType() const { return Type; }293 294  Qualifiers::ObjCLifetime getObjCLifetime() const {295    return Quals.getObjCLifetime();296  }297 298  bool isObjCIvar() const { return Ivar; }299  void setObjCIvar(bool Value) { Ivar = Value; }300 301  bool isObjCArray() const { return ObjIsArray; }302  void setObjCArray(bool Value) { ObjIsArray = Value; }303 304  bool isNonGC () const { return NonGC; }305  void setNonGC(bool Value) { NonGC = Value; }306 307  bool isGlobalObjCRef() const { return GlobalObjCRef; }308  void setGlobalObjCRef(bool Value) { GlobalObjCRef = Value; }309 310  bool isThreadLocalRef() const { return ThreadLocalRef; }311  void setThreadLocalRef(bool Value) { ThreadLocalRef = Value;}312 313  ARCPreciseLifetime_t isARCPreciseLifetime() const {314    return ARCPreciseLifetime_t(!ImpreciseLifetime);315  }316  void setARCPreciseLifetime(ARCPreciseLifetime_t value) {317    ImpreciseLifetime = (value == ARCImpreciseLifetime);318  }319  bool isNontemporal() const { return Nontemporal; }320  void setNontemporal(bool Value) { Nontemporal = Value; }321 322  bool isObjCWeak() const {323    return Quals.getObjCGCAttr() == Qualifiers::Weak;324  }325  bool isObjCStrong() const {326    return Quals.getObjCGCAttr() == Qualifiers::Strong;327  }328 329  bool isVolatile() const {330    return Quals.hasVolatile();331  }332 333  Expr *getBaseIvarExp() const { return BaseIvarExp; }334  void setBaseIvarExp(Expr *V) { BaseIvarExp = V; }335 336  TBAAAccessInfo getTBAAInfo() const { return TBAAInfo; }337  void setTBAAInfo(TBAAAccessInfo Info) { TBAAInfo = Info; }338 339  const Qualifiers &getQuals() const { return Quals; }340  Qualifiers &getQuals() { return Quals; }341 342  LangAS getAddressSpace() const { return Quals.getAddressSpace(); }343 344  CharUnits getAlignment() const { return Addr.getAlignment(); }345  void setAlignment(CharUnits A) { Addr.setAlignment(A); }346 347  LValueBaseInfo getBaseInfo() const { return BaseInfo; }348  void setBaseInfo(LValueBaseInfo Info) { BaseInfo = Info; }349 350  KnownNonNull_t isKnownNonNull() const { return Addr.isKnownNonNull(); }351  LValue setKnownNonNull() {352    Addr.setKnownNonNull();353    return *this;354  }355 356  // simple lvalue357  llvm::Value *getPointer(CodeGenFunction &CGF) const;358  llvm::Value *emitResignedPointer(QualType PointeeTy,359                                   CodeGenFunction &CGF) const;360  llvm::Value *emitRawPointer(CodeGenFunction &CGF) const;361 362  Address getAddress() const { return Addr; }363 364  void setAddress(Address address) { Addr = address; }365 366  CGPointerAuthInfo getPointerAuthInfo() const {367    return Addr.getPointerAuthInfo();368  }369 370  // vector elt lvalue371  Address getVectorAddress() const {372    assert(isVectorElt());373    return Addr;374  }375  llvm::Value *getRawVectorPointer(CodeGenFunction &CGF) const {376    assert(isVectorElt());377    return Addr.emitRawPointer(CGF);378  }379  llvm::Value *getVectorPointer() const {380    assert(isVectorElt());381    return Addr.getBasePointer();382  }383  llvm::Value *getVectorIdx() const {384    assert(isVectorElt());385    return VectorIdx;386  }387 388  Address getMatrixAddress() const {389    assert(isMatrixElt());390    return Addr;391  }392  llvm::Value *getMatrixPointer() const {393    assert(isMatrixElt());394    return Addr.getBasePointer();395  }396  llvm::Value *getMatrixIdx() const {397    assert(isMatrixElt());398    return VectorIdx;399  }400 401  // extended vector elements.402  Address getExtVectorAddress() const {403    assert(isExtVectorElt());404    return Addr;405  }406  llvm::Value *getRawExtVectorPointer(CodeGenFunction &CGF) const {407    assert(isExtVectorElt());408    return Addr.emitRawPointer(CGF);409  }410  llvm::Constant *getExtVectorElts() const {411    assert(isExtVectorElt());412    return VectorElts;413  }414 415  // bitfield lvalue416  Address getBitFieldAddress() const {417    assert(isBitField());418    return Addr;419  }420  llvm::Value *getRawBitFieldPointer(CodeGenFunction &CGF) const {421    assert(isBitField());422    return Addr.emitRawPointer(CGF);423  }424 425  const CGBitFieldInfo &getBitFieldInfo() const {426    assert(isBitField());427    return *BitFieldInfo;428  }429 430  // global register lvalue431  llvm::Value *getGlobalReg() const { assert(isGlobalReg()); return V; }432 433  static LValue MakeAddr(Address Addr, QualType type, ASTContext &Context,434                         LValueBaseInfo BaseInfo, TBAAAccessInfo TBAAInfo) {435    LValue R;436    R.LVType = Simple;437    R.initializeSimpleLValue(Addr, type, BaseInfo, TBAAInfo, Context);438    R.Addr = Addr;439    assert(Addr.getType()->isPointerTy());440    return R;441  }442 443  static LValue MakeVectorElt(Address vecAddress, llvm::Value *Idx,444                              QualType type, LValueBaseInfo BaseInfo,445                              TBAAAccessInfo TBAAInfo) {446    LValue R;447    R.LVType = VectorElt;448    R.VectorIdx = Idx;449    R.Initialize(type, type.getQualifiers(), vecAddress, BaseInfo, TBAAInfo);450    return R;451  }452 453  static LValue MakeExtVectorElt(Address Addr, llvm::Constant *Elts,454                                 QualType type, LValueBaseInfo BaseInfo,455                                 TBAAAccessInfo TBAAInfo) {456    LValue R;457    R.LVType = ExtVectorElt;458    R.VectorElts = Elts;459    R.Initialize(type, type.getQualifiers(), Addr, BaseInfo, TBAAInfo);460    return R;461  }462 463  /// Create a new object to represent a bit-field access.464  ///465  /// \param Addr - The base address of the bit-field sequence this466  /// bit-field refers to.467  /// \param Info - The information describing how to perform the bit-field468  /// access.469  static LValue MakeBitfield(Address Addr, const CGBitFieldInfo &Info,470                             QualType type, LValueBaseInfo BaseInfo,471                             TBAAAccessInfo TBAAInfo) {472    LValue R;473    R.LVType = BitField;474    R.BitFieldInfo = &Info;475    R.Initialize(type, type.getQualifiers(), Addr, BaseInfo, TBAAInfo);476    return R;477  }478 479  static LValue MakeGlobalReg(llvm::Value *V, CharUnits alignment,480                              QualType type) {481    LValue R;482    R.LVType = GlobalReg;483    R.Initialize(type, type.getQualifiers(), Address::invalid(),484                 LValueBaseInfo(AlignmentSource::Decl), TBAAAccessInfo());485    R.V = V;486    return R;487  }488 489  static LValue MakeMatrixElt(Address matAddress, llvm::Value *Idx,490                              QualType type, LValueBaseInfo BaseInfo,491                              TBAAAccessInfo TBAAInfo) {492    LValue R;493    R.LVType = MatrixElt;494    R.VectorIdx = Idx;495    R.Initialize(type, type.getQualifiers(), matAddress, BaseInfo, TBAAInfo);496    return R;497  }498 499  RValue asAggregateRValue() const {500    return RValue::getAggregate(getAddress(), isVolatileQualified());501  }502};503 504/// An aggregate value slot.505class AggValueSlot {506  /// The address.507  Address Addr;508 509  // Qualifiers510  Qualifiers Quals;511 512  /// DestructedFlag - This is set to true if some external code is513  /// responsible for setting up a destructor for the slot.  Otherwise514  /// the code which constructs it should push the appropriate cleanup.515  bool DestructedFlag : 1;516 517  /// ObjCGCFlag - This is set to true if writing to the memory in the518  /// slot might require calling an appropriate Objective-C GC519  /// barrier.  The exact interaction here is unnecessarily mysterious.520  bool ObjCGCFlag : 1;521 522  /// ZeroedFlag - This is set to true if the memory in the slot is523  /// known to be zero before the assignment into it.  This means that524  /// zero fields don't need to be set.525  bool ZeroedFlag : 1;526 527  /// AliasedFlag - This is set to true if the slot might be aliased528  /// and it's not undefined behavior to access it through such an529  /// alias.  Note that it's always undefined behavior to access a C++530  /// object that's under construction through an alias derived from531  /// outside the construction process.532  ///533  /// This flag controls whether calls that produce the aggregate534  /// value may be evaluated directly into the slot, or whether they535  /// must be evaluated into an unaliased temporary and then memcpy'ed536  /// over.  Since it's invalid in general to memcpy a non-POD C++537  /// object, it's important that this flag never be set when538  /// evaluating an expression which constructs such an object.539  bool AliasedFlag : 1;540 541  /// This is set to true if the tail padding of this slot might overlap542  /// another object that may have already been initialized (and whose543  /// value must be preserved by this initialization). If so, we may only544  /// store up to the dsize of the type. Otherwise we can widen stores to545  /// the size of the type.546  bool OverlapFlag : 1;547 548  /// If is set to true, sanitizer checks are already generated for this address549  /// or not required. For instance, if this address represents an object550  /// created in 'new' expression, sanitizer checks for memory is made as a part551  /// of 'operator new' emission and object constructor should not generate552  /// them.553  bool SanitizerCheckedFlag : 1;554 555  AggValueSlot(Address Addr, Qualifiers Quals, bool DestructedFlag,556               bool ObjCGCFlag, bool ZeroedFlag, bool AliasedFlag,557               bool OverlapFlag, bool SanitizerCheckedFlag)558      : Addr(Addr), Quals(Quals), DestructedFlag(DestructedFlag),559        ObjCGCFlag(ObjCGCFlag), ZeroedFlag(ZeroedFlag),560        AliasedFlag(AliasedFlag), OverlapFlag(OverlapFlag),561        SanitizerCheckedFlag(SanitizerCheckedFlag) {}562 563public:564  enum IsAliased_t { IsNotAliased, IsAliased };565  enum IsDestructed_t { IsNotDestructed, IsDestructed };566  enum IsZeroed_t { IsNotZeroed, IsZeroed };567  enum Overlap_t { DoesNotOverlap, MayOverlap };568  enum NeedsGCBarriers_t { DoesNotNeedGCBarriers, NeedsGCBarriers };569  enum IsSanitizerChecked_t { IsNotSanitizerChecked, IsSanitizerChecked };570 571  /// ignored - Returns an aggregate value slot indicating that the572  /// aggregate value is being ignored.573  static AggValueSlot ignored() {574    return forAddr(Address::invalid(), Qualifiers(), IsNotDestructed,575                   DoesNotNeedGCBarriers, IsNotAliased, DoesNotOverlap);576  }577 578  /// forAddr - Make a slot for an aggregate value.579  ///580  /// \param quals - The qualifiers that dictate how the slot should581  /// be initialied. Only 'volatile' and the Objective-C lifetime582  /// qualifiers matter.583  ///584  /// \param isDestructed - true if something else is responsible585  ///   for calling destructors on this object586  /// \param needsGC - true if the slot is potentially located587  ///   somewhere that ObjC GC calls should be emitted for588  static AggValueSlot forAddr(Address addr,589                              Qualifiers quals,590                              IsDestructed_t isDestructed,591                              NeedsGCBarriers_t needsGC,592                              IsAliased_t isAliased,593                              Overlap_t mayOverlap,594                              IsZeroed_t isZeroed = IsNotZeroed,595                       IsSanitizerChecked_t isChecked = IsNotSanitizerChecked) {596    if (addr.isValid())597      addr.setKnownNonNull();598    return AggValueSlot(addr, quals, isDestructed, needsGC, isZeroed, isAliased,599                        mayOverlap, isChecked);600  }601 602  static AggValueSlot603  forLValue(const LValue &LV, IsDestructed_t isDestructed,604            NeedsGCBarriers_t needsGC, IsAliased_t isAliased,605            Overlap_t mayOverlap, IsZeroed_t isZeroed = IsNotZeroed,606            IsSanitizerChecked_t isChecked = IsNotSanitizerChecked) {607    return forAddr(LV.getAddress(), LV.getQuals(), isDestructed, needsGC,608                   isAliased, mayOverlap, isZeroed, isChecked);609  }610 611  IsDestructed_t isExternallyDestructed() const {612    return IsDestructed_t(DestructedFlag);613  }614  void setExternallyDestructed(bool destructed = true) {615    DestructedFlag = destructed;616  }617 618  Qualifiers getQualifiers() const { return Quals; }619 620  bool isVolatile() const {621    return Quals.hasVolatile();622  }623 624  void setVolatile(bool flag) {625    if (flag)626      Quals.addVolatile();627    else628      Quals.removeVolatile();629  }630 631  Qualifiers::ObjCLifetime getObjCLifetime() const {632    return Quals.getObjCLifetime();633  }634 635  NeedsGCBarriers_t requiresGCollection() const {636    return NeedsGCBarriers_t(ObjCGCFlag);637  }638 639  llvm::Value *getPointer(QualType PointeeTy, CodeGenFunction &CGF) const;640 641  llvm::Value *emitRawPointer(CodeGenFunction &CGF) const {642    return Addr.isValid() ? Addr.emitRawPointer(CGF) : nullptr;643  }644 645  Address getAddress() const {646    return Addr;647  }648 649  bool isIgnored() const { return !Addr.isValid(); }650 651  CharUnits getAlignment() const {652    return Addr.getAlignment();653  }654 655  IsAliased_t isPotentiallyAliased() const {656    return IsAliased_t(AliasedFlag);657  }658 659  Overlap_t mayOverlap() const {660    return Overlap_t(OverlapFlag);661  }662 663  bool isSanitizerChecked() const {664    return SanitizerCheckedFlag;665  }666 667  RValue asRValue() const {668    if (isIgnored()) {669      return RValue::getIgnored();670    } else {671      return RValue::getAggregate(getAddress(), isVolatile());672    }673  }674 675  void setZeroed(bool V = true) { ZeroedFlag = V; }676  IsZeroed_t isZeroed() const {677    return IsZeroed_t(ZeroedFlag);678  }679 680  /// Get the preferred size to use when storing a value to this slot. This681  /// is the type size unless that might overlap another object, in which682  /// case it's the dsize.683  CharUnits getPreferredSize(ASTContext &Ctx, QualType Type) const {684    return mayOverlap() ? Ctx.getTypeInfoDataSizeInChars(Type).Width685                        : Ctx.getTypeSizeInChars(Type);686  }687};688 689}  // end namespace CodeGen690}  // end namespace clang691 692#endif693