// 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/pad_structs.h" #include #include #include "src/tint/transform/test_helper.h" namespace tint::transform { namespace { using PadStructsTest = TransformTest; TEST_F(PadStructsTest, EmptyModule) { auto* src = ""; auto* expect = src; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, Uniform) { auto* src = R"( struct S { x : i32, } @group(0) @binding(0) var u : S; fn main() { let x = u.x; } )"; auto* expect = R"( struct S { x : i32, pad : u32, pad_1 : u32, pad_2 : u32, } @group(0) @binding(0) var u : S; fn main() { let x = u.x; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, Size) { auto* src = R"( struct S { @size(12) x : i32, y : i32, } @group(0) @binding(0) var u : S; fn main() { let x = u.x; } )"; auto* expect = R"( struct S { x : i32, pad : u32, pad_1 : u32, y : i32, } @group(0) @binding(0) var u : S; fn main() { let x = u.x; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, SizeUniformAndPrivate) { auto* src = R"( struct S { @size(12) x : i32, y : i32, } @group(0) @binding(0) var u : S; var p : S; fn main() { p.x = u.x; } )"; auto* expect = R"( struct S { x : i32, pad : u32, pad_1 : u32, y : i32, } @group(0) @binding(0) var u : S; var p : S; fn main() { p.x = u.x; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, SizeStorageAndPrivate) { auto* src = R"( struct S { @size(12) x : i32, y : i32, } @group(0) @binding(0) var s : S; var p : S; fn main() { p.x = 123; s.x = p.x; } )"; auto* expect = R"( struct S { x : i32, pad : u32, pad_1 : u32, y : i32, } @group(0) @binding(0) var s : S; var p : S; fn main() { p.x = 123; s.x = p.x; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, SizeUniformAndStorage) { auto* src = R"( struct S { @size(12) x : i32, y : i32, } @group(0) @binding(0) var u : S; @group(0) @binding(1) var s : S; fn main() { s.x = u.x; } )"; auto* expect = R"( struct S { x : i32, pad : u32, pad_1 : u32, y : i32, } @group(0) @binding(0) var u : S; @group(0) @binding(1) var s : S; fn main() { s.x = u.x; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, SizePrivateOnly) { // Structs that are not host-visible should have no explicit padding. auto* src = R"( struct S { @size(12) x : i32, y : i32, } var p : S; fn main() { p.x = 123; } )"; auto* expect = R"( struct S { @size(12) x : i32, y : i32, } var p : S; fn main() { p.x = 123; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, AlignUniformAndPrivate) { auto* src = R"( struct S { a : i32, @align(16) b : i32, } @group(0) @binding(0) var u : S; var p : S; fn main() { p.a = u.b; p.b = u.a; } )"; auto* expect = R"( struct S { a : i32, pad : u32, pad_1 : u32, pad_2 : u32, b : i32, pad_3 : u32, pad_4 : u32, pad_5 : u32, } @group(0) @binding(0) var u : S; var p : S; fn main() { p.a = u.b; p.b = u.a; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, AlignStorageAndPrivate) { auto* src = R"( struct S { a : i32, @align(16) b : i32, } @group(0) @binding(0) var s : S; var p : S; fn main() { p.a = 123; p.b = 321; s.a = p.b; s.b = p.a; } )"; auto* expect = R"( struct S { a : i32, pad : u32, pad_1 : u32, pad_2 : u32, b : i32, pad_3 : u32, pad_4 : u32, pad_5 : u32, } @group(0) @binding(0) var s : S; var p : S; fn main() { p.a = 123; p.b = 321; s.a = p.b; s.b = p.a; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, AlignUniformAndStorage) { auto* src = R"( struct S { a : i32, @align(16) b : i32, } @group(0) @binding(0) var u : S; @group(0) @binding(1) var s : S; fn main() { s.a = u.b; s.b = u.a; } )"; auto* expect = R"( struct S { a : i32, pad : u32, pad_1 : u32, pad_2 : u32, b : i32, pad_3 : u32, pad_4 : u32, pad_5 : u32, } @group(0) @binding(0) var u : S; @group(0) @binding(1) var s : S; fn main() { s.a = u.b; s.b = u.a; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, AlignPrivateOnly) { // Structs that are not host-visible should have no explicit padding. auto* src = R"( struct S { a : i32, @align(16) b : i32, } var p : S; fn main() { p.a = 123; p.b = 321; } )"; auto* expect = R"( struct S { a : i32, @align(16) b : i32, } var p : S; fn main() { p.a = 123; p.b = 321; } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, LastMemberRuntimeSizeArray) { // Structs with runtime-sized arrays should not be padded after the // last member. auto* src = R"( struct T { a : f32, b : i32, } struct S { a : vec4, b : array, } @group(0) @binding(0) var s : S; fn main() { s.b[0] = T(1.0f, 23); } )"; auto* expect = R"( struct T { a : f32, b : i32, } struct S { a : vec4, b : array, } @group(0) @binding(0) var s : S; fn main() { s.b[0] = T(1.0f, 23); } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, LastMemberFixedSizeArray) { // Structs without runtime-sized arrays should be padded after the last // member. auto* src = R"( struct T { a : f32, b : i32, } struct S { a : vec4, b : array, } @group(0) @binding(0) var s : S; fn main() { s.b[0] = T(1.0f, 23); } )"; auto* expect = R"( struct T { a : f32, b : i32, } struct S { a : vec4, b : array, pad : u32, pad_1 : u32, } @group(0) @binding(0) var s : S; fn main() { s.b[0] = T(1.0f, 23); } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, Initializer) { // Calls to a initializer of a padded struct must be modified to initialize the padding. auto* src = R"( struct S { a : f32, @align(8) b : i32, } @group(0) @binding(0) var s : S; fn main() { s = S(1.0f, 2); } )"; auto* expect = R"( struct S { a : f32, pad : u32, b : i32, pad_1 : u32, } @group(0) @binding(0) var s : S; fn main() { s = S(1.0f, 0u, 2, 0u); } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } TEST_F(PadStructsTest, InitializerZeroArgs) { // Calls to a zero-argument initializer of a padded struct should not be modified. auto* src = R"( struct S { a : f32, @align(8) b : i32, } @group(0) @binding(0) var s : S; fn main() { s = S(); } )"; auto* expect = R"( struct S { a : f32, pad : u32, b : i32, pad_1 : u32, } @group(0) @binding(0) var s : S; fn main() { s = S(); } )"; DataMap data; auto got = Run(src, data); EXPECT_EQ(expect, str(got)); } } // namespace } // namespace tint::transform