msl tests: Verify that the structure layout is correct

The Metal programming language is a C++14-based Specification with
extensions and restrictions.

Tint is written in C++14.

Take advantage of the fact that MSL is based on the same language that
Tint is written in, and validate that the field members match what the
C++ compiler expects.

Fixed: tint:650
Change-Id: I352871d6efa3f0a5631e7b986284fb5f1a0b3e9f
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/45060
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton 2021-03-18 11:08:14 +00:00 committed by Commit Bot service account
parent 5907039597
commit 4f390a196e
1 changed files with 215 additions and 63 deletions

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <array>
#include "src/ast/struct_block_decoration.h"
#include "src/type/access_control_type.h"
#include "src/type/depth_texture_type.h"
@ -25,6 +27,34 @@ namespace writer {
namespace msl {
namespace {
#define CHECK_TYPE_SIZE_AND_ALIGN(TYPE, SIZE, ALIGN) \
static_assert(sizeof(TYPE) == SIZE, "Bad type size"); \
static_assert(alignof(TYPE) == ALIGN, "Bad type alignment")
// Declare C++ types that match the size and alignment of the types of the same
// name in MSL.
#define DECLARE_TYPE(NAME, SIZE, ALIGN) \
struct alignas(ALIGN) NAME { \
uint8_t _[SIZE]; \
}; \
CHECK_TYPE_SIZE_AND_ALIGN(NAME, SIZE, ALIGN)
// Size and alignments taken from the MSL spec:
// https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
DECLARE_TYPE(packed_float2, 8, 4);
DECLARE_TYPE(packed_float3, 12, 4);
DECLARE_TYPE(packed_float4, 16, 4);
DECLARE_TYPE(float2x2, 16, 8);
DECLARE_TYPE(float2x3, 32, 16);
DECLARE_TYPE(float2x4, 32, 16);
DECLARE_TYPE(float3x2, 24, 8);
DECLARE_TYPE(float3x3, 48, 16);
DECLARE_TYPE(float3x4, 48, 16);
DECLARE_TYPE(float4x2, 32, 8);
DECLARE_TYPE(float4x3, 64, 16);
DECLARE_TYPE(float4x4, 64, 16);
using uint = unsigned int;
using MslGeneratorImplTest = TestHelper;
TEST_F(MslGeneratorImplTest, EmitType_Alias) {
@ -206,48 +236,78 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_NonComposites) {
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
EXPECT_EQ(gen.result(), R"(struct S {
/* 0x0000 */ int a;
/* 0x0004 */ int8_t _tint_pad_0[124];
/* 0x0080 */ float b;
/* 0x0084 */ int8_t _tint_pad_1[124];
/* 0x0100 */ packed_float2 c;
/* 0x0108 */ uint d;
/* 0x010c */ int8_t _tint_pad_2[4];
/* 0x0110 */ packed_float3 e;
/* 0x011c */ uint f;
/* 0x0120 */ packed_float4 g;
/* 0x0130 */ uint h;
/* 0x0134 */ int8_t _tint_pad_3[4];
/* 0x0138 */ float2x2 i;
/* 0x0148 */ uint j;
/* 0x014c */ int8_t _tint_pad_4[4];
/* 0x0150 */ float2x3 k;
/* 0x0170 */ uint l;
/* 0x0174 */ int8_t _tint_pad_5[12];
/* 0x0180 */ float2x4 m;
/* 0x01a0 */ uint n;
/* 0x01a4 */ int8_t _tint_pad_6[4];
/* 0x01a8 */ float3x2 o;
/* 0x01c0 */ uint p;
/* 0x01c4 */ int8_t _tint_pad_7[12];
/* 0x01d0 */ float3x3 q;
/* 0x0200 */ uint r;
/* 0x0204 */ int8_t _tint_pad_8[12];
/* 0x0210 */ float3x4 s;
/* 0x0240 */ uint t;
/* 0x0244 */ int8_t _tint_pad_9[4];
/* 0x0248 */ float4x2 u;
/* 0x0268 */ uint v;
/* 0x026c */ int8_t _tint_pad_10[4];
/* 0x0270 */ float4x3 w;
/* 0x02b0 */ uint x;
/* 0x02b4 */ int8_t _tint_pad_11[12];
/* 0x02c0 */ float4x4 y;
/* 0x0300 */ float z;
/* 0x0304 */ int8_t _tint_pad_12[124];
};
)");
// ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
// for each field of the structure s.
#define ALL_FIELDS() \
FIELD(0x0000, int, a, /*NO SUFFIX*/) \
FIELD(0x0004, int8_t, _tint_pad_0, [124]) \
FIELD(0x0080, float, b, /*NO SUFFIX*/) \
FIELD(0x0084, int8_t, _tint_pad_1, [124]) \
FIELD(0x0100, packed_float2, c, /*NO SUFFIX*/) \
FIELD(0x0108, uint, d, /*NO SUFFIX*/) \
FIELD(0x010c, int8_t, _tint_pad_2, [4]) \
FIELD(0x0110, packed_float3, e, /*NO SUFFIX*/) \
FIELD(0x011c, uint, f, /*NO SUFFIX*/) \
FIELD(0x0120, packed_float4, g, /*NO SUFFIX*/) \
FIELD(0x0130, uint, h, /*NO SUFFIX*/) \
FIELD(0x0134, int8_t, _tint_pad_3, [4]) \
FIELD(0x0138, float2x2, i, /*NO SUFFIX*/) \
FIELD(0x0148, uint, j, /*NO SUFFIX*/) \
FIELD(0x014c, int8_t, _tint_pad_4, [4]) \
FIELD(0x0150, float2x3, k, /*NO SUFFIX*/) \
FIELD(0x0170, uint, l, /*NO SUFFIX*/) \
FIELD(0x0174, int8_t, _tint_pad_5, [12]) \
FIELD(0x0180, float2x4, m, /*NO SUFFIX*/) \
FIELD(0x01a0, uint, n, /*NO SUFFIX*/) \
FIELD(0x01a4, int8_t, _tint_pad_6, [4]) \
FIELD(0x01a8, float3x2, o, /*NO SUFFIX*/) \
FIELD(0x01c0, uint, p, /*NO SUFFIX*/) \
FIELD(0x01c4, int8_t, _tint_pad_7, [12]) \
FIELD(0x01d0, float3x3, q, /*NO SUFFIX*/) \
FIELD(0x0200, uint, r, /*NO SUFFIX*/) \
FIELD(0x0204, int8_t, _tint_pad_8, [12]) \
FIELD(0x0210, float3x4, s, /*NO SUFFIX*/) \
FIELD(0x0240, uint, t, /*NO SUFFIX*/) \
FIELD(0x0244, int8_t, _tint_pad_9, [4]) \
FIELD(0x0248, float4x2, u, /*NO SUFFIX*/) \
FIELD(0x0268, uint, v, /*NO SUFFIX*/) \
FIELD(0x026c, int8_t, _tint_pad_10, [4]) \
FIELD(0x0270, float4x3, w, /*NO SUFFIX*/) \
FIELD(0x02b0, uint, x, /*NO SUFFIX*/) \
FIELD(0x02b4, int8_t, _tint_pad_11, [12]) \
FIELD(0x02c0, float4x4, y, /*NO SUFFIX*/) \
FIELD(0x0300, float, z, /*NO SUFFIX*/) \
FIELD(0x0304, int8_t, _tint_pad_12, [124])
// Check that the generated string is as expected.
#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
" /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
#undef FIELD
EXPECT_EQ(gen.result(), expect);
// 1.4 Metal and C++14
// The Metal programming language is a C++14-based Specification with
// extensions and restrictions. Refer to the C++14 Specification (also known
// as the ISO/IEC JTC1/SC22/WG21 N4431 Language Specification) for a detailed
// description of the language grammar.
//
// Tint is written in C++14, so use the compiler to verify the generated
// layout is as expected for C++14 / MSL.
{
struct S {
#define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX;
ALL_FIELDS()
#undef FIELD
};
#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
ALL_FIELDS()
#undef FIELD
}
#undef ALL_FIELDS
}
TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_Structures) {
@ -278,16 +338,59 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_Structures) {
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
EXPECT_EQ(gen.result(), R"(struct S {
/* 0x0000 */ int a;
/* 0x0004 */ int8_t _tint_pad_0[508];
/* 0x0200 */ inner_x b;
/* 0x0600 */ float c;
/* 0x0604 */ inner_y d;
/* 0x0808 */ float e;
/* 0x080c */ int8_t _tint_pad_1[500];
};
)");
// ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
// for each field of the structure s.
#define ALL_FIELDS() \
FIELD(0x0000, int, a, /*NO SUFFIX*/) \
FIELD(0x0004, int8_t, _tint_pad_0, [508]) \
FIELD(0x0200, inner_x, b, /*NO SUFFIX*/) \
FIELD(0x0600, float, c, /*NO SUFFIX*/) \
FIELD(0x0604, inner_y, d, /*NO SUFFIX*/) \
FIELD(0x0808, float, e, /*NO SUFFIX*/) \
FIELD(0x080c, int8_t, _tint_pad_1, [500])
// Check that the generated string is as expected.
#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
" /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
#undef FIELD
EXPECT_EQ(gen.result(), expect);
// 1.4 Metal and C++14
// The Metal programming language is a C++14-based Specification with
// extensions and restrictions. Refer to the C++14 Specification (also known
// as the ISO/IEC JTC1/SC22/WG21 N4431 Language Specification) for a detailed
// description of the language grammar.
//
// Tint is written in C++14, so use the compiler to verify the generated
// layout is as expected for C++14 / MSL.
{
struct inner_x {
uint32_t a;
alignas(512) float b;
};
CHECK_TYPE_SIZE_AND_ALIGN(inner_x, 1024, 512);
struct inner_y {
uint32_t a[128];
float b;
};
CHECK_TYPE_SIZE_AND_ALIGN(inner_y, 516, 4);
struct S {
#define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX;
ALL_FIELDS()
#undef FIELD
};
#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
ALL_FIELDS()
#undef FIELD
}
#undef ALL_FIELDS
}
TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_ArrayDefaultStride) {
@ -321,17 +424,66 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_ArrayDefaultStride) {
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
EXPECT_EQ(gen.result(), R"(struct S {
/* 0x0000 */ int a;
/* 0x0004 */ float b[7];
/* 0x0020 */ float c;
/* 0x0024 */ int8_t _tint_pad_0[476];
/* 0x0200 */ inner d[4];
/* 0x1200 */ float e;
/* 0x1204 */ float f[1];
/* 0x1208 */ int8_t _tint_pad_1[504];
};
)");
// ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
// for each field of the structure s.
#define ALL_FIELDS() \
FIELD(0x0000, int, a, /*NO SUFFIX*/) \
FIELD(0x0004, float, b, [7]) \
FIELD(0x0020, float, c, /*NO SUFFIX*/) \
FIELD(0x0024, int8_t, _tint_pad_0, [476]) \
FIELD(0x0200, inner, d, [4]) \
FIELD(0x1200, float, e, /*NO SUFFIX*/) \
FIELD(0x1204, float, f, [1]) \
FIELD(0x1208, int8_t, _tint_pad_1, [504])
// Check that the generated string is as expected.
#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
" /* " #ADDR " */ " #TYPE " " #NAME #SUFFIX ";\n"
auto* expect = "struct S {\n" ALL_FIELDS() "};\n";
#undef FIELD
EXPECT_EQ(gen.result(), expect);
// 1.4 Metal and C++14
// The Metal programming language is a C++14-based Specification with
// extensions and restrictions. Refer to the C++14 Specification (also known
// as the ISO/IEC JTC1/SC22/WG21 N4431 Language Specification) for a detailed
// description of the language grammar.
//
// Tint is written in C++14, so use the compiler to verify the generated
// layout is as expected for C++14 / MSL.
{
struct inner {
uint32_t a;
alignas(512) float b;
};
CHECK_TYPE_SIZE_AND_ALIGN(inner, 1024, 512);
// array_x: size(28), align(4)
using array_x = std::array<float, 7>;
CHECK_TYPE_SIZE_AND_ALIGN(array_x, 28, 4);
// array_y: size(4096), align(512)
using array_y = std::array<inner, 4>;
CHECK_TYPE_SIZE_AND_ALIGN(array_y, 4096, 512);
// array_z: size(4), align(4)
using array_z = std::array<float, 1>;
CHECK_TYPE_SIZE_AND_ALIGN(array_z, 4, 4);
struct S {
#define FIELD(ADDR, TYPE, NAME, SUFFIX) TYPE NAME SUFFIX;
ALL_FIELDS()
#undef FIELD
};
#define FIELD(ADDR, TYPE, NAME, SUFFIX) \
EXPECT_EQ(ADDR, static_cast<int>(offsetof(S, NAME))) << "Field " << #NAME;
ALL_FIELDS()
#undef FIELD
}
#undef ALL_FIELDS
}
// TODO(crbug.com/tint/649): Add tests for array with explicit stride.