// Copyright 2022 The Tint Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "src/tint/transform/preserve_padding.h" #include #include "src/tint/transform/test_helper.h" namespace tint::transform { namespace { using PreservePaddingTest = TransformTest; TEST_F(PreservePaddingTest, ShouldRun_EmptyModule) { auto* src = R"()"; EXPECT_FALSE(ShouldRun(src)); } TEST_F(PreservePaddingTest, ShouldRun_NonStructVec3) { auto* src = R"( @group(0) @binding(0) var v : vec3; @compute @workgroup_size(1) fn foo() { v = vec3(); } )"; EXPECT_FALSE(ShouldRun(src)); } TEST_F(PreservePaddingTest, ShouldRun_StructWithoutPadding) { auto* src = R"( struct S { a : u32, b : u32, c : u32, d : u32, e : vec3, f : u32, } @group(0) @binding(0) var v : S; @compute @workgroup_size(1) fn foo() { v = S(); } )"; EXPECT_FALSE(ShouldRun(src)); } TEST_F(PreservePaddingTest, ShouldRun_ArrayWithoutPadding) { auto* src = R"( @group(0) @binding(0) var v : array, 4>; @compute @workgroup_size(1) fn foo() { v = array, 4>(); } )"; EXPECT_FALSE(ShouldRun(src)); } TEST_F(PreservePaddingTest, EmptyModule) { auto* src = R"()"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, StructTrailingPadding) { auto* src = R"( struct S { a : u32, b : u32, c : u32, d : u32, e : vec3, } @group(0) @binding(0) var v : S; @compute @workgroup_size(1) fn foo() { v = S(); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct S { a : u32, b : u32, c : u32, d : u32, e : vec3, } @group(0) @binding(0) var v : S; fn assign_and_preserve_padding(dest : ptr, value : S) { (*(dest)).a = value.a; (*(dest)).b = value.b; (*(dest)).c = value.c; (*(dest)).d = value.d; (*(dest)).e = value.e; } @compute @workgroup_size(1) fn foo() { assign_and_preserve_padding(&(v), S()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, StructInternalPadding) { auto* src = R"( struct S { a : u32, b : vec4, } @group(0) @binding(0) var v : S; @compute @workgroup_size(1) fn foo() { v = S(); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct S { a : u32, b : vec4, } @group(0) @binding(0) var v : S; fn assign_and_preserve_padding(dest : ptr, value : S) { (*(dest)).a = value.a; (*(dest)).b = value.b; } @compute @workgroup_size(1) fn foo() { assign_and_preserve_padding(&(v), S()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, StructExplicitSize_TrailingPadding) { auto* src = R"( struct S { @size(16) a : u32, } @group(0) @binding(0) var v : S; @compute @workgroup_size(1) fn foo() { v = S(); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct S { @size(16) a : u32, } @group(0) @binding(0) var v : S; fn assign_and_preserve_padding(dest : ptr, value : S) { (*(dest)).a = value.a; } @compute @workgroup_size(1) fn foo() { assign_and_preserve_padding(&(v), S()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, StructExplicitSize_InternalPadding) { auto* src = R"( struct S { @size(16) a : u32, b : u32, } @group(0) @binding(0) var v : S; @compute @workgroup_size(1) fn foo() { v = S(); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct S { @size(16) a : u32, b : u32, } @group(0) @binding(0) var v : S; fn assign_and_preserve_padding(dest : ptr, value : S) { (*(dest)).a = value.a; (*(dest)).b = value.b; } @compute @workgroup_size(1) fn foo() { assign_and_preserve_padding(&(v), S()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, NestedStructs) { auto* src = R"( struct S1 { a1 : u32, b1 : vec3, c1 : u32, } struct S2 { a2 : u32, b2 : S1, c2 : S1, } struct S3 { a3 : S1, b3 : S2, c3 : S2, } @group(0) @binding(0) var v : S3; @compute @workgroup_size(1) fn foo() { v = S3(); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct S1 { a1 : u32, b1 : vec3, c1 : u32, } struct S2 { a2 : u32, b2 : S1, c2 : S1, } struct S3 { a3 : S1, b3 : S2, c3 : S2, } @group(0) @binding(0) var v : S3; fn assign_and_preserve_padding_1(dest : ptr, value : S1) { (*(dest)).a1 = value.a1; (*(dest)).b1 = value.b1; (*(dest)).c1 = value.c1; } fn assign_and_preserve_padding_2(dest : ptr, value : S2) { (*(dest)).a2 = value.a2; assign_and_preserve_padding_1(&((*(dest)).b2), value.b2); assign_and_preserve_padding_1(&((*(dest)).c2), value.c2); } fn assign_and_preserve_padding(dest : ptr, value : S3) { assign_and_preserve_padding_1(&((*(dest)).a3), value.a3); assign_and_preserve_padding_2(&((*(dest)).b3), value.b3); assign_and_preserve_padding_2(&((*(dest)).c3), value.c3); } @compute @workgroup_size(1) fn foo() { assign_and_preserve_padding(&(v), S3()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, ArrayOfVec3) { auto* src = R"( @group(0) @binding(0) var v : array, 4>; @compute @workgroup_size(1) fn foo() { v = array, 4>(); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var v : array, 4>; fn assign_and_preserve_padding(dest : ptr, 4u>, read_write>, value : array, 4u>) { for(var i = 0u; (i < 4u); i = (i + 1u)) { (*(dest))[i] = value[i]; } } @compute @workgroup_size(1) fn foo() { assign_and_preserve_padding(&(v), array, 4>()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, ArrayOfArray) { auto* src = R"( type Array = array, 4>, 3>; @group(0) @binding(0) var v : Array; @compute @workgroup_size(1) fn foo() { v = Array(); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; type Array = array, 4>, 3>; @group(0) @binding(0) var v : Array; fn assign_and_preserve_padding_1(dest : ptr, 4u>, read_write>, value : array, 4u>) { for(var i = 0u; (i < 4u); i = (i + 1u)) { (*(dest))[i] = value[i]; } } fn assign_and_preserve_padding(dest : ptr, 4u>, 3u>, read_write>, value : array, 4u>, 3u>) { for(var i = 0u; (i < 3u); i = (i + 1u)) { assign_and_preserve_padding_1(&((*(dest))[i]), value[i]); } } @compute @workgroup_size(1) fn foo() { assign_and_preserve_padding(&(v), Array()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, ArrayOfStructOfArray) { auto* src = R"( struct S { a : u32, b : array, 4>, } @group(0) @binding(0) var v : array; @compute @workgroup_size(1) fn foo() { v = array(); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct S { a : u32, b : array, 4>, } @group(0) @binding(0) var v : array; fn assign_and_preserve_padding_2(dest : ptr, 4u>, read_write>, value : array, 4u>) { for(var i = 0u; (i < 4u); i = (i + 1u)) { (*(dest))[i] = value[i]; } } fn assign_and_preserve_padding_1(dest : ptr, value : S) { (*(dest)).a = value.a; assign_and_preserve_padding_2(&((*(dest)).b), value.b); } fn assign_and_preserve_padding(dest : ptr, read_write>, value : array) { for(var i = 0u; (i < 3u); i = (i + 1u)) { assign_and_preserve_padding_1(&((*(dest))[i]), value[i]); } } @compute @workgroup_size(1) fn foo() { assign_and_preserve_padding(&(v), array()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, NoModify_Vec3) { auto* src = R"( @group(0) @binding(0) var v : vec3; @compute @workgroup_size(1) fn foo() { v = vec3(); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, AvoidDuplicateEnables) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct S { @size(16) a : u32, } @group(0) @binding(0) var v : S; @compute @workgroup_size(1) fn foo() { v = S(); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct S { @size(16) a : u32, } @group(0) @binding(0) var v : S; fn assign_and_preserve_padding(dest : ptr, value : S) { (*(dest)).a = value.a; } @compute @workgroup_size(1) fn foo() { assign_and_preserve_padding(&(v), S()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, NoModify_Mat3x3) { auto* src = R"( @group(0) @binding(0) var v : mat3x3; @compute @workgroup_size(1) fn foo() { v = mat3x3(); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, NoModify_StructNoPadding) { auto* src = R"( struct S { a : u32, b : u32, c : u32, d : u32, e : vec4, } @group(0) @binding(0) var v : S; @compute @workgroup_size(1) fn foo() { v = S(); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, NoModify_ArrayNoPadding) { auto* src = R"( @group(0) @binding(0) var v : array, 4>; @compute @workgroup_size(1) fn foo() { v = array, 4>(); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, NoModify_ArrayOfStructNoPadding) { auto* src = R"( struct S { a : u32, b : u32, c : u32, d : u32, e : vec4, } @group(0) @binding(0) var v : array; @compute @workgroup_size(1) fn foo() { v = array(); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, NoModify_Workgroup) { auto* src = R"( struct S { a : u32, b : vec3, } var v : S; @compute @workgroup_size(1) fn foo() { v = S(); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, NoModify_Private) { auto* src = R"( struct S { a : u32, b : vec3, } var v : S; @compute @workgroup_size(1) fn foo() { v = S(); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(PreservePaddingTest, NoModify_Function) { auto* src = R"( struct S { a : u32, b : vec3, } @compute @workgroup_size(1) fn foo() { var v : S; v = S(); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } } // namespace } // namespace tint::transform