From f3d9ea4beaf1f2746c5ede59c747829a9d38eb94 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 13 Sep 2022 22:47:12 +0000 Subject: [PATCH] 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 Reviewed-by: David Neto Kokoro: Kokoro --- docs/tint/origin-trial-changes.md | 4 + src/tint/resolver/const_eval.cc | 27 +- src/tint/resolver/const_eval_test.cc | 230 +------- src/tint/transform/robustness.cc | 183 ++---- src/tint/transform/robustness_test.cc | 797 ++++++-------------------- 5 files changed, 256 insertions(+), 985 deletions(-) diff --git a/docs/tint/origin-trial-changes.md b/docs/tint/origin-trial-changes.md index 93c53e5161..6c9e632dc4 100644 --- a/docs/tint/origin-trial-changes.md +++ b/docs/tint/origin-trial-changes.md @@ -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) diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc index a0974e4443..8e46f9a410 100644 --- a/src/tint/resolver/const_eval.cc +++ b/src/tint/resolver/const_eval.cc @@ -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(); - if (idx < 0 || idx >= el_count) { - auto clamped = std::min(std::max(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), - idx_expr->Declaration()->source); - idx = 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); + return utils::Failure; + } + + auto obj_val = obj_expr->ConstantValue(); + if (!obj_val) { + return nullptr; } return obj_val->Index(static_cast(idx)); diff --git a/src/tint/resolver/const_eval_test.cc b/src/tint/resolver/const_eval_test.cc index 1d5b7d2596..f06fa32b66 100644 --- a/src/tint/resolver/const_eval_test.cc +++ b/src/tint/resolver/const_eval_test.cc @@ -2498,34 +2498,16 @@ TEST_F(ResolverConstEvalTest, Vec3_Index_OOB_High) { auto* expr = IndexAccessor(vec3(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()); - 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(), 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(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()); - 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(), 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(); - 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(), 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(), 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(); - 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(), 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(), 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(); - ASSERT_NE(vec, nullptr); - EXPECT_TRUE(vec->type()->Is()); - 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(), 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(), 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(), 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(); - ASSERT_NE(vec, nullptr); - EXPECT_TRUE(vec->type()->Is()); - 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()), 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(), 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(), 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(), 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(), 2_u), // array, 2u> - mat2x3(vec3(1_f, 2_f, 3_f), // - vec3(4_f, 5_f, 6_f)), // - mat2x3(vec3(7_f, 8_f, 9_f), // - vec3(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(); - ASSERT_NE(mat->Type(), nullptr); - EXPECT_TRUE(ty->ColumnType()->Is()); - 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(), 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(), 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(), 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(), 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(), 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(), 6_f); - } - { - auto* vec = Sem().Get(vec_expr); - EXPECT_NE(vec, nullptr); - auto* ty = vec->Type()->As(); - ASSERT_NE(vec->Type(), nullptr); - EXPECT_TRUE(ty->type()->Is()); - 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(), 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(), 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(), 3_f); - } - { - auto* f = Sem().Get(f32_expr); - EXPECT_NE(f, nullptr); - EXPECT_TRUE(f->Type()->Is()); - 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(), 3_f); - } -} - //////////////////////////////////////////////////////////////////////////////////////////////////// // Member accessing //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/tint/transform/robustness.cc b/src/tint/transform/robustness.cc index beb11082f1..e662e56b4c 100644 --- a/src/tint/transform/robustness.cc +++ b/src/tint/transform/robustness.cc @@ -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(); + auto* ret_type = sem->Type(); auto* ref = ret_type->As(); 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()) { - size.u32 = vec->Width(); - - } else if (auto* arr = ret_unwrapped->As()) { - size.u32 = arr->Count(); - } else if (auto* mat = ret_unwrapped->As()) { - // 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 { - return nullptr; - } - - if (size.u32 == 0) { - if (!ret_unwrapped->Is()) { - b.Diagnostics().add_error(diag::System::Transform, "invalid 0 sized non-array", - expr->source); - 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); - } else { - // Constant size - limit.u32 = size.u32 - 1u; - } - - Value idx; // index value - - auto* idx_sem = ctx.src->Sem().Get(expr->index); - auto* idx_ty = idx_sem->Type()->UnwrapRef(); - if (!idx_ty->IsAnyOf()) { - TINT_ICE(Transform, b.Diagnostics()) - << "index must be u32 or i32, got " << idx_sem->Type()->TypeInfo().name; - return nullptr; - } - - if (auto* idx_constant = idx_sem->ConstantValue()) { - // Constant value index - auto val = std::get(idx_constant->Value()); - if (idx_constant->Type()->Is()) { - idx.i32 = static_cast(val); - idx.is_signed = true; - } else if (idx_constant->Type()->Is()) { - idx.u32 = static_cast(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(); - } - - // 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(idx.expr); - idx.is_signed = false; - } else { - idx.u32 = static_cast(std::max(idx.i32, 0)); - idx.is_signed = false; + 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; } - } - // 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)); - } + 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; + } - // 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( - std::min(limit.u32, std::numeric_limits::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); - } + 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 { + if (sem->Index()->ConstantValue()) { + // Index and size is constant. + // Validation will have rejected any OOB accesses. + return nullptr; + } + max = b.Expr(u32(arr->Count() - 1u)); + } + return b.Call("min", idx(), max); + }, + [&](Default) { + TINT_ICE(Transform, b.Diagnostics()) + << "unhandled object type in robustness of array index: " + << ctx.src->FriendlyName(ret_type->UnwrapRef()); + return nullptr; + }); + + if (!clamped_idx) { + return nullptr; // Clamping not needed } - // Convert idx to an expression, so we can emit the new accessor. - if (!idx.expr) { - idx.expr = idx.is_signed ? static_cast(b.Expr(i32(idx.i32))) - : static_cast(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 diff --git a/src/tint/transform/robustness_test.cc b/src/tint/transform/robustness_test.cc index 8dab595725..e3d08f0904 100644 --- a/src/tint/transform/robustness_test.cc +++ b/src/tint/transform/robustness_test.cc @@ -25,20 +25,18 @@ TEST_F(RobustnessTest, Array_Let_Idx_Clamp) { auto* src = R"( var a : array; -let c : u32 = 1u; - fn f() { - let b : f32 = a[c]; + let l : u32 = 1u; + let b : f32 = a[l]; } )"; auto* expect = R"( var a : array; -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 a : array; +)"; + + auto* expect = R"( +fn f() { + let c : u32 = 1u; + let b : f32 = a[min(c, 2u)]; +} + +var a : array; +)"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + TEST_F(RobustnessTest, Array_Const_Idx_Clamp) { auto* src = R"( var a : array; @@ -63,35 +85,9 @@ var a : array; const c : u32 = 1u; -fn f() { - let b : f32 = a[1u]; -} -)"; - - auto got = Run(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 a : array; -)"; - - auto* expect = R"( -fn f() { - let b : f32 = a[1u]; -} - -const c : u32 = 1u; - -var a : array; )"; auto got = Run(src); @@ -112,7 +108,7 @@ var a : array; auto* expect = R"( fn f() { - let b : f32 = a[1u]; + let b : f32 = a[c]; } const c : u32 = 1u; @@ -281,94 +277,6 @@ var a : array; EXPECT_EQ(expect, str(got)); } -TEST_F(RobustnessTest, Array_Idx_Negative) { - auto* src = R"( -var a : array; - -fn f() { - var b : f32 = a[-1]; -} -)"; - - auto* expect = R"( -var a : array; - -fn f() { - var b : f32 = a[0i]; -} -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - -TEST_F(RobustnessTest, Array_Idx_Negative_OutOfOrder) { - auto* src = R"( -fn f() { - var b : f32 = a[-1]; -} - -var a : array; -)"; - - auto* expect = R"( -fn f() { - var b : f32 = a[0i]; -} - -var a : array; -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - -TEST_F(RobustnessTest, Array_Idx_OutOfBounds) { - auto* src = R"( -var a : array; - -fn f() { - var b : f32 = a[3]; -} -)"; - - auto* expect = R"( -var a : array; - -fn f() { - var b : f32 = a[2i]; -} -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - -TEST_F(RobustnessTest, Array_Idx_OutOfBounds_OutOfOrder) { - auto* src = R"( -fn f() { - var b : f32 = a[3]; -} - -var a : array; -)"; - - auto* expect = R"( -fn f() { - var b : f32 = a[2i]; -} - -var a : array; -)"; - - auto got = Run(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 a : vec3; EXPECT_EQ(expect, str(got)); } -TEST_F(RobustnessTest, Vector_Swizzle_Idx_Scalar) { - auto* src = R"( -var a : vec3; - -fn f() { - var b : f32 = a.xy[2]; -} -)"; - - auto* expect = R"( -var a : vec3; - -fn f() { - var b : f32 = a.xy[1i]; -} -)"; - - auto got = Run(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 a : vec3; -)"; - - auto* expect = R"( -fn f() { - var b : f32 = a.xy[1i]; -} - -var a : vec3; -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - TEST_F(RobustnessTest, Vector_Swizzle_Idx_Var) { auto* src = R"( var a : vec3; @@ -693,94 +557,6 @@ var a : vec3; EXPECT_EQ(expect, str(got)); } -TEST_F(RobustnessTest, Vector_Idx_Negative) { - auto* src = R"( -var a : vec3; - -fn f() { - var b : f32 = a[-1]; -} -)"; - - auto* expect = R"( -var a : vec3; - -fn f() { - var b : f32 = a[0i]; -} -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - -TEST_F(RobustnessTest, Vector_Idx_Negative_OutOfOrder) { - auto* src = R"( -fn f() { - var b : f32 = a[-1]; -} - -var a : vec3; -)"; - - auto* expect = R"( -fn f() { - var b : f32 = a[0i]; -} - -var a : vec3; -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - -TEST_F(RobustnessTest, Vector_Idx_OutOfBounds) { - auto* src = R"( -var a : vec3; - -fn f() { - var b : f32 = a[3]; -} -)"; - - auto* expect = R"( -var a : vec3; - -fn f() { - var b : f32 = a[2i]; -} -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - -TEST_F(RobustnessTest, Vector_Idx_OutOfBounds_OutOfOrder) { - auto* src = R"( -fn f() { - var b : f32 = a[3]; -} - -var a : vec3; -)"; - - auto* expect = R"( -fn f() { - var b : f32 = a[2i]; -} - -var a : vec3; -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - TEST_F(RobustnessTest, Matrix_Idx_Scalar) { auto* src = R"( var a : mat3x2; @@ -842,7 +618,7 @@ var a : mat3x2; var 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 a : mat3x2; 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 c : i32; @@ -894,7 +670,7 @@ var a : mat3x2; var 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 a : mat3x2; 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 c : i32; @@ -929,182 +705,6 @@ var a : mat3x2; EXPECT_EQ(expect, str(got)); } -TEST_F(RobustnessTest, Matrix_Idx_Negative_Column) { - auto* src = R"( -var a : mat3x2; - -fn f() { - var b : f32 = a[-1][1]; -} -)"; - - auto* expect = R"( -var a : mat3x2; - -fn f() { - var b : f32 = a[0i][1i]; -} -)"; - - auto got = Run(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 a : mat3x2; -)"; - - auto* expect = R"( -fn f() { - var b : f32 = a[0i][1i]; -} - -var a : mat3x2; -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - -TEST_F(RobustnessTest, Matrix_Idx_Negative_Row) { - auto* src = R"( -var a : mat3x2; - -fn f() { - var b : f32 = a[2][-1]; -} -)"; - - auto* expect = R"( -var a : mat3x2; - -fn f() { - var b : f32 = a[2i][0i]; -} -)"; - - auto got = Run(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 a : mat3x2; -)"; - - auto* expect = R"( -fn f() { - var b : f32 = a[2i][0i]; -} - -var a : mat3x2; -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - -TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Column) { - auto* src = R"( -var a : mat3x2; - -fn f() { - var b : f32 = a[5][1]; -} -)"; - - auto* expect = R"( -var a : mat3x2; - -fn f() { - var b : f32 = a[2i][1i]; -} -)"; - - auto got = Run(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 a : mat3x2; -)"; - - auto* expect = R"( -fn f() { - var b : f32 = a[2i][1i]; -} - -var a : mat3x2; -)"; - - auto got = Run(src); - - EXPECT_EQ(expect, str(got)); -} - -TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Row) { - auto* src = R"( -var a : mat3x2; - -fn f() { - var b : f32 = a[2][5]; -} -)"; - - auto* expect = R"( -var a : mat3x2; - -fn f() { - var b : f32 = a[2i][1i]; -} -)"; - - auto got = Run(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 a : mat3x2; -)"; - - auto* expect = R"( -fn f() { - var b : f32 = a[2i][1i]; -} - -var a : mat3x2; -)"; - - auto got = Run(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 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 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, - b : array, + vector : vec3, + fixed_arr : array, + runtime_arr : array, }; @group(0) @binding(0) var s : S; -type UArr = array, 4>; struct U { - a : UArr, + vector : vec4, + fixed_arr : array, 4>, }; @group(1) @binding(0) var 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 = 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 = 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, - b : array, + vector : vec3, + fixed_arr : array, + runtime_arr : array, } @group(0) @binding(0) var s : S; -type UArr = array, 4>; - struct U { - a : UArr, + vector : vec4, + fixed_arr : array, 4>, } @group(1) @binding(0) var 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 = 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 = 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, - b : array, + vector : vec3, + fixed_arr : array, + runtime_arr : array, } @group(0) @binding(0) var s : S; -type UArr = array, 4>; - struct U { - a : UArr, + vector : vec4, + fixed_arr : array, 4>, } @group(1) @binding(0) var 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 = 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 = 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, - b : array, + vector : vec3, + fixed_arr : array, + runtime_arr : array, } @group(0) @binding(0) var s : S; -type UArr = array, 4>; - struct U { - a : UArr, + vector : vec4, + fixed_arr : array, 4>, } @group(1) @binding(0) var 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 = 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 = 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, - b : array, + vector : vec3, + fixed_arr : array, + runtime_arr : array, } @group(0) @binding(0) var s : S; -type UArr = array, 4>; - struct U { - a : UArr, + vector : vec4, + fixed_arr : array, 4>, } @group(1) @binding(0) var 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 = 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 = u.fixed_arr[i]; + var uniform_fixed_arr_vector : f32 = u.fixed_arr[0][i]; + } } )";