| // 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/direct_variable_access.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/matrix.h" |
| #include "src/tint/lang/core/type/pointer.h" |
| #include "src/tint/lang/core/type/struct.h" |
| |
| namespace tint::core::ir::transform { |
| namespace { |
| |
| using namespace tint::core::fluent_types; // NOLINT |
| using namespace tint::core::number_suffixes; // NOLINT |
| |
| namespace { |
| |
| static constexpr DirectVariableAccessOptions kTransformPrivate = { |
| /* transform_private */ true, |
| /* transform_function */ false, |
| }; |
| |
| static constexpr DirectVariableAccessOptions kTransformFunction = { |
| /* transform_private */ false, |
| /* transform_function */ true, |
| }; |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // remove uncalled |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace remove_uncalled { |
| |
| using IR_DirectVariableAccessTest_RemoveUncalled = TransformTest; |
| |
| TEST_F(IR_DirectVariableAccessTest_RemoveUncalled, PtrUniform) { |
| b.Append(b.ir.root_block, [&] { b.Var<private_>("keep_me", 42_i); }); |
| |
| auto* u = b.Function("u", ty.i32()); |
| auto* p = b.FunctionParam("p", ty.ptr<uniform, i32>()); |
| u->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(u->Block(), [&] { b.Return(u, b.Load(p)); }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| %u = func(%pre:i32, %p:ptr<uniform, i32, read>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_RemoveUncalled, PtrStorage) { |
| b.Append(b.ir.root_block, [&] { b.Var<private_>("keep_me", 42_i); }); |
| |
| auto* s = b.Function("s", ty.i32()); |
| auto* p = b.FunctionParam("p", ty.ptr<storage, i32, read>()); |
| s->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(s->Block(), [&] { b.Return(s, b.Load(p)); }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| %s = func(%pre:i32, %p:ptr<storage, i32, read>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_RemoveUncalled, PtrWorkgroup) { |
| b.Append(b.ir.root_block, [&] { b.Var<private_>("keep_me", 42_i); }); |
| |
| auto* w = b.Function("w", ty.i32()); |
| auto* p = b.FunctionParam("p", ty.ptr<workgroup, i32>()); |
| w->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(w->Block(), [&] { b.Return(w, b.Load(p)); }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| %w = func(%pre:i32, %p:ptr<workgroup, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_RemoveUncalled, PtrPrivate_Disabled) { |
| b.Append(b.ir.root_block, [&] { b.Var<private_>("keep_me", 42_i); }); |
| |
| auto* f = b.Function("f", ty.i32()); |
| auto* p = b.FunctionParam("p", ty.ptr<private_, i32>()); |
| f->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(f->Block(), [&] { b.Return(f, b.Load(p)); }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| %f = func(%pre:i32, %p:ptr<private, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_RemoveUncalled, PtrPrivate_Enabled) { |
| b.Append(b.ir.root_block, [&] { b.Var<private_>("keep_me", 42_i); }); |
| |
| auto* f = b.Function("f", ty.i32()); |
| auto* p = b.FunctionParam("p", ty.ptr<private_, i32>()); |
| f->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(f->Block(), [&] { b.Return(f, b.Load(p)); }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| %f = func(%pre:i32, %p:ptr<private, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| )"; |
| Run(DirectVariableAccess, kTransformPrivate); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_RemoveUncalled, PtrFunction_Disabled) { |
| b.Append(b.ir.root_block, [&] { b.Var<private_>("keep_me", 42_i); }); |
| |
| auto* f = b.Function("f", ty.i32()); |
| auto* p = b.FunctionParam("p", ty.ptr<function, i32>()); |
| f->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(f->Block(), [&] { b.Return(f, b.Load(p)); }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| %f = func(%pre:i32, %p:ptr<function, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_RemoveUncalled, PtrFunction_Enabled) { |
| b.Append(b.ir.root_block, [&] { b.Var<private_>("keep_me", 42_i); }); |
| |
| auto* f = b.Function("f", ty.i32()); |
| auto* p = b.FunctionParam("p", ty.ptr<function, i32>()); |
| f->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(f->Block(), [&] { b.Return(f, b.Load(p)); }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| %f = func(%pre:i32, %p:ptr<function, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| )"; |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %keep_me:ptr<private, i32, read_write> = var, 42i |
| } |
| |
| )"; |
| |
| Run(DirectVariableAccess, kTransformFunction); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| } // namespace remove_uncalled |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // pointer chains |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace pointer_chains_tests { |
| |
| using IR_DirectVariableAccessTest_PtrChains = TransformTest; |
| |
| TEST_F(IR_DirectVariableAccessTest_PtrChains, ConstantIndices) { |
| Var* U = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| U = b.Var<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>("U"); |
| U->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.vec4<i32>()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<uniform, vec4<i32>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* p0 = b.Let("p0", U); |
| auto* p1 = b.Access(ty.ptr<uniform, array<array<vec4<i32>, 8>, 8>>(), p0, 1_i); |
| b.ir.SetName(p1, "p1"); |
| auto* p2 = b.Access(ty.ptr<uniform, array<vec4<i32>, 8>>(), p1, 2_i); |
| b.ir.SetName(p2, "p2"); |
| auto* p3 = b.Access(ty.ptr<uniform, vec4<i32>>(), p2, 3_i); |
| b.ir.SetName(p3, "p3"); |
| b.Call(ty.vec4<i32>(), fn_a, 10_i, p3, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* fn_c = b.Function("c", ty.void_()); |
| auto* fn_c_p = b.FunctionParam("p", ty.ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>()); |
| fn_c->SetParams({fn_c_p}); |
| b.Append(fn_c->Block(), [&] { |
| auto* p0 = b.Let("p0", fn_c_p); |
| auto* p1 = b.Access(ty.ptr<uniform, array<array<vec4<i32>, 8>, 8>>(), p0, 1_i); |
| b.ir.SetName(p1, "p1"); |
| auto* p2 = b.Access(ty.ptr<uniform, array<vec4<i32>, 8>>(), p1, 2_i); |
| b.ir.SetName(p2, "p2"); |
| auto* p3 = b.Access(ty.ptr<uniform, vec4<i32>>(), p2, 3_i); |
| b.ir.SetName(p3, "p3"); |
| b.Call(ty.vec4<i32>(), fn_a, 10_i, p3, 20_i); |
| b.Return(fn_c); |
| }); |
| |
| auto* fn_d = b.Function("d", ty.void_()); |
| b.Append(fn_d->Block(), [&] { |
| b.Call(ty.void_(), fn_c, U); |
| b.Return(fn_d); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %U:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<uniform, vec4<i32>, read>, %post:i32):vec4<i32> { |
| $B2: { |
| %6:vec4<i32> = load %p |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %p0:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read> = let %U |
| %p1:ptr<uniform, array<array<vec4<i32>, 8>, 8>, read> = access %p0, 1i |
| %p2:ptr<uniform, array<vec4<i32>, 8>, read> = access %p1, 2i |
| %p3:ptr<uniform, vec4<i32>, read> = access %p2, 3i |
| %12:vec4<i32> = call %a, 10i, %p3, 20i |
| ret |
| } |
| } |
| %c = func(%p_1:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read>):void { # %p_1: 'p' |
| $B4: { |
| %p0_1:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read> = let %p_1 # %p0_1: 'p0' |
| %p1_1:ptr<uniform, array<array<vec4<i32>, 8>, 8>, read> = access %p0_1, 1i # %p1_1: 'p1' |
| %p2_1:ptr<uniform, array<vec4<i32>, 8>, read> = access %p1_1, 2i # %p2_1: 'p2' |
| %p3_1:ptr<uniform, vec4<i32>, read> = access %p2_1, 3i # %p3_1: 'p3' |
| %19:vec4<i32> = call %a, 10i, %p3_1, 20i |
| ret |
| } |
| } |
| %d = func():void { |
| $B5: { |
| %21:void = call %c, %U |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = |
| R"( |
| $B1: { # root |
| %U:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %p_indices:array<u32, 3>, %post:i32):vec4<i32> { |
| $B2: { |
| %6:u32 = access %p_indices, 0u |
| %7:u32 = access %p_indices, 1u |
| %8:u32 = access %p_indices, 2u |
| %9:ptr<uniform, vec4<i32>, read> = access %U, %6, %7, %8 |
| %10:vec4<i32> = load %9 |
| ret %10 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %12:u32 = convert 3i |
| %13:u32 = convert 2i |
| %14:u32 = convert 1i |
| %15:array<u32, 3> = construct %14, %13, %12 |
| %16:vec4<i32> = call %a, 10i, %15, 20i |
| ret |
| } |
| } |
| %c = func():void { |
| $B4: { |
| %18:u32 = convert 3i |
| %19:u32 = convert 2i |
| %20:u32 = convert 1i |
| %21:array<u32, 3> = construct %20, %19, %18 |
| %22:vec4<i32> = call %a, 10i, %21, 20i |
| ret |
| } |
| } |
| %d = func():void { |
| $B5: { |
| %24:void = call %c |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PtrChains, DynamicIndices) { |
| Var* U = nullptr; |
| Var* i = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| U = b.Var<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>("U"); |
| U->SetBindingPoint(0, 0); |
| i = b.Var<private_, i32>("i"); |
| }); |
| |
| auto* fn_first = b.Function("first", ty.i32()); |
| auto* fn_second = b.Function("second", ty.i32()); |
| auto* fn_third = b.Function("third", ty.i32()); |
| for (auto fn : {fn_first, fn_second, fn_third}) { |
| b.Append(fn->Block(), [&] { |
| b.Store(i, b.Add(ty.i32(), b.Load(i), 1_i)); |
| b.Return(fn, b.Load(i)); |
| }); |
| } |
| |
| auto* fn_a = b.Function("a", ty.vec4<i32>()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<uniform, vec4<i32>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* p0 = b.Let("p0", U); |
| auto* first = b.Call(fn_first); |
| auto* p1 = b.Access(ty.ptr<uniform, array<array<vec4<i32>, 8>, 8>>(), p0, first); |
| b.ir.SetName(p1, "p1"); |
| auto* second = b.Call(fn_second); |
| auto* third = b.Call(fn_third); |
| auto* p2 = b.Access(ty.ptr<uniform, vec4<i32>>(), p1, second, third); |
| b.ir.SetName(p2, "p2"); |
| b.Call(ty.vec4<i32>(), fn_a, 10_i, p2, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* fn_c = b.Function("c", ty.void_()); |
| auto* fn_c_p = b.FunctionParam("p", ty.ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>()); |
| fn_c->SetParams({fn_c_p}); |
| b.Append(fn_c->Block(), [&] { |
| auto* p0 = b.Let("p0", fn_c_p); |
| auto* first = b.Call(fn_first); |
| auto* p1 = b.Access(ty.ptr<uniform, array<array<vec4<i32>, 8>, 8>>(), p0, first); |
| b.ir.SetName(p1, "p1"); |
| auto* second = b.Call(fn_second); |
| auto* third = b.Call(fn_third); |
| auto* p2 = b.Access(ty.ptr<uniform, vec4<i32>>(), p1, second, third); |
| b.ir.SetName(p2, "p2"); |
| b.Call(ty.vec4<i32>(), fn_a, 10_i, p2, 20_i); |
| b.Return(fn_c); |
| }); |
| |
| auto* fn_d = b.Function("d", ty.void_()); |
| b.Append(fn_d->Block(), [&] { |
| b.Call(ty.void_(), fn_c, U); |
| b.Return(fn_d); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %U:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read> = var @binding_point(0, 0) |
| %i:ptr<private, i32, read_write> = var |
| } |
| |
| %first = func():i32 { |
| $B2: { |
| %4:i32 = load %i |
| %5:i32 = add %4, 1i |
| store %i, %5 |
| %6:i32 = load %i |
| ret %6 |
| } |
| } |
| %second = func():i32 { |
| $B3: { |
| %8:i32 = load %i |
| %9:i32 = add %8, 1i |
| store %i, %9 |
| %10:i32 = load %i |
| ret %10 |
| } |
| } |
| %third = func():i32 { |
| $B4: { |
| %12:i32 = load %i |
| %13:i32 = add %12, 1i |
| store %i, %13 |
| %14:i32 = load %i |
| ret %14 |
| } |
| } |
| %a = func(%pre:i32, %p:ptr<uniform, vec4<i32>, read>, %post:i32):vec4<i32> { |
| $B5: { |
| %19:vec4<i32> = load %p |
| ret %19 |
| } |
| } |
| %b = func():void { |
| $B6: { |
| %p0:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read> = let %U |
| %22:i32 = call %first |
| %p1:ptr<uniform, array<array<vec4<i32>, 8>, 8>, read> = access %p0, %22 |
| %24:i32 = call %second |
| %25:i32 = call %third |
| %p2:ptr<uniform, vec4<i32>, read> = access %p1, %24, %25 |
| %27:vec4<i32> = call %a, 10i, %p2, 20i |
| ret |
| } |
| } |
| %c = func(%p_1:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read>):void { # %p_1: 'p' |
| $B7: { |
| %p0_1:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read> = let %p_1 # %p0_1: 'p0' |
| %31:i32 = call %first |
| %p1_1:ptr<uniform, array<array<vec4<i32>, 8>, 8>, read> = access %p0_1, %31 # %p1_1: 'p1' |
| %33:i32 = call %second |
| %34:i32 = call %third |
| %p2_1:ptr<uniform, vec4<i32>, read> = access %p1_1, %33, %34 # %p2_1: 'p2' |
| %36:vec4<i32> = call %a, 10i, %p2_1, 20i |
| ret |
| } |
| } |
| %d = func():void { |
| $B8: { |
| %38:void = call %c, %U |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %U:ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>, read> = var @binding_point(0, 0) |
| %i:ptr<private, i32, read_write> = var |
| } |
| |
| %first = func():i32 { |
| $B2: { |
| %4:i32 = load %i |
| %5:i32 = add %4, 1i |
| store %i, %5 |
| %6:i32 = load %i |
| ret %6 |
| } |
| } |
| %second = func():i32 { |
| $B3: { |
| %8:i32 = load %i |
| %9:i32 = add %8, 1i |
| store %i, %9 |
| %10:i32 = load %i |
| ret %10 |
| } |
| } |
| %third = func():i32 { |
| $B4: { |
| %12:i32 = load %i |
| %13:i32 = add %12, 1i |
| store %i, %13 |
| %14:i32 = load %i |
| ret %14 |
| } |
| } |
| %a = func(%pre:i32, %p_indices:array<u32, 3>, %post:i32):vec4<i32> { |
| $B5: { |
| %19:u32 = access %p_indices, 0u |
| %20:u32 = access %p_indices, 1u |
| %21:u32 = access %p_indices, 2u |
| %22:ptr<uniform, vec4<i32>, read> = access %U, %19, %20, %21 |
| %23:vec4<i32> = load %22 |
| ret %23 |
| } |
| } |
| %b = func():void { |
| $B6: { |
| %25:i32 = call %first |
| %26:i32 = call %second |
| %27:i32 = call %third |
| %28:u32 = convert %26 |
| %29:u32 = convert %27 |
| %30:u32 = convert %25 |
| %31:array<u32, 3> = construct %30, %28, %29 |
| %32:vec4<i32> = call %a, 10i, %31, 20i |
| ret |
| } |
| } |
| %c = func():void { |
| $B7: { |
| %34:i32 = call %first |
| %35:i32 = call %second |
| %36:i32 = call %third |
| %37:u32 = convert %35 |
| %38:u32 = convert %36 |
| %39:u32 = convert %34 |
| %40:array<u32, 3> = construct %39, %37, %38 |
| %41:vec4<i32> = call %a, 10i, %40, 20i |
| ret |
| } |
| } |
| %d = func():void { |
| $B8: { |
| %43:void = call %c |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| } // namespace pointer_chains_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // 'uniform' address space |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace uniform_as_tests { |
| |
| using IR_DirectVariableAccessTest_UniformAS = TransformTest; |
| |
| TEST_F(IR_DirectVariableAccessTest_UniformAS, Param_ptr_i32_read) { |
| Var* U = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| U = b.Var<uniform, i32>("U"); |
| U->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<uniform, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| b.Call(fn_a, 10_i, U, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %U:ptr<uniform, i32, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<uniform, i32, read>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:i32 = call %a, 10i, %U, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %U:ptr<uniform, i32, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %post:i32):i32 { |
| $B2: { |
| %5:ptr<uniform, i32, read> = access %U |
| %6:i32 = load %5 |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:i32 = call %a, 10i, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_UniformAS, Param_ptr_vec4i32_Via_array_DynamicRead) { |
| Var* U = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| U = b.Var<uniform, array<vec4<i32>, 8>>("U"); |
| U->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.vec4<i32>()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<uniform, vec4<i32>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* I = b.Let("I", 3_i); |
| auto* access = b.Access(ty.ptr<uniform, vec4<i32>>(), U, I); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %U:ptr<uniform, array<vec4<i32>, 8>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<uniform, vec4<i32>, read>, %post:i32):vec4<i32> { |
| $B2: { |
| %6:vec4<i32> = load %p |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %I:i32 = let 3i |
| %9:ptr<uniform, vec4<i32>, read> = access %U, %I |
| %10:vec4<i32> = call %a, 10i, %9, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %U:ptr<uniform, array<vec4<i32>, 8>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):vec4<i32> { |
| $B2: { |
| %6:u32 = access %p_indices, 0u |
| %7:ptr<uniform, vec4<i32>, read> = access %U, %6 |
| %8:vec4<i32> = load %7 |
| ret %8 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %I:i32 = let 3i |
| %11:u32 = convert %I |
| %12:array<u32, 1> = construct %11 |
| %13:vec4<i32> = call %a, 10i, %12, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_UniformAS, CallChaining) { |
| auto* Inner = |
| ty.Struct(mod.symbols.New("Inner"), { |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| auto* Outer = |
| ty.Struct(mod.symbols.New("Outer"), { |
| {mod.symbols.Register("arr"), ty.array(Inner, 4)}, |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| Var* U = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| U = b.Var("U", ty.ptr<uniform>(Outer)); |
| U->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_0 = b.Function("f0", ty.f32()); |
| auto* fn_0_p = b.FunctionParam("p", ty.ptr<uniform, vec4<f32>>()); |
| fn_0->SetParams({fn_0_p}); |
| b.Append(fn_0->Block(), [&] { b.Return(fn_0, b.LoadVectorElement(fn_0_p, 0_u)); }); |
| |
| auto* fn_1 = b.Function("f1", ty.f32()); |
| auto* fn_1_p = b.FunctionParam("p", ty.ptr<uniform, mat3x4<f32>>()); |
| fn_1->SetParams({fn_1_p}); |
| b.Append(fn_1->Block(), [&] { |
| auto* res = b.Var<function, f32>("res"); |
| { |
| // res += f0(&(*p)[1]); |
| auto* call_0 = b.Call(fn_0, b.Access(ty.ptr<uniform, vec4<f32>>(), fn_1_p, 1_i)); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &(*p)[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<uniform, vec4<f32>>(), fn_1_p, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // res += f0(&U.arr[2].mat[1]); |
| auto* access = b.Access(ty.ptr<uniform, vec4<f32>>(), U, 0_u, 2_i, 0_u, 1_i); |
| auto* call_0 = b.Call(fn_0, access); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &U.arr[2].mat[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<uniform, vec4<f32>>(), U, 0_u, 2_i, 0_u, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| |
| b.Return(fn_1, b.Load(res)); |
| }); |
| |
| auto* fn_2 = b.Function("f2", ty.f32()); |
| auto* fn_2_p = b.FunctionParam("p", ty.ptr<uniform>(Inner)); |
| fn_2->SetParams({fn_2_p}); |
| b.Append(fn_2->Block(), [&] { |
| auto* p_mat = b.Access(ty.ptr<uniform, mat3x4<f32>>(), fn_2_p, 0_u); |
| b.ir.SetName(p_mat, "p_mat"); |
| b.Return(fn_2, b.Call(fn_1, p_mat)); |
| }); |
| |
| auto* fn_3 = b.Function("f3", ty.f32()); |
| auto* fn_3_p0 = b.FunctionParam("p0", ty.ptr<uniform>(ty.array(Inner, 4))); |
| auto* fn_3_p1 = b.FunctionParam("p1", ty.ptr<uniform, mat3x4<f32>>()); |
| fn_3->SetParams({fn_3_p0, fn_3_p1}); |
| b.Append(fn_3->Block(), [&] { |
| auto* p0_inner = b.Access(ty.ptr<uniform>(Inner), fn_3_p0, 3_i); |
| b.ir.SetName(p0_inner, "p0_inner"); |
| auto* call_0 = b.Call(ty.f32(), fn_2, p0_inner); |
| auto* call_1 = b.Call(ty.f32(), fn_1, fn_3_p1); |
| b.Return(fn_3, b.Add(ty.f32(), call_0, call_1)); |
| }); |
| |
| auto* fn_4 = b.Function("f4", ty.f32()); |
| auto* fn_4_p = b.FunctionParam("p", ty.ptr<uniform>(Outer)); |
| fn_4->SetParams({fn_4_p}); |
| b.Append(fn_4->Block(), [&] { |
| auto* access_0 = b.Access(ty.ptr<uniform>(ty.array(Inner, 4)), fn_4_p, 0_u); |
| auto* access_1 = b.Access(ty.ptr<uniform, mat3x4<f32>>(), U, 1_u); |
| b.Return(fn_4, b.Call(ty.f32(), fn_3, access_0, access_1)); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| b.Call(ty.f32(), fn_4, U); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| $B1: { # root |
| %U:ptr<uniform, Outer, read> = var @binding_point(0, 0) |
| } |
| |
| %f0 = func(%p:ptr<uniform, vec4<f32>, read>):f32 { |
| $B2: { |
| %4:f32 = load_vector_element %p, 0u |
| ret %4 |
| } |
| } |
| %f1 = func(%p_1:ptr<uniform, mat3x4<f32>, read>):f32 { # %p_1: 'p' |
| $B3: { |
| %res:ptr<function, f32, read_write> = var |
| %8:ptr<uniform, vec4<f32>, read> = access %p_1, 1i |
| %9:f32 = call %f0, %8 |
| %10:f32 = load %res |
| %11:f32 = add %10, %9 |
| store %res, %11 |
| %p_vec:ptr<uniform, vec4<f32>, read> = access %p_1, 1i |
| %13:f32 = call %f0, %p_vec |
| %14:f32 = load %res |
| %15:f32 = add %14, %13 |
| store %res, %15 |
| %16:ptr<uniform, vec4<f32>, read> = access %U, 0u, 2i, 0u, 1i |
| %17:f32 = call %f0, %16 |
| %18:f32 = load %res |
| %19:f32 = add %18, %17 |
| store %res, %19 |
| %p_vec_1:ptr<uniform, vec4<f32>, read> = access %U, 0u, 2i, 0u, 1i # %p_vec_1: 'p_vec' |
| %21:f32 = call %f0, %p_vec_1 |
| %22:f32 = load %res |
| %23:f32 = add %22, %21 |
| store %res, %23 |
| %24:f32 = load %res |
| ret %24 |
| } |
| } |
| %f2 = func(%p_2:ptr<uniform, Inner, read>):f32 { # %p_2: 'p' |
| $B4: { |
| %p_mat:ptr<uniform, mat3x4<f32>, read> = access %p_2, 0u |
| %28:f32 = call %f1, %p_mat |
| ret %28 |
| } |
| } |
| %f3 = func(%p0:ptr<uniform, array<Inner, 4>, read>, %p1:ptr<uniform, mat3x4<f32>, read>):f32 { |
| $B5: { |
| %p0_inner:ptr<uniform, Inner, read> = access %p0, 3i |
| %33:f32 = call %f2, %p0_inner |
| %34:f32 = call %f1, %p1 |
| %35:f32 = add %33, %34 |
| ret %35 |
| } |
| } |
| %f4 = func(%p_3:ptr<uniform, Outer, read>):f32 { # %p_3: 'p' |
| $B6: { |
| %38:ptr<uniform, array<Inner, 4>, read> = access %p_3, 0u |
| %39:ptr<uniform, mat3x4<f32>, read> = access %U, 1u |
| %40:f32 = call %f3, %38, %39 |
| ret %40 |
| } |
| } |
| %b = func():void { |
| $B7: { |
| %42:f32 = call %f4, %U |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| $B1: { # root |
| %U:ptr<uniform, Outer, read> = var @binding_point(0, 0) |
| } |
| |
| %f0 = func(%p_indices:array<u32, 1>):f32 { |
| $B2: { |
| %4:u32 = access %p_indices, 0u |
| %5:ptr<uniform, vec4<f32>, read> = access %U, 1u, %4 |
| %6:f32 = load_vector_element %5, 0u |
| ret %6 |
| } |
| } |
| %f0_1 = func(%p_indices_1:array<u32, 2>):f32 { # %f0_1: 'f0', %p_indices_1: 'p_indices' |
| $B3: { |
| %9:u32 = access %p_indices_1, 0u |
| %10:u32 = access %p_indices_1, 1u |
| %11:ptr<uniform, vec4<f32>, read> = access %U, 0u, %9, 0u, %10 |
| %12:f32 = load_vector_element %11, 0u |
| ret %12 |
| } |
| } |
| %f1 = func():f32 { |
| $B4: { |
| %res:ptr<function, f32, read_write> = var |
| %15:u32 = convert 1i |
| %16:array<u32, 1> = construct %15 |
| %17:f32 = call %f0, %16 |
| %18:f32 = load %res |
| %19:f32 = add %18, %17 |
| store %res, %19 |
| %20:u32 = convert 1i |
| %21:array<u32, 1> = construct %20 |
| %22:f32 = call %f0, %21 |
| %23:f32 = load %res |
| %24:f32 = add %23, %22 |
| store %res, %24 |
| %25:u32 = convert 2i |
| %26:u32 = convert 1i |
| %27:array<u32, 2> = construct %25, %26 |
| %28:f32 = call %f0_1, %27 |
| %29:f32 = load %res |
| %30:f32 = add %29, %28 |
| store %res, %30 |
| %31:u32 = convert 2i |
| %32:u32 = convert 1i |
| %33:array<u32, 2> = construct %31, %32 |
| %34:f32 = call %f0_1, %33 |
| %35:f32 = load %res |
| %36:f32 = add %35, %34 |
| store %res, %36 |
| %37:f32 = load %res |
| ret %37 |
| } |
| } |
| %f1_1 = func(%p_indices_2:array<u32, 1>):f32 { # %f1_1: 'f1', %p_indices_2: 'p_indices' |
| $B5: { |
| %40:u32 = access %p_indices_2, 0u |
| %res_1:ptr<function, f32, read_write> = var # %res_1: 'res' |
| %42:u32 = convert 1i |
| %43:array<u32, 2> = construct %40, %42 |
| %44:f32 = call %f0_1, %43 |
| %45:f32 = load %res_1 |
| %46:f32 = add %45, %44 |
| store %res_1, %46 |
| %47:u32 = convert 1i |
| %48:array<u32, 2> = construct %40, %47 |
| %49:f32 = call %f0_1, %48 |
| %50:f32 = load %res_1 |
| %51:f32 = add %50, %49 |
| store %res_1, %51 |
| %52:u32 = convert 2i |
| %53:u32 = convert 1i |
| %54:array<u32, 2> = construct %52, %53 |
| %55:f32 = call %f0_1, %54 |
| %56:f32 = load %res_1 |
| %57:f32 = add %56, %55 |
| store %res_1, %57 |
| %58:u32 = convert 2i |
| %59:u32 = convert 1i |
| %60:array<u32, 2> = construct %58, %59 |
| %61:f32 = call %f0_1, %60 |
| %62:f32 = load %res_1 |
| %63:f32 = add %62, %61 |
| store %res_1, %63 |
| %64:f32 = load %res_1 |
| ret %64 |
| } |
| } |
| %f2 = func(%p_indices_3:array<u32, 1>):f32 { # %p_indices_3: 'p_indices' |
| $B6: { |
| %67:u32 = access %p_indices_3, 0u |
| %68:array<u32, 1> = construct %67 |
| %69:f32 = call %f1_1, %68 |
| ret %69 |
| } |
| } |
| %f3 = func():f32 { |
| $B7: { |
| %71:u32 = convert 3i |
| %72:array<u32, 1> = construct %71 |
| %73:f32 = call %f2, %72 |
| %74:f32 = call %f1 |
| %75:f32 = add %73, %74 |
| ret %75 |
| } |
| } |
| %f4 = func():f32 { |
| $B8: { |
| %77:f32 = call %f3 |
| ret %77 |
| } |
| } |
| %b = func():void { |
| $B9: { |
| %79:f32 = call %f4 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_UniformAS, CallChaining2) { |
| auto* T3 = ty.vec4<i32>(); |
| auto* T2 = ty.array(T3, 5); |
| auto* T1 = ty.array(T2, 5); |
| auto* T = ty.array(T1, 5); |
| |
| Var* input = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| input = b.Var("U", ty.ptr<uniform>(T)); |
| input->SetBindingPoint(0, 0); |
| }); |
| |
| auto* f2 = b.Function("f2", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<uniform>(T2)); |
| f2->SetParams({p}); |
| b.Append(f2->Block(), |
| [&] { b.Return(f2, b.Load(b.Access<ptr<uniform, vec4<i32>>>(p, 3_u))); }); |
| } |
| |
| auto* f1 = b.Function("f1", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<uniform>(T1)); |
| f1->SetParams({p}); |
| b.Append(f1->Block(), |
| [&] { b.Return(f1, b.Call(f2, b.Access(ty.ptr<uniform>(T2), p, 2_u))); }); |
| } |
| |
| auto* f0 = b.Function("f0", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<uniform>(T)); |
| f0->SetParams({p}); |
| b.Append(f0->Block(), |
| [&] { b.Return(f0, b.Call(f1, b.Access(ty.ptr<uniform>(T1), p, 1_u))); }); |
| } |
| |
| auto* main = b.Function("main", ty.void_()); |
| b.Append(main->Block(), [&] { |
| b.Call(f0, input); |
| b.Return(main); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %U:ptr<uniform, array<array<array<vec4<i32>, 5>, 5>, 5>, read> = var @binding_point(0, 0) |
| } |
| |
| %f2 = func(%p:ptr<uniform, array<vec4<i32>, 5>, read>):vec4<i32> { |
| $B2: { |
| %4:ptr<uniform, vec4<i32>, read> = access %p, 3u |
| %5:vec4<i32> = load %4 |
| ret %5 |
| } |
| } |
| %f1 = func(%p_1:ptr<uniform, array<array<vec4<i32>, 5>, 5>, read>):vec4<i32> { # %p_1: 'p' |
| $B3: { |
| %8:ptr<uniform, array<vec4<i32>, 5>, read> = access %p_1, 2u |
| %9:vec4<i32> = call %f2, %8 |
| ret %9 |
| } |
| } |
| %f0 = func(%p_2:ptr<uniform, array<array<array<vec4<i32>, 5>, 5>, 5>, read>):vec4<i32> { # %p_2: 'p' |
| $B4: { |
| %12:ptr<uniform, array<array<vec4<i32>, 5>, 5>, read> = access %p_2, 1u |
| %13:vec4<i32> = call %f1, %12 |
| ret %13 |
| } |
| } |
| %main = func():void { |
| $B5: { |
| %15:vec4<i32> = call %f0, %U |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %U:ptr<uniform, array<array<array<vec4<i32>, 5>, 5>, 5>, read> = var @binding_point(0, 0) |
| } |
| |
| %f2 = func(%p_indices:array<u32, 2>):vec4<i32> { |
| $B2: { |
| %4:u32 = access %p_indices, 0u |
| %5:u32 = access %p_indices, 1u |
| %6:ptr<uniform, array<vec4<i32>, 5>, read> = access %U, %4, %5 |
| %7:ptr<uniform, vec4<i32>, read> = access %6, 3u |
| %8:vec4<i32> = load %7 |
| ret %8 |
| } |
| } |
| %f1 = func(%p_indices_1:array<u32, 1>):vec4<i32> { # %p_indices_1: 'p_indices' |
| $B3: { |
| %11:u32 = access %p_indices_1, 0u |
| %12:array<u32, 2> = construct %11, 2u |
| %13:vec4<i32> = call %f2, %12 |
| ret %13 |
| } |
| } |
| %f0 = func():vec4<i32> { |
| $B4: { |
| %15:array<u32, 1> = construct 1u |
| %16:vec4<i32> = call %f1, %15 |
| ret %16 |
| } |
| } |
| %main = func():void { |
| $B5: { |
| %18:vec4<i32> = call %f0 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| } // namespace uniform_as_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // 'storage' address space |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace storage_as_tests { |
| |
| using IR_DirectVariableAccessTest_StorageAS = TransformTest; |
| |
| TEST_F(IR_DirectVariableAccessTest_StorageAS, Param_ptr_i32_Via_struct_read) { |
| auto* str_ = ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("i"), ty.i32()}, |
| }); |
| |
| Var* S = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| S = b.Var("S", ty.ptr<storage, read>(str_)); |
| S->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<storage, i32, read>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* access = b.Access(ty.ptr<storage, i32, read>(), S, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| $B1: { # root |
| %S:ptr<storage, str, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<storage, i32, read>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:ptr<storage, i32, read> = access %S, 0u |
| %9:i32 = call %a, 10i, %8, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| $B1: { # root |
| %S:ptr<storage, str, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %post:i32):i32 { |
| $B2: { |
| %5:ptr<storage, i32, read> = access %S, 0u |
| %6:i32 = load %5 |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:i32 = call %a, 10i, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_StorageAS, Param_ptr_arr_i32_Via_struct_write) { |
| auto* str_ = |
| ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("arr"), ty.array<i32, 4>()}, |
| }); |
| |
| Var* S = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| S = b.Var("S", ty.ptr<storage>(str_)); |
| S->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.void_()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<storage, array<i32, 4>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { |
| b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4)); |
| b.Return(fn_a); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* access = b.Access(ty.ptr<storage, array<i32, 4>>(), S, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| arr:array<i32, 4> @offset(0) |
| } |
| |
| $B1: { # root |
| %S:ptr<storage, str, read_write> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<storage, array<i32, 4>, read_write>, %post:i32):void { |
| $B2: { |
| store %p, array<i32, 4>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %7:ptr<storage, array<i32, 4>, read_write> = access %S, 0u |
| %8:void = call %a, 10i, %7, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| str = struct @align(4) { |
| arr:array<i32, 4> @offset(0) |
| } |
| |
| $B1: { # root |
| %S:ptr<storage, str, read_write> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %post:i32):void { |
| $B2: { |
| %5:ptr<storage, array<i32, 4>, read_write> = access %S, 0u |
| store %5, array<i32, 4>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %7:void = call %a, 10i, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_StorageAS, Param_ptr_vec4i32_Via_array_DynamicWrite) { |
| Var* S = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| S = b.Var<storage, array<vec4<i32>, 8>>("S"); |
| S->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.void_()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<storage, vec4<i32>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { |
| b.Store(fn_a_p, b.Splat(ty.vec4<i32>(), 0_i, 4)); |
| b.Return(fn_a); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* I = b.Let("I", 3_i); |
| auto* access = b.Access(ty.ptr<storage, vec4<i32>>(), S, I); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %S:ptr<storage, array<vec4<i32>, 8>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<storage, vec4<i32>, read_write>, %post:i32):void { |
| $B2: { |
| store %p, vec4<i32>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %I:i32 = let 3i |
| %8:ptr<storage, vec4<i32>, read_write> = access %S, %I |
| %9:void = call %a, 10i, %8, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %S:ptr<storage, array<vec4<i32>, 8>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):void { |
| $B2: { |
| %6:u32 = access %p_indices, 0u |
| %7:ptr<storage, vec4<i32>, read_write> = access %S, %6 |
| store %7, vec4<i32>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %I:i32 = let 3i |
| %10:u32 = convert %I |
| %11:array<u32, 1> = construct %10 |
| %12:void = call %a, 10i, %11, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_StorageAS, CallChaining) { |
| auto* Inner = |
| ty.Struct(mod.symbols.New("Inner"), { |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| auto* Outer = |
| ty.Struct(mod.symbols.New("Outer"), { |
| {mod.symbols.Register("arr"), ty.array(Inner, 4)}, |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| Var* S = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| S = b.Var("S", ty.ptr<storage, read>(Outer)); |
| S->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_0 = b.Function("f0", ty.f32()); |
| auto* fn_0_p = b.FunctionParam("p", ty.ptr<storage, vec4<f32>, read>()); |
| fn_0->SetParams({fn_0_p}); |
| b.Append(fn_0->Block(), [&] { b.Return(fn_0, b.LoadVectorElement(fn_0_p, 0_u)); }); |
| |
| auto* fn_1 = b.Function("f1", ty.f32()); |
| auto* fn_1_p = b.FunctionParam("p", ty.ptr<storage, mat3x4<f32>, read>()); |
| fn_1->SetParams({fn_1_p}); |
| b.Append(fn_1->Block(), [&] { |
| auto* res = b.Var<function, f32>("res"); |
| { |
| // res += f0(&(*p)[1]); |
| auto* call_0 = b.Call(fn_0, b.Access(ty.ptr<storage, vec4<f32>, read>(), fn_1_p, 1_i)); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &(*p)[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<storage, vec4<f32>, read>(), fn_1_p, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // res += f0(&U.arr[2].mat[1]); |
| auto* access = b.Access(ty.ptr<storage, vec4<f32>, read>(), S, 0_u, 2_i, 0_u, 1_i); |
| auto* call_0 = b.Call(fn_0, access); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &U.arr[2].mat[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<storage, vec4<f32>, read>(), S, 0_u, 2_i, 0_u, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| |
| b.Return(fn_1, b.Load(res)); |
| }); |
| |
| auto* fn_2 = b.Function("f2", ty.f32()); |
| auto* fn_2_p = b.FunctionParam("p", ty.ptr<storage, read>(Inner)); |
| fn_2->SetParams({fn_2_p}); |
| b.Append(fn_2->Block(), [&] { |
| auto* p_mat = b.Access(ty.ptr<storage, mat3x4<f32>, read>(), fn_2_p, 0_u); |
| b.ir.SetName(p_mat, "p_mat"); |
| b.Return(fn_2, b.Call(fn_1, p_mat)); |
| }); |
| |
| auto* fn_3 = b.Function("f3", ty.f32()); |
| auto* fn_3_p0 = b.FunctionParam("p0", ty.ptr<storage, read>(ty.array(Inner, 4))); |
| auto* fn_3_p1 = b.FunctionParam("p1", ty.ptr<storage, mat3x4<f32>, read>()); |
| fn_3->SetParams({fn_3_p0, fn_3_p1}); |
| b.Append(fn_3->Block(), [&] { |
| auto* p0_inner = b.Access(ty.ptr<storage, read>(Inner), fn_3_p0, 3_i); |
| b.ir.SetName(p0_inner, "p0_inner"); |
| auto* call_0 = b.Call(ty.f32(), fn_2, p0_inner); |
| auto* call_1 = b.Call(ty.f32(), fn_1, fn_3_p1); |
| b.Return(fn_3, b.Add(ty.f32(), call_0, call_1)); |
| }); |
| |
| auto* fn_4 = b.Function("f4", ty.f32()); |
| auto* fn_4_p = b.FunctionParam("p", ty.ptr<storage, read>(Outer)); |
| fn_4->SetParams({fn_4_p}); |
| b.Append(fn_4->Block(), [&] { |
| auto* access_0 = b.Access(ty.ptr<storage, read>(ty.array(Inner, 4)), fn_4_p, 0_u); |
| auto* access_1 = b.Access(ty.ptr<storage, mat3x4<f32>, read>(), S, 1_u); |
| b.Return(fn_4, b.Call(ty.f32(), fn_3, access_0, access_1)); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| b.Call(ty.f32(), fn_4, S); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| $B1: { # root |
| %S:ptr<storage, Outer, read> = var @binding_point(0, 0) |
| } |
| |
| %f0 = func(%p:ptr<storage, vec4<f32>, read>):f32 { |
| $B2: { |
| %4:f32 = load_vector_element %p, 0u |
| ret %4 |
| } |
| } |
| %f1 = func(%p_1:ptr<storage, mat3x4<f32>, read>):f32 { # %p_1: 'p' |
| $B3: { |
| %res:ptr<function, f32, read_write> = var |
| %8:ptr<storage, vec4<f32>, read> = access %p_1, 1i |
| %9:f32 = call %f0, %8 |
| %10:f32 = load %res |
| %11:f32 = add %10, %9 |
| store %res, %11 |
| %p_vec:ptr<storage, vec4<f32>, read> = access %p_1, 1i |
| %13:f32 = call %f0, %p_vec |
| %14:f32 = load %res |
| %15:f32 = add %14, %13 |
| store %res, %15 |
| %16:ptr<storage, vec4<f32>, read> = access %S, 0u, 2i, 0u, 1i |
| %17:f32 = call %f0, %16 |
| %18:f32 = load %res |
| %19:f32 = add %18, %17 |
| store %res, %19 |
| %p_vec_1:ptr<storage, vec4<f32>, read> = access %S, 0u, 2i, 0u, 1i # %p_vec_1: 'p_vec' |
| %21:f32 = call %f0, %p_vec_1 |
| %22:f32 = load %res |
| %23:f32 = add %22, %21 |
| store %res, %23 |
| %24:f32 = load %res |
| ret %24 |
| } |
| } |
| %f2 = func(%p_2:ptr<storage, Inner, read>):f32 { # %p_2: 'p' |
| $B4: { |
| %p_mat:ptr<storage, mat3x4<f32>, read> = access %p_2, 0u |
| %28:f32 = call %f1, %p_mat |
| ret %28 |
| } |
| } |
| %f3 = func(%p0:ptr<storage, array<Inner, 4>, read>, %p1:ptr<storage, mat3x4<f32>, read>):f32 { |
| $B5: { |
| %p0_inner:ptr<storage, Inner, read> = access %p0, 3i |
| %33:f32 = call %f2, %p0_inner |
| %34:f32 = call %f1, %p1 |
| %35:f32 = add %33, %34 |
| ret %35 |
| } |
| } |
| %f4 = func(%p_3:ptr<storage, Outer, read>):f32 { # %p_3: 'p' |
| $B6: { |
| %38:ptr<storage, array<Inner, 4>, read> = access %p_3, 0u |
| %39:ptr<storage, mat3x4<f32>, read> = access %S, 1u |
| %40:f32 = call %f3, %38, %39 |
| ret %40 |
| } |
| } |
| %b = func():void { |
| $B7: { |
| %42:f32 = call %f4, %S |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| $B1: { # root |
| %S:ptr<storage, Outer, read> = var @binding_point(0, 0) |
| } |
| |
| %f0 = func(%p_indices:array<u32, 1>):f32 { |
| $B2: { |
| %4:u32 = access %p_indices, 0u |
| %5:ptr<storage, vec4<f32>, read> = access %S, 1u, %4 |
| %6:f32 = load_vector_element %5, 0u |
| ret %6 |
| } |
| } |
| %f0_1 = func(%p_indices_1:array<u32, 2>):f32 { # %f0_1: 'f0', %p_indices_1: 'p_indices' |
| $B3: { |
| %9:u32 = access %p_indices_1, 0u |
| %10:u32 = access %p_indices_1, 1u |
| %11:ptr<storage, vec4<f32>, read> = access %S, 0u, %9, 0u, %10 |
| %12:f32 = load_vector_element %11, 0u |
| ret %12 |
| } |
| } |
| %f1 = func():f32 { |
| $B4: { |
| %res:ptr<function, f32, read_write> = var |
| %15:u32 = convert 1i |
| %16:array<u32, 1> = construct %15 |
| %17:f32 = call %f0, %16 |
| %18:f32 = load %res |
| %19:f32 = add %18, %17 |
| store %res, %19 |
| %20:u32 = convert 1i |
| %21:array<u32, 1> = construct %20 |
| %22:f32 = call %f0, %21 |
| %23:f32 = load %res |
| %24:f32 = add %23, %22 |
| store %res, %24 |
| %25:u32 = convert 2i |
| %26:u32 = convert 1i |
| %27:array<u32, 2> = construct %25, %26 |
| %28:f32 = call %f0_1, %27 |
| %29:f32 = load %res |
| %30:f32 = add %29, %28 |
| store %res, %30 |
| %31:u32 = convert 2i |
| %32:u32 = convert 1i |
| %33:array<u32, 2> = construct %31, %32 |
| %34:f32 = call %f0_1, %33 |
| %35:f32 = load %res |
| %36:f32 = add %35, %34 |
| store %res, %36 |
| %37:f32 = load %res |
| ret %37 |
| } |
| } |
| %f1_1 = func(%p_indices_2:array<u32, 1>):f32 { # %f1_1: 'f1', %p_indices_2: 'p_indices' |
| $B5: { |
| %40:u32 = access %p_indices_2, 0u |
| %res_1:ptr<function, f32, read_write> = var # %res_1: 'res' |
| %42:u32 = convert 1i |
| %43:array<u32, 2> = construct %40, %42 |
| %44:f32 = call %f0_1, %43 |
| %45:f32 = load %res_1 |
| %46:f32 = add %45, %44 |
| store %res_1, %46 |
| %47:u32 = convert 1i |
| %48:array<u32, 2> = construct %40, %47 |
| %49:f32 = call %f0_1, %48 |
| %50:f32 = load %res_1 |
| %51:f32 = add %50, %49 |
| store %res_1, %51 |
| %52:u32 = convert 2i |
| %53:u32 = convert 1i |
| %54:array<u32, 2> = construct %52, %53 |
| %55:f32 = call %f0_1, %54 |
| %56:f32 = load %res_1 |
| %57:f32 = add %56, %55 |
| store %res_1, %57 |
| %58:u32 = convert 2i |
| %59:u32 = convert 1i |
| %60:array<u32, 2> = construct %58, %59 |
| %61:f32 = call %f0_1, %60 |
| %62:f32 = load %res_1 |
| %63:f32 = add %62, %61 |
| store %res_1, %63 |
| %64:f32 = load %res_1 |
| ret %64 |
| } |
| } |
| %f2 = func(%p_indices_3:array<u32, 1>):f32 { # %p_indices_3: 'p_indices' |
| $B6: { |
| %67:u32 = access %p_indices_3, 0u |
| %68:array<u32, 1> = construct %67 |
| %69:f32 = call %f1_1, %68 |
| ret %69 |
| } |
| } |
| %f3 = func():f32 { |
| $B7: { |
| %71:u32 = convert 3i |
| %72:array<u32, 1> = construct %71 |
| %73:f32 = call %f2, %72 |
| %74:f32 = call %f1 |
| %75:f32 = add %73, %74 |
| ret %75 |
| } |
| } |
| %f4 = func():f32 { |
| $B8: { |
| %77:f32 = call %f3 |
| ret %77 |
| } |
| } |
| %b = func():void { |
| $B9: { |
| %79:f32 = call %f4 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_StorageAS, CallChaining2) { |
| auto* T3 = ty.vec4<i32>(); |
| auto* T2 = ty.array(T3, 5); |
| auto* T1 = ty.array(T2, 5); |
| auto* T = ty.array(T1, 5); |
| |
| Var* input = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| input = b.Var("U", ty.ptr<storage>(T)); |
| input->SetBindingPoint(0, 0); |
| }); |
| |
| auto* f2 = b.Function("f2", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<storage>(T2)); |
| f2->SetParams({p}); |
| b.Append(f2->Block(), |
| [&] { b.Return(f2, b.Load(b.Access<ptr<storage, vec4<i32>>>(p, 3_u))); }); |
| } |
| |
| auto* f1 = b.Function("f1", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<storage>(T1)); |
| f1->SetParams({p}); |
| b.Append(f1->Block(), |
| [&] { b.Return(f1, b.Call(f2, b.Access(ty.ptr<storage>(T2), p, 2_u))); }); |
| } |
| |
| auto* f0 = b.Function("f0", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<storage>(T)); |
| f0->SetParams({p}); |
| b.Append(f0->Block(), |
| [&] { b.Return(f0, b.Call(f1, b.Access(ty.ptr<storage>(T1), p, 1_u))); }); |
| } |
| |
| auto* main = b.Function("main", ty.void_()); |
| b.Append(main->Block(), [&] { |
| b.Call(f0, input); |
| b.Return(main); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %U:ptr<storage, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %f2 = func(%p:ptr<storage, array<vec4<i32>, 5>, read_write>):vec4<i32> { |
| $B2: { |
| %4:ptr<storage, vec4<i32>, read_write> = access %p, 3u |
| %5:vec4<i32> = load %4 |
| ret %5 |
| } |
| } |
| %f1 = func(%p_1:ptr<storage, array<array<vec4<i32>, 5>, 5>, read_write>):vec4<i32> { # %p_1: 'p' |
| $B3: { |
| %8:ptr<storage, array<vec4<i32>, 5>, read_write> = access %p_1, 2u |
| %9:vec4<i32> = call %f2, %8 |
| ret %9 |
| } |
| } |
| %f0 = func(%p_2:ptr<storage, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>):vec4<i32> { # %p_2: 'p' |
| $B4: { |
| %12:ptr<storage, array<array<vec4<i32>, 5>, 5>, read_write> = access %p_2, 1u |
| %13:vec4<i32> = call %f1, %12 |
| ret %13 |
| } |
| } |
| %main = func():void { |
| $B5: { |
| %15:vec4<i32> = call %f0, %U |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %U:ptr<storage, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %f2 = func(%p_indices:array<u32, 2>):vec4<i32> { |
| $B2: { |
| %4:u32 = access %p_indices, 0u |
| %5:u32 = access %p_indices, 1u |
| %6:ptr<storage, array<vec4<i32>, 5>, read_write> = access %U, %4, %5 |
| %7:ptr<storage, vec4<i32>, read_write> = access %6, 3u |
| %8:vec4<i32> = load %7 |
| ret %8 |
| } |
| } |
| %f1 = func(%p_indices_1:array<u32, 1>):vec4<i32> { # %p_indices_1: 'p_indices' |
| $B3: { |
| %11:u32 = access %p_indices_1, 0u |
| %12:array<u32, 2> = construct %11, 2u |
| %13:vec4<i32> = call %f2, %12 |
| ret %13 |
| } |
| } |
| %f0 = func():vec4<i32> { |
| $B4: { |
| %15:array<u32, 1> = construct 1u |
| %16:vec4<i32> = call %f1, %15 |
| ret %16 |
| } |
| } |
| %main = func():void { |
| $B5: { |
| %18:vec4<i32> = call %f0 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| } // namespace storage_as_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // 'workgroup' address space |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace workgroup_as_tests { |
| |
| using IR_DirectVariableAccessTest_WorkgroupAS = TransformTest; |
| |
| TEST_F(IR_DirectVariableAccessTest_WorkgroupAS, Param_ptr_vec4i32_Via_array_StaticRead) { |
| Var* W = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| W = b.Var("W", ty.ptr<workgroup, array<vec4<i32>, 8>>()); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.vec4<i32>()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<workgroup, vec4<i32>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* access = b.Access(ty.ptr<workgroup, vec4<i32>>(), W, 3_i); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %W:ptr<workgroup, array<vec4<i32>, 8>, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<workgroup, vec4<i32>, read_write>, %post:i32):vec4<i32> { |
| $B2: { |
| %6:vec4<i32> = load %p |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:ptr<workgroup, vec4<i32>, read_write> = access %W, 3i |
| %9:vec4<i32> = call %a, 10i, %8, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %W:ptr<workgroup, array<vec4<i32>, 8>, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):vec4<i32> { |
| $B2: { |
| %6:u32 = access %p_indices, 0u |
| %7:ptr<workgroup, vec4<i32>, read_write> = access %W, %6 |
| %8:vec4<i32> = load %7 |
| ret %8 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %10:u32 = convert 3i |
| %11:array<u32, 1> = construct %10 |
| %12:vec4<i32> = call %a, 10i, %11, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_WorkgroupAS, Param_ptr_vec4i32_Via_array_StaticWrite) { |
| Var* W = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| W = b.Var<workgroup, array<vec4<i32>, 8>>("W"); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.void_()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<workgroup, vec4<i32>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { |
| b.Store(fn_a_p, b.Splat(ty.vec4<i32>(), 0_i, 4)); |
| b.Return(fn_a); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* access = b.Access(ty.ptr<workgroup, vec4<i32>>(), W, 3_i); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %W:ptr<workgroup, array<vec4<i32>, 8>, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<workgroup, vec4<i32>, read_write>, %post:i32):void { |
| $B2: { |
| store %p, vec4<i32>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %7:ptr<workgroup, vec4<i32>, read_write> = access %W, 3i |
| %8:void = call %a, 10i, %7, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %W:ptr<workgroup, array<vec4<i32>, 8>, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p_indices:array<u32, 1>, %post:i32):void { |
| $B2: { |
| %6:u32 = access %p_indices, 0u |
| %7:ptr<workgroup, vec4<i32>, read_write> = access %W, %6 |
| store %7, vec4<i32>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %9:u32 = convert 3i |
| %10:array<u32, 1> = construct %9 |
| %11:void = call %a, 10i, %10, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_WorkgroupAS, CallChaining) { |
| auto* Inner = |
| ty.Struct(mod.symbols.New("Inner"), { |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| auto* Outer = |
| ty.Struct(mod.symbols.New("Outer"), { |
| {mod.symbols.Register("arr"), ty.array(Inner, 4)}, |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| Var* W = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| W = b.Var("W", ty.ptr<workgroup>(Outer)); |
| }); |
| |
| auto* fn_0 = b.Function("f0", ty.f32()); |
| auto* fn_0_p = b.FunctionParam("p", ty.ptr<workgroup, vec4<f32>>()); |
| fn_0->SetParams({fn_0_p}); |
| b.Append(fn_0->Block(), [&] { b.Return(fn_0, b.LoadVectorElement(fn_0_p, 0_u)); }); |
| |
| auto* fn_1 = b.Function("f1", ty.f32()); |
| auto* fn_1_p = b.FunctionParam("p", ty.ptr<workgroup, mat3x4<f32>>()); |
| fn_1->SetParams({fn_1_p}); |
| b.Append(fn_1->Block(), [&] { |
| auto* res = b.Var<function, f32>("res"); |
| { |
| // res += f0(&(*p)[1]); |
| auto* call_0 = b.Call(fn_0, b.Access(ty.ptr<workgroup, vec4<f32>>(), fn_1_p, 1_i)); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &(*p)[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<workgroup, vec4<f32>>(), fn_1_p, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // res += f0(&U.arr[2].mat[1]); |
| auto* access = b.Access(ty.ptr<workgroup, vec4<f32>>(), W, 0_u, 2_i, 0_u, 1_i); |
| auto* call_0 = b.Call(fn_0, access); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &U.arr[2].mat[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<workgroup, vec4<f32>>(), W, 0_u, 2_i, 0_u, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| |
| b.Return(fn_1, b.Load(res)); |
| }); |
| |
| auto* fn_2 = b.Function("f2", ty.f32()); |
| auto* fn_2_p = b.FunctionParam("p", ty.ptr<workgroup>(Inner)); |
| fn_2->SetParams({fn_2_p}); |
| b.Append(fn_2->Block(), [&] { |
| auto* p_mat = b.Access(ty.ptr<workgroup, mat3x4<f32>>(), fn_2_p, 0_u); |
| b.ir.SetName(p_mat, "p_mat"); |
| b.Return(fn_2, b.Call(fn_1, p_mat)); |
| }); |
| |
| auto* fn_3 = b.Function("f3", ty.f32()); |
| auto* fn_3_p0 = b.FunctionParam("p0", ty.ptr<workgroup>(ty.array(Inner, 4))); |
| auto* fn_3_p1 = b.FunctionParam("p1", ty.ptr<workgroup, mat3x4<f32>>()); |
| fn_3->SetParams({fn_3_p0, fn_3_p1}); |
| b.Append(fn_3->Block(), [&] { |
| auto* p0_inner = b.Access(ty.ptr<workgroup>(Inner), fn_3_p0, 3_i); |
| b.ir.SetName(p0_inner, "p0_inner"); |
| auto* call_0 = b.Call(ty.f32(), fn_2, p0_inner); |
| auto* call_1 = b.Call(ty.f32(), fn_1, fn_3_p1); |
| b.Return(fn_3, b.Add(ty.f32(), call_0, call_1)); |
| }); |
| |
| auto* fn_4 = b.Function("f4", ty.f32()); |
| auto* fn_4_p = b.FunctionParam("p", ty.ptr<workgroup>(Outer)); |
| fn_4->SetParams({fn_4_p}); |
| b.Append(fn_4->Block(), [&] { |
| auto* access_0 = b.Access(ty.ptr<workgroup>(ty.array(Inner, 4)), fn_4_p, 0_u); |
| auto* access_1 = b.Access(ty.ptr<workgroup, mat3x4<f32>>(), W, 1_u); |
| b.Return(fn_4, b.Call(ty.f32(), fn_3, access_0, access_1)); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| b.Call(ty.f32(), fn_4, W); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| $B1: { # root |
| %W:ptr<workgroup, Outer, read_write> = var |
| } |
| |
| %f0 = func(%p:ptr<workgroup, vec4<f32>, read_write>):f32 { |
| $B2: { |
| %4:f32 = load_vector_element %p, 0u |
| ret %4 |
| } |
| } |
| %f1 = func(%p_1:ptr<workgroup, mat3x4<f32>, read_write>):f32 { # %p_1: 'p' |
| $B3: { |
| %res:ptr<function, f32, read_write> = var |
| %8:ptr<workgroup, vec4<f32>, read_write> = access %p_1, 1i |
| %9:f32 = call %f0, %8 |
| %10:f32 = load %res |
| %11:f32 = add %10, %9 |
| store %res, %11 |
| %p_vec:ptr<workgroup, vec4<f32>, read_write> = access %p_1, 1i |
| %13:f32 = call %f0, %p_vec |
| %14:f32 = load %res |
| %15:f32 = add %14, %13 |
| store %res, %15 |
| %16:ptr<workgroup, vec4<f32>, read_write> = access %W, 0u, 2i, 0u, 1i |
| %17:f32 = call %f0, %16 |
| %18:f32 = load %res |
| %19:f32 = add %18, %17 |
| store %res, %19 |
| %p_vec_1:ptr<workgroup, vec4<f32>, read_write> = access %W, 0u, 2i, 0u, 1i # %p_vec_1: 'p_vec' |
| %21:f32 = call %f0, %p_vec_1 |
| %22:f32 = load %res |
| %23:f32 = add %22, %21 |
| store %res, %23 |
| %24:f32 = load %res |
| ret %24 |
| } |
| } |
| %f2 = func(%p_2:ptr<workgroup, Inner, read_write>):f32 { # %p_2: 'p' |
| $B4: { |
| %p_mat:ptr<workgroup, mat3x4<f32>, read_write> = access %p_2, 0u |
| %28:f32 = call %f1, %p_mat |
| ret %28 |
| } |
| } |
| %f3 = func(%p0:ptr<workgroup, array<Inner, 4>, read_write>, %p1:ptr<workgroup, mat3x4<f32>, read_write>):f32 { |
| $B5: { |
| %p0_inner:ptr<workgroup, Inner, read_write> = access %p0, 3i |
| %33:f32 = call %f2, %p0_inner |
| %34:f32 = call %f1, %p1 |
| %35:f32 = add %33, %34 |
| ret %35 |
| } |
| } |
| %f4 = func(%p_3:ptr<workgroup, Outer, read_write>):f32 { # %p_3: 'p' |
| $B6: { |
| %38:ptr<workgroup, array<Inner, 4>, read_write> = access %p_3, 0u |
| %39:ptr<workgroup, mat3x4<f32>, read_write> = access %W, 1u |
| %40:f32 = call %f3, %38, %39 |
| ret %40 |
| } |
| } |
| %b = func():void { |
| $B7: { |
| %42:f32 = call %f4, %W |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| $B1: { # root |
| %W:ptr<workgroup, Outer, read_write> = var |
| } |
| |
| %f0 = func(%p_indices:array<u32, 1>):f32 { |
| $B2: { |
| %4:u32 = access %p_indices, 0u |
| %5:ptr<workgroup, vec4<f32>, read_write> = access %W, 1u, %4 |
| %6:f32 = load_vector_element %5, 0u |
| ret %6 |
| } |
| } |
| %f0_1 = func(%p_indices_1:array<u32, 2>):f32 { # %f0_1: 'f0', %p_indices_1: 'p_indices' |
| $B3: { |
| %9:u32 = access %p_indices_1, 0u |
| %10:u32 = access %p_indices_1, 1u |
| %11:ptr<workgroup, vec4<f32>, read_write> = access %W, 0u, %9, 0u, %10 |
| %12:f32 = load_vector_element %11, 0u |
| ret %12 |
| } |
| } |
| %f1 = func():f32 { |
| $B4: { |
| %res:ptr<function, f32, read_write> = var |
| %15:u32 = convert 1i |
| %16:array<u32, 1> = construct %15 |
| %17:f32 = call %f0, %16 |
| %18:f32 = load %res |
| %19:f32 = add %18, %17 |
| store %res, %19 |
| %20:u32 = convert 1i |
| %21:array<u32, 1> = construct %20 |
| %22:f32 = call %f0, %21 |
| %23:f32 = load %res |
| %24:f32 = add %23, %22 |
| store %res, %24 |
| %25:u32 = convert 2i |
| %26:u32 = convert 1i |
| %27:array<u32, 2> = construct %25, %26 |
| %28:f32 = call %f0_1, %27 |
| %29:f32 = load %res |
| %30:f32 = add %29, %28 |
| store %res, %30 |
| %31:u32 = convert 2i |
| %32:u32 = convert 1i |
| %33:array<u32, 2> = construct %31, %32 |
| %34:f32 = call %f0_1, %33 |
| %35:f32 = load %res |
| %36:f32 = add %35, %34 |
| store %res, %36 |
| %37:f32 = load %res |
| ret %37 |
| } |
| } |
| %f1_1 = func(%p_indices_2:array<u32, 1>):f32 { # %f1_1: 'f1', %p_indices_2: 'p_indices' |
| $B5: { |
| %40:u32 = access %p_indices_2, 0u |
| %res_1:ptr<function, f32, read_write> = var # %res_1: 'res' |
| %42:u32 = convert 1i |
| %43:array<u32, 2> = construct %40, %42 |
| %44:f32 = call %f0_1, %43 |
| %45:f32 = load %res_1 |
| %46:f32 = add %45, %44 |
| store %res_1, %46 |
| %47:u32 = convert 1i |
| %48:array<u32, 2> = construct %40, %47 |
| %49:f32 = call %f0_1, %48 |
| %50:f32 = load %res_1 |
| %51:f32 = add %50, %49 |
| store %res_1, %51 |
| %52:u32 = convert 2i |
| %53:u32 = convert 1i |
| %54:array<u32, 2> = construct %52, %53 |
| %55:f32 = call %f0_1, %54 |
| %56:f32 = load %res_1 |
| %57:f32 = add %56, %55 |
| store %res_1, %57 |
| %58:u32 = convert 2i |
| %59:u32 = convert 1i |
| %60:array<u32, 2> = construct %58, %59 |
| %61:f32 = call %f0_1, %60 |
| %62:f32 = load %res_1 |
| %63:f32 = add %62, %61 |
| store %res_1, %63 |
| %64:f32 = load %res_1 |
| ret %64 |
| } |
| } |
| %f2 = func(%p_indices_3:array<u32, 1>):f32 { # %p_indices_3: 'p_indices' |
| $B6: { |
| %67:u32 = access %p_indices_3, 0u |
| %68:array<u32, 1> = construct %67 |
| %69:f32 = call %f1_1, %68 |
| ret %69 |
| } |
| } |
| %f3 = func():f32 { |
| $B7: { |
| %71:u32 = convert 3i |
| %72:array<u32, 1> = construct %71 |
| %73:f32 = call %f2, %72 |
| %74:f32 = call %f1 |
| %75:f32 = add %73, %74 |
| ret %75 |
| } |
| } |
| %f4 = func():f32 { |
| $B8: { |
| %77:f32 = call %f3 |
| ret %77 |
| } |
| } |
| %b = func():void { |
| $B9: { |
| %79:f32 = call %f4 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_WorkgroupAS, CallChaining2) { |
| auto* T3 = ty.vec4<i32>(); |
| auto* T2 = ty.array(T3, 5); |
| auto* T1 = ty.array(T2, 5); |
| auto* T = ty.array(T1, 5); |
| |
| Var* input = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| input = b.Var("U", ty.ptr<workgroup>(T)); |
| input->SetBindingPoint(0, 0); |
| }); |
| |
| auto* f2 = b.Function("f2", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<workgroup>(T2)); |
| f2->SetParams({p}); |
| b.Append(f2->Block(), |
| [&] { b.Return(f2, b.Load(b.Access<ptr<workgroup, vec4<i32>>>(p, 3_u))); }); |
| } |
| |
| auto* f1 = b.Function("f1", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<workgroup>(T1)); |
| f1->SetParams({p}); |
| b.Append(f1->Block(), |
| [&] { b.Return(f1, b.Call(f2, b.Access(ty.ptr<workgroup>(T2), p, 2_u))); }); |
| } |
| |
| auto* f0 = b.Function("f0", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<workgroup>(T)); |
| f0->SetParams({p}); |
| b.Append(f0->Block(), |
| [&] { b.Return(f0, b.Call(f1, b.Access(ty.ptr<workgroup>(T1), p, 1_u))); }); |
| } |
| |
| auto* main = b.Function("main", ty.void_()); |
| b.Append(main->Block(), [&] { |
| b.Call(f0, input); |
| b.Return(main); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %U:ptr<workgroup, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %f2 = func(%p:ptr<workgroup, array<vec4<i32>, 5>, read_write>):vec4<i32> { |
| $B2: { |
| %4:ptr<workgroup, vec4<i32>, read_write> = access %p, 3u |
| %5:vec4<i32> = load %4 |
| ret %5 |
| } |
| } |
| %f1 = func(%p_1:ptr<workgroup, array<array<vec4<i32>, 5>, 5>, read_write>):vec4<i32> { # %p_1: 'p' |
| $B3: { |
| %8:ptr<workgroup, array<vec4<i32>, 5>, read_write> = access %p_1, 2u |
| %9:vec4<i32> = call %f2, %8 |
| ret %9 |
| } |
| } |
| %f0 = func(%p_2:ptr<workgroup, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>):vec4<i32> { # %p_2: 'p' |
| $B4: { |
| %12:ptr<workgroup, array<array<vec4<i32>, 5>, 5>, read_write> = access %p_2, 1u |
| %13:vec4<i32> = call %f1, %12 |
| ret %13 |
| } |
| } |
| %main = func():void { |
| $B5: { |
| %15:vec4<i32> = call %f0, %U |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %U:ptr<workgroup, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %f2 = func(%p_indices:array<u32, 2>):vec4<i32> { |
| $B2: { |
| %4:u32 = access %p_indices, 0u |
| %5:u32 = access %p_indices, 1u |
| %6:ptr<workgroup, array<vec4<i32>, 5>, read_write> = access %U, %4, %5 |
| %7:ptr<workgroup, vec4<i32>, read_write> = access %6, 3u |
| %8:vec4<i32> = load %7 |
| ret %8 |
| } |
| } |
| %f1 = func(%p_indices_1:array<u32, 1>):vec4<i32> { # %p_indices_1: 'p_indices' |
| $B3: { |
| %11:u32 = access %p_indices_1, 0u |
| %12:array<u32, 2> = construct %11, 2u |
| %13:vec4<i32> = call %f2, %12 |
| ret %13 |
| } |
| } |
| %f0 = func():vec4<i32> { |
| $B4: { |
| %15:array<u32, 1> = construct 1u |
| %16:vec4<i32> = call %f1, %15 |
| ret %16 |
| } |
| } |
| %main = func():void { |
| $B5: { |
| %18:vec4<i32> = call %f0 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| } // namespace workgroup_as_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // 'private' address space |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace private_as_tests { |
| |
| using IR_DirectVariableAccessTest_PrivateAS = TransformTest; |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Enabled_Param_ptr_i32_read) { |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_, i32>()); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<private_, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| b.Call(fn_a, 10_i, P, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %P:ptr<private, i32, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<private, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:i32 = call %a, 10i, %P, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %P:ptr<private, i32, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %post:i32):i32 { |
| $B2: { |
| %5:ptr<private, i32, read_write> = access %P |
| %6:i32 = load %5 |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:i32 = call %a, 10i, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformPrivate); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Enabled_Param_ptr_i32_write) { |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_, i32>()); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.void_()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<private_, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { |
| b.Store(fn_a_p, 42_i); |
| b.Return(fn_a); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| b.Call(fn_a, 10_i, P, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %P:ptr<private, i32, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<private, i32, read_write>, %post:i32):void { |
| $B2: { |
| store %p, 42i |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %7:void = call %a, 10i, %P, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %P:ptr<private, i32, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %post:i32):void { |
| $B2: { |
| %5:ptr<private, i32, read_write> = access %P |
| store %5, 42i |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %7:void = call %a, 10i, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformPrivate); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Enabled_Param_ptr_i32_Via_struct_read) { |
| auto* str_ = ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("i"), ty.i32()}, |
| }); |
| |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_>(str_)); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<private_, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* access = b.Access(ty.ptr<private_, i32>(), P, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| $B1: { # root |
| %P:ptr<private, str, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<private, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:ptr<private, i32, read_write> = access %P, 0u |
| %9:i32 = call %a, 10i, %8, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| $B1: { # root |
| %P:ptr<private, str, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %post:i32):i32 { |
| $B2: { |
| %5:ptr<private, i32, read_write> = access %P, 0u |
| %6:i32 = load %5 |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:i32 = call %a, 10i, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformPrivate); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Disabled_Param_ptr_i32_Via_struct_read) { |
| auto* str_ = ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("i"), ty.i32()}, |
| }); |
| |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_>(str_)); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<private_, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* access = b.Access(ty.ptr<private_, i32>(), P, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| $B1: { # root |
| %P:ptr<private, str, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<private, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %p |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %8:ptr<private, i32, read_write> = access %P, 0u |
| %9:i32 = call %a, 10i, %8, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Enabled_Param_ptr_arr_i32_Via_struct_write) { |
| auto* str_ = |
| ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("arr"), ty.array<i32, 4>()}, |
| }); |
| |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_>(str_)); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.void_()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<private_, array<i32, 4>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { |
| b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4)); |
| b.Return(fn_a); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* access = b.Access(ty.ptr<private_, array<i32, 4>>(), P, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| arr:array<i32, 4> @offset(0) |
| } |
| |
| $B1: { # root |
| %P:ptr<private, str, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<private, array<i32, 4>, read_write>, %post:i32):void { |
| $B2: { |
| store %p, array<i32, 4>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %7:ptr<private, array<i32, 4>, read_write> = access %P, 0u |
| %8:void = call %a, 10i, %7, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| str = struct @align(4) { |
| arr:array<i32, 4> @offset(0) |
| } |
| |
| $B1: { # root |
| %P:ptr<private, str, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %post:i32):void { |
| $B2: { |
| %5:ptr<private, array<i32, 4>, read_write> = access %P, 0u |
| store %5, array<i32, 4>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %7:void = call %a, 10i, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformPrivate); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Disabled_Param_ptr_arr_i32_Via_struct_write) { |
| auto* str_ = |
| ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("arr"), ty.array<i32, 4>()}, |
| }); |
| |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_>(str_)); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.void_()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<private_, array<i32, 4>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { |
| b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4)); |
| b.Return(fn_a); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* access = b.Access(ty.ptr<private_, array<i32, 4>>(), P, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| arr:array<i32, 4> @offset(0) |
| } |
| |
| $B1: { # root |
| %P:ptr<private, str, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<private, array<i32, 4>, read_write>, %post:i32):void { |
| $B2: { |
| store %p, array<i32, 4>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %7:ptr<private, array<i32, 4>, read_write> = access %P, 0u |
| %8:void = call %a, 10i, %7, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Enabled_Param_ptr_i32_mixed) { |
| auto* str_ = ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("i"), ty.i32()}, |
| }); |
| |
| Var* Pi = nullptr; |
| Var* Ps = nullptr; |
| Var* Pa = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| Pi = b.Var("Pi", ty.ptr<private_, i32>()); |
| Ps = b.Var("Ps", ty.ptr<private_>(str_)); |
| Pa = b.Var("Pa", ty.ptr<private_, array<i32, 4>>()); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<private_, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| { // a(10, &Pi, 20); |
| b.Call(fn_a, 10_i, Pi, 20_i); |
| } |
| { // a(30, &Ps.i, 40); |
| auto* access = b.Access(ty.ptr<private_, i32>(), Ps, 0_u); |
| b.Call(fn_a, 30_i, access, 40_i); |
| } |
| { // a(50, &Pa[2], 60); |
| auto* access = b.Access(ty.ptr<private_, i32>(), Pa, 2_i); |
| b.Call(fn_a, 50_i, access, 60_i); |
| } |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| $B1: { # root |
| %Pi:ptr<private, i32, read_write> = var |
| %Ps:ptr<private, str, read_write> = var |
| %Pa:ptr<private, array<i32, 4>, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<private, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %8:i32 = load %p |
| ret %8 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %10:i32 = call %a, 10i, %Pi, 20i |
| %11:ptr<private, i32, read_write> = access %Ps, 0u |
| %12:i32 = call %a, 30i, %11, 40i |
| %13:ptr<private, i32, read_write> = access %Pa, 2i |
| %14:i32 = call %a, 50i, %13, 60i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| $B1: { # root |
| %Pi:ptr<private, i32, read_write> = var |
| %Ps:ptr<private, str, read_write> = var |
| %Pa:ptr<private, array<i32, 4>, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %post:i32):i32 { |
| $B2: { |
| %7:ptr<private, i32, read_write> = access %Pi |
| %8:i32 = load %7 |
| ret %8 |
| } |
| } |
| %a_1 = func(%pre_1:i32, %post_1:i32):i32 { # %a_1: 'a', %pre_1: 'pre', %post_1: 'post' |
| $B3: { |
| %12:ptr<private, i32, read_write> = access %Ps, 0u |
| %13:i32 = load %12 |
| ret %13 |
| } |
| } |
| %a_2 = func(%pre_2:i32, %p_indices:array<u32, 1>, %post_2:i32):i32 { # %a_2: 'a', %pre_2: 'pre', %post_2: 'post' |
| $B4: { |
| %18:u32 = access %p_indices, 0u |
| %19:ptr<private, i32, read_write> = access %Pa, %18 |
| %20:i32 = load %19 |
| ret %20 |
| } |
| } |
| %b = func():void { |
| $B5: { |
| %22:i32 = call %a, 10i, 20i |
| %23:i32 = call %a_1, 30i, 40i |
| %24:u32 = convert 2i |
| %25:array<u32, 1> = construct %24 |
| %26:i32 = call %a_2, 50i, %25, 60i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformPrivate); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Disabled_Param_ptr_i32_mixed) { |
| auto* str_ = ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("i"), ty.i32()}, |
| }); |
| |
| Var* Pi = nullptr; |
| Var* Ps = nullptr; |
| Var* Pa = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| Pi = b.Var("Pi", ty.ptr<private_, i32>()); |
| Ps = b.Var("Ps", ty.ptr<private_>(str_)); |
| Pa = b.Var("Pa", ty.ptr<private_, array<i32, 4>>()); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<private_, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| { // a(10, &Pi, 20); |
| b.Call(fn_a, 10_i, Pi, 20_i); |
| } |
| { // a(30, &Ps.i, 40); |
| auto* access = b.Access(ty.ptr<private_, i32>(), Ps, 0_u); |
| b.Call(fn_a, 30_i, access, 40_i); |
| } |
| { // a(50, &Pa[2], 60); |
| auto* access = b.Access(ty.ptr<private_, i32>(), Pa, 2_i); |
| b.Call(fn_a, 50_i, access, 60_i); |
| } |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| $B1: { # root |
| %Pi:ptr<private, i32, read_write> = var |
| %Ps:ptr<private, str, read_write> = var |
| %Pa:ptr<private, array<i32, 4>, read_write> = var |
| } |
| |
| %a = func(%pre:i32, %p:ptr<private, i32, read_write>, %post:i32):i32 { |
| $B2: { |
| %8:i32 = load %p |
| ret %8 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %10:i32 = call %a, 10i, %Pi, 20i |
| %11:ptr<private, i32, read_write> = access %Ps, 0u |
| %12:i32 = call %a, 30i, %11, 40i |
| %13:ptr<private, i32, read_write> = access %Pa, 2i |
| %14:i32 = call %a, 50i, %13, 60i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Enabled_CallChaining) { |
| auto* Inner = |
| ty.Struct(mod.symbols.New("Inner"), { |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| auto* Outer = |
| ty.Struct(mod.symbols.New("Outer"), { |
| {mod.symbols.Register("arr"), ty.array(Inner, 4)}, |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_>(Outer)); |
| }); |
| |
| auto* fn_0 = b.Function("f0", ty.f32()); |
| auto* fn_0_p = b.FunctionParam("p", ty.ptr<private_, vec4<f32>>()); |
| fn_0->SetParams({fn_0_p}); |
| b.Append(fn_0->Block(), [&] { b.Return(fn_0, b.LoadVectorElement(fn_0_p, 0_u)); }); |
| |
| auto* fn_1 = b.Function("f1", ty.f32()); |
| auto* fn_1_p = b.FunctionParam("p", ty.ptr<private_, mat3x4<f32>>()); |
| fn_1->SetParams({fn_1_p}); |
| b.Append(fn_1->Block(), [&] { |
| auto* res = b.Var<function, f32>("res"); |
| { |
| // res += f0(&(*p)[1]); |
| auto* call_0 = b.Call(fn_0, b.Access(ty.ptr<private_, vec4<f32>>(), fn_1_p, 1_i)); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &(*p)[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<private_, vec4<f32>>(), fn_1_p, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // res += f0(&U.arr[2].mat[1]); |
| auto* access = b.Access(ty.ptr<private_, vec4<f32>>(), P, 0_u, 2_i, 0_u, 1_i); |
| auto* call_0 = b.Call(fn_0, access); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &U.arr[2].mat[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<private_, vec4<f32>>(), P, 0_u, 2_i, 0_u, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| |
| b.Return(fn_1, b.Load(res)); |
| }); |
| |
| auto* fn_2 = b.Function("f2", ty.f32()); |
| auto* fn_2_p = b.FunctionParam("p", ty.ptr<private_>(Inner)); |
| fn_2->SetParams({fn_2_p}); |
| b.Append(fn_2->Block(), [&] { |
| auto* p_mat = b.Access(ty.ptr<private_, mat3x4<f32>>(), fn_2_p, 0_u); |
| b.ir.SetName(p_mat, "p_mat"); |
| b.Return(fn_2, b.Call(fn_1, p_mat)); |
| }); |
| |
| auto* fn_3 = b.Function("f3", ty.f32()); |
| auto* fn_3_p0 = b.FunctionParam("p0", ty.ptr<private_>(ty.array(Inner, 4))); |
| auto* fn_3_p1 = b.FunctionParam("p1", ty.ptr<private_, mat3x4<f32>>()); |
| fn_3->SetParams({fn_3_p0, fn_3_p1}); |
| b.Append(fn_3->Block(), [&] { |
| auto* p0_inner = b.Access(ty.ptr<private_>(Inner), fn_3_p0, 3_i); |
| b.ir.SetName(p0_inner, "p0_inner"); |
| auto* call_0 = b.Call(ty.f32(), fn_2, p0_inner); |
| auto* call_1 = b.Call(ty.f32(), fn_1, fn_3_p1); |
| b.Return(fn_3, b.Add(ty.f32(), call_0, call_1)); |
| }); |
| |
| auto* fn_4 = b.Function("f4", ty.f32()); |
| auto* fn_4_p = b.FunctionParam("p", ty.ptr<private_>(Outer)); |
| fn_4->SetParams({fn_4_p}); |
| b.Append(fn_4->Block(), [&] { |
| auto* access_0 = b.Access(ty.ptr<private_>(ty.array(Inner, 4)), fn_4_p, 0_u); |
| auto* access_1 = b.Access(ty.ptr<private_, mat3x4<f32>>(), P, 1_u); |
| b.Return(fn_4, b.Call(ty.f32(), fn_3, access_0, access_1)); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| b.Call(ty.f32(), fn_4, P); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| $B1: { # root |
| %P:ptr<private, Outer, read_write> = var |
| } |
| |
| %f0 = func(%p:ptr<private, vec4<f32>, read_write>):f32 { |
| $B2: { |
| %4:f32 = load_vector_element %p, 0u |
| ret %4 |
| } |
| } |
| %f1 = func(%p_1:ptr<private, mat3x4<f32>, read_write>):f32 { # %p_1: 'p' |
| $B3: { |
| %res:ptr<function, f32, read_write> = var |
| %8:ptr<private, vec4<f32>, read_write> = access %p_1, 1i |
| %9:f32 = call %f0, %8 |
| %10:f32 = load %res |
| %11:f32 = add %10, %9 |
| store %res, %11 |
| %p_vec:ptr<private, vec4<f32>, read_write> = access %p_1, 1i |
| %13:f32 = call %f0, %p_vec |
| %14:f32 = load %res |
| %15:f32 = add %14, %13 |
| store %res, %15 |
| %16:ptr<private, vec4<f32>, read_write> = access %P, 0u, 2i, 0u, 1i |
| %17:f32 = call %f0, %16 |
| %18:f32 = load %res |
| %19:f32 = add %18, %17 |
| store %res, %19 |
| %p_vec_1:ptr<private, vec4<f32>, read_write> = access %P, 0u, 2i, 0u, 1i # %p_vec_1: 'p_vec' |
| %21:f32 = call %f0, %p_vec_1 |
| %22:f32 = load %res |
| %23:f32 = add %22, %21 |
| store %res, %23 |
| %24:f32 = load %res |
| ret %24 |
| } |
| } |
| %f2 = func(%p_2:ptr<private, Inner, read_write>):f32 { # %p_2: 'p' |
| $B4: { |
| %p_mat:ptr<private, mat3x4<f32>, read_write> = access %p_2, 0u |
| %28:f32 = call %f1, %p_mat |
| ret %28 |
| } |
| } |
| %f3 = func(%p0:ptr<private, array<Inner, 4>, read_write>, %p1:ptr<private, mat3x4<f32>, read_write>):f32 { |
| $B5: { |
| %p0_inner:ptr<private, Inner, read_write> = access %p0, 3i |
| %33:f32 = call %f2, %p0_inner |
| %34:f32 = call %f1, %p1 |
| %35:f32 = add %33, %34 |
| ret %35 |
| } |
| } |
| %f4 = func(%p_3:ptr<private, Outer, read_write>):f32 { # %p_3: 'p' |
| $B6: { |
| %38:ptr<private, array<Inner, 4>, read_write> = access %p_3, 0u |
| %39:ptr<private, mat3x4<f32>, read_write> = access %P, 1u |
| %40:f32 = call %f3, %38, %39 |
| ret %40 |
| } |
| } |
| %b = func():void { |
| $B7: { |
| %42:f32 = call %f4, %P |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| $B1: { # root |
| %P:ptr<private, Outer, read_write> = var |
| } |
| |
| %f0 = func(%p_indices:array<u32, 1>):f32 { |
| $B2: { |
| %4:u32 = access %p_indices, 0u |
| %5:ptr<private, vec4<f32>, read_write> = access %P, 1u, %4 |
| %6:f32 = load_vector_element %5, 0u |
| ret %6 |
| } |
| } |
| %f0_1 = func(%p_indices_1:array<u32, 2>):f32 { # %f0_1: 'f0', %p_indices_1: 'p_indices' |
| $B3: { |
| %9:u32 = access %p_indices_1, 0u |
| %10:u32 = access %p_indices_1, 1u |
| %11:ptr<private, vec4<f32>, read_write> = access %P, 0u, %9, 0u, %10 |
| %12:f32 = load_vector_element %11, 0u |
| ret %12 |
| } |
| } |
| %f1 = func():f32 { |
| $B4: { |
| %res:ptr<function, f32, read_write> = var |
| %15:u32 = convert 1i |
| %16:array<u32, 1> = construct %15 |
| %17:f32 = call %f0, %16 |
| %18:f32 = load %res |
| %19:f32 = add %18, %17 |
| store %res, %19 |
| %20:u32 = convert 1i |
| %21:array<u32, 1> = construct %20 |
| %22:f32 = call %f0, %21 |
| %23:f32 = load %res |
| %24:f32 = add %23, %22 |
| store %res, %24 |
| %25:u32 = convert 2i |
| %26:u32 = convert 1i |
| %27:array<u32, 2> = construct %25, %26 |
| %28:f32 = call %f0_1, %27 |
| %29:f32 = load %res |
| %30:f32 = add %29, %28 |
| store %res, %30 |
| %31:u32 = convert 2i |
| %32:u32 = convert 1i |
| %33:array<u32, 2> = construct %31, %32 |
| %34:f32 = call %f0_1, %33 |
| %35:f32 = load %res |
| %36:f32 = add %35, %34 |
| store %res, %36 |
| %37:f32 = load %res |
| ret %37 |
| } |
| } |
| %f1_1 = func(%p_indices_2:array<u32, 1>):f32 { # %f1_1: 'f1', %p_indices_2: 'p_indices' |
| $B5: { |
| %40:u32 = access %p_indices_2, 0u |
| %res_1:ptr<function, f32, read_write> = var # %res_1: 'res' |
| %42:u32 = convert 1i |
| %43:array<u32, 2> = construct %40, %42 |
| %44:f32 = call %f0_1, %43 |
| %45:f32 = load %res_1 |
| %46:f32 = add %45, %44 |
| store %res_1, %46 |
| %47:u32 = convert 1i |
| %48:array<u32, 2> = construct %40, %47 |
| %49:f32 = call %f0_1, %48 |
| %50:f32 = load %res_1 |
| %51:f32 = add %50, %49 |
| store %res_1, %51 |
| %52:u32 = convert 2i |
| %53:u32 = convert 1i |
| %54:array<u32, 2> = construct %52, %53 |
| %55:f32 = call %f0_1, %54 |
| %56:f32 = load %res_1 |
| %57:f32 = add %56, %55 |
| store %res_1, %57 |
| %58:u32 = convert 2i |
| %59:u32 = convert 1i |
| %60:array<u32, 2> = construct %58, %59 |
| %61:f32 = call %f0_1, %60 |
| %62:f32 = load %res_1 |
| %63:f32 = add %62, %61 |
| store %res_1, %63 |
| %64:f32 = load %res_1 |
| ret %64 |
| } |
| } |
| %f2 = func(%p_indices_3:array<u32, 1>):f32 { # %p_indices_3: 'p_indices' |
| $B6: { |
| %67:u32 = access %p_indices_3, 0u |
| %68:array<u32, 1> = construct %67 |
| %69:f32 = call %f1_1, %68 |
| ret %69 |
| } |
| } |
| %f3 = func():f32 { |
| $B7: { |
| %71:u32 = convert 3i |
| %72:array<u32, 1> = construct %71 |
| %73:f32 = call %f2, %72 |
| %74:f32 = call %f1 |
| %75:f32 = add %73, %74 |
| ret %75 |
| } |
| } |
| %f4 = func():f32 { |
| $B8: { |
| %77:f32 = call %f3 |
| ret %77 |
| } |
| } |
| %b = func():void { |
| $B9: { |
| %79:f32 = call %f4 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformPrivate); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Disabled_CallChaining) { |
| auto* Inner = |
| ty.Struct(mod.symbols.New("Inner"), { |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| auto* Outer = |
| ty.Struct(mod.symbols.New("Outer"), { |
| {mod.symbols.Register("arr"), ty.array(Inner, 4)}, |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_>(Outer)); |
| }); |
| |
| auto* fn_0 = b.Function("f0", ty.f32()); |
| auto* fn_0_p = b.FunctionParam("p", ty.ptr<private_, vec4<f32>>()); |
| fn_0->SetParams({fn_0_p}); |
| b.Append(fn_0->Block(), [&] { b.Return(fn_0, b.LoadVectorElement(fn_0_p, 0_u)); }); |
| |
| auto* fn_1 = b.Function("f1", ty.f32()); |
| auto* fn_1_p = b.FunctionParam("p", ty.ptr<private_, mat3x4<f32>>()); |
| fn_1->SetParams({fn_1_p}); |
| b.Append(fn_1->Block(), [&] { |
| auto* res = b.Var<function, f32>("res"); |
| { |
| // res += f0(&(*p)[1]); |
| auto* call_0 = b.Call(fn_0, b.Access(ty.ptr<private_, vec4<f32>>(), fn_1_p, 1_i)); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &(*p)[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<private_, vec4<f32>>(), fn_1_p, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // res += f0(&U.arr[2].mat[1]); |
| auto* access = b.Access(ty.ptr<private_, vec4<f32>>(), P, 0_u, 2_i, 0_u, 1_i); |
| auto* call_0 = b.Call(fn_0, access); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &U.arr[2].mat[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<private_, vec4<f32>>(), P, 0_u, 2_i, 0_u, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(fn_0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| |
| b.Return(fn_1, b.Load(res)); |
| }); |
| |
| auto* fn_2 = b.Function("f2", ty.f32()); |
| auto* fn_2_p = b.FunctionParam("p", ty.ptr<private_>(Inner)); |
| fn_2->SetParams({fn_2_p}); |
| b.Append(fn_2->Block(), [&] { |
| auto* p_mat = b.Access(ty.ptr<private_, mat3x4<f32>>(), fn_2_p, 0_u); |
| b.ir.SetName(p_mat, "p_mat"); |
| b.Return(fn_2, b.Call(fn_1, p_mat)); |
| }); |
| |
| auto* fn_3 = b.Function("f3", ty.f32()); |
| auto* fn_3_p0 = b.FunctionParam("p0", ty.ptr<private_>(ty.array(Inner, 4))); |
| auto* fn_3_p1 = b.FunctionParam("p1", ty.ptr<private_, mat3x4<f32>>()); |
| fn_3->SetParams({fn_3_p0, fn_3_p1}); |
| b.Append(fn_3->Block(), [&] { |
| auto* p0_inner = b.Access(ty.ptr<private_>(Inner), fn_3_p0, 3_i); |
| b.ir.SetName(p0_inner, "p0_inner"); |
| auto* call_0 = b.Call(ty.f32(), fn_2, p0_inner); |
| auto* call_1 = b.Call(ty.f32(), fn_1, fn_3_p1); |
| b.Return(fn_3, b.Add(ty.f32(), call_0, call_1)); |
| }); |
| |
| auto* fn_4 = b.Function("f4", ty.f32()); |
| auto* fn_4_p = b.FunctionParam("p", ty.ptr<private_>(Outer)); |
| fn_4->SetParams({fn_4_p}); |
| b.Append(fn_4->Block(), [&] { |
| auto* access_0 = b.Access(ty.ptr<private_>(ty.array(Inner, 4)), fn_4_p, 0_u); |
| auto* access_1 = b.Access(ty.ptr<private_, mat3x4<f32>>(), P, 1_u); |
| b.Return(fn_4, b.Call(ty.f32(), fn_3, access_0, access_1)); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| b.Call(ty.f32(), fn_4, P); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| $B1: { # root |
| %P:ptr<private, Outer, read_write> = var |
| } |
| |
| %f0 = func(%p:ptr<private, vec4<f32>, read_write>):f32 { |
| $B2: { |
| %4:f32 = load_vector_element %p, 0u |
| ret %4 |
| } |
| } |
| %f1 = func(%p_1:ptr<private, mat3x4<f32>, read_write>):f32 { # %p_1: 'p' |
| $B3: { |
| %res:ptr<function, f32, read_write> = var |
| %8:ptr<private, vec4<f32>, read_write> = access %p_1, 1i |
| %9:f32 = call %f0, %8 |
| %10:f32 = load %res |
| %11:f32 = add %10, %9 |
| store %res, %11 |
| %p_vec:ptr<private, vec4<f32>, read_write> = access %p_1, 1i |
| %13:f32 = call %f0, %p_vec |
| %14:f32 = load %res |
| %15:f32 = add %14, %13 |
| store %res, %15 |
| %16:ptr<private, vec4<f32>, read_write> = access %P, 0u, 2i, 0u, 1i |
| %17:f32 = call %f0, %16 |
| %18:f32 = load %res |
| %19:f32 = add %18, %17 |
| store %res, %19 |
| %p_vec_1:ptr<private, vec4<f32>, read_write> = access %P, 0u, 2i, 0u, 1i # %p_vec_1: 'p_vec' |
| %21:f32 = call %f0, %p_vec_1 |
| %22:f32 = load %res |
| %23:f32 = add %22, %21 |
| store %res, %23 |
| %24:f32 = load %res |
| ret %24 |
| } |
| } |
| %f2 = func(%p_2:ptr<private, Inner, read_write>):f32 { # %p_2: 'p' |
| $B4: { |
| %p_mat:ptr<private, mat3x4<f32>, read_write> = access %p_2, 0u |
| %28:f32 = call %f1, %p_mat |
| ret %28 |
| } |
| } |
| %f3 = func(%p0:ptr<private, array<Inner, 4>, read_write>, %p1:ptr<private, mat3x4<f32>, read_write>):f32 { |
| $B5: { |
| %p0_inner:ptr<private, Inner, read_write> = access %p0, 3i |
| %33:f32 = call %f2, %p0_inner |
| %34:f32 = call %f1, %p1 |
| %35:f32 = add %33, %34 |
| ret %35 |
| } |
| } |
| %f4 = func(%p_3:ptr<private, Outer, read_write>):f32 { # %p_3: 'p' |
| $B6: { |
| %38:ptr<private, array<Inner, 4>, read_write> = access %p_3, 0u |
| %39:ptr<private, mat3x4<f32>, read_write> = access %P, 1u |
| %40:f32 = call %f3, %38, %39 |
| ret %40 |
| } |
| } |
| %b = func():void { |
| $B7: { |
| %42:f32 = call %f4, %P |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Enabled_CallChaining2) { |
| auto* T3 = ty.vec4<i32>(); |
| auto* T2 = ty.array(T3, 5); |
| auto* T1 = ty.array(T2, 5); |
| auto* T = ty.array(T1, 5); |
| |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_>(T)); |
| }); |
| |
| auto* f2 = b.Function("f2", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<private_>(T2)); |
| f2->SetParams({p}); |
| b.Append(f2->Block(), [&] { |
| b.Return(f2, b.Load(b.Access<ptr<private_, vec4<i32>, read_write>>(p, 3_u))); |
| }); |
| } |
| |
| auto* f1 = b.Function("f1", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<private_>(T1)); |
| f1->SetParams({p}); |
| b.Append(f1->Block(), |
| [&] { b.Return(f1, b.Call(f2, b.Access(ty.ptr<private_>(T2), p, 2_u))); }); |
| } |
| |
| auto* f0 = b.Function("f0", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<private_>(T)); |
| f0->SetParams({p}); |
| b.Append(f0->Block(), |
| [&] { b.Return(f0, b.Call(f1, b.Access(ty.ptr<private_>(T1), p, 1_u))); }); |
| } |
| |
| auto* main = b.Function("main", ty.void_()); |
| b.Append(main->Block(), [&] { |
| b.Call(f0, P); |
| b.Return(main); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %P:ptr<private, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var |
| } |
| |
| %f2 = func(%p:ptr<private, array<vec4<i32>, 5>, read_write>):vec4<i32> { |
| $B2: { |
| %4:ptr<private, vec4<i32>, read_write> = access %p, 3u |
| %5:vec4<i32> = load %4 |
| ret %5 |
| } |
| } |
| %f1 = func(%p_1:ptr<private, array<array<vec4<i32>, 5>, 5>, read_write>):vec4<i32> { # %p_1: 'p' |
| $B3: { |
| %8:ptr<private, array<vec4<i32>, 5>, read_write> = access %p_1, 2u |
| %9:vec4<i32> = call %f2, %8 |
| ret %9 |
| } |
| } |
| %f0 = func(%p_2:ptr<private, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>):vec4<i32> { # %p_2: 'p' |
| $B4: { |
| %12:ptr<private, array<array<vec4<i32>, 5>, 5>, read_write> = access %p_2, 1u |
| %13:vec4<i32> = call %f1, %12 |
| ret %13 |
| } |
| } |
| %main = func():void { |
| $B5: { |
| %15:vec4<i32> = call %f0, %P |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %P:ptr<private, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var |
| } |
| |
| %f2 = func(%p_indices:array<u32, 2>):vec4<i32> { |
| $B2: { |
| %4:u32 = access %p_indices, 0u |
| %5:u32 = access %p_indices, 1u |
| %6:ptr<private, array<vec4<i32>, 5>, read_write> = access %P, %4, %5 |
| %7:ptr<private, vec4<i32>, read_write> = access %6, 3u |
| %8:vec4<i32> = load %7 |
| ret %8 |
| } |
| } |
| %f1 = func(%p_indices_1:array<u32, 1>):vec4<i32> { # %p_indices_1: 'p_indices' |
| $B3: { |
| %11:u32 = access %p_indices_1, 0u |
| %12:array<u32, 2> = construct %11, 2u |
| %13:vec4<i32> = call %f2, %12 |
| ret %13 |
| } |
| } |
| %f0 = func():vec4<i32> { |
| $B4: { |
| %15:array<u32, 1> = construct 1u |
| %16:vec4<i32> = call %f1, %15 |
| ret %16 |
| } |
| } |
| %main = func():void { |
| $B5: { |
| %18:vec4<i32> = call %f0 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformPrivate); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_PrivateAS, Disabled_CallChaining2) { |
| auto* T3 = ty.vec4<i32>(); |
| auto* T2 = ty.array(T3, 5); |
| auto* T1 = ty.array(T2, 5); |
| auto* T = ty.array(T1, 5); |
| |
| Var* P = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| P = b.Var("P", ty.ptr<private_>(T)); |
| }); |
| |
| auto* f2 = b.Function("f2", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<private_>(T2)); |
| f2->SetParams({p}); |
| b.Append(f2->Block(), [&] { |
| b.Return(f2, b.Load(b.Access<ptr<private_, vec4<i32>, read_write>>(p, 3_u))); |
| }); |
| } |
| |
| auto* f1 = b.Function("f1", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<private_>(T1)); |
| f1->SetParams({p}); |
| b.Append(f1->Block(), |
| [&] { b.Return(f1, b.Call(f2, b.Access(ty.ptr<private_>(T2), p, 2_u))); }); |
| } |
| |
| auto* f0 = b.Function("f0", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<private_>(T)); |
| f0->SetParams({p}); |
| b.Append(f0->Block(), |
| [&] { b.Return(f0, b.Call(f1, b.Access(ty.ptr<private_>(T1), p, 1_u))); }); |
| } |
| |
| auto* main = b.Function("main", ty.void_()); |
| b.Append(main->Block(), [&] { |
| b.Call(f0, P); |
| b.Return(main); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %P:ptr<private, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var |
| } |
| |
| %f2 = func(%p:ptr<private, array<vec4<i32>, 5>, read_write>):vec4<i32> { |
| $B2: { |
| %4:ptr<private, vec4<i32>, read_write> = access %p, 3u |
| %5:vec4<i32> = load %4 |
| ret %5 |
| } |
| } |
| %f1 = func(%p_1:ptr<private, array<array<vec4<i32>, 5>, 5>, read_write>):vec4<i32> { # %p_1: 'p' |
| $B3: { |
| %8:ptr<private, array<vec4<i32>, 5>, read_write> = access %p_1, 2u |
| %9:vec4<i32> = call %f2, %8 |
| ret %9 |
| } |
| } |
| %f0 = func(%p_2:ptr<private, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>):vec4<i32> { # %p_2: 'p' |
| $B4: { |
| %12:ptr<private, array<array<vec4<i32>, 5>, 5>, read_write> = access %p_2, 1u |
| %13:vec4<i32> = call %f1, %12 |
| ret %13 |
| } |
| } |
| %main = func():void { |
| $B5: { |
| %15:vec4<i32> = call %f0, %P |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| } // namespace private_as_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // 'function' address space |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace function_as_tests { |
| |
| using IR_DirectVariableAccessTest_FunctionAS = TransformTest; |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Enabled_LocalPtr) { |
| auto* fn = b.Function("f", ty.void_()); |
| b.Append(fn->Block(), [&] { |
| auto* v = b.Var<function, i32>("v"); |
| auto* p = b.Let("p", v); |
| b.Var<function>("x", b.Load(p)); |
| b.Return(fn); |
| }); |
| |
| auto* src = R"( |
| %f = func():void { |
| $B1: { |
| %v:ptr<function, i32, read_write> = var |
| %p:ptr<function, i32, read_write> = let %v |
| %4:i32 = load %p |
| %x:ptr<function, i32, read_write> = var, %4 |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; // Nothing changes |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Enabled_Param_ptr_i32_read) { |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<function, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* F = b.Var<function, i32>("F"); |
| b.Call(fn_a, 10_i, F, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| %a = func(%pre:i32, %p:ptr<function, i32, read_write>, %post:i32):i32 { |
| $B1: { |
| %5:i32 = load %p |
| ret %5 |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, i32, read_write> = var |
| %8:i32 = call %a, 10i, %F, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %a = func(%pre:i32, %p_root:ptr<function, i32, read_write>, %post:i32):i32 { |
| $B1: { |
| %5:ptr<function, i32, read_write> = access %p_root |
| %6:i32 = load %5 |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, i32, read_write> = var |
| %9:i32 = call %a, 10i, %F, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformFunction); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Enabled_Param_ptr_i32_write) { |
| auto* fn_a = b.Function("a", ty.void_()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<function, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { |
| b.Store(fn_a_p, 42_i); |
| b.Return(fn_a); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* F = b.Var<function, i32>("F"); |
| b.Call(fn_a, 10_i, F, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| %a = func(%pre:i32, %p:ptr<function, i32, read_write>, %post:i32):void { |
| $B1: { |
| store %p, 42i |
| ret |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, i32, read_write> = var |
| %7:void = call %a, 10i, %F, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %a = func(%pre:i32, %p_root:ptr<function, i32, read_write>, %post:i32):void { |
| $B1: { |
| %5:ptr<function, i32, read_write> = access %p_root |
| store %5, 42i |
| ret |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, i32, read_write> = var |
| %8:void = call %a, 10i, %F, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformFunction); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Enabled_Param_ptr_i32_Via_struct_read) { |
| auto* str_ = ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("i"), ty.i32()}, |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<function, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* F = b.Var("F", ty.ptr<function>(str_)); |
| auto* access = b.Access(ty.ptr<function, i32>(), F, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<function, i32, read_write>, %post:i32):i32 { |
| $B1: { |
| %5:i32 = load %p |
| ret %5 |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, str, read_write> = var |
| %8:ptr<function, i32, read_write> = access %F, 0u |
| %9:i32 = call %a, 10i, %8, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| %a = func(%pre:i32, %p_root:ptr<function, str, read_write>, %post:i32):i32 { |
| $B1: { |
| %5:ptr<function, i32, read_write> = access %p_root, 0u |
| %6:i32 = load %5 |
| ret %6 |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, str, read_write> = var |
| %9:i32 = call %a, 10i, %F, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformFunction); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Enabled_Param_ptr_arr_i32_Via_struct_write) { |
| auto* str_ = |
| ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("arr"), ty.array<i32, 4>()}, |
| }); |
| |
| auto* fn_a = b.Function("a", ty.void_()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<function, array<i32, 4>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { |
| b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4)); |
| b.Return(fn_a); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* F = b.Var("F", ty.ptr<function>(str_)); |
| auto* access = b.Access(ty.ptr<function, array<i32, 4>>(), F, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| arr:array<i32, 4> @offset(0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<function, array<i32, 4>, read_write>, %post:i32):void { |
| $B1: { |
| store %p, array<i32, 4>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, str, read_write> = var |
| %7:ptr<function, array<i32, 4>, read_write> = access %F, 0u |
| %8:void = call %a, 10i, %7, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| str = struct @align(4) { |
| arr:array<i32, 4> @offset(0) |
| } |
| |
| %a = func(%pre:i32, %p_root:ptr<function, str, read_write>, %post:i32):void { |
| $B1: { |
| %5:ptr<function, array<i32, 4>, read_write> = access %p_root, 0u |
| store %5, array<i32, 4>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, str, read_write> = var |
| %8:void = call %a, 10i, %F, 20i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformFunction); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Enabled_Param_ptr_i32_mixed) { |
| auto* str_ = ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("i"), ty.i32()}, |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<function, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* Fi = b.Var("Fi", ty.ptr<function, i32>()); |
| auto* Fs = b.Var("Fs", ty.ptr<function>(str_)); |
| auto* Fa = b.Var("Fa", ty.ptr<function, array<i32, 4>>()); |
| { // a(10, &Fi, 20); |
| b.Call(fn_a, 10_i, Fi, 20_i); |
| } |
| { // a(30, &Fs.i, 40); |
| auto* access = b.Access(ty.ptr<function, i32>(), Fs, 0_u); |
| b.Call(fn_a, 30_i, access, 40_i); |
| } |
| { // a(50, &Fa[2], 60); |
| auto* access = b.Access(ty.ptr<function, i32>(), Fa, 2_i); |
| b.Call(fn_a, 50_i, access, 60_i); |
| } |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<function, i32, read_write>, %post:i32):i32 { |
| $B1: { |
| %5:i32 = load %p |
| ret %5 |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %Fi:ptr<function, i32, read_write> = var |
| %Fs:ptr<function, str, read_write> = var |
| %Fa:ptr<function, array<i32, 4>, read_write> = var |
| %10:i32 = call %a, 10i, %Fi, 20i |
| %11:ptr<function, i32, read_write> = access %Fs, 0u |
| %12:i32 = call %a, 30i, %11, 40i |
| %13:ptr<function, i32, read_write> = access %Fa, 2i |
| %14:i32 = call %a, 50i, %13, 60i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| %a = func(%pre:i32, %p_root:ptr<function, i32, read_write>, %post:i32):i32 { |
| $B1: { |
| %5:ptr<function, i32, read_write> = access %p_root |
| %6:i32 = load %5 |
| ret %6 |
| } |
| } |
| %a_1 = func(%pre_1:i32, %p_root_1:ptr<function, str, read_write>, %post_1:i32):i32 { # %a_1: 'a', %pre_1: 'pre', %p_root_1: 'p_root', %post_1: 'post' |
| $B2: { |
| %11:ptr<function, i32, read_write> = access %p_root_1, 0u |
| %12:i32 = load %11 |
| ret %12 |
| } |
| } |
| %a_2 = func(%pre_2:i32, %p_root_2:ptr<function, array<i32, 4>, read_write>, %p_indices:array<u32, 1>, %post_2:i32):i32 { # %a_2: 'a', %pre_2: 'pre', %p_root_2: 'p_root', %post_2: 'post' |
| $B3: { |
| %18:u32 = access %p_indices, 0u |
| %19:ptr<function, i32, read_write> = access %p_root_2, %18 |
| %20:i32 = load %19 |
| ret %20 |
| } |
| } |
| %b = func():void { |
| $B4: { |
| %Fi:ptr<function, i32, read_write> = var |
| %Fs:ptr<function, str, read_write> = var |
| %Fa:ptr<function, array<i32, 4>, read_write> = var |
| %25:i32 = call %a, 10i, %Fi, 20i |
| %26:i32 = call %a_1, 30i, %Fs, 40i |
| %27:u32 = convert 2i |
| %28:array<u32, 1> = construct %27 |
| %29:i32 = call %a_2, 50i, %Fa, %28, 60i |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformFunction); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Disabled_Param_ptr_i32_Via_struct_read) { |
| auto* str_ = ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("i"), ty.i32()}, |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<function, i32>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* F = b.Var("F", ty.ptr<function>(str_)); |
| auto* access = b.Access(ty.ptr<function, i32>(), F, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| i:i32 @offset(0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<function, i32, read_write>, %post:i32):i32 { |
| $B1: { |
| %5:i32 = load %p |
| ret %5 |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, str, read_write> = var |
| %8:ptr<function, i32, read_write> = access %F, 0u |
| %9:i32 = call %a, 10i, %8, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Disabled_Param_ptr_arr_i32_Via_struct_write) { |
| auto* str_ = |
| ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("arr"), ty.array<i32, 4>()}, |
| }); |
| |
| auto* fn_a = b.Function("a", ty.void_()); |
| auto* fn_a_p = b.FunctionParam("p", ty.ptr<function, array<i32, 4>>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_p, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { |
| b.Store(fn_a_p, b.Splat(ty.array<i32, 4>(), 0_i, 4)); |
| b.Return(fn_a); |
| }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* F = b.Var("F", ty.ptr<function>(str_)); |
| auto* access = b.Access(ty.ptr<function, array<i32, 4>>(), F, 0_u); |
| b.Call(fn_a, 10_i, access, 20_i); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(4) { |
| arr:array<i32, 4> @offset(0) |
| } |
| |
| %a = func(%pre:i32, %p:ptr<function, array<i32, 4>, read_write>, %post:i32):void { |
| $B1: { |
| store %p, array<i32, 4>(0i) |
| ret |
| } |
| } |
| %b = func():void { |
| $B2: { |
| %F:ptr<function, str, read_write> = var |
| %7:ptr<function, array<i32, 4>, read_write> = access %F, 0u |
| %8:void = call %a, 10i, %7, 20i |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Enabled_CallChaining) { |
| auto* Inner = |
| ty.Struct(mod.symbols.New("Inner"), { |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| auto* Outer = |
| ty.Struct(mod.symbols.New("Outer"), { |
| {mod.symbols.Register("arr"), ty.array(Inner, 4)}, |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| |
| auto* f0 = b.Function("f0", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function, vec4<f32>>()); |
| f0->SetParams({p}); |
| b.Append(f0->Block(), [&] { b.Return(f0, b.LoadVectorElement(p, 0_u)); }); |
| } |
| |
| auto* f1 = b.Function("f1", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function, mat3x4<f32>>()); |
| f1->SetParams({p}); |
| b.Append(f1->Block(), [&] { |
| auto* res = b.Var<function, f32>("res"); |
| { |
| // res += f0(&(*p)[1]); |
| auto* call_0 = b.Call(f0, b.Access(ty.ptr<function, vec4<f32>>(), p, 1_i)); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &(*p)[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<function, vec4<f32>>(), p, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(f0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| b.Return(f1, b.Load(res)); |
| }); |
| } |
| |
| auto* f2 = b.Function("f2", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(Inner)); |
| f2->SetParams({p}); |
| b.Append(f2->Block(), [&] { |
| auto* p_mat = b.Access(ty.ptr<function, mat3x4<f32>>(), p, 0_u); |
| b.ir.SetName(p_mat, "p_mat"); |
| b.Return(f2, b.Call(f1, p_mat)); |
| }); |
| } |
| |
| auto* f3 = b.Function("f3", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(ty.array(Inner, 4))); |
| f3->SetParams({p}); |
| b.Append(f3->Block(), [&] { |
| auto* p_inner = b.Access(ty.ptr<function>(Inner), p, 3_i); |
| b.ir.SetName(p_inner, "p_inner"); |
| b.Return(f3, b.Call(f2, p_inner)); |
| }); |
| } |
| |
| auto* f4 = b.Function("f4", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(Outer)); |
| f4->SetParams({p}); |
| b.Append(f4->Block(), [&] { |
| auto* access = b.Access(ty.ptr<function>(ty.array(Inner, 4)), p, 0_u); |
| b.Return(f4, b.Call(f3, access)); |
| }); |
| } |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto F = b.Var("F", ty.ptr<function>(Outer)); |
| b.Call(f4, F); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| %f0 = func(%p:ptr<function, vec4<f32>, read_write>):f32 { |
| $B1: { |
| %3:f32 = load_vector_element %p, 0u |
| ret %3 |
| } |
| } |
| %f1 = func(%p_1:ptr<function, mat3x4<f32>, read_write>):f32 { # %p_1: 'p' |
| $B2: { |
| %res:ptr<function, f32, read_write> = var |
| %7:ptr<function, vec4<f32>, read_write> = access %p_1, 1i |
| %8:f32 = call %f0, %7 |
| %9:f32 = load %res |
| %10:f32 = add %9, %8 |
| store %res, %10 |
| %p_vec:ptr<function, vec4<f32>, read_write> = access %p_1, 1i |
| %12:f32 = call %f0, %p_vec |
| %13:f32 = load %res |
| %14:f32 = add %13, %12 |
| store %res, %14 |
| %15:f32 = load %res |
| ret %15 |
| } |
| } |
| %f2 = func(%p_2:ptr<function, Inner, read_write>):f32 { # %p_2: 'p' |
| $B3: { |
| %p_mat:ptr<function, mat3x4<f32>, read_write> = access %p_2, 0u |
| %19:f32 = call %f1, %p_mat |
| ret %19 |
| } |
| } |
| %f3 = func(%p_3:ptr<function, array<Inner, 4>, read_write>):f32 { # %p_3: 'p' |
| $B4: { |
| %p_inner:ptr<function, Inner, read_write> = access %p_3, 3i |
| %23:f32 = call %f2, %p_inner |
| ret %23 |
| } |
| } |
| %f4 = func(%p_4:ptr<function, Outer, read_write>):f32 { # %p_4: 'p' |
| $B5: { |
| %26:ptr<function, array<Inner, 4>, read_write> = access %p_4, 0u |
| %27:f32 = call %f3, %26 |
| ret %27 |
| } |
| } |
| %b = func():void { |
| $B6: { |
| %F:ptr<function, Outer, read_write> = var |
| %30:f32 = call %f4, %F |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| %f0 = func(%p_root:ptr<function, Outer, read_write>, %p_indices:array<u32, 2>):f32 { |
| $B1: { |
| %4:u32 = access %p_indices, 0u |
| %5:u32 = access %p_indices, 1u |
| %6:ptr<function, vec4<f32>, read_write> = access %p_root, 0u, %4, 0u, %5 |
| %7:f32 = load_vector_element %6, 0u |
| ret %7 |
| } |
| } |
| %f1 = func(%p_root_1:ptr<function, Outer, read_write>, %p_indices_1:array<u32, 1>):f32 { # %p_root_1: 'p_root', %p_indices_1: 'p_indices' |
| $B2: { |
| %11:u32 = access %p_indices_1, 0u |
| %res:ptr<function, f32, read_write> = var |
| %13:u32 = convert 1i |
| %14:array<u32, 2> = construct %11, %13 |
| %15:f32 = call %f0, %p_root_1, %14 |
| %16:f32 = load %res |
| %17:f32 = add %16, %15 |
| store %res, %17 |
| %18:u32 = convert 1i |
| %19:array<u32, 2> = construct %11, %18 |
| %20:f32 = call %f0, %p_root_1, %19 |
| %21:f32 = load %res |
| %22:f32 = add %21, %20 |
| store %res, %22 |
| %23:f32 = load %res |
| ret %23 |
| } |
| } |
| %f2 = func(%p_root_2:ptr<function, Outer, read_write>, %p_indices_2:array<u32, 1>):f32 { # %p_root_2: 'p_root', %p_indices_2: 'p_indices' |
| $B3: { |
| %27:u32 = access %p_indices_2, 0u |
| %28:array<u32, 1> = construct %27 |
| %29:f32 = call %f1, %p_root_2, %28 |
| ret %29 |
| } |
| } |
| %f3 = func(%p_root_3:ptr<function, Outer, read_write>):f32 { # %p_root_3: 'p_root' |
| $B4: { |
| %32:u32 = convert 3i |
| %33:array<u32, 1> = construct %32 |
| %34:f32 = call %f2, %p_root_3, %33 |
| ret %34 |
| } |
| } |
| %f4 = func(%p_root_4:ptr<function, Outer, read_write>):f32 { # %p_root_4: 'p_root' |
| $B5: { |
| %37:f32 = call %f3, %p_root_4 |
| ret %37 |
| } |
| } |
| %b = func():void { |
| $B6: { |
| %F:ptr<function, Outer, read_write> = var |
| %40:f32 = call %f4, %F |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformFunction); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Disabled_CallChaining) { |
| auto* Inner = |
| ty.Struct(mod.symbols.New("Inner"), { |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| auto* Outer = |
| ty.Struct(mod.symbols.New("Outer"), { |
| {mod.symbols.Register("arr"), ty.array(Inner, 4)}, |
| {mod.symbols.Register("mat"), ty.mat3x4<f32>()}, |
| }); |
| |
| auto* f0 = b.Function("f0", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function, vec4<f32>>()); |
| f0->SetParams({p}); |
| b.Append(f0->Block(), [&] { b.Return(f0, b.LoadVectorElement(p, 0_u)); }); |
| } |
| |
| auto* f1 = b.Function("f1", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function, mat3x4<f32>>()); |
| f1->SetParams({p}); |
| b.Append(f1->Block(), [&] { |
| auto* res = b.Var<function, f32>("res"); |
| { |
| // res += f0(&(*p)[1]); |
| auto* call_0 = b.Call(f0, b.Access(ty.ptr<function, vec4<f32>>(), p, 1_i)); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| { |
| // let p_vec = &(*p)[1]; |
| // res += f0(p_vec); |
| auto* p_vec = b.Access(ty.ptr<function, vec4<f32>>(), p, 1_i); |
| b.ir.SetName(p_vec, "p_vec"); |
| auto* call_0 = b.Call(f0, p_vec); |
| b.Store(res, b.Add(ty.f32(), b.Load(res), call_0)); |
| } |
| b.Return(f1, b.Load(res)); |
| }); |
| } |
| |
| auto* f2 = b.Function("f2", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(Inner)); |
| f2->SetParams({p}); |
| b.Append(f2->Block(), [&] { |
| auto* p_mat = b.Access(ty.ptr<function, mat3x4<f32>>(), p, 0_u); |
| b.ir.SetName(p_mat, "p_mat"); |
| b.Return(f2, b.Call(f1, p_mat)); |
| }); |
| } |
| |
| auto* f3 = b.Function("f3", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(ty.array(Inner, 4))); |
| f3->SetParams({p}); |
| b.Append(f3->Block(), [&] { |
| auto* p_inner = b.Access(ty.ptr<function>(Inner), p, 3_i); |
| b.ir.SetName(p_inner, "p_inner"); |
| b.Return(f3, b.Call(f2, p_inner)); |
| }); |
| } |
| |
| auto* f4 = b.Function("f4", ty.f32()); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(Outer)); |
| f4->SetParams({p}); |
| b.Append(f4->Block(), [&] { |
| auto* access = b.Access(ty.ptr<function>(ty.array(Inner, 4)), p, 0_u); |
| b.Return(f4, b.Call(f3, access)); |
| }); |
| } |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto F = b.Var("F", ty.ptr<function>(Outer)); |
| b.Call(f4, F); |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| Inner = struct @align(16) { |
| mat:mat3x4<f32> @offset(0) |
| } |
| |
| Outer = struct @align(16) { |
| arr:array<Inner, 4> @offset(0) |
| mat:mat3x4<f32> @offset(192) |
| } |
| |
| %f0 = func(%p:ptr<function, vec4<f32>, read_write>):f32 { |
| $B1: { |
| %3:f32 = load_vector_element %p, 0u |
| ret %3 |
| } |
| } |
| %f1 = func(%p_1:ptr<function, mat3x4<f32>, read_write>):f32 { # %p_1: 'p' |
| $B2: { |
| %res:ptr<function, f32, read_write> = var |
| %7:ptr<function, vec4<f32>, read_write> = access %p_1, 1i |
| %8:f32 = call %f0, %7 |
| %9:f32 = load %res |
| %10:f32 = add %9, %8 |
| store %res, %10 |
| %p_vec:ptr<function, vec4<f32>, read_write> = access %p_1, 1i |
| %12:f32 = call %f0, %p_vec |
| %13:f32 = load %res |
| %14:f32 = add %13, %12 |
| store %res, %14 |
| %15:f32 = load %res |
| ret %15 |
| } |
| } |
| %f2 = func(%p_2:ptr<function, Inner, read_write>):f32 { # %p_2: 'p' |
| $B3: { |
| %p_mat:ptr<function, mat3x4<f32>, read_write> = access %p_2, 0u |
| %19:f32 = call %f1, %p_mat |
| ret %19 |
| } |
| } |
| %f3 = func(%p_3:ptr<function, array<Inner, 4>, read_write>):f32 { # %p_3: 'p' |
| $B4: { |
| %p_inner:ptr<function, Inner, read_write> = access %p_3, 3i |
| %23:f32 = call %f2, %p_inner |
| ret %23 |
| } |
| } |
| %f4 = func(%p_4:ptr<function, Outer, read_write>):f32 { # %p_4: 'p' |
| $B5: { |
| %26:ptr<function, array<Inner, 4>, read_write> = access %p_4, 0u |
| %27:f32 = call %f3, %26 |
| ret %27 |
| } |
| } |
| %b = func():void { |
| $B6: { |
| %F:ptr<function, Outer, read_write> = var |
| %30:f32 = call %f4, %F |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Enabled_CallChaining2) { |
| auto* T3 = ty.vec4<i32>(); |
| auto* T2 = ty.array(T3, 5); |
| auto* T1 = ty.array(T2, 5); |
| auto* T = ty.array(T1, 5); |
| |
| auto* f2 = b.Function("f2", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(T2)); |
| f2->SetParams({p}); |
| b.Append(f2->Block(), [&] { |
| b.Return(f2, b.Load(b.Access<ptr<function, vec4<i32>, read_write>>(p, 3_u))); |
| }); |
| } |
| |
| auto* f1 = b.Function("f1", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(T1)); |
| f1->SetParams({p}); |
| b.Append(f1->Block(), |
| [&] { b.Return(f1, b.Call(f2, b.Access(ty.ptr<function>(T2), p, 2_u))); }); |
| } |
| |
| auto* f0 = b.Function("f0", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(T)); |
| f0->SetParams({p}); |
| b.Append(f0->Block(), |
| [&] { b.Return(f0, b.Call(f1, b.Access(ty.ptr<function>(T1), p, 1_u))); }); |
| } |
| |
| auto* main = b.Function("main", ty.void_()); |
| b.Append(main->Block(), [&] { |
| auto* F = b.Var("F", ty.ptr<function>(T)); |
| b.Call(f0, F); |
| b.Return(main); |
| }); |
| |
| auto* src = R"( |
| %f2 = func(%p:ptr<function, array<vec4<i32>, 5>, read_write>):vec4<i32> { |
| $B1: { |
| %3:ptr<function, vec4<i32>, read_write> = access %p, 3u |
| %4:vec4<i32> = load %3 |
| ret %4 |
| } |
| } |
| %f1 = func(%p_1:ptr<function, array<array<vec4<i32>, 5>, 5>, read_write>):vec4<i32> { # %p_1: 'p' |
| $B2: { |
| %7:ptr<function, array<vec4<i32>, 5>, read_write> = access %p_1, 2u |
| %8:vec4<i32> = call %f2, %7 |
| ret %8 |
| } |
| } |
| %f0 = func(%p_2:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>):vec4<i32> { # %p_2: 'p' |
| $B3: { |
| %11:ptr<function, array<array<vec4<i32>, 5>, 5>, read_write> = access %p_2, 1u |
| %12:vec4<i32> = call %f1, %11 |
| ret %12 |
| } |
| } |
| %main = func():void { |
| $B4: { |
| %F:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var |
| %15:vec4<i32> = call %f0, %F |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| %f2 = func(%p_root:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>, %p_indices:array<u32, 2>):vec4<i32> { |
| $B1: { |
| %4:u32 = access %p_indices, 0u |
| %5:u32 = access %p_indices, 1u |
| %6:ptr<function, array<vec4<i32>, 5>, read_write> = access %p_root, %4, %5 |
| %7:ptr<function, vec4<i32>, read_write> = access %6, 3u |
| %8:vec4<i32> = load %7 |
| ret %8 |
| } |
| } |
| %f1 = func(%p_root_1:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>, %p_indices_1:array<u32, 1>):vec4<i32> { # %p_root_1: 'p_root', %p_indices_1: 'p_indices' |
| $B2: { |
| %12:u32 = access %p_indices_1, 0u |
| %13:array<u32, 2> = construct %12, 2u |
| %14:vec4<i32> = call %f2, %p_root_1, %13 |
| ret %14 |
| } |
| } |
| %f0 = func(%p_root_2:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>):vec4<i32> { # %p_root_2: 'p_root' |
| $B3: { |
| %17:array<u32, 1> = construct 1u |
| %18:vec4<i32> = call %f1, %p_root_2, %17 |
| ret %18 |
| } |
| } |
| %main = func():void { |
| $B4: { |
| %F:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var |
| %21:vec4<i32> = call %f0, %F |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, kTransformFunction); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_FunctionAS, Disabled_CallChaining2) { |
| auto* T3 = ty.vec4<i32>(); |
| auto* T2 = ty.array(T3, 5); |
| auto* T1 = ty.array(T2, 5); |
| auto* T = ty.array(T1, 5); |
| |
| auto* f2 = b.Function("f2", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(T2)); |
| f2->SetParams({p}); |
| b.Append(f2->Block(), [&] { |
| b.Return(f2, b.Load(b.Access<ptr<function, vec4<i32>, read_write>>(p, 3_u))); |
| }); |
| } |
| |
| auto* f1 = b.Function("f1", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(T1)); |
| f1->SetParams({p}); |
| b.Append(f1->Block(), |
| [&] { b.Return(f1, b.Call(f2, b.Access(ty.ptr<function>(T2), p, 2_u))); }); |
| } |
| |
| auto* f0 = b.Function("f0", T3); |
| { |
| auto* p = b.FunctionParam("p", ty.ptr<function>(T)); |
| f0->SetParams({p}); |
| b.Append(f0->Block(), |
| [&] { b.Return(f0, b.Call(f1, b.Access(ty.ptr<function>(T1), p, 1_u))); }); |
| } |
| |
| auto* main = b.Function("main", ty.void_()); |
| b.Append(main->Block(), [&] { |
| auto* F = b.Var("F", ty.ptr<function>(T)); |
| b.Call(f0, F); |
| b.Return(main); |
| }); |
| |
| auto* src = R"( |
| %f2 = func(%p:ptr<function, array<vec4<i32>, 5>, read_write>):vec4<i32> { |
| $B1: { |
| %3:ptr<function, vec4<i32>, read_write> = access %p, 3u |
| %4:vec4<i32> = load %3 |
| ret %4 |
| } |
| } |
| %f1 = func(%p_1:ptr<function, array<array<vec4<i32>, 5>, 5>, read_write>):vec4<i32> { # %p_1: 'p' |
| $B2: { |
| %7:ptr<function, array<vec4<i32>, 5>, read_write> = access %p_1, 2u |
| %8:vec4<i32> = call %f2, %7 |
| ret %8 |
| } |
| } |
| %f0 = func(%p_2:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write>):vec4<i32> { # %p_2: 'p' |
| $B3: { |
| %11:ptr<function, array<array<vec4<i32>, 5>, 5>, read_write> = access %p_2, 1u |
| %12:vec4<i32> = call %f1, %11 |
| ret %12 |
| } |
| } |
| %main = func():void { |
| $B4: { |
| %F:ptr<function, array<array<array<vec4<i32>, 5>, 5>, 5>, read_write> = var |
| %15:vec4<i32> = call %f0, %F |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = src; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| } // namespace function_as_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // builtin function calls |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace builtin_fn_calls { |
| |
| using IR_DirectVariableAccessTest_BuiltinFn = TransformTest; |
| |
| TEST_F(IR_DirectVariableAccessTest_BuiltinFn, ArrayLength) { |
| Var* S = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| S = b.Var<storage, array<f32>>("S"); |
| S->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_len = b.Function("len", ty.u32()); |
| auto* fn_len_p = b.FunctionParam("p", ty.ptr<storage, array<f32>>()); |
| fn_len->SetParams({fn_len_p}); |
| b.Append(fn_len->Block(), |
| [&] { // |
| b.Return(fn_len, b.Call(ty.u32(), core::BuiltinFn::kArrayLength, fn_len_p)); |
| }); |
| |
| auto* fn_f = b.Function("b", ty.void_()); |
| b.Append(fn_f->Block(), [&] { |
| b.Call(fn_len, S); |
| b.Return(fn_f); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %S:ptr<storage, array<f32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %len = func(%p:ptr<storage, array<f32>, read_write>):u32 { |
| $B2: { |
| %4:u32 = arrayLength %p |
| ret %4 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %6:u32 = call %len, %S |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %S:ptr<storage, array<f32>, read_write> = var @binding_point(0, 0) |
| } |
| |
| %len = func():u32 { |
| $B2: { |
| %3:ptr<storage, array<f32>, read_write> = access %S |
| %4:u32 = arrayLength %3 |
| ret %4 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %6:u32 = call %len |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_BuiltinFn, AtomicLoad) { |
| Var* W = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| W = b.Var("W", ty.ptr<workgroup>(ty.atomic<i32>())); |
| }); |
| |
| auto* fn_load = b.Function("load", ty.i32()); |
| auto* fn_load_p = b.FunctionParam("p", ty.ptr<workgroup>(ty.atomic<i32>())); |
| fn_load->SetParams({fn_load_p}); |
| b.Append(fn_load->Block(), |
| [&] { // |
| b.Return(fn_load, b.Call(ty.i32(), core::BuiltinFn::kAtomicLoad, fn_load_p)); |
| }); |
| |
| auto* fn_f = b.Function("b", ty.void_()); |
| b.Append(fn_f->Block(), [&] { |
| b.Call(fn_load, W); |
| b.Return(fn_f); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %W:ptr<workgroup, atomic<i32>, read_write> = var |
| } |
| |
| %load = func(%p:ptr<workgroup, atomic<i32>, read_write>):i32 { |
| $B2: { |
| %4:i32 = atomicLoad %p |
| ret %4 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %6:i32 = call %load, %W |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %W:ptr<workgroup, atomic<i32>, read_write> = var |
| } |
| |
| %load = func():i32 { |
| $B2: { |
| %3:ptr<workgroup, atomic<i32>, read_write> = access %W |
| %4:i32 = atomicLoad %3 |
| ret %4 |
| } |
| } |
| %b = func():void { |
| $B3: { |
| %6:i32 = call %load |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| } // namespace builtin_fn_calls |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // complex tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace complex_tests { |
| |
| using IR_DirectVariableAccessTest_Complex = TransformTest; |
| |
| TEST_F(IR_DirectVariableAccessTest_Complex, Param_ptr_mixed_vec4i32_ViaMultiple) { |
| auto* str_ = ty.Struct(mod.symbols.New("str"), { |
| {mod.symbols.Register("i"), ty.vec4<i32>()}, |
| }); |
| |
| Var* U = nullptr; |
| Var* U_str = nullptr; |
| Var* U_arr = nullptr; |
| Var* U_arr_arr = nullptr; |
| Var* S = nullptr; |
| Var* S_str = nullptr; |
| Var* S_arr = nullptr; |
| Var* S_arr_arr = nullptr; |
| Var* W = nullptr; |
| Var* W_str = nullptr; |
| Var* W_arr = nullptr; |
| Var* W_arr_arr = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| U = b.Var<uniform, vec4<i32>>("U"); |
| U->SetBindingPoint(0, 0); |
| U_str = b.Var("U_str", ty.ptr<uniform>(str_)); |
| U_str->SetBindingPoint(0, 1); |
| U_arr = b.Var<uniform, array<vec4<i32>, 8>>("U_arr"); |
| U_arr->SetBindingPoint(0, 2); |
| U_arr_arr = b.Var<uniform, array<array<vec4<i32>, 8>, 4>>("U_arr_arr"); |
| U_arr_arr->SetBindingPoint(0, 3); |
| |
| S = b.Var<storage, vec4<i32>, read>("S"); |
| S->SetBindingPoint(1, 0); |
| S_str = b.Var("S_str", ty.ptr<storage, read>(str_)); |
| S_str->SetBindingPoint(1, 1); |
| S_arr = b.Var<storage, array<vec4<i32>, 8>, read>("S_arr"); |
| S_arr->SetBindingPoint(1, 2); |
| S_arr_arr = b.Var<storage, array<array<vec4<i32>, 8>, 4>, read>("S_arr_arr"); |
| S_arr_arr->SetBindingPoint(1, 3); |
| |
| W = b.Var<workgroup, vec4<i32>>("W"); |
| W_str = b.Var("W_str", ty.ptr<workgroup>(str_)); |
| W_arr = b.Var<workgroup, array<vec4<i32>, 8>>("W_arr"); |
| W_arr_arr = b.Var<workgroup, array<array<vec4<i32>, 8>, 4>>("W_arr_arr"); |
| }); |
| |
| auto* fn_u = b.Function("fn_u", ty.vec4<i32>()); |
| auto* fn_u_p = b.FunctionParam("p", ty.ptr<uniform, vec4<i32>, read>()); |
| fn_u->SetParams({fn_u_p}); |
| b.Append(fn_u->Block(), [&] { b.Return(fn_u, b.Load(fn_u_p)); }); |
| |
| auto* fn_s = b.Function("fn_s", ty.vec4<i32>()); |
| auto* fn_s_p = b.FunctionParam("p", ty.ptr<storage, vec4<i32>, read>()); |
| fn_s->SetParams({fn_s_p}); |
| b.Append(fn_s->Block(), [&] { b.Return(fn_s, b.Load(fn_s_p)); }); |
| |
| auto* fn_w = b.Function("fn_w", ty.vec4<i32>()); |
| auto* fn_w_p = b.FunctionParam("p", ty.ptr<workgroup, vec4<i32>>()); |
| fn_w->SetParams({fn_w_p}); |
| b.Append(fn_w->Block(), [&] { b.Return(fn_w, b.Load(fn_w_p)); }); |
| |
| auto* fn_b = b.Function("b", ty.void_()); |
| b.Append(fn_b->Block(), [&] { |
| auto* I = b.Let("I", 3_i); |
| auto* J = b.Let("J", 4_i); |
| |
| auto* u = b.Call(fn_u, U); |
| b.ir.SetName(u, "u"); |
| auto* u_str = b.Call(fn_u, b.Access(ty.ptr<uniform, vec4<i32>>(), U_str, 0_u)); |
| b.ir.SetName(u_str, "u_str"); |
| auto* u_arr0 = b.Call(fn_u, b.Access(ty.ptr<uniform, vec4<i32>>(), U_arr, 0_i)); |
| b.ir.SetName(u_arr0, "u_arr0"); |
| auto* u_arr1 = b.Call(fn_u, b.Access(ty.ptr<uniform, vec4<i32>>(), U_arr, 1_i)); |
| b.ir.SetName(u_arr1, "u_arr1"); |
| auto* u_arrI = b.Call(fn_u, b.Access(ty.ptr<uniform, vec4<i32>>(), U_arr, I)); |
| b.ir.SetName(u_arrI, "u_arrI"); |
| auto* u_arr1_arr0 = |
| b.Call(fn_u, b.Access(ty.ptr<uniform, vec4<i32>>(), U_arr_arr, 1_i, 0_i)); |
| b.ir.SetName(u_arr1_arr0, "u_arr1_arr0"); |
| auto* u_arr2_arrI = b.Call(fn_u, b.Access(ty.ptr<uniform, vec4<i32>>(), U_arr_arr, 2_i, I)); |
| b.ir.SetName(u_arr2_arrI, "u_arr2_arrI"); |
| auto* u_arrI_arr2 = b.Call(fn_u, b.Access(ty.ptr<uniform, vec4<i32>>(), U_arr_arr, I, 2_i)); |
| b.ir.SetName(u_arrI_arr2, "u_arrI_arr2"); |
| auto* u_arrI_arrJ = b.Call(fn_u, b.Access(ty.ptr<uniform, vec4<i32>>(), U_arr_arr, I, J)); |
| b.ir.SetName(u_arrI_arrJ, "u_arrI_arrJ"); |
| |
| auto* s = b.Call(fn_s, S); |
| b.ir.SetName(s, "s"); |
| auto* s_str = b.Call(fn_s, b.Access(ty.ptr<storage, vec4<i32>, read>(), S_str, 0_u)); |
| b.ir.SetName(s_str, "s_str"); |
| auto* s_arr0 = b.Call(fn_s, b.Access(ty.ptr<storage, vec4<i32>, read>(), S_arr, 0_i)); |
| b.ir.SetName(s_arr0, "s_arr0"); |
| auto* s_arr1 = b.Call(fn_s, b.Access(ty.ptr<storage, vec4<i32>, read>(), S_arr, 1_i)); |
| b.ir.SetName(s_arr1, "s_arr1"); |
| auto* s_arrI = b.Call(fn_s, b.Access(ty.ptr<storage, vec4<i32>, read>(), S_arr, I)); |
| b.ir.SetName(s_arrI, "s_arrI"); |
| auto* s_arr1_arr0 = |
| b.Call(fn_s, b.Access(ty.ptr<storage, vec4<i32>, read>(), S_arr_arr, 1_i, 0_i)); |
| b.ir.SetName(s_arr1_arr0, "s_arr1_arr0"); |
| auto* s_arr2_arrI = |
| b.Call(fn_s, b.Access(ty.ptr<storage, vec4<i32>, read>(), S_arr_arr, 2_i, I)); |
| b.ir.SetName(s_arr2_arrI, "s_arr2_arrI"); |
| auto* s_arrI_arr2 = |
| b.Call(fn_s, b.Access(ty.ptr<storage, vec4<i32>, read>(), S_arr_arr, I, 2_i)); |
| b.ir.SetName(s_arrI_arr2, "s_arrI_arr2"); |
| auto* s_arrI_arrJ = |
| b.Call(fn_s, b.Access(ty.ptr<storage, vec4<i32>, read>(), S_arr_arr, I, J)); |
| b.ir.SetName(s_arrI_arrJ, "s_arrI_arrJ"); |
| |
| auto* w = b.Call(fn_w, W); |
| b.ir.SetName(w, "w"); |
| auto* w_str = b.Call(fn_w, b.Access(ty.ptr<workgroup, vec4<i32>>(), W_str, 0_u)); |
| b.ir.SetName(w_str, "w_str"); |
| auto* w_arr0 = b.Call(fn_w, b.Access(ty.ptr<workgroup, vec4<i32>>(), W_arr, 0_i)); |
| b.ir.SetName(w_arr0, "w_arr0"); |
| auto* w_arr1 = b.Call(fn_w, b.Access(ty.ptr<workgroup, vec4<i32>>(), W_arr, 1_i)); |
| b.ir.SetName(w_arr1, "w_arr1"); |
| auto* w_arrI = b.Call(fn_w, b.Access(ty.ptr<workgroup, vec4<i32>>(), W_arr, I)); |
| b.ir.SetName(w_arrI, "w_arrI"); |
| auto* w_arr1_arr0 = |
| b.Call(fn_w, b.Access(ty.ptr<workgroup, vec4<i32>>(), W_arr_arr, 1_i, 0_i)); |
| b.ir.SetName(w_arr1_arr0, "w_arr1_arr0"); |
| auto* w_arr2_arrI = |
| b.Call(fn_w, b.Access(ty.ptr<workgroup, vec4<i32>>(), W_arr_arr, 2_i, I)); |
| b.ir.SetName(w_arr2_arrI, "w_arr2_arrI"); |
| auto* w_arrI_arr2 = |
| b.Call(fn_w, b.Access(ty.ptr<workgroup, vec4<i32>>(), W_arr_arr, I, 2_i)); |
| b.ir.SetName(w_arrI_arr2, "w_arrI_arr2"); |
| auto* w_arrI_arrJ = b.Call(fn_w, b.Access(ty.ptr<workgroup, vec4<i32>>(), W_arr_arr, I, J)); |
| b.ir.SetName(w_arrI_arrJ, "w_arrI_arrJ"); |
| |
| b.Return(fn_b); |
| }); |
| |
| auto* src = R"( |
| str = struct @align(16) { |
| i:vec4<i32> @offset(0) |
| } |
| |
| $B1: { # root |
| %U:ptr<uniform, vec4<i32>, read> = var @binding_point(0, 0) |
| %U_str:ptr<uniform, str, read> = var @binding_point(0, 1) |
| %U_arr:ptr<uniform, array<vec4<i32>, 8>, read> = var @binding_point(0, 2) |
| %U_arr_arr:ptr<uniform, array<array<vec4<i32>, 8>, 4>, read> = var @binding_point(0, 3) |
| %S:ptr<storage, vec4<i32>, read> = var @binding_point(1, 0) |
| %S_str:ptr<storage, str, read> = var @binding_point(1, 1) |
| %S_arr:ptr<storage, array<vec4<i32>, 8>, read> = var @binding_point(1, 2) |
| %S_arr_arr:ptr<storage, array<array<vec4<i32>, 8>, 4>, read> = var @binding_point(1, 3) |
| %W:ptr<workgroup, vec4<i32>, read_write> = var |
| %W_str:ptr<workgroup, str, read_write> = var |
| %W_arr:ptr<workgroup, array<vec4<i32>, 8>, read_write> = var |
| %W_arr_arr:ptr<workgroup, array<array<vec4<i32>, 8>, 4>, read_write> = var |
| } |
| |
| %fn_u = func(%p:ptr<uniform, vec4<i32>, read>):vec4<i32> { |
| $B2: { |
| %15:vec4<i32> = load %p |
| ret %15 |
| } |
| } |
| %fn_s = func(%p_1:ptr<storage, vec4<i32>, read>):vec4<i32> { # %p_1: 'p' |
| $B3: { |
| %18:vec4<i32> = load %p_1 |
| ret %18 |
| } |
| } |
| %fn_w = func(%p_2:ptr<workgroup, vec4<i32>, read_write>):vec4<i32> { # %p_2: 'p' |
| $B4: { |
| %21:vec4<i32> = load %p_2 |
| ret %21 |
| } |
| } |
| %b = func():void { |
| $B5: { |
| %I:i32 = let 3i |
| %J:i32 = let 4i |
| %u:vec4<i32> = call %fn_u, %U |
| %26:ptr<uniform, vec4<i32>, read> = access %U_str, 0u |
| %u_str:vec4<i32> = call %fn_u, %26 |
| %28:ptr<uniform, vec4<i32>, read> = access %U_arr, 0i |
| %u_arr0:vec4<i32> = call %fn_u, %28 |
| %30:ptr<uniform, vec4<i32>, read> = access %U_arr, 1i |
| %u_arr1:vec4<i32> = call %fn_u, %30 |
| %32:ptr<uniform, vec4<i32>, read> = access %U_arr, %I |
| %u_arrI:vec4<i32> = call %fn_u, %32 |
| %34:ptr<uniform, vec4<i32>, read> = access %U_arr_arr, 1i, 0i |
| %u_arr1_arr0:vec4<i32> = call %fn_u, %34 |
| %36:ptr<uniform, vec4<i32>, read> = access %U_arr_arr, 2i, %I |
| %u_arr2_arrI:vec4<i32> = call %fn_u, %36 |
| %38:ptr<uniform, vec4<i32>, read> = access %U_arr_arr, %I, 2i |
| %u_arrI_arr2:vec4<i32> = call %fn_u, %38 |
| %40:ptr<uniform, vec4<i32>, read> = access %U_arr_arr, %I, %J |
| %u_arrI_arrJ:vec4<i32> = call %fn_u, %40 |
| %s:vec4<i32> = call %fn_s, %S |
| %43:ptr<storage, vec4<i32>, read> = access %S_str, 0u |
| %s_str:vec4<i32> = call %fn_s, %43 |
| %45:ptr<storage, vec4<i32>, read> = access %S_arr, 0i |
| %s_arr0:vec4<i32> = call %fn_s, %45 |
| %47:ptr<storage, vec4<i32>, read> = access %S_arr, 1i |
| %s_arr1:vec4<i32> = call %fn_s, %47 |
| %49:ptr<storage, vec4<i32>, read> = access %S_arr, %I |
| %s_arrI:vec4<i32> = call %fn_s, %49 |
| %51:ptr<storage, vec4<i32>, read> = access %S_arr_arr, 1i, 0i |
| %s_arr1_arr0:vec4<i32> = call %fn_s, %51 |
| %53:ptr<storage, vec4<i32>, read> = access %S_arr_arr, 2i, %I |
| %s_arr2_arrI:vec4<i32> = call %fn_s, %53 |
| %55:ptr<storage, vec4<i32>, read> = access %S_arr_arr, %I, 2i |
| %s_arrI_arr2:vec4<i32> = call %fn_s, %55 |
| %57:ptr<storage, vec4<i32>, read> = access %S_arr_arr, %I, %J |
| %s_arrI_arrJ:vec4<i32> = call %fn_s, %57 |
| %w:vec4<i32> = call %fn_w, %W |
| %60:ptr<workgroup, vec4<i32>, read_write> = access %W_str, 0u |
| %w_str:vec4<i32> = call %fn_w, %60 |
| %62:ptr<workgroup, vec4<i32>, read_write> = access %W_arr, 0i |
| %w_arr0:vec4<i32> = call %fn_w, %62 |
| %64:ptr<workgroup, vec4<i32>, read_write> = access %W_arr, 1i |
| %w_arr1:vec4<i32> = call %fn_w, %64 |
| %66:ptr<workgroup, vec4<i32>, read_write> = access %W_arr, %I |
| %w_arrI:vec4<i32> = call %fn_w, %66 |
| %68:ptr<workgroup, vec4<i32>, read_write> = access %W_arr_arr, 1i, 0i |
| %w_arr1_arr0:vec4<i32> = call %fn_w, %68 |
| %70:ptr<workgroup, vec4<i32>, read_write> = access %W_arr_arr, 2i, %I |
| %w_arr2_arrI:vec4<i32> = call %fn_w, %70 |
| %72:ptr<workgroup, vec4<i32>, read_write> = access %W_arr_arr, %I, 2i |
| %w_arrI_arr2:vec4<i32> = call %fn_w, %72 |
| %74:ptr<workgroup, vec4<i32>, read_write> = access %W_arr_arr, %I, %J |
| %w_arrI_arrJ:vec4<i32> = call %fn_w, %74 |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| str = struct @align(16) { |
| i:vec4<i32> @offset(0) |
| } |
| |
| $B1: { # root |
| %U:ptr<uniform, vec4<i32>, read> = var @binding_point(0, 0) |
| %U_str:ptr<uniform, str, read> = var @binding_point(0, 1) |
| %U_arr:ptr<uniform, array<vec4<i32>, 8>, read> = var @binding_point(0, 2) |
| %U_arr_arr:ptr<uniform, array<array<vec4<i32>, 8>, 4>, read> = var @binding_point(0, 3) |
| %S:ptr<storage, vec4<i32>, read> = var @binding_point(1, 0) |
| %S_str:ptr<storage, str, read> = var @binding_point(1, 1) |
| %S_arr:ptr<storage, array<vec4<i32>, 8>, read> = var @binding_point(1, 2) |
| %S_arr_arr:ptr<storage, array<array<vec4<i32>, 8>, 4>, read> = var @binding_point(1, 3) |
| %W:ptr<workgroup, vec4<i32>, read_write> = var |
| %W_str:ptr<workgroup, str, read_write> = var |
| %W_arr:ptr<workgroup, array<vec4<i32>, 8>, read_write> = var |
| %W_arr_arr:ptr<workgroup, array<array<vec4<i32>, 8>, 4>, read_write> = var |
| } |
| |
| %fn_u = func():vec4<i32> { |
| $B2: { |
| %14:ptr<uniform, vec4<i32>, read> = access %U |
| %15:vec4<i32> = load %14 |
| ret %15 |
| } |
| } |
| %fn_u_1 = func():vec4<i32> { # %fn_u_1: 'fn_u' |
| $B3: { |
| %17:ptr<uniform, vec4<i32>, read> = access %U_str, 0u |
| %18:vec4<i32> = load %17 |
| ret %18 |
| } |
| } |
| %fn_u_2 = func(%p_indices:array<u32, 1>):vec4<i32> { # %fn_u_2: 'fn_u' |
| $B4: { |
| %21:u32 = access %p_indices, 0u |
| %22:ptr<uniform, vec4<i32>, read> = access %U_arr, %21 |
| %23:vec4<i32> = load %22 |
| ret %23 |
| } |
| } |
| %fn_u_3 = func(%p_indices_1:array<u32, 2>):vec4<i32> { # %fn_u_3: 'fn_u', %p_indices_1: 'p_indices' |
| $B5: { |
| %26:u32 = access %p_indices_1, 0u |
| %27:u32 = access %p_indices_1, 1u |
| %28:ptr<uniform, vec4<i32>, read> = access %U_arr_arr, %26, %27 |
| %29:vec4<i32> = load %28 |
| ret %29 |
| } |
| } |
| %fn_s = func():vec4<i32> { |
| $B6: { |
| %31:ptr<storage, vec4<i32>, read> = access %S |
| %32:vec4<i32> = load %31 |
| ret %32 |
| } |
| } |
| %fn_s_1 = func():vec4<i32> { # %fn_s_1: 'fn_s' |
| $B7: { |
| %34:ptr<storage, vec4<i32>, read> = access %S_str, 0u |
| %35:vec4<i32> = load %34 |
| ret %35 |
| } |
| } |
| %fn_s_2 = func(%p_indices_2:array<u32, 1>):vec4<i32> { # %fn_s_2: 'fn_s', %p_indices_2: 'p_indices' |
| $B8: { |
| %38:u32 = access %p_indices_2, 0u |
| %39:ptr<storage, vec4<i32>, read> = access %S_arr, %38 |
| %40:vec4<i32> = load %39 |
| ret %40 |
| } |
| } |
| %fn_s_3 = func(%p_indices_3:array<u32, 2>):vec4<i32> { # %fn_s_3: 'fn_s', %p_indices_3: 'p_indices' |
| $B9: { |
| %43:u32 = access %p_indices_3, 0u |
| %44:u32 = access %p_indices_3, 1u |
| %45:ptr<storage, vec4<i32>, read> = access %S_arr_arr, %43, %44 |
| %46:vec4<i32> = load %45 |
| ret %46 |
| } |
| } |
| %fn_w = func():vec4<i32> { |
| $B10: { |
| %48:ptr<workgroup, vec4<i32>, read_write> = access %W |
| %49:vec4<i32> = load %48 |
| ret %49 |
| } |
| } |
| %fn_w_1 = func():vec4<i32> { # %fn_w_1: 'fn_w' |
| $B11: { |
| %51:ptr<workgroup, vec4<i32>, read_write> = access %W_str, 0u |
| %52:vec4<i32> = load %51 |
| ret %52 |
| } |
| } |
| %fn_w_2 = func(%p_indices_4:array<u32, 1>):vec4<i32> { # %fn_w_2: 'fn_w', %p_indices_4: 'p_indices' |
| $B12: { |
| %55:u32 = access %p_indices_4, 0u |
| %56:ptr<workgroup, vec4<i32>, read_write> = access %W_arr, %55 |
| %57:vec4<i32> = load %56 |
| ret %57 |
| } |
| } |
| %fn_w_3 = func(%p_indices_5:array<u32, 2>):vec4<i32> { # %fn_w_3: 'fn_w', %p_indices_5: 'p_indices' |
| $B13: { |
| %60:u32 = access %p_indices_5, 0u |
| %61:u32 = access %p_indices_5, 1u |
| %62:ptr<workgroup, vec4<i32>, read_write> = access %W_arr_arr, %60, %61 |
| %63:vec4<i32> = load %62 |
| ret %63 |
| } |
| } |
| %b = func():void { |
| $B14: { |
| %I:i32 = let 3i |
| %J:i32 = let 4i |
| %u:vec4<i32> = call %fn_u |
| %u_str:vec4<i32> = call %fn_u_1 |
| %69:u32 = convert 0i |
| %70:array<u32, 1> = construct %69 |
| %u_arr0:vec4<i32> = call %fn_u_2, %70 |
| %72:u32 = convert 1i |
| %73:array<u32, 1> = construct %72 |
| %u_arr1:vec4<i32> = call %fn_u_2, %73 |
| %75:u32 = convert %I |
| %76:array<u32, 1> = construct %75 |
| %u_arrI:vec4<i32> = call %fn_u_2, %76 |
| %78:u32 = convert 1i |
| %79:u32 = convert 0i |
| %80:array<u32, 2> = construct %78, %79 |
| %u_arr1_arr0:vec4<i32> = call %fn_u_3, %80 |
| %82:u32 = convert 2i |
| %83:u32 = convert %I |
| %84:array<u32, 2> = construct %82, %83 |
| %u_arr2_arrI:vec4<i32> = call %fn_u_3, %84 |
| %86:u32 = convert %I |
| %87:u32 = convert 2i |
| %88:array<u32, 2> = construct %86, %87 |
| %u_arrI_arr2:vec4<i32> = call %fn_u_3, %88 |
| %90:u32 = convert %I |
| %91:u32 = convert %J |
| %92:array<u32, 2> = construct %90, %91 |
| %u_arrI_arrJ:vec4<i32> = call %fn_u_3, %92 |
| %s:vec4<i32> = call %fn_s |
| %s_str:vec4<i32> = call %fn_s_1 |
| %96:u32 = convert 0i |
| %97:array<u32, 1> = construct %96 |
| %s_arr0:vec4<i32> = call %fn_s_2, %97 |
| %99:u32 = convert 1i |
| %100:array<u32, 1> = construct %99 |
| %s_arr1:vec4<i32> = call %fn_s_2, %100 |
| %102:u32 = convert %I |
| %103:array<u32, 1> = construct %102 |
| %s_arrI:vec4<i32> = call %fn_s_2, %103 |
| %105:u32 = convert 1i |
| %106:u32 = convert 0i |
| %107:array<u32, 2> = construct %105, %106 |
| %s_arr1_arr0:vec4<i32> = call %fn_s_3, %107 |
| %109:u32 = convert 2i |
| %110:u32 = convert %I |
| %111:array<u32, 2> = construct %109, %110 |
| %s_arr2_arrI:vec4<i32> = call %fn_s_3, %111 |
| %113:u32 = convert %I |
| %114:u32 = convert 2i |
| %115:array<u32, 2> = construct %113, %114 |
| %s_arrI_arr2:vec4<i32> = call %fn_s_3, %115 |
| %117:u32 = convert %I |
| %118:u32 = convert %J |
| %119:array<u32, 2> = construct %117, %118 |
| %s_arrI_arrJ:vec4<i32> = call %fn_s_3, %119 |
| %w:vec4<i32> = call %fn_w |
| %w_str:vec4<i32> = call %fn_w_1 |
| %123:u32 = convert 0i |
| %124:array<u32, 1> = construct %123 |
| %w_arr0:vec4<i32> = call %fn_w_2, %124 |
| %126:u32 = convert 1i |
| %127:array<u32, 1> = construct %126 |
| %w_arr1:vec4<i32> = call %fn_w_2, %127 |
| %129:u32 = convert %I |
| %130:array<u32, 1> = construct %129 |
| %w_arrI:vec4<i32> = call %fn_w_2, %130 |
| %132:u32 = convert 1i |
| %133:u32 = convert 0i |
| %134:array<u32, 2> = construct %132, %133 |
| %w_arr1_arr0:vec4<i32> = call %fn_w_3, %134 |
| %136:u32 = convert 2i |
| %137:u32 = convert %I |
| %138:array<u32, 2> = construct %136, %137 |
| %w_arr2_arrI:vec4<i32> = call %fn_w_3, %138 |
| %140:u32 = convert %I |
| %141:u32 = convert 2i |
| %142:array<u32, 2> = construct %140, %141 |
| %w_arrI_arr2:vec4<i32> = call %fn_w_3, %142 |
| %144:u32 = convert %I |
| %145:u32 = convert %J |
| %146:array<u32, 2> = construct %144, %145 |
| %w_arrI_arrJ:vec4<i32> = call %fn_w_3, %146 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_Complex, Indexing) { |
| Var* S = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| S = b.Var<storage, array<array<array<array<i32, 9>, 9>, 9>, 50>, read>("S"); |
| S->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_i = b.FunctionParam("i", ty.i32()); |
| fn_a->SetParams({fn_a_i}); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, fn_a_i); }); |
| |
| auto* fn_b = b.Function("b", ty.i32()); |
| auto* fn_b_p = b.FunctionParam("p", ty.ptr<storage, array<array<array<i32, 9>, 9>, 9>, read>()); |
| fn_b->SetParams({fn_b_p}); |
| b.Append(fn_b->Block(), [&] { |
| auto load_0 = b.Load(b.Access(ty.ptr<storage, i32, read>(), fn_b_p, 0_i, 1_i, 2_i)); |
| auto call_0 = b.Call(fn_a, load_0); |
| auto call_1 = b.Call(fn_a, 3_i); |
| auto load_1 = b.Load(b.Access(ty.ptr<storage, i32, read>(), fn_b_p, call_1, 4_i, 5_i)); |
| auto call_2 = b.Call(fn_a, load_1); |
| auto call_3 = b.Call(fn_a, 7_i); |
| auto load_2 = b.Load(b.Access(ty.ptr<storage, i32, read>(), fn_b_p, 6_i, call_3, 8_i)); |
| auto call_4 = b.Call(fn_a, load_2); |
| auto load_3 = |
| b.Load(b.Access(ty.ptr<storage, i32, read>(), fn_b_p, call_0, call_2, call_4)); |
| |
| b.Return(fn_b, load_3); |
| }); |
| |
| auto* fn_c = b.Function("c", ty.void_()); |
| b.Append(fn_c->Block(), [&] { |
| auto* access = |
| b.Access(ty.ptr<storage, array<array<array<i32, 9>, 9>, 9>, read>(), S, 42_i); |
| auto* v = b.Call(fn_b, access); |
| b.ir.SetName(v, "v"); |
| b.Return(fn_c); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %S:ptr<storage, array<array<array<array<i32, 9>, 9>, 9>, 50>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%i:i32):i32 { |
| $B2: { |
| ret %i |
| } |
| } |
| %b = func(%p:ptr<storage, array<array<array<i32, 9>, 9>, 9>, read>):i32 { |
| $B3: { |
| %6:ptr<storage, i32, read> = access %p, 0i, 1i, 2i |
| %7:i32 = load %6 |
| %8:i32 = call %a, %7 |
| %9:i32 = call %a, 3i |
| %10:ptr<storage, i32, read> = access %p, %9, 4i, 5i |
| %11:i32 = load %10 |
| %12:i32 = call %a, %11 |
| %13:i32 = call %a, 7i |
| %14:ptr<storage, i32, read> = access %p, 6i, %13, 8i |
| %15:i32 = load %14 |
| %16:i32 = call %a, %15 |
| %17:ptr<storage, i32, read> = access %p, %8, %12, %16 |
| %18:i32 = load %17 |
| ret %18 |
| } |
| } |
| %c = func():void { |
| $B4: { |
| %20:ptr<storage, array<array<array<i32, 9>, 9>, 9>, read> = access %S, 42i |
| %v:i32 = call %b, %20 |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %S:ptr<storage, array<array<array<array<i32, 9>, 9>, 9>, 50>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%i:i32):i32 { |
| $B2: { |
| ret %i |
| } |
| } |
| %b = func(%p_indices:array<u32, 1>):i32 { |
| $B3: { |
| %6:u32 = access %p_indices, 0u |
| %7:ptr<storage, array<array<array<i32, 9>, 9>, 9>, read> = access %S, %6 |
| %8:ptr<storage, i32, read> = access %7, 0i, 1i, 2i |
| %9:i32 = load %8 |
| %10:i32 = call %a, %9 |
| %11:i32 = call %a, 3i |
| %12:ptr<storage, i32, read> = access %7, %11, 4i, 5i |
| %13:i32 = load %12 |
| %14:i32 = call %a, %13 |
| %15:i32 = call %a, 7i |
| %16:ptr<storage, i32, read> = access %7, 6i, %15, 8i |
| %17:i32 = load %16 |
| %18:i32 = call %a, %17 |
| %19:ptr<storage, i32, read> = access %7, %10, %14, %18 |
| %20:i32 = load %19 |
| ret %20 |
| } |
| } |
| %c = func():void { |
| $B4: { |
| %22:u32 = convert 42i |
| %23:array<u32, 1> = construct %22 |
| %v:i32 = call %b, %23 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_Complex, IndexingInPtrCall) { |
| Var* S = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| S = b.Var<storage, array<array<array<array<i32, 9>, 9>, 9>, 50>, read>("S"); |
| S->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_i = b.FunctionParam("i", ty.ptr<storage, i32, read>()); |
| fn_a->SetParams({ |
| b.FunctionParam("pre", ty.i32()), |
| fn_a_i, |
| b.FunctionParam("post", ty.i32()), |
| }); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, b.Load(fn_a_i)); }); |
| |
| auto* fn_b = b.Function("b", ty.i32()); |
| auto* fn_b_p = b.FunctionParam("p", ty.ptr<storage, array<array<array<i32, 9>, 9>, 9>, read>()); |
| fn_b->SetParams({fn_b_p}); |
| b.Append(fn_b->Block(), [&] { |
| auto access_0 = b.Access(ty.ptr<storage, i32, read>(), fn_b_p, 0_i, 1_i, 2_i); |
| auto call_0 = b.Call(fn_a, 20_i, access_0, 30_i); |
| |
| auto access_1 = b.Access(ty.ptr<storage, i32, read>(), fn_b_p, 3_i, 4_i, 5_i); |
| auto call_1 = b.Call(fn_a, 40_i, access_1, 50_i); |
| |
| auto access_2 = b.Access(ty.ptr<storage, i32, read>(), fn_b_p, 6_i, 7_i, 8_i); |
| auto call_2 = b.Call(fn_a, 60_i, access_2, 70_i); |
| |
| auto access_3 = b.Access(ty.ptr<storage, i32, read>(), fn_b_p, call_0, call_1, call_2); |
| auto call_3 = b.Call(fn_a, 10_i, access_3, 80_i); |
| |
| b.Return(fn_b, call_3); |
| }); |
| |
| auto* fn_c = b.Function("c", ty.void_()); |
| b.Append(fn_c->Block(), [&] { |
| auto* access = |
| b.Access(ty.ptr<storage, array<array<array<i32, 9>, 9>, 9>, read>(), S, 42_i); |
| auto* v = b.Call(fn_b, access); |
| b.ir.SetName(v, "v"); |
| b.Return(fn_c); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %S:ptr<storage, array<array<array<array<i32, 9>, 9>, 9>, 50>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %i:ptr<storage, i32, read>, %post:i32):i32 { |
| $B2: { |
| %6:i32 = load %i |
| ret %6 |
| } |
| } |
| %b = func(%p:ptr<storage, array<array<array<i32, 9>, 9>, 9>, read>):i32 { |
| $B3: { |
| %9:ptr<storage, i32, read> = access %p, 0i, 1i, 2i |
| %10:i32 = call %a, 20i, %9, 30i |
| %11:ptr<storage, i32, read> = access %p, 3i, 4i, 5i |
| %12:i32 = call %a, 40i, %11, 50i |
| %13:ptr<storage, i32, read> = access %p, 6i, 7i, 8i |
| %14:i32 = call %a, 60i, %13, 70i |
| %15:ptr<storage, i32, read> = access %p, %10, %12, %14 |
| %16:i32 = call %a, 10i, %15, 80i |
| ret %16 |
| } |
| } |
| %c = func():void { |
| $B4: { |
| %18:ptr<storage, array<array<array<i32, 9>, 9>, 9>, read> = access %S, 42i |
| %v:i32 = call %b, %18 |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %S:ptr<storage, array<array<array<array<i32, 9>, 9>, 9>, 50>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%pre:i32, %i_indices:array<u32, 4>, %post:i32):i32 { |
| $B2: { |
| %6:u32 = access %i_indices, 0u |
| %7:u32 = access %i_indices, 1u |
| %8:u32 = access %i_indices, 2u |
| %9:u32 = access %i_indices, 3u |
| %10:ptr<storage, i32, read> = access %S, %6, %7, %8, %9 |
| %11:i32 = load %10 |
| ret %11 |
| } |
| } |
| %b = func(%p_indices:array<u32, 1>):i32 { |
| $B3: { |
| %14:u32 = access %p_indices, 0u |
| %15:u32 = convert 0i |
| %16:u32 = convert 1i |
| %17:u32 = convert 2i |
| %18:array<u32, 4> = construct %14, %15, %16, %17 |
| %19:i32 = call %a, 20i, %18, 30i |
| %20:u32 = convert 3i |
| %21:u32 = convert 4i |
| %22:u32 = convert 5i |
| %23:array<u32, 4> = construct %14, %20, %21, %22 |
| %24:i32 = call %a, 40i, %23, 50i |
| %25:u32 = convert 6i |
| %26:u32 = convert 7i |
| %27:u32 = convert 8i |
| %28:array<u32, 4> = construct %14, %25, %26, %27 |
| %29:i32 = call %a, 60i, %28, 70i |
| %30:u32 = convert %19 |
| %31:u32 = convert %24 |
| %32:u32 = convert %29 |
| %33:array<u32, 4> = construct %14, %30, %31, %32 |
| %34:i32 = call %a, 10i, %33, 80i |
| ret %34 |
| } |
| } |
| %c = func():void { |
| $B4: { |
| %36:u32 = convert 42i |
| %37:array<u32, 1> = construct %36 |
| %v:i32 = call %b, %37 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| TEST_F(IR_DirectVariableAccessTest_Complex, IndexingDualPointers) { |
| Var* S = nullptr; |
| Var* U = nullptr; |
| b.Append(b.ir.root_block, |
| [&] { // |
| S = b.Var<storage, array<array<array<i32, 9>, 9>, 50>, read>("S"); |
| S->SetBindingPoint(0, 0); |
| U = b.Var<uniform, array<array<array<vec4<i32>, 9>, 9>, 50>, read>("U"); |
| U->SetBindingPoint(0, 0); |
| }); |
| |
| auto* fn_a = b.Function("a", ty.i32()); |
| auto* fn_a_i = b.FunctionParam("i", ty.i32()); |
| fn_a->SetParams({fn_a_i}); |
| b.Append(fn_a->Block(), [&] { b.Return(fn_a, fn_a_i); }); |
| |
| auto* fn_b = b.Function("b", ty.i32()); |
| auto* fn_b_s = b.FunctionParam("s", ty.ptr<storage, array<array<i32, 9>, 9>, read>()); |
| auto* fn_b_u = b.FunctionParam("u", ty.ptr<uniform, array<array<vec4<i32>, 9>, 9>, read>()); |
| fn_b->SetParams({fn_b_s, fn_b_u}); |
| b.Append(fn_b->Block(), [&] { |
| auto access_0 = b.Access(ty.ptr<uniform, vec4<i32>, read>(), fn_b_u, 0_i, 1_i); |
| auto call_0 = b.Call(fn_a, b.LoadVectorElement(access_0, 0_u)); |
| auto call_1 = b.Call(fn_a, 3_i); |
| |
| auto access_1 = b.Access(ty.ptr<uniform, vec4<i32>, read>(), fn_b_u, call_1, 4_i); |
| auto call_2 = b.Call(fn_a, b.LoadVectorElement(access_1, 1_u)); |
| |
| auto access_2 = b.Access(ty.ptr<storage, i32, read>(), fn_b_s, call_0, call_2); |
| |
| b.Return(fn_b, b.Load(access_2)); |
| }); |
| |
| auto* fn_c = b.Function("c", ty.void_()); |
| b.Append(fn_c->Block(), [&] { |
| auto* access_0 = b.Access(ty.ptr<storage, array<array<i32, 9>, 9>, read>(), S, 42_i); |
| auto* access_1 = b.Access(ty.ptr<uniform, array<array<vec4<i32>, 9>, 9>, read>(), U, 24_i); |
| auto* v = b.Call(fn_b, access_0, access_1); |
| b.ir.SetName(v, "v"); |
| b.Return(fn_c); |
| }); |
| |
| auto* src = R"( |
| $B1: { # root |
| %S:ptr<storage, array<array<array<i32, 9>, 9>, 50>, read> = var @binding_point(0, 0) |
| %U:ptr<uniform, array<array<array<vec4<i32>, 9>, 9>, 50>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%i:i32):i32 { |
| $B2: { |
| ret %i |
| } |
| } |
| %b = func(%s:ptr<storage, array<array<i32, 9>, 9>, read>, %u:ptr<uniform, array<array<vec4<i32>, 9>, 9>, read>):i32 { |
| $B3: { |
| %8:ptr<uniform, vec4<i32>, read> = access %u, 0i, 1i |
| %9:i32 = load_vector_element %8, 0u |
| %10:i32 = call %a, %9 |
| %11:i32 = call %a, 3i |
| %12:ptr<uniform, vec4<i32>, read> = access %u, %11, 4i |
| %13:i32 = load_vector_element %12, 1u |
| %14:i32 = call %a, %13 |
| %15:ptr<storage, i32, read> = access %s, %10, %14 |
| %16:i32 = load %15 |
| ret %16 |
| } |
| } |
| %c = func():void { |
| $B4: { |
| %18:ptr<storage, array<array<i32, 9>, 9>, read> = access %S, 42i |
| %19:ptr<uniform, array<array<vec4<i32>, 9>, 9>, read> = access %U, 24i |
| %v:i32 = call %b, %18, %19 |
| ret |
| } |
| } |
| )"; |
| |
| EXPECT_EQ(src, str()); |
| |
| auto* expect = R"( |
| $B1: { # root |
| %S:ptr<storage, array<array<array<i32, 9>, 9>, 50>, read> = var @binding_point(0, 0) |
| %U:ptr<uniform, array<array<array<vec4<i32>, 9>, 9>, 50>, read> = var @binding_point(0, 0) |
| } |
| |
| %a = func(%i:i32):i32 { |
| $B2: { |
| ret %i |
| } |
| } |
| %b = func(%s_indices:array<u32, 1>, %u_indices:array<u32, 1>):i32 { |
| $B3: { |
| %8:u32 = access %s_indices, 0u |
| %9:ptr<storage, array<array<i32, 9>, 9>, read> = access %S, %8 |
| %10:u32 = access %u_indices, 0u |
| %11:ptr<uniform, array<array<vec4<i32>, 9>, 9>, read> = access %U, %10 |
| %12:ptr<uniform, vec4<i32>, read> = access %11, 0i, 1i |
| %13:i32 = load_vector_element %12, 0u |
| %14:i32 = call %a, %13 |
| %15:i32 = call %a, 3i |
| %16:ptr<uniform, vec4<i32>, read> = access %11, %15, 4i |
| %17:i32 = load_vector_element %16, 1u |
| %18:i32 = call %a, %17 |
| %19:ptr<storage, i32, read> = access %9, %14, %18 |
| %20:i32 = load %19 |
| ret %20 |
| } |
| } |
| %c = func():void { |
| $B4: { |
| %22:u32 = convert 42i |
| %23:array<u32, 1> = construct %22 |
| %24:u32 = convert 24i |
| %25:array<u32, 1> = construct %24 |
| %v:i32 = call %b, %23, %25 |
| ret |
| } |
| } |
| )"; |
| |
| Run(DirectVariableAccess, DirectVariableAccessOptions{}); |
| |
| EXPECT_EQ(expect, str()); |
| } |
| |
| } // namespace complex_tests |
| |
| } // namespace |
| } // namespace tint::core::ir::transform |