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:
parent
5907039597
commit
4f390a196e
|
@ -12,6 +12,8 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "src/ast/struct_block_decoration.h"
|
#include "src/ast/struct_block_decoration.h"
|
||||||
#include "src/type/access_control_type.h"
|
#include "src/type/access_control_type.h"
|
||||||
#include "src/type/depth_texture_type.h"
|
#include "src/type/depth_texture_type.h"
|
||||||
|
@ -25,6 +27,34 @@ namespace writer {
|
||||||
namespace msl {
|
namespace msl {
|
||||||
namespace {
|
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;
|
using MslGeneratorImplTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, EmitType_Alias) {
|
TEST_F(MslGeneratorImplTest, EmitType_Alias) {
|
||||||
|
@ -206,48 +236,78 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_NonComposites) {
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
|
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
|
||||||
EXPECT_EQ(gen.result(), R"(struct S {
|
|
||||||
/* 0x0000 */ int a;
|
// ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
|
||||||
/* 0x0004 */ int8_t _tint_pad_0[124];
|
// for each field of the structure s.
|
||||||
/* 0x0080 */ float b;
|
#define ALL_FIELDS() \
|
||||||
/* 0x0084 */ int8_t _tint_pad_1[124];
|
FIELD(0x0000, int, a, /*NO SUFFIX*/) \
|
||||||
/* 0x0100 */ packed_float2 c;
|
FIELD(0x0004, int8_t, _tint_pad_0, [124]) \
|
||||||
/* 0x0108 */ uint d;
|
FIELD(0x0080, float, b, /*NO SUFFIX*/) \
|
||||||
/* 0x010c */ int8_t _tint_pad_2[4];
|
FIELD(0x0084, int8_t, _tint_pad_1, [124]) \
|
||||||
/* 0x0110 */ packed_float3 e;
|
FIELD(0x0100, packed_float2, c, /*NO SUFFIX*/) \
|
||||||
/* 0x011c */ uint f;
|
FIELD(0x0108, uint, d, /*NO SUFFIX*/) \
|
||||||
/* 0x0120 */ packed_float4 g;
|
FIELD(0x010c, int8_t, _tint_pad_2, [4]) \
|
||||||
/* 0x0130 */ uint h;
|
FIELD(0x0110, packed_float3, e, /*NO SUFFIX*/) \
|
||||||
/* 0x0134 */ int8_t _tint_pad_3[4];
|
FIELD(0x011c, uint, f, /*NO SUFFIX*/) \
|
||||||
/* 0x0138 */ float2x2 i;
|
FIELD(0x0120, packed_float4, g, /*NO SUFFIX*/) \
|
||||||
/* 0x0148 */ uint j;
|
FIELD(0x0130, uint, h, /*NO SUFFIX*/) \
|
||||||
/* 0x014c */ int8_t _tint_pad_4[4];
|
FIELD(0x0134, int8_t, _tint_pad_3, [4]) \
|
||||||
/* 0x0150 */ float2x3 k;
|
FIELD(0x0138, float2x2, i, /*NO SUFFIX*/) \
|
||||||
/* 0x0170 */ uint l;
|
FIELD(0x0148, uint, j, /*NO SUFFIX*/) \
|
||||||
/* 0x0174 */ int8_t _tint_pad_5[12];
|
FIELD(0x014c, int8_t, _tint_pad_4, [4]) \
|
||||||
/* 0x0180 */ float2x4 m;
|
FIELD(0x0150, float2x3, k, /*NO SUFFIX*/) \
|
||||||
/* 0x01a0 */ uint n;
|
FIELD(0x0170, uint, l, /*NO SUFFIX*/) \
|
||||||
/* 0x01a4 */ int8_t _tint_pad_6[4];
|
FIELD(0x0174, int8_t, _tint_pad_5, [12]) \
|
||||||
/* 0x01a8 */ float3x2 o;
|
FIELD(0x0180, float2x4, m, /*NO SUFFIX*/) \
|
||||||
/* 0x01c0 */ uint p;
|
FIELD(0x01a0, uint, n, /*NO SUFFIX*/) \
|
||||||
/* 0x01c4 */ int8_t _tint_pad_7[12];
|
FIELD(0x01a4, int8_t, _tint_pad_6, [4]) \
|
||||||
/* 0x01d0 */ float3x3 q;
|
FIELD(0x01a8, float3x2, o, /*NO SUFFIX*/) \
|
||||||
/* 0x0200 */ uint r;
|
FIELD(0x01c0, uint, p, /*NO SUFFIX*/) \
|
||||||
/* 0x0204 */ int8_t _tint_pad_8[12];
|
FIELD(0x01c4, int8_t, _tint_pad_7, [12]) \
|
||||||
/* 0x0210 */ float3x4 s;
|
FIELD(0x01d0, float3x3, q, /*NO SUFFIX*/) \
|
||||||
/* 0x0240 */ uint t;
|
FIELD(0x0200, uint, r, /*NO SUFFIX*/) \
|
||||||
/* 0x0244 */ int8_t _tint_pad_9[4];
|
FIELD(0x0204, int8_t, _tint_pad_8, [12]) \
|
||||||
/* 0x0248 */ float4x2 u;
|
FIELD(0x0210, float3x4, s, /*NO SUFFIX*/) \
|
||||||
/* 0x0268 */ uint v;
|
FIELD(0x0240, uint, t, /*NO SUFFIX*/) \
|
||||||
/* 0x026c */ int8_t _tint_pad_10[4];
|
FIELD(0x0244, int8_t, _tint_pad_9, [4]) \
|
||||||
/* 0x0270 */ float4x3 w;
|
FIELD(0x0248, float4x2, u, /*NO SUFFIX*/) \
|
||||||
/* 0x02b0 */ uint x;
|
FIELD(0x0268, uint, v, /*NO SUFFIX*/) \
|
||||||
/* 0x02b4 */ int8_t _tint_pad_11[12];
|
FIELD(0x026c, int8_t, _tint_pad_10, [4]) \
|
||||||
/* 0x02c0 */ float4x4 y;
|
FIELD(0x0270, float4x3, w, /*NO SUFFIX*/) \
|
||||||
/* 0x0300 */ float z;
|
FIELD(0x02b0, uint, x, /*NO SUFFIX*/) \
|
||||||
/* 0x0304 */ int8_t _tint_pad_12[124];
|
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) {
|
TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_Structures) {
|
||||||
|
@ -278,16 +338,59 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_Structures) {
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
|
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
|
||||||
EXPECT_EQ(gen.result(), R"(struct S {
|
|
||||||
/* 0x0000 */ int a;
|
// ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
|
||||||
/* 0x0004 */ int8_t _tint_pad_0[508];
|
// for each field of the structure s.
|
||||||
/* 0x0200 */ inner_x b;
|
#define ALL_FIELDS() \
|
||||||
/* 0x0600 */ float c;
|
FIELD(0x0000, int, a, /*NO SUFFIX*/) \
|
||||||
/* 0x0604 */ inner_y d;
|
FIELD(0x0004, int8_t, _tint_pad_0, [508]) \
|
||||||
/* 0x0808 */ float e;
|
FIELD(0x0200, inner_x, b, /*NO SUFFIX*/) \
|
||||||
/* 0x080c */ int8_t _tint_pad_1[500];
|
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) {
|
TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_ArrayDefaultStride) {
|
||||||
|
@ -321,17 +424,66 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_ArrayDefaultStride) {
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
|
||||||
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
|
ASSERT_TRUE(gen.EmitStructType(s)) << gen.error();
|
||||||
EXPECT_EQ(gen.result(), R"(struct S {
|
|
||||||
/* 0x0000 */ int a;
|
// ALL_FIELDS() calls the macro FIELD(ADDR, TYPE, NAME, SUFFIX)
|
||||||
/* 0x0004 */ float b[7];
|
// for each field of the structure s.
|
||||||
/* 0x0020 */ float c;
|
#define ALL_FIELDS() \
|
||||||
/* 0x0024 */ int8_t _tint_pad_0[476];
|
FIELD(0x0000, int, a, /*NO SUFFIX*/) \
|
||||||
/* 0x0200 */ inner d[4];
|
FIELD(0x0004, float, b, [7]) \
|
||||||
/* 0x1200 */ float e;
|
FIELD(0x0020, float, c, /*NO SUFFIX*/) \
|
||||||
/* 0x1204 */ float f[1];
|
FIELD(0x0024, int8_t, _tint_pad_0, [476]) \
|
||||||
/* 0x1208 */ int8_t _tint_pad_1[504];
|
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.
|
// TODO(crbug.com/tint/649): Add tests for array with explicit stride.
|
||||||
|
|
Loading…
Reference in New Issue