blob: 77b799cf1af9ed14be7e3b4225d699052388e60f [file] [log] [blame]
// 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.
// GEN_BUILD:CONDITION(tint_build_wgsl_reader && tint_build_wgsl_writer)
#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/wgsl/reader/program_to_ir/program_to_ir.h"
#include "src/tint/lang/wgsl/reader/reader.h"
#include "src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.h"
#include "src/tint/lang/wgsl/writer/raise/raise.h"
#include "src/tint/lang/wgsl/writer/writer.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,
};
class DirectVariableAccessTest : public TransformTestBase<testing::Test> {
public:
std::string Run(std::string in,
const DirectVariableAccessOptions& transform_options = {},
const wgsl::writer::ProgramOptions program_options = {}) {
wgsl::reader::Options parser_options;
parser_options.allowed_features = wgsl::AllowedFeatures::Everything();
Source::File file{"test", in};
auto program_in = wgsl::reader::Parse(&file, parser_options);
if (!program_in.IsValid()) {
return "wgsl::reader::Parse() failed: \n" + program_in.Diagnostics().str();
}
auto module = wgsl::reader::ProgramToIR(program_in);
if (module != Success) {
return "ProgramToIR() failed:\n" + module.Failure().reason.str();
}
auto res = DirectVariableAccess(module.Get(), transform_options);
if (res != Success) {
return "DirectVariableAccess failed:\n" + res.Failure().reason.str();
}
auto pre_raise = ir::Disassemble(module.Get());
if (auto raise = wgsl::writer::Raise(module.Get()); raise != Success) {
return "wgsl::writer::Raise failed:\n" + res.Failure().reason.str();
}
auto program_out = wgsl::writer::IRToProgram(module.Get(), program_options);
if (!program_out.IsValid()) {
return "wgsl::writer::IRToProgram() failed: \n" + program_out.Diagnostics().str() +
"\n\nIR (pre):\n" + pre_raise + //
"\n\nIR (post):\n" + ir::Disassemble(module.Get()) + //
"\n\nAST:\n" + Program::printer(program_out);
}
auto output = wgsl::writer::Generate(program_out, wgsl::writer::Options{});
if (output != Success) {
return "wgsl::writer::IRToProgram() failed: \n" + output.Failure().reason.str() +
"\n\nIR:\n" + ir::Disassemble(module.Get());
}
return "\n" + output->wgsl;
}
};
} // namespace
////////////////////////////////////////////////////////////////////////////////
// remove uncalled
////////////////////////////////////////////////////////////////////////////////
namespace remove_uncalled {
using IR_DirectVariableAccessWgslTest_RemoveUncalled = DirectVariableAccessTest;
TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrUniform) {
auto* src = R"(
var<private> keep_me : i32 = 42i;
fn u(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
return *(p);
}
)";
auto* expect = R"(
var<private> keep_me : i32 = 42i;
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrStorage) {
auto* src = R"(
var<private> keep_me : i32 = 42i;
fn s(pre : i32, p : ptr<storage, i32>, post : i32) -> i32 {
return *(p);
}
)";
auto* expect = R"(
var<private> keep_me : i32 = 42i;
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrWorkgroup) {
auto* src = R"(
var<private> keep_me : i32 = 42i;
fn w(pre : i32, p : ptr<workgroup, i32>, post : i32) -> i32 {
return *(p);
}
)";
auto* expect = R"(
var<private> keep_me : i32 = 42i;
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrPrivate_Disabled) {
auto* src = R"(
var<private> keep_me : i32 = 42i;
fn f(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
return *(p);
}
)";
auto* expect = src;
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrPrivate_Enabled) {
auto* src = R"(
var<private> keep_me : i32 = 42i;
fn f(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
return *(p);
}
)";
auto* expect = R"(
var<private> keep_me : i32 = 42i;
)";
auto got = Run(src, kTransformPrivate);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrFunction_Disabled) {
auto* src = R"(
var<private> keep_me : i32 = 42i;
fn f(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
return *(p);
}
)";
auto* expect = src;
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_RemoveUncalled, PtrFunction_Enabled) {
auto* src = R"(
var<private> keep_me : i32 = 42i;
fn f(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
return *(p);
}
)";
auto* expect = R"(
var<private> keep_me : i32 = 42i;
)";
auto got = Run(src, kTransformFunction);
EXPECT_EQ(expect, got);
}
} // namespace remove_uncalled
////////////////////////////////////////////////////////////////////////////////
// pointer chains
////////////////////////////////////////////////////////////////////////////////
namespace pointer_chains_tests {
using IR_DirectVariableAccessWgslTest_PtrChains = DirectVariableAccessTest;
TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, ConstantIndices) {
auto* src = R"(
@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
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<uniform, array<array<array<vec4<i32>, 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"(
@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8u>, 8u>, 8u>;
fn a(pre : i32, p_indices : array<u32, 3u>, post : i32) -> vec4<i32> {
return U[p_indices[0u]][p_indices[1u]][p_indices[2u]];
}
fn b() {
a(10i, array<u32, 3u>(u32(1i), u32(2i), u32(3i)), 20i);
}
fn c() {
a(10i, array<u32, 3u>(u32(1i), u32(2i), u32(3i)), 20i);
}
fn d() {
c();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndices) {
auto* src = R"(
@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
var<private> 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<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
return *p;
}
fn b() {
let p0 = &U;
let p1 = &(*p0)[first()];
let p2 = &(*p1)[second()][third()];
a(10, p2, 20);
}
fn c(p : ptr<uniform, array<array<array<vec4<i32>, 8>, 8>, 8>>) {
let p0 = p;
let p1 = &(*p0)[first()];
let p2 = &(*p1)[second()][third()];
a(10, p2, 20);
}
fn d() {
c(&U);
}
)";
auto* expect = R"(
@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8u>, 8u>, 8u>;
var<private> i : i32;
fn first() -> i32 {
i = (i + 1i);
return i;
}
fn second() -> i32 {
i = (i + 1i);
return i;
}
fn third() -> i32 {
i = (i + 1i);
return i;
}
fn a(pre : i32, p_indices : array<u32, 3u>, post : i32) -> vec4<i32> {
return U[p_indices[0u]][p_indices[1u]][p_indices[2u]];
}
fn b() {
let v = first();
let v_1 = second();
a(10i, array<u32, 3u>(u32(v), u32(v_1), u32(third())), 20i);
}
fn c() {
let v_2 = first();
let v_3 = second();
a(10i, array<u32, 3u>(u32(v_2), u32(v_3), u32(third())), 20i);
}
fn d() {
c();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndicesForLoopInit) {
auto* src = R"(
@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
var<private> i : i32;
fn first() -> i32 {
i++;
return i;
}
fn second() -> i32 {
i++;
return i;
}
fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
return *p;
}
fn b() {
for (let p1 = &U[first()]; true; ) {
a(10, &(*p1)[second()], 20);
}
}
fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
for (let p1 = &(*p)[first()]; true; ) {
a(10, &(*p1)[second()], 20);
}
}
fn d() {
c(&U);
}
)";
auto* expect = R"(
@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8u>, 8u>;
var<private> i : i32;
fn first() -> i32 {
i = (i + 1i);
return i;
}
fn second() -> i32 {
i = (i + 1i);
return i;
}
fn a(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
return U[p_indices[0u]][p_indices[1u]];
}
fn b() {
for(let v = first(); true; ) {
a(10i, array<u32, 2u>(u32(v), u32(second())), 20i);
}
}
fn c() {
for(let v_1 = first(); true; ) {
a(10i, array<u32, 2u>(u32(v_1), u32(second())), 20i);
}
}
fn d() {
c();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndicesForLoopCond) {
auto* src = R"(
@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
var<private> i : i32;
fn first() -> i32 {
i++;
return i;
}
fn second() -> i32 {
i++;
return i;
}
fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
return *p;
}
fn b() {
let p = &U[first()][second()];
for (; a(10, p, 20).x < 4; ) {
let body = 1;
}
}
fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
let p2 = &(*p)[first()][second()];
for (; a(10, p2, 20).x < 4; ) {
let body = 1;
}
}
fn d() {
c(&U);
}
)";
auto* expect = R"(
@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8u>, 8u>;
var<private> i : i32;
fn first() -> i32 {
i = (i + 1i);
return i;
}
fn second() -> i32 {
i = (i + 1i);
return i;
}
fn a(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
return U[p_indices[0u]][p_indices[1u]];
}
fn b() {
let v = first();
let v_1 = second();
while((a(10i, array<u32, 2u>(u32(v), u32(v_1)), 20i).x < 4i)) {
let body = 1i;
}
}
fn c() {
let v_2 = first();
let v_3 = second();
while((a(10i, array<u32, 2u>(u32(v_2), u32(v_3)), 20i).x < 4i)) {
let body = 1i;
}
}
fn d() {
c();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndicesForLoopCont) {
auto* src = R"(
@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
var<private> i : i32;
fn first() -> i32 {
i++;
return i;
}
fn second() -> i32 {
i++;
return i;
}
fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
return *p;
}
fn b() {
let p = &U[first()][second()];
for (var i = 0; i < 3; a(10, p, 20)) {
i++;
}
}
fn c(p : ptr<uniform, array<array<vec4<i32>, 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"(
@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8u>, 8u>;
var<private> i : i32;
fn first() -> i32 {
i = (i + 1i);
return i;
}
fn second() -> i32 {
i = (i + 1i);
return i;
}
fn a(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
return U[p_indices[0u]][p_indices[1u]];
}
fn b() {
let v = first();
let v_1 = second();
for(var i : i32 = 0i; (i < 3i); a(10i, array<u32, 2u>(u32(v), u32(v_1)), 20i)) {
i = (i + 1i);
}
}
fn c() {
let v_2 = first();
let v_3 = second();
for(var i : i32 = 0i; (i < 3i); a(10i, array<u32, 2u>(u32(v_2), u32(v_3)), 20i)) {
i = (i + 1i);
}
}
fn d() {
c();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PtrChains, DynamicIndicesWhileCond) {
auto* src = R"(
@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8>, 8>;
var<private> i : i32;
fn first() -> i32 {
i++;
return i;
}
fn second() -> i32 {
i++;
return i;
}
fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
return *p;
}
fn b() {
let p = &U[first()][second()];
while (a(10, p, 20).x < 4) {
let body = 1;
}
}
fn c(p : ptr<uniform, array<array<vec4<i32>, 8>, 8>>) {
let p2 = &(*p)[first()][second()];
while (a(10, p2, 20).x < 4) {
let body = 1;
}
}
fn d() {
c(&U);
}
)";
auto* expect = R"(
@group(0) @binding(0) var<uniform> U : array<array<vec4<i32>, 8u>, 8u>;
var<private> i : i32;
fn first() -> i32 {
i = (i + 1i);
return i;
}
fn second() -> i32 {
i = (i + 1i);
return i;
}
fn a(pre : i32, p_indices : array<u32, 2u>, post : i32) -> vec4<i32> {
return U[p_indices[0u]][p_indices[1u]];
}
fn b() {
let v = first();
let v_1 = second();
while((a(10i, array<u32, 2u>(u32(v), u32(v_1)), 20i).x < 4i)) {
let body = 1i;
}
}
fn c() {
let v_2 = first();
let v_3 = second();
while((a(10i, array<u32, 2u>(u32(v_2), u32(v_3)), 20i).x < 4i)) {
let body = 1i;
}
}
fn d() {
c();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
} // namespace pointer_chains_tests
////////////////////////////////////////////////////////////////////////////////
// 'uniform' address space
////////////////////////////////////////////////////////////////////////////////
namespace uniform_as_tests {
using IR_DirectVariableAccessWgslTest_UniformAS = DirectVariableAccessTest;
TEST_F(IR_DirectVariableAccessWgslTest_UniformAS, Param_ptr_i32_read) {
auto* src = R"(
@group(0) @binding(0) var<uniform> U : i32;
fn a(pre : i32, p : ptr<uniform, i32>, post : i32) -> i32 {
return *p;
}
fn b() {
a(10, &U, 20);
}
)";
auto* expect = R"(
@group(0) @binding(0) var<uniform> U : i32;
fn a(pre : i32, post : i32) -> i32 {
return U;
}
fn b() {
a(10i, 20i);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_UniformAS, Param_ptr_vec4i32_Via_array_DynamicRead) {
auto* src = R"(
@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
fn a(pre : i32, p : ptr<uniform, vec4<i32>>, post : i32) -> vec4<i32> {
return *p;
}
fn b() {
let I = 3;
a(10, &U[I], 20);
}
)";
auto* expect = R"(
@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8u>;
fn a(pre : i32, p_indices : array<u32, 1u>, post : i32) -> vec4<i32> {
return U[p_indices[0u]];
}
fn b() {
let I = 3i;
a(10i, array<u32, 1u>(u32(I)), 20i);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_UniformAS, CallChaining) {
auto* src = R"(
struct Inner {
mat : mat3x4<f32>,
};
alias InnerArr = array<Inner, 4>;
struct Outer {
arr : InnerArr,
mat : mat3x4<f32>,
};
@group(0) @binding(0) var<uniform> U : Outer;
fn f0(p : ptr<uniform, vec4<f32>>) -> f32 {
return (*p).x;
}
fn f1(p : ptr<uniform, mat3x4<f32>>) -> 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<uniform, Inner>) -> f32 {
let p_mat = &(*p).mat;
return f1(p_mat);
}
fn f3(p0 : ptr<uniform, InnerArr>, p1 : ptr<uniform, mat3x4<f32>>) -> f32 {
let p0_inner = &(*p0)[3];
return f2(p0_inner) + f1(p1);
}
fn f4(p : ptr<uniform, Outer>) -> f32 {
return f3(&(*p).arr, &U.mat);
}
fn b() {
f4(&U);
}
)";
auto* expect = R"(
struct Inner {
mat : mat3x4<f32>,
}
struct Outer {
arr : array<Inner, 4u>,
mat : mat3x4<f32>,
}
@group(0) @binding(0) var<uniform> U : Outer;
fn f0(p_indices : array<u32, 1u>) -> f32 {
return U.mat[p_indices[0u]].x;
}
fn f0_1(p_indices : array<u32, 2u>) -> f32 {
return U.arr[p_indices[0u]].mat[p_indices[1u]].x;
}
fn f1() -> f32 {
var res : f32;
let v = f0(array<u32, 1u>(u32(1i)));
res = (res + v);
let v_1 = f0(array<u32, 1u>(u32(1i)));
res = (res + v_1);
let v_2 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_2);
let v_3 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_3);
return res;
}
fn f1_1(p_indices : array<u32, 1u>) -> f32 {
let v_4 = p_indices[0u];
var res : f32;
let v_5 = f0_1(array<u32, 2u>(v_4, u32(1i)));
res = (res + v_5);
let v_6 = f0_1(array<u32, 2u>(v_4, u32(1i)));
res = (res + v_6);
let v_7 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_7);
let v_8 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_8);
return res;
}
fn f2(p_indices : array<u32, 1u>) -> f32 {
return f1_1(array<u32, 1u>(p_indices[0u]));
}
fn f3() -> f32 {
return (f2(array<u32, 1u>(u32(3i))) + f1());
}
fn f4() -> f32 {
return f3();
}
fn b() {
f4();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
} // namespace uniform_as_tests
////////////////////////////////////////////////////////////////////////////////
// 'storage' address space
////////////////////////////////////////////////////////////////////////////////
namespace storage_as_tests {
using IR_DirectVariableAccessWgslTest_StorageAS = DirectVariableAccessTest;
TEST_F(IR_DirectVariableAccessWgslTest_StorageAS, Param_ptr_i32_Via_struct_read) {
auto* src = R"(
struct str {
i : i32,
};
@group(0) @binding(0) var<storage> S : str;
fn a(pre : i32, p : ptr<storage, i32>, post : i32) -> i32 {
return *p;
}
fn b() {
a(10, &S.i, 20);
}
)";
auto* expect = R"(
struct str {
i : i32,
}
@group(0) @binding(0) var<storage, read> S : str;
fn a(pre : i32, post : i32) -> i32 {
return S.i;
}
fn b() {
a(10i, 20i);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_StorageAS, Param_ptr_arr_i32_Via_struct_write) {
auto* src = R"(
struct str {
arr : array<i32, 4>,
};
@group(0) @binding(0) var<storage, read_write> S : str;
fn a(pre : i32, p : ptr<storage, array<i32, 4>, read_write>, post : i32) {
*p = array<i32, 4>();
}
fn b() {
a(10, &S.arr, 20);
}
)";
auto* expect = R"(
struct str {
arr : array<i32, 4u>,
}
@group(0) @binding(0) var<storage, read_write> S : str;
fn a(pre : i32, post : i32) {
S.arr = array<i32, 4u>();
}
fn b() {
a(10i, 20i);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_StorageAS, Param_ptr_vec4i32_Via_array_DynamicWrite) {
auto* src = R"(
@group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8>;
fn a(pre : i32, p : ptr<storage, vec4<i32>, read_write>, post : i32) {
*p = vec4<i32>();
}
fn b() {
let I = 3;
a(10, &S[I], 20);
}
)";
auto* expect = R"(
@group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8u>;
fn a(pre : i32, p_indices : array<u32, 1u>, post : i32) {
S[p_indices[0u]] = vec4<i32>();
}
fn b() {
let I = 3i;
a(10i, array<u32, 1u>(u32(I)), 20i);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_StorageAS, CallChaining) {
auto* src = R"(
struct Inner {
mat : mat3x4<f32>,
};
alias InnerArr = array<Inner, 4>;
struct Outer {
arr : InnerArr,
mat : mat3x4<f32>,
};
@group(0) @binding(0) var<storage> S : Outer;
fn f0(p : ptr<storage, vec4<f32>>) -> f32 {
return (*p).x;
}
fn f1(p : ptr<storage, mat3x4<f32>>) -> 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<storage, Inner>) -> f32 {
let p_mat = &(*p).mat;
return f1(p_mat);
}
fn f3(p0 : ptr<storage, InnerArr>, p1 : ptr<storage, mat3x4<f32>>) -> f32 {
let p0_inner = &(*p0)[3];
return f2(p0_inner) + f1(p1);
}
fn f4(p : ptr<storage, Outer>) -> f32 {
return f3(&(*p).arr, &S.mat);
}
fn b() {
f4(&S);
}
)";
auto* expect = R"(
struct Inner {
mat : mat3x4<f32>,
}
struct Outer {
arr : array<Inner, 4u>,
mat : mat3x4<f32>,
}
@group(0) @binding(0) var<storage, read> S : Outer;
fn f0(p_indices : array<u32, 1u>) -> f32 {
return S.mat[p_indices[0u]].x;
}
fn f0_1(p_indices : array<u32, 2u>) -> f32 {
return S.arr[p_indices[0u]].mat[p_indices[1u]].x;
}
fn f1() -> f32 {
var res : f32;
let v = f0(array<u32, 1u>(u32(1i)));
res = (res + v);
let v_1 = f0(array<u32, 1u>(u32(1i)));
res = (res + v_1);
let v_2 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_2);
let v_3 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_3);
return res;
}
fn f1_1(p_indices : array<u32, 1u>) -> f32 {
let v_4 = p_indices[0u];
var res : f32;
let v_5 = f0_1(array<u32, 2u>(v_4, u32(1i)));
res = (res + v_5);
let v_6 = f0_1(array<u32, 2u>(v_4, u32(1i)));
res = (res + v_6);
let v_7 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_7);
let v_8 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_8);
return res;
}
fn f2(p_indices : array<u32, 1u>) -> f32 {
return f1_1(array<u32, 1u>(p_indices[0u]));
}
fn f3() -> f32 {
return (f2(array<u32, 1u>(u32(3i))) + f1());
}
fn f4() -> f32 {
return f3();
}
fn b() {
f4();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
} // namespace storage_as_tests
////////////////////////////////////////////////////////////////////////////////
// 'workgroup' address space
////////////////////////////////////////////////////////////////////////////////
namespace workgroup_as_tests {
using IR_DirectVariableAccessWgslTest_WorkgroupAS = DirectVariableAccessTest;
TEST_F(IR_DirectVariableAccessWgslTest_WorkgroupAS, Param_ptr_vec4i32_Via_array_StaticRead) {
auto* src = R"(
var<workgroup> W : array<vec4<i32>, 8>;
fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) -> vec4<i32> {
return *p;
}
fn b() {
a(10, &W[3], 20);
}
)";
auto* expect = R"(
var<workgroup> W : array<vec4<i32>, 8u>;
fn a(pre : i32, p_indices : array<u32, 1u>, post : i32) -> vec4<i32> {
return W[p_indices[0u]];
}
fn b() {
a(10i, array<u32, 1u>(u32(3i)), 20i);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_WorkgroupAS, Param_ptr_vec4i32_Via_array_StaticWrite) {
auto* src = R"(
var<workgroup> W : array<vec4<i32>, 8>;
fn a(pre : i32, p : ptr<workgroup, vec4<i32>>, post : i32) {
*p = vec4<i32>();
}
fn b() {
a(10, &W[3], 20);
}
)";
auto* expect = R"(
var<workgroup> W : array<vec4<i32>, 8u>;
fn a(pre : i32, p_indices : array<u32, 1u>, post : i32) {
W[p_indices[0u]] = vec4<i32>();
}
fn b() {
a(10i, array<u32, 1u>(u32(3i)), 20i);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_WorkgroupAS, CallChaining) {
auto* src = R"(
struct Inner {
mat : mat3x4<f32>,
};
alias InnerArr = array<Inner, 4>;
struct Outer {
arr : InnerArr,
mat : mat3x4<f32>,
};
var<workgroup> W : Outer;
fn f0(p : ptr<workgroup, vec4<f32>>) -> f32 {
return (*p).x;
}
fn f1(p : ptr<workgroup, mat3x4<f32>>) -> 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<workgroup, Inner>) -> f32 {
let p_mat = &(*p).mat;
return f1(p_mat);
}
fn f3(p0 : ptr<workgroup, InnerArr>, p1 : ptr<workgroup, mat3x4<f32>>) -> f32 {
let p0_inner = &(*p0)[3];
return f2(p0_inner) + f1(p1);
}
fn f4(p : ptr<workgroup, Outer>) -> f32 {
return f3(&(*p).arr, &W.mat);
}
fn b() {
f4(&W);
}
)";
auto* expect = R"(
struct Inner {
mat : mat3x4<f32>,
}
struct Outer {
arr : array<Inner, 4u>,
mat : mat3x4<f32>,
}
var<workgroup> W : Outer;
fn f0(p_indices : array<u32, 1u>) -> f32 {
return W.mat[p_indices[0u]].x;
}
fn f0_1(p_indices : array<u32, 2u>) -> f32 {
return W.arr[p_indices[0u]].mat[p_indices[1u]].x;
}
fn f1() -> f32 {
var res : f32;
let v = f0(array<u32, 1u>(u32(1i)));
res = (res + v);
let v_1 = f0(array<u32, 1u>(u32(1i)));
res = (res + v_1);
let v_2 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_2);
let v_3 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_3);
return res;
}
fn f1_1(p_indices : array<u32, 1u>) -> f32 {
let v_4 = p_indices[0u];
var res : f32;
let v_5 = f0_1(array<u32, 2u>(v_4, u32(1i)));
res = (res + v_5);
let v_6 = f0_1(array<u32, 2u>(v_4, u32(1i)));
res = (res + v_6);
let v_7 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_7);
let v_8 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_8);
return res;
}
fn f2(p_indices : array<u32, 1u>) -> f32 {
return f1_1(array<u32, 1u>(p_indices[0u]));
}
fn f3() -> f32 {
return (f2(array<u32, 1u>(u32(3i))) + f1());
}
fn f4() -> f32 {
return f3();
}
fn b() {
f4();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
} // namespace workgroup_as_tests
////////////////////////////////////////////////////////////////////////////////
// 'private' address space
////////////////////////////////////////////////////////////////////////////////
namespace private_as_tests {
using IR_DirectVariableAccessWgslTest_PrivateAS = DirectVariableAccessTest;
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_i32_read) {
auto* src = R"(
fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
return *(p);
}
var<private> P : i32;
fn b() {
a(10, &(P), 20);
}
)";
auto* expect = R"(
var<private> P : i32;
fn a(pre : i32, post : i32) -> i32 {
return P;
}
fn b() {
a(10i, 20i);
}
)";
auto got = Run(src, kTransformPrivate);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_i32_write) {
auto* src = R"(
fn a(pre : i32, p : ptr<private, i32>, post : i32) {
*(p) = 42;
}
var<private> P : i32;
fn b() {
a(10, &(P), 20);
}
)";
auto* expect = R"(
var<private> P : i32;
fn a(pre : i32, post : i32) {
P = 42i;
}
fn b() {
a(10i, 20i);
}
)";
auto got = Run(src, kTransformPrivate);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_i32_Via_struct_read) {
auto* src = R"(
struct str {
i : i32,
};
fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
return *p;
}
var<private> P : str;
fn b() {
a(10, &P.i, 20);
}
)";
auto* expect = R"(
struct str {
i : i32,
}
var<private> P : str;
fn a(pre : i32, post : i32) -> i32 {
return P.i;
}
fn b() {
a(10i, 20i);
}
)";
auto got = Run(src, kTransformPrivate);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Disabled_Param_ptr_i32_Via_struct_read) {
auto* src = R"(
struct str {
i : i32,
}
var<private> P : str;
fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
return *(p);
}
fn b() {
a(10i, &(P.i), 20i);
}
)";
auto* expect = src;
wgsl::writer::ProgramOptions program_options;
program_options.allowed_features.features.emplace(
wgsl::LanguageFeature::kUnrestrictedPointerParameters);
auto got = Run(src, /* transform_options */ {}, program_options);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_arr_i32_Via_struct_write) {
auto* src = R"(
struct str {
arr : array<i32, 4>,
};
fn a(pre : i32, p : ptr<private, array<i32, 4>>, post : i32) {
*p = array<i32, 4>();
}
var<private> P : str;
fn b() {
a(10, &P.arr, 20);
}
)";
auto* expect = R"(
struct str {
arr : array<i32, 4u>,
}
var<private> P : str;
fn a(pre : i32, post : i32) {
P.arr = array<i32, 4u>();
}
fn b() {
a(10i, 20i);
}
)";
auto got = Run(src, kTransformPrivate);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Disabled_Param_ptr_arr_i32_Via_struct_write) {
auto* src = R"(
struct str {
arr : array<i32, 4u>,
}
var<private> P : str;
fn a(pre : i32, p : ptr<private, array<i32, 4u>>, post : i32) {
*(p) = array<i32, 4u>();
}
fn b() {
a(10i, &(P.arr), 20i);
}
)";
auto* expect = src;
wgsl::writer::ProgramOptions program_options;
program_options.allowed_features.features.emplace(
wgsl::LanguageFeature::kUnrestrictedPointerParameters);
auto got = Run(src, /* transform_options */ {}, program_options);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_Param_ptr_i32_mixed) {
auto* src = R"(
struct str {
i : i32,
};
fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
return *p;
}
var<private> Pi : i32;
var<private> Ps : str;
var<private> Pa : array<i32, 4>;
fn b() {
a(10, &Pi, 20);
a(30, &Ps.i, 40);
a(50, &Pa[2], 60);
}
)";
auto* expect = R"(
var<private> Pi : i32;
struct str {
i : i32,
}
var<private> Ps : str;
var<private> Pa : array<i32, 4u>;
fn a(pre : i32, post : i32) -> i32 {
return Pi;
}
fn a_1(pre : i32, post : i32) -> i32 {
return Ps.i;
}
fn a_2(pre : i32, p_indices : array<u32, 1u>, post : i32) -> i32 {
return Pa[p_indices[0u]];
}
fn b() {
a(10i, 20i);
a_1(30i, 40i);
a_2(50i, array<u32, 1u>(u32(2i)), 60i);
}
)";
auto got = Run(src, kTransformPrivate);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Disabled_Param_ptr_i32_mixed) {
auto* src = R"(
var<private> Pi : i32;
struct str {
i : i32,
}
var<private> Ps : str;
var<private> Pa : array<i32, 4u>;
fn a(pre : i32, p : ptr<private, i32>, post : i32) -> i32 {
return *(p);
}
fn b() {
a(10i, &(Pi), 20i);
a(30i, &(Ps.i), 40i);
a(50i, &(Pa[2i]), 60i);
}
)";
auto* expect = src;
wgsl::writer::ProgramOptions program_options;
program_options.allowed_features.features.emplace(
wgsl::LanguageFeature::kUnrestrictedPointerParameters);
auto got = Run(src, /* transform_options */ {}, program_options);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Enabled_CallChaining) {
auto* src = R"(
struct Inner {
mat : mat3x4<f32>,
};
alias InnerArr = array<Inner, 4>;
struct Outer {
arr : InnerArr,
mat : mat3x4<f32>,
};
var<private> P : Outer;
fn f0(p : ptr<private, vec4<f32>>) -> f32 {
return (*p).x;
}
fn f1(p : ptr<private, mat3x4<f32>>) -> 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<private, Inner>) -> f32 {
let p_mat = &(*p).mat;
return f1(p_mat);
}
fn f3(p0 : ptr<private, InnerArr>, p1 : ptr<private, mat3x4<f32>>) -> f32 {
let p0_inner = &(*p0)[3];
return f2(p0_inner) + f1(p1);
}
fn f4(p : ptr<private, Outer>) -> f32 {
return f3(&(*p).arr, &P.mat);
}
fn b() {
f4(&P);
}
)";
auto* expect = R"(
struct Inner {
mat : mat3x4<f32>,
}
struct Outer {
arr : array<Inner, 4u>,
mat : mat3x4<f32>,
}
var<private> P : Outer;
fn f0(p_indices : array<u32, 1u>) -> f32 {
return P.mat[p_indices[0u]].x;
}
fn f0_1(p_indices : array<u32, 2u>) -> f32 {
return P.arr[p_indices[0u]].mat[p_indices[1u]].x;
}
fn f1() -> f32 {
var res : f32;
let v = f0(array<u32, 1u>(u32(1i)));
res = (res + v);
let v_1 = f0(array<u32, 1u>(u32(1i)));
res = (res + v_1);
let v_2 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_2);
let v_3 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_3);
return res;
}
fn f1_1(p_indices : array<u32, 1u>) -> f32 {
let v_4 = p_indices[0u];
var res : f32;
let v_5 = f0_1(array<u32, 2u>(v_4, u32(1i)));
res = (res + v_5);
let v_6 = f0_1(array<u32, 2u>(v_4, u32(1i)));
res = (res + v_6);
let v_7 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_7);
let v_8 = f0_1(array<u32, 2u>(u32(2i), u32(1i)));
res = (res + v_8);
return res;
}
fn f2(p_indices : array<u32, 1u>) -> f32 {
return f1_1(array<u32, 1u>(p_indices[0u]));
}
fn f3() -> f32 {
return (f2(array<u32, 1u>(u32(3i))) + f1());
}
fn f4() -> f32 {
return f3();
}
fn b() {
f4();
}
)";
auto got = Run(src, kTransformPrivate);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_PrivateAS, Disabled_CallChaining) {
auto* src = R"(
struct Inner {
mat : mat3x4<f32>,
}
struct Outer {
arr : array<Inner, 4u>,
mat : mat3x4<f32>,
}
var<private> P : Outer;
fn f0(p : ptr<private, vec4<f32>>) -> f32 {
return (*(p)).x;
}
fn f1(p : ptr<private, mat3x4<f32>>) -> f32 {
var res : f32;
let v = f0(&((*(p))[1i]));
res = (res + v);
let p_vec = &((*(p))[1i]);
let v_1 = f0(p_vec);
res = (res + v_1);
let v_2 = f0(&(P.arr[2i].mat[1i]));
res = (res + v_2);
let p_vec_1 = &(P.arr[2i].mat[1i]);
let v_3 = f0(p_vec_1);
res = (res + v_3);
return res;
}
fn f2(p : ptr<private, Inner>) -> f32 {
let p_mat = &((*(p)).mat);
return f1(p_mat);
}
fn f3(p0 : ptr<private, array<Inner, 4u>>, p1 : ptr<private, mat3x4<f32>>) -> f32 {
let p0_inner = &((*(p0))[3i]);
return (f2(p0_inner) + f1(p1));
}
fn f4(p : ptr<private, Outer>) -> f32 {
return f3(&((*(p)).arr), &(P.mat));
}
fn b() {
f4(&(P));
}
)";
auto* expect = src;
wgsl::writer::ProgramOptions program_options;
program_options.allowed_features.features.emplace(
wgsl::LanguageFeature::kUnrestrictedPointerParameters);
auto got = Run(src, /* transform_options */ {}, program_options);
EXPECT_EQ(expect, got);
}
} // namespace private_as_tests
////////////////////////////////////////////////////////////////////////////////
// 'function' address space
////////////////////////////////////////////////////////////////////////////////
namespace function_as_tests {
using IR_DirectVariableAccessWgslTest_FunctionAS = DirectVariableAccessTest;
TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_LocalPtr) {
auto* src = R"(
fn f() {
var v : i32;
let p = &(v);
var x : i32 = *(p);
}
)";
auto* expect = src; // Nothing changes
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_i32_read) {
auto* src = R"(
fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
return *(p);
}
fn b() {
var F : i32;
a(10, &(F), 20);
}
)";
auto* expect = R"(
fn a(pre : i32, p_root : ptr<function, i32>, post : i32) -> i32 {
return *(p_root);
}
fn b() {
var F : i32;
a(10i, &(F), 20i);
}
)";
auto got = Run(src, kTransformFunction);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_i32_write) {
auto* src = R"(
fn a(pre : i32, p : ptr<function, i32>, post : i32) {
*(p) = 42;
}
fn b() {
var F : i32;
a(10, &(F), 20);
}
)";
auto* expect = R"(
fn a(pre : i32, p_root : ptr<function, i32>, post : i32) {
*(p_root) = 42i;
}
fn b() {
var F : i32;
a(10i, &(F), 20i);
}
)";
auto got = Run(src, kTransformFunction);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_i32_Via_struct_read) {
auto* src = R"(
struct str {
i : i32,
};
fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
return *p;
}
fn b() {
var F : str;
a(10, &F.i, 20);
}
)";
auto* expect = R"(
struct str {
i : i32,
}
fn a(pre : i32, p_root : ptr<function, str>, post : i32) -> i32 {
return (*(p_root)).i;
}
fn b() {
var F : str;
a(10i, &(F), 20i);
}
)";
auto got = Run(src, kTransformFunction);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_arr_i32_Via_struct_write) {
auto* src = R"(
struct str {
arr : array<i32, 4>,
};
fn a(pre : i32, p : ptr<function, array<i32, 4>>, post : i32) {
*p = array<i32, 4>();
}
fn b() {
var F : str;
a(10, &F.arr, 20);
}
)";
auto* expect = R"(
struct str {
arr : array<i32, 4u>,
}
fn a(pre : i32, p_root : ptr<function, str>, post : i32) {
(*(p_root)).arr = array<i32, 4u>();
}
fn b() {
var F : str;
a(10i, &(F), 20i);
}
)";
auto got = Run(src, kTransformFunction);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Enabled_Param_ptr_i32_mixed) {
auto* src = R"(
struct str {
i : i32,
};
fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
return *p;
}
fn b() {
var Fi : i32;
var Fs : str;
var Fa : array<i32, 4>;
a(10, &Fi, 20);
a(30, &Fs.i, 40);
a(50, &Fa[2], 60);
}
)";
auto* expect = R"(
fn a(pre : i32, p_root : ptr<function, i32>, post : i32) -> i32 {
return *(p_root);
}
struct str {
i : i32,
}
fn a_1(pre : i32, p_root : ptr<function, str>, post : i32) -> i32 {
return (*(p_root)).i;
}
fn a_2(pre : i32, p_root : ptr<function, array<i32, 4u>>, p_indices : array<u32, 1u>, post : i32) -> i32 {
return (*(p_root))[p_indices[0u]];
}
fn b() {
var Fi : i32;
var Fs : str;
var Fa : array<i32, 4u>;
a(10i, &(Fi), 20i);
a_1(30i, &(Fs), 40i);
a_2(50i, &(Fa), array<u32, 1u>(u32(2i)), 60i);
}
)";
auto got = Run(src, kTransformFunction);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Disabled_Param_ptr_i32_Via_struct_read) {
auto* src = R"(
fn a(pre : i32, p : ptr<function, i32>, post : i32) -> i32 {
return *(p);
}
struct str {
i : i32,
}
fn b() {
var F : str;
a(10i, &(F.i), 20i);
}
)";
auto* expect = src;
wgsl::writer::ProgramOptions program_options;
program_options.allowed_features.features.emplace(
wgsl::LanguageFeature::kUnrestrictedPointerParameters);
auto got = Run(src, /* transform_options */ {}, program_options);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_FunctionAS, Disabled_Param_ptr_arr_i32_Via_struct_write) {
auto* src = R"(
fn a(pre : i32, p : ptr<function, array<i32, 4u>>, post : i32) {
*(p) = array<i32, 4u>();
}
struct str {
arr : array<i32, 4u>,
}
fn b() {
var F : str;
a(10i, &(F.arr), 20i);
}
)";
auto* expect = src;
wgsl::writer::ProgramOptions program_options;
program_options.allowed_features.features.emplace(
wgsl::LanguageFeature::kUnrestrictedPointerParameters);
auto got = Run(src, /* transform_options */ {}, program_options);
EXPECT_EQ(expect, got);
}
} // namespace function_as_tests
////////////////////////////////////////////////////////////////////////////////
// builtin function calls
////////////////////////////////////////////////////////////////////////////////
namespace builtin_fn_calls {
using IR_DirectVariableAccessWgslTest_BuiltinFn = DirectVariableAccessTest;
TEST_F(IR_DirectVariableAccessWgslTest_BuiltinFn, ArrayLength) {
auto* src = R"(
@group(0) @binding(0) var<storage> S : array<f32>;
fn len(p : ptr<storage, array<f32>>) -> u32 {
return arrayLength(p);
}
fn f() {
let n = len(&S);
}
)";
auto* expect = R"(
@group(0) @binding(0) var<storage, read> S : array<f32>;
fn len() -> u32 {
return arrayLength(&(S));
}
fn f() {
let n = len();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_BuiltinFn, WorkgroupUniformLoad) {
auto* src = R"(
var<workgroup> W : f32;
fn load(p : ptr<workgroup, f32>) -> f32 {
return workgroupUniformLoad(p);
}
fn f() {
let v = load(&W);
}
)";
auto* expect = R"(
var<workgroup> W : f32;
fn load() -> f32 {
return workgroupUniformLoad(&(W));
}
fn f() {
let v = load();
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
} // namespace builtin_fn_calls
////////////////////////////////////////////////////////////////////////////////
// complex tests
////////////////////////////////////////////////////////////////////////////////
namespace complex_tests {
using IR_DirectVariableAccessWgslTest_Complex = DirectVariableAccessTest;
TEST_F(IR_DirectVariableAccessWgslTest_Complex, Param_ptr_mixed_vec4i32_ViaMultiple) {
auto* src = R"(
struct str {
i : vec4<i32>,
};
@group(0) @binding(0) var<uniform> U : vec4<i32>;
@group(0) @binding(1) var<uniform> U_str : str;
@group(0) @binding(2) var<uniform> U_arr : array<vec4<i32>, 8>;
@group(0) @binding(3) var<uniform> U_arr_arr : array<array<vec4<i32>, 8>, 4>;
@group(1) @binding(0) var<storage> S : vec4<i32>;
@group(1) @binding(1) var<storage> S_str : str;
@group(1) @binding(2) var<storage> S_arr : array<vec4<i32>, 8>;
@group(1) @binding(3) var<storage> S_arr_arr : array<array<vec4<i32>, 8>, 4>;
var<workgroup> W : vec4<i32>;
var<workgroup> W_str : str;
var<workgroup> W_arr : array<vec4<i32>, 8>;
var<workgroup> W_arr_arr : array<array<vec4<i32>, 8>, 4>;
fn fn_u(p : ptr<uniform, vec4<i32>>) -> vec4<i32> {
return *p;
}
fn fn_s(p : ptr<storage, vec4<i32>>) -> vec4<i32> {
return *p;
}
fn fn_w(p : ptr<workgroup, vec4<i32>>) -> vec4<i32> {
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"(
@group(0) @binding(0) var<uniform> U : vec4<i32>;
struct str {
i : vec4<i32>,
}
@group(0) @binding(1) var<uniform> U_str : str;
@group(0) @binding(2) var<uniform> U_arr : array<vec4<i32>, 8u>;
@group(0) @binding(3) var<uniform> U_arr_arr : array<array<vec4<i32>, 8u>, 4u>;
@group(1) @binding(0) var<storage, read> S : vec4<i32>;
@group(1) @binding(1) var<storage, read> S_str : str;
@group(1) @binding(2) var<storage, read> S_arr : array<vec4<i32>, 8u>;
@group(1) @binding(3) var<storage, read> S_arr_arr : array<array<vec4<i32>, 8u>, 4u>;
var<workgroup> W : vec4<i32>;
var<workgroup> W_str : str;
var<workgroup> W_arr : array<vec4<i32>, 8u>;
var<workgroup> W_arr_arr : array<array<vec4<i32>, 8u>, 4u>;
fn fn_u() -> vec4<i32> {
return U;
}
fn fn_u_1() -> vec4<i32> {
return U_str.i;
}
fn fn_u_2(p_indices : array<u32, 1u>) -> vec4<i32> {
return U_arr[p_indices[0u]];
}
fn fn_u_3(p_indices : array<u32, 2u>) -> vec4<i32> {
return U_arr_arr[p_indices[0u]][p_indices[1u]];
}
fn fn_s() -> vec4<i32> {
return S;
}
fn fn_s_1() -> vec4<i32> {
return S_str.i;
}
fn fn_s_2(p_indices : array<u32, 1u>) -> vec4<i32> {
return S_arr[p_indices[0u]];
}
fn fn_s_3(p_indices : array<u32, 2u>) -> vec4<i32> {
return S_arr_arr[p_indices[0u]][p_indices[1u]];
}
fn fn_w() -> vec4<i32> {
return W;
}
fn fn_w_1() -> vec4<i32> {
return W_str.i;
}
fn fn_w_2(p_indices : array<u32, 1u>) -> vec4<i32> {
return W_arr[p_indices[0u]];
}
fn fn_w_3(p_indices : array<u32, 2u>) -> vec4<i32> {
return W_arr_arr[p_indices[0u]][p_indices[1u]];
}
fn b() {
let I = 3i;
let J = 4i;
let u = fn_u();
let u_str = fn_u_1();
let u_arr0 = fn_u_2(array<u32, 1u>(u32(0i)));
let u_arr1 = fn_u_2(array<u32, 1u>(u32(1i)));
let u_arrI = fn_u_2(array<u32, 1u>(u32(I)));
let u_arr1_arr0 = fn_u_3(array<u32, 2u>(u32(1i), u32(0i)));
let u_arr2_arrI = fn_u_3(array<u32, 2u>(u32(2i), u32(I)));
let u_arrI_arr2 = fn_u_3(array<u32, 2u>(u32(I), u32(2i)));
let u_arrI_arrJ = fn_u_3(array<u32, 2u>(u32(I), u32(J)));
let s = fn_s();
let s_str = fn_s_1();
let s_arr0 = fn_s_2(array<u32, 1u>(u32(0i)));
let s_arr1 = fn_s_2(array<u32, 1u>(u32(1i)));
let s_arrI = fn_s_2(array<u32, 1u>(u32(I)));
let s_arr1_arr0 = fn_s_3(array<u32, 2u>(u32(1i), u32(0i)));
let s_arr2_arrI = fn_s_3(array<u32, 2u>(u32(2i), u32(I)));
let s_arrI_arr2 = fn_s_3(array<u32, 2u>(u32(I), u32(2i)));
let s_arrI_arrJ = fn_s_3(array<u32, 2u>(u32(I), u32(J)));
let w = fn_w();
let w_str = fn_w_1();
let w_arr0 = fn_w_2(array<u32, 1u>(u32(0i)));
let w_arr1 = fn_w_2(array<u32, 1u>(u32(1i)));
let w_arrI = fn_w_2(array<u32, 1u>(u32(I)));
let w_arr1_arr0 = fn_w_3(array<u32, 2u>(u32(1i), u32(0i)));
let w_arr2_arrI = fn_w_3(array<u32, 2u>(u32(2i), u32(I)));
let w_arrI_arr2 = fn_w_3(array<u32, 2u>(u32(I), u32(2i)));
let w_arrI_arrJ = fn_w_3(array<u32, 2u>(u32(I), u32(J)));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_Complex, Indexing) {
auto* src = R"(
@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
fn a(i : i32) -> i32 { return i; }
fn b(p : ptr<storage, array<array<array<i32, 9>, 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"(
@group(0) @binding(0) var<storage, read> S : array<array<array<array<i32, 9u>, 9u>, 9u>, 50u>;
fn a(i : i32) -> i32 {
return i;
}
fn b(p_indices : array<u32, 1u>) -> i32 {
let v = &(S[p_indices[0u]]);
return (*(v))[a((*(v))[0i][1i][2i])][a((*(v))[a(3i)][4i][5i])][a((*(v))[6i][a(7i)][8i])];
}
fn c() {
let v = b(array<u32, 1u>(u32(42i)));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_Complex, IndexingInPtrCall) {
auto* src = R"(
@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
fn a(pre : i32, i : ptr<storage, i32>, post : i32) -> i32 {
return *i;
}
fn b(p : ptr<storage, array<array<array<i32, 9>, 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"(
@group(0) @binding(0) var<storage, read> S : array<array<array<array<i32, 9u>, 9u>, 9u>, 50u>;
fn a(pre : i32, i_indices : array<u32, 4u>, post : i32) -> i32 {
return S[i_indices[0u]][i_indices[1u]][i_indices[2u]][i_indices[3u]];
}
fn b(p_indices : array<u32, 1u>) -> i32 {
let v = p_indices[0u];
let v_1 = a(20i, array<u32, 4u>(v, u32(0i), u32(1i), u32(2i)), 30i);
let v_2 = a(40i, array<u32, 4u>(v, u32(3i), u32(4i), u32(5i)), 50i);
return a(10i, array<u32, 4u>(v, u32(v_1), u32(v_2), u32(a(60i, array<u32, 4u>(v, u32(6i), u32(7i), u32(8i)), 70i))), 80i);
}
fn c() {
let v = b(array<u32, 1u>(u32(42i)));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
TEST_F(IR_DirectVariableAccessWgslTest_Complex, IndexingDualPointers) {
auto* src = R"(
@group(0) @binding(0) var<storage> S : array<array<array<i32, 9>, 9>, 50>;
@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 9>, 9>, 50>;
fn a(i : i32) -> i32 { return i; }
fn b(s : ptr<storage, array<array<i32, 9>, 9>>,
u : ptr<uniform, array<array<vec4<i32>, 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"(
@group(0) @binding(0) var<storage, read> S : array<array<array<i32, 9u>, 9u>, 50u>;
@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 9u>, 9u>, 50u>;
fn a(i : i32) -> i32 {
return i;
}
fn b(s_indices : array<u32, 1u>, u_indices : array<u32, 1u>) -> i32 {
let v = &(U[u_indices[0u]]);
return S[s_indices[0u]][a((*(v))[0i][1i].x)][a((*(v))[a(3i)][4i].y)];
}
fn c() {
let v = b(array<u32, 1u>(u32(42i)), array<u32, 1u>(u32(24i)));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, got);
}
} // namespace complex_tests
} // namespace
} // namespace tint::core::ir::transform