diff options
Diffstat (limited to 'lib/AST/RecordLayoutBuilder.cpp')
| -rw-r--r-- | lib/AST/RecordLayoutBuilder.cpp | 221 | 
1 files changed, 146 insertions, 75 deletions
| diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index 0d070a4bfbfc..2101a5534a6b 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -55,6 +55,52 @@ struct BaseSubobjectInfo {    const BaseSubobjectInfo *Derived;  }; +/// \brief Externally provided layout. Typically used when the AST source, such +/// as DWARF, lacks all the information that was available at compile time, such +/// as alignment attributes on fields and pragmas in effect. +struct ExternalLayout { +  ExternalLayout() : Size(0), Align(0) {} + +  /// \brief Overall record size in bits. +  uint64_t Size; + +  /// \brief Overall record alignment in bits. +  uint64_t Align; + +  /// \brief Record field offsets in bits. +  llvm::DenseMap<const FieldDecl *, uint64_t> FieldOffsets; + +  /// \brief Direct, non-virtual base offsets. +  llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsets; + +  /// \brief Virtual base offsets. +  llvm::DenseMap<const CXXRecordDecl *, CharUnits> VirtualBaseOffsets; + +  /// Get the offset of the given field. The external source must provide +  /// entries for all fields in the record. +  uint64_t getExternalFieldOffset(const FieldDecl *FD) { +    assert(FieldOffsets.count(FD) && +           "Field does not have an external offset"); +    return FieldOffsets[FD]; +  } + +  bool getExternalNVBaseOffset(const CXXRecordDecl *RD, CharUnits &BaseOffset) { +    auto Known = BaseOffsets.find(RD); +    if (Known == BaseOffsets.end()) +      return false; +    BaseOffset = Known->second; +    return true; +  } + +  bool getExternalVBaseOffset(const CXXRecordDecl *RD, CharUnits &BaseOffset) { +    auto Known = VirtualBaseOffsets.find(RD); +    if (Known == VirtualBaseOffsets.end()) +      return false; +    BaseOffset = Known->second; +    return true; +  } +}; +  /// EmptySubobjectMap - Keeps track of which empty subobjects exist at different  /// offsets while laying out a C++ class.  class EmptySubobjectMap { @@ -541,7 +587,7 @@ protected:    /// \brief Whether the external AST source has provided a layout for this    /// record. -  unsigned ExternalLayout : 1; +  unsigned UseExternalLayout : 1;    /// \brief Whether we need to infer alignment, even when we have an     /// externally-provided layout. @@ -607,26 +653,14 @@ protected:    /// avoid visiting virtual bases more than once.    llvm::SmallPtrSet<const CXXRecordDecl *, 4> VisitedVirtualBases; -  /// \brief Externally-provided size. -  uint64_t ExternalSize; -   -  /// \brief Externally-provided alignment. -  uint64_t ExternalAlign; -   -  /// \brief Externally-provided field offsets. -  llvm::DenseMap<const FieldDecl *, uint64_t> ExternalFieldOffsets; - -  /// \brief Externally-provided direct, non-virtual base offsets. -  llvm::DenseMap<const CXXRecordDecl *, CharUnits> ExternalBaseOffsets; - -  /// \brief Externally-provided virtual base offsets. -  llvm::DenseMap<const CXXRecordDecl *, CharUnits> ExternalVirtualBaseOffsets; +  /// Valid if UseExternalLayout is true. +  ExternalLayout External;    RecordLayoutBuilder(const ASTContext &Context,                        EmptySubobjectMap *EmptySubobjects)      : Context(Context), EmptySubobjects(EmptySubobjects), Size(0),         Alignment(CharUnits::One()), UnpackedAlignment(CharUnits::One()), -      ExternalLayout(false), InferAlignment(false),  +      UseExternalLayout(false), InferAlignment(false),        Packed(false), IsUnion(false), IsMac68kAlign(false), IsMsStruct(false),        UnfilledBitsInLastUnit(0), LastBitfieldTypeSize(0),        MaxFieldAlignment(CharUnits::Zero()),  @@ -748,8 +782,8 @@ protected:    void setDataSize(CharUnits NewSize) { DataSize = Context.toBits(NewSize); }    void setDataSize(uint64_t NewSize) { DataSize = NewSize; } -  RecordLayoutBuilder(const RecordLayoutBuilder &) LLVM_DELETED_FUNCTION; -  void operator=(const RecordLayoutBuilder &) LLVM_DELETED_FUNCTION; +  RecordLayoutBuilder(const RecordLayoutBuilder &) = delete; +  void operator=(const RecordLayoutBuilder &) = delete;  };  } // end anonymous namespace @@ -1134,21 +1168,12 @@ CharUnits RecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) {    // Query the external layout to see if it provides an offset.    bool HasExternalLayout = false; -  if (ExternalLayout) { +  if (UseExternalLayout) {      llvm::DenseMap<const CXXRecordDecl *, CharUnits>::iterator Known; -    if (Base->IsVirtual) { -      Known = ExternalVirtualBaseOffsets.find(Base->Class); -      if (Known != ExternalVirtualBaseOffsets.end()) { -        Offset = Known->second; -        HasExternalLayout = true; -      } -    } else { -      Known = ExternalBaseOffsets.find(Base->Class); -      if (Known != ExternalBaseOffsets.end()) { -        Offset = Known->second; -        HasExternalLayout = true; -      } -    } +    if (Base->IsVirtual) +      HasExternalLayout = External.getExternalNVBaseOffset(Base->Class, Offset); +    else +      HasExternalLayout = External.getExternalVBaseOffset(Base->Class, Offset);    }    CharUnits UnpackedBaseAlign = Layout.getNonVirtualAlignment(); @@ -1235,18 +1260,15 @@ void RecordLayoutBuilder::InitializeLayout(const Decl *D) {    // If there is an external AST source, ask it for the various offsets.    if (const RecordDecl *RD = dyn_cast<RecordDecl>(D)) -    if (ExternalASTSource *External = Context.getExternalSource()) { -      ExternalLayout = External->layoutRecordType(RD,  -                                                  ExternalSize, -                                                  ExternalAlign, -                                                  ExternalFieldOffsets, -                                                  ExternalBaseOffsets, -                                                  ExternalVirtualBaseOffsets); -       +    if (ExternalASTSource *Source = Context.getExternalSource()) { +      UseExternalLayout = Source->layoutRecordType( +          RD, External.Size, External.Align, External.FieldOffsets, +          External.BaseOffsets, External.VirtualBaseOffsets); +        // Update based on external alignment. -      if (ExternalLayout) { -        if (ExternalAlign > 0) { -          Alignment = Context.toCharUnitsFromBits(ExternalAlign); +      if (UseExternalLayout) { +        if (External.Align > 0) { +          Alignment = Context.toCharUnitsFromBits(External.Align);          } else {            // The external source didn't have alignment information; infer it.            InferAlignment = true; @@ -1588,7 +1610,7 @@ void RecordLayoutBuilder::LayoutBitField(const FieldDecl *D) {    // If we're using external layout, give the external layout a chance    // to override this information. -  if (ExternalLayout) +  if (UseExternalLayout)      FieldOffset = updateExternalFieldOffset(D, FieldOffset);    // Okay, place the bitfield at the calculated offset. @@ -1604,7 +1626,7 @@ void RecordLayoutBuilder::LayoutBitField(const FieldDecl *D) {      FieldAlign = UnpackedFieldAlign = 1;    // Diagnose differences in layout due to padding or packing. -  if (!ExternalLayout) +  if (!UseExternalLayout)      CheckFieldPadding(FieldOffset, UnpaddedFieldOffset, UnpackedFieldOffset,                        UnpackedFieldAlign, FieldPacked, D); @@ -1727,7 +1749,7 @@ void RecordLayoutBuilder::LayoutField(const FieldDecl *D,    UnpackedFieldOffset =       UnpackedFieldOffset.RoundUpToAlignment(UnpackedFieldAlign); -  if (ExternalLayout) { +  if (UseExternalLayout) {      FieldOffset = Context.toCharUnitsFromBits(                      updateExternalFieldOffset(D, Context.toBits(FieldOffset))); @@ -1750,7 +1772,7 @@ void RecordLayoutBuilder::LayoutField(const FieldDecl *D,    // Place this field at the current location.    FieldOffsets.push_back(Context.toBits(FieldOffset)); -  if (!ExternalLayout) +  if (!UseExternalLayout)      CheckFieldPadding(Context.toBits(FieldOffset), UnpaddedFieldOffset,                         Context.toBits(UnpackedFieldOffset),                        Context.toBits(UnpackedFieldAlign), FieldPacked, D); @@ -1802,15 +1824,15 @@ void RecordLayoutBuilder::FinishLayout(const NamedDecl *D) {    uint64_t RoundedSize      = llvm::RoundUpToAlignment(getSizeInBits(), Context.toBits(Alignment)); -  if (ExternalLayout) { +  if (UseExternalLayout) {      // If we're inferring alignment, and the external size is smaller than      // our size after we've rounded up to alignment, conservatively set the      // alignment to 1. -    if (InferAlignment && ExternalSize < RoundedSize) { +    if (InferAlignment && External.Size < RoundedSize) {        Alignment = CharUnits::One();        InferAlignment = false;      } -    setSize(ExternalSize); +    setSize(External.Size);      return;    } @@ -1846,18 +1868,18 @@ void RecordLayoutBuilder::UpdateAlignment(CharUnits NewAlignment,                                            CharUnits UnpackedNewAlignment) {    // The alignment is not modified when using 'mac68k' alignment or when    // we have an externally-supplied layout that also provides overall alignment. -  if (IsMac68kAlign || (ExternalLayout && !InferAlignment)) +  if (IsMac68kAlign || (UseExternalLayout && !InferAlignment))      return;    if (NewAlignment > Alignment) { -    assert(llvm::isPowerOf2_32(NewAlignment.getQuantity() &&  -           "Alignment not a power of 2")); +    assert(llvm::isPowerOf2_64(NewAlignment.getQuantity()) && +           "Alignment not a power of 2");      Alignment = NewAlignment;    }    if (UnpackedNewAlignment > UnpackedAlignment) { -    assert(llvm::isPowerOf2_32(UnpackedNewAlignment.getQuantity() && -           "Alignment not a power of 2")); +    assert(llvm::isPowerOf2_64(UnpackedNewAlignment.getQuantity()) && +           "Alignment not a power of 2");      UnpackedAlignment = UnpackedNewAlignment;    }  } @@ -1865,11 +1887,8 @@ void RecordLayoutBuilder::UpdateAlignment(CharUnits NewAlignment,  uint64_t  RecordLayoutBuilder::updateExternalFieldOffset(const FieldDecl *Field,                                                  uint64_t ComputedOffset) { -  assert(ExternalFieldOffsets.find(Field) != ExternalFieldOffsets.end() && -         "Field does not have an external offset"); -   -  uint64_t ExternalFieldOffset = ExternalFieldOffsets[Field]; -   +  uint64_t ExternalFieldOffset = External.getExternalFieldOffset(Field); +    if (InferAlignment && ExternalFieldOffset < ComputedOffset) {      // The externally-supplied field offset is before the field offset we      // computed. Assume that the structure is packed. @@ -2152,9 +2171,8 @@ struct MicrosoftRecordLayoutBuilder {    typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetsMapTy;    MicrosoftRecordLayoutBuilder(const ASTContext &Context) : Context(Context) {}  private: -  MicrosoftRecordLayoutBuilder(const MicrosoftRecordLayoutBuilder &) -  LLVM_DELETED_FUNCTION; -  void operator=(const MicrosoftRecordLayoutBuilder &) LLVM_DELETED_FUNCTION; +  MicrosoftRecordLayoutBuilder(const MicrosoftRecordLayoutBuilder &) = delete; +  void operator=(const MicrosoftRecordLayoutBuilder &) = delete;  public:    void layout(const RecordDecl *RD);    void cxxLayout(const CXXRecordDecl *RD); @@ -2252,6 +2270,13 @@ public:    /// \brief True if this class is zero sized or first base is zero sized or    /// has this property.  Only used for MS-ABI.    bool LeadsWithZeroSizedBase : 1; + +  /// \brief True if the external AST source provided a layout for this record. +  bool UseExternalLayout : 1; + +  /// \brief The layout provided by the external AST source. Only active if +  /// UseExternalLayout is true. +  ExternalLayout External;  };  } // namespace @@ -2354,8 +2379,9 @@ void MicrosoftRecordLayoutBuilder::initializeLayout(const RecordDecl *RD) {    // In 64-bit mode we always perform an alignment step after laying out vbases.    // In 32-bit mode we do not.  The check to see if we need to perform alignment    // checks the RequiredAlignment field and performs alignment if it isn't 0. -  RequiredAlignment = Context.getTargetInfo().getPointerWidth(0) == 64 ? -                      CharUnits::One() : CharUnits::Zero(); +  RequiredAlignment = Context.getTargetInfo().getTriple().isArch64Bit() +                          ? CharUnits::One() +                          : CharUnits::Zero();    // Compute the maximum field alignment.    MaxFieldAlignment = CharUnits::Zero();    // Honor the default struct packing maximum alignment flag. @@ -2371,6 +2397,13 @@ void MicrosoftRecordLayoutBuilder::initializeLayout(const RecordDecl *RD) {    // Packed attribute forces max field alignment to be 1.    if (RD->hasAttr<PackedAttr>())      MaxFieldAlignment = CharUnits::One(); + +  // Try to respect the external layout if present. +  UseExternalLayout = false; +  if (ExternalASTSource *Source = Context.getExternalSource()) +    UseExternalLayout = Source->layoutRecordType( +        RD, External.Size, External.Align, External.FieldOffsets, +        External.BaseOffsets, External.VirtualBaseOffsets);  }  void @@ -2385,7 +2418,8 @@ MicrosoftRecordLayoutBuilder::initializeCXXLayout(const CXXRecordDecl *RD) {    // injection.    PointerInfo.Size =        Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); -  PointerInfo.Alignment = PointerInfo.Size; +  PointerInfo.Alignment = +      Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0));    // Respect pragma pack.    if (!MaxFieldAlignment.isZero())      PointerInfo.Alignment = std::min(PointerInfo.Alignment, MaxFieldAlignment); @@ -2475,7 +2509,18 @@ void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(        BaseLayout.leadsWithZeroSizedBase())      Size++;    ElementInfo Info = getAdjustedElementInfo(BaseLayout); -  CharUnits BaseOffset = Size.RoundUpToAlignment(Info.Alignment); +  CharUnits BaseOffset; + +  // Respect the external AST source base offset, if present. +  bool FoundBase = false; +  if (UseExternalLayout) { +    FoundBase = External.getExternalNVBaseOffset(BaseDecl, BaseOffset); +    if (FoundBase) +      assert(BaseOffset >= Size && "base offset already allocated"); +  } + +  if (!FoundBase) +    BaseOffset = Size.RoundUpToAlignment(Info.Alignment);    Bases.insert(std::make_pair(BaseDecl, BaseOffset));    Size = BaseOffset + BaseLayout.getNonVirtualSize();    PreviousBaseLayout = &BaseLayout; @@ -2499,7 +2544,14 @@ void MicrosoftRecordLayoutBuilder::layoutField(const FieldDecl *FD) {      placeFieldAtOffset(CharUnits::Zero());      Size = std::max(Size, Info.Size);    } else { -    CharUnits FieldOffset = Size.RoundUpToAlignment(Info.Alignment); +    CharUnits FieldOffset; +    if (UseExternalLayout) { +      FieldOffset = +          Context.toCharUnitsFromBits(External.getExternalFieldOffset(FD)); +      assert(FieldOffset >= Size && "field offset already allocated"); +    } else { +      FieldOffset = Size.RoundUpToAlignment(Info.Alignment); +    }      placeFieldAtOffset(FieldOffset);      Size = FieldOffset + Info.Size;    } @@ -2573,14 +2625,16 @@ void MicrosoftRecordLayoutBuilder::injectVBPtr(const CXXRecordDecl *RD) {    CharUnits InjectionSite = VBPtrOffset;    // But before we do, make sure it's properly aligned.    VBPtrOffset = VBPtrOffset.RoundUpToAlignment(PointerInfo.Alignment); +  // Shift everything after the vbptr down, unless we're using an external +  // layout. +  if (UseExternalLayout) +    return;    // Determine where the first field should be laid out after the vbptr.    CharUnits FieldStart = VBPtrOffset + PointerInfo.Size;    // Make sure that the amount we push the fields back by is a multiple of the    // alignment.    CharUnits Offset = (FieldStart - InjectionSite).RoundUpToAlignment(        std::max(RequiredAlignment, Alignment)); -  // Increase the size of the object and push back all fields by the offset -  // amount.    Size += Offset;    for (uint64_t &FieldOffset : FieldOffsets)      FieldOffset += Context.toBits(Offset); @@ -2647,7 +2701,18 @@ void MicrosoftRecordLayoutBuilder::layoutVirtualBases(const CXXRecordDecl *RD) {      }      // Insert the virtual base.      ElementInfo Info = getAdjustedElementInfo(BaseLayout); -    CharUnits BaseOffset = Size.RoundUpToAlignment(Info.Alignment); +    CharUnits BaseOffset; + +    // Respect the external AST source base offset, if present. +    bool FoundBase = false; +    if (UseExternalLayout) { +      FoundBase = External.getExternalVBaseOffset(BaseDecl, BaseOffset); +      if (FoundBase) +        assert(BaseOffset >= Size && "base offset already allocated"); +    } +    if (!FoundBase) +      BaseOffset = Size.RoundUpToAlignment(Info.Alignment); +      VBases.insert(std::make_pair(BaseDecl,          ASTRecordLayout::VBaseInfo(BaseOffset, HasVtordisp)));      Size = BaseOffset + BaseLayout.getNonVirtualSize(); @@ -2677,6 +2742,12 @@ void MicrosoftRecordLayoutBuilder::finalizeLayout(const RecordDecl *RD) {      else        Size = MinEmptyStructSize;    } + +  if (UseExternalLayout) { +    Size = Context.toCharUnitsFromBits(External.Size); +    if (External.Align) +      Alignment = Context.toCharUnitsFromBits(External.Align); +  }  }  // Recursively walks the non-virtual bases of a class and determines if any of @@ -2815,7 +2886,7 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {    const ASTRecordLayout *NewEntry = nullptr; -  if (isMsLayout(D) && !D->getASTContext().getExternalSource()) { +  if (isMsLayout(D)) {      NewEntry = BuildMicrosoftASTRecordLayout(D);    } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {      EmptySubobjectMap EmptySubobjects(*this, RD); @@ -2905,11 +2976,11 @@ void ASTContext::setNonKeyFunction(const CXXMethodDecl *Method) {    // Look up the cache entry.  Since we're working with the first    // declaration, its parent must be the class definition, which is    // the correct key for the KeyFunctions hash. -  llvm::DenseMap<const CXXRecordDecl*, LazyDeclPtr>::iterator -    I = KeyFunctions.find(Method->getParent()); +  const auto &Map = KeyFunctions; +  auto I = Map.find(Method->getParent());    // If it's not cached, there's nothing to do. -  if (I == KeyFunctions.end()) return; +  if (I == Map.end()) return;    // If it is cached, check whether it's the target method, and if so,    // remove it from the cache. Note, the call to 'get' might invalidate | 
