| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Unit tests for per-member record kinds: ABI emptiness and type identity. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "mlir/IR/BuiltinOps.h" |
| #include "mlir/IR/Diagnostics.h" |
| #include "mlir/IR/MLIRContext.h" |
| #include "clang/CIR/Dialect/IR/CIRDialect.h" |
| #include "clang/CIR/Dialect/IR/CIRTypes.h" |
| #include "gtest/gtest.h" |
| |
| using namespace mlir; |
| using namespace cir; |
| |
| /// Swallows verifier diagnostics, so a getChecked failure can be asserted |
| /// without the error reaching stderr. |
| struct ScopedDiagnosticCapture { |
| explicit ScopedDiagnosticCapture(MLIRContext &context) |
| : handler(&context, [this](mlir::Diagnostic &diag) { |
| ++count; |
| lastMessage = diag.str(); |
| }) {} |
| |
| unsigned count = 0; |
| std::string lastMessage; |
| |
| private: |
| mlir::ScopedDiagnosticHandler handler; |
| }; |
| |
| class RecordMemberKindTest : public ::testing::Test { |
| protected: |
| RecordMemberKindTest() { context.loadDialect<cir::CIRDialect>(); } |
| |
| MLIRContext context; |
| |
| mlir::Location getLoc() { return mlir::UnknownLoc::get(&context); } |
| |
| mlir::StringAttr getName(llvm::StringRef name) { |
| return mlir::StringAttr::get(&context, name); |
| } |
| |
| IntType getU8() { return IntType::get(&context, 8, false); } |
| |
| /// One bit-field as the source declared it. |
| BitFieldDeclAttr getDecl(mlir::Type declaredType, uint64_t width, |
| bool isUnnamed = false) { |
| return BitFieldDeclAttr::get(declaredType, width, isUnnamed); |
| } |
| |
| /// An access unit stored as \p storageType, holding \p fields. |
| BitFieldType getUnit(mlir::Type storageType, |
| llvm::ArrayRef<BitFieldDeclAttr> fields) { |
| return BitFieldType::get(&context, storageType, fields); |
| } |
| |
| /// A zero-width bit-field declared with \p declaredType, which belongs to no |
| /// access unit. |
| BitFieldType getZeroWidth(mlir::Type declaredType) { |
| return BitFieldType::get(&context, /*storage_type=*/mlir::Type{}, |
| getDecl(declaredType, 0, /*isUnnamed=*/true)); |
| } |
| |
| RecordType makeStruct(llvm::StringRef name, |
| llvm::ArrayRef<mlir::Type> members, |
| llvm::ArrayRef<RecordMemberKind> kinds) { |
| auto ty = StructType::get(&context, getName(name), /*is_class=*/false); |
| ty.complete(members, /*packed=*/false, kinds); |
| return ty; |
| } |
| }; |
| |
| TEST_F(RecordMemberKindTest, EmptyForTheABIWhenNoMemberHoldsData) { |
| IntType u8 = getU8(); |
| // A record with no members is vacuously empty. |
| EXPECT_TRUE(makeStruct("none", {}, {}).isEmptyForABI()); |
| EXPECT_TRUE(makeStruct("p1", {u8}, {RecordMemberKind::Pad}).isEmptyForABI()); |
| EXPECT_TRUE( |
| makeStruct("e1", {u8}, {RecordMemberKind::Empty}).isEmptyForABI()); |
| EXPECT_TRUE(makeStruct("pe", {u8, u8}, |
| {RecordMemberKind::Pad, RecordMemberKind::Empty}) |
| .isEmptyForABI()); |
| EXPECT_FALSE( |
| makeStruct("d1", {u8}, {RecordMemberKind::Data}).isEmptyForABI()); |
| EXPECT_FALSE(makeStruct("dp", {u8, u8}, |
| {RecordMemberKind::Data, RecordMemberKind::Pad}) |
| .isEmptyForABI()); |
| // A named bit-field holds data the same way a field does. |
| EXPECT_FALSE( |
| makeStruct("b1", {u8}, {RecordMemberKind::BitField}).isEmptyForABI()); |
| EXPECT_FALSE(makeStruct("be", {u8, u8}, |
| {RecordMemberKind::BitField, RecordMemberKind::Empty}) |
| .isEmptyForABI()); |
| } |
| |
| TEST_F(RecordMemberKindTest, AnAccessUnitHoldsTheFieldsTheSourceDeclared) { |
| IntType s32 = IntType::get(&context, 32, true); |
| IntType s64 = IntType::get(&context, 64, true); |
| IntType u32 = IntType::get(&context, 32, false); |
| |
| // struct { int a : 4; long long b : 27; int : 1; }; One unit, three fields, |
| // and neither the declared types nor the names survive in the unit itself. |
| BitFieldType unit = getUnit(u32, {getDecl(s32, 4), getDecl(s64, 27), |
| getDecl(s32, 1, /*isUnnamed=*/true)}); |
| EXPECT_TRUE(unit.ownsBytes()); |
| EXPECT_FALSE(unit.isZeroWidth()); |
| EXPECT_TRUE(unit.holdsNamedField()); |
| EXPECT_TRUE(cir::memberOwnsBytes(unit)); |
| EXPECT_EQ(cir::memberStorageType(unit), u32); |
| |
| EXPECT_EQ(unit.getFields().size(), 3u); |
| EXPECT_EQ(unit.getFields()[1].getDeclaredType(), s64); |
| EXPECT_EQ(unit.getFields()[1].getWidth(), 27u); |
| |
| // The fields are contiguous and in declaration order, so each starts where |
| // the ones ahead of it end. |
| EXPECT_EQ(unit.getFieldBitOffset(0), 0u); |
| EXPECT_EQ(unit.getFieldBitOffset(1), 4u); |
| EXPECT_EQ(unit.getFieldBitOffset(2), 31u); |
| |
| // A unit no field of the source names is storage all the same. |
| BitFieldType unnamed = getUnit(u32, getDecl(s32, 8, /*isUnnamed=*/true)); |
| EXPECT_FALSE(unnamed.holdsNamedField()); |
| EXPECT_TRUE(unnamed.ownsBytes()); |
| |
| // A zero-width bit-field belongs to no unit and has no storage. |
| BitFieldType zeroWidth = getZeroWidth(s32); |
| EXPECT_FALSE(zeroWidth.ownsBytes()); |
| EXPECT_TRUE(zeroWidth.isZeroWidth()); |
| EXPECT_FALSE(cir::memberOwnsBytes(zeroWidth)); |
| EXPECT_FALSE(zeroWidth.holdsNamedField()); |
| |
| // Any other member owns its own bytes and is its own storage. |
| EXPECT_TRUE(cir::memberOwnsBytes(s32)); |
| EXPECT_EQ(cir::memberStorageType(s32), s32); |
| } |
| |
| TEST_F(RecordMemberKindTest, RejectsAnAccessUnitThatHoldsNothingCoherent) { |
| IntType s32 = IntType::get(&context, 32, true); |
| IntType u32 = IntType::get(&context, 32, false); |
| |
| ScopedDiagnosticCapture diags(context); |
| |
| // A unit exists to hold bit-fields. |
| EXPECT_FALSE(BitFieldType::getChecked(getLoc(), &context, mlir::Type(u32), |
| llvm::ArrayRef<BitFieldDeclAttr>{})); |
| EXPECT_EQ(diags.count, 1u); |
| EXPECT_EQ(diags.lastMessage, |
| "bit-field member must hold at least one bit-field"); |
| |
| // Storage is an access unit, never another bit-field. |
| BitFieldType unit = getUnit(u32, getDecl(s32, 4)); |
| llvm::SmallVector<BitFieldDeclAttr> oneField{getDecl(s32, 4)}; |
| EXPECT_FALSE( |
| BitFieldType::getChecked(getLoc(), &context, mlir::Type(unit), |
| llvm::ArrayRef<BitFieldDeclAttr>(oneField))); |
| EXPECT_EQ(diags.count, 2u); |
| EXPECT_EQ(diags.lastMessage, "bit-field access unit storage cannot itself " |
| "be a bit-field type"); |
| |
| // A zero-width bit-field ends the run before it rather than joining a unit. |
| llvm::SmallVector<BitFieldDeclAttr> withZeroWidth{ |
| getDecl(s32, 4), getDecl(s32, 0, /*isUnnamed=*/true)}; |
| EXPECT_FALSE(BitFieldType::getChecked( |
| getLoc(), &context, mlir::Type(u32), |
| llvm::ArrayRef<BitFieldDeclAttr>(withZeroWidth))); |
| EXPECT_EQ(diags.count, 3u); |
| EXPECT_EQ(diags.lastMessage, |
| "a zero-width bit-field cannot occupy an access unit"); |
| |
| // And a member with no storage is that zero-width bit-field, nothing else. |
| llvm::SmallVector<BitFieldDeclAttr> holdsBits{ |
| getDecl(s32, 3, /*isUnnamed=*/true)}; |
| EXPECT_FALSE( |
| BitFieldType::getChecked(getLoc(), &context, mlir::Type{}, |
| llvm::ArrayRef<BitFieldDeclAttr>(holdsBits))); |
| EXPECT_EQ(diags.count, 4u); |
| EXPECT_EQ(diags.lastMessage, "a bit-field member without storage must hold " |
| "a single zero-width bit-field"); |
| |
| // A unit and a zero-width bit-field after it is what a run looks like. |
| llvm::SmallVector<mlir::Type> run{unit, getZeroWidth(s32)}; |
| llvm::SmallVector<RecordMemberKind> runKinds{RecordMemberKind::BitField, |
| RecordMemberKind::Empty}; |
| EXPECT_TRUE(StructType::getChecked( |
| getLoc(), &context, llvm::ArrayRef<mlir::Type>(run), |
| /*packed=*/false, /*is_class=*/false, |
| llvm::ArrayRef<RecordMemberKind>(runKinds))); |
| EXPECT_EQ(diags.count, 4u); |
| } |
| |
| TEST_F(RecordMemberKindTest, OnlyAZeroWidthBitFieldHoldsNoDataForTheABI) { |
| IntType u8 = getU8(); |
| IntType s32 = IntType::get(&context, 32, true); |
| IntType u32 = IntType::get(&context, 32, false); |
| BitFieldType zeroWidth = getZeroWidth(s32); |
| auto zeroLen = cir::ArrayType::get(s32, 0); |
| |
| // The mark tells a unit that some field of the source names from one that |
| // none does, and only the latter can be empty for the ABI. |
| EXPECT_TRUE(cir::holdsDataForABI(RecordMemberKind::BitField)); |
| EXPECT_TRUE(cir::holdsDataForABI(RecordMemberKind::Data)); |
| EXPECT_FALSE(cir::holdsDataForABI(RecordMemberKind::Pad)); |
| EXPECT_FALSE(cir::holdsDataForABI(RecordMemberKind::Empty)); |
| |
| // A record of nothing but zero-width bit-fields declares no storage. |
| EXPECT_TRUE( |
| makeStruct("zw", {zeroWidth}, {RecordMemberKind::Empty}).isEmptyForABI()); |
| EXPECT_TRUE(makeStruct("zwzw", {zeroWidth, zeroWidth}, |
| {RecordMemberKind::Empty, RecordMemberKind::Empty}) |
| .isEmptyForABI()); |
| EXPECT_TRUE(makeStruct("zwpad", {zeroWidth, u8}, |
| {RecordMemberKind::Empty, RecordMemberKind::Pad}) |
| .isEmptyForABI()); |
| // A unit of unnamed bit-fields is storage the classifier reads, so the mark |
| // alone does not answer this: whether the unit holds bytes does. |
| EXPECT_FALSE(makeStruct("unnamed", |
| {getUnit(u32, getDecl(s32, 8, /*isUnnamed=*/true))}, |
| {RecordMemberKind::Empty}) |
| .isEmptyForABI()); |
| EXPECT_FALSE(makeStruct("unnamedRun", |
| {getUnit(u32, {getDecl(s32, 8, /*isUnnamed=*/true), |
| getDecl(s32, 4, /*isUnnamed=*/true)})}, |
| {RecordMemberKind::Empty}) |
| .isEmptyForABI()); |
| // A zero-length array under `data` is a flexible array member, which holds |
| // data. |
| EXPECT_FALSE( |
| makeStruct("fam", {zeroLen}, {RecordMemberKind::Data}).isEmptyForABI()); |
| EXPECT_FALSE(makeStruct("zwdata", {zeroWidth, u8}, |
| {RecordMemberKind::Empty, RecordMemberKind::Data}) |
| .isEmptyForABI()); |
| } |
| |
| TEST_F(RecordMemberKindTest, AZeroWidthBitFieldLendsNoSizeOrAlignment) { |
| IntType s8 = IntType::get(&context, 8, true); |
| IntType s64 = IntType::get(&context, 64, true); |
| IntType u8 = getU8(); |
| BitFieldType zeroWidth = getZeroWidth(s64); |
| auto pad7 = cir::ArrayType::get(u8, 7); |
| |
| // Without the member the record is two bytes at alignment one. The declared |
| // type would take it to sixteen bytes at alignment eight. |
| mlir::Type members[] = {s8, pad7, zeroWidth, s8}; |
| cir::RecordMemberKind kinds[] = { |
| RecordMemberKind::Data, RecordMemberKind::Pad, RecordMemberKind::Empty, |
| RecordMemberKind::Data}; |
| auto ty = StructType::get(&context, getName("ZwLayout"), /*is_class=*/false); |
| ty.complete(members, /*packed=*/false, kinds); |
| |
| OpBuilder builder(&context); |
| auto module = ModuleOp::create(builder.getUnknownLoc()); |
| mlir::DataLayout dl(module); |
| |
| EXPECT_EQ(dl.getTypeSizeInBits(ty).getFixedValue(), 72u); |
| EXPECT_EQ(dl.getTypeABIAlignment(ty), 1u); |
| // The member sits where the storage ahead of it ends, and the member after it |
| // is not pushed along by the declared type's alignment. |
| EXPECT_EQ(ty.getElementOffset(dl, 2), 8u); |
| EXPECT_EQ(ty.getElementOffset(dl, 3), 8u); |
| |
| module->erase(); |
| } |
| |
| TEST_F(RecordMemberKindTest, ATrailingZeroWidthBitFieldLeavesTailPadding) { |
| IntType s8 = IntType::get(&context, 8, true); |
| IntType s32 = IntType::get(&context, 32, true); |
| IntType u8 = getU8(); |
| BitFieldType zeroWidth = getZeroWidth(s32); |
| auto pad3 = cir::ArrayType::get(u8, 3); |
| |
| // The trailing run is the pad plus the zero-width bit-field, so a derived |
| // class may reuse every byte after the first. |
| mlir::Type members[] = {s8, pad3, zeroWidth}; |
| cir::RecordMemberKind kinds[] = { |
| RecordMemberKind::Data, RecordMemberKind::Pad, RecordMemberKind::Empty}; |
| auto ty = StructType::get(&context, getName("ZwTail"), /*is_class=*/false); |
| ty.complete(members, /*packed=*/false, kinds); |
| |
| OpBuilder builder(&context); |
| auto module = ModuleOp::create(builder.getUnknownLoc()); |
| mlir::DataLayout dl(module); |
| |
| EXPECT_EQ(ty.computeStructDataSize(dl), 1u); |
| |
| module->erase(); |
| } |
| |
| TEST_F(RecordMemberKindTest, ARunOfBitFieldsIsOneMemberOfTheRecord) { |
| IntType s32 = IntType::get(&context, 32, true); |
| IntType u16 = IntType::get(&context, 16, false); |
| IntType u8 = getU8(); |
| auto pad2 = cir::ArrayType::get(u8, 2); |
| |
| // struct { int a; int b : 3; int : 5; int c : 8; int : 0; }; The three |
| // bit-fields share one two-byte unit, the zero-width one holds no storage, |
| // and the pad fills the record out to eight bytes. |
| mlir::Type members[] = { |
| s32, |
| getUnit(u16, {getDecl(s32, 3), getDecl(s32, 5, true), getDecl(s32, 8)}), |
| getZeroWidth(s32), pad2}; |
| cir::RecordMemberKind kinds[] = { |
| RecordMemberKind::Data, RecordMemberKind::BitField, |
| RecordMemberKind::Empty, RecordMemberKind::Pad}; |
| auto ty = StructType::get(&context, getName("Run"), /*is_class=*/false); |
| ty.complete(members, /*packed=*/false, kinds); |
| |
| OpBuilder builder(&context); |
| auto module = ModuleOp::create(builder.getUnknownLoc()); |
| mlir::DataLayout dl(module); |
| |
| // The unit lends the record the size of its storage, and the zero-width |
| // bit-field lends it nothing. |
| EXPECT_EQ(dl.getTypeSizeInBits(ty).getFixedValue(), 64u); |
| EXPECT_EQ(dl.getTypeABIAlignment(ty), 4u); |
| |
| EXPECT_EQ(ty.getElementOffset(dl, 1), 4u); |
| EXPECT_EQ(ty.getElementOffset(dl, 2), 6u); |
| EXPECT_EQ(ty.getElementOffset(dl, 3), 6u); |
| |
| // The lowered body drops the member that holds no storage, so the members |
| // behind it shift down. |
| EXPECT_EQ(ty.getLLVMFieldIndex(0), 0u); |
| EXPECT_EQ(ty.getLLVMFieldIndex(1), 1u); |
| EXPECT_EQ(ty.getLLVMFieldIndex(3), 2u); |
| |
| module->erase(); |
| } |
| |
| TEST_F(RecordMemberKindTest, PaddedFollowsThePadKinds) { |
| IntType u8 = getU8(); |
| EXPECT_FALSE(makeStruct("d", {u8}, {RecordMemberKind::Data}).getPadded()); |
| EXPECT_FALSE(makeStruct("e", {u8}, {RecordMemberKind::Empty}).getPadded()); |
| // A bit-field's storage is declared, so it is not padding. |
| EXPECT_FALSE(makeStruct("b", {u8}, {RecordMemberKind::BitField}).getPadded()); |
| EXPECT_TRUE(makeStruct("p", {u8}, {RecordMemberKind::Pad}).getPadded()); |
| // Interior padding counts too, not just a trailing run. |
| EXPECT_TRUE(makeStruct("dpd", {u8, u8, u8}, |
| {RecordMemberKind::Data, RecordMemberKind::Pad, |
| RecordMemberKind::Data}) |
| .getPadded()); |
| // An incomplete struct has no members to read a kind from. |
| EXPECT_FALSE(StructType::get(&context, getName("inc"), /*is_class=*/false) |
| .getPadded()); |
| } |
| |
| TEST_F(RecordMemberKindTest, AUnionsPaddingComesFromItsPaddingSlot) { |
| IntType u8 = getU8(); |
| llvm::SmallVector<mlir::Type> members{u8}; |
| llvm::SmallVector<RecordMemberKind> empty{RecordMemberKind::Empty}; |
| llvm::ArrayRef<mlir::Type> membersRef(members); |
| |
| EXPECT_FALSE(UnionType::get(&context, membersRef, getName("ub"), |
| /*packed=*/false, /*padding=*/mlir::Type{}, |
| RecordType::getAllDataKinds(membersRef)) |
| .getPadded()); |
| EXPECT_TRUE(UnionType::get(&context, membersRef, getName("up"), |
| /*packed=*/false, /*padding=*/u8, |
| RecordType::getAllDataKinds(membersRef)) |
| .getPadded()); |
| // An empty kind on a member is not padding. |
| EXPECT_FALSE(UnionType::get(&context, membersRef, getName("ue"), |
| /*packed=*/false, /*padding=*/mlir::Type{}, |
| llvm::ArrayRef<RecordMemberKind>(empty)) |
| .getPadded()); |
| } |
| |
| TEST_F(RecordMemberKindTest, RejectsAKindListThatDoesNotNameEveryMember) { |
| // The assembly syntax cannot express either of these, since it builds one |
| // kind per member, but a C++ caller can. |
| llvm::SmallVector<mlir::Type> members{getU8(), getU8()}; |
| llvm::SmallVector<RecordMemberKind> tooFew{RecordMemberKind::Pad}; |
| |
| ScopedDiagnosticCapture diags(context); |
| llvm::ArrayRef<mlir::Type> membersRef(members); |
| llvm::ArrayRef<RecordMemberKind> kindsRef(tooFew); |
| EXPECT_FALSE(StructType::getChecked(getLoc(), &context, membersRef, |
| /*packed=*/false, /*is_class=*/false, |
| kindsRef)); |
| EXPECT_EQ(diags.count, 1u); |
| EXPECT_EQ(diags.lastMessage, "expected 2 member kinds, got 1"); |
| |
| // An omitted list is not shorthand for all-data. |
| EXPECT_FALSE(StructType::getChecked(getLoc(), &context, membersRef, |
| /*packed=*/false, /*is_class=*/false, |
| llvm::ArrayRef<RecordMemberKind>{})); |
| EXPECT_EQ(diags.count, 2u); |
| EXPECT_EQ(diags.lastMessage, "expected 2 member kinds, got 0"); |
| |
| // A union answers to the same check. |
| EXPECT_FALSE(UnionType::getChecked(getLoc(), &context, membersRef, |
| /*packed=*/false, /*padding=*/mlir::Type{}, |
| llvm::ArrayRef<RecordMemberKind>{})); |
| EXPECT_EQ(diags.count, 3u); |
| EXPECT_EQ(diags.lastMessage, "expected 2 member kinds, got 0"); |
| } |
| |
| TEST_F(RecordMemberKindTest, RejectsPadOnAUnionMember) { |
| llvm::SmallVector<mlir::Type> members{getU8()}; |
| llvm::SmallVector<RecordMemberKind> pad{RecordMemberKind::Pad}; |
| llvm::SmallVector<RecordMemberKind> empty{RecordMemberKind::Empty}; |
| |
| ScopedDiagnosticCapture diags(context); |
| llvm::ArrayRef<mlir::Type> membersRef(members); |
| EXPECT_FALSE(UnionType::getChecked(getLoc(), &context, membersRef, |
| /*packed=*/false, /*padding=*/mlir::Type{}, |
| llvm::ArrayRef<RecordMemberKind>(pad))); |
| EXPECT_EQ(diags.count, 1u); |
| EXPECT_TRUE(UnionType::getChecked(getLoc(), &context, membersRef, |
| /*packed=*/false, /*padding=*/mlir::Type{}, |
| llvm::ArrayRef<RecordMemberKind>(empty))); |
| EXPECT_EQ(diags.count, 1u); |
| // A union variant can be a bit-field, which is not padding. |
| llvm::SmallVector<RecordMemberKind> bitField{RecordMemberKind::BitField}; |
| EXPECT_TRUE( |
| UnionType::getChecked(getLoc(), &context, membersRef, |
| /*packed=*/false, /*padding=*/mlir::Type{}, |
| llvm::ArrayRef<RecordMemberKind>(bitField))); |
| EXPECT_EQ(diags.count, 1u); |
| } |
| |
| TEST_F(RecordMemberKindTest, AnIncompleteRecordIsNotEmptyForTheABI) { |
| RecordType ty = StructType::get(&context, getName("I"), /*is_class=*/false); |
| EXPECT_FALSE(ty.isEmptyForABI()); |
| } |
| |
| TEST_F(RecordMemberKindTest, AUnionsTailPaddingSlotIsNotAMember) { |
| IntType u8 = getU8(); |
| llvm::SmallVector<mlir::Type> members{u8}; |
| llvm::SmallVector<RecordMemberKind> empty{RecordMemberKind::Empty}; |
| llvm::ArrayRef<mlir::Type> membersRef(members); |
| |
| RecordType allEmpty = |
| UnionType::get(&context, membersRef, getName("ue"), /*packed=*/false, |
| /*padding=*/u8, llvm::ArrayRef<RecordMemberKind>(empty)); |
| EXPECT_TRUE(allEmpty.isEmptyForABI()); |
| RecordType holdsData = |
| UnionType::get(&context, membersRef, getName("ud"), /*packed=*/false, |
| /*padding=*/u8, RecordType::getAllDataKinds(membersRef)); |
| EXPECT_FALSE(holdsData.isEmptyForABI()); |
| } |
| |
| TEST_F(RecordMemberKindTest, KindsTakePartInAnonymousTypeIdentity) { |
| IntType u8 = getU8(); |
| auto kindsPad = |
| StructType::get(&context, {u8, u8}, /*packed=*/false, /*is_class=*/false, |
| {RecordMemberKind::Data, RecordMemberKind::Pad}); |
| auto kindsEmpty = |
| StructType::get(&context, {u8, u8}, /*packed=*/false, /*is_class=*/false, |
| {RecordMemberKind::Data, RecordMemberKind::Empty}); |
| EXPECT_NE(kindsPad, kindsEmpty); |
| |
| // Kinds are provenance rather than layout. |
| EXPECT_TRUE(kindsPad.isLayoutIdentical(kindsEmpty)); |
| |
| auto kindsBitField = |
| StructType::get(&context, {u8, u8}, /*packed=*/false, /*is_class=*/false, |
| {RecordMemberKind::BitField, RecordMemberKind::Pad}); |
| EXPECT_NE(kindsPad, kindsBitField); |
| EXPECT_TRUE(kindsPad.isLayoutIdentical(kindsBitField)); |
| |
| llvm::SmallVector<mlir::Type> unionMembers{u8, u8}; |
| llvm::SmallVector<RecordMemberKind> unionEmpty{RecordMemberKind::Data, |
| RecordMemberKind::Empty}; |
| llvm::ArrayRef<mlir::Type> unionMembersRef(unionMembers); |
| auto unionMarked = UnionType::get( |
| &context, unionMembersRef, /*packed=*/false, /*padding=*/mlir::Type{}, |
| llvm::ArrayRef<RecordMemberKind>(unionEmpty)); |
| auto unionAllData = UnionType::get( |
| &context, unionMembersRef, /*packed=*/false, |
| /*padding=*/mlir::Type{}, RecordType::getAllDataKinds(unionMembersRef)); |
| EXPECT_NE(unionMarked, unionAllData); |
| EXPECT_TRUE(unionMarked.isLayoutIdentical(unionAllData)); |
| } |