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>
diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc
index 564d1ec..d13ad94 100644
--- a/src/writer/msl/generator_impl_type_test.cc
+++ b/src/writer/msl/generator_impl_type_test.cc
@@ -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 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 @@
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 @@
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 @@
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.