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

View File

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

View File

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

View File

@ -48,6 +48,7 @@
#include "src/tint/ast/variable_decl_statement.h" #include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/ast/vector.h" #include "src/tint/ast/vector.h"
#include "src/tint/ast/workgroup_attribute.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/array.h"
#include "src/tint/sem/atomic.h" #include "src/tint/sem/atomic.h"
#include "src/tint/sem/call.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>()) { if (lhs->Is<ast::PhonyExpression>()) {
// https://www.w3.org/TR/WGSL/#phony-assignment-section // https://www.w3.org/TR/WGSL/#phony-assignment-section
auto* ty = rhs_ty->UnwrapRef(); 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) + AddError("cannot assign '" + sem_.TypeNameOf(rhs_ty) +
"' to '_'. '_' can only be assigned a constructible, pointer, " "' to '_'. '_' can only be assigned a constructible, pointer, "
"texture or sampler type", "texture or sampler type",