Use min for bounded array accessing.

This CL updates the BoundArrayAccess transform to use `min(u32(val),
size)` instead of `clamp(val, 0, size)` so as to reduce the number of
instructions needed to clamp within range.

Bug: tint:285
Change-Id: Ic12bd67f3d755c8e52590f0585bac114ba9eaa94
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31360
Commit-Queue: David Neto <dneto@google.com>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
dan sinclair 2020-10-29 17:56:06 +00:00 committed by Commit Bot service account
parent c8b2d23e9d
commit bfc2fceb84
2 changed files with 80 additions and 80 deletions

View File

@ -237,16 +237,17 @@ bool BoundArrayAccessorsTransform::ProcessAccessExpression(
} else {
auto* u32 = ctx_->type_mgr().Get(std::make_unique<ast::type::U32Type>());
ast::ExpressionList cast_expr;
cast_expr.push_back(expr->take_idx_expr());
ast::ExpressionList params;
params.push_back(expr->take_idx_expr());
params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
std::make_unique<ast::UintLiteral>(u32, 0)));
params.push_back(std::make_unique<ast::TypeConstructorExpression>(
u32, std::move(cast_expr)));
params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
std::make_unique<ast::UintLiteral>(u32, size - 1)));
auto call_expr = std::make_unique<ast::CallExpression>(
std::make_unique<ast::IdentifierExpression>("clamp"),
std::move(params));
std::make_unique<ast::IdentifierExpression>("min"), std::move(params));
call_expr->set_result_type(u32);
expr->set_idx_expr(std::move(call_expr));

View File

@ -35,6 +35,7 @@
#include "src/ast/type/u32_type.h"
#include "src/ast/type/vector_type.h"
#include "src/ast/type/void_type.h"
#include "src/ast/type_constructor_expression.h"
#include "src/ast/uint_literal.h"
#include "src/ast/variable.h"
#include "src/ast/variable_decl_statement.h"
@ -82,7 +83,7 @@ TEST_F(BoundArrayAccessorsTest, Ptrs_Clamp) {
// const c : u32 = 1;
// const b : ptr<function, f32> = a[c]
//
// -> const b : ptr<function, i32> = a[clamp(c, 0, 2)]
// -> const b : ptr<function, i32> = a[min(u32(c), 2)]
ast::type::F32Type f32;
ast::type::U32Type u32;
@ -119,21 +120,21 @@ TEST_F(BoundArrayAccessorsTest, Ptrs_Clamp) {
auto* idx = ptr->idx_expr()->AsCall();
ASSERT_TRUE(idx->func()->IsIdentifier());
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "clamp");
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "min");
ASSERT_EQ(idx->params().size(), 3u);
ASSERT_EQ(idx->params()[0].get(), access_ptr);
ASSERT_EQ(idx->params().size(), 2u);
ASSERT_TRUE(idx->params()[0]->IsConstructor());
ASSERT_TRUE(idx->params()[0]->AsConstructor()->IsTypeConstructor());
auto* tc = idx->params()[0]->AsConstructor()->AsTypeConstructor();
EXPECT_TRUE(tc->type()->IsU32());
ASSERT_EQ(tc->values().size(), 1u);
ASSERT_EQ(tc->values()[0].get(), access_ptr);
ASSERT_TRUE(idx->params()[1]->IsConstructor());
ASSERT_TRUE(idx->params()[1]->AsConstructor()->IsScalarConstructor());
auto* scalar = idx->params()[1]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 0u);
ASSERT_TRUE(idx->params()[2]->IsConstructor());
ASSERT_TRUE(idx->params()[2]->AsConstructor()->IsScalarConstructor());
scalar = idx->params()[2]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 2u);
ASSERT_NE(ptr->idx_expr()->result_type(), nullptr);
@ -146,7 +147,7 @@ TEST_F(BoundArrayAccessorsTest, Array_Idx_Nested_Scalar) {
// var i : u32;
// var c : f32 = a[b[i]];
//
// -> var c : f32 = a[clamp(b[clamp(i, 0, 4)], 0, 2)];
// -> var c : f32 = a[min(u32(b[min(u32(i), 4)]), 2)];
ast::type::F32Type f32;
ast::type::U32Type u32;
@ -186,42 +187,41 @@ TEST_F(BoundArrayAccessorsTest, Array_Idx_Nested_Scalar) {
auto* idx = ptr->idx_expr()->AsCall();
ASSERT_TRUE(idx->func()->IsIdentifier());
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "clamp");
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "min");
ASSERT_EQ(idx->params().size(), 3u);
ASSERT_EQ(idx->params().size(), 2u);
auto* sub = idx->params()[0].get();
ASSERT_TRUE(idx->params()[0]->IsConstructor());
ASSERT_TRUE(idx->params()[0]->AsConstructor()->IsTypeConstructor());
auto* tc = idx->params()[0]->AsConstructor()->AsTypeConstructor();
EXPECT_TRUE(tc->type()->IsU32());
ASSERT_EQ(tc->values().size(), 1u);
auto* sub = tc->values()[0].get();
ASSERT_TRUE(sub->IsArrayAccessor());
ASSERT_TRUE(sub->AsArrayAccessor()->idx_expr()->IsCall());
auto* sub_idx = sub->AsArrayAccessor()->idx_expr()->AsCall();
ASSERT_TRUE(sub_idx->func()->IsIdentifier());
EXPECT_EQ(sub_idx->func()->AsIdentifier()->name(), "clamp");
EXPECT_EQ(sub_idx->func()->AsIdentifier()->name(), "min");
ASSERT_EQ(sub_idx->params()[0].get(), b_access_ptr);
ASSERT_TRUE(sub_idx->params()[0]->IsConstructor());
ASSERT_TRUE(sub_idx->params()[0]->AsConstructor()->IsTypeConstructor());
tc = sub_idx->params()[0]->AsConstructor()->AsTypeConstructor();
EXPECT_TRUE(tc->type()->IsU32());
ASSERT_EQ(tc->values().size(), 1u);
ASSERT_EQ(tc->values()[0].get(), b_access_ptr);
ASSERT_TRUE(sub_idx->params()[1]->IsConstructor());
ASSERT_TRUE(sub_idx->params()[1]->AsConstructor()->IsScalarConstructor());
auto* scalar = sub_idx->params()[1]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 0u);
ASSERT_TRUE(sub_idx->params()[2]->IsConstructor());
ASSERT_TRUE(sub_idx->params()[2]->AsConstructor()->IsScalarConstructor());
scalar = sub_idx->params()[2]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 4u);
ASSERT_TRUE(idx->params()[1]->IsConstructor());
ASSERT_TRUE(idx->params()[1]->AsConstructor()->IsScalarConstructor());
scalar = idx->params()[1]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 0u);
ASSERT_TRUE(idx->params()[2]->IsConstructor());
ASSERT_TRUE(idx->params()[2]->AsConstructor()->IsScalarConstructor());
scalar = idx->params()[2]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 2u);
ASSERT_NE(ptr->idx_expr()->result_type(), nullptr);
@ -273,7 +273,7 @@ TEST_F(BoundArrayAccessorsTest, Array_Idx_Expr) {
// var c : u32;
// var b : f32 = a[c + 2 - 3]
//
// -> var b : f32 = a[clamp((c + 2 - 3), 0, 2)]
// -> var b : f32 = a[min(u32(c + 2 - 3), 2)]
ast::type::F32Type f32;
ast::type::U32Type u32;
@ -312,21 +312,21 @@ TEST_F(BoundArrayAccessorsTest, Array_Idx_Expr) {
auto* idx = ptr->idx_expr()->AsCall();
ASSERT_TRUE(idx->func()->IsIdentifier());
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "clamp");
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "min");
ASSERT_EQ(idx->params().size(), 3u);
ASSERT_EQ(idx->params()[0].get(), access_ptr);
ASSERT_EQ(idx->params().size(), 2u);
ASSERT_TRUE(idx->params()[0]->IsConstructor());
ASSERT_TRUE(idx->params()[0]->AsConstructor()->IsTypeConstructor());
auto* tc = idx->params()[0]->AsConstructor()->AsTypeConstructor();
EXPECT_TRUE(tc->type()->IsU32());
ASSERT_EQ(tc->values().size(), 1u);
ASSERT_EQ(tc->values()[0].get(), access_ptr);
ASSERT_TRUE(idx->params()[1]->IsConstructor());
ASSERT_TRUE(idx->params()[1]->AsConstructor()->IsScalarConstructor());
auto* scalar = idx->params()[1]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 0u);
ASSERT_TRUE(idx->params()[2]->IsConstructor());
ASSERT_TRUE(idx->params()[2]->AsConstructor()->IsScalarConstructor());
scalar = idx->params()[2]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 2u);
ASSERT_NE(ptr->idx_expr()->result_type(), nullptr);
@ -458,7 +458,7 @@ TEST_F(BoundArrayAccessorsTest, Vector_Idx_Expr) {
// var c : u32;
// var b : f32 = a[c + 2 - 3]
//
// -> var b : f32 = a[clamp((c + 2 - 3), 0, 2)]
// -> var b : f32 = a[min(u32(c + 2 - 3), 2)]
ast::type::F32Type f32;
ast::type::U32Type u32;
@ -497,21 +497,20 @@ TEST_F(BoundArrayAccessorsTest, Vector_Idx_Expr) {
auto* idx = ptr->idx_expr()->AsCall();
ASSERT_TRUE(idx->func()->IsIdentifier());
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "clamp");
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "min");
ASSERT_EQ(idx->params().size(), 3u);
ASSERT_EQ(idx->params()[0].get(), access_ptr);
ASSERT_EQ(idx->params().size(), 2u);
ASSERT_TRUE(idx->params()[0]->IsConstructor());
ASSERT_TRUE(idx->params()[0]->AsConstructor()->IsTypeConstructor());
auto* tc = idx->params()[0]->AsConstructor()->AsTypeConstructor();
EXPECT_TRUE(tc->type()->IsU32());
ASSERT_EQ(tc->values().size(), 1u);
ASSERT_EQ(tc->values()[0].get(), access_ptr);
ASSERT_TRUE(idx->params()[1]->IsConstructor());
ASSERT_TRUE(idx->params()[1]->AsConstructor()->IsScalarConstructor());
auto* scalar = idx->params()[1]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 0u);
ASSERT_TRUE(idx->params()[2]->IsConstructor());
ASSERT_TRUE(idx->params()[2]->AsConstructor()->IsScalarConstructor());
scalar = idx->params()[2]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 2u);
ASSERT_NE(ptr->idx_expr()->result_type(), nullptr);
@ -659,7 +658,7 @@ TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Column) {
// var c : u32;
// var b : f32 = a[c + 2 - 3][1]
//
// -> var b : f32 = a[clamp((c + 2 - 3), 0, 2)][1]
// -> var b : f32 = a[min(u32(c + 2 - 3), 2)][1]
ast::type::F32Type f32;
ast::type::U32Type u32;
@ -705,21 +704,21 @@ TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Column) {
ASSERT_TRUE(ary->idx_expr()->IsCall());
auto* idx = ary->idx_expr()->AsCall();
ASSERT_TRUE(idx->func()->IsIdentifier());
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "clamp");
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "min");
ASSERT_EQ(idx->params().size(), 3u);
ASSERT_EQ(idx->params()[0].get(), access_ptr);
ASSERT_EQ(idx->params().size(), 2u);
ASSERT_TRUE(idx->params()[0]->IsConstructor());
ASSERT_TRUE(idx->params()[0]->AsConstructor()->IsTypeConstructor());
auto* tc = idx->params()[0]->AsConstructor()->AsTypeConstructor();
EXPECT_TRUE(tc->type()->IsU32());
ASSERT_EQ(tc->values().size(), 1u);
ASSERT_EQ(tc->values()[0].get(), access_ptr);
ASSERT_TRUE(idx->params()[1]->IsConstructor());
ASSERT_TRUE(idx->params()[1]->AsConstructor()->IsScalarConstructor());
auto* scalar = idx->params()[1]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 0u);
ASSERT_TRUE(idx->params()[2]->IsConstructor());
ASSERT_TRUE(idx->params()[2]->AsConstructor()->IsScalarConstructor());
scalar = idx->params()[2]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 2u);
ASSERT_NE(ary->idx_expr()->result_type(), nullptr);
@ -741,7 +740,7 @@ TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Row) {
// var c : u32;
// var b : f32 = a[1][c + 2 - 3]
//
// -> var b : f32 = a[1][clamp((c + 2 - 3), 0, 1)]
// -> var b : f32 = a[1][min(u32(c + 2 - 3), 1)]
ast::type::F32Type f32;
ast::type::U32Type u32;
@ -794,21 +793,21 @@ TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Row) {
ASSERT_TRUE(ptr->idx_expr()->IsCall());
auto* idx = ptr->idx_expr()->AsCall();
ASSERT_TRUE(idx->func()->IsIdentifier());
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "clamp");
EXPECT_EQ(idx->func()->AsIdentifier()->name(), "min");
ASSERT_EQ(idx->params().size(), 3u);
ASSERT_EQ(idx->params()[0].get(), access_ptr);
ASSERT_EQ(idx->params().size(), 2u);
ASSERT_TRUE(idx->params()[0]->IsConstructor());
ASSERT_TRUE(idx->params()[0]->AsConstructor()->IsTypeConstructor());
auto* tc = idx->params()[0]->AsConstructor()->AsTypeConstructor();
EXPECT_TRUE(tc->type()->IsU32());
ASSERT_EQ(tc->values().size(), 1u);
ASSERT_EQ(tc->values()[0].get(), access_ptr);
ASSERT_TRUE(idx->params()[1]->IsConstructor());
ASSERT_TRUE(idx->params()[1]->AsConstructor()->IsScalarConstructor());
scalar = idx->params()[1]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 0u);
ASSERT_TRUE(idx->params()[2]->IsConstructor());
ASSERT_TRUE(idx->params()[2]->AsConstructor()->IsScalarConstructor());
scalar = idx->params()[2]->AsConstructor()->AsScalarConstructor();
ASSERT_TRUE(scalar->literal()->IsUint());
EXPECT_EQ(scalar->literal()->AsUint()->value(), 1u);
ASSERT_NE(ary->idx_expr()->result_type(), nullptr);
@ -1046,7 +1045,7 @@ TEST_F(BoundArrayAccessorsTest, DISABLED_Vector_Constant_Id_Clamps) {
// var a : vec3<f32>
// var b : f32 = a[idx]
//
// ->var b : f32 = a[clamp(idx, 0, 2)]
// ->var b : f32 = a[min(u32(idx), 2)]
}
// TODO(dsinclair): Implement when constant_id exists
@ -1055,7 +1054,7 @@ TEST_F(BoundArrayAccessorsTest, DISABLED_Array_Constant_Id_Clamps) {
// var a : array<f32, 4>
// var b : f32 = a[idx]
//
// -> var b : f32 = a[clamp(idx, 0, 3)]
// -> var b : f32 = a[min(u32(idx), 3)]
}
// TODO(dsinclair): Implement when constant_id exists
@ -1064,7 +1063,7 @@ TEST_F(BoundArrayAccessorsTest, DISABLED_Matrix_Column_Constant_Id_Clamps) {
// var a : mat3x2<f32>
// var b : f32 = a[idx][1]
//
// -> var b : f32 = a[clamp(idx, 0, 2)][1]
// -> var b : f32 = a[min(u32(idx), 2)][1]
}
// TODO(dsinclair): Implement when constant_id exists
@ -1073,7 +1072,7 @@ TEST_F(BoundArrayAccessorsTest, DISABLED_Matrix_Row_Constant_Id_Clamps) {
// var a : mat3x2<f32>
// var b : f32 = a[1][idx]
//
// -> var b : f32 = a[1][clamp(idx, 0, 1)]
// -> var b : f32 = a[1][min(u32(idx), 0, 1)]
}
// TODO(dsinclair): Implement when we have arrayLength for Runtime Arrays
@ -1085,7 +1084,7 @@ TEST_F(BoundArrayAccessorsTest, DISABLED_RuntimeArray_Clamps) {
// S s;
// var b : f32 = s.b[25]
//
// -> var b : f32 = s.b[clamp(25, 0, arrayLength(s.b))]
// -> var b : f32 = s.b[min(u32(25), arrayLength(s.b))]
}
// TODO(dsinclair): Clamp atomics when available.
@ -1109,8 +1108,8 @@ TEST_F(BoundArrayAccessorsTest, DISABLED_Scoped_Variable) {
// }
// var c : f32 = a[i];
//
// -> var b : f32 = a[clamp(i, 0, 4)];
// var c : f32 = a[clamp(i, 0, 2)];
// -> var b : f32 = a[min(u32(i), 4)];
// var c : f32 = a[min(u32(i), 2)];
FAIL();
}