| // Copyright 2023 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "src/tint/lang/core/ir/transform/robustness.h" |
| |
| #include <utility> |
| |
| #include "src/tint/lang/core/ir/transform/helper_test.h" |
| #include "src/tint/lang/core/type/array.h" |
| #include "src/tint/lang/core/type/depth_multisampled_texture.h" |
| #include "src/tint/lang/core/type/depth_texture.h" |
| #include "src/tint/lang/core/type/external_texture.h" |
| #include "src/tint/lang/core/type/matrix.h" |
| #include "src/tint/lang/core/type/multisampled_texture.h" |
| #include "src/tint/lang/core/type/pointer.h" |
| #include "src/tint/lang/core/type/sampled_texture.h" |
| #include "src/tint/lang/core/type/storage_texture.h" |
| #include "src/tint/lang/core/type/struct.h" |
| #include "src/tint/lang/core/type/vector.h" |
| |
| namespace tint::core::ir::transform { |
| namespace { |
| |
| using namespace tint::core::fluent_types; // NOLINT |
| using namespace tint::core::number_suffixes; // NOLINT |
| |
| using IR_RobustnessTest = TransformTestWithParam<bool>; |
| |
| //////////////////////////////////////////////////////////////// |
| // These tests use the function address space. |
| // Test clamping of vectors, matrices, and fixed-size arrays. |
| // Test indices that are const, const-via-let, and dynamic. |
| // Test signed vs unsigned indices. |
| //////////////////////////////////////////////////////////////// |
| |
| TEST_P(IR_RobustnessTest, VectorLoad_ConstIndex) { |
| auto* func = b.Function("foo", ty.u32()); |
| b.Append(func->Block(), [&] { |
| auto* vec = b.Var("vec", ty.ptr(function, ty.vec4<u32>())); |
| auto* load = b.LoadVectorElement(vec, b.Constant(5_u)); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func():u32 -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %3:u32 = load_vector_element %vec, 5u |
| ret %3 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func():u32 -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %3:u32 = load_vector_element %vec, 3u |
| ret %3 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, VectorLoad_ConstIndexViaLet) { |
| auto* func = b.Function("foo", ty.u32()); |
| b.Append(func->Block(), [&] { |
| auto* vec = b.Var("vec", ty.ptr(function, ty.vec4<u32>())); |
| auto* idx = b.Let("idx", b.Constant(5_u)); |
| auto* load = b.LoadVectorElement(vec, idx); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func():u32 -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %idx:u32 = let 5u |
| %4:u32 = load_vector_element %vec, %idx |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func():u32 -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %idx:u32 = let 5u |
| %4:u32 = min %idx, 3u |
| %5:u32 = load_vector_element %vec, %4 |
| ret %5 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, VectorLoad_DynamicIndex) { |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* vec = b.Var("vec", ty.ptr(function, ty.vec4<u32>())); |
| auto* load = b.LoadVectorElement(vec, idx); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %4:u32 = load_vector_element %vec, %idx |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %4:u32 = min %idx, 3u |
| %5:u32 = load_vector_element %vec, %4 |
| ret %5 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, VectorLoad_DynamicIndex_Signed) { |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.i32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* vec = b.Var("vec", ty.ptr(function, ty.vec4<u32>())); |
| auto* load = b.LoadVectorElement(vec, idx); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:i32):u32 -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %4:u32 = load_vector_element %vec, %idx |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:i32):u32 -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %4:u32 = convert %idx |
| %5:u32 = min %4, 3u |
| %6:u32 = load_vector_element %vec, %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, VectorStore_ConstIndex) { |
| auto* func = b.Function("foo", ty.void_()); |
| b.Append(func->Block(), [&] { |
| auto* vec = b.Var("vec", ty.ptr(function, ty.vec4<u32>())); |
| b.StoreVectorElement(vec, b.Constant(5_u), b.Constant(0_u)); |
| b.Return(func); |
| }); |
| |
| auto* src = R"( |
| %foo = func():void -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| store_vector_element %vec, 5u, 0u |
| ret |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func():void -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| store_vector_element %vec, 3u, 0u |
| ret |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, VectorStore_ConstIndexViaLet) { |
| auto* func = b.Function("foo", ty.void_()); |
| b.Append(func->Block(), [&] { |
| auto* vec = b.Var("vec", ty.ptr(function, ty.vec4<u32>())); |
| auto* idx = b.Let("idx", b.Constant(5_u)); |
| b.StoreVectorElement(vec, idx, b.Constant(0_u)); |
| b.Return(func); |
| }); |
| |
| auto* src = R"( |
| %foo = func():void -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %idx:u32 = let 5u |
| store_vector_element %vec, %idx, 0u |
| ret |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func():void -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %idx:u32 = let 5u |
| %4:u32 = min %idx, 3u |
| store_vector_element %vec, %4, 0u |
| ret |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, VectorStore_DynamicIndex) { |
| auto* func = b.Function("foo", ty.void_()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* vec = b.Var("vec", ty.ptr(function, ty.vec4<u32>())); |
| b.StoreVectorElement(vec, idx, b.Constant(0_u)); |
| b.Return(func); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:u32):void -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| store_vector_element %vec, %idx, 0u |
| ret |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:u32):void -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %4:u32 = min %idx, 3u |
| store_vector_element %vec, %4, 0u |
| ret |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, VectorStore_DynamicIndex_Signed) { |
| auto* func = b.Function("foo", ty.void_()); |
| auto* idx = b.FunctionParam("idx", ty.i32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* vec = b.Var("vec", ty.ptr(function, ty.vec4<u32>())); |
| b.StoreVectorElement(vec, idx, b.Constant(0_u)); |
| b.Return(func); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:i32):void -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| store_vector_element %vec, %idx, 0u |
| ret |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:i32):void -> %b1 { |
| %b1 = block { |
| %vec:ptr<function, vec4<u32>, read_write> = var |
| %4:u32 = convert %idx |
| %5:u32 = min %4, 3u |
| store_vector_element %vec, %5, 0u |
| ret |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Matrix_ConstIndex) { |
| auto* func = b.Function("foo", ty.vec4<f32>()); |
| b.Append(func->Block(), [&] { |
| auto* mat = b.Var("mat", ty.ptr(function, ty.mat4x4<f32>())); |
| auto* access = b.Access(ty.ptr(function, ty.vec4<f32>()), mat, b.Constant(2_u)); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func():vec4<f32> -> %b1 { |
| %b1 = block { |
| %mat:ptr<function, mat4x4<f32>, read_write> = var |
| %3:ptr<function, vec4<f32>, read_write> = access %mat, 2u |
| %4:vec4<f32> = load %3 |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Matrix_ConstIndexViaLet) { |
| auto* func = b.Function("foo", ty.vec4<f32>()); |
| b.Append(func->Block(), [&] { |
| auto* mat = b.Var("mat", ty.ptr(function, ty.mat4x4<f32>())); |
| auto* idx = b.Let("idx", b.Constant(2_u)); |
| auto* access = b.Access(ty.ptr(function, ty.vec4<f32>()), mat, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func():vec4<f32> -> %b1 { |
| %b1 = block { |
| %mat:ptr<function, mat4x4<f32>, read_write> = var |
| %idx:u32 = let 2u |
| %4:ptr<function, vec4<f32>, read_write> = access %mat, %idx |
| %5:vec4<f32> = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func():vec4<f32> -> %b1 { |
| %b1 = block { |
| %mat:ptr<function, mat4x4<f32>, read_write> = var |
| %idx:u32 = let 2u |
| %4:u32 = min %idx, 3u |
| %5:ptr<function, vec4<f32>, read_write> = access %mat, %4 |
| %6:vec4<f32> = load %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Matrix_DynamicIndex) { |
| auto* func = b.Function("foo", ty.vec4<f32>()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* mat = b.Var("mat", ty.ptr(function, ty.mat4x4<f32>())); |
| auto* access = b.Access(ty.ptr(function, ty.vec4<f32>()), mat, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:u32):vec4<f32> -> %b1 { |
| %b1 = block { |
| %mat:ptr<function, mat4x4<f32>, read_write> = var |
| %4:ptr<function, vec4<f32>, read_write> = access %mat, %idx |
| %5:vec4<f32> = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:u32):vec4<f32> -> %b1 { |
| %b1 = block { |
| %mat:ptr<function, mat4x4<f32>, read_write> = var |
| %4:u32 = min %idx, 3u |
| %5:ptr<function, vec4<f32>, read_write> = access %mat, %4 |
| %6:vec4<f32> = load %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Matrix_DynamicIndex_Signed) { |
| auto* func = b.Function("foo", ty.vec4<f32>()); |
| auto* idx = b.FunctionParam("idx", ty.i32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* mat = b.Var("mat", ty.ptr(function, ty.mat4x4<f32>())); |
| auto* access = b.Access(ty.ptr(function, ty.vec4<f32>()), mat, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:i32):vec4<f32> -> %b1 { |
| %b1 = block { |
| %mat:ptr<function, mat4x4<f32>, read_write> = var |
| %4:ptr<function, vec4<f32>, read_write> = access %mat, %idx |
| %5:vec4<f32> = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:i32):vec4<f32> -> %b1 { |
| %b1 = block { |
| %mat:ptr<function, mat4x4<f32>, read_write> = var |
| %4:u32 = convert %idx |
| %5:u32 = min %4, 3u |
| %6:ptr<function, vec4<f32>, read_write> = access %mat, %5 |
| %7:vec4<f32> = load %6 |
| ret %7 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Array_ConstSize_ConstIndex) { |
| auto* func = b.Function("foo", ty.u32()); |
| b.Append(func->Block(), [&] { |
| auto* arr = b.Var("arr", ty.ptr(function, ty.array<u32, 4>())); |
| auto* access = b.Access(ty.ptr<function, u32>(), arr, b.Constant(2_u)); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func():u32 -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<u32, 4>, read_write> = var |
| %3:ptr<function, u32, read_write> = access %arr, 2u |
| %4:u32 = load %3 |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Array_ConstSize_ConstIndexViaLet) { |
| auto* func = b.Function("foo", ty.u32()); |
| b.Append(func->Block(), [&] { |
| auto* arr = b.Var("arr", ty.ptr(function, ty.array<u32, 4>())); |
| auto* idx = b.Let("idx", b.Constant(2_u)); |
| auto* access = b.Access(ty.ptr<function, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func():u32 -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<u32, 4>, read_write> = var |
| %idx:u32 = let 2u |
| %4:ptr<function, u32, read_write> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func():u32 -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<u32, 4>, read_write> = var |
| %idx:u32 = let 2u |
| %4:u32 = min %idx, 3u |
| %5:ptr<function, u32, read_write> = access %arr, %4 |
| %6:u32 = load %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Array_ConstSize_DynamicIndex) { |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* arr = b.Var("arr", ty.ptr(function, ty.array<u32, 4>())); |
| auto* access = b.Access(ty.ptr<function, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<u32, 4>, read_write> = var |
| %4:ptr<function, u32, read_write> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<u32, 4>, read_write> = var |
| %4:u32 = min %idx, 3u |
| %5:ptr<function, u32, read_write> = access %arr, %4 |
| %6:u32 = load %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Array_ConstSize_DynamicIndex_Signed) { |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.i32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* arr = b.Var("arr", ty.ptr(function, ty.array<u32, 4>())); |
| auto* access = b.Access(ty.ptr<function, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:i32):u32 -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<u32, 4>, read_write> = var |
| %4:ptr<function, u32, read_write> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:i32):u32 -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<u32, 4>, read_write> = var |
| %4:u32 = convert %idx |
| %5:u32 = min %4, 3u |
| %6:ptr<function, u32, read_write> = access %arr, %5 |
| %7:u32 = load %6 |
| ret %7 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, NestedArrays) { |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx1 = b.FunctionParam("idx1", ty.u32()); |
| auto* idx2 = b.FunctionParam("idx2", ty.u32()); |
| auto* idx3 = b.FunctionParam("idx3", ty.u32()); |
| auto* idx4 = b.FunctionParam("idx4", ty.u32()); |
| func->SetParams({idx1, idx2, idx3, idx4}); |
| b.Append(func->Block(), [&] { |
| auto* arr = b.Var( |
| "arr", ty.ptr(function, ty.array(ty.array(ty.array(ty.array(ty.u32(), 4), 5), 6), 7))); |
| auto* access = b.Access(ty.ptr<function, u32>(), arr, idx1, idx2, idx3, idx4); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx1:u32, %idx2:u32, %idx3:u32, %idx4:u32):u32 -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<array<array<array<u32, 4>, 5>, 6>, 7>, read_write> = var |
| %7:ptr<function, u32, read_write> = access %arr, %idx1, %idx2, %idx3, %idx4 |
| %8:u32 = load %7 |
| ret %8 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx1:u32, %idx2:u32, %idx3:u32, %idx4:u32):u32 -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<array<array<array<u32, 4>, 5>, 6>, 7>, read_write> = var |
| %7:u32 = min %idx1, 6u |
| %8:u32 = min %idx2, 5u |
| %9:u32 = min %idx3, 4u |
| %10:u32 = min %idx4, 3u |
| %11:ptr<function, u32, read_write> = access %arr, %7, %8, %9, %10 |
| %12:u32 = load %11 |
| ret %12 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, NestedMixedTypes) { |
| auto* structure = ty.Struct(mod.symbols.Register("structure"), |
| { |
| {mod.symbols.Register("arr"), ty.array(ty.mat3x4<f32>(), 4)}, |
| }); |
| auto* func = b.Function("foo", ty.vec4<f32>()); |
| auto* idx1 = b.FunctionParam("idx1", ty.u32()); |
| auto* idx2 = b.FunctionParam("idx2", ty.u32()); |
| auto* idx3 = b.FunctionParam("idx3", ty.u32()); |
| func->SetParams({idx1, idx2, idx3}); |
| b.Append(func->Block(), [&] { |
| auto* arr = b.Var("arr", ty.ptr(function, ty.array(structure, 8))); |
| auto* access = |
| b.Access(ty.ptr<function, vec4<f32>>(), arr, idx1, b.Constant(0_u), idx2, idx3); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| structure = struct @align(16) { |
| arr:array<mat3x4<f32>, 4> @offset(0) |
| } |
| |
| %foo = func(%idx1:u32, %idx2:u32, %idx3:u32):vec4<f32> -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<structure, 8>, read_write> = var |
| %6:ptr<function, vec4<f32>, read_write> = access %arr, %idx1, 0u, %idx2, %idx3 |
| %7:vec4<f32> = load %6 |
| ret %7 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| structure = struct @align(16) { |
| arr:array<mat3x4<f32>, 4> @offset(0) |
| } |
| |
| %foo = func(%idx1:u32, %idx2:u32, %idx3:u32):vec4<f32> -> %b1 { |
| %b1 = block { |
| %arr:ptr<function, array<structure, 8>, read_write> = var |
| %6:u32 = min %idx1, 7u |
| %7:u32 = min %idx2, 3u |
| %8:u32 = min %idx3, 2u |
| %9:ptr<function, vec4<f32>, read_write> = access %arr, %6, 0u, %7, %8 |
| %10:vec4<f32> = load %9 |
| ret %10 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_function = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| //////////////////////////////////////////////////////////////// |
| // Test the clamp toggles for every other address space. |
| //////////////////////////////////////////////////////////////// |
| |
| TEST_P(IR_RobustnessTest, Private_LoadVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(private_, ty.vec4<u32>())); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* load = b.LoadVectorElement(vec, idx); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<private, vec4<u32>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = load_vector_element %vec, %idx |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<private, vec4<u32>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:u32 = load_vector_element %vec, %4 |
| ret %5 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_private = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Private_StoreVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(private_, ty.vec4<u32>())); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.void_()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| b.StoreVectorElement(vec, idx, b.Constant(0_u)); |
| b.Return(func); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<private, vec4<u32>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| store_vector_element %vec, %idx, 0u |
| ret |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<private, vec4<u32>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| store_vector_element %vec, %4, 0u |
| ret |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_private = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Private_Access) { |
| auto* arr = b.Var("arr", ty.ptr(private_, ty.array<u32, 4>())); |
| mod.root_block->Append(arr); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<private_, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %arr:ptr<private, array<u32, 4>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:ptr<private, u32, read_write> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %arr:ptr<private, array<u32, 4>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:ptr<private, u32, read_write> = access %arr, %4 |
| %6:u32 = load %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_private = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, PushConstant_LoadVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(push_constant, ty.vec4<u32>())); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* load = b.LoadVectorElement(vec, idx); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<push_constant, vec4<u32>, read> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = load_vector_element %vec, %idx |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<push_constant, vec4<u32>, read> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:u32 = load_vector_element %vec, %4 |
| ret %5 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_push_constant = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, PushConstant_StoreVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(push_constant, ty.vec4<u32>())); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.void_()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| b.StoreVectorElement(vec, idx, b.Constant(0_u)); |
| b.Return(func); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<push_constant, vec4<u32>, read> = var |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| store_vector_element %vec, %idx, 0u |
| ret |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<push_constant, vec4<u32>, read> = var |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| store_vector_element %vec, %4, 0u |
| ret |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_push_constant = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, PushConstant_Access) { |
| auto* arr = b.Var("arr", ty.ptr(push_constant, ty.array<u32, 4>())); |
| mod.root_block->Append(arr); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<push_constant, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %arr:ptr<push_constant, array<u32, 4>, read> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:ptr<push_constant, u32, read> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %arr:ptr<push_constant, array<u32, 4>, read> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:ptr<push_constant, u32, read> = access %arr, %4 |
| %6:u32 = load %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_push_constant = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Storage_LoadVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(storage, ty.vec4<u32>())); |
| vec->SetBindingPoint(0, 0); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* load = b.LoadVectorElement(vec, idx); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<storage, vec4<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = load_vector_element %vec, %idx |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<storage, vec4<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:u32 = load_vector_element %vec, %4 |
| ret %5 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_storage = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Storage_StoreVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(storage, ty.vec4<u32>())); |
| vec->SetBindingPoint(0, 0); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.void_()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| b.StoreVectorElement(vec, idx, b.Constant(0_u)); |
| b.Return(func); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<storage, vec4<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| store_vector_element %vec, %idx, 0u |
| ret |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<storage, vec4<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| store_vector_element %vec, %4, 0u |
| ret |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_storage = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Storage_Access) { |
| auto* arr = b.Var("arr", ty.ptr(storage, ty.array<u32, 4>())); |
| arr->SetBindingPoint(0, 0); |
| mod.root_block->Append(arr); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<storage, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %arr:ptr<storage, array<u32, 4>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:ptr<storage, u32, read_write> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %arr:ptr<storage, array<u32, 4>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:ptr<storage, u32, read_write> = access %arr, %4 |
| %6:u32 = load %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_storage = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Unifom_LoadVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(uniform, ty.vec4<u32>())); |
| vec->SetBindingPoint(0, 0); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* load = b.LoadVectorElement(vec, idx); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<uniform, vec4<u32>, read> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = load_vector_element %vec, %idx |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<uniform, vec4<u32>, read> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:u32 = load_vector_element %vec, %4 |
| ret %5 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_uniform = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Unifom_StoreVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(uniform, ty.vec4<u32>())); |
| vec->SetBindingPoint(0, 0); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.void_()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| b.StoreVectorElement(vec, idx, b.Constant(0_u)); |
| b.Return(func); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<uniform, vec4<u32>, read> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| store_vector_element %vec, %idx, 0u |
| ret |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<uniform, vec4<u32>, read> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| store_vector_element %vec, %4, 0u |
| ret |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_uniform = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Uniform_Access) { |
| auto* arr = b.Var("arr", ty.ptr(uniform, ty.array<u32, 4>())); |
| arr->SetBindingPoint(0, 0); |
| mod.root_block->Append(arr); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<uniform, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %arr:ptr<uniform, array<u32, 4>, read> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:ptr<uniform, u32, read> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %arr:ptr<uniform, array<u32, 4>, read> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:ptr<uniform, u32, read> = access %arr, %4 |
| %6:u32 = load %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_uniform = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Workgroup_LoadVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(workgroup, ty.vec4<u32>())); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* load = b.LoadVectorElement(vec, idx); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<workgroup, vec4<u32>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = load_vector_element %vec, %idx |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<workgroup, vec4<u32>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:u32 = load_vector_element %vec, %4 |
| ret %5 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_workgroup = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Workgroup_StoreVectorElement) { |
| auto* vec = b.Var("vec", ty.ptr(workgroup, ty.vec4<u32>())); |
| mod.root_block->Append(vec); |
| |
| auto* func = b.Function("foo", ty.void_()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| b.StoreVectorElement(vec, idx, b.Constant(0_u)); |
| b.Return(func); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %vec:ptr<workgroup, vec4<u32>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| store_vector_element %vec, %idx, 0u |
| ret |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %vec:ptr<workgroup, vec4<u32>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):void -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| store_vector_element %vec, %4, 0u |
| ret |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_workgroup = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, Workgroup_Access) { |
| auto* arr = b.Var("arr", ty.ptr(workgroup, ty.array<u32, 4>())); |
| mod.root_block->Append(arr); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<workgroup, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %arr:ptr<workgroup, array<u32, 4>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:ptr<workgroup, u32, read_write> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %arr:ptr<workgroup, array<u32, 4>, read_write> = var |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = min %idx, 3u |
| %5:ptr<workgroup, u32, read_write> = access %arr, %4 |
| %6:u32 = load %5 |
| ret %6 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_workgroup = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| //////////////////////////////////////////////////////////////// |
| // Test clamping non-pointer values. |
| //////////////////////////////////////////////////////////////// |
| |
| TEST_P(IR_RobustnessTest, ConstantVector_DynamicIndex) { |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* vec = mod.constant_values.Composite(ty.vec4<u32>(), Vector{ |
| mod.constant_values.Get(1_u), |
| mod.constant_values.Get(2_u), |
| mod.constant_values.Get(3_u), |
| mod.constant_values.Get(4_u), |
| }); |
| auto* element = b.Access(ty.u32(), b.Constant(vec), idx); |
| b.Return(func, element); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %3:u32 = access vec4<u32>(1u, 2u, 3u, 4u), %idx |
| ret %3 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %3:u32 = min %idx, 3u |
| %4:u32 = access vec4<u32>(1u, 2u, 3u, 4u), %3 |
| ret %4 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_value = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, ConstantArray_DynamicIndex) { |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* arr = |
| mod.constant_values.Composite(ty.array<u32, 4>(), Vector{ |
| mod.constant_values.Get(1_u), |
| mod.constant_values.Get(2_u), |
| mod.constant_values.Get(3_u), |
| mod.constant_values.Get(4_u), |
| }); |
| auto* element = b.Access(ty.u32(), b.Constant(arr), idx); |
| b.Return(func, element); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %3:u32 = access array<u32, 4>(1u, 2u, 3u, 4u), %idx |
| ret %3 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %3:u32 = min %idx, 3u |
| %4:u32 = access array<u32, 4>(1u, 2u, 3u, 4u), %3 |
| ret %4 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_value = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, ParamValueArray_DynamicIndex) { |
| auto* func = b.Function("foo", ty.u32()); |
| auto* arr = b.FunctionParam("arr", ty.array<u32, 4>()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({arr, idx}); |
| b.Append(func->Block(), [&] { |
| auto* element = b.Access(ty.u32(), arr, idx); |
| b.Return(func, element); |
| }); |
| |
| auto* src = R"( |
| %foo = func(%arr:array<u32, 4>, %idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %4:u32 = access %arr, %idx |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %foo = func(%arr:array<u32, 4>, %idx:u32):u32 -> %b1 { |
| %b1 = block { |
| %4:u32 = min %idx, 3u |
| %5:u32 = access %arr, %4 |
| ret %5 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_value = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(, IR_RobustnessTest, testing::Values(false, true)); |
| |
| //////////////////////////////////////////////////////////////// |
| // Test clamping runtime-sized arrays. |
| //////////////////////////////////////////////////////////////// |
| |
| TEST_P(IR_RobustnessTest, RuntimeSizedArray_ConstIndex) { |
| auto* arr = b.Var("arr", ty.ptr(storage, ty.array<u32>())); |
| arr->SetBindingPoint(0, 0); |
| mod.root_block->Append(arr); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<storage, u32>(), arr, b.Constant(42_u)); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %arr:ptr<storage, array<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func():u32 -> %b2 { |
| %b2 = block { |
| %3:ptr<storage, u32, read_write> = access %arr, 42u |
| %4:u32 = load %3 |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %arr:ptr<storage, array<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func():u32 -> %b2 { |
| %b2 = block { |
| %3:u32 = arrayLength %arr |
| %4:u32 = sub %3, 1u |
| %5:u32 = min 42u, %4 |
| %6:ptr<storage, u32, read_write> = access %arr, %5 |
| %7:u32 = load %6 |
| ret %7 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_storage = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, RuntimeSizedArray_DynamicIndex) { |
| auto* arr = b.Var("arr", ty.ptr(storage, ty.array<u32>())); |
| arr->SetBindingPoint(0, 0); |
| mod.root_block->Append(arr); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<storage, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %arr:ptr<storage, array<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:ptr<storage, u32, read_write> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %arr:ptr<storage, array<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = arrayLength %arr |
| %5:u32 = sub %4, 1u |
| %6:u32 = min %idx, %5 |
| %7:ptr<storage, u32, read_write> = access %arr, %6 |
| %8:u32 = load %7 |
| ret %8 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_storage = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, RuntimeSizedArray_InStruct_ConstIndex) { |
| auto* structure = ty.Struct(mod.symbols.Register("structure"), |
| { |
| {mod.symbols.Register("arr"), ty.array<u32>()}, |
| }); |
| |
| auto* buffer = b.Var("buffer", ty.ptr(storage, structure)); |
| buffer->SetBindingPoint(0, 0); |
| mod.root_block->Append(buffer); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<storage, u32>(), buffer, b.Constant(0_u), b.Constant(42_u)); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| structure = struct @align(4) { |
| arr:array<u32> @offset(0) |
| } |
| |
| %b1 = block { # root |
| %buffer:ptr<storage, structure, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func():u32 -> %b2 { |
| %b2 = block { |
| %3:ptr<storage, u32, read_write> = access %buffer, 0u, 42u |
| %4:u32 = load %3 |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| structure = struct @align(4) { |
| arr:array<u32> @offset(0) |
| } |
| |
| %b1 = block { # root |
| %buffer:ptr<storage, structure, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func():u32 -> %b2 { |
| %b2 = block { |
| %3:ptr<storage, array<u32>, read_write> = access %buffer, 0u |
| %4:u32 = arrayLength %3 |
| %5:u32 = sub %4, 1u |
| %6:u32 = min 42u, %5 |
| %7:ptr<storage, u32, read_write> = access %buffer, 0u, %6 |
| %8:u32 = load %7 |
| ret %8 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_storage = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, RuntimeSizedArray_InStruct_DynamicIndex) { |
| auto* structure = ty.Struct(mod.symbols.Register("structure"), |
| { |
| {mod.symbols.Register("arr"), ty.array<u32>()}, |
| }); |
| |
| auto* buffer = b.Var("buffer", ty.ptr(storage, structure)); |
| buffer->SetBindingPoint(0, 0); |
| mod.root_block->Append(buffer); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<storage, u32>(), buffer, b.Constant(0_u), idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| structure = struct @align(4) { |
| arr:array<u32> @offset(0) |
| } |
| |
| %b1 = block { # root |
| %buffer:ptr<storage, structure, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:ptr<storage, u32, read_write> = access %buffer, 0u, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| structure = struct @align(4) { |
| arr:array<u32> @offset(0) |
| } |
| |
| %b1 = block { # root |
| %buffer:ptr<storage, structure, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:ptr<storage, array<u32>, read_write> = access %buffer, 0u |
| %5:u32 = arrayLength %4 |
| %6:u32 = sub %5, 1u |
| %7:u32 = min %idx, %6 |
| %8:ptr<storage, u32, read_write> = access %buffer, 0u, %7 |
| %9:u32 = load %8 |
| ret %9 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_storage = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, RuntimeSizedArray_DisableClamping) { |
| auto* arr = b.Var("arr", ty.ptr(storage, ty.array<u32>())); |
| arr->SetBindingPoint(0, 0); |
| mod.root_block->Append(arr); |
| |
| auto* func = b.Function("foo", ty.u32()); |
| auto* idx = b.FunctionParam("idx", ty.u32()); |
| func->SetParams({idx}); |
| b.Append(func->Block(), [&] { |
| auto* access = b.Access(ty.ptr<storage, u32>(), arr, idx); |
| auto* load = b.Load(access); |
| b.Return(func, load); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %arr:ptr<storage, array<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:ptr<storage, u32, read_write> = access %arr, %idx |
| %5:u32 = load %4 |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %arr:ptr<storage, array<u32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%idx:u32):u32 -> %b2 { |
| %b2 = block { |
| %4:u32 = arrayLength %arr |
| %5:u32 = sub %4, 1u |
| %6:u32 = min %idx, %5 |
| %7:ptr<storage, u32, read_write> = access %arr, %6 |
| %8:u32 = load %7 |
| ret %8 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_storage = true; |
| cfg.disable_runtime_sized_array_index_clamping = !GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| //////////////////////////////////////////////////////////////// |
| // Test clamping texture builtin calls. |
| //////////////////////////////////////////////////////////////// |
| |
| TEST_P(IR_RobustnessTest, TextureDimensions) { |
| auto* texture = b.Var( |
| "texture", |
| ty.ptr(handle, ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()), read)); |
| texture->SetBindingPoint(0, 0); |
| mod.root_block->Append(texture); |
| |
| auto* func = b.Function("foo", ty.vec2<u32>()); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* dims = b.Call(ty.vec2<u32>(), core::BuiltinFn::kTextureDimensions, handle); |
| b.Return(func, dims); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_2d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %foo = func():vec2<u32> -> %b2 { |
| %b2 = block { |
| %3:texture_2d<f32> = load %texture |
| %4:vec2<u32> = textureDimensions %3 |
| ret %4 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_texture = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, TextureDimensions_WithLevel) { |
| auto* texture = b.Var( |
| "texture", |
| ty.ptr(handle, ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()), read)); |
| texture->SetBindingPoint(0, 0); |
| mod.root_block->Append(texture); |
| |
| auto* func = b.Function("foo", ty.vec2<u32>()); |
| auto* level = b.FunctionParam("level", ty.u32()); |
| func->SetParams({level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* dims = b.Call(ty.vec2<u32>(), core::BuiltinFn::kTextureDimensions, handle, level); |
| b.Return(func, dims); |
| }); |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_2d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%level:u32):vec2<u32> -> %b2 { |
| %b2 = block { |
| %4:texture_2d<f32> = load %texture |
| %5:vec2<u32> = textureDimensions %4, %level |
| ret %5 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_2d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %foo = func(%level:u32):vec2<u32> -> %b2 { |
| %b2 = block { |
| %4:texture_2d<f32> = load %texture |
| %5:u32 = textureNumLevels %4 |
| %6:u32 = sub %5, 1u |
| %7:u32 = min %level, %6 |
| %8:vec2<u32> = textureDimensions %4, %7 |
| ret %8 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_texture = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, TextureLoad_Sampled1D) { |
| auto* texture = b.Var( |
| "texture", |
| ty.ptr(handle, ty.Get<type::SampledTexture>(type::TextureDimension::k1d, ty.f32()), read)); |
| texture->SetBindingPoint(0, 0); |
| mod.root_block->Append(texture); |
| |
| { |
| auto* func = b.Function("load_signed", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.i32()); |
| auto* level = b.FunctionParam("level", ty.i32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| { |
| auto* func = b.Function("load_unsigned", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.u32()); |
| auto* level = b.FunctionParam("level", ty.u32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_1d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:i32, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %5:texture_1d<f32> = load %texture |
| %6:vec4<f32> = textureLoad %5, %coords, %level |
| ret %6 |
| } |
| } |
| %load_unsigned = func(%coords_1:u32, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %level_1: 'level' |
| %b3 = block { |
| %10:texture_1d<f32> = load %texture |
| %11:vec4<f32> = textureLoad %10, %coords_1, %level_1 |
| ret %11 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_1d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:i32, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %5:texture_1d<f32> = load %texture |
| %6:u32 = textureDimensions %5 |
| %7:u32 = sub %6, 1u |
| %8:u32 = convert %coords |
| %9:u32 = min %8, %7 |
| %10:u32 = textureNumLevels %5 |
| %11:u32 = sub %10, 1u |
| %12:u32 = convert %level |
| %13:u32 = min %12, %11 |
| %14:vec4<f32> = textureLoad %5, %9, %13 |
| ret %14 |
| } |
| } |
| %load_unsigned = func(%coords_1:u32, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %level_1: 'level' |
| %b3 = block { |
| %18:texture_1d<f32> = load %texture |
| %19:u32 = textureDimensions %18 |
| %20:u32 = sub %19, 1u |
| %21:u32 = min %coords_1, %20 |
| %22:u32 = textureNumLevels %18 |
| %23:u32 = sub %22, 1u |
| %24:u32 = min %level_1, %23 |
| %25:vec4<f32> = textureLoad %18, %21, %24 |
| ret %25 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_texture = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, TextureLoad_Sampled2D) { |
| auto* texture = b.Var( |
| "texture", |
| ty.ptr(handle, ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()), read)); |
| texture->SetBindingPoint(0, 0); |
| mod.root_block->Append(texture); |
| |
| { |
| auto* func = b.Function("load_signed", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.vec2<i32>()); |
| auto* level = b.FunctionParam("level", ty.i32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| { |
| auto* func = b.Function("load_unsigned", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.vec2<u32>()); |
| auto* level = b.FunctionParam("level", ty.u32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_2d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:vec2<i32>, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %5:texture_2d<f32> = load %texture |
| %6:vec4<f32> = textureLoad %5, %coords, %level |
| ret %6 |
| } |
| } |
| %load_unsigned = func(%coords_1:vec2<u32>, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %level_1: 'level' |
| %b3 = block { |
| %10:texture_2d<f32> = load %texture |
| %11:vec4<f32> = textureLoad %10, %coords_1, %level_1 |
| ret %11 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_2d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:vec2<i32>, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %5:texture_2d<f32> = load %texture |
| %6:vec2<u32> = textureDimensions %5 |
| %7:vec2<u32> = sub %6, vec2<u32>(1u) |
| %8:vec2<u32> = convert %coords |
| %9:vec2<u32> = min %8, %7 |
| %10:u32 = textureNumLevels %5 |
| %11:u32 = sub %10, 1u |
| %12:u32 = convert %level |
| %13:u32 = min %12, %11 |
| %14:vec4<f32> = textureLoad %5, %9, %13 |
| ret %14 |
| } |
| } |
| %load_unsigned = func(%coords_1:vec2<u32>, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %level_1: 'level' |
| %b3 = block { |
| %18:texture_2d<f32> = load %texture |
| %19:vec2<u32> = textureDimensions %18 |
| %20:vec2<u32> = sub %19, vec2<u32>(1u) |
| %21:vec2<u32> = min %coords_1, %20 |
| %22:u32 = textureNumLevels %18 |
| %23:u32 = sub %22, 1u |
| %24:u32 = min %level_1, %23 |
| %25:vec4<f32> = textureLoad %18, %21, %24 |
| ret %25 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_texture = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, TextureLoad_Sampled2DArray) { |
| auto* texture = b.Var( |
| "texture", |
| ty.ptr(handle, ty.Get<type::SampledTexture>(type::TextureDimension::k2dArray, ty.f32()), |
| read)); |
| texture->SetBindingPoint(0, 0); |
| mod.root_block->Append(texture); |
| |
| { |
| auto* func = b.Function("load_signed", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.vec2<i32>()); |
| auto* layer = b.FunctionParam("layer", ty.i32()); |
| auto* level = b.FunctionParam("level", ty.i32()); |
| func->SetParams({coords, layer, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, layer, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| { |
| auto* func = b.Function("load_unsigned", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.vec2<u32>()); |
| auto* layer = b.FunctionParam("layer", ty.u32()); |
| auto* level = b.FunctionParam("level", ty.u32()); |
| func->SetParams({coords, layer, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, layer, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_2d_array<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:vec2<i32>, %layer:i32, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %6:texture_2d_array<f32> = load %texture |
| %7:vec4<f32> = textureLoad %6, %coords, %layer, %level |
| ret %7 |
| } |
| } |
| %load_unsigned = func(%coords_1:vec2<u32>, %layer_1:u32, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %layer_1: 'layer', %level_1: 'level' |
| %b3 = block { |
| %12:texture_2d_array<f32> = load %texture |
| %13:vec4<f32> = textureLoad %12, %coords_1, %layer_1, %level_1 |
| ret %13 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_2d_array<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:vec2<i32>, %layer:i32, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %6:texture_2d_array<f32> = load %texture |
| %7:vec2<u32> = textureDimensions %6 |
| %8:vec2<u32> = sub %7, vec2<u32>(1u) |
| %9:vec2<u32> = convert %coords |
| %10:vec2<u32> = min %9, %8 |
| %11:u32 = textureNumLayers %6 |
| %12:u32 = sub %11, 1u |
| %13:u32 = convert %layer |
| %14:u32 = min %13, %12 |
| %15:u32 = textureNumLevels %6 |
| %16:u32 = sub %15, 1u |
| %17:u32 = convert %level |
| %18:u32 = min %17, %16 |
| %19:vec4<f32> = textureLoad %6, %10, %14, %18 |
| ret %19 |
| } |
| } |
| %load_unsigned = func(%coords_1:vec2<u32>, %layer_1:u32, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %layer_1: 'layer', %level_1: 'level' |
| %b3 = block { |
| %24:texture_2d_array<f32> = load %texture |
| %25:vec2<u32> = textureDimensions %24 |
| %26:vec2<u32> = sub %25, vec2<u32>(1u) |
| %27:vec2<u32> = min %coords_1, %26 |
| %28:u32 = textureNumLayers %24 |
| %29:u32 = sub %28, 1u |
| %30:u32 = min %layer_1, %29 |
| %31:u32 = textureNumLevels %24 |
| %32:u32 = sub %31, 1u |
| %33:u32 = min %level_1, %32 |
| %34:vec4<f32> = textureLoad %24, %27, %30, %33 |
| ret %34 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_texture = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, TextureLoad_Sampled3D) { |
| auto* texture = b.Var( |
| "texture", |
| ty.ptr(handle, ty.Get<type::SampledTexture>(type::TextureDimension::k3d, ty.f32()), read)); |
| texture->SetBindingPoint(0, 0); |
| mod.root_block->Append(texture); |
| |
| { |
| auto* func = b.Function("load_signed", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.vec3<i32>()); |
| auto* level = b.FunctionParam("level", ty.i32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| { |
| auto* func = b.Function("load_unsigned", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.vec3<u32>()); |
| auto* level = b.FunctionParam("level", ty.u32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_3d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:vec3<i32>, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %5:texture_3d<f32> = load %texture |
| %6:vec4<f32> = textureLoad %5, %coords, %level |
| ret %6 |
| } |
| } |
| %load_unsigned = func(%coords_1:vec3<u32>, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %level_1: 'level' |
| %b3 = block { |
| %10:texture_3d<f32> = load %texture |
| %11:vec4<f32> = textureLoad %10, %coords_1, %level_1 |
| ret %11 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_3d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:vec3<i32>, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %5:texture_3d<f32> = load %texture |
| %6:vec3<u32> = textureDimensions %5 |
| %7:vec3<u32> = sub %6, vec3<u32>(1u) |
| %8:vec3<u32> = convert %coords |
| %9:vec3<u32> = min %8, %7 |
| %10:u32 = textureNumLevels %5 |
| %11:u32 = sub %10, 1u |
| %12:u32 = convert %level |
| %13:u32 = min %12, %11 |
| %14:vec4<f32> = textureLoad %5, %9, %13 |
| ret %14 |
| } |
| } |
| %load_unsigned = func(%coords_1:vec3<u32>, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %level_1: 'level' |
| %b3 = block { |
| %18:texture_3d<f32> = load %texture |
| %19:vec3<u32> = textureDimensions %18 |
| %20:vec3<u32> = sub %19, vec3<u32>(1u) |
| %21:vec3<u32> = min %coords_1, %20 |
| %22:u32 = textureNumLevels %18 |
| %23:u32 = sub %22, 1u |
| %24:u32 = min %level_1, %23 |
| %25:vec4<f32> = textureLoad %18, %21, %24 |
| ret %25 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_texture = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, TextureLoad_Multisampled2D) { |
| auto* texture = b.Var( |
| "texture", |
| ty.ptr(handle, ty.Get<type::MultisampledTexture>(type::TextureDimension::k2d, ty.f32()), |
| read)); |
| texture->SetBindingPoint(0, 0); |
| mod.root_block->Append(texture); |
| |
| { |
| auto* func = b.Function("load_signed", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.vec2<i32>()); |
| auto* level = b.FunctionParam("level", ty.i32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| { |
| auto* func = b.Function("load_unsigned", ty.vec4<f32>()); |
| auto* coords = b.FunctionParam("coords", ty.vec2<u32>()); |
| auto* level = b.FunctionParam("level", ty.u32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = |
| b.Call(ty.vec4<f32>(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_multisampled_2d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:vec2<i32>, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %5:texture_multisampled_2d<f32> = load %texture |
| %6:vec4<f32> = textureLoad %5, %coords, %level |
| ret %6 |
| } |
| } |
| %load_unsigned = func(%coords_1:vec2<u32>, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %level_1: 'level' |
| %b3 = block { |
| %10:texture_multisampled_2d<f32> = load %texture |
| %11:vec4<f32> = textureLoad %10, %coords_1, %level_1 |
| ret %11 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_multisampled_2d<f32>, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:vec2<i32>, %level:i32):vec4<f32> -> %b2 { |
| %b2 = block { |
| %5:texture_multisampled_2d<f32> = load %texture |
| %6:vec2<u32> = textureDimensions %5 |
| %7:vec2<u32> = sub %6, vec2<u32>(1u) |
| %8:vec2<u32> = convert %coords |
| %9:vec2<u32> = min %8, %7 |
| %10:vec4<f32> = textureLoad %5, %9, %level |
| ret %10 |
| } |
| } |
| %load_unsigned = func(%coords_1:vec2<u32>, %level_1:u32):vec4<f32> -> %b3 { # %coords_1: 'coords', %level_1: 'level' |
| %b3 = block { |
| %14:texture_multisampled_2d<f32> = load %texture |
| %15:vec2<u32> = textureDimensions %14 |
| %16:vec2<u32> = sub %15, vec2<u32>(1u) |
| %17:vec2<u32> = min %coords_1, %16 |
| %18:vec4<f32> = textureLoad %14, %17, %level_1 |
| ret %18 |
| } |
| } |
| )"; |
| |
| RobustnessConfig cfg; |
| cfg.clamp_texture = GetParam(); |
| Run(Robustness, cfg); |
| |
| EXPECT_EQ(GetParam() ? expect : src, str()); |
| } |
| |
| TEST_P(IR_RobustnessTest, TextureLoad_Depth2D) { |
| auto* texture = b.Var( |
| "texture", ty.ptr(handle, ty.Get<type::DepthTexture>(type::TextureDimension::k2d), read)); |
| texture->SetBindingPoint(0, 0); |
| mod.root_block->Append(texture); |
| |
| { |
| auto* func = b.Function("load_signed", ty.f32()); |
| auto* coords = b.FunctionParam("coords", ty.vec2<i32>()); |
| auto* level = b.FunctionParam("level", ty.i32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = b.Call(ty.f32(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| { |
| auto* func = b.Function("load_unsigned", ty.f32()); |
| auto* coords = b.FunctionParam("coords", ty.vec2<u32>()); |
| auto* level = b.FunctionParam("level", ty.u32()); |
| func->SetParams({coords, level}); |
| b.Append(func->Block(), [&] { |
| auto* handle = b.Load(texture); |
| auto* texel = b.Call(ty.f32(), core::BuiltinFn::kTextureLoad, handle, coords, level); |
| b.Return(func, texel); |
| }); |
| } |
| |
| auto* src = R"( |
| %b1 = block { # root |
| %texture:ptr<handle, texture_depth_2d, read> = var @binding_point(0, 0) |
| } |
| |
| %load_signed = func(%coords:vec2<i32>, %level:i32):f32 -> %b2 { |
| %b2 = block { |
| %5:texture_depth_2d = load %texture |
| %6:f32 = textureLoad %5, %coords, %level |
| ret %6 |
| } |
| } |
| %load_unsigned = func(%coords_1:vec2<u32>, %level_1:u32):f32 -> %b3 { |