// Copyright 2020 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/robustness.h" #include "src/tint/transform/test_helper.h" namespace tint::transform { namespace { using RobustnessTest = TransformTest; TEST_F(RobustnessTest, Array_Let_Idx_Clamp) { auto* src = R"( var a : array; fn f() { let l : u32 = 1u; let b : f32 = a[l]; } )"; auto* expect = R"( var a : array; fn f() { let l : u32 = 1u; let b : f32 = a[min(l, 2u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Let_Idx_Clamp_OutOfOrder) { auto* src = R"( fn f() { let c : u32 = 1u; let b : f32 = a[c]; } var a : array; )"; auto* expect = R"( fn f() { let c : u32 = 1u; let b : f32 = a[min(c, 2u)]; } var a : array; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Const_Idx_Clamp) { auto* src = R"( var a : array; const c : u32 = 1u; fn f() { let b : f32 = a[c]; } )"; auto* expect = R"( var a : array; const c : u32 = 1u; fn f() { let b : f32 = a[c]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Const_Idx_Clamp_OutOfOrder) { auto* src = R"( fn f() { let b : f32 = a[c]; } const c : u32 = 1u; var a : array; )"; auto* expect = R"( fn f() { let b : f32 = a[c]; } const c : u32 = 1u; var a : array; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Idx_Nested_Scalar) { auto* src = R"( var a : array; var b : array; var i : u32; fn f() { var c : f32 = a[ b[i] ]; } )"; auto* expect = R"( var a : array; var b : array; var i : u32; fn f() { var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Idx_Nested_Scalar_OutOfOrder) { auto* src = R"( fn f() { var c : f32 = a[ b[i] ]; } var i : u32; var b : array; var a : array; )"; auto* expect = R"( fn f() { var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)]; } var i : u32; var b : array; var a : array; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Idx_Scalar) { auto* src = R"( var a : array; fn f() { var b : f32 = a[1i]; } )"; auto* expect = R"( var a : array; fn f() { var b : f32 = a[1i]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Idx_Scalar_OutOfOrder) { auto* src = R"( fn f() { var b : f32 = a[1i]; } var a : array; )"; auto* expect = R"( fn f() { var b : f32 = a[1i]; } var a : array; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Idx_Expr) { auto* src = R"( var a : array; var c : i32; fn f() { var b : f32 = a[c + 2 - 3]; } )"; auto* expect = R"( var a : array; var c : i32; fn f() { var b : f32 = a[min(u32(((c + 2) - 3)), 2u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Idx_Expr_OutOfOrder) { auto* src = R"( fn f() { var b : f32 = a[c + 2 - 3]; } var c : i32; var a : array; )"; auto* expect = R"( fn f() { var b : f32 = a[min(u32(((c + 2) - 3)), 2u)]; } var c : i32; var a : array; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Vector_Idx_Scalar) { auto* src = R"( var a : vec3; fn f() { var b : f32 = a[1i]; } )"; auto* expect = R"( var a : vec3; fn f() { var b : f32 = a[1i]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Vector_Idx_Scalar_OutOfOrder) { auto* src = R"( fn f() { var b : f32 = a[1i]; } var a : vec3; )"; auto* expect = R"( fn f() { var b : f32 = a[1i]; } var a : vec3; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Vector_Idx_Expr) { auto* src = R"( var a : vec3; var c : i32; fn f() { var b : f32 = a[c + 2 - 3]; } )"; auto* expect = R"( var a : vec3; var c : i32; fn f() { var b : f32 = a[min(u32(((c + 2) - 3)), 2u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Vector_Idx_Expr_OutOfOrder) { auto* src = R"( fn f() { var b : f32 = a[c + 2 - 3]; } var c : i32; var a : vec3; )"; auto* expect = R"( fn f() { var b : f32 = a[min(u32(((c + 2) - 3)), 2u)]; } var c : i32; var a : vec3; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Vector_Swizzle_Idx_Var) { auto* src = R"( var a : vec3; var c : i32; fn f() { var b : f32 = a.xy[c]; } )"; auto* expect = R"( var a : vec3; var c : i32; fn f() { var b : f32 = a.xy[min(u32(c), 1u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Vector_Swizzle_Idx_Var_OutOfOrder) { auto* src = R"( fn f() { var b : f32 = a.xy[c]; } var c : i32; var a : vec3; )"; auto* expect = R"( fn f() { var b : f32 = a.xy[min(u32(c), 1u)]; } var c : i32; var a : vec3; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Vector_Swizzle_Idx_Expr) { auto* src = R"( var a : vec3; var c : i32; fn f() { var b : f32 = a.xy[c + 2 - 3]; } )"; auto* expect = R"( var a : vec3; var c : i32; fn f() { var b : f32 = a.xy[min(u32(((c + 2) - 3)), 1u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Vector_Swizzle_Idx_Expr_OutOfOrder) { auto* src = R"( fn f() { var b : f32 = a.xy[c + 2 - 3]; } var c : i32; var a : vec3; )"; auto* expect = R"( fn f() { var b : f32 = a.xy[min(u32(((c + 2) - 3)), 1u)]; } var c : i32; var a : vec3; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Matrix_Idx_Scalar) { auto* src = R"( var a : mat3x2; fn f() { var b : f32 = a[2i][1i]; } )"; auto* expect = R"( var a : mat3x2; fn f() { var b : f32 = a[2i][1i]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Matrix_Idx_Scalar_OutOfOrder) { auto* src = R"( fn f() { var b : f32 = a[2i][1i]; } var a : mat3x2; )"; auto* expect = R"( fn f() { var b : f32 = a[2i][1i]; } var a : mat3x2; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Matrix_Idx_Expr_Column) { auto* src = R"( var a : mat3x2; var c : i32; fn f() { var b : f32 = a[c + 2 - 3][1]; } )"; auto* expect = R"( var a : mat3x2; var c : i32; fn f() { var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Matrix_Idx_Expr_Column_OutOfOrder) { auto* src = R"( fn f() { var b : f32 = a[c + 2 - 3][1]; } var c : i32; var a : mat3x2; )"; auto* expect = R"( fn f() { var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1]; } var c : i32; var a : mat3x2; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Matrix_Idx_Expr_Row) { auto* src = R"( var a : mat3x2; var c : i32; fn f() { var b : f32 = a[1][c + 2 - 3]; } )"; auto* expect = R"( var a : mat3x2; var c : i32; fn f() { var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Matrix_Idx_Expr_Row_OutOfOrder) { auto* src = R"( fn f() { var b : f32 = a[1][c + 2 - 3]; } var c : i32; var a : mat3x2; )"; auto* expect = R"( fn f() { var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)]; } var c : i32; var a : mat3x2; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Vector_Constant_Id_Clamps) { auto* src = R"( @id(1300) override idx : i32; fn f() { var a : vec3; var b : f32 = a[idx]; } )"; auto* expect = R"( @id(1300) override idx : i32; fn f() { var a : vec3; var b : f32 = a[min(u32(idx), 2u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Array_Constant_Id_Clamps) { auto* src = R"( @id(1300) override idx : i32; fn f() { var a : array; var b : f32 = a[idx]; } )"; auto* expect = R"( @id(1300) override idx : i32; fn f() { var a : array; var b : f32 = a[min(u32(idx), 3u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Matrix_Column_Constant_Id_Clamps) { auto* src = R"( @id(1300) override idx : i32; fn f() { var a : mat3x2; var b : f32 = a[idx][1]; } )"; auto* expect = R"( @id(1300) override idx : i32; fn f() { var a : mat3x2; var b : f32 = a[min(u32(idx), 2u)][1]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Matrix_Row_Constant_Id_Clamps) { auto* src = R"( @id(1300) override idx : i32; fn f() { var a : mat3x2; var b : f32 = a[1][idx]; } )"; auto* expect = R"( @id(1300) override idx : i32; fn f() { var a : mat3x2; var b : f32 = a[1][min(u32(idx), 1u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, RuntimeArray_Clamps) { auto* src = R"( struct S { a : f32, b : array, }; @group(0) @binding(0) var s : S; fn f() { var d : f32 = s.b[25]; } )"; auto* expect = R"( struct S { a : f32, b : array, } @group(0) @binding(0) var s : S; fn f() { var d : f32 = s.b[min(u32(25), (arrayLength(&(s.b)) - 1u))]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, RuntimeArray_Clamps_OutOfOrder) { auto* src = R"( fn f() { var d : f32 = s.b[25]; } @group(0) @binding(0) var s : S; struct S { a : f32, b : array, }; )"; auto* expect = R"( fn f() { var d : f32 = s.b[min(u32(25), (arrayLength(&(s.b)) - 1u))]; } @group(0) @binding(0) var s : S; struct S { a : f32, b : array, } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } // Clamp textureLoad() coord, array_index and level values TEST_F(RobustnessTest, TextureLoad_Clamp) { auto* src = R"( @group(0) @binding(0) var tex_1d : texture_1d; @group(0) @binding(0) var tex_2d : texture_2d; @group(0) @binding(0) var tex_2d_arr : texture_2d_array; @group(0) @binding(0) var tex_3d : texture_3d; @group(0) @binding(0) var tex_ms_2d : texture_multisampled_2d; @group(0) @binding(0) var tex_depth_2d : texture_depth_2d; @group(0) @binding(0) var tex_depth_2d_arr : texture_depth_2d_array; @group(0) @binding(0) var tex_external : texture_external; fn f() { var array_idx : i32; var level_idx : i32; var sample_idx : i32; textureLoad(tex_1d, 1, level_idx); textureLoad(tex_2d, vec2(1, 2), level_idx); textureLoad(tex_2d_arr, vec2(1, 2), array_idx, level_idx); textureLoad(tex_3d, vec3(1, 2, 3), level_idx); textureLoad(tex_ms_2d, vec2(1, 2), sample_idx); textureLoad(tex_depth_2d, vec2(1, 2), level_idx); textureLoad(tex_depth_2d_arr, vec2(1, 2), array_idx, level_idx); textureLoad(tex_external, vec2(1, 2)); } )"; auto* expect = R"( @group(0) @binding(0) var tex_1d : texture_1d; @group(0) @binding(0) var tex_2d : texture_2d; @group(0) @binding(0) var tex_2d_arr : texture_2d_array; @group(0) @binding(0) var tex_3d : texture_3d; @group(0) @binding(0) var tex_ms_2d : texture_multisampled_2d; @group(0) @binding(0) var tex_depth_2d : texture_depth_2d; @group(0) @binding(0) var tex_depth_2d_arr : texture_depth_2d_array; @group(0) @binding(0) var tex_external : texture_external; fn f() { var array_idx : i32; var level_idx : i32; var sample_idx : i32; textureLoad(tex_1d, clamp(1, i32(), (textureDimensions(tex_1d, clamp(level_idx, 0i, (textureNumLevels(tex_1d) - 1i))) - i32(1i))), clamp(level_idx, 0i, (textureNumLevels(tex_1d) - 1i))); textureLoad(tex_2d, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_2d, clamp(level_idx, 0i, (textureNumLevels(tex_2d) - 1i))) - vec2(1i))), clamp(level_idx, 0i, (textureNumLevels(tex_2d) - 1i))); textureLoad(tex_2d_arr, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_2d_arr, clamp(level_idx, 0i, (textureNumLevels(tex_2d_arr) - 1i))) - vec2(1i))), clamp(array_idx, 0i, (textureNumLayers(tex_2d_arr) - 1i)), clamp(level_idx, 0i, (textureNumLevels(tex_2d_arr) - 1i))); textureLoad(tex_3d, clamp(vec3(1, 2, 3), vec3(), (textureDimensions(tex_3d, clamp(level_idx, 0i, (textureNumLevels(tex_3d) - 1i))) - vec3(1i))), clamp(level_idx, 0i, (textureNumLevels(tex_3d) - 1i))); textureLoad(tex_ms_2d, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_ms_2d) - vec2(1i))), sample_idx); textureLoad(tex_depth_2d, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_depth_2d, clamp(level_idx, 0i, (textureNumLevels(tex_depth_2d) - 1i))) - vec2(1i))), clamp(level_idx, 0i, (textureNumLevels(tex_depth_2d) - 1i))); textureLoad(tex_depth_2d_arr, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_depth_2d_arr, clamp(level_idx, 0i, (textureNumLevels(tex_depth_2d_arr) - 1i))) - vec2(1i))), clamp(array_idx, 0i, (textureNumLayers(tex_depth_2d_arr) - 1i)), clamp(level_idx, 0i, (textureNumLevels(tex_depth_2d_arr) - 1i))); textureLoad(tex_external, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_external) - vec2(1i)))); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } // Clamp textureLoad() coord, array_index and level values TEST_F(RobustnessTest, TextureLoad_Clamp_OutOfOrder) { auto* src = R"( fn f() { var array_idx : i32; var level_idx : i32; var sample_idx : i32; textureLoad(tex_1d, 1, level_idx); textureLoad(tex_2d, vec2(1, 2), level_idx); textureLoad(tex_2d_arr, vec2(1, 2), array_idx, level_idx); textureLoad(tex_3d, vec3(1, 2, 3), level_idx); textureLoad(tex_ms_2d, vec2(1, 2), sample_idx); textureLoad(tex_depth_2d, vec2(1, 2), level_idx); textureLoad(tex_depth_2d_arr, vec2(1, 2), array_idx, level_idx); textureLoad(tex_external, vec2(1, 2)); } @group(0) @binding(0) var tex_1d : texture_1d; @group(0) @binding(0) var tex_2d : texture_2d; @group(0) @binding(0) var tex_2d_arr : texture_2d_array; @group(0) @binding(0) var tex_3d : texture_3d; @group(0) @binding(0) var tex_ms_2d : texture_multisampled_2d; @group(0) @binding(0) var tex_depth_2d : texture_depth_2d; @group(0) @binding(0) var tex_depth_2d_arr : texture_depth_2d_array; @group(0) @binding(0) var tex_external : texture_external; )"; auto* expect = R"( fn f() { var array_idx : i32; var level_idx : i32; var sample_idx : i32; textureLoad(tex_1d, clamp(1, i32(), (textureDimensions(tex_1d, clamp(level_idx, 0i, (textureNumLevels(tex_1d) - 1i))) - i32(1i))), clamp(level_idx, 0i, (textureNumLevels(tex_1d) - 1i))); textureLoad(tex_2d, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_2d, clamp(level_idx, 0i, (textureNumLevels(tex_2d) - 1i))) - vec2(1i))), clamp(level_idx, 0i, (textureNumLevels(tex_2d) - 1i))); textureLoad(tex_2d_arr, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_2d_arr, clamp(level_idx, 0i, (textureNumLevels(tex_2d_arr) - 1i))) - vec2(1i))), clamp(array_idx, 0i, (textureNumLayers(tex_2d_arr) - 1i)), clamp(level_idx, 0i, (textureNumLevels(tex_2d_arr) - 1i))); textureLoad(tex_3d, clamp(vec3(1, 2, 3), vec3(), (textureDimensions(tex_3d, clamp(level_idx, 0i, (textureNumLevels(tex_3d) - 1i))) - vec3(1i))), clamp(level_idx, 0i, (textureNumLevels(tex_3d) - 1i))); textureLoad(tex_ms_2d, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_ms_2d) - vec2(1i))), sample_idx); textureLoad(tex_depth_2d, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_depth_2d, clamp(level_idx, 0i, (textureNumLevels(tex_depth_2d) - 1i))) - vec2(1i))), clamp(level_idx, 0i, (textureNumLevels(tex_depth_2d) - 1i))); textureLoad(tex_depth_2d_arr, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_depth_2d_arr, clamp(level_idx, 0i, (textureNumLevels(tex_depth_2d_arr) - 1i))) - vec2(1i))), clamp(array_idx, 0i, (textureNumLayers(tex_depth_2d_arr) - 1i)), clamp(level_idx, 0i, (textureNumLevels(tex_depth_2d_arr) - 1i))); textureLoad(tex_external, clamp(vec2(1, 2), vec2(), (textureDimensions(tex_external) - vec2(1i)))); } @group(0) @binding(0) var tex_1d : texture_1d; @group(0) @binding(0) var tex_2d : texture_2d; @group(0) @binding(0) var tex_2d_arr : texture_2d_array; @group(0) @binding(0) var tex_3d : texture_3d; @group(0) @binding(0) var tex_ms_2d : texture_multisampled_2d; @group(0) @binding(0) var tex_depth_2d : texture_depth_2d; @group(0) @binding(0) var tex_depth_2d_arr : texture_depth_2d_array; @group(0) @binding(0) var tex_external : texture_external; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } // Clamp textureStore() coord, array_index and level values TEST_F(RobustnessTest, TextureStore_Clamp) { auto* src = R"( @group(0) @binding(0) var tex1d : texture_storage_1d; @group(0) @binding(1) var tex2d : texture_storage_2d; @group(0) @binding(2) var tex2d_arr : texture_storage_2d_array; @group(0) @binding(3) var tex3d : texture_storage_3d; fn f() { textureStore(tex1d, 10, vec4()); textureStore(tex2d, vec2(10, 20), vec4()); textureStore(tex2d_arr, vec2(10, 20), 50, vec4()); textureStore(tex3d, vec3(10, 20, 30), vec4()); } )"; auto* expect = R"( @group(0) @binding(0) var tex1d : texture_storage_1d; @group(0) @binding(1) var tex2d : texture_storage_2d; @group(0) @binding(2) var tex2d_arr : texture_storage_2d_array; @group(0) @binding(3) var tex3d : texture_storage_3d; fn f() { textureStore(tex1d, clamp(10, i32(), (textureDimensions(tex1d) - i32(1i))), vec4()); textureStore(tex2d, clamp(vec2(10, 20), vec2(), (textureDimensions(tex2d) - vec2(1i))), vec4()); textureStore(tex2d_arr, clamp(vec2(10, 20), vec2(), (textureDimensions(tex2d_arr) - vec2(1i))), clamp(50, 0i, (textureNumLayers(tex2d_arr) - 1i)), vec4()); textureStore(tex3d, clamp(vec3(10, 20, 30), vec3(), (textureDimensions(tex3d) - vec3(1i))), vec4()); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } // Clamp textureStore() coord, array_index and level values TEST_F(RobustnessTest, TextureStore_Clamp_OutOfOrder) { auto* src = R"( fn f() { textureStore(tex1d, 10, vec4()); textureStore(tex2d, vec2(10, 20), vec4()); textureStore(tex2d_arr, vec2(10, 20), 50, vec4()); textureStore(tex3d, vec3(10, 20, 30), vec4()); } @group(0) @binding(0) var tex1d : texture_storage_1d; @group(0) @binding(1) var tex2d : texture_storage_2d; @group(0) @binding(2) var tex2d_arr : texture_storage_2d_array; @group(0) @binding(3) var tex3d : texture_storage_3d; )"; auto* expect = R"( fn f() { textureStore(tex1d, clamp(10, i32(), (textureDimensions(tex1d) - i32(1i))), vec4()); textureStore(tex2d, clamp(vec2(10, 20), vec2(), (textureDimensions(tex2d) - vec2(1i))), vec4()); textureStore(tex2d_arr, clamp(vec2(10, 20), vec2(), (textureDimensions(tex2d_arr) - vec2(1i))), clamp(50, 0i, (textureNumLayers(tex2d_arr) - 1i)), vec4()); textureStore(tex3d, clamp(vec3(10, 20, 30), vec3(), (textureDimensions(tex3d) - vec3(1i))), vec4()); } @group(0) @binding(0) var tex1d : texture_storage_1d; @group(0) @binding(1) var tex2d : texture_storage_2d; @group(0) @binding(2) var tex2d_arr : texture_storage_2d_array; @group(0) @binding(3) var tex3d : texture_storage_3d; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, Shadowed_Variable) { auto* src = R"( fn f() { var a : array; var i : u32; { var a : array; var b : f32 = a[i]; } var c : f32 = a[i]; } )"; auto* expect = R"( fn f() { var a : array; var i : u32; { var a : array; var b : f32 = a[min(i, 4u)]; } var c : f32 = a[min(i, 2u)]; } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } // Check that existing use of min() and arrayLength() do not get renamed. TEST_F(RobustnessTest, DontRenameSymbols) { auto* src = R"( struct S { a : f32, b : array, }; @group(0) @binding(0) var s : S; const c : u32 = 1u; fn f() { let b : f32 = s.b[c]; let x : i32 = min(1, 2); let y : u32 = arrayLength(&s.b); } )"; auto* expect = R"( struct S { a : f32, b : array, } @group(0) @binding(0) var s : S; const c : u32 = 1u; fn f() { let b : f32 = s.b[min(c, (arrayLength(&(s.b)) - 1u))]; let x : i32 = min(1, 2); let y : u32 = arrayLength(&(s.b)); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } const char* kOmitSourceShader = R"( struct S { vector : vec3, fixed_arr : array, runtime_arr : array, }; @group(0) @binding(0) var s : S; struct U { vector : vec4, fixed_arr : array, 4>, }; @group(1) @binding(0) var u : U; fn f() { // i32 { let i = 0i; var storage_vector : f32 = s.vector[i]; var storage_fixed_arr : f32 = s.fixed_arr[i]; var storage_runtime_arr : f32 = s.runtime_arr[i]; var uniform_vector : f32 = u.vector[i]; var uniform_fixed_arr : vec4 = u.fixed_arr[i]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i]; } // u32 { let i = 0u; var storage_vector : f32 = s.vector[i]; var storage_fixed_arr : f32 = s.fixed_arr[i]; var storage_runtime_arr : f32 = s.runtime_arr[i]; var uniform_vector : f32 = u.vector[i]; var uniform_fixed_arr : vec4 = u.fixed_arr[i]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i]; } } )"; TEST_F(RobustnessTest, OmitNone) { auto* expect = R"( struct S { vector : vec3, fixed_arr : array, runtime_arr : array, } @group(0) @binding(0) var s : S; struct U { vector : vec4, fixed_arr : array, 4>, } @group(1) @binding(0) var u : U; fn f() { { let i = 0i; var storage_vector : f32 = s.vector[min(u32(i), 2u)]; var storage_fixed_arr : f32 = s.fixed_arr[min(u32(i), 3u)]; var storage_runtime_arr : f32 = s.runtime_arr[min(u32(i), (arrayLength(&(s.runtime_arr)) - 1u))]; var uniform_vector : f32 = u.vector[min(u32(i), 3u)]; var uniform_fixed_arr : vec4 = u.fixed_arr[min(u32(i), 3u)]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][min(u32(i), 3u)]; } { let i = 0u; var storage_vector : f32 = s.vector[min(i, 2u)]; var storage_fixed_arr : f32 = s.fixed_arr[min(i, 3u)]; var storage_runtime_arr : f32 = s.runtime_arr[min(i, (arrayLength(&(s.runtime_arr)) - 1u))]; var uniform_vector : f32 = u.vector[min(i, 3u)]; var uniform_fixed_arr : vec4 = u.fixed_arr[min(i, 3u)]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][min(i, 3u)]; } } )"; Robustness::Config cfg; DataMap data; data.Add(cfg); auto got = Run(kOmitSourceShader, data); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, OmitStorage) { auto* expect = R"( struct S { vector : vec3, fixed_arr : array, runtime_arr : array, } @group(0) @binding(0) var s : S; struct U { vector : vec4, fixed_arr : array, 4>, } @group(1) @binding(0) var u : U; fn f() { { let i = 0i; var storage_vector : f32 = s.vector[i]; var storage_fixed_arr : f32 = s.fixed_arr[i]; var storage_runtime_arr : f32 = s.runtime_arr[i]; var uniform_vector : f32 = u.vector[min(u32(i), 3u)]; var uniform_fixed_arr : vec4 = u.fixed_arr[min(u32(i), 3u)]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][min(u32(i), 3u)]; } { let i = 0u; var storage_vector : f32 = s.vector[i]; var storage_fixed_arr : f32 = s.fixed_arr[i]; var storage_runtime_arr : f32 = s.runtime_arr[i]; var uniform_vector : f32 = u.vector[min(i, 3u)]; var uniform_fixed_arr : vec4 = u.fixed_arr[min(i, 3u)]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][min(i, 3u)]; } } )"; Robustness::Config cfg; cfg.omitted_classes.insert(Robustness::AddressSpace::kStorage); DataMap data; data.Add(cfg); auto got = Run(kOmitSourceShader, data); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, OmitUniform) { auto* expect = R"( struct S { vector : vec3, fixed_arr : array, runtime_arr : array, } @group(0) @binding(0) var s : S; struct U { vector : vec4, fixed_arr : array, 4>, } @group(1) @binding(0) var u : U; fn f() { { let i = 0i; var storage_vector : f32 = s.vector[min(u32(i), 2u)]; var storage_fixed_arr : f32 = s.fixed_arr[min(u32(i), 3u)]; var storage_runtime_arr : f32 = s.runtime_arr[min(u32(i), (arrayLength(&(s.runtime_arr)) - 1u))]; var uniform_vector : f32 = u.vector[i]; var uniform_fixed_arr : vec4 = u.fixed_arr[i]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i]; } { let i = 0u; var storage_vector : f32 = s.vector[min(i, 2u)]; var storage_fixed_arr : f32 = s.fixed_arr[min(i, 3u)]; var storage_runtime_arr : f32 = s.runtime_arr[min(i, (arrayLength(&(s.runtime_arr)) - 1u))]; var uniform_vector : f32 = u.vector[i]; var uniform_fixed_arr : vec4 = u.fixed_arr[i]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i]; } } )"; Robustness::Config cfg; cfg.omitted_classes.insert(Robustness::AddressSpace::kUniform); DataMap data; data.Add(cfg); auto got = Run(kOmitSourceShader, data); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, OmitBoth) { auto* expect = R"( struct S { vector : vec3, fixed_arr : array, runtime_arr : array, } @group(0) @binding(0) var s : S; struct U { vector : vec4, fixed_arr : array, 4>, } @group(1) @binding(0) var u : U; fn f() { { let i = 0i; var storage_vector : f32 = s.vector[i]; var storage_fixed_arr : f32 = s.fixed_arr[i]; var storage_runtime_arr : f32 = s.runtime_arr[i]; var uniform_vector : f32 = u.vector[i]; var uniform_fixed_arr : vec4 = u.fixed_arr[i]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i]; } { let i = 0u; var storage_vector : f32 = s.vector[i]; var storage_fixed_arr : f32 = s.fixed_arr[i]; var storage_runtime_arr : f32 = s.runtime_arr[i]; var uniform_vector : f32 = u.vector[i]; var uniform_fixed_arr : vec4 = u.fixed_arr[i]; var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i]; } } )"; Robustness::Config cfg; cfg.omitted_classes.insert(Robustness::AddressSpace::kStorage); cfg.omitted_classes.insert(Robustness::AddressSpace::kUniform); DataMap data; data.Add(cfg); auto got = Run(kOmitSourceShader, data); EXPECT_EQ(expect, str(got)); } TEST_F(RobustnessTest, WorkgroupOverrideCount) { auto* src = R"( override N = 123; var w : array; fn f() { var b : f32 = w[1i]; } )"; auto* expect = R"(error: array size is an override-expression, when expected a constant-expression. Was the SubstituteOverride transform run?)"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } } // namespace } // namespace tint::transform