Home | History | Annotate | Line # | Download | only in CodeGen
      1 //===- ConstantInitBuilder.h - Builder for LLVM IR constants ----*- 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-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // This class provides a convenient interface for building complex
     10 // global initializers of the sort that are frequently required for
     11 // language ABIs.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #ifndef LLVM_CLANG_CODEGEN_CONSTANTINITBUILDER_H
     16 #define LLVM_CLANG_CODEGEN_CONSTANTINITBUILDER_H
     17 
     18 #include "llvm/ADT/ArrayRef.h"
     19 #include "llvm/ADT/SmallVector.h"
     20 #include "llvm/IR/Constants.h"
     21 #include "llvm/IR/GlobalValue.h"
     22 #include "clang/AST/CharUnits.h"
     23 #include "clang/CodeGen/ConstantInitFuture.h"
     24 
     25 #include <vector>
     26 
     27 namespace clang {
     28 namespace CodeGen {
     29 
     30 class CodeGenModule;
     31 
     32 /// A convenience builder class for complex constant initializers,
     33 /// especially for anonymous global structures used by various language
     34 /// runtimes.
     35 ///
     36 /// The basic usage pattern is expected to be something like:
     37 ///    ConstantInitBuilder builder(CGM);
     38 ///    auto toplevel = builder.beginStruct();
     39 ///    toplevel.addInt(CGM.SizeTy, widgets.size());
     40 ///    auto widgetArray = builder.beginArray();
     41 ///    for (auto &widget : widgets) {
     42 ///      auto widgetDesc = widgetArray.beginStruct();
     43 ///      widgetDesc.addInt(CGM.SizeTy, widget.getPower());
     44 ///      widgetDesc.add(CGM.GetAddrOfConstantString(widget.getName()));
     45 ///      widgetDesc.add(CGM.GetAddrOfGlobal(widget.getInitializerDecl()));
     46 ///      widgetDesc.finishAndAddTo(widgetArray);
     47 ///    }
     48 ///    widgetArray.finishAndAddTo(toplevel);
     49 ///    auto global = toplevel.finishAndCreateGlobal("WIDGET_LIST", Align,
     50 ///                                                 /*constant*/ true);
     51 class ConstantInitBuilderBase {
     52   struct SelfReference {
     53     llvm::GlobalVariable *Dummy;
     54     llvm::SmallVector<llvm::Constant*, 4> Indices;
     55 
     56     SelfReference(llvm::GlobalVariable *dummy) : Dummy(dummy) {}
     57   };
     58   CodeGenModule &CGM;
     59   llvm::SmallVector<llvm::Constant*, 16> Buffer;
     60   std::vector<SelfReference> SelfReferences;
     61   bool Frozen = false;
     62 
     63   friend class ConstantInitFuture;
     64   friend class ConstantAggregateBuilderBase;
     65   template <class, class>
     66   friend class ConstantAggregateBuilderTemplateBase;
     67 
     68 protected:
     69   explicit ConstantInitBuilderBase(CodeGenModule &CGM) : CGM(CGM) {}
     70 
     71   ~ConstantInitBuilderBase() {
     72     assert(Buffer.empty() && "didn't claim all values out of buffer");
     73     assert(SelfReferences.empty() && "didn't apply all self-references");
     74   }
     75 
     76 private:
     77   llvm::GlobalVariable *createGlobal(llvm::Constant *initializer,
     78                                      const llvm::Twine &name,
     79                                      CharUnits alignment,
     80                                      bool constant = false,
     81                                      llvm::GlobalValue::LinkageTypes linkage
     82                                        = llvm::GlobalValue::InternalLinkage,
     83                                      unsigned addressSpace = 0);
     84 
     85   ConstantInitFuture createFuture(llvm::Constant *initializer);
     86 
     87   void setGlobalInitializer(llvm::GlobalVariable *GV,
     88                             llvm::Constant *initializer);
     89 
     90   void resolveSelfReferences(llvm::GlobalVariable *GV);
     91 
     92   void abandon(size_t newEnd);
     93 };
     94 
     95 /// A concrete base class for struct and array aggregate
     96 /// initializer builders.
     97 class ConstantAggregateBuilderBase {
     98 protected:
     99   ConstantInitBuilderBase &Builder;
    100   ConstantAggregateBuilderBase *Parent;
    101   size_t Begin;
    102   mutable size_t CachedOffsetEnd = 0;
    103   bool Finished = false;
    104   bool Frozen = false;
    105   bool Packed = false;
    106   mutable CharUnits CachedOffsetFromGlobal;
    107 
    108   llvm::SmallVectorImpl<llvm::Constant*> &getBuffer() {
    109     return Builder.Buffer;
    110   }
    111 
    112   const llvm::SmallVectorImpl<llvm::Constant*> &getBuffer() const {
    113     return Builder.Buffer;
    114   }
    115 
    116   ConstantAggregateBuilderBase(ConstantInitBuilderBase &builder,
    117                                ConstantAggregateBuilderBase *parent)
    118       : Builder(builder), Parent(parent), Begin(builder.Buffer.size()) {
    119     if (parent) {
    120       assert(!parent->Frozen && "parent already has child builder active");
    121       parent->Frozen = true;
    122     } else {
    123       assert(!builder.Frozen && "builder already has child builder active");
    124       builder.Frozen = true;
    125     }
    126   }
    127 
    128   ~ConstantAggregateBuilderBase() {
    129     assert(Finished && "didn't finish aggregate builder");
    130   }
    131 
    132   void markFinished() {
    133     assert(!Frozen && "child builder still active");
    134     assert(!Finished && "builder already finished");
    135     Finished = true;
    136     if (Parent) {
    137       assert(Parent->Frozen &&
    138              "parent not frozen while child builder active");
    139       Parent->Frozen = false;
    140     } else {
    141       assert(Builder.Frozen &&
    142              "builder not frozen while child builder active");
    143       Builder.Frozen = false;
    144     }
    145   }
    146 
    147 public:
    148   // Not copyable.
    149   ConstantAggregateBuilderBase(const ConstantAggregateBuilderBase &) = delete;
    150   ConstantAggregateBuilderBase &operator=(const ConstantAggregateBuilderBase &)
    151     = delete;
    152 
    153   // Movable, mostly to allow returning.  But we have to write this out
    154   // properly to satisfy the assert in the destructor.
    155   ConstantAggregateBuilderBase(ConstantAggregateBuilderBase &&other)
    156     : Builder(other.Builder), Parent(other.Parent), Begin(other.Begin),
    157       CachedOffsetEnd(other.CachedOffsetEnd),
    158       Finished(other.Finished), Frozen(other.Frozen), Packed(other.Packed),
    159       CachedOffsetFromGlobal(other.CachedOffsetFromGlobal) {
    160     other.Finished = true;
    161   }
    162   ConstantAggregateBuilderBase &operator=(ConstantAggregateBuilderBase &&other)
    163     = delete;
    164 
    165   /// Return the number of elements that have been added to
    166   /// this struct or array.
    167   size_t size() const {
    168     assert(!this->Finished && "cannot query after finishing builder");
    169     assert(!this->Frozen && "cannot query while sub-builder is active");
    170     assert(this->Begin <= this->getBuffer().size());
    171     return this->getBuffer().size() - this->Begin;
    172   }
    173 
    174   /// Return true if no elements have yet been added to this struct or array.
    175   bool empty() const {
    176     return size() == 0;
    177   }
    178 
    179   /// Abandon this builder completely.
    180   void abandon() {
    181     markFinished();
    182     Builder.abandon(Begin);
    183   }
    184 
    185   /// Add a new value to this initializer.
    186   void add(llvm::Constant *value) {
    187     assert(value && "adding null value to constant initializer");
    188     assert(!Finished && "cannot add more values after finishing builder");
    189     assert(!Frozen && "cannot add values while subbuilder is active");
    190     Builder.Buffer.push_back(value);
    191   }
    192 
    193   /// Add an integer value of type size_t.
    194   void addSize(CharUnits size);
    195 
    196   /// Add an integer value of a specific type.
    197   void addInt(llvm::IntegerType *intTy, uint64_t value,
    198               bool isSigned = false) {
    199     add(llvm::ConstantInt::get(intTy, value, isSigned));
    200   }
    201 
    202   /// Add a null pointer of a specific type.
    203   void addNullPointer(llvm::PointerType *ptrTy) {
    204     add(llvm::ConstantPointerNull::get(ptrTy));
    205   }
    206 
    207   /// Add a bitcast of a value to a specific type.
    208   void addBitCast(llvm::Constant *value, llvm::Type *type) {
    209     add(llvm::ConstantExpr::getBitCast(value, type));
    210   }
    211 
    212   /// Add a bunch of new values to this initializer.
    213   void addAll(llvm::ArrayRef<llvm::Constant *> values) {
    214     assert(!Finished && "cannot add more values after finishing builder");
    215     assert(!Frozen && "cannot add values while subbuilder is active");
    216     Builder.Buffer.append(values.begin(), values.end());
    217   }
    218 
    219   /// Add a relative offset to the given target address, i.e. the
    220   /// static difference between the target address and the address
    221   /// of the relative offset.  The target must be known to be defined
    222   /// in the current linkage unit.  The offset will have the given
    223   /// integer type, which must be no wider than intptr_t.  Some
    224   /// targets may not fully support this operation.
    225   void addRelativeOffset(llvm::IntegerType *type, llvm::Constant *target) {
    226     add(getRelativeOffset(type, target));
    227   }
    228 
    229   /// Same as addRelativeOffset(), but instead relative to an element in this
    230   /// aggregate, identified by its index.
    231   void addRelativeOffsetToPosition(llvm::IntegerType *type,
    232                                    llvm::Constant *target, size_t position) {
    233     add(getRelativeOffsetToPosition(type, target, position));
    234   }
    235 
    236   /// Add a relative offset to the target address, plus a small
    237   /// constant offset.  This is primarily useful when the relative
    238   /// offset is known to be a multiple of (say) four and therefore
    239   /// the tag can be used to express an extra two bits of information.
    240   void addTaggedRelativeOffset(llvm::IntegerType *type,
    241                                llvm::Constant *address,
    242                                unsigned tag) {
    243     llvm::Constant *offset = getRelativeOffset(type, address);
    244     if (tag) {
    245       offset = llvm::ConstantExpr::getAdd(offset,
    246                                           llvm::ConstantInt::get(type, tag));
    247     }
    248     add(offset);
    249   }
    250 
    251   /// Return the offset from the start of the initializer to the
    252   /// next position, assuming no padding is required prior to it.
    253   ///
    254   /// This operation will not succeed if any unsized placeholders are
    255   /// currently in place in the initializer.
    256   CharUnits getNextOffsetFromGlobal() const {
    257     assert(!Finished && "cannot add more values after finishing builder");
    258     assert(!Frozen && "cannot add values while subbuilder is active");
    259     return getOffsetFromGlobalTo(Builder.Buffer.size());
    260   }
    261 
    262   /// An opaque class to hold the abstract position of a placeholder.
    263   class PlaceholderPosition {
    264     size_t Index;
    265     friend class ConstantAggregateBuilderBase;
    266     PlaceholderPosition(size_t index) : Index(index) {}
    267   };
    268 
    269   /// Add a placeholder value to the structure.  The returned position
    270   /// can be used to set the value later; it will not be invalidated by
    271   /// any intermediate operations except (1) filling the same position or
    272   /// (2) finishing the entire builder.
    273   ///
    274   /// This is useful for emitting certain kinds of structure which
    275   /// contain some sort of summary field, generally a count, before any
    276   /// of the data.  By emitting a placeholder first, the structure can
    277   /// be emitted eagerly.
    278   PlaceholderPosition addPlaceholder() {
    279     assert(!Finished && "cannot add more values after finishing builder");
    280     assert(!Frozen && "cannot add values while subbuilder is active");
    281     Builder.Buffer.push_back(nullptr);
    282     return Builder.Buffer.size() - 1;
    283   }
    284 
    285   /// Add a placeholder, giving the expected type that will be filled in.
    286   PlaceholderPosition addPlaceholderWithSize(llvm::Type *expectedType);
    287 
    288   /// Fill a previously-added placeholder.
    289   void fillPlaceholderWithInt(PlaceholderPosition position,
    290                               llvm::IntegerType *type, uint64_t value,
    291                               bool isSigned = false) {
    292     fillPlaceholder(position, llvm::ConstantInt::get(type, value, isSigned));
    293   }
    294 
    295   /// Fill a previously-added placeholder.
    296   void fillPlaceholder(PlaceholderPosition position, llvm::Constant *value) {
    297     assert(!Finished && "cannot change values after finishing builder");
    298     assert(!Frozen && "cannot add values while subbuilder is active");
    299     llvm::Constant *&slot = Builder.Buffer[position.Index];
    300     assert(slot == nullptr && "placeholder already filled");
    301     slot = value;
    302   }
    303 
    304   /// Produce an address which will eventually point to the next
    305   /// position to be filled.  This is computed with an indexed
    306   /// getelementptr rather than by computing offsets.
    307   ///
    308   /// The returned pointer will have type T*, where T is the given type. This
    309   /// type can differ from the type of the actual element.
    310   llvm::Constant *getAddrOfCurrentPosition(llvm::Type *type);
    311 
    312   /// Produce an address which points to a position in the aggregate being
    313   /// constructed. This is computed with an indexed getelementptr rather than by
    314   /// computing offsets.
    315   ///
    316   /// The returned pointer will have type T*, where T is the given type. This
    317   /// type can differ from the type of the actual element.
    318   llvm::Constant *getAddrOfPosition(llvm::Type *type, size_t position);
    319 
    320   llvm::ArrayRef<llvm::Constant*> getGEPIndicesToCurrentPosition(
    321                            llvm::SmallVectorImpl<llvm::Constant*> &indices) {
    322     getGEPIndicesTo(indices, Builder.Buffer.size());
    323     return indices;
    324   }
    325 
    326 protected:
    327   llvm::Constant *finishArray(llvm::Type *eltTy);
    328   llvm::Constant *finishStruct(llvm::StructType *structTy);
    329 
    330 private:
    331   void getGEPIndicesTo(llvm::SmallVectorImpl<llvm::Constant*> &indices,
    332                        size_t position) const;
    333 
    334   llvm::Constant *getRelativeOffset(llvm::IntegerType *offsetType,
    335                                     llvm::Constant *target);
    336 
    337   llvm::Constant *getRelativeOffsetToPosition(llvm::IntegerType *offsetType,
    338                                               llvm::Constant *target,
    339                                               size_t position);
    340 
    341   CharUnits getOffsetFromGlobalTo(size_t index) const;
    342 };
    343 
    344 template <class Impl, class Traits>
    345 class ConstantAggregateBuilderTemplateBase
    346     : public Traits::AggregateBuilderBase {
    347   using super = typename Traits::AggregateBuilderBase;
    348 public:
    349   using InitBuilder = typename Traits::InitBuilder;
    350   using ArrayBuilder = typename Traits::ArrayBuilder;
    351   using StructBuilder = typename Traits::StructBuilder;
    352   using AggregateBuilderBase = typename Traits::AggregateBuilderBase;
    353 
    354 protected:
    355   ConstantAggregateBuilderTemplateBase(InitBuilder &builder,
    356                                        AggregateBuilderBase *parent)
    357     : super(builder, parent) {}
    358 
    359   Impl &asImpl() { return *static_cast<Impl*>(this); }
    360 
    361 public:
    362   ArrayBuilder beginArray(llvm::Type *eltTy = nullptr) {
    363     return ArrayBuilder(static_cast<InitBuilder&>(this->Builder), this, eltTy);
    364   }
    365 
    366   StructBuilder beginStruct(llvm::StructType *ty = nullptr) {
    367     return StructBuilder(static_cast<InitBuilder&>(this->Builder), this, ty);
    368   }
    369 
    370   /// Given that this builder was created by beginning an array or struct
    371   /// component on the given parent builder, finish the array/struct
    372   /// component and add it to the parent.
    373   ///
    374   /// It is an intentional choice that the parent is passed in explicitly
    375   /// despite it being redundant with information already kept in the
    376   /// builder.  This aids in readability by making it easier to find the
    377   /// places that add components to a builder, as well as "bookending"
    378   /// the sub-builder more explicitly.
    379   void finishAndAddTo(AggregateBuilderBase &parent) {
    380     assert(this->Parent == &parent && "adding to non-parent builder");
    381     parent.add(asImpl().finishImpl());
    382   }
    383 
    384   /// Given that this builder was created by beginning an array or struct
    385   /// directly on a ConstantInitBuilder, finish the array/struct and
    386   /// create a global variable with it as the initializer.
    387   template <class... As>
    388   llvm::GlobalVariable *finishAndCreateGlobal(As &&...args) {
    389     assert(!this->Parent && "finishing non-root builder");
    390     return this->Builder.createGlobal(asImpl().finishImpl(),
    391                                       std::forward<As>(args)...);
    392   }
    393 
    394   /// Given that this builder was created by beginning an array or struct
    395   /// directly on a ConstantInitBuilder, finish the array/struct and
    396   /// set it as the initializer of the given global variable.
    397   void finishAndSetAsInitializer(llvm::GlobalVariable *global) {
    398     assert(!this->Parent && "finishing non-root builder");
    399     return this->Builder.setGlobalInitializer(global, asImpl().finishImpl());
    400   }
    401 
    402   /// Given that this builder was created by beginning an array or struct
    403   /// directly on a ConstantInitBuilder, finish the array/struct and
    404   /// return a future which can be used to install the initializer in
    405   /// a global later.
    406   ///
    407   /// This is useful for allowing a finished initializer to passed to
    408   /// an API which will build the global.  However, the "future" preserves
    409   /// a dependency on the original builder; it is an error to pass it aside.
    410   ConstantInitFuture finishAndCreateFuture() {
    411     assert(!this->Parent && "finishing non-root builder");
    412     return this->Builder.createFuture(asImpl().finishImpl());
    413   }
    414 };
    415 
    416 template <class Traits>
    417 class ConstantArrayBuilderTemplateBase
    418   : public ConstantAggregateBuilderTemplateBase<typename Traits::ArrayBuilder,
    419                                                 Traits> {
    420   using super =
    421     ConstantAggregateBuilderTemplateBase<typename Traits::ArrayBuilder, Traits>;
    422 
    423 public:
    424   using InitBuilder = typename Traits::InitBuilder;
    425   using AggregateBuilderBase = typename Traits::AggregateBuilderBase;
    426 
    427 private:
    428   llvm::Type *EltTy;
    429 
    430   template <class, class>
    431   friend class ConstantAggregateBuilderTemplateBase;
    432 
    433 protected:
    434   ConstantArrayBuilderTemplateBase(InitBuilder &builder,
    435                                    AggregateBuilderBase *parent,
    436                                    llvm::Type *eltTy)
    437     : super(builder, parent), EltTy(eltTy) {}
    438 
    439 private:
    440   /// Form an array constant from the values that have been added to this
    441   /// builder.
    442   llvm::Constant *finishImpl() {
    443     return AggregateBuilderBase::finishArray(EltTy);
    444   }
    445 };
    446 
    447 /// A template class designed to allow other frontends to
    448 /// easily customize the builder classes used by ConstantInitBuilder,
    449 /// and thus to extend the API to work with the abstractions they
    450 /// prefer.  This would probably not be necessary if C++ just
    451 /// supported extension methods.
    452 template <class Traits>
    453 class ConstantStructBuilderTemplateBase
    454   : public ConstantAggregateBuilderTemplateBase<typename Traits::StructBuilder,
    455                                                 Traits> {
    456   using super =
    457     ConstantAggregateBuilderTemplateBase<typename Traits::StructBuilder,Traits>;
    458 
    459 public:
    460   using InitBuilder = typename Traits::InitBuilder;
    461   using AggregateBuilderBase = typename Traits::AggregateBuilderBase;
    462 
    463 private:
    464   llvm::StructType *StructTy;
    465 
    466   template <class, class>
    467   friend class ConstantAggregateBuilderTemplateBase;
    468 
    469 protected:
    470   ConstantStructBuilderTemplateBase(InitBuilder &builder,
    471                                     AggregateBuilderBase *parent,
    472                                     llvm::StructType *structTy)
    473     : super(builder, parent), StructTy(structTy) {
    474     if (structTy) this->Packed = structTy->isPacked();
    475   }
    476 
    477 public:
    478   void setPacked(bool packed) {
    479     this->Packed = packed;
    480   }
    481 
    482   /// Use the given type for the struct if its element count is correct.
    483   /// Don't add more elements after calling this.
    484   void suggestType(llvm::StructType *structTy) {
    485     if (this->size() == structTy->getNumElements()) {
    486       StructTy = structTy;
    487     }
    488   }
    489 
    490 private:
    491   /// Form an array constant from the values that have been added to this
    492   /// builder.
    493   llvm::Constant *finishImpl() {
    494     return AggregateBuilderBase::finishStruct(StructTy);
    495   }
    496 };
    497 
    498 /// A template class designed to allow other frontends to
    499 /// easily customize the builder classes used by ConstantInitBuilder,
    500 /// and thus to extend the API to work with the abstractions they
    501 /// prefer.  This would probably not be necessary if C++ just
    502 /// supported extension methods.
    503 template <class Traits>
    504 class ConstantInitBuilderTemplateBase : public ConstantInitBuilderBase {
    505 protected:
    506   ConstantInitBuilderTemplateBase(CodeGenModule &CGM)
    507     : ConstantInitBuilderBase(CGM) {}
    508 
    509 public:
    510   using InitBuilder = typename Traits::InitBuilder;
    511   using ArrayBuilder = typename Traits::ArrayBuilder;
    512   using StructBuilder = typename Traits::StructBuilder;
    513 
    514   ArrayBuilder beginArray(llvm::Type *eltTy = nullptr) {
    515     return ArrayBuilder(static_cast<InitBuilder&>(*this), nullptr, eltTy);
    516   }
    517 
    518   StructBuilder beginStruct(llvm::StructType *structTy = nullptr) {
    519     return StructBuilder(static_cast<InitBuilder&>(*this), nullptr, structTy);
    520   }
    521 };
    522 
    523 class ConstantInitBuilder;
    524 class ConstantStructBuilder;
    525 class ConstantArrayBuilder;
    526 
    527 struct ConstantInitBuilderTraits {
    528   using InitBuilder = ConstantInitBuilder;
    529   using AggregateBuilderBase = ConstantAggregateBuilderBase;
    530   using ArrayBuilder = ConstantArrayBuilder;
    531   using StructBuilder = ConstantStructBuilder;
    532 };
    533 
    534 /// The standard implementation of ConstantInitBuilder used in Clang.
    535 class ConstantInitBuilder
    536     : public ConstantInitBuilderTemplateBase<ConstantInitBuilderTraits> {
    537 public:
    538   explicit ConstantInitBuilder(CodeGenModule &CGM) :
    539     ConstantInitBuilderTemplateBase(CGM) {}
    540 };
    541 
    542 /// A helper class of ConstantInitBuilder, used for building constant
    543 /// array initializers.
    544 class ConstantArrayBuilder
    545     : public ConstantArrayBuilderTemplateBase<ConstantInitBuilderTraits> {
    546   template <class Traits>
    547   friend class ConstantInitBuilderTemplateBase;
    548 
    549   // The use of explicit qualification is a GCC workaround.
    550   template <class Impl, class Traits>
    551   friend class CodeGen::ConstantAggregateBuilderTemplateBase;
    552 
    553   ConstantArrayBuilder(ConstantInitBuilder &builder,
    554                        ConstantAggregateBuilderBase *parent,
    555                        llvm::Type *eltTy)
    556     : ConstantArrayBuilderTemplateBase(builder, parent, eltTy) {}
    557 };
    558 
    559 /// A helper class of ConstantInitBuilder, used for building constant
    560 /// struct initializers.
    561 class ConstantStructBuilder
    562     : public ConstantStructBuilderTemplateBase<ConstantInitBuilderTraits> {
    563   template <class Traits>
    564   friend class ConstantInitBuilderTemplateBase;
    565 
    566   // The use of explicit qualification is a GCC workaround.
    567   template <class Impl, class Traits>
    568   friend class CodeGen::ConstantAggregateBuilderTemplateBase;
    569 
    570   ConstantStructBuilder(ConstantInitBuilder &builder,
    571                         ConstantAggregateBuilderBase *parent,
    572                         llvm::StructType *structTy)
    573     : ConstantStructBuilderTemplateBase(builder, parent, structTy) {}
    574 };
    575 
    576 }  // end namespace CodeGen
    577 }  // end namespace clang
    578 
    579 #endif
    580