diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc index 564d1ec88b..d13ad94383 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 + #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(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(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; + CHECK_TYPE_SIZE_AND_ALIGN(array_x, 28, 4); + + // array_y: size(4096), align(512) + using array_y = std::array; + CHECK_TYPE_SIZE_AND_ALIGN(array_y, 4096, 512); + + // array_z: size(4), align(4) + using array_z = std::array; + 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(offsetof(S, NAME))) << "Field " << #NAME; + ALL_FIELDS() +#undef FIELD + } + +#undef ALL_FIELDS } // TODO(crbug.com/tint/649): Add tests for array with explicit stride.