tint: Error if statically indexing out of bounds.

Fixed: tint:1665
Change-Id: Icd5f24f3b4d6ebbdc18b536dc426da92558a3a4b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/101183
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Ben Clayton 2022-09-13 22:47:12 +00:00 committed by Dawn LUCI CQ
parent 75943c234f
commit f3d9ea4bea
5 changed files with 256 additions and 985 deletions

View File

@ -7,6 +7,10 @@
* `array()` constructor can now infer type and count. [tint:1628](crbug.com/tint/1628)
* `static_assert` statement has been added. [tint:1625](crbug.com/tint/1625)
### Breaking changes
* Indexing an array, vector or matrix with a compile-time expression that's out-of-bounds is now an error [tint:1665](crbug.com/tint/1665)
### Deprecated Features
* The list of reserved words has been sync'd to the WGSL specification. [tint:1463](crbug.com/tint/1463)

View File

@ -899,27 +899,28 @@ ConstEval::ConstantResult ConstEval::MatCtorV(const sem::Type* ty,
ConstEval::ConstantResult ConstEval::Index(const sem::Expression* obj_expr,
const sem::Expression* idx_expr) {
auto obj_val = obj_expr->ConstantValue();
if (!obj_val) {
return nullptr;
}
auto idx_val = idx_expr->ConstantValue();
if (!idx_val) {
return nullptr;
}
uint32_t el_count = 0;
sem::Type::ElementOf(obj_val->Type(), &el_count);
sem::Type::ElementOf(obj_expr->Type()->UnwrapRef(), &el_count);
AInt idx = idx_val->As<AInt>();
if (idx < 0 || idx >= el_count) {
auto clamped = std::min<AInt::type>(std::max<AInt::type>(idx, 0), el_count - 1);
AddWarning("index " + std::to_string(idx) + " out of bounds [0.." +
std::to_string(el_count - 1) + "]. Clamping index to " +
std::to_string(clamped),
if (idx < 0 || (el_count > 0 && idx >= el_count)) {
std::string range;
if (el_count > 0) {
range = " [0.." + std::to_string(el_count - 1) + "]";
}
AddError("index " + std::to_string(idx) + " out of bounds" + range,
idx_expr->Declaration()->source);
idx = clamped;
return utils::Failure;
}
auto obj_val = obj_expr->ConstantValue();
if (!obj_val) {
return nullptr;
}
return obj_val->Index(static_cast<size_t>(idx));

View File

@ -2498,34 +2498,16 @@ TEST_F(ResolverConstEvalTest, Vec3_Index_OOB_High) {
auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, 3_i));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 warning: index 3 out of bounds [0..2]. Clamping index to 2");
auto* sem = Sem().Get(expr);
ASSERT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::I32>());
EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue()->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->AllZero());
EXPECT_EQ(sem->ConstantValue()->As<i32>(), 3_i);
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 error: index 3 out of bounds [0..2]");
}
TEST_F(ResolverConstEvalTest, Vec3_Index_OOB_Low) {
auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, -3_i));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 warning: index -3 out of bounds [0..2]. Clamping index to 0");
auto* sem = Sem().Get(expr);
ASSERT_NE(sem, nullptr);
ASSERT_TRUE(sem->Type()->Is<sem::I32>());
EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue()->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->AllZero());
EXPECT_EQ(sem->ConstantValue()->As<i32>(), 1_i);
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 error: index -3 out of bounds [0..2]");
}
TEST_F(ResolverConstEvalTest, Vec3_Swizzle_Scalar) {
@ -2616,25 +2598,8 @@ TEST_F(ResolverConstEvalTest, Mat3x2_Index_OOB_High) {
Expr(Source{{12, 34}}, 3_i));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 warning: index 3 out of bounds [0..2]. Clamping index to 2");
auto* sem = Sem().Get(expr);
ASSERT_NE(sem, nullptr);
auto* vec = sem->Type()->As<sem::Vector>();
ASSERT_NE(vec, nullptr);
EXPECT_EQ(vec->Width(), 2u);
EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 5._a);
EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 6._a);
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 error: index 3 out of bounds [0..2]");
}
TEST_F(ResolverConstEvalTest, Mat3x2_Index_OOB_Low) {
@ -2643,25 +2608,8 @@ TEST_F(ResolverConstEvalTest, Mat3x2_Index_OOB_Low) {
Expr(Source{{12, 34}}, -3_i));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 warning: index -3 out of bounds [0..2]. Clamping index to 0");
auto* sem = Sem().Get(expr);
ASSERT_NE(sem, nullptr);
auto* vec = sem->Type()->As<sem::Vector>();
ASSERT_NE(vec, nullptr);
EXPECT_EQ(vec->Width(), 2u);
EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 1._a);
EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 2._a);
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 error: index -3 out of bounds [0..2]");
}
TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index) {
@ -2702,31 +2650,8 @@ TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index_OOB_High) {
Expr(Source{{12, 34}}, 2_i));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 warning: index 2 out of bounds [0..1]. Clamping index to 1");
auto* sem = Sem().Get(expr);
ASSERT_NE(sem, nullptr);
auto* vec = sem->Type()->As<sem::Vector>();
ASSERT_NE(vec, nullptr);
EXPECT_TRUE(vec->type()->Is<sem::F32>());
EXPECT_EQ(vec->Width(), 3u);
EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 4_f);
EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 5_f);
EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 6_f);
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 error: index 2 out of bounds [0..1]");
}
TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index_OOB_Low) {
@ -2735,34 +2660,18 @@ TEST_F(ResolverConstEvalTest, Array_vec3_f32_Index_OOB_Low) {
Expr(Source{{12, 34}}, -2_i));
WrapInFunction(expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 warning: index -2 out of bounds [0..1]. Clamping index to 0");
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 error: index -2 out of bounds [0..1]");
}
auto* sem = Sem().Get(expr);
ASSERT_NE(sem, nullptr);
auto* vec = sem->Type()->As<sem::Vector>();
ASSERT_NE(vec, nullptr);
EXPECT_TRUE(vec->type()->Is<sem::F32>());
EXPECT_EQ(vec->Width(), 3u);
EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
EXPECT_FALSE(sem->ConstantValue()->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->AllZero());
TEST_F(ResolverConstEvalTest, RuntimeArray_vec3_f32_Index_OOB_Low) {
auto* sb = GlobalVar("sb", ty.array(ty.vec3<f32>()), Group(0_a), Binding(0_a),
ast::StorageClass::kStorage);
auto* expr = IndexAccessor(sb, Expr(Source{{12, 34}}, -2_i));
WrapInFunction(expr);
EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(0)->As<f32>(), 1_f);
EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(1)->As<f32>(), 2_f);
EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
EXPECT_EQ(sem->ConstantValue()->Index(2)->As<f32>(), 3_f);
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), "12:34 error: index -2 out of bounds");
}
TEST_F(ResolverConstEvalTest, ChainedIndex) {
@ -2861,105 +2770,6 @@ TEST_F(ResolverConstEvalTest, ChainedIndex) {
}
}
TEST_F(ResolverConstEvalTest, ChainedIndex_OOB) {
auto* arr_expr = Construct(ty.array(ty.mat2x3<f32>(), 2_u), // array<mat2x3<f32>, 2u>
mat2x3<f32>(vec3<f32>(1_f, 2_f, 3_f), //
vec3<f32>(4_f, 5_f, 6_f)), //
mat2x3<f32>(vec3<f32>(7_f, 8_f, 9_f), //
vec3<f32>(10_f, 11_f, 12_f)));
auto* mat_expr = IndexAccessor(arr_expr, Expr(Source{{1, 2}}, -3_i)); // arr[3]
auto* vec_expr = IndexAccessor(mat_expr, Expr(Source{{3, 4}}, -2_i)); // arr[3][-2]
auto* f32_expr = IndexAccessor(vec_expr, Expr(Source{{5, 6}}, 4_i)); // arr[3][-2][4]
WrapInFunction(f32_expr);
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), R"(1:2 warning: index -3 out of bounds [0..1]. Clamping index to 0
3:4 warning: index -2 out of bounds [0..1]. Clamping index to 0
5:6 warning: index 4 out of bounds [0..2]. Clamping index to 2)");
{
auto* mat = Sem().Get(mat_expr);
EXPECT_NE(mat, nullptr);
auto* ty = mat->Type()->As<sem::Matrix>();
ASSERT_NE(mat->Type(), nullptr);
EXPECT_TRUE(ty->ColumnType()->Is<sem::Vector>());
EXPECT_EQ(ty->columns(), 2u);
EXPECT_EQ(ty->rows(), 3u);
EXPECT_EQ(mat->ConstantValue()->Type(), mat->Type());
EXPECT_FALSE(mat->ConstantValue()->AllEqual());
EXPECT_FALSE(mat->ConstantValue()->AnyZero());
EXPECT_FALSE(mat->ConstantValue()->AllZero());
EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(0)->AllEqual());
EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AnyZero());
EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(0)->AllZero());
EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(0)->As<f32>(), 1_f);
EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(1)->AllEqual());
EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(1)->AnyZero());
EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(1)->AllZero());
EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(1)->As<f32>(), 2_f);
EXPECT_TRUE(mat->ConstantValue()->Index(0)->Index(2)->AllEqual());
EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AnyZero());
EXPECT_FALSE(mat->ConstantValue()->Index(0)->Index(2)->AllZero());
EXPECT_EQ(mat->ConstantValue()->Index(0)->Index(2)->As<f32>(), 3_f);
EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(0)->AllEqual());
EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AnyZero());
EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(0)->AllZero());
EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(0)->As<f32>(), 4_f);
EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(1)->AllEqual());
EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AnyZero());
EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(1)->AllZero());
EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(1)->As<f32>(), 5_f);
EXPECT_TRUE(mat->ConstantValue()->Index(1)->Index(2)->AllEqual());
EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AnyZero());
EXPECT_FALSE(mat->ConstantValue()->Index(1)->Index(2)->AllZero());
EXPECT_EQ(mat->ConstantValue()->Index(1)->Index(2)->As<f32>(), 6_f);
}
{
auto* vec = Sem().Get(vec_expr);
EXPECT_NE(vec, nullptr);
auto* ty = vec->Type()->As<sem::Vector>();
ASSERT_NE(vec->Type(), nullptr);
EXPECT_TRUE(ty->type()->Is<sem::F32>());
EXPECT_EQ(ty->Width(), 3u);
EXPECT_EQ(vec->ConstantValue()->Type(), vec->Type());
EXPECT_FALSE(vec->ConstantValue()->AllEqual());
EXPECT_FALSE(vec->ConstantValue()->AnyZero());
EXPECT_FALSE(vec->ConstantValue()->AllZero());
EXPECT_TRUE(vec->ConstantValue()->Index(0)->AllEqual());
EXPECT_FALSE(vec->ConstantValue()->Index(0)->AnyZero());
EXPECT_FALSE(vec->ConstantValue()->Index(0)->AllZero());
EXPECT_EQ(vec->ConstantValue()->Index(0)->As<f32>(), 1_f);
EXPECT_TRUE(vec->ConstantValue()->Index(1)->AllEqual());
EXPECT_FALSE(vec->ConstantValue()->Index(1)->AnyZero());
EXPECT_FALSE(vec->ConstantValue()->Index(1)->AllZero());
EXPECT_EQ(vec->ConstantValue()->Index(1)->As<f32>(), 2_f);
EXPECT_TRUE(vec->ConstantValue()->Index(2)->AllEqual());
EXPECT_FALSE(vec->ConstantValue()->Index(2)->AnyZero());
EXPECT_FALSE(vec->ConstantValue()->Index(2)->AllZero());
EXPECT_EQ(vec->ConstantValue()->Index(2)->As<f32>(), 3_f);
}
{
auto* f = Sem().Get(f32_expr);
EXPECT_NE(f, nullptr);
EXPECT_TRUE(f->Type()->Is<sem::F32>());
EXPECT_EQ(f->ConstantValue()->Type(), f->Type());
EXPECT_TRUE(f->ConstantValue()->AllEqual());
EXPECT_FALSE(f->ConstantValue()->AnyZero());
EXPECT_FALSE(f->ConstantValue()->AllZero());
EXPECT_EQ(f->ConstantValue()->As<f32>(), 3_f);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Member accessing
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -22,6 +22,7 @@
#include "src/tint/sem/block_statement.h"
#include "src/tint/sem/call.h"
#include "src/tint/sem/expression.h"
#include "src/tint/sem/index_accessor_expression.h"
#include "src/tint/sem/reference.h"
#include "src/tint/sem/statement.h"
@ -48,150 +49,80 @@ struct Robustness::State {
/// Apply bounds clamping to array, vector and matrix indexing
/// @param expr the array, vector or matrix index expression
/// @return the clamped replacement expression, or nullptr if `expr` should be
/// cloned without changes.
/// @return the clamped replacement expression, or nullptr if `expr` should be cloned without
/// changes.
const ast::IndexAccessorExpression* Transform(const ast::IndexAccessorExpression* expr) {
auto* ret_type = ctx.src->Sem().Get(expr->object)->Type();
auto* sem =
ctx.src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::IndexAccessorExpression>();
auto* ret_type = sem->Type();
auto* ref = ret_type->As<sem::Reference>();
if (ref && omitted_classes.count(ref->StorageClass()) != 0) {
return nullptr;
}
auto* ret_unwrapped = ret_type->UnwrapRef();
ProgramBuilder& b = *ctx.dst;
struct Value {
const ast::Expression* expr = nullptr; // If null, then is a constant
union {
uint32_t u32 = 0; // use if is_signed == false
int32_t i32; // use if is_signed == true
};
bool is_signed = false;
// idx return the cloned index expression, as a u32.
auto idx = [&]() -> const ast::Expression* {
auto* i = ctx.Clone(expr->index);
if (sem->Index()->Type()->UnwrapRef()->is_signed_integer_scalar()) {
return b.Construct(b.ty.u32(), i); // u32(idx)
}
return i;
};
Value size; // size of the array, vector or matrix
size.is_signed = false; // size is always unsigned
if (auto* vec = ret_unwrapped->As<sem::Vector>()) {
size.u32 = vec->Width();
} else if (auto* arr = ret_unwrapped->As<sem::Array>()) {
size.u32 = arr->Count();
} else if (auto* mat = ret_unwrapped->As<sem::Matrix>()) {
// The row accessor would have been an embedded index accessor and already
// handled, so we just need to do columns here.
size.u32 = mat->columns();
} else {
auto* clamped_idx = Switch(
sem->Object()->Type()->UnwrapRef(), //
[&](const sem::Vector* vec) -> const ast::Expression* {
if (sem->Index()->ConstantValue()) {
// Index and size is constant.
// Validation will have rejected any OOB accesses.
return nullptr;
}
if (size.u32 == 0) {
if (!ret_unwrapped->Is<sem::Array>()) {
b.Diagnostics().add_error(diag::System::Transform, "invalid 0 sized non-array",
expr->source);
return b.Call("min", idx(), u32(vec->Width() - 1u));
},
[&](const sem::Matrix* mat) -> const ast::Expression* {
if (sem->Index()->ConstantValue()) {
// Index and size is constant.
// Validation will have rejected any OOB accesses.
return nullptr;
}
// Runtime sized array
auto* arr = ctx.Clone(expr->object);
size.expr = b.Call("arrayLength", b.AddressOf(arr));
}
// Calculate the maximum possible index value (size-1u)
// Size must be positive (non-zero), so we can safely subtract 1 here
// without underflow.
Value limit;
limit.is_signed = false; // Like size, limit is always unsigned.
if (size.expr) {
// Dynamic size
limit.expr = b.Sub(size.expr, 1_u);
return b.Call("min", idx(), u32(mat->columns() - 1u));
},
[&](const sem::Array* arr) -> const ast::Expression* {
const ast::Expression* max = nullptr;
if (arr->IsRuntimeSized()) {
// Size is unknown until runtime.
// Must clamp, even if the index is constant.
auto* arr_ptr = b.AddressOf(ctx.Clone(expr->object));
max = b.Sub(b.Call("arrayLength", arr_ptr), 1_u);
} else {
// Constant size
limit.u32 = size.u32 - 1u;
if (sem->Index()->ConstantValue()) {
// Index and size is constant.
// Validation will have rejected any OOB accesses.
return nullptr;
}
Value idx; // index value
auto* idx_sem = ctx.src->Sem().Get(expr->index);
auto* idx_ty = idx_sem->Type()->UnwrapRef();
if (!idx_ty->IsAnyOf<sem::I32, sem::U32>()) {
max = b.Expr(u32(arr->Count() - 1u));
}
return b.Call("min", idx(), max);
},
[&](Default) {
TINT_ICE(Transform, b.Diagnostics())
<< "index must be u32 or i32, got " << idx_sem->Type()->TypeInfo().name;
<< "unhandled object type in robustness of array index: "
<< ctx.src->FriendlyName(ret_type->UnwrapRef());
return nullptr;
});
if (!clamped_idx) {
return nullptr; // Clamping not needed
}
if (auto* idx_constant = idx_sem->ConstantValue()) {
// Constant value index
auto val = std::get<AInt>(idx_constant->Value());
if (idx_constant->Type()->Is<sem::I32>()) {
idx.i32 = static_cast<int32_t>(val);
idx.is_signed = true;
} else if (idx_constant->Type()->Is<sem::U32>()) {
idx.u32 = static_cast<uint32_t>(val);
idx.is_signed = false;
} else {
TINT_ICE(Transform, b.Diagnostics()) << "unsupported constant value for accessor "
<< idx_constant->Type()->TypeInfo().name;
return nullptr;
}
} else {
// Dynamic value index
idx.expr = ctx.Clone(expr->index);
idx.is_signed = idx_ty->Is<sem::I32>();
}
// Clamp the index so that it cannot exceed limit.
if (idx.expr || limit.expr) {
// One of, or both of idx and limit are non-constant.
// If the index is signed, cast it to a u32 (with clamping if constant).
if (idx.is_signed) {
if (idx.expr) {
// We don't use a max(idx, 0) here, as that incurs a runtime
// performance cost, and if the unsigned value will be clamped by
// limit, resulting in a value between [0..limit)
idx.expr = b.Construct<u32>(idx.expr);
idx.is_signed = false;
} else {
idx.u32 = static_cast<uint32_t>(std::max(idx.i32, 0));
idx.is_signed = false;
}
}
// Convert idx and limit to expressions, so we can emit `min(idx, limit)`.
if (!idx.expr) {
idx.expr = b.Expr(u32(idx.u32));
}
if (!limit.expr) {
limit.expr = b.Expr(u32(limit.u32));
}
// Perform the clamp with `min(idx, limit)`
idx.expr = b.Call("min", idx.expr, limit.expr);
} else {
// Both idx and max are constant.
if (idx.is_signed) {
// The index is signed. Calculate limit as signed.
int32_t signed_limit = static_cast<int32_t>(
std::min<uint32_t>(limit.u32, std::numeric_limits<int32_t>::max()));
idx.i32 = std::max(idx.i32, 0);
idx.i32 = std::min(idx.i32, signed_limit);
} else {
// The index is unsigned.
idx.u32 = std::min(idx.u32, limit.u32);
}
}
// Convert idx to an expression, so we can emit the new accessor.
if (!idx.expr) {
idx.expr = idx.is_signed ? static_cast<const ast::Expression*>(b.Expr(i32(idx.i32)))
: static_cast<const ast::Expression*>(b.Expr(u32(idx.u32)));
}
// Clone arguments outside of create() call to have deterministic ordering
auto src = ctx.Clone(expr->source);
auto* obj = ctx.Clone(expr->object);
return b.IndexAccessor(src, obj, idx.expr);
return b.IndexAccessor(src, obj, clamped_idx);
}
/// @param type builtin type

View File

@ -25,20 +25,18 @@ TEST_F(RobustnessTest, Array_Let_Idx_Clamp) {
auto* src = R"(
var<private> a : array<f32, 3>;
let c : u32 = 1u;
fn f() {
let b : f32 = a[c];
let l : u32 = 1u;
let b : f32 = a[l];
}
)";
auto* expect = R"(
var<private> a : array<f32, 3>;
const c : u32 = 1u;
fn f() {
let b : f32 = a[1u];
let l : u32 = 1u;
let b : f32 = a[min(l, 2u)];
}
)";
@ -47,6 +45,30 @@ fn f() {
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Array_Let_Idx_Clamp_OutOfOrder) {
auto* src = R"(
fn f() {
let c : u32 = 1u;
let b : f32 = a[c];
}
var<private> a : array<f32, 3>;
)";
auto* expect = R"(
fn f() {
let c : u32 = 1u;
let b : f32 = a[min(c, 2u)];
}
var<private> a : array<f32, 3>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Array_Const_Idx_Clamp) {
auto* src = R"(
var<private> a : array<f32, 3>;
@ -63,35 +85,9 @@ var<private> a : array<f32, 3>;
const c : u32 = 1u;
fn f() {
let b : f32 = a[1u];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Array_Let_Idx_Clamp_OutOfOrder) {
auto* src = R"(
fn f() {
let b : f32 = a[c];
}
let c : u32 = 1u;
var<private> a : array<f32, 3>;
)";
auto* expect = R"(
fn f() {
let b : f32 = a[1u];
}
const c : u32 = 1u;
var<private> a : array<f32, 3>;
)";
auto got = Run<Robustness>(src);
@ -112,7 +108,7 @@ var<private> a : array<f32, 3>;
auto* expect = R"(
fn f() {
let b : f32 = a[1u];
let b : f32 = a[c];
}
const c : u32 = 1u;
@ -281,94 +277,6 @@ var<private> a : array<f32, 3>;
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Array_Idx_Negative) {
auto* src = R"(
var<private> a : array<f32, 3>;
fn f() {
var b : f32 = a[-1];
}
)";
auto* expect = R"(
var<private> a : array<f32, 3>;
fn f() {
var b : f32 = a[0i];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Array_Idx_Negative_OutOfOrder) {
auto* src = R"(
fn f() {
var b : f32 = a[-1];
}
var<private> a : array<f32, 3>;
)";
auto* expect = R"(
fn f() {
var b : f32 = a[0i];
}
var<private> a : array<f32, 3>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Array_Idx_OutOfBounds) {
auto* src = R"(
var<private> a : array<f32, 3>;
fn f() {
var b : f32 = a[3];
}
)";
auto* expect = R"(
var<private> a : array<f32, 3>;
fn f() {
var b : f32 = a[2i];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Array_Idx_OutOfBounds_OutOfOrder) {
auto* src = R"(
fn f() {
var b : f32 = a[3];
}
var<private> a : array<f32, 3>;
)";
auto* expect = R"(
fn f() {
var b : f32 = a[2i];
}
var<private> a : array<f32, 3>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
// TODO(crbug.com/tint/1177) - Validation currently forbids arrays larger than
// 0xffffffff. If WGSL supports 64-bit indexing, re-enable this test.
TEST_F(RobustnessTest, DISABLED_LargeArrays_Idx) {
@ -545,50 +453,6 @@ var<private> a : vec3<f32>;
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Vector_Swizzle_Idx_Scalar) {
auto* src = R"(
var<private> a : vec3<f32>;
fn f() {
var b : f32 = a.xy[2];
}
)";
auto* expect = R"(
var<private> a : vec3<f32>;
fn f() {
var b : f32 = a.xy[1i];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Vector_Swizzle_Idx_Scalar_OutOfOrder) {
auto* src = R"(
fn f() {
var b : f32 = a.xy[2];
}
var<private> a : vec3<f32>;
)";
auto* expect = R"(
fn f() {
var b : f32 = a.xy[1i];
}
var<private> a : vec3<f32>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Vector_Swizzle_Idx_Var) {
auto* src = R"(
var<private> a : vec3<f32>;
@ -693,94 +557,6 @@ var<private> a : vec3<f32>;
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Vector_Idx_Negative) {
auto* src = R"(
var<private> a : vec3<f32>;
fn f() {
var b : f32 = a[-1];
}
)";
auto* expect = R"(
var<private> a : vec3<f32>;
fn f() {
var b : f32 = a[0i];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Vector_Idx_Negative_OutOfOrder) {
auto* src = R"(
fn f() {
var b : f32 = a[-1];
}
var<private> a : vec3<f32>;
)";
auto* expect = R"(
fn f() {
var b : f32 = a[0i];
}
var<private> a : vec3<f32>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Vector_Idx_OutOfBounds) {
auto* src = R"(
var<private> a : vec3<f32>;
fn f() {
var b : f32 = a[3];
}
)";
auto* expect = R"(
var<private> a : vec3<f32>;
fn f() {
var b : f32 = a[2i];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Vector_Idx_OutOfBounds_OutOfOrder) {
auto* src = R"(
fn f() {
var b : f32 = a[3];
}
var<private> a : vec3<f32>;
)";
auto* expect = R"(
fn f() {
var b : f32 = a[2i];
}
var<private> a : vec3<f32>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Matrix_Idx_Scalar) {
auto* src = R"(
var<private> a : mat3x2<f32>;
@ -842,7 +618,7 @@ var<private> a : mat3x2<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1i];
var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1];
}
)";
@ -864,7 +640,7 @@ var<private> a : mat3x2<f32>;
auto* expect = R"(
fn f() {
var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1i];
var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1];
}
var<private> c : i32;
@ -894,7 +670,7 @@ var<private> a : mat3x2<f32>;
var<private> c : i32;
fn f() {
var b : f32 = a[1i][min(u32(((c + 2) - 3)), 1u)];
var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)];
}
)";
@ -916,7 +692,7 @@ var<private> a : mat3x2<f32>;
auto* expect = R"(
fn f() {
var b : f32 = a[1i][min(u32(((c + 2) - 3)), 1u)];
var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)];
}
var<private> c : i32;
@ -929,182 +705,6 @@ var<private> a : mat3x2<f32>;
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Matrix_Idx_Negative_Column) {
auto* src = R"(
var<private> a : mat3x2<f32>;
fn f() {
var b : f32 = a[-1][1];
}
)";
auto* expect = R"(
var<private> a : mat3x2<f32>;
fn f() {
var b : f32 = a[0i][1i];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Matrix_Idx_Negative_Column_OutOfOrder) {
auto* src = R"(
fn f() {
var b : f32 = a[-1][1];
}
var<private> a : mat3x2<f32>;
)";
auto* expect = R"(
fn f() {
var b : f32 = a[0i][1i];
}
var<private> a : mat3x2<f32>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Matrix_Idx_Negative_Row) {
auto* src = R"(
var<private> a : mat3x2<f32>;
fn f() {
var b : f32 = a[2][-1];
}
)";
auto* expect = R"(
var<private> a : mat3x2<f32>;
fn f() {
var b : f32 = a[2i][0i];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Matrix_Idx_Negative_Row_OutOfOrder) {
auto* src = R"(
fn f() {
var b : f32 = a[2][-1];
}
var<private> a : mat3x2<f32>;
)";
auto* expect = R"(
fn f() {
var b : f32 = a[2i][0i];
}
var<private> a : mat3x2<f32>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Column) {
auto* src = R"(
var<private> a : mat3x2<f32>;
fn f() {
var b : f32 = a[5][1];
}
)";
auto* expect = R"(
var<private> a : mat3x2<f32>;
fn f() {
var b : f32 = a[2i][1i];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Column_OutOfOrder) {
auto* src = R"(
fn f() {
var b : f32 = a[5][1];
}
var<private> a : mat3x2<f32>;
)";
auto* expect = R"(
fn f() {
var b : f32 = a[2i][1i];
}
var<private> a : mat3x2<f32>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Row) {
auto* src = R"(
var<private> a : mat3x2<f32>;
fn f() {
var b : f32 = a[2][5];
}
)";
auto* expect = R"(
var<private> a : mat3x2<f32>;
fn f() {
var b : f32 = a[2i][1i];
}
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Row_OutOfOrder) {
auto* src = R"(
fn f() {
var b : f32 = a[2][5];
}
var<private> a : mat3x2<f32>;
)";
auto* expect = R"(
fn f() {
var b : f32 = a[2i][1i];
}
var<private> a : mat3x2<f32>;
)";
auto got = Run<Robustness>(src);
EXPECT_EQ(expect, str(got));
}
// TODO(dsinclair): Implement when constant_id exists
TEST_F(RobustnessTest, DISABLED_Vector_Constant_Id_Clamps) {
// @id(1300) override idx : i32;
@ -1163,7 +763,7 @@ struct S {
@group(0) @binding(0) var<storage, read> s : S;
fn f() {
var d : f32 = s.b[min(25u, (arrayLength(&(s.b)) - 1u))];
var d : f32 = s.b[min(u32(25), (arrayLength(&(s.b)) - 1u))];
}
)";
@ -1188,7 +788,7 @@ struct S {
auto* expect = R"(
fn f() {
var d : f32 = s.b[min(25u, (arrayLength(&(s.b)) - 1u))];
var d : f32 = s.b[min(u32(25), (arrayLength(&(s.b)) - 1u))];
}
@group(0) @binding(0) var<storage, read> s : S;
@ -1464,7 +1064,7 @@ struct S {
const c : u32 = 1u;
fn f() {
let b : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
let b : f32 = s.b[min(c, (arrayLength(&(s.b)) - 1u))];
let x : i32 = min(1, 2);
let y : u32 = arrayLength(&(s.b));
}
@ -1477,112 +1077,79 @@ fn f() {
const char* kOmitSourceShader = R"(
struct S {
a : array<f32, 4>,
b : array<f32>,
vector : vec3<f32>,
fixed_arr : array<f32, 4>,
runtime_arr : array<f32>,
};
@group(0) @binding(0) var<storage, read> s : S;
type UArr = array<vec4<f32>, 4>;
struct U {
a : UArr,
vector : vec4<f32>,
fixed_arr : array<vec4<f32>, 4>,
};
@group(1) @binding(0) var<uniform> u : U;
fn f() {
// Signed
var i32_sa1 : f32 = s.a[4];
var i32_sa2 : f32 = s.a[1];
var i32_sa3 : f32 = s.a[0];
var i32_sa4 : f32 = s.a[-1];
var i32_sa5 : f32 = s.a[-4];
var i32_sb1 : f32 = s.b[4];
var i32_sb2 : f32 = s.b[1];
var i32_sb3 : f32 = s.b[0];
var i32_sb4 : f32 = s.b[-1];
var i32_sb5 : f32 = s.b[-4];
var i32_ua1 : f32 = u.a[4].x;
var i32_ua2 : f32 = u.a[1].x;
var i32_ua3 : f32 = u.a[0].x;
var i32_ua4 : f32 = u.a[-1].x;
var i32_ua5 : f32 = u.a[-4].x;
// Unsigned
var u32_sa1 : f32 = s.a[0u];
var u32_sa2 : f32 = s.a[1u];
var u32_sa3 : f32 = s.a[3u];
var u32_sa4 : f32 = s.a[4u];
var u32_sa5 : f32 = s.a[10u];
var u32_sa6 : f32 = s.a[100u];
var u32_sb1 : f32 = s.b[0u];
var u32_sb2 : f32 = s.b[1u];
var u32_sb3 : f32 = s.b[3u];
var u32_sb4 : f32 = s.b[4u];
var u32_sb5 : f32 = s.b[10u];
var u32_sb6 : f32 = s.b[100u];
var u32_ua1 : f32 = u.a[0u].x;
var u32_ua2 : f32 = u.a[1u].x;
var u32_ua3 : f32 = u.a[3u].x;
var u32_ua4 : f32 = u.a[4u].x;
var u32_ua5 : f32 = u.a[10u].x;
var u32_ua6 : f32 = u.a[100u].x;
// i32
{
let i = 0i;
var storage_vector : f32 = s.vector[i];
var storage_fixed_arr : f32 = s.fixed_arr[i];
var storage_runtime_arr : f32 = s.runtime_arr[i];
var uniform_vector : f32 = u.vector[i];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[i];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i];
}
// u32
{
let i = 0u;
var storage_vector : f32 = s.vector[i];
var storage_fixed_arr : f32 = s.fixed_arr[i];
var storage_runtime_arr : f32 = s.runtime_arr[i];
var uniform_vector : f32 = u.vector[i];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[i];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i];
}
}
)";
TEST_F(RobustnessTest, OmitNone) {
auto* expect = R"(
auto* expect =
R"(
struct S {
a : array<f32, 4>,
b : array<f32>,
vector : vec3<f32>,
fixed_arr : array<f32, 4>,
runtime_arr : array<f32>,
}
@group(0) @binding(0) var<storage, read> s : S;
type UArr = array<vec4<f32>, 4>;
struct U {
a : UArr,
vector : vec4<f32>,
fixed_arr : array<vec4<f32>, 4>,
}
@group(1) @binding(0) var<uniform> u : U;
fn f() {
var i32_sa1 : f32 = s.a[3i];
var i32_sa2 : f32 = s.a[1i];
var i32_sa3 : f32 = s.a[0i];
var i32_sa4 : f32 = s.a[0i];
var i32_sa5 : f32 = s.a[0i];
var i32_sb1 : f32 = s.b[min(4u, (arrayLength(&(s.b)) - 1u))];
var i32_sb2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
var i32_sb3 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
var i32_sb4 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
var i32_sb5 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
var i32_ua1 : f32 = u.a[3i].x;
var i32_ua2 : f32 = u.a[1i].x;
var i32_ua3 : f32 = u.a[0i].x;
var i32_ua4 : f32 = u.a[0i].x;
var i32_ua5 : f32 = u.a[0i].x;
var u32_sa1 : f32 = s.a[0u];
var u32_sa2 : f32 = s.a[1u];
var u32_sa3 : f32 = s.a[3u];
var u32_sa4 : f32 = s.a[3u];
var u32_sa5 : f32 = s.a[3u];
var u32_sa6 : f32 = s.a[3u];
var u32_sb1 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
var u32_sb2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
var u32_sb3 : f32 = s.b[min(3u, (arrayLength(&(s.b)) - 1u))];
var u32_sb4 : f32 = s.b[min(4u, (arrayLength(&(s.b)) - 1u))];
var u32_sb5 : f32 = s.b[min(10u, (arrayLength(&(s.b)) - 1u))];
var u32_sb6 : f32 = s.b[min(100u, (arrayLength(&(s.b)) - 1u))];
var u32_ua1 : f32 = u.a[0u].x;
var u32_ua2 : f32 = u.a[1u].x;
var u32_ua3 : f32 = u.a[3u].x;
var u32_ua4 : f32 = u.a[3u].x;
var u32_ua5 : f32 = u.a[3u].x;
var u32_ua6 : f32 = u.a[3u].x;
{
let i = 0i;
var storage_vector : f32 = s.vector[min(u32(i), 2u)];
var storage_fixed_arr : f32 = s.fixed_arr[min(u32(i), 3u)];
var storage_runtime_arr : f32 = s.runtime_arr[min(u32(i), (arrayLength(&(s.runtime_arr)) - 1u))];
var uniform_vector : f32 = u.vector[min(u32(i), 3u)];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[min(u32(i), 3u)];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][min(u32(i), 3u)];
}
{
let i = 0u;
var storage_vector : f32 = s.vector[min(i, 2u)];
var storage_fixed_arr : f32 = s.fixed_arr[min(i, 3u)];
var storage_runtime_arr : f32 = s.runtime_arr[min(i, (arrayLength(&(s.runtime_arr)) - 1u))];
var uniform_vector : f32 = u.vector[min(i, 3u)];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[min(i, 3u)];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][min(i, 3u)];
}
}
)";
@ -1596,56 +1163,42 @@ fn f() {
}
TEST_F(RobustnessTest, OmitStorage) {
auto* expect = R"(
auto* expect =
R"(
struct S {
a : array<f32, 4>,
b : array<f32>,
vector : vec3<f32>,
fixed_arr : array<f32, 4>,
runtime_arr : array<f32>,
}
@group(0) @binding(0) var<storage, read> s : S;
type UArr = array<vec4<f32>, 4>;
struct U {
a : UArr,
vector : vec4<f32>,
fixed_arr : array<vec4<f32>, 4>,
}
@group(1) @binding(0) var<uniform> u : U;
fn f() {
var i32_sa1 : f32 = s.a[4];
var i32_sa2 : f32 = s.a[1];
var i32_sa3 : f32 = s.a[0];
var i32_sa4 : f32 = s.a[-1];
var i32_sa5 : f32 = s.a[-4];
var i32_sb1 : f32 = s.b[4];
var i32_sb2 : f32 = s.b[1];
var i32_sb3 : f32 = s.b[0];
var i32_sb4 : f32 = s.b[-1];
var i32_sb5 : f32 = s.b[-4];
var i32_ua1 : f32 = u.a[3i].x;
var i32_ua2 : f32 = u.a[1i].x;
var i32_ua3 : f32 = u.a[0i].x;
var i32_ua4 : f32 = u.a[0i].x;
var i32_ua5 : f32 = u.a[0i].x;
var u32_sa1 : f32 = s.a[0u];
var u32_sa2 : f32 = s.a[1u];
var u32_sa3 : f32 = s.a[3u];
var u32_sa4 : f32 = s.a[4u];
var u32_sa5 : f32 = s.a[10u];
var u32_sa6 : f32 = s.a[100u];
var u32_sb1 : f32 = s.b[0u];
var u32_sb2 : f32 = s.b[1u];
var u32_sb3 : f32 = s.b[3u];
var u32_sb4 : f32 = s.b[4u];
var u32_sb5 : f32 = s.b[10u];
var u32_sb6 : f32 = s.b[100u];
var u32_ua1 : f32 = u.a[0u].x;
var u32_ua2 : f32 = u.a[1u].x;
var u32_ua3 : f32 = u.a[3u].x;
var u32_ua4 : f32 = u.a[3u].x;
var u32_ua5 : f32 = u.a[3u].x;
var u32_ua6 : f32 = u.a[3u].x;
{
let i = 0i;
var storage_vector : f32 = s.vector[i];
var storage_fixed_arr : f32 = s.fixed_arr[i];
var storage_runtime_arr : f32 = s.runtime_arr[i];
var uniform_vector : f32 = u.vector[min(u32(i), 3u)];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[min(u32(i), 3u)];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][min(u32(i), 3u)];
}
{
let i = 0u;
var storage_vector : f32 = s.vector[i];
var storage_fixed_arr : f32 = s.fixed_arr[i];
var storage_runtime_arr : f32 = s.runtime_arr[i];
var uniform_vector : f32 = u.vector[min(i, 3u)];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[min(i, 3u)];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][min(i, 3u)];
}
}
)";
@ -1661,56 +1214,42 @@ fn f() {
}
TEST_F(RobustnessTest, OmitUniform) {
auto* expect = R"(
auto* expect =
R"(
struct S {
a : array<f32, 4>,
b : array<f32>,
vector : vec3<f32>,
fixed_arr : array<f32, 4>,
runtime_arr : array<f32>,
}
@group(0) @binding(0) var<storage, read> s : S;
type UArr = array<vec4<f32>, 4>;
struct U {
a : UArr,
vector : vec4<f32>,
fixed_arr : array<vec4<f32>, 4>,
}
@group(1) @binding(0) var<uniform> u : U;
fn f() {
var i32_sa1 : f32 = s.a[3i];
var i32_sa2 : f32 = s.a[1i];
var i32_sa3 : f32 = s.a[0i];
var i32_sa4 : f32 = s.a[0i];
var i32_sa5 : f32 = s.a[0i];
var i32_sb1 : f32 = s.b[min(4u, (arrayLength(&(s.b)) - 1u))];
var i32_sb2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
var i32_sb3 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
var i32_sb4 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
var i32_sb5 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
var i32_ua1 : f32 = u.a[4].x;
var i32_ua2 : f32 = u.a[1].x;
var i32_ua3 : f32 = u.a[0].x;
var i32_ua4 : f32 = u.a[-1].x;
var i32_ua5 : f32 = u.a[-4].x;
var u32_sa1 : f32 = s.a[0u];
var u32_sa2 : f32 = s.a[1u];
var u32_sa3 : f32 = s.a[3u];
var u32_sa4 : f32 = s.a[3u];
var u32_sa5 : f32 = s.a[3u];
var u32_sa6 : f32 = s.a[3u];
var u32_sb1 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
var u32_sb2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
var u32_sb3 : f32 = s.b[min(3u, (arrayLength(&(s.b)) - 1u))];
var u32_sb4 : f32 = s.b[min(4u, (arrayLength(&(s.b)) - 1u))];
var u32_sb5 : f32 = s.b[min(10u, (arrayLength(&(s.b)) - 1u))];
var u32_sb6 : f32 = s.b[min(100u, (arrayLength(&(s.b)) - 1u))];
var u32_ua1 : f32 = u.a[0u].x;
var u32_ua2 : f32 = u.a[1u].x;
var u32_ua3 : f32 = u.a[3u].x;
var u32_ua4 : f32 = u.a[4u].x;
var u32_ua5 : f32 = u.a[10u].x;
var u32_ua6 : f32 = u.a[100u].x;
{
let i = 0i;
var storage_vector : f32 = s.vector[min(u32(i), 2u)];
var storage_fixed_arr : f32 = s.fixed_arr[min(u32(i), 3u)];
var storage_runtime_arr : f32 = s.runtime_arr[min(u32(i), (arrayLength(&(s.runtime_arr)) - 1u))];
var uniform_vector : f32 = u.vector[i];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[i];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i];
}
{
let i = 0u;
var storage_vector : f32 = s.vector[min(i, 2u)];
var storage_fixed_arr : f32 = s.fixed_arr[min(i, 3u)];
var storage_runtime_arr : f32 = s.runtime_arr[min(i, (arrayLength(&(s.runtime_arr)) - 1u))];
var uniform_vector : f32 = u.vector[i];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[i];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i];
}
}
)";
@ -1726,56 +1265,42 @@ fn f() {
}
TEST_F(RobustnessTest, OmitBoth) {
auto* expect = R"(
auto* expect =
R"(
struct S {
a : array<f32, 4>,
b : array<f32>,
vector : vec3<f32>,
fixed_arr : array<f32, 4>,
runtime_arr : array<f32>,
}
@group(0) @binding(0) var<storage, read> s : S;
type UArr = array<vec4<f32>, 4>;
struct U {
a : UArr,
vector : vec4<f32>,
fixed_arr : array<vec4<f32>, 4>,
}
@group(1) @binding(0) var<uniform> u : U;
fn f() {
var i32_sa1 : f32 = s.a[4];
var i32_sa2 : f32 = s.a[1];
var i32_sa3 : f32 = s.a[0];
var i32_sa4 : f32 = s.a[-1];
var i32_sa5 : f32 = s.a[-4];
var i32_sb1 : f32 = s.b[4];
var i32_sb2 : f32 = s.b[1];
var i32_sb3 : f32 = s.b[0];
var i32_sb4 : f32 = s.b[-1];
var i32_sb5 : f32 = s.b[-4];
var i32_ua1 : f32 = u.a[4].x;
var i32_ua2 : f32 = u.a[1].x;
var i32_ua3 : f32 = u.a[0].x;
var i32_ua4 : f32 = u.a[-1].x;
var i32_ua5 : f32 = u.a[-4].x;
var u32_sa1 : f32 = s.a[0u];
var u32_sa2 : f32 = s.a[1u];
var u32_sa3 : f32 = s.a[3u];
var u32_sa4 : f32 = s.a[4u];
var u32_sa5 : f32 = s.a[10u];
var u32_sa6 : f32 = s.a[100u];
var u32_sb1 : f32 = s.b[0u];
var u32_sb2 : f32 = s.b[1u];
var u32_sb3 : f32 = s.b[3u];
var u32_sb4 : f32 = s.b[4u];
var u32_sb5 : f32 = s.b[10u];
var u32_sb6 : f32 = s.b[100u];
var u32_ua1 : f32 = u.a[0u].x;
var u32_ua2 : f32 = u.a[1u].x;
var u32_ua3 : f32 = u.a[3u].x;
var u32_ua4 : f32 = u.a[4u].x;
var u32_ua5 : f32 = u.a[10u].x;
var u32_ua6 : f32 = u.a[100u].x;
{
let i = 0i;
var storage_vector : f32 = s.vector[i];
var storage_fixed_arr : f32 = s.fixed_arr[i];
var storage_runtime_arr : f32 = s.runtime_arr[i];
var uniform_vector : f32 = u.vector[i];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[i];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i];
}
{
let i = 0u;
var storage_vector : f32 = s.vector[i];
var storage_fixed_arr : f32 = s.fixed_arr[i];
var storage_runtime_arr : f32 = s.runtime_arr[i];
var uniform_vector : f32 = u.vector[i];
var uniform_fixed_arr : vec4<f32> = u.fixed_arr[i];
var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i];
}
}
)";