diff --git a/clang/include/clang/Frontend/LayoutOverrideSource.h b/clang/include/clang/Frontend/LayoutOverrideSource.h --- a/clang/include/clang/Frontend/LayoutOverrideSource.h +++ b/clang/include/clang/Frontend/LayoutOverrideSource.h @@ -30,6 +30,12 @@ /// The alignment of the record. uint64_t Align; + /// The offsets of non-virtual base classes in the record. + SmallVector BaseOffsets; + + /// The offsets of virtual base classes in the record. + SmallVector VBaseOffsets; + /// The offsets of the fields, in source order. SmallVector FieldOffsets; }; diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -2926,8 +2926,7 @@ bool FoundBase = false; if (UseExternalLayout) { FoundBase = External.getExternalNVBaseOffset(BaseDecl, BaseOffset); - if (FoundBase) { - assert(BaseOffset >= Size && "base offset already allocated"); + if (BaseOffset > Size) { Size = BaseOffset; } } @@ -3723,6 +3722,32 @@ if (Target->defaultsToAIXPowerAlignment()) OS << " PreferredAlignment:" << toBits(Info.getPreferredAlignment()) << "\n"; + if (Info.CXXInfo) { + SmallVector Bases; + for (auto Base: Info.CXXInfo->BaseOffsets) { + Bases.push_back(Base.second.getQuantity()); + } + llvm::sort(Bases); + OS << " BaseOffsets: ["; + for (unsigned i = 0, e = Bases.size(); i != e; ++i) { + if (i) + OS << ", "; + OS << Bases[i]; + } + OS << "]>\n"; + SmallVector VBases; + for (auto VBase: Info.CXXInfo->VBaseOffsets) { + VBases.push_back(VBase.second.VBaseOffset.getQuantity()); + } + llvm::sort(VBases); + OS << " VBaseOffsets: ["; + for (unsigned i = 0, e = VBases.size(); i != e; ++i) { + if (i) + OS << ", "; + OS << VBases[i]; + } + OS << "]>\n"; + } OS << " FieldOffsets: ["; for (unsigned i = 0, e = Info.getFieldCount(); i != e; ++i) { if (i) diff --git a/clang/lib/Frontend/LayoutOverrideSource.cpp b/clang/lib/Frontend/LayoutOverrideSource.cpp --- a/clang/lib/Frontend/LayoutOverrideSource.cpp +++ b/clang/lib/Frontend/LayoutOverrideSource.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/LayoutOverrideSource.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/Basic/CharInfo.h" #include "llvm/Support/raw_ostream.h" #include @@ -26,6 +27,20 @@ return S.substr(0, Offset).str(); } +/// Parse an unsigned integer and move S to the next non-digit character. +static bool parseUnsigned(StringRef &S, unsigned long long &ULL) { + if (S.empty() || !isDigit(S[0])) + return false; + unsigned Idx = 1; + while (Idx < S.size() && isDigit(S[Idx])) + ++Idx; + unsigned long long Offset = 0; + (void)S.substr(0, Idx).getAsInteger(10, Offset); + S = S.substr(Idx); + ULL = Offset; + return true; +} + LayoutOverrideSource::LayoutOverrideSource(StringRef Filename) { std::ifstream Input(Filename.str().c_str()); if (!Input.is_open()) @@ -80,8 +95,8 @@ LineStr = LineStr.substr(Pos + strlen(" Size:")); unsigned long long Size = 0; - (void)LineStr.getAsInteger(10, Size); - CurrentLayout.Size = Size; + if (parseUnsigned(LineStr, Size)) + CurrentLayout.Size = Size; continue; } @@ -92,12 +107,13 @@ LineStr = LineStr.substr(Pos + strlen("Alignment:")); unsigned long long Alignment = 0; - (void)LineStr.getAsInteger(10, Alignment); - CurrentLayout.Align = Alignment; + if (parseUnsigned(LineStr, Alignment)) + CurrentLayout.Align = Alignment; continue; } - // Check for the size/alignment of the type. + // Check for the size/alignment of the type. The number follows "size=" or + // "align=" indicates number of bytes. Pos = LineStr.find("sizeof="); if (Pos != StringRef::npos) { /* Skip past the sizeof= prefix. */ @@ -105,8 +121,8 @@ // Parse size. unsigned long long Size = 0; - (void)LineStr.getAsInteger(10, Size); - CurrentLayout.Size = Size; + if (parseUnsigned(LineStr, Size)) + CurrentLayout.Size = Size * 8; Pos = LineStr.find("align="); if (Pos != StringRef::npos) { @@ -115,8 +131,8 @@ // Parse alignment. unsigned long long Alignment = 0; - (void)LineStr.getAsInteger(10, Alignment); - CurrentLayout.Align = Alignment; + if (parseUnsigned(LineStr, Alignment)) + CurrentLayout.Align = Alignment * 8; } continue; @@ -124,25 +140,50 @@ // Check for the field offsets of the type. Pos = LineStr.find("FieldOffsets: ["); - if (Pos == StringRef::npos) - continue; + if (Pos != StringRef::npos) { + LineStr = LineStr.substr(Pos + strlen("FieldOffsets: [")); + while (!LineStr.empty() && isDigit(LineStr[0])) { + unsigned long long Offset = 0; + if (parseUnsigned(LineStr, Offset)) + CurrentLayout.FieldOffsets.push_back(Offset); + + // Skip over this offset, the following comma, and any spaces. + LineStr = LineStr.substr(1); + while (!LineStr.empty() && isWhitespace(LineStr[0])) + LineStr = LineStr.substr(1); + } + } - LineStr = LineStr.substr(Pos + strlen("FieldOffsets: [")); - while (!LineStr.empty() && isDigit(LineStr[0])) { - // Parse this offset. - unsigned Idx = 1; - while (Idx < LineStr.size() && isDigit(LineStr[Idx])) - ++Idx; + // Check for the base offsets. + Pos = LineStr.find("BaseOffsets: ["); + if (Pos != StringRef::npos) { + LineStr = LineStr.substr(Pos + strlen("BaseOffsets: [")); + while (!LineStr.empty() && isDigit(LineStr[0])) { + unsigned long long Offset = 0; + if (parseUnsigned(LineStr, Offset)) + CurrentLayout.BaseOffsets.push_back(CharUnits::fromQuantity(Offset)); - unsigned long long Offset = 0; - (void)LineStr.substr(0, Idx).getAsInteger(10, Offset); + // Skip over this offset, the following comma, and any spaces. + LineStr = LineStr.substr(1); + while (!LineStr.empty() && isWhitespace(LineStr[0])) + LineStr = LineStr.substr(1); + } + } - CurrentLayout.FieldOffsets.push_back(Offset); + // Check for the virtual base offsets. + Pos = LineStr.find("VBaseOffsets: ["); + if (Pos != StringRef::npos) { + LineStr = LineStr.substr(Pos + strlen("VBaseOffsets: [")); + while (!LineStr.empty() && isDigit(LineStr[0])) { + unsigned long long Offset = 0; + if (parseUnsigned(LineStr, Offset)) + CurrentLayout.VBaseOffsets.push_back(CharUnits::fromQuantity(Offset)); - // Skip over this offset, the following comma, and any spaces. - LineStr = LineStr.substr(Idx + 1); - while (!LineStr.empty() && isWhitespace(LineStr[0])) + // Skip over this offset, the following comma, and any spaces. LineStr = LineStr.substr(1); + while (!LineStr.empty() && isWhitespace(LineStr[0])) + LineStr = LineStr.substr(1); + } } } @@ -182,6 +223,24 @@ if (NumFields != Known->second.FieldOffsets.size()) return false; + // Provide base offsets. + if (const auto *RD = dyn_cast(Record)) { + unsigned NumNB = 0; + unsigned NumVB = 0; + for (const auto &I : RD->bases()) { + const CXXRecordDecl *Base = I.getType()->getAsCXXRecordDecl(); + if (I.isVirtual()) { + if (NumVB >= Known->second.VBaseOffsets.size()) + continue; + VirtualBaseOffsets[Base] = Known->second.VBaseOffsets[NumVB++]; + } else { + if (NumNB >= Known->second.BaseOffsets.size()) + continue; + BaseOffsets[Base] = Known->second.BaseOffsets[NumNB++]; + } + } + } + Size = Known->second.Size; Alignment = Known->second.Align; return true; diff --git a/clang/test/CodeGenCXX/Inputs/override-layout-ms.layout b/clang/test/CodeGenCXX/Inputs/override-layout-ms.layout new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/Inputs/override-layout-ms.layout @@ -0,0 +1,39 @@ +*** Dumping AST Record Layout +Type: struct E1 + +Layout: + VBaseOffsets: []> + FieldOffsets: []> + +*** Dumping AST Record Layout +Type: struct Mid + +Layout: + VBaseOffsets: []> + FieldOffsets: [0]> + +*** Dumping AST Record Layout +Type: struct E2 + +Layout: + VBaseOffsets: []> + FieldOffsets: []> + +*** Dumping AST Record Layout +Type: struct Combine + +Layout: + VBaseOffsets: []> + FieldOffsets: []> diff --git a/clang/test/CodeGenCXX/override-layout-ms.cpp b/clang/test/CodeGenCXX/override-layout-ms.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/override-layout-ms.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -w -triple=x86_64-pc-win32 -fms-compatibility -fdump-record-layouts -foverride-record-layout=%S/Inputs/override-layout-ms.layout %s | FileCheck %s + +// CHECK: *** Dumping AST Record Layout +// CHECK: 0 | struct E1 (empty) +// CHECK: | [sizeof=1, align=1, +// CHECK: | nvsize=0, nvalign=1] +// CHECK: *** Dumping AST Record Layout +// CHECK: 0 | struct Mid +// CHECK: 0 | void * p +// CHECK: | [sizeof=8, align=8, +// CHECK: | nvsize=8, nvalign=8] +// CHECK: *** Dumping AST Record Layout +// CHECK: 0 | struct E2 (empty) +// CHECK: | [sizeof=1, align=1, +// CHECK: | nvsize=0, nvalign=1] +// CHECK: *** Dumping AST Record Layout +// CHECK: 0 | struct Combine +// CHECK: 0 | struct E1 (base) (empty) +// CHECK: 0 | struct Mid (base) +// CHECK: 0 | void * p +// CHECK: 0 | struct E2 (base) (empty) +// CHECK: | [sizeof=8, align=8, +// CHECK: | nvsize=8, nvalign=8] + + +struct E1 {}; +struct E2 {}; +struct Mid {void *p; }; +struct __declspec(empty_bases) Combine : E1, Mid, E2 {}; +Combine g; +