tint/resolver: Materialize RHS of non-phony assignments

Phony assignments do not materialize the RHS.
See: https://github.com/gpuweb/gpuweb/pull/2968

Bug: tint:1504
Change-Id: I29bb15e813d2b01876b5ec670c31b7aff3230986
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91963
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Ben Clayton 2022-05-31 20:40:59 +00:00 committed by Dawn LUCI CQ
parent 34f42aa453
commit 22bd004409
4 changed files with 95 additions and 37 deletions

View File

@ -147,29 +147,33 @@ TEST_F(ResolverAssignmentValidationTest, AssignToScalar_Fail) {
// var my_var : i32 = 2i;
// 1 = my_var;
auto* var = Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2_i));
WrapInFunction(var, Assign(Expr(Source{{12, 34}}, 1_i), "my_var"));
WrapInFunction(Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2_i)), //
Assign(Expr(Source{{12, 34}}, 1_i), "my_var"));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: cannot assign to value of type 'i32'");
}
TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypes_Pass) {
// var a : i32 = 2i;
// a = 2i
auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2_i));
WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2_i));
// var a : i32 = 1i;
// a = 2i;
// a = 3;
WrapInFunction(Var("a", ty.i32(), ast::StorageClass::kNone, Expr(1_i)), //
Assign("a", 2_i), //
Assign("a", 3_a));
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypesThroughAlias_Pass) {
// alias myint = i32;
// var a : myint = 2i;
// a = 2
auto* myint = Alias("myint", ty.i32());
auto* var = Var("a", ty.Of(myint), ast::StorageClass::kNone, Expr(2_i));
WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2_i));
// alias myint = u32;
// var a : myint = 1u;
// a = 2u;
// a = 3;
auto* myint = Alias("myint", ty.u32());
WrapInFunction(Var("a", ty.Of(myint), ast::StorageClass::kNone, Expr(1_u)), //
Assign("a", 2_u), //
Assign("a", 3_a));
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
@ -178,9 +182,9 @@ TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypesInferRHSLoad_Pass)
// var a : i32 = 2i;
// var b : i32 = 3i;
// a = b;
auto* var_a = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2_i));
auto* var_b = Var("b", ty.i32(), ast::StorageClass::kNone, Expr(3_i));
WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, "a", "b"));
WrapInFunction(Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2_i)), //
Var("b", ty.i32(), ast::StorageClass::kNone, Expr(3_i)), //
Assign("a", "b"));
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
@ -190,14 +194,26 @@ TEST_F(ResolverAssignmentValidationTest, AssignThroughPointer_Pass) {
// let b : ptr<function,i32> = &a;
// *b = 2i;
const auto func = ast::StorageClass::kFunction;
auto* var_a = Var("a", ty.i32(), func, Expr(2_i));
auto* var_b = Let("b", ty.pointer<i32>(func), AddressOf(Expr("a")));
WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, Deref("b"), 2_i));
WrapInFunction(Var("a", ty.i32(), func, Expr(2_i)), //
Let("b", ty.pointer<i32>(func), AddressOf(Expr("a"))), //
Assign(Deref("b"), 2_i));
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverAssignmentValidationTest, AssignToConstant_Fail) {
TEST_F(ResolverAssignmentValidationTest, AssignMaterializedThroughPointer_Pass) {
// var a : i32;
// let b : ptr<function,i32> = &a;
// *b = 2;
const auto func = ast::StorageClass::kFunction;
auto* var_a = Var("a", ty.i32(), func, Expr(2_i));
auto* var_b = Let("b", ty.pointer<i32>(func), AddressOf(Expr("a")));
WrapInFunction(var_a, var_b, Assign(Deref("b"), 2_a));
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverAssignmentValidationTest, AssignToLet_Fail) {
// {
// let a : i32 = 2i;
// a = 2i
@ -328,8 +344,12 @@ TEST_F(ResolverAssignmentValidationTest, AssignToPhony_Pass) {
// fn f() {
// _ = 1i;
// _ = 2u;
// _ = 3.0;
// _ = vec2<bool>();
// _ = 3.0f;
// _ = 4;
// _ = 5.0;
// _ = vec2(6);
// _ = vec3(7.0);
// _ = vec4<bool>();
// _ = tex;
// _ = smp;
// _ = &s;
@ -354,7 +374,11 @@ TEST_F(ResolverAssignmentValidationTest, AssignToPhony_Pass) {
WrapInFunction(Assign(Phony(), 1_i), //
Assign(Phony(), 2_u), //
Assign(Phony(), 3_f), //
Assign(Phony(), vec2<bool>()), //
Assign(Phony(), 4_a), //
Assign(Phony(), 5.0_a), //
Assign(Phony(), vec(nullptr, 2u, 6_a)), //
Assign(Phony(), vec(nullptr, 3u, 7.0_a)), //
Assign(Phony(), vec4<bool>()), //
Assign(Phony(), "tex"), //
Assign(Phony(), "smp"), //
Assign(Phony(), AddressOf("s")), //

View File

@ -81,6 +81,13 @@ enum class Method {
// let a : target_type = abstract_expr;
kLet,
// var a : target_type;
// a = abstract_expr;
kAssign,
// _ = abstract_expr;
kPhonyAssign,
// fn F(v : target_type) {}
// fn x() {
// F(abstract_expr);
@ -147,6 +154,10 @@ static std::ostream& operator<<(std::ostream& o, Method m) {
return o << "var";
case Method::kLet:
return o << "let";
case Method::kAssign:
return o << "assign";
case Method::kPhonyAssign:
return o << "phony-assign";
case Method::kFnArg:
return o << "fn-arg";
case Method::kBuiltinArg:
@ -251,6 +262,12 @@ TEST_P(MaterializeAbstractNumericToConcreteType, Test) {
case Method::kLet:
WrapInFunction(Decl(Let("a", target_ty(), abstract_expr)));
break;
case Method::kAssign:
WrapInFunction(Decl(Var("a", target_ty(), nullptr)), Assign("a", abstract_expr));
break;
case Method::kPhonyAssign:
WrapInFunction(Assign(Phony(), abstract_expr));
break;
case Method::kFnArg:
Func("F", {Param("P", target_ty())}, ty.void_(), {});
WrapInFunction(CallStmt(Call("F", abstract_expr)));
@ -364,20 +381,20 @@ TEST_P(MaterializeAbstractNumericToConcreteType, Test) {
/// Methods that support scalar materialization
constexpr Method kScalarMethods[] = {
Method::kLet, Method::kVar, Method::kFnArg, Method::kBuiltinArg,
Method::kLet, Method::kVar, Method::kAssign, Method::kFnArg, Method::kBuiltinArg,
Method::kReturn, Method::kArray, Method::kStruct, Method::kBinaryOp,
};
/// Methods that support vector materialization
constexpr Method kVectorMethods[] = {
Method::kLet, Method::kVar, Method::kFnArg, Method::kBuiltinArg,
Method::kLet, Method::kVar, Method::kAssign, Method::kFnArg, Method::kBuiltinArg,
Method::kReturn, Method::kArray, Method::kStruct, Method::kBinaryOp,
};
/// Methods that support matrix materialization
constexpr Method kMatrixMethods[] = {
Method::kLet, Method::kVar, Method::kFnArg, Method::kReturn,
Method::kArray, Method::kStruct, Method::kBinaryOp,
Method::kLet, Method::kVar, Method::kAssign, Method::kFnArg,
Method::kReturn, Method::kArray, Method::kStruct, Method::kBinaryOp,
};
/// Methods that support materialization for switch cases
@ -388,6 +405,12 @@ constexpr Method kSwitchMethods[] = {
Method::kSwitchCaseWithAbstractCase,
};
/// Methods that do not materialize
constexpr Method kNoMaterializeMethods[] = {
Method::kPhonyAssign,
// TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary ops:
// Method::kBuiltinArg, Method::kBinaryOp,
};
INSTANTIATE_TEST_SUITE_P(
MaterializeScalar,
MaterializeAbstractNumericToConcreteType,
@ -486,18 +509,18 @@ INSTANTIATE_TEST_SUITE_P(MaterializeWorkgroupSize,
Types<u32, AInt>(65535_a, 65535.0), //
})));
// TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary ops.
INSTANTIATE_TEST_SUITE_P(DISABLED_NoMaterialize,
INSTANTIATE_TEST_SUITE_P(NoMaterialize,
MaterializeAbstractNumericToConcreteType,
testing::Combine(testing::Values(Expectation::kNoMaterialize),
testing::Values(Method::kBuiltinArg, Method::kBinaryOp),
testing::ValuesIn(kNoMaterializeMethods),
testing::ValuesIn(std::vector<Data>{
Types<AInt, AInt>(), //
Types<AFloat, AFloat>(), //
Types<AIntV, AIntV>(), //
Types<AFloatV, AFloatV>(), //
Types<AFloatM, AFloatM>(), //
Types<AInt, AInt>(1_a, 1_a), //
Types<AIntV, AIntV>(1_a, 1_a), //
Types<AFloat, AFloat>(1.0_a, 1.0_a), //
Types<AFloatV, AFloatV>(1.0_a, 1.0_a), //
Types<AFloatM, AFloatM>(1.0_a, 1.0_a), //
})));
INSTANTIATE_TEST_SUITE_P(InvalidConversion,
MaterializeAbstractNumericToConcreteType,
testing::Combine(testing::Values(Expectation::kInvalidConversion),

View File

@ -2409,14 +2409,23 @@ sem::Statement* Resolver::AssignmentStatement(const ast::AssignmentStatement* st
return false;
}
auto* rhs = Expression(stmt->rhs);
const bool is_phony_assignment = stmt->lhs->Is<ast::PhonyExpression>();
const auto* rhs = Expression(stmt->rhs);
if (!rhs) {
return false;
}
if (!is_phony_assignment) {
rhs = Materialize(rhs, lhs->Type()->UnwrapRef());
if (!rhs) {
return false;
}
}
auto& behaviors = sem->Behaviors();
behaviors = rhs->Behaviors();
if (!stmt->lhs->Is<ast::PhonyExpression>()) {
if (!is_phony_assignment) {
behaviors.Add(lhs->Behaviors());
}

View File

@ -48,6 +48,7 @@
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/ast/vector.h"
#include "src/tint/ast/workgroup_attribute.h"
#include "src/tint/sem/abstract_numeric.h"
#include "src/tint/sem/array.h"
#include "src/tint/sem/atomic.h"
#include "src/tint/sem/call.h"
@ -2141,7 +2142,8 @@ bool Validator::Assignment(const ast::Statement* a, const sem::Type* rhs_ty) con
if (lhs->Is<ast::PhonyExpression>()) {
// https://www.w3.org/TR/WGSL/#phony-assignment-section
auto* ty = rhs_ty->UnwrapRef();
if (!ty->IsConstructible() && !ty->IsAnyOf<sem::Pointer, sem::Texture, sem::Sampler>()) {
if (!ty->IsConstructible() &&
!ty->IsAnyOf<sem::Pointer, sem::Texture, sem::Sampler, sem::AbstractNumeric>()) {
AddError("cannot assign '" + sem_.TypeNameOf(rhs_ty) +
"' to '_'. '_' can only be assigned a constructible, pointer, "
"texture or sampler type",