// Copyright 2022 The Tint Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "src/tint/transform/direct_variable_access.h" #include #include #include "src/tint/transform/test_helper.h" #include "src/tint/utils/string.h" namespace tint::transform { namespace { /// @returns a DataMap with DirectVariableAccess::Config::transform_private enabled. static DataMap EnablePrivate() { DirectVariableAccess::Options opts; opts.transform_private = true; DataMap inputs; inputs.Add(opts); return inputs; } /// @returns a DataMap with DirectVariableAccess::Config::transform_function enabled. static DataMap EnableFunction() { DirectVariableAccess::Options opts; opts.transform_function = true; DataMap inputs; inputs.Add(opts); return inputs; } //////////////////////////////////////////////////////////////////////////////// // ShouldRun tests //////////////////////////////////////////////////////////////////////////////// namespace should_run { using DirectVariableAccessShouldRunTest = TransformTest; TEST_F(DirectVariableAccessShouldRunTest, EmptyModule) { auto* src = R"()"; EXPECT_FALSE(ShouldRun(src)); } } // namespace should_run //////////////////////////////////////////////////////////////////////////////// // remove uncalled //////////////////////////////////////////////////////////////////////////////// namespace remove_uncalled { using DirectVariableAccessRemoveUncalledTest = TransformTest; TEST_F(DirectVariableAccessRemoveUncalledTest, PtrUniform) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; fn u(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessRemoveUncalledTest, PtrStorage) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; fn s(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessRemoveUncalledTest, PtrWorkgroup) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; fn w(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessRemoveUncalledTest, PtrPrivate_Disabled) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; fn f(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessRemoveUncalledTest, PtrPrivate_Enabled) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; )"; auto* expect = src; auto got = Run(src, EnablePrivate()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessRemoveUncalledTest, PtrFunction_Disabled) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; fn f(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessRemoveUncalledTest, PtrFunction_Enabled) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; var keep_me = 42; )"; auto* expect = src; auto got = Run(src, EnableFunction()); EXPECT_EQ(expect, str(got)); } } // namespace remove_uncalled //////////////////////////////////////////////////////////////////////////////// // pointer chains //////////////////////////////////////////////////////////////////////////////// namespace pointer_chains_tests { using DirectVariableAccessPtrChainsTest = TransformTest; TEST_F(DirectVariableAccessPtrChainsTest, ConstantIndices) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>, 8>; fn a(pre : i32, p : ptr>, post : i32) -> vec4 { return *p; } fn b() { let p0 = &U; let p1 = &(*p0)[1]; let p2 = &(*p1)[1+1]; let p3 = &(*p2)[2*2 - 1]; a(10, p3, 20); } fn c(p : ptr, 8>, 8>, 8>>) { let p0 = p; let p1 = &(*p0)[1]; let p2 = &(*p1)[1+1]; let p3 = &(*p2)[2*2 - 1]; a(10, p3, 20); } fn d() { c(&U); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>, 8>; alias U_X_X_X = array; fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4 { return U[p[0]][p[1]][p[2]]; } fn b() { let p0 = &(U); let p1 = &((*(p0))[1]); let p2 = &((*(p1))[(1 + 1)]); let p3 = &((*(p2))[((2 * 2) - 1)]); a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20); } fn c_U() { let p0 = &(U); let p1 = &(U[1]); let p2 = &(U[1][2]); let p3 = &(U[1][2][3]); a_U_X_X_X(10, U_X_X_X(1, 2, 3), 20); } fn d() { c_U(); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPtrChainsTest, HoistIndices) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } fn third() -> i32 { i++; return i; } fn a(pre : i32, p : ptr>, post : i32) -> vec4 { return *p; } fn b() { let p0 = &U; let p1 = &(*p0)[first()]; let p2 = &(*p1)[second()][third()]; a(10, p2, 20); } fn c(p : ptr, 8>, 8>, 8>>) { let p0 = &U; let p1 = &(*p0)[first()]; let p2 = &(*p1)[second()][third()]; a(10, p2, 20); } fn d() { c(&U); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } fn third() -> i32 { i++; return i; } alias U_X_X_X = array; fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4 { return U[p[0]][p[1]][p[2]]; } fn b() { let p0 = &(U); let ptr_index_save = first(); let p1 = &((*(p0))[ptr_index_save]); let ptr_index_save_1 = second(); let ptr_index_save_2 = third(); let p2 = &((*(p1))[ptr_index_save_1][ptr_index_save_2]); a_U_X_X_X(10, U_X_X_X(u32(ptr_index_save), u32(ptr_index_save_1), u32(ptr_index_save_2)), 20); } fn c_U() { let p0 = &(U); let ptr_index_save_3 = first(); let p1 = &((*(p0))[ptr_index_save_3]); let ptr_index_save_4 = second(); let ptr_index_save_5 = third(); let p2 = &((*(p1))[ptr_index_save_4][ptr_index_save_5]); a_U_X_X_X(10, U_X_X_X(u32(ptr_index_save_3), u32(ptr_index_save_4), u32(ptr_index_save_5)), 20); } fn d() { c_U(); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopInit) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } fn a(pre : i32, p : ptr>, post : i32) -> vec4 { return *p; } fn b() { for (let p1 = &U[first()]; true; ) { a(10, &(*p1)[second()], 20); } } fn c(p : ptr, 8>, 8>>) { for (let p1 = &(*p)[first()]; true; ) { a(10, &(*p1)[second()], 20); } } fn d() { c(&U); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } alias U_X_X = array; fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4 { return U[p[0]][p[1]]; } fn b() { let ptr_index_save = first(); for(let p1 = &(U[ptr_index_save]); true; ) { a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(second())), 20); } } fn c_U() { let ptr_index_save_1 = first(); for(let p1 = &(U[ptr_index_save_1]); true; ) { a_U_X_X(10, U_X_X(u32(ptr_index_save_1), u32(second())), 20); } } fn d() { c_U(); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopCond) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } fn a(pre : i32, p : ptr>, post : i32) -> vec4 { return *p; } fn b() { let p = &U[first()][second()]; for (; a(10, p, 20).x < 4; ) { let body = 1; } } fn c(p : ptr, 8>, 8>>) { let p2 = &(*p)[first()][second()]; for (; a(10, p2, 20).x < 4; ) { let body = 1; } } fn d() { c(&U); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } alias U_X_X = array; fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4 { return U[p[0]][p[1]]; } fn b() { let ptr_index_save = first(); let ptr_index_save_1 = second(); let p = &(U[ptr_index_save][ptr_index_save_1]); for(; (a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(ptr_index_save_1)), 20).x < 4); ) { let body = 1; } } fn c_U() { let ptr_index_save_2 = first(); let ptr_index_save_3 = second(); let p2 = &(U[ptr_index_save_2][ptr_index_save_3]); for(; (a_U_X_X(10, U_X_X(u32(ptr_index_save_2), u32(ptr_index_save_3)), 20).x < 4); ) { let body = 1; } } fn d() { c_U(); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPtrChainsTest, HoistInForLoopCont) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } fn a(pre : i32, p : ptr>, post : i32) -> vec4 { return *p; } fn b() { let p = &U[first()][second()]; for (var i = 0; i < 3; a(10, p, 20)) { i++; } } fn c(p : ptr, 8>, 8>>) { let p2 = &(*p)[first()][second()]; for (var i = 0; i < 3; a(10, p2, 20)) { i++; } } fn d() { c(&U); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } alias U_X_X = array; fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4 { return U[p[0]][p[1]]; } fn b() { let ptr_index_save = first(); let ptr_index_save_1 = second(); let p = &(U[ptr_index_save][ptr_index_save_1]); for(var i = 0; (i < 3); a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(ptr_index_save_1)), 20)) { i++; } } fn c_U() { let ptr_index_save_2 = first(); let ptr_index_save_3 = second(); let p2 = &(U[ptr_index_save_2][ptr_index_save_3]); for(var i = 0; (i < 3); a_U_X_X(10, U_X_X(u32(ptr_index_save_2), u32(ptr_index_save_3)), 20)) { i++; } } fn d() { c_U(); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPtrChainsTest, HoistInWhileCond) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } fn a(pre : i32, p : ptr>, post : i32) -> vec4 { return *p; } fn b() { let p = &U[first()][second()]; while (a(10, p, 20).x < 4) { let body = 1; } } fn c(p : ptr, 8>, 8>>) { let p2 = &(*p)[first()][second()]; while (a(10, p2, 20).x < 4) { let body = 1; } } fn d() { c(&U); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>, 8>; var i : i32; fn first() -> i32 { i++; return i; } fn second() -> i32 { i++; return i; } alias U_X_X = array; fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4 { return U[p[0]][p[1]]; } fn b() { let ptr_index_save = first(); let ptr_index_save_1 = second(); let p = &(U[ptr_index_save][ptr_index_save_1]); while((a_U_X_X(10, U_X_X(u32(ptr_index_save), u32(ptr_index_save_1)), 20).x < 4)) { let body = 1; } } fn c_U() { let ptr_index_save_2 = first(); let ptr_index_save_3 = second(); let p2 = &(U[ptr_index_save_2][ptr_index_save_3]); while((a_U_X_X(10, U_X_X(u32(ptr_index_save_2), u32(ptr_index_save_3)), 20).x < 4)) { let body = 1; } } fn d() { c_U(); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } } // namespace pointer_chains_tests //////////////////////////////////////////////////////////////////////////////// // 'uniform' address space //////////////////////////////////////////////////////////////////////////////// namespace uniform_as_tests { using DirectVariableAccessUniformASTest = TransformTest; TEST_F(DirectVariableAccessUniformASTest, Param_ptr_i32_read) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : i32; fn a(pre : i32, p : ptr, post : i32) -> i32 { return *p; } fn b() { a(10, &U, 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : i32; fn a_U(pre : i32, post : i32) -> i32 { return U; } fn b() { a_U(10, 20); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessUniformASTest, Param_ptr_vec4i32_Via_array_DynamicRead) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>; fn a(pre : i32, p : ptr>, post : i32) -> vec4 { return *p; } fn b() { let I = 3; a(10, &U[I], 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var U : array, 8>; alias U_X = array; fn a_U_X(pre : i32, p : U_X, post : i32) -> vec4 { return U[p[0]]; } fn b() { let I = 3; a_U_X(10, U_X(u32(I)), 20); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessUniformASTest, CallChaining) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct Inner { mat : mat3x4, }; type InnerArr = array; struct Outer { arr : InnerArr, mat : mat3x4, }; @group(0) @binding(0) var U : Outer; fn f0(p : ptr>) -> f32 { return (*p).x; } fn f1(p : ptr>) -> f32 { var res : f32; { // call f0() with inline usage of p res += f0(&(*p)[1]); } { // call f0() with pointer-let usage of p let p_vec = &(*p)[1]; res += f0(p_vec); } { // call f0() with inline usage of U res += f0(&U.arr[2].mat[1]); } { // call f0() with pointer-let usage of U let p_vec = &U.arr[2].mat[1]; res += f0(p_vec); } return res; } fn f2(p : ptr) -> f32 { let p_mat = &(*p).mat; return f1(p_mat); } fn f3(p0 : ptr, p1 : ptr>) -> f32 { let p0_inner = &(*p0)[3]; return f2(p0_inner) + f1(p1); } fn f4(p : ptr) -> f32 { return f3(&(*p).arr, &U.mat); } fn b() { f4(&U); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct Inner { mat : mat3x4, } alias InnerArr = array; struct Outer { arr : InnerArr, mat : mat3x4, } @group(0) @binding(0) var U : Outer; alias U_mat_X = array; fn f0_U_mat_X(p : U_mat_X) -> f32 { return U.mat[p[0]].x; } alias U_arr_X_mat_X = array; fn f0_U_arr_X_mat_X(p : U_arr_X_mat_X) -> f32 { return U.arr[p[0]].mat[p[0]].x; } alias U_arr_X_mat_X_1 = array; fn f0_U_arr_X_mat_X_1(p : U_arr_X_mat_X_1) -> f32 { return U.arr[p[0]].mat[p[1]].x; } fn f1_U_mat() -> f32 { var res : f32; { res += f0_U_mat_X(U_mat_X(1)); } { let p_vec = &(U.mat[1]); res += f0_U_mat_X(U_mat_X(1)); } { res += f0_U_arr_X_mat_X_1(U_arr_X_mat_X_1(2, 1)); } { let p_vec = &(U.arr[2].mat[1]); res += f0_U_arr_X_mat_X_1(U_arr_X_mat_X_1(2, 1)); } return res; } alias U_arr_X_mat = array; fn f1_U_arr_X_mat(p : U_arr_X_mat) -> f32 { var res : f32; { res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1)); } { let p_vec = &(U.arr[p[0]].mat[1]); res += f0_U_arr_X_mat_X(U_arr_X_mat_X(p[0u], 1)); } { res += f0_U_arr_X_mat_X_1(U_arr_X_mat_X_1(2, 1)); } { let p_vec = &(U.arr[2].mat[1]); res += f0_U_arr_X_mat_X_1(U_arr_X_mat_X_1(2, 1)); } return res; } alias U_arr_X = array; fn f2_U_arr_X(p : U_arr_X) -> f32 { let p_mat = &(U.arr[p[0]].mat); return f1_U_arr_X_mat(U_arr_X_mat(p[0u])); } fn f3_U_arr_U_mat() -> f32 { let p0_inner = &(U.arr[3]); return (f2_U_arr_X(U_arr_X(3)) + f1_U_mat()); } fn f4_U() -> f32 { return f3_U_arr_U_mat(); } fn b() { f4_U(); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } } // namespace uniform_as_tests //////////////////////////////////////////////////////////////////////////////// // 'storage' address space //////////////////////////////////////////////////////////////////////////////// namespace storage_as_tests { using DirectVariableAccessStorageASTest = TransformTest; TEST_F(DirectVariableAccessStorageASTest, Param_ptr_i32_Via_struct_read) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, }; @group(0) @binding(0) var S : str; fn a(pre : i32, p : ptr, post : i32) -> i32 { return *p; } fn b() { a(10, &S.i, 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, } @group(0) @binding(0) var S : str; fn a_S_i(pre : i32, post : i32) -> i32 { return S.i; } fn b() { a_S_i(10, 20); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessStorageASTest, Param_ptr_arr_i32_Via_struct_write) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { arr : array, }; @group(0) @binding(0) var S : str; fn a(pre : i32, p : ptr, read_write>, post : i32) { *p = array(); } fn b() { a(10, &S.arr, 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct str { arr : array, } @group(0) @binding(0) var S : str; fn a_S_arr(pre : i32, post : i32) { S.arr = array(); } fn b() { a_S_arr(10, 20); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessStorageASTest, Param_ptr_vec4i32_Via_array_DynamicWrite) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var S : array, 8>; fn a(pre : i32, p : ptr, read_write>, post : i32) { *p = vec4(); } fn b() { let I = 3; a(10, &S[I], 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var S : array, 8>; alias S_X = array; fn a_S_X(pre : i32, p : S_X, post : i32) { S[p[0]] = vec4(); } fn b() { let I = 3; a_S_X(10, S_X(u32(I)), 20); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessStorageASTest, CallChaining) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct Inner { mat : mat3x4, }; type InnerArr = array; struct Outer { arr : InnerArr, mat : mat3x4, }; @group(0) @binding(0) var S : Outer; fn f0(p : ptr>) -> f32 { return (*p).x; } fn f1(p : ptr>) -> f32 { var res : f32; { // call f0() with inline usage of p res += f0(&(*p)[1]); } { // call f0() with pointer-let usage of p let p_vec = &(*p)[1]; res += f0(p_vec); } { // call f0() with inline usage of S res += f0(&S.arr[2].mat[1]); } { // call f0() with pointer-let usage of S let p_vec = &S.arr[2].mat[1]; res += f0(p_vec); } return res; } fn f2(p : ptr) -> f32 { let p_mat = &(*p).mat; return f1(p_mat); } fn f3(p0 : ptr, p1 : ptr>) -> f32 { let p0_inner = &(*p0)[3]; return f2(p0_inner) + f1(p1); } fn f4(p : ptr) -> f32 { return f3(&(*p).arr, &S.mat); } fn b() { f4(&S); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct Inner { mat : mat3x4, } alias InnerArr = array; struct Outer { arr : InnerArr, mat : mat3x4, } @group(0) @binding(0) var S : Outer; alias S_mat_X = array; fn f0_S_mat_X(p : S_mat_X) -> f32 { return S.mat[p[0]].x; } alias S_arr_X_mat_X = array; fn f0_S_arr_X_mat_X(p : S_arr_X_mat_X) -> f32 { return S.arr[p[0]].mat[p[0]].x; } alias S_arr_X_mat_X_1 = array; fn f0_S_arr_X_mat_X_1(p : S_arr_X_mat_X_1) -> f32 { return S.arr[p[0]].mat[p[1]].x; } fn f1_S_mat() -> f32 { var res : f32; { res += f0_S_mat_X(S_mat_X(1)); } { let p_vec = &(S.mat[1]); res += f0_S_mat_X(S_mat_X(1)); } { res += f0_S_arr_X_mat_X_1(S_arr_X_mat_X_1(2, 1)); } { let p_vec = &(S.arr[2].mat[1]); res += f0_S_arr_X_mat_X_1(S_arr_X_mat_X_1(2, 1)); } return res; } alias S_arr_X_mat = array; fn f1_S_arr_X_mat(p : S_arr_X_mat) -> f32 { var res : f32; { res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1)); } { let p_vec = &(S.arr[p[0]].mat[1]); res += f0_S_arr_X_mat_X(S_arr_X_mat_X(p[0u], 1)); } { res += f0_S_arr_X_mat_X_1(S_arr_X_mat_X_1(2, 1)); } { let p_vec = &(S.arr[2].mat[1]); res += f0_S_arr_X_mat_X_1(S_arr_X_mat_X_1(2, 1)); } return res; } alias S_arr_X = array; fn f2_S_arr_X(p : S_arr_X) -> f32 { let p_mat = &(S.arr[p[0]].mat); return f1_S_arr_X_mat(S_arr_X_mat(p[0u])); } fn f3_S_arr_S_mat() -> f32 { let p0_inner = &(S.arr[3]); return (f2_S_arr_X(S_arr_X(3)) + f1_S_mat()); } fn f4_S() -> f32 { return f3_S_arr_S_mat(); } fn b() { f4_S(); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } } // namespace storage_as_tests //////////////////////////////////////////////////////////////////////////////// // 'workgroup' address space //////////////////////////////////////////////////////////////////////////////// namespace workgroup_as_tests { using DirectVariableAccessWorkgroupASTest = TransformTest; TEST_F(DirectVariableAccessWorkgroupASTest, Param_ptr_vec4i32_Via_array_StaticRead) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; var W : array, 8>; fn a(pre : i32, p : ptr>, post : i32) -> vec4 { return *p; } fn b() { a(10, &W[3], 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; var W : array, 8>; alias W_X = array; fn a_W_X(pre : i32, p : W_X, post : i32) -> vec4 { return W[p[0]]; } fn b() { a_W_X(10, W_X(3), 20); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessWorkgroupASTest, Param_ptr_vec4i32_Via_array_StaticWrite) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; var W : array, 8>; fn a(pre : i32, p : ptr>, post : i32) { *p = vec4(); } fn b() { a(10, &W[3], 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; var W : array, 8>; alias W_X = array; fn a_W_X(pre : i32, p : W_X, post : i32) { W[p[0]] = vec4(); } fn b() { a_W_X(10, W_X(3), 20); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessWorkgroupASTest, CallChaining) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct Inner { mat : mat3x4, }; type InnerArr = array; struct Outer { arr : InnerArr, mat : mat3x4, }; var W : Outer; fn f0(p : ptr>) -> f32 { return (*p).x; } fn f1(p : ptr>) -> f32 { var res : f32; { // call f0() with inline usage of p res += f0(&(*p)[1]); } { // call f0() with pointer-let usage of p let p_vec = &(*p)[1]; res += f0(p_vec); } { // call f0() with inline usage of W res += f0(&W.arr[2].mat[1]); } { // call f0() with pointer-let usage of W let p_vec = &W.arr[2].mat[1]; res += f0(p_vec); } return res; } fn f2(p : ptr) -> f32 { let p_mat = &(*p).mat; return f1(p_mat); } fn f3(p0 : ptr, p1 : ptr>) -> f32 { let p0_inner = &(*p0)[3]; return f2(p0_inner) + f1(p1); } fn f4(p : ptr) -> f32 { return f3(&(*p).arr, &W.mat); } fn b() { f4(&W); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct Inner { mat : mat3x4, } alias InnerArr = array; struct Outer { arr : InnerArr, mat : mat3x4, } var W : Outer; alias W_mat_X = array; fn f0_W_mat_X(p : W_mat_X) -> f32 { return W.mat[p[0]].x; } alias W_arr_X_mat_X = array; fn f0_W_arr_X_mat_X(p : W_arr_X_mat_X) -> f32 { return W.arr[p[0]].mat[p[0]].x; } alias W_arr_X_mat_X_1 = array; fn f0_W_arr_X_mat_X_1(p : W_arr_X_mat_X_1) -> f32 { return W.arr[p[0]].mat[p[1]].x; } fn f1_W_mat() -> f32 { var res : f32; { res += f0_W_mat_X(W_mat_X(1)); } { let p_vec = &(W.mat[1]); res += f0_W_mat_X(W_mat_X(1)); } { res += f0_W_arr_X_mat_X_1(W_arr_X_mat_X_1(2, 1)); } { let p_vec = &(W.arr[2].mat[1]); res += f0_W_arr_X_mat_X_1(W_arr_X_mat_X_1(2, 1)); } return res; } alias W_arr_X_mat = array; fn f1_W_arr_X_mat(p : W_arr_X_mat) -> f32 { var res : f32; { res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1)); } { let p_vec = &(W.arr[p[0]].mat[1]); res += f0_W_arr_X_mat_X(W_arr_X_mat_X(p[0u], 1)); } { res += f0_W_arr_X_mat_X_1(W_arr_X_mat_X_1(2, 1)); } { let p_vec = &(W.arr[2].mat[1]); res += f0_W_arr_X_mat_X_1(W_arr_X_mat_X_1(2, 1)); } return res; } alias W_arr_X = array; fn f2_W_arr_X(p : W_arr_X) -> f32 { let p_mat = &(W.arr[p[0]].mat); return f1_W_arr_X_mat(W_arr_X_mat(p[0u])); } fn f3_W_arr_W_mat() -> f32 { let p0_inner = &(W.arr[3]); return (f2_W_arr_X(W_arr_X(3)) + f1_W_mat()); } fn f4_W() -> f32 { return f3_W_arr_W_mat(); } fn b() { f4_W(); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } } // namespace workgroup_as_tests //////////////////////////////////////////////////////////////////////////////// // 'private' address space //////////////////////////////////////////////////////////////////////////////// namespace private_as_tests { using DirectVariableAccessPrivateASTest = TransformTest; TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_read) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; fn a(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } var P : i32; fn b() { a(10, &(P), 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; fn a_F(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } var P : i32; fn b() { a_F(10, &(P), 20); } )"; auto got = Run(src, EnablePrivate()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_write) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; fn a(pre : i32, p : ptr, post : i32) { *(p) = 42; } var P : i32; fn b() { a(10, &(P), 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; fn a_F(pre : i32, p : ptr, post : i32) { *(p) = 42; } var P : i32; fn b() { a_F(10, &(P), 20); } )"; auto got = Run(src, EnablePrivate()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_Via_struct_read) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, }; fn a(pre : i32, p : ptr, post : i32) -> i32 { return *p; } var P : str; fn b() { a(10, &P.i, 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, } fn a_F_i(pre : i32, p : ptr, post : i32) -> i32 { return (*(p)).i; } var P : str; fn b() { a_F_i(10, &(P), 20); } )"; auto got = Run(src, EnablePrivate()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_i32_Via_struct_read) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, } fn a(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } var P : str; fn b() { a(10, &(P.i), 20); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_arr_i32_Via_struct_write) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { arr : array, }; fn a(pre : i32, p : ptr>, post : i32) { *p = array(); } var P : str; fn b() { a(10, &P.arr, 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct str { arr : array, } fn a_F_arr(pre : i32, p : ptr, post : i32) { (*(p)).arr = array(); } var P : str; fn b() { a_F_arr(10, &(P), 20); } )"; auto got = Run(src, EnablePrivate()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_arr_i32_Via_struct_write) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { arr : array, } fn a(pre : i32, p : ptr>, post : i32) { *(p) = array(); } var P : str; fn b() { a(10, &(P.arr), 20); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPrivateASTest, Enabled_Param_ptr_i32_mixed) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, }; fn a(pre : i32, p : ptr, post : i32) -> i32 { return *p; } var Pi : i32; var Ps : str; var Pa : array; fn b() { a(10, &Pi, 20); a(30, &Ps.i, 40); a(50, &Pa[2], 60); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, } fn a_F(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } fn a_F_i(pre : i32, p : ptr, post : i32) -> i32 { return (*(p)).i; } alias F_X = array; fn a_F_X(pre : i32, p_base : ptr>, p_indices : F_X, post : i32) -> i32 { return (*(p_base))[p_indices[0]]; } var Pi : i32; var Ps : str; var Pa : array; alias F_X_1 = array; fn b() { a_F(10, &(Pi), 20); a_F_i(30, &(Ps), 40); a_F_X(50, &(Pa), F_X_1(2), 60); } )"; auto got = Run(src, EnablePrivate()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPrivateASTest, Disabled_Param_ptr_i32_mixed) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, } fn a(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } var Pi : i32; var Ps : str; var Pa : array; fn b() { a(10, &(Pi), 20); a(10, &(Ps.i), 20); a(10, &(Pa[2]), 20); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPrivateASTest, Enabled_CallChaining) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct Inner { mat : mat3x4, }; type InnerArr = array; struct Outer { arr : InnerArr, mat : mat3x4, }; var P : Outer; fn f0(p : ptr>) -> f32 { return (*p).x; } fn f1(p : ptr>) -> f32 { var res : f32; { // call f0() with inline usage of p res += f0(&(*p)[1]); } { // call f0() with pointer-let usage of p let p_vec = &(*p)[1]; res += f0(p_vec); } { // call f0() with inline usage of P res += f0(&P.arr[2].mat[1]); } { // call f0() with pointer-let usage of P let p_vec = &P.arr[2].mat[1]; res += f0(p_vec); } return res; } fn f2(p : ptr) -> f32 { let p_mat = &(*p).mat; return f1(p_mat); } fn f3(p0 : ptr, p1 : ptr>) -> f32 { let p0_inner = &(*p0)[3]; return f2(p0_inner) + f1(p1); } fn f4(p : ptr) -> f32 { return f3(&(*p).arr, &P.mat); } fn b() { f4(&P); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct Inner { mat : mat3x4, } alias InnerArr = array; struct Outer { arr : InnerArr, mat : mat3x4, } var P : Outer; alias F_mat_X = array; fn f0_F_mat_X(p_base : ptr, p_indices : F_mat_X) -> f32 { return (*(p_base)).mat[p_indices[0]].x; } alias F_arr_X_mat_X = array; fn f0_F_arr_X_mat_X(p_base : ptr, p_indices : F_arr_X_mat_X) -> f32 { return (*(p_base)).arr[p_indices[0]].mat[p_indices[0]].x; } alias F_arr_X_mat_X_1 = array; fn f0_F_arr_X_mat_X_1(p_base : ptr, p_indices : F_arr_X_mat_X_1) -> f32 { return (*(p_base)).arr[p_indices[0]].mat[p_indices[1]].x; } alias F_mat_X_1 = array; alias F_arr_X_mat_X_2 = array; fn f1_F_mat(p : ptr) -> f32 { var res : f32; { res += f0_F_mat_X(p, F_mat_X_1(1)); } { let p_vec = &((*(p)).mat[1]); res += f0_F_mat_X(p, F_mat_X_1(1)); } { res += f0_F_arr_X_mat_X_1(&(P), F_arr_X_mat_X_2(2, 1)); } { let p_vec = &(P.arr[2].mat[1]); res += f0_F_arr_X_mat_X_1(&(P), F_arr_X_mat_X_2(2, 1)); } return res; } alias F_arr_X_mat = array; alias F_arr_X_mat_X_3 = array; fn f1_F_arr_X_mat(p_base : ptr, p_indices : F_arr_X_mat) -> f32 { var res : f32; { res += f0_F_arr_X_mat_X(p_base, F_arr_X_mat_X_3(p_indices[0u], 1)); } { let p_vec = &((*(p_base)).arr[p_indices[0]].mat[1]); res += f0_F_arr_X_mat_X(p_base, F_arr_X_mat_X_3(p_indices[0u], 1)); } { res += f0_F_arr_X_mat_X_1(&(P), F_arr_X_mat_X_2(2, 1)); } { let p_vec = &(P.arr[2].mat[1]); res += f0_F_arr_X_mat_X_1(&(P), F_arr_X_mat_X_2(2, 1)); } return res; } alias F_arr_X = array; alias F_arr_X_mat_1 = array; fn f2_F_arr_X(p_base : ptr, p_indices : F_arr_X) -> f32 { let p_mat = &((*(p_base)).arr[p_indices[0]].mat); return f1_F_arr_X_mat(p_base, F_arr_X_mat_1(p_indices[0u])); } alias F_arr_X_1 = array; fn f3_F_arr_F_mat(p0 : ptr, p1 : ptr) -> f32 { let p0_inner = &((*(p0)).arr[3]); return (f2_F_arr_X(p0, F_arr_X_1(3)) + f1_F_mat(p1)); } fn f4_F(p : ptr) -> f32 { return f3_F_arr_F_mat(p, &(P)); } fn b() { f4_F(&(P)); } )"; auto got = Run(src, EnablePrivate()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessPrivateASTest, Disabled_CallChaining) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct Inner { mat : mat3x4, } alias InnerArr = array; struct Outer { arr : InnerArr, mat : mat3x4, } var P : Outer; fn f0(p : ptr>) -> f32 { return (*(p)).x; } fn f1(p : ptr>) -> f32 { var res : f32; { res += f0(&((*(p))[1])); } { let p_vec = &((*(p))[1]); res += f0(p_vec); } { res += f0(&(P.arr[2].mat[1])); } { let p_vec = &(P.arr[2].mat[1]); res += f0(p_vec); } return res; } fn f2(p : ptr) -> f32 { let p_mat = &((*(p)).mat); return f1(p_mat); } fn f3(p0 : ptr, p1 : ptr>) -> f32 { let p0_inner = &((*(p0))[3]); return (f2(p0_inner) + f1(p1)); } fn f4(p : ptr) -> f32 { return f3(&((*(p)).arr), &(P.mat)); } fn b() { f4(&(P)); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } } // namespace private_as_tests //////////////////////////////////////////////////////////////////////////////// // 'function' address space //////////////////////////////////////////////////////////////////////////////// namespace function_as_tests { using DirectVariableAccessFunctionASTest = TransformTest; TEST_F(DirectVariableAccessFunctionASTest, Enabled_LocalPtr) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; fn f() { var v : i32; let p : ptr = &(v); var x : i32 = *(p); } )"; auto* expect = src; // Nothing changes auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_read) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; fn a(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } fn b() { var F : i32; a(10, &(F), 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; fn a_F(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } fn b() { var F : i32; a_F(10, &(F), 20); } )"; auto got = Run(src, EnableFunction()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_write) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; fn a(pre : i32, p : ptr, post : i32) { *(p) = 42; } fn b() { var F : i32; a(10, &(F), 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; fn a_F(pre : i32, p : ptr, post : i32) { *(p) = 42; } fn b() { var F : i32; a_F(10, &(F), 20); } )"; auto got = Run(src, EnableFunction()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_Via_struct_read) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, }; fn a(pre : i32, p : ptr, post : i32) -> i32 { return *p; } fn b() { var F : str; a(10, &F.i, 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, } fn a_F_i(pre : i32, p : ptr, post : i32) -> i32 { return (*(p)).i; } fn b() { var F : str; a_F_i(10, &(F), 20); } )"; auto got = Run(src, EnableFunction()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_arr_i32_Via_struct_write) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { arr : array, }; fn a(pre : i32, p : ptr>, post : i32) { *p = array(); } fn b() { var F : str; a(10, &F.arr, 20); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct str { arr : array, } fn a_F_arr(pre : i32, p : ptr, post : i32) { (*(p)).arr = array(); } fn b() { var F : str; a_F_arr(10, &(F), 20); } )"; auto got = Run(src, EnableFunction()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessFunctionASTest, Enabled_Param_ptr_i32_mixed) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, }; fn a(pre : i32, p : ptr, post : i32) -> i32 { return *p; } fn b() { var Fi : i32; var Fs : str; var Fa : array; a(10, &Fi, 20); a(30, &Fs.i, 40); a(50, &Fa[2], 60); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, } fn a_F(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } fn a_F_i(pre : i32, p : ptr, post : i32) -> i32 { return (*(p)).i; } alias F_X = array; fn a_F_X(pre : i32, p_base : ptr>, p_indices : F_X, post : i32) -> i32 { return (*(p_base))[p_indices[0]]; } alias F_X_1 = array; fn b() { var Fi : i32; var Fs : str; var Fa : array; a_F(10, &(Fi), 20); a_F_i(30, &(Fs), 40); a_F_X(50, &(Fa), F_X_1(2), 60); } )"; auto got = Run(src, EnableFunction()); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessFunctionASTest, Disabled_Param_ptr_i32_Via_struct_read) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : i32, } fn a(pre : i32, p : ptr, post : i32) -> i32 { return *(p); } fn b() { var F : str; a(10, &(F.i), 20); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessFunctionASTest, Disabled_Param_ptr_arr_i32_Via_struct_write) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { arr : array, } fn a(pre : i32, p : ptr>, post : i32) { *(p) = array(); } fn b() { var F : str; a(10, &(F.arr), 20); } )"; auto* expect = src; auto got = Run(src); EXPECT_EQ(expect, str(got)); } } // namespace function_as_tests //////////////////////////////////////////////////////////////////////////////// // complex tests //////////////////////////////////////////////////////////////////////////////// namespace complex_tests { using DirectVariableAccessComplexTest = TransformTest; TEST_F(DirectVariableAccessComplexTest, Param_ptr_mixed_vec4i32_ViaMultiple) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : vec4, }; @group(0) @binding(0) var U : vec4; @group(0) @binding(1) var U_str : str; @group(0) @binding(2) var U_arr : array, 8>; @group(0) @binding(3) var U_arr_arr : array, 8>, 4>; @group(1) @binding(0) var S : vec4; @group(1) @binding(1) var S_str : str; @group(1) @binding(2) var S_arr : array, 8>; @group(1) @binding(3) var S_arr_arr : array, 8>, 4>; var W : vec4; var W_str : str; var W_arr : array, 8>; var W_arr_arr : array, 8>, 4>; fn fn_u(p : ptr>) -> vec4 { return *p; } fn fn_s(p : ptr>) -> vec4 { return *p; } fn fn_w(p : ptr>) -> vec4 { return *p; } fn b() { let I = 3; let J = 4; let u = fn_u(&U); let u_str = fn_u(&U_str.i); let u_arr0 = fn_u(&U_arr[0]); let u_arr1 = fn_u(&U_arr[1]); let u_arrI = fn_u(&U_arr[I]); let u_arr1_arr0 = fn_u(&U_arr_arr[1][0]); let u_arr2_arrI = fn_u(&U_arr_arr[2][I]); let u_arrI_arr2 = fn_u(&U_arr_arr[I][2]); let u_arrI_arrJ = fn_u(&U_arr_arr[I][J]); let s = fn_s(&S); let s_str = fn_s(&S_str.i); let s_arr0 = fn_s(&S_arr[0]); let s_arr1 = fn_s(&S_arr[1]); let s_arrI = fn_s(&S_arr[I]); let s_arr1_arr0 = fn_s(&S_arr_arr[1][0]); let s_arr2_arrI = fn_s(&S_arr_arr[2][I]); let s_arrI_arr2 = fn_s(&S_arr_arr[I][2]); let s_arrI_arrJ = fn_s(&S_arr_arr[I][J]); let w = fn_w(&W); let w_str = fn_w(&W_str.i); let w_arr0 = fn_w(&W_arr[0]); let w_arr1 = fn_w(&W_arr[1]); let w_arrI = fn_w(&W_arr[I]); let w_arr1_arr0 = fn_w(&W_arr_arr[1][0]); let w_arr2_arrI = fn_w(&W_arr_arr[2][I]); let w_arrI_arr2 = fn_w(&W_arr_arr[I][2]); let w_arrI_arrJ = fn_w(&W_arr_arr[I][J]); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; struct str { i : vec4, } @group(0) @binding(0) var U : vec4; @group(0) @binding(1) var U_str : str; @group(0) @binding(2) var U_arr : array, 8>; @group(0) @binding(3) var U_arr_arr : array, 8>, 4>; @group(1) @binding(0) var S : vec4; @group(1) @binding(1) var S_str : str; @group(1) @binding(2) var S_arr : array, 8>; @group(1) @binding(3) var S_arr_arr : array, 8>, 4>; var W : vec4; var W_str : str; var W_arr : array, 8>; var W_arr_arr : array, 8>, 4>; fn fn_u_U() -> vec4 { return U; } fn fn_u_U_str_i() -> vec4 { return U_str.i; } alias U_arr_X = array; fn fn_u_U_arr_X(p : U_arr_X) -> vec4 { return U_arr[p[0]]; } alias U_arr_arr_X_X = array; fn fn_u_U_arr_arr_X_X(p : U_arr_arr_X_X) -> vec4 { return U_arr_arr[p[0]][p[1]]; } fn fn_s_S() -> vec4 { return S; } fn fn_s_S_str_i() -> vec4 { return S_str.i; } alias S_arr_X = array; fn fn_s_S_arr_X(p : S_arr_X) -> vec4 { return S_arr[p[0]]; } alias S_arr_arr_X_X = array; fn fn_s_S_arr_arr_X_X(p : S_arr_arr_X_X) -> vec4 { return S_arr_arr[p[0]][p[1]]; } fn fn_w_W() -> vec4 { return W; } fn fn_w_W_str_i() -> vec4 { return W_str.i; } alias W_arr_X = array; fn fn_w_W_arr_X(p : W_arr_X) -> vec4 { return W_arr[p[0]]; } alias W_arr_arr_X_X = array; fn fn_w_W_arr_arr_X_X(p : W_arr_arr_X_X) -> vec4 { return W_arr_arr[p[0]][p[1]]; } fn b() { let I = 3; let J = 4; let u = fn_u_U(); let u_str = fn_u_U_str_i(); let u_arr0 = fn_u_U_arr_X(U_arr_X(0)); let u_arr1 = fn_u_U_arr_X(U_arr_X(1)); let u_arrI = fn_u_U_arr_X(U_arr_X(u32(I))); let u_arr1_arr0 = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(1, 0)); let u_arr2_arrI = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(2, u32(I))); let u_arrI_arr2 = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(u32(I), 2)); let u_arrI_arrJ = fn_u_U_arr_arr_X_X(U_arr_arr_X_X(u32(I), u32(J))); let s = fn_s_S(); let s_str = fn_s_S_str_i(); let s_arr0 = fn_s_S_arr_X(S_arr_X(0)); let s_arr1 = fn_s_S_arr_X(S_arr_X(1)); let s_arrI = fn_s_S_arr_X(S_arr_X(u32(I))); let s_arr1_arr0 = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(1, 0)); let s_arr2_arrI = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(2, u32(I))); let s_arrI_arr2 = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(u32(I), 2)); let s_arrI_arrJ = fn_s_S_arr_arr_X_X(S_arr_arr_X_X(u32(I), u32(J))); let w = fn_w_W(); let w_str = fn_w_W_str_i(); let w_arr0 = fn_w_W_arr_X(W_arr_X(0)); let w_arr1 = fn_w_W_arr_X(W_arr_X(1)); let w_arrI = fn_w_W_arr_X(W_arr_X(u32(I))); let w_arr1_arr0 = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(1, 0)); let w_arr2_arrI = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(2, u32(I))); let w_arrI_arr2 = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(u32(I), 2)); let w_arrI_arrJ = fn_w_W_arr_arr_X_X(W_arr_arr_X_X(u32(I), u32(J))); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessComplexTest, Indexing) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var S : array, 9>, 9>, 50>; fn a(i : i32) -> i32 { return i; } fn b(p : ptr, 9>, 9>>) -> i32 { return (*p) [ a( (*p)[0][1][2] )] [ a( (*p)[a(3)][4][5] )] [ a( (*p)[6][a(7)][8] )]; } fn c() { let v = b(&S[42]); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var S : array, 9>, 9>, 50>; fn a(i : i32) -> i32 { return i; } alias S_X = array; fn b_S_X(p : S_X) -> i32 { return S[p[0]][a(S[p[0]][0][1][2])][a(S[p[0]][a(3)][4][5])][a(S[p[0]][6][a(7)][8])]; } fn c() { let v = b_S_X(S_X(42)); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessComplexTest, IndexingInPtrCall) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var S : array, 9>, 9>, 50>; fn a(pre : i32, i : ptr, post : i32) -> i32 { return *i; } fn b(p : ptr, 9>, 9>>) -> i32 { return a(10, &(*p)[ a( 20, &(*p)[0][1][2], 30 )] [ a( 40, &(*p)[3][4][5], 50 )] [ a( 60, &(*p)[6][7][8], 70 )], 80); } fn c() { let v = b(&S[42]); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var S : array, 9>, 9>, 50>; alias S_X_X_X_X = array; fn a_S_X_X_X_X(pre : i32, i : S_X_X_X_X, post : i32) -> i32 { return S[i[0]][i[0]][i[1]][i[2]]; } alias S_X = array; fn b_S_X(p : S_X) -> i32 { return a_S_X_X_X_X(10, S_X_X_X_X(p[0u], u32(a_S_X_X_X_X(20, S_X_X_X_X(p[0u], 0, 1, 2), 30)), u32(a_S_X_X_X_X(40, S_X_X_X_X(p[0u], 3, 4, 5), 50)), u32(a_S_X_X_X_X(60, S_X_X_X_X(p[0u], 6, 7, 8), 70))), 80); } fn c() { let v = b_S_X(S_X(42)); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } TEST_F(DirectVariableAccessComplexTest, IndexingDualPointers) { auto* src = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var S : array, 9>, 50>; @group(0) @binding(0) var U : array, 9>, 9>, 50>; fn a(i : i32) -> i32 { return i; } fn b(s : ptr, 9>>, u : ptr, 9>, 9>>) -> i32 { return (*s) [ a( (*u)[0][1].x )] [ a( (*u)[a(3)][4].y )]; } fn c() { let v = b(&S[42], &U[24]); } )"; auto* expect = R"( enable chromium_experimental_full_ptr_parameters; @group(0) @binding(0) var S : array, 9>, 50>; @group(0) @binding(0) var U : array, 9>, 9>, 50>; fn a(i : i32) -> i32 { return i; } alias S_X = array; alias U_X = array; fn b_S_X_U_X(s : S_X, u : U_X) -> i32 { return S[s[0]][a(U[u[0]][0][1].x)][a(U[u[0]][a(3)][4].y)]; } fn c() { let v = b_S_X_U_X(S_X(42), U_X(24)); } )"; auto got = Run(src); EXPECT_EQ(expect, str(got)); } } // namespace complex_tests } // namespace } // namespace tint::transform