blob: 1cd715f61db1cb596a88252973565fe2d053c75d [file] [log] [blame]
// Copyright 2020 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/wgsl/ast/transform/robustness.h"
#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
namespace tint::ast::transform {
static std::ostream& operator<<(std::ostream& out, Robustness::Action action) {
switch (action) {
case Robustness::Action::kIgnore:
return out << "ignore";
case Robustness::Action::kClamp:
return out << "clamp";
case Robustness::Action::kPredicate:
return out << "predicate";
}
return out << "unknown";
}
namespace {
DataMap Config(Robustness::Action action, bool disable_runtime_sized_array_index_clamping = false) {
Robustness::Config cfg;
cfg.value_action = action;
cfg.texture_action = action;
cfg.function_action = action;
cfg.private_action = action;
cfg.push_constant_action = action;
cfg.storage_action = action;
cfg.uniform_action = action;
cfg.workgroup_action = action;
cfg.disable_runtime_sized_array_index_clamping = disable_runtime_sized_array_index_clamping;
DataMap data;
data.Add<Robustness::Config>(cfg);
return data;
}
const char* Expect(Robustness::Action action,
const char* expect_ignore,
const char* expect_clamp,
const char* expect_predicate) {
switch (action) {
case Robustness::Action::kIgnore:
return expect_ignore;
case Robustness::Action::kClamp:
return expect_clamp;
case Robustness::Action::kPredicate:
return expect_predicate;
}
return "<invalid action>";
}
using RobustnessTest = TransformTestWithParam<Robustness::Action>;
////////////////////////////////////////////////////////////////////////////////
// Constant sized array
////////////////////////////////////////////////////////////////////////////////
TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithLiteral) {
auto* src = R"(
fn f() {
var b : f32 = array<f32, 3>()[1i];
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithConst) {
auto* src = R"(
const c : u32 = 1u;
fn f() {
let b : f32 = array<f32, 3>()[c];
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithLet) {
auto* src = R"(
fn f() {
let l : u32 = 1u;
let b : f32 = array<f32, 3>()[l];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
fn f() {
let l : u32 = 1u;
let b : f32 = array<f32, 3>()[min(l, 2u)];
}
)",
/* predicate */ R"(
fn f() {
let l : u32 = 1u;
let index = l;
let predicate = (u32(index) <= 2u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = array<f32, 3>()[index];
}
let b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithRuntimeArrayIndex) {
auto* src = R"(
var<private> i : u32;
fn f() {
let a = array<f32, 3>();
let b = array<i32, 5>();
var c : f32 = a[b[i]];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> i : u32;
fn f() {
let a = array<f32, 3>();
let b = array<i32, 5>();
var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)];
}
)",
/* predicate */ R"(
var<private> i : u32;
fn f() {
let a = array<f32, 3>();
let b = array<i32, 5>();
let index = i;
let predicate = (u32(index) <= 4u);
var predicated_expr : i32;
if (predicate) {
predicated_expr = b[index];
}
let index_1 = predicated_expr;
let predicate_1 = (u32(index_1) <= 2u);
var predicated_expr_1 : f32;
if (predicate_1) {
predicated_expr_1 = a[index_1];
}
var c : f32 = predicated_expr_1;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithRuntimeExpression) {
auto* src = R"(
var<private> c : i32;
fn f() {
var b : f32 = array<f32, 3>()[((c + 2) - 3)];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> c : i32;
fn f() {
var b : f32 = array<f32, 3>()[min(u32(((c + 2) - 3)), 2u)];
}
)",
/* predicate */ R"(
var<private> c : i32;
fn f() {
let index = ((c + 2) - 3);
let predicate = (u32(index) <= 2u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = array<f32, 3>()[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_NestedConstantSizedArraysVal_IndexWithRuntimeExpressions) {
auto* src = R"(
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let a = array<array<array<f32, 1>, 2>, 3>();
var r = a[x][y][z];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let a = array<array<array<f32, 1>, 2>, 3>();
var r = a[min(u32(x), 2u)][min(u32(y), 1u)][min(u32(z), 0u)];
}
)",
/* predicate */ R"(
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let a = array<array<array<f32, 1>, 2>, 3>();
let index = x;
let predicate = (u32(index) <= 2u);
var predicated_expr : array<array<f32, 1u>, 2u>;
if (predicate) {
predicated_expr = a[index];
}
let index_1 = y;
let predicate_1 = (u32(index_1) <= 1u);
var predicated_expr_1 : array<f32, 1u>;
if (predicate_1) {
predicated_expr_1 = predicated_expr[index_1];
}
let index_2 = z;
let predicate_2 = (u32(index_2) <= 0u);
var predicated_expr_2 : f32;
if (predicate_2) {
predicated_expr_2 = predicated_expr_1[index_2];
}
var r = predicated_expr_2;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayVal_IndexWithOverride) {
auto* src = R"(
@id(1300) override idx : i32;
fn f() {
let a = array<f32, 4>();
var b : f32 = a[idx];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@id(1300) override idx : i32;
fn f() {
let a = array<f32, 4>();
var b : f32 = a[min(u32(idx), 3u)];
}
)",
/* predicate */ R"(
@id(1300) override idx : i32;
fn f() {
let a = array<f32, 4>();
let index = idx;
let predicate = (u32(index) <= 3u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithLiteral) {
auto* src = R"(
var<private> a : array<f32, 3>;
fn f() {
var b : f32 = a[1i];
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithConst) {
auto* src = R"(
var<private> a : array<f32, 3>;
const c : u32 = 1u;
fn f() {
let b : f32 = a[c];
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithLet) {
auto* src = R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let b : f32 = a[l];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let b : f32 = a[min(l, 2u)];
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let index = l;
let predicate = (u32(index) <= 2u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[index];
}
let b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithRuntimeArrayIndex) {
auto* src = R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
var c : f32 = a[b[i]];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)];
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let index = i;
let predicate = (u32(index) <= 4u);
var predicated_expr : i32;
if (predicate) {
predicated_expr = b[index];
}
let index_1 = predicated_expr;
let predicate_1 = (u32(index_1) <= 2u);
var predicated_expr_1 : f32;
if (predicate_1) {
predicated_expr_1 = a[index_1];
}
var c : f32 = predicated_expr_1;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithRuntimeArrayIndexViaPointerIndex) {
auto* src = R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let p1 = &(a);
let p2 = &(b);
var c : f32 = p1[p2[i]];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let p1 = &(a);
let p2 = &(b);
var c : f32 = p1[min(u32(p2[min(i, 4u)]), 2u)];
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let p1 = &(a);
let p2 = &(b);
let index = i;
let predicate = (u32(index) <= 4u);
var predicated_expr : i32;
if (predicate) {
predicated_expr = p2[index];
}
let index_1 = predicated_expr;
let predicate_1 = (u32(index_1) <= 2u);
var predicated_expr_1 : f32;
if (predicate_1) {
predicated_expr_1 = p1[index_1];
}
var c : f32 = predicated_expr_1;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithRuntimeExpression) {
auto* src = R"(
var<private> a : array<f32, 3>;
var<private> c : i32;
fn f() {
var b : f32 = a[((c + 2) - 3)];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
var<private> c : i32;
fn f() {
var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
var<private> c : i32;
fn f() {
let index = ((c + 2) - 3);
let predicate = (u32(index) <= 2u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_NestedConstantSizedArraysRef_IndexWithRuntimeExpressions) {
auto* src = R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
var r = a[x][y][z];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
var r = a[min(u32(x), 2u)][min(u32(y), 1u)][min(u32(z), 0u)];
}
)",
/* predicate */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let index = x;
let predicate = (u32(index) <= 2u);
let index_1 = y;
let predicate_1 = (predicate & (u32(index_1) <= 1u));
let index_2 = z;
let predicate_2 = (predicate_1 & (u32(index_2) <= 0u));
var predicated_expr : f32;
if (predicate_2) {
predicated_expr = a[index][index_1][index_2];
}
var r = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayRef_IndexWithOverride) {
auto* src = R"(
@id(1300) override idx : i32;
fn f() {
var a : array<f32, 4>;
var b : f32 = a[idx];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@id(1300) override idx : i32;
fn f() {
var a : array<f32, 4>;
var b : f32 = a[min(u32(idx), 3u)];
}
)",
/* predicate */ R"(
@id(1300) override idx : i32;
fn f() {
var a : array<f32, 4>;
let index = idx;
let predicate = (u32(index) <= 3u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayPtr_IndexWithLet) {
auto* src = R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let p = &(a[l]);
let f : f32 = *(p);
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let p = &(a[min(l, 2u)]);
let f : f32 = *(p);
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let index = l;
let predicate = (u32(index) <= 2u);
let p = &(a[index]);
var predicated_expr : f32;
if (predicate) {
predicated_expr = *(p);
}
let f : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_ConstantSizedArrayPtr_IndexWithRuntimeArrayIndex) {
auto* src = R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let pa = &(a);
let pb = &(b);
let p0 = &((*(pb))[i]);
let p1 = &(a[*(p0)]);
var x : f32 = *(p1);
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let pa = &(a);
let pb = &(b);
let p0 = &((*(pb))[min(i, 4u)]);
let p1 = &(a[min(u32(*(p0)), 2u)]);
var x : f32 = *(p1);
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let pa = &(a);
let pb = &(b);
let index = i;
let predicate = (u32(index) <= 4u);
let p0 = &((*(pb))[index]);
var predicated_expr : i32;
if (predicate) {
predicated_expr = *(p0);
}
let index_1 = predicated_expr;
let predicate_1 = (u32(index_1) <= 2u);
let p1 = &(a[index_1]);
var predicated_expr_1 : f32;
if (predicate_1) {
predicated_expr_1 = *(p1);
}
var x : f32 = predicated_expr_1;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_NestedConstantSizedArraysPtr_IndexWithRuntimeExpressions) {
auto* src = R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let p0 = &(a);
let p1 = &((*(p0))[x]);
let p2 = &((*(p1))[y]);
let p3 = &((*(p2))[z]);
var r = *(p3);
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let p0 = &(a);
let p1 = &((*(p0))[min(u32(x), 2u)]);
let p2 = &((*(p1))[min(u32(y), 1u)]);
let p3 = &((*(p2))[min(u32(z), 0u)]);
var r = *(p3);
}
)",
/* predicate */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let p0 = &(a);
let index = x;
let predicate = (u32(index) <= 2u);
let p1 = &((*(p0))[index]);
let index_1 = y;
let predicate_1 = (predicate & (u32(index_1) <= 1u));
let p2 = &((*(p1))[index_1]);
let index_2 = z;
let predicate_2 = (predicate_1 & (u32(index_2) <= 0u));
let p3 = &((*(p2))[index_2]);
var predicated_expr : f32;
if (predicate_2) {
predicated_expr = *(p3);
}
var r = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_NestedConstantSizedArrays_MixedAccess) {
auto* src = R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
const y = 1;
override z : i32;
fn f() {
let p0 = &(a);
let p1 = &((*(p0))[x]);
let p2 = &((*(p1))[y]);
let p3 = &((*(p2))[z]);
var r = *(p3);
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
const y = 1;
override z : i32;
fn f() {
let p0 = &(a);
let p1 = &((*(p0))[min(u32(x), 2u)]);
let p2 = &((*(p1))[y]);
let p3 = &((*(p2))[min(u32(z), 0u)]);
var r = *(p3);
}
)",
/* predicate */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
const y = 1;
override z : i32;
fn f() {
let p0 = &(a);
let index = x;
let predicate = (u32(index) <= 2u);
let p1 = &((*(p0))[index]);
let p2 = &((*(p1))[y]);
let index_1 = z;
let predicate_1 = (predicate & (u32(index_1) <= 0u));
let p3 = &((*(p2))[index_1]);
var predicated_expr : f32;
if (predicate_1) {
predicated_expr = *(p3);
}
var r = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Assign_ConstantSizedArray_IndexWithLet) {
auto* src = R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
a[l] = 42.0f;
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
a[min(l, 2u)] = 42.0f;
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let index = l;
let predicate = (u32(index) <= 2u);
if (predicate) {
a[index] = 42.0f;
}
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Assign_ConstantSizedArrayPtr_IndexWithLet) {
auto* src = R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let p = &(a[l]);
*(p) = 42.0f;
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let p = &(a[min(l, 2u)]);
*(p) = 42.0f;
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let index = l;
let predicate = (u32(index) <= 2u);
let p = &(a[index]);
if (predicate) {
*(p) = 42.0f;
}
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Assign_ConstantSizedArrayPtr_IndexWithRuntimeArrayIndex) {
auto* src = R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let pa = &(a);
let pb = &(b);
let p0 = &((*(pb))[i]);
let p1 = &(a[*(p0)]);
*(p1) = 42.0f;
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let pa = &(a);
let pb = &(b);
let p0 = &((*(pb))[min(i, 4u)]);
let p1 = &(a[min(u32(*(p0)), 2u)]);
*(p1) = 42.0f;
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
var<private> b : array<i32, 5>;
var<private> i : u32;
fn f() {
let pa = &(a);
let pb = &(b);
let index = i;
let predicate = (u32(index) <= 4u);
let p0 = &((*(pb))[index]);
var predicated_expr : i32;
if (predicate) {
predicated_expr = *(p0);
}
let index_1 = predicated_expr;
let predicate_1 = (u32(index_1) <= 2u);
let p1 = &(a[index_1]);
if (predicate_1) {
*(p1) = 42.0f;
}
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Assign_NestedConstantSizedArraysPtr_IndexWithRuntimeExpressions) {
auto* src = R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let p0 = &(a);
let p1 = &((*(p0))[x]);
let p2 = &((*(p1))[y]);
let p3 = &((*(p2))[z]);
*(p3) = 42.0f;
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let p0 = &(a);
let p1 = &((*(p0))[min(u32(x), 2u)]);
let p2 = &((*(p1))[min(u32(y), 1u)]);
let p3 = &((*(p2))[min(u32(z), 0u)]);
*(p3) = 42.0f;
}
)",
/* predicate */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
var<private> y : i32;
var<private> z : i32;
fn f() {
let p0 = &(a);
let index = x;
let predicate = (u32(index) <= 2u);
let p1 = &((*(p0))[index]);
let index_1 = y;
let predicate_1 = (predicate & (u32(index_1) <= 1u));
let p2 = &((*(p1))[index_1]);
let index_2 = z;
let predicate_2 = (predicate_1 & (u32(index_2) <= 0u));
let p3 = &((*(p2))[index_2]);
if (predicate_2) {
*(p3) = 42.0f;
}
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Assign_NestedConstantSizedArrays_MixedAccess) {
auto* src = R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
const y = 1;
override z : i32;
fn f() {
let p0 = &(a);
let p1 = &((*(p0))[x]);
let p2 = &((*(p1))[y]);
let p3 = &((*(p2))[z]);
*(p3) = 42.0f;
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
const y = 1;
override z : i32;
fn f() {
let p0 = &(a);
let p1 = &((*(p0))[min(u32(x), 2u)]);
let p2 = &((*(p1))[y]);
let p3 = &((*(p2))[min(u32(z), 0u)]);
*(p3) = 42.0f;
}
)",
/* predicate */ R"(
var<private> a : array<array<array<f32, 1>, 2>, 3>;
var<private> x : i32;
const y = 1;
override z : i32;
fn f() {
let p0 = &(a);
let index = x;
let predicate = (u32(index) <= 2u);
let p1 = &((*(p0))[index]);
let p2 = &((*(p1))[y]);
let index_1 = z;
let predicate_1 = (predicate & (u32(index_1) <= 0u));
let p3 = &((*(p2))[index_1]);
if (predicate_1) {
*(p3) = 42.0f;
}
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, CompoundAssign_ConstantSizedArray_IndexWithLet) {
auto* src = R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
a[l] += 42.0f;
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
a[min(l, 2u)] += 42.0f;
}
)",
/* predicate */ R"(
var<private> a : array<f32, 3>;
fn f() {
let l : u32 = 1u;
let index = l;
let predicate = (u32(index) <= 2u);
if (predicate) {
a[index] += 42.0f;
}
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Increment_ConstantSizedArray_IndexWithLet) {
auto* src = R"(
var<private> a : array<i32, 3>;
fn f() {
let l : u32 = 1u;
a[l]++;
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : array<i32, 3>;
fn f() {
let l : u32 = 1u;
a[min(l, 2u)]++;
}
)",
/* predicate */ R"(
var<private> a : array<i32, 3>;
fn f() {
let l : u32 = 1u;
let index = l;
let predicate = (u32(index) <= 2u);
if (predicate) {
a[index]++;
}
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// Runtime sized array
////////////////////////////////////////////////////////////////////////////////
TEST_P(RobustnessTest, Read_RuntimeArray_IndexWithLiteral) {
auto* src = R"(
struct S {
a : f32,
b : array<f32>,
}
@group(0) @binding(0) var<storage, read> s : S;
fn f() {
var d : f32 = s.b[25];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
struct S {
a : f32,
b : array<f32>,
}
@group(0) @binding(0) var<storage, read> s : S;
fn f() {
var d : f32 = s.b[min(u32(25), (arrayLength(&(s.b)) - 1u))];
}
)",
/* predicate */ R"(
struct S {
a : f32,
b : array<f32>,
}
@group(0) @binding(0) var<storage, read> s : S;
fn f() {
let index = 25;
let predicate = (u32(index) <= (arrayLength(&(s.b)) - 1u));
var predicated_expr : f32;
if (predicate) {
predicated_expr = s.b[index];
}
var d : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// Vector
////////////////////////////////////////////////////////////////////////////////
TEST_P(RobustnessTest, Read_Vector_IndexWithLiteral) {
auto* src = R"(
var<private> a : vec3<f32>;
fn f() {
var b : f32 = a[1i];
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_Vector_IndexWithConst) {
auto* src = R"(
var<private> a : vec3<f32>;
fn f() {
const i = 1;
var b : f32 = a[i];
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_Vector_IndexWithLet) {
auto* src = R"(
fn f() {
let i = 99;
let v = vec4<f32>()[i];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
fn f() {
let i = 99;
let v = vec4<f32>()[min(u32(i), 3u)];
}
)",
/* predicate */ R"(
fn f() {
let i = 99;
let index = i;
let predicate = (u32(index) <= 3u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = vec4<f32>()[index];
}
let v = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_Vector_IndexWithRuntimeExpression) {
auto* src = R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a[((c + 2) - 3)];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
}
)",
/* predicate */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
let index = ((c + 2) - 3);
let predicate = (u32(index) <= 2u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_Vector_IndexWithRuntimeExpression_ViaPointerIndex) {
auto* src = R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
let p = &(a);
var b : f32 = p[((c + 2) - 3)];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
let p = &(a);
var b : f32 = p[min(u32(((c + 2) - 3)), 2u)];
}
)",
/* predicate */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
let p = &(a);
let index = ((c + 2) - 3);
let predicate = (u32(index) <= 2u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = p[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_Vector_SwizzleIndexWithGlobalVar) {
auto* src = R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a.xy[c];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a.xy[min(u32(c), 1u)];
}
)",
/* predicate */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
let index = c;
let predicate = (u32(index) <= 1u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a.xy[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_Vector_SwizzleIndexWithRuntimeExpression) {
auto* src = R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a.xy[((c + 2) - 3)];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a.xy[min(u32(((c + 2) - 3)), 1u)];
}
)",
/* predicate */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
let index = ((c + 2) - 3);
let predicate = (u32(index) <= 1u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a.xy[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_Vector_SwizzleIndexWithRuntimeExpression_ViaPointerDot) {
auto* src = R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
let p = &(a);
var b : f32 = p.xy[((c + 2) - 3)];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
let p = &(a);
var b : f32 = p.xy[min(u32(((c + 2) - 3)), 1u)];
}
)",
/* predicate */ R"(
var<private> a : vec3<f32>;
var<private> c : i32;
fn f() {
let p = &(a);
let index = ((c + 2) - 3);
let predicate = (u32(index) <= 1u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = p.xy[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_Vector_IndexWithOverride) {
auto* src = R"(
@id(1300) override idx : i32;
fn f() {
var a : vec3<f32>;
var b : f32 = a[idx];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@id(1300) override idx : i32;
fn f() {
var a : vec3<f32>;
var b : f32 = a[min(u32(idx), 2u)];
}
)",
/* predicate */ R"(
@id(1300) override idx : i32;
fn f() {
var a : vec3<f32>;
let index = idx;
let predicate = (u32(index) <= 2u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// Matrix
////////////////////////////////////////////////////////////////////////////////
TEST_P(RobustnessTest, Read_MatrixRef_IndexingWithLiterals) {
auto* src = R"(
var<private> a : mat3x2<f32>;
fn f() {
var b : f32 = a[2i][1i];
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_MatrixRef_IndexWithRuntimeExpressionThenLiteral) {
auto* src = R"(
var<private> a : mat3x2<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a[((c + 2) - 3)][1];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : mat3x2<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1];
}
)",
/* predicate */ R"(
var<private> a : mat3x2<f32>;
var<private> c : i32;
fn f() {
let index = ((c + 2) - 3);
let predicate = (u32(index) <= 2u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[index][1];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_MatrixRef_IndexWithLiteralThenRuntimeExpression) {
auto* src = R"(
var<private> a : mat3x2<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a[1][((c + 2) - 3)];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> a : mat3x2<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)];
}
)",
/* predicate */ R"(
var<private> a : mat3x2<f32>;
var<private> c : i32;
fn f() {
let index = ((c + 2) - 3);
let predicate = (u32(index) <= 1u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[1][index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_MatrixRef_IndexWithOverrideThenLiteral) {
auto* src = R"(
@id(1300) override idx : i32;
fn f() {
var a : mat3x2<f32>;
var b : f32 = a[idx][1];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@id(1300) override idx : i32;
fn f() {
var a : mat3x2<f32>;
var b : f32 = a[min(u32(idx), 2u)][1];
}
)",
/* predicate */ R"(
@id(1300) override idx : i32;
fn f() {
var a : mat3x2<f32>;
let index = idx;
let predicate = (u32(index) <= 2u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[index][1];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_MatrixRef_IndexWithLetThenSwizzle) {
auto* src = R"(
fn f() {
let i = 1;
var m = mat3x2<f32>();
var v = m[i].yx;
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
fn f() {
let i = 1;
var m = mat3x2<f32>();
var v = m[min(u32(i), 2u)].yx;
}
)",
/* predicate */ R"(
fn f() {
let i = 1;
var m = mat3x2<f32>();
let index = i;
let predicate = (u32(index) <= 2u);
var predicated_expr : vec2<f32>;
if (predicate) {
predicated_expr = m[index];
}
var v = predicated_expr.yx;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Read_MatrixRef_IndexWithLiteralThenOverride) {
auto* src = R"(
@id(1300) override idx : i32;
fn f() {
var a : mat3x2<f32>;
var b : f32 = a[1][idx];
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@id(1300) override idx : i32;
fn f() {
var a : mat3x2<f32>;
var b : f32 = a[1][min(u32(idx), 1u)];
}
)",
/* predicate */ R"(
@id(1300) override idx : i32;
fn f() {
var a : mat3x2<f32>;
let index = idx;
let predicate = (u32(index) <= 1u);
var predicated_expr : f32;
if (predicate) {
predicated_expr = a[1][index];
}
var b : f32 = predicated_expr;
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, Assign_Matrix_IndexWithLet) {
auto* src = R"(
var<private> m : mat3x4f;
fn f() {
let c = 1;
m[c] = vec4f(1);
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> m : mat3x4f;
fn f() {
let c = 1;
m[min(u32(c), 2u)] = vec4f(1);
}
)",
/* predicate */ R"(
var<private> m : mat3x4f;
fn f() {
let c = 1;
let index = c;
let predicate = (u32(index) <= 2u);
if (predicate) {
m[index] = vec4f(1);
}
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, CompoundAssign_Matrix_IndexWithLet) {
auto* src = R"(
var<private> m : mat3x4f;
fn f() {
let c = 1;
m[c] += vec4f(1);
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
var<private> m : mat3x4f;
fn f() {
let c = 1;
m[min(u32(c), 2u)] += vec4f(1);
}
)",
/* predicate */ R"(
var<private> m : mat3x4f;
fn f() {
let c = 1;
let index = c;
let predicate = (u32(index) <= 2u);
if (predicate) {
m[index] += vec4f(1);
}
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// Texture builtin calls.
////////////////////////////////////////////////////////////////////////////////
TEST_P(RobustnessTest, TextureDimensions) {
auto* src = R"(
@group(0) @binding(0) var t : texture_2d<f32>;
fn dimensions() {
let l = textureDimensions(t);
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureDimensions_Level) {
auto* src = R"(
@group(0) @binding(0) var t : texture_2d<f32>;
fn dimensions_signed(level : i32) {
let l = textureDimensions(t, level);
}
fn dimensions_unsigned(level : u32) {
let l = textureDimensions(t, level);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
dimensions_signed(i32(non_uniform.x));
dimensions_unsigned(u32(non_uniform.x));
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@group(0) @binding(0) var t : texture_2d<f32>;
fn dimensions_signed(level : i32) {
let level_idx = min(u32(level), (textureNumLevels(t) - 1));
let l = textureDimensions(t, level_idx);
}
fn dimensions_unsigned(level : u32) {
let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureDimensions(t, level_idx_1);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
dimensions_signed(i32(non_uniform.x));
dimensions_unsigned(u32(non_uniform.x));
}
)",
/* predicate */ R"(
@group(0) @binding(0) var t : texture_2d<f32>;
fn dimensions_signed(level : i32) {
let level_idx = u32(level);
let num_levels = textureNumLevels(t);
var predicated_value : vec2<u32>;
if ((level_idx < num_levels)) {
predicated_value = textureDimensions(t, level_idx);
}
let l = predicated_value;
}
fn dimensions_unsigned(level : u32) {
let level_idx_1 = u32(level);
let num_levels_1 = textureNumLevels(t);
var predicated_value_1 : vec2<u32>;
if ((level_idx_1 < num_levels_1)) {
predicated_value_1 = textureDimensions(t, level_idx_1);
}
let l = predicated_value_1;
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
dimensions_signed(i32(non_uniform.x));
dimensions_unsigned(u32(non_uniform.x));
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureGather) {
auto* src = R"(
@group(0) @binding(0) var t : texture_2d<f32>;
@group(0) @binding(1) var s : sampler;
fn gather(coords : vec2f) {
let l = textureGather(0, t, s, coords);
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureGather_Array) {
auto* src = R"(
@group(0) @binding(0) var t : texture_2d_array<f32>;
@group(0) @binding(1) var s : sampler;
fn gather_signed(coords : vec2f, array : i32) {
let l = textureGather(1, t, s, coords, array);
}
fn gather_unsigned(coords : vec2f, array : u32) {
let l = textureGather(1, t, s, coords, array);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
gather_signed(non_uniform.xy, i32(non_uniform.x));
gather_unsigned(non_uniform.xy, u32(non_uniform.x));
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureGatherCompare) {
auto* src = R"(
@group(0) @binding(0) var t : texture_depth_2d;
@group(0) @binding(1) var s : sampler_comparison;
fn gather(coords : vec2f, depth_ref : f32) {
let l = textureGatherCompare(t, s, coords, depth_ref);
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureGatherCompare_Array) {
auto* src = R"(
@group(0) @binding(0) var t : texture_depth_2d_array;
@group(0) @binding(1) var s : sampler_comparison;
fn gather_signed(coords : vec2f, array : i32, depth_ref : f32) {
let l = textureGatherCompare(t, s, coords, array, depth_ref);
}
fn gather_unsigned(coords : vec2f, array : u32, depth_ref : f32) {
let l = textureGatherCompare(t, s, coords, array, depth_ref);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
gather_signed(non_uniform.xy, i32(non_uniform.x), non_uniform.x);
gather_unsigned(non_uniform.xy, u32(non_uniform.x), non_uniform.x);
}
)";
auto* expect = src;
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureLoad_1D) {
auto* src = R"(
@group(0) @binding(0) var t : texture_1d<f32>;
fn load_signed(coords : i32, level : i32) {
let l = textureLoad(t, coords, level);
}
fn load_unsigned(coords : u32, level : u32) {
let l = textureLoad(t, coords, level);
}
fn load_mixed(coords : i32, level : u32) {
let l = textureLoad(t, coords, level);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(i32(non_uniform.x), i32(non_uniform.x));
load_unsigned(u32(non_uniform.x), u32(non_uniform.x));
load_mixed(i32(non_uniform.x), u32(non_uniform.x));
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@group(0) @binding(0) var t : texture_1d<f32>;
fn load_signed(coords : i32, level : i32) {
let level_idx = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, clamp(coords, 0, i32((textureDimensions(t, level_idx) - 1))), level_idx);
}
fn load_unsigned(coords : u32, level : u32) {
let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - 1)), level_idx_1);
}
fn load_mixed(coords : i32, level : u32) {
let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, clamp(coords, 0, i32((textureDimensions(t, level_idx_2) - 1))), level_idx_2);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(i32(non_uniform.x), i32(non_uniform.x));
load_unsigned(u32(non_uniform.x), u32(non_uniform.x));
load_mixed(i32(non_uniform.x), u32(non_uniform.x));
}
)",
/* predicate */ R"(
@group(0) @binding(0) var t : texture_1d<f32>;
fn load_signed(coords : i32, level : i32) {
let level_idx = u32(level);
let num_levels = textureNumLevels(t);
let coords_1 = u32(coords);
var predicated_value : vec4<f32>;
if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
predicated_value = textureLoad(t, coords_1, level_idx);
}
let l = predicated_value;
}
fn load_unsigned(coords : u32, level : u32) {
let level_idx_1 = u32(level);
let num_levels_1 = textureNumLevels(t);
let coords_2 = u32(coords);
var predicated_value_1 : vec4<f32>;
if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
}
let l = predicated_value_1;
}
fn load_mixed(coords : i32, level : u32) {
let level_idx_2 = u32(level);
let num_levels_2 = textureNumLevels(t);
let coords_3 = u32(coords);
var predicated_value_2 : vec4<f32>;
if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
}
let l = predicated_value_2;
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(i32(non_uniform.x), i32(non_uniform.x));
load_unsigned(u32(non_uniform.x), u32(non_uniform.x));
load_mixed(i32(non_uniform.x), u32(non_uniform.x));
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureLoad_2D) {
auto* src = R"(
@group(0) @binding(0) var t : texture_2d<f32>;
fn load_signed(coords : vec2i, level : i32) {
let l = textureLoad(t, coords, level);
}
fn load_unsigned(coords : vec2u, level : u32) {
let l = textureLoad(t, coords, level);
}
fn load_mixed(coords : vec2u, level : i32) {
let l = textureLoad(t, coords, level);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x));
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@group(0) @binding(0) var t : texture_2d<f32>;
fn load_signed(coords : vec2i, level : i32) {
let level_idx = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx);
}
fn load_unsigned(coords : vec2u, level : u32) {
let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), level_idx_1);
}
fn load_mixed(coords : vec2u, level : i32) {
let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec2(1))), level_idx_2);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x));
}
)",
/* predicate */ R"(
@group(0) @binding(0) var t : texture_2d<f32>;
fn load_signed(coords : vec2i, level : i32) {
let level_idx = u32(level);
let num_levels = textureNumLevels(t);
let coords_1 = vec2<u32>(coords);
var predicated_value : vec4<f32>;
if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
predicated_value = textureLoad(t, coords_1, level_idx);
}
let l = predicated_value;
}
fn load_unsigned(coords : vec2u, level : u32) {
let level_idx_1 = u32(level);
let num_levels_1 = textureNumLevels(t);
let coords_2 = vec2<u32>(coords);
var predicated_value_1 : vec4<f32>;
if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
}
let l = predicated_value_1;
}
fn load_mixed(coords : vec2u, level : i32) {
let level_idx_2 = u32(level);
let num_levels_2 = textureNumLevels(t);
let coords_3 = vec2<u32>(coords);
var predicated_value_2 : vec4<f32>;
if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
}
let l = predicated_value_2;
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x));
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureLoad_2DArray) {
auto* src = R"(
@group(0) @binding(0) var t : texture_2d_array<f32>;
fn load_signed(coords : vec2i, array : i32, level : i32) {
let l = textureLoad(t, coords, array, level);
}
fn load_unsigned(coords : vec2u, array : u32, level : u32) {
let l = textureLoad(t, coords, array, level);
}
fn load_mixed(coords : vec2u, array : i32, level : u32) {
let l = textureLoad(t, coords, array, level);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@group(0) @binding(0) var t : texture_2d_array<f32>;
fn load_signed(coords : vec2i, array : i32, level : i32) {
let level_idx = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx);
}
fn load_unsigned(coords : vec2u, array : u32, level : u32) {
let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), min(array, (textureNumLayers(t) - 1)), level_idx_1);
}
fn load_mixed(coords : vec2u, array : i32, level : u32) {
let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec2(1))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx_2);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
}
)",
/* predicate */ R"(
@group(0) @binding(0) var t : texture_2d_array<f32>;
fn load_signed(coords : vec2i, array : i32, level : i32) {
let level_idx = u32(level);
let num_levels = textureNumLevels(t);
let coords_1 = vec2<u32>(coords);
let array_idx = u32(array);
var predicated_value : vec4<f32>;
if ((((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1)))))) & (array_idx < textureNumLayers(t)))) {
predicated_value = textureLoad(t, coords_1, array_idx, level_idx);
}
let l = predicated_value;
}
fn load_unsigned(coords : vec2u, array : u32, level : u32) {
let level_idx_1 = u32(level);
let num_levels_1 = textureNumLevels(t);
let coords_2 = vec2<u32>(coords);
let array_idx_1 = u32(array);
var predicated_value_1 : vec4<f32>;
if ((((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1)))))) & (array_idx_1 < textureNumLayers(t)))) {
predicated_value_1 = textureLoad(t, coords_2, array_idx_1, level_idx_1);
}
let l = predicated_value_1;
}
fn load_mixed(coords : vec2u, array : i32, level : u32) {
let level_idx_2 = u32(level);
let num_levels_2 = textureNumLevels(t);
let coords_3 = vec2<u32>(coords);
let array_idx_2 = u32(array);
var predicated_value_2 : vec4<f32>;
if ((((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1)))))) & (array_idx_2 < textureNumLayers(t)))) {
predicated_value_2 = textureLoad(t, coords_3, array_idx_2, level_idx_2);
}
let l = predicated_value_2;
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureLoad_3D) {
auto* src = R"(
@group(0) @binding(0) var t : texture_3d<f32>;
fn load_signed(coords : vec3i, level : i32) {
let l = textureLoad(t, coords, level);
}
fn load_unsigned(coords : vec3u, level : u32) {
let l = textureLoad(t, coords, level);
}
fn load_mixed(coords : vec3u, level : i32) {
let l = textureLoad(t, coords, level);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec3i(non_uniform.xyz), i32(non_uniform.x));
load_unsigned(vec3u(non_uniform.xyz), u32(non_uniform.x));
load_mixed(vec3u(non_uniform.xyz), i32(non_uniform.x));
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@group(0) @binding(0) var t : texture_3d<f32>;
fn load_signed(coords : vec3i, level : i32) {
let level_idx = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, clamp(coords, vec3(0), vec3<i32>((textureDimensions(t, level_idx) - vec3(1)))), level_idx);
}
fn load_unsigned(coords : vec3u, level : u32) {
let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec3(1))), level_idx_1);
}
fn load_mixed(coords : vec3u, level : i32) {
let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec3(1))), level_idx_2);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec3i(non_uniform.xyz), i32(non_uniform.x));
load_unsigned(vec3u(non_uniform.xyz), u32(non_uniform.x));
load_mixed(vec3u(non_uniform.xyz), i32(non_uniform.x));
}
)",
/* predicate */ R"(
@group(0) @binding(0) var t : texture_3d<f32>;
fn load_signed(coords : vec3i, level : i32) {
let level_idx = u32(level);
let num_levels = textureNumLevels(t);
let coords_1 = vec3<u32>(coords);
var predicated_value : vec4<f32>;
if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
predicated_value = textureLoad(t, coords_1, level_idx);
}
let l = predicated_value;
}
fn load_unsigned(coords : vec3u, level : u32) {
let level_idx_1 = u32(level);
let num_levels_1 = textureNumLevels(t);
let coords_2 = vec3<u32>(coords);
var predicated_value_1 : vec4<f32>;
if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
}
let l = predicated_value_1;
}
fn load_mixed(coords : vec3u, level : i32) {
let level_idx_2 = u32(level);
let num_levels_2 = textureNumLevels(t);
let coords_3 = vec3<u32>(coords);
var predicated_value_2 : vec4<f32>;
if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
}
let l = predicated_value_2;
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec3i(non_uniform.xyz), i32(non_uniform.x));
load_unsigned(vec3u(non_uniform.xyz), u32(non_uniform.x));
load_mixed(vec3u(non_uniform.xyz), i32(non_uniform.x));
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureLoad_Multisampled2D) {
auto* src = R"(
@group(0) @binding(0) var t : texture_multisampled_2d<f32>;
fn load_signed(coords : vec2i, sample : i32) {
let l = textureLoad(t, coords, sample);
}
fn load_unsigned(coords : vec2u, sample : u32) {
let l = textureLoad(t, coords, sample);
}
fn load_mixed(coords : vec2i, sample : u32) {
let l = textureLoad(t, coords, sample);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@group(0) @binding(0) var t : texture_multisampled_2d<f32>;
fn load_signed(coords : vec2i, sample : i32) {
let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t) - vec2(1)))), sample);
}
fn load_unsigned(coords : vec2u, sample : u32) {
let l = textureLoad(t, min(coords, (textureDimensions(t) - vec2(1))), sample);
}
fn load_mixed(coords : vec2i, sample : u32) {
let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t) - vec2(1)))), sample);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
}
)",
/* predicate */ R"(
@group(0) @binding(0) var t : texture_multisampled_2d<f32>;
fn load_signed(coords : vec2i, sample : i32) {
let coords_1 = vec2<u32>(coords);
var predicated_value : vec4<f32>;
if (all((coords_1 < textureDimensions(t)))) {
predicated_value = textureLoad(t, coords_1, sample);
}
let l = predicated_value;
}
fn load_unsigned(coords : vec2u, sample : u32) {
let coords_2 = vec2<u32>(coords);
var predicated_value_1 : vec4<f32>;
if (all((coords_2 < textureDimensions(t)))) {
predicated_value_1 = textureLoad(t, coords_2, sample);
}
let l = predicated_value_1;
}
fn load_mixed(coords : vec2i, sample : u32) {
let coords_3 = vec2<u32>(coords);
var predicated_value_2 : vec4<f32>;
if (all((coords_3 < textureDimensions(t)))) {
predicated_value_2 = textureLoad(t, coords_3, sample);
}
let l = predicated_value_2;
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureLoad_Depth2D) {
auto* src = R"(
@group(0) @binding(0) var t : texture_depth_2d;
fn load_signed(coords : vec2i, level : i32) {
let l = textureLoad(t, coords, level);
}
fn load_unsigned(coords : vec2u, level : u32) {
let l = textureLoad(t, coords, level);
}
fn load_mixed(coords : vec2i, level : u32) {
let l = textureLoad(t, coords, level);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@group(0) @binding(0) var t : texture_depth_2d;
fn load_signed(coords : vec2i, level : i32) {
let level_idx = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), level_idx);
}
fn load_unsigned(coords : vec2u, level : u32) {
let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), level_idx_1);
}
fn load_mixed(coords : vec2i, level : u32) {
let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx_2) - vec2(1)))), level_idx_2);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
}
)",
/* predicate */ R"(
@group(0) @binding(0) var t : texture_depth_2d;
fn load_signed(coords : vec2i, level : i32) {
let level_idx = u32(level);
let num_levels = textureNumLevels(t);
let coords_1 = vec2<u32>(coords);
var predicated_value : f32;
if (((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1))))))) {
predicated_value = textureLoad(t, coords_1, level_idx);
}
let l = predicated_value;
}
fn load_unsigned(coords : vec2u, level : u32) {
let level_idx_1 = u32(level);
let num_levels_1 = textureNumLevels(t);
let coords_2 = vec2<u32>(coords);
var predicated_value_1 : f32;
if (((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1))))))) {
predicated_value_1 = textureLoad(t, coords_2, level_idx_1);
}
let l = predicated_value_1;
}
fn load_mixed(coords : vec2i, level : u32) {
let level_idx_2 = u32(level);
let num_levels_2 = textureNumLevels(t);
let coords_3 = vec2<u32>(coords);
var predicated_value_2 : f32;
if (((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1))))))) {
predicated_value_2 = textureLoad(t, coords_3, level_idx_2);
}
let l = predicated_value_2;
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x));
load_mixed(vec2i(non_uniform.xy), u32(non_uniform.x));
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureLoad_Depth2DArray) {
auto* src = R"(
@group(0) @binding(0) var t : texture_depth_2d_array;
fn load_signed(coords : vec2i, array : i32, level : i32) {
let l = textureLoad(t, coords, array, level);
}
fn load_unsigned(coords : vec2u, array : u32, level : u32) {
let l = textureLoad(t, coords, array, level);
}
fn load_mixed(coords : vec2u, array : i32, level : u32) {
let l = textureLoad(t, coords, array, level);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@group(0) @binding(0) var t : texture_depth_2d_array;
fn load_signed(coords : vec2i, array : i32, level : i32) {
let level_idx = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t, level_idx) - vec2(1)))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx);
}
fn load_unsigned(coords : vec2u, array : u32, level : u32) {
let level_idx_1 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_1) - vec2(1))), min(array, (textureNumLayers(t) - 1)), level_idx_1);
}
fn load_mixed(coords : vec2u, array : i32, level : u32) {
let level_idx_2 = min(u32(level), (textureNumLevels(t) - 1));
let l = textureLoad(t, min(coords, (textureDimensions(t, level_idx_2) - vec2(1))), clamp(array, 0, i32((textureNumLayers(t) - 1))), level_idx_2);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
}
)",
/* predicate */ R"(
@group(0) @binding(0) var t : texture_depth_2d_array;
fn load_signed(coords : vec2i, array : i32, level : i32) {
let level_idx = u32(level);
let num_levels = textureNumLevels(t);
let coords_1 = vec2<u32>(coords);
let array_idx = u32(array);
var predicated_value : f32;
if ((((level_idx < num_levels) & all((coords_1 < textureDimensions(t, min(level_idx, (num_levels - 1)))))) & (array_idx < textureNumLayers(t)))) {
predicated_value = textureLoad(t, coords_1, array_idx, level_idx);
}
let l = predicated_value;
}
fn load_unsigned(coords : vec2u, array : u32, level : u32) {
let level_idx_1 = u32(level);
let num_levels_1 = textureNumLevels(t);
let coords_2 = vec2<u32>(coords);
let array_idx_1 = u32(array);
var predicated_value_1 : f32;
if ((((level_idx_1 < num_levels_1) & all((coords_2 < textureDimensions(t, min(level_idx_1, (num_levels_1 - 1)))))) & (array_idx_1 < textureNumLayers(t)))) {
predicated_value_1 = textureLoad(t, coords_2, array_idx_1, level_idx_1);
}
let l = predicated_value_1;
}
fn load_mixed(coords : vec2u, array : i32, level : u32) {
let level_idx_2 = u32(level);
let num_levels_2 = textureNumLevels(t);
let coords_3 = vec2<u32>(coords);
let array_idx_2 = u32(array);
var predicated_value_2 : f32;
if ((((level_idx_2 < num_levels_2) & all((coords_3 < textureDimensions(t, min(level_idx_2, (num_levels_2 - 1)))))) & (array_idx_2 < textureNumLayers(t)))) {
predicated_value_2 = textureLoad(t, coords_3, array_idx_2, level_idx_2);
}
let l = predicated_value_2;
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy), i32(non_uniform.x), i32(non_uniform.x));
load_unsigned(vec2u(non_uniform.xy), u32(non_uniform.x), u32(non_uniform.x));
load_mixed(vec2u(non_uniform.xy), i32(non_uniform.x), u32(non_uniform.x));
}
)");
auto got = Run<Robustness>(src, Config(GetParam()));
EXPECT_EQ(expect, str(got));
}
TEST_P(RobustnessTest, TextureLoad_External) {
auto* src = R"(
@group(0) @binding(0) var t : texture_external;
fn load_signed(coords : vec2i) {
let l = textureLoad(t, coords);
}
fn load_unsigned(coords : vec2u) {
let l = textureLoad(t, coords);
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy));
load_unsigned(vec2u(non_uniform.xy));
}
)";
auto* expect = Expect(GetParam(),
/* ignore */ src,
/* clamp */ R"(
@group(0) @binding(0) var t : texture_external;
fn load_signed(coords : vec2i) {
let l = textureLoad(t, clamp(coords, vec2(0), vec2<i32>((textureDimensions(t) - vec2(1)))));
}
fn load_unsigned(coords : vec2u) {
let l = textureLoad(t, min(coords, (textureDimensions(t) - vec2(1))));
}
@fragment
fn main(@builtin(position) non_uniform : vec4f) {
load_signed(vec2i(non_uniform.xy));
load_unsigned(vec2u(non_uniform.xy));
}
)",
/* predicate */ R"(
@group(0) @binding(0) var t : texture_external;
fn load_signed(coords : vec2i) {
let coords_1 = vec2<u32>(coords);
var predicated_value : vec4<f32>;
if (all((coords_1 < textureDimensions(t)))) {
predicated_value = textureLoad(t, coords_1);
}
let l = predicated_value;
}
fn load_unsigned(coords : vec2u) {
let coords_2 = vec2<u32>(coords);
var predicated_value_1 : vec4<f32>;
if (all((coords_2 < textureDimensions(t)))) {
predicated_value_1 = textureLoad(t, coords_2);
}