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>
diff --git a/src/transform/bound_array_accessors_transform.cc b/src/transform/bound_array_accessors_transform.cc
index 39ace7e..f0f6985 100644
--- a/src/transform/bound_array_accessors_transform.cc
+++ b/src/transform/bound_array_accessors_transform.cc
@@ -237,16 +237,17 @@
} 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));
diff --git a/src/transform/bound_array_accessors_transform_test.cc b/src/transform/bound_array_accessors_transform_test.cc
index b1b774d..b235a14 100644
--- a/src/transform/bound_array_accessors_transform_test.cc
+++ b/src/transform/bound_array_accessors_transform_test.cc
@@ -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 @@
// 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// }
// 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();
}