Import Tint changes from Dawn
Changes:
- 394676bb7fb25598b36b9cb68b5eef16539d946c tint: Clean up CMakeLists.txt file list by Ben Clayton <bclayton@google.com>
- 932418ef4637efa35ba8dd15e895acca540b921b tint: Implement abstract-numeric materialization by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: 394676bb7fb25598b36b9cb68b5eef16539d946c
Change-Id: I0ac1104f8ec2dd501b17851673d1c2c92ef835b4
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/91460
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index fbf443c..057433a 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -410,6 +410,7 @@
"sem/if_statement.h",
"sem/info.h",
"sem/loop_statement.h",
+ "sem/materialize.h",
"sem/matrix.h",
"sem/module.h",
"sem/multisampled_texture.h",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 85ec3db..1eef3a1 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -62,18 +62,17 @@
../../include/tint/tint.h
ast/access.cc
ast/access.h
- ast/attribute.cc
- ast/attribute.h
ast/alias.cc
ast/alias.h
- ast/index_accessor_expression.cc
- ast/index_accessor_expression.h
ast/array.cc
ast/array.h
ast/assignment_statement.cc
ast/assignment_statement.h
+ ast/ast_type.cc # TODO(bclayton) - rename to type.cc
ast/atomic.cc
ast/atomic.h
+ ast/attribute.cc
+ ast/attribute.h
ast/binary_expression.cc
ast/binary_expression.h
ast/binding_attribute.cc
@@ -104,10 +103,10 @@
ast/continue_statement.h
ast/depth_multisampled_texture.cc
ast/depth_multisampled_texture.h
- ast/disable_validation_attribute.cc
- ast/disable_validation_attribute.h
ast/depth_texture.cc
ast/depth_texture.h
+ ast/disable_validation_attribute.cc
+ ast/disable_validation_attribute.h
ast/discard_statement.cc
ast/discard_statement.h
ast/enable.cc
@@ -142,6 +141,8 @@
ast/if_statement.h
ast/increment_decrement_statement.cc
ast/increment_decrement_statement.h
+ ast/index_accessor_expression.cc
+ ast/index_accessor_expression.h
ast/int_literal_expression.cc
ast/int_literal_expression.h
ast/internal_attribute.cc
@@ -203,14 +204,11 @@
ast/texture.cc
ast/texture.h
ast/traverse_expressions.h
- ast/type_name.cc
- ast/type_name.h
- ast/ast_type.cc # TODO(bclayton) - rename to type.cc
- ast/type.h
ast/type_decl.cc
ast/type_decl.h
ast/type_name.cc
ast/type_name.h
+ ast/type.h
ast/u32.cc
ast/u32.h
ast/unary_op_expression.cc
@@ -233,7 +231,6 @@
clone_context.h
demangler.cc
demangler.h
- number.h
inspector/entry_point.cc
inspector/entry_point.h
inspector/inspector.cc
@@ -242,6 +239,7 @@
inspector/resource_binding.h
inspector/scalar.cc
inspector/scalar.h
+ number.h
program_builder.cc
program_builder.h
program_id.cc
@@ -257,8 +255,8 @@
resolver/intrinsic_table.cc
resolver/intrinsic_table.h
resolver/intrinsic_table.inl
- resolver/resolver.cc
resolver/resolver_constants.cc
+ resolver/resolver.cc
resolver/resolver.h
resolver/sem_helper.cc
resolver/sem_helper.h
@@ -282,6 +280,8 @@
sem/binding_point.h
sem/block_statement.cc
sem/block_statement.h
+ sem/bool.cc
+ sem/bool.h
sem/builtin_type.cc
sem/builtin_type.h
sem/builtin.cc
@@ -294,24 +294,74 @@
sem/constant.h
sem/depth_multisampled_texture.cc
sem/depth_multisampled_texture.h
+ sem/depth_texture.cc
+ sem/depth_texture.h
sem/expression.cc
sem/expression.h
+ sem/external_texture.cc
+ sem/external_texture.h
+ sem/f16.cc
+ sem/f16.h
+ sem/f32.cc
+ sem/f32.h
+ sem/for_loop_statement.cc
+ sem/for_loop_statement.h
sem/function.cc
+ sem/i32.cc
+ sem/i32.h
+ sem/if_statement.cc
+ sem/if_statement.h
sem/info.cc
sem/info.h
+ sem/loop_statement.cc
+ sem/loop_statement.h
+ sem/materialize.cc
+ sem/materialize.h
+ sem/matrix.cc
+ sem/matrix.h
sem/member_accessor_expression.cc
+ sem/module.cc
+ sem/module.h
+ sem/multisampled_texture.cc
+ sem/multisampled_texture.h
+ sem/node.cc
+ sem/node.h
sem/parameter_usage.cc
sem/parameter_usage.h
sem/pipeline_stage_set.h
- sem/node.cc
- sem/node.h
- sem/module.cc
- sem/module.h
+ sem/pointer.cc
+ sem/pointer.h
+ sem/reference.cc
+ sem/reference.h
+ sem/sampled_texture.cc
+ sem/sampled_texture.h
sem/sampler_texture_pair.h
+ sem/sampler.cc
+ sem/sampler.h
sem/statement.cc
+ sem/storage_texture.cc
+ sem/storage_texture.h
sem/struct.cc
+ sem/switch_statement.cc
+ sem/switch_statement.h
+ sem/texture.cc
+ sem/texture.h
+ sem/type_constructor.cc
+ sem/type_constructor.h
+ sem/type_conversion.cc
+ sem/type_conversion.h
+ sem/type_manager.cc
+ sem/type_manager.h
sem/type_mappings.h
+ sem/type.cc
+ sem/type.h
+ sem/u32.cc
+ sem/u32.h
sem/variable.cc
+ sem/vector.cc
+ sem/vector.h
+ sem/void.cc
+ sem/void.h
symbol_table.cc
symbol_table.h
symbol.cc
@@ -331,10 +381,10 @@
transform/builtin_polyfill.h
transform/calculate_array_length.cc
transform/calculate_array_length.h
- transform/combine_samplers.cc
- transform/combine_samplers.h
transform/canonicalize_entry_point_io.cc
transform/canonicalize_entry_point_io.h
+ transform/combine_samplers.cc
+ transform/combine_samplers.h
transform/decompose_memory_access.cc
transform/decompose_memory_access.h
transform/decompose_strided_array.cc
@@ -343,18 +393,18 @@
transform/decompose_strided_matrix.h
transform/disable_uniformity_analysis.cc
transform/disable_uniformity_analysis.h
+ transform/expand_compound_assignment.cc
+ transform/expand_compound_assignment.h
transform/first_index_offset.cc
transform/first_index_offset.h
transform/fold_constants.cc
transform/fold_constants.h
transform/fold_trivial_single_use_lets.cc
transform/fold_trivial_single_use_lets.h
- transform/localize_struct_array_assignment.cc
- transform/localize_struct_array_assignment.h
transform/for_loop_to_loop.cc
transform/for_loop_to_loop.h
- transform/expand_compound_assignment.cc
- transform/expand_compound_assignment.h
+ transform/localize_struct_array_assignment.cc
+ transform/localize_struct_array_assignment.h
transform/loop_to_for_loop.cc
transform/loop_to_for_loop.h
transform/manager.cc
@@ -369,10 +419,10 @@
transform/promote_initializers_to_const_var.h
transform/promote_side_effects_to_decl.cc
transform/promote_side_effects_to_decl.h
- transform/remove_phonies.cc
- transform/remove_phonies.h
transform/remove_continue_in_switch.cc
transform/remove_continue_in_switch.h
+ transform/remove_phonies.cc
+ transform/remove_phonies.h
transform/remove_unreachable_statements.cc
transform/remove_unreachable_statements.h
transform/renamer.cc
@@ -389,72 +439,20 @@
transform/unshadow.h
transform/unwind_discard_functions.cc
transform/unwind_discard_functions.h
- transform/vectorize_scalar_matrix_constructors.cc
- transform/vectorize_scalar_matrix_constructors.h
+ transform/utils/get_insertion_point.cc
+ transform/utils/get_insertion_point.h
+ transform/utils/hoist_to_decl_before.cc
+ transform/utils/hoist_to_decl_before.h
transform/var_for_dynamic_index.cc
transform/var_for_dynamic_index.h
+ transform/vectorize_scalar_matrix_constructors.cc
+ transform/vectorize_scalar_matrix_constructors.h
transform/vertex_pulling.cc
transform/vertex_pulling.h
transform/wrap_arrays_in_structs.cc
transform/wrap_arrays_in_structs.h
transform/zero_init_workgroup_memory.cc
transform/zero_init_workgroup_memory.h
- transform/utils/get_insertion_point.cc
- transform/utils/get_insertion_point.h
- transform/utils/hoist_to_decl_before.cc
- transform/utils/hoist_to_decl_before.h
- sem/bool.cc
- sem/bool.h
- sem/depth_texture.cc
- sem/depth_texture.h
- sem/external_texture.cc
- sem/external_texture.h
- sem/f16.cc
- sem/f16.h
- sem/f32.cc
- sem/f32.h
- sem/for_loop_statement.cc
- sem/for_loop_statement.h
- sem/i32.cc
- sem/i32.h
- sem/if_statement.cc
- sem/if_statement.h
- sem/loop_statement.cc
- sem/loop_statement.h
- sem/materialize.cc
- sem/materialize.h
- sem/matrix.cc
- sem/matrix.h
- sem/multisampled_texture.cc
- sem/multisampled_texture.h
- sem/pointer.cc
- sem/pointer.h
- sem/reference.cc
- sem/reference.h
- sem/sampled_texture.cc
- sem/sampled_texture.h
- sem/sampler.cc
- sem/sampler.h
- sem/storage_texture.cc
- sem/storage_texture.h
- sem/switch_statement.cc
- sem/switch_statement.h
- sem/texture.cc
- sem/texture.h
- sem/type_constructor.cc
- sem/type_constructor.h
- sem/type_conversion.cc
- sem/type_conversion.h
- sem/type.cc
- sem/type.h
- sem/type_manager.cc
- sem/type_manager.h
- sem/u32.cc
- sem/u32.h
- sem/vector.cc
- sem/vector.h
- sem/void.cc
- sem/void.h
utils/bitcast.h
utils/block_allocator.h
utils/crc32.h
@@ -774,6 +772,7 @@
resolver/intrinsic_table_test.cc
resolver/is_host_shareable_test.cc
resolver/is_storeable_test.cc
+ resolver/materialize_test.cc
resolver/pipeline_overridable_constant_test.cc
resolver/ptr_ref_test.cc
resolver/ptr_ref_validation_test.cc
diff --git a/src/tint/resolver/intrinsic_table.h b/src/tint/resolver/intrinsic_table.h
index 5a8985f..312f952 100644
--- a/src/tint/resolver/intrinsic_table.h
+++ b/src/tint/resolver/intrinsic_table.h
@@ -43,17 +43,17 @@
struct UnaryOperator {
/// The result type of the unary operator
const sem::Type* result;
- /// The type of the arg of the unary operator
- const sem::Type* arg;
+ /// The type of the parameter of the unary operator
+ const sem::Type* parameter;
};
/// BinaryOperator describes a resolved binary operator
struct BinaryOperator {
/// The result type of the binary operator
const sem::Type* result;
- /// The type of LHS of the binary operator
+ /// The type of LHS parameter of the binary operator
const sem::Type* lhs;
- /// The type of RHS of the binary operator
+ /// The type of RHS parameter of the binary operator
const sem::Type* rhs;
};
diff --git a/src/tint/resolver/materialize_test.cc b/src/tint/resolver/materialize_test.cc
new file mode 100644
index 0000000..e2e1a02
--- /dev/null
+++ b/src/tint/resolver/materialize_test.cc
@@ -0,0 +1,391 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/sem/materialize.h"
+
+#include "src/tint/resolver/resolver.h"
+#include "src/tint/resolver/resolver_test_helper.h"
+#include "src/tint/sem/test_helper.h"
+
+#include "gmock/gmock.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+using AFloatV = builder::vec<3, AFloat>;
+using AFloatM = builder::mat<3, 2, AFloat>;
+using AIntV = builder::vec<3, AInt>;
+using f32V = builder::vec<3, f32>;
+using f16V = builder::vec<3, f16>;
+using i32V = builder::vec<3, i32>;
+using u32V = builder::vec<3, u32>;
+using f32M = builder::mat<3, 2, f32>;
+
+////////////////////////////////////////////////////////////////////////////////
+// MaterializeTests
+////////////////////////////////////////////////////////////////////////////////
+namespace MaterializeTests {
+
+// How should the materialization occur?
+enum class Method {
+ // var a : T = literal;
+ kVar,
+
+ // let a : T = literal;
+ kLet,
+
+ // fn F(v : T) {}
+ // fn x() {
+ // F(literal);
+ // }
+ kFnArg,
+
+ // min(target_expr, literal);
+ kBuiltinArg,
+
+ // fn F() : T {
+ // return literal;
+ // }
+ kReturn,
+
+ // array<T, 1>(literal);
+ kArray,
+
+ // struct S {
+ // v : T
+ // };
+ // fn x() {
+ // _ = S(literal)
+ // }
+ kStruct,
+
+ // target_expr + literal
+ kBinaryOp,
+
+ // switch (literal) {
+ // case target_expr: {}
+ // default: {}
+ // }
+ kSwitchCond,
+
+ // switch (target_expr) {
+ // case literal: {}
+ // default: {}
+ // }
+ kSwitchCase,
+
+ // switch (literal) {
+ // case 123: {}
+ // case target_expr: {}
+ // default: {}
+ // }
+ kSwitchCondWithAbstractCase,
+
+ // switch (target_expr) {
+ // case 123: {}
+ // case literal: {}
+ // default: {}
+ // }
+ kSwitchCaseWithAbstractCase,
+};
+
+static std::ostream& operator<<(std::ostream& o, Method m) {
+ switch (m) {
+ case Method::kVar:
+ return o << "var";
+ case Method::kLet:
+ return o << "let";
+ case Method::kFnArg:
+ return o << "fn-arg";
+ case Method::kBuiltinArg:
+ return o << "builtin-arg";
+ case Method::kReturn:
+ return o << "return";
+ case Method::kArray:
+ return o << "array";
+ case Method::kStruct:
+ return o << "struct";
+ case Method::kBinaryOp:
+ return o << "binary-op";
+ case Method::kSwitchCond:
+ return o << "switch-cond";
+ case Method::kSwitchCase:
+ return o << "switch-case";
+ case Method::kSwitchCondWithAbstractCase:
+ return o << "switch-cond-with-abstract";
+ case Method::kSwitchCaseWithAbstractCase:
+ return o << "switch-case-with-abstract";
+ }
+ return o << "<unknown>";
+}
+
+struct Data {
+ std::string target_type_name;
+ builder::ast_type_func_ptr target_ast_ty;
+ builder::sem_type_func_ptr target_sem_ty;
+ builder::ast_expr_func_ptr target_expr;
+ std::string literal_type_name;
+ builder::ast_expr_func_ptr literal_value;
+ std::variant<AInt, AFloat> materialized_value;
+};
+
+template <typename TARGET_TYPE, typename LITERAL_TYPE, typename MATERIALIZED_TYPE = AInt>
+Data Types(MATERIALIZED_TYPE materialized_value = 0_a) {
+ return {
+ builder::DataType<TARGET_TYPE>::Name(), //
+ builder::DataType<TARGET_TYPE>::AST, //
+ builder::DataType<TARGET_TYPE>::Sem, //
+ builder::DataType<TARGET_TYPE>::Expr, //
+ builder::DataType<LITERAL_TYPE>::Name(), //
+ builder::DataType<LITERAL_TYPE>::Expr, //
+ materialized_value,
+ };
+}
+
+static std::ostream& operator<<(std::ostream& o, const Data& c) {
+ return o << "[" << c.target_type_name << " <- " << c.literal_type_name << "]";
+}
+
+enum class Expectation {
+ kMaterialize,
+ kNoMaterialize,
+ kInvalidCast,
+};
+
+static std::ostream& operator<<(std::ostream& o, Expectation m) {
+ switch (m) {
+ case Expectation::kMaterialize:
+ return o << "pass";
+ case Expectation::kNoMaterialize:
+ return o << "no-materialize";
+ case Expectation::kInvalidCast:
+ return o << "invalid-cast";
+ }
+ return o << "<unknown>";
+}
+
+using MaterializeAbstractNumeric =
+ resolver::ResolverTestWithParam<std::tuple<Expectation, Method, Data>>;
+
+TEST_P(MaterializeAbstractNumeric, Test) {
+ // Once F16 is properly supported, we'll need to enable this:
+ // Enable(ast::Extension::kF16);
+
+ const auto& param = GetParam();
+ const auto& expectation = std::get<0>(param);
+ const auto& method = std::get<1>(param);
+ const auto& data = std::get<2>(param);
+
+ auto target_ty = [&] { return data.target_ast_ty(*this); };
+ auto target_expr = [&] { return data.target_expr(*this, 42); };
+ auto* literal = data.literal_value(*this, 1);
+ switch (method) {
+ case Method::kVar:
+ WrapInFunction(Decl(Var("a", target_ty(), literal)));
+ break;
+ case Method::kLet:
+ WrapInFunction(Decl(Let("a", target_ty(), literal)));
+ break;
+ case Method::kFnArg:
+ Func("F", {Param("P", target_ty())}, ty.void_(), {});
+ WrapInFunction(CallStmt(Call("F", literal)));
+ break;
+ case Method::kBuiltinArg:
+ WrapInFunction(CallStmt(Call("min", target_expr(), literal)));
+ break;
+ case Method::kReturn:
+ Func("F", {}, target_ty(), {Return(literal)});
+ break;
+ case Method::kArray:
+ WrapInFunction(Construct(ty.array(target_ty(), 1_i), literal));
+ break;
+ case Method::kStruct:
+ Structure("S", {Member("v", target_ty())});
+ WrapInFunction(Construct(ty.type_name("S"), literal));
+ break;
+ case Method::kBinaryOp:
+ WrapInFunction(Add(target_expr(), literal));
+ break;
+ case Method::kSwitchCond:
+ WrapInFunction(Switch(literal, //
+ Case(target_expr()->As<ast::IntLiteralExpression>()), //
+ DefaultCase()));
+ break;
+ case Method::kSwitchCase:
+ WrapInFunction(Switch(target_expr(), //
+ Case(literal->As<ast::IntLiteralExpression>()), //
+ DefaultCase()));
+ break;
+ case Method::kSwitchCondWithAbstractCase:
+ WrapInFunction(Switch(literal, //
+ Case(Expr(123_a)), //
+ Case(target_expr()->As<ast::IntLiteralExpression>()), //
+ DefaultCase()));
+ break;
+ case Method::kSwitchCaseWithAbstractCase:
+ WrapInFunction(Switch(target_expr(), //
+ Case(Expr(123_a)), //
+ Case(literal->As<ast::IntLiteralExpression>()), //
+ DefaultCase()));
+ break;
+ }
+
+ auto check_types_and_values = [&](const sem::Expression* expr) {
+ auto* target_sem_ty = data.target_sem_ty(*this);
+
+ EXPECT_TYPE(expr->Type(), target_sem_ty);
+ EXPECT_TYPE(expr->ConstantValue().Type(), target_sem_ty);
+
+ uint32_t num_elems = 0;
+ const sem::Type* target_sem_el_ty = sem::Type::ElementOf(target_sem_ty, &num_elems);
+ EXPECT_TYPE(expr->ConstantValue().ElementType(), target_sem_el_ty);
+ std::visit(
+ [&](auto&& v) {
+ EXPECT_EQ(expr->ConstantValue().Elements(), sem::Constant::Scalars(num_elems, {v}));
+ },
+ data.materialized_value);
+ };
+
+ switch (expectation) {
+ case Expectation::kMaterialize: {
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ auto* materialize = Sem().Get<sem::Materialize>(literal);
+ ASSERT_NE(materialize, nullptr);
+ check_types_and_values(materialize);
+ break;
+ }
+ case Expectation::kNoMaterialize: {
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(literal);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->Is<sem::Materialize>());
+ check_types_and_values(sem);
+ break;
+ }
+ case Expectation::kInvalidCast: {
+ ASSERT_FALSE(r()->Resolve());
+ std::string expect;
+ switch (method) {
+ case Method::kBuiltinArg:
+ expect = "error: no matching call to min(" + data.target_type_name + ", " +
+ data.literal_type_name + ")";
+ break;
+ case Method::kBinaryOp:
+ expect = "error: no matching overload for operator + (" +
+ data.target_type_name + ", " + data.literal_type_name + ")";
+ break;
+ default:
+ expect = "error: cannot convert value of type '" + data.literal_type_name +
+ "' to type '" + data.target_type_name + "'";
+ break;
+ }
+ EXPECT_THAT(r()->error(), testing::StartsWith(expect));
+ break;
+ }
+ }
+}
+
+// TODO(crbug.com/tint/1504): Test for abstract-numeric values not fitting in materialized types.
+
+INSTANTIATE_TEST_SUITE_P(MaterializeScalar,
+ MaterializeAbstractNumeric, //
+ testing::Combine(testing::Values(Expectation::kMaterialize), //
+ testing::Values(Method::kLet, //
+ Method::kVar, //
+ Method::kFnArg, //
+ Method::kBuiltinArg, //
+ Method::kReturn, //
+ Method::kArray, //
+ Method::kStruct, //
+ Method::kBinaryOp), //
+ testing::Values(Types<i32, AInt>(1_a), //
+ Types<u32, AInt>(1_a), //
+ Types<f32, AFloat>(1.0_a) //
+ /* Types<f16, AFloat>(1.0_a), */ //
+ /* Types<f16, AFloat>(1.0_a), */)));
+
+INSTANTIATE_TEST_SUITE_P(MaterializeVector,
+ MaterializeAbstractNumeric, //
+ testing::Combine(testing::Values(Expectation::kMaterialize), //
+ testing::Values(Method::kLet, //
+ Method::kVar, //
+ Method::kFnArg, //
+ Method::kBuiltinArg, //
+ Method::kReturn, //
+ Method::kArray, //
+ Method::kStruct, //
+ Method::kBinaryOp), //
+ testing::Values(Types<i32V, AIntV>(1_a), //
+ Types<u32V, AIntV>(1_a), //
+ Types<f32V, AFloatV>(1.0_a) //
+ /* Types<f16V, AFloatV>(1.0_a), */ //
+ /* Types<f16V, AFloatV>(1.0_a), */)));
+
+INSTANTIATE_TEST_SUITE_P(MaterializeMatrix,
+ MaterializeAbstractNumeric, //
+ testing::Combine(testing::Values(Expectation::kMaterialize), //
+ testing::Values(Method::kLet, //
+ Method::kVar, //
+ Method::kFnArg, //
+ Method::kReturn, //
+ Method::kArray, //
+ Method::kStruct, //
+ Method::kBinaryOp), //
+ testing::Values(Types<f32M, AFloatM>(1.0_a) //
+ /* Types<f16V, AFloatM>(1.0_a), */ //
+ )));
+
+INSTANTIATE_TEST_SUITE_P(MaterializeSwitch,
+ MaterializeAbstractNumeric, //
+ testing::Combine(testing::Values(Expectation::kMaterialize), //
+ testing::Values(Method::kSwitchCond, //
+ Method::kSwitchCase, //
+ Method::kSwitchCondWithAbstractCase, //
+ Method::kSwitchCaseWithAbstractCase), //
+ testing::Values(Types<i32, AInt>(1_a), //
+ Types<u32, AInt>(1_a))));
+
+// TODO(crbug.com/tint/1504): Enable once we have abstract overloads of builtins / binary ops.
+INSTANTIATE_TEST_SUITE_P(DISABLED_NoMaterialize,
+ MaterializeAbstractNumeric, //
+ testing::Combine(testing::Values(Expectation::kNoMaterialize), //
+ testing::Values(Method::kBuiltinArg, //
+ Method::kBinaryOp), //
+ testing::Values(Types<AInt, AInt>(1_a), //
+ Types<AFloat, AFloat>(1.0_a), //
+ Types<AIntV, AIntV>(1_a), //
+ Types<AFloatV, AFloatV>(1.0_a), //
+ Types<AFloatM, AFloatM>(1.0_a))));
+INSTANTIATE_TEST_SUITE_P(InvalidCast,
+ MaterializeAbstractNumeric, //
+ testing::Combine(testing::Values(Expectation::kInvalidCast), //
+ testing::Values(Method::kLet, //
+ Method::kVar, //
+ Method::kFnArg, //
+ Method::kBuiltinArg, //
+ Method::kReturn, //
+ Method::kArray, //
+ Method::kStruct, //
+ Method::kBinaryOp), //
+ testing::Values(Types<i32, AFloat>(), //
+ Types<u32, AFloat>(), //
+ Types<i32V, AFloatV>(), //
+ Types<u32V, AFloatV>())));
+
+} // namespace MaterializeTests
+
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 270acf1..9d3d7a4 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -62,6 +62,7 @@
#include "src/tint/sem/function.h"
#include "src/tint/sem/if_statement.h"
#include "src/tint/sem/loop_statement.h"
+#include "src/tint/sem/materialize.h"
#include "src/tint/sem/member_accessor_expression.h"
#include "src/tint/sem/module.h"
#include "src/tint/sem/multisampled_texture.h"
@@ -318,7 +319,11 @@
// Does the variable have a constructor?
if (var->constructor) {
- rhs = Expression(var->constructor);
+ auto* ctor = Expression(var->constructor);
+ if (!ctor) {
+ return nullptr;
+ }
+ rhs = Materialize(ctor, storage_ty);
if (!rhs) {
return nullptr;
}
@@ -1100,6 +1105,83 @@
return nullptr;
}
+const sem::Expression* Resolver::Materialize(const sem::Expression* expr,
+ const sem::Type* target_type /* = nullptr */) {
+ // Helper for actually creating the the materialize node, performing the constant cast, updating
+ // the ast -> sem binding, and performing validation.
+ auto materialize = [&](const sem::Type* target_ty) -> sem::Materialize* {
+ auto expr_val = EvaluateConstantValue(expr->Declaration(), expr->Type());
+ if (!expr_val.IsValid()) {
+ TINT_ICE(Resolver, builder_->Diagnostics())
+ << expr->Declaration()->source
+ << " EvaluateConstantValue() returned invalid value for materialized "
+ "value of type: "
+ << (expr->Type() ? expr->Type()->FriendlyName(builder_->Symbols()) : "<null>");
+ return nullptr;
+ }
+ auto materialized_val = ConstantCast(expr_val, target_ty);
+ auto* m = builder_->create<sem::Materialize>(expr, current_statement_, materialized_val);
+ m->Behaviors() = expr->Behaviors();
+ builder_->Sem().Replace(expr->Declaration(), m);
+ return validator_.Materialize(m) ? m : nullptr;
+ };
+
+ // Helpers for constructing semantic types
+ auto i32 = [&] { return builder_->create<sem::I32>(); };
+ auto f32 = [&] { return builder_->create<sem::F32>(); };
+ auto i32v = [&](uint32_t width) { return builder_->create<sem::Vector>(i32(), width); };
+ auto f32v = [&](uint32_t width) { return builder_->create<sem::Vector>(f32(), width); };
+ auto f32m = [&](uint32_t columns, uint32_t rows) {
+ return builder_->create<sem::Matrix>(f32v(columns), rows);
+ };
+
+ // Type dispatch based on the expression type
+ return Switch<sem::Expression*>(
+ expr->Type(), //
+ [&](const sem::AbstractInt*) { return materialize(target_type ? target_type : i32()); },
+ [&](const sem::AbstractFloat*) { return materialize(target_type ? target_type : f32()); },
+ [&](const sem::Vector* v) {
+ return Switch(
+ v->type(), //
+ [&](const sem::AbstractInt*) {
+ return materialize(target_type ? target_type : i32v(v->Width()));
+ },
+ [&](const sem::AbstractFloat*) {
+ return materialize(target_type ? target_type : f32v(v->Width()));
+ },
+ [&](Default) { return expr; });
+ },
+ [&](const sem::Matrix* m) {
+ return Switch(
+ m->type(), //
+ [&](const sem::AbstractFloat*) {
+ return materialize(target_type ? target_type : f32m(m->columns(), m->rows()));
+ },
+ [&](Default) { return expr; });
+ },
+ [&](Default) { return expr; });
+}
+
+bool Resolver::MaterializeArguments(std::vector<const sem::Expression*>& args,
+ const sem::CallTarget* target) {
+ for (size_t i = 0, n = std::min(args.size(), target->Parameters().size()); i < n; i++) {
+ const auto* param_ty = target->Parameters()[i]->Type();
+ if (ShouldMaterializeArgument(param_ty)) {
+ auto* materialized = Materialize(args[i], param_ty);
+ if (!materialized) {
+ return false;
+ }
+ args[i] = materialized;
+ }
+ }
+ return true;
+}
+
+bool Resolver::ShouldMaterializeArgument(const sem::Type* parameter_ty) const {
+ const auto* param_el_ty = sem::Type::ElementOf(parameter_ty);
+ return param_el_ty && !param_el_ty->Is<sem::AbstractNumeric>();
+}
+
sem::Expression* Resolver::IndexAccessor(const ast::IndexAccessorExpression* expr) {
auto* idx = sem_.Get(expr->index);
auto* obj = sem_.Get(expr->object);
@@ -1192,6 +1274,9 @@
if (!call_target) {
return nullptr;
}
+ if (!MaterializeArguments(args, call_target)) {
+ return nullptr;
+ }
auto value = EvaluateConstantValue(expr, call_target->ReturnType());
return builder_->create<sem::Call>(expr, call_target, std::move(args), current_statement_,
value, has_side_effects);
@@ -1227,6 +1312,9 @@
}
return builder_->create<sem::TypeConstructor>(arr, std::move(params));
});
+ if (!MaterializeArguments(args, call_target)) {
+ return nullptr;
+ }
auto value = EvaluateConstantValue(expr, call_target->ReturnType());
return builder_->create<sem::Call>(expr, call_target, std::move(args),
current_statement_, value, has_side_effects);
@@ -1246,6 +1334,9 @@
}
return builder_->create<sem::TypeConstructor>(str, std::move(params));
});
+ if (!MaterializeArguments(args, call_target)) {
+ return nullptr;
+ }
auto value = EvaluateConstantValue(expr, call_target->ReturnType());
return builder_->create<sem::Call>(expr, call_target, std::move(args),
current_statement_, value, has_side_effects);
@@ -1368,6 +1459,10 @@
}
}
+ if (!MaterializeArguments(args, builtin)) {
+ return nullptr;
+ }
+
if (builtin->IsDeprecated()) {
AddWarning("use of deprecated builtin", expr->source);
}
@@ -1425,6 +1520,10 @@
auto sym = expr->target.name->symbol;
auto name = builder_->Symbols().NameFor(sym);
+ if (!MaterializeArguments(args, target)) {
+ return nullptr;
+ }
+
// TODO(crbug.com/tint/1420): For now, assume all function calls have side
// effects.
bool has_side_effects = true;
@@ -1715,6 +1814,18 @@
if (!op.result) {
return nullptr;
}
+ if (ShouldMaterializeArgument(op.lhs)) {
+ lhs = Materialize(lhs, op.lhs);
+ if (!lhs) {
+ return nullptr;
+ }
+ }
+ if (ShouldMaterializeArgument(op.rhs)) {
+ rhs = Materialize(rhs, op.rhs);
+ if (!rhs) {
+ return nullptr;
+ }
+ }
auto val = EvaluateConstantValue(expr, op.result);
bool has_side_effects = lhs->HasSideEffects() || rhs->HasSideEffects();
@@ -1775,10 +1886,17 @@
break;
default: {
- ty = intrinsic_table_->Lookup(unary->op, expr_ty, unary->source).result;
- if (!ty) {
+ auto op = intrinsic_table_->Lookup(unary->op, expr_ty, unary->source);
+ if (!op.result) {
return nullptr;
}
+ if (ShouldMaterializeArgument(op.parameter)) {
+ expr = Materialize(expr, op.parameter);
+ if (!expr) {
+ return nullptr;
+ }
+ }
+ ty = op.result;
break;
}
}
@@ -2118,7 +2236,11 @@
const sem::Type* value_ty = nullptr;
if (auto* value = stmt->value) {
- auto* expr = Expression(value);
+ const auto* expr = Expression(value);
+ if (!expr) {
+ return false;
+ }
+ expr = Materialize(expr, current_function_->ReturnType());
if (!expr) {
return false;
}
@@ -2141,22 +2263,54 @@
return StatementScope(stmt, sem, [&] {
auto& behaviors = sem->Behaviors();
- auto* cond = Expression(stmt->condition);
+ const auto* cond = Expression(stmt->condition);
if (!cond) {
return false;
}
behaviors = cond->Behaviors() - sem::Behavior::kNext;
+ auto* cond_ty = cond->Type()->UnwrapRef();
+
+ utils::UniqueVector<const sem::Type*> types;
+ types.add(cond_ty);
+
+ std::vector<sem::CaseStatement*> cases;
+ cases.reserve(stmt->body.size());
for (auto* case_stmt : stmt->body) {
Mark(case_stmt);
auto* c = CaseStatement(case_stmt);
if (!c) {
return false;
}
+ for (auto* expr : c->Selectors()) {
+ types.add(expr->Type()->UnwrapRef());
+ }
+ cases.emplace_back(c);
behaviors.Add(c->Behaviors());
sem->Cases().emplace_back(c);
}
+ // Determine the common type across all selectors and the switch expression
+ // This must materialize to an integer scalar (non-abstract).
+ auto* common_ty = sem::Type::Common(types.data(), types.size());
+ if (!common_ty || !common_ty->is_integer_scalar()) {
+ // No common type found or the common type was abstract.
+ // Pick i32 and let validation deal with any mismatches.
+ common_ty = builder_->create<sem::I32>();
+ }
+ cond = Materialize(cond, common_ty);
+ if (!cond) {
+ return false;
+ }
+ for (auto* c : cases) {
+ for (auto*& sel : c->Selectors()) { // Note: pointer reference
+ sel = Materialize(sel, common_ty);
+ if (!sel) {
+ return false;
+ }
+ }
+ }
+
if (behaviors.Contains(sem::Behavior::kBreak)) {
behaviors.Add(sem::Behavior::kNext);
}
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 1725072..b03bb32 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -198,6 +198,30 @@
sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
+ /// If `expr` is not of an abstract-numeric type, then Materialize() will just return `expr`.
+ /// If `expr` is of an abstract-numeric type:
+ /// * Materialize will create and return a sem::Materialize node wrapping `expr`.
+ /// * The AST -> Sem binding will be updated to point to the new sem::Materialize node.
+ /// * The sem::Materialize node will have a new concrete type, which will be `target_type` if
+ /// not nullptr, otherwise:
+ /// * a type with the element type of `i32` (e.g. `i32`, `vec2<i32>`) if `expr` has a
+ /// element type of abstract-integer...
+ /// * ... or a type with the element type of `f32` (e.g. `f32`, vec3<f32>`, `mat2x3<f32>`)
+ /// if `expr` has a element type of abstract-float.
+ /// * The sem::Materialize constant value will be the value of `expr` value-converted to the
+ /// materialized type.
+ const sem::Expression* Materialize(const sem::Expression* expr,
+ const sem::Type* target_type = nullptr);
+
+ /// Materializes all the arguments in `args` to the parameter types of `target`.
+ /// @returns true on success, false on failure.
+ bool MaterializeArguments(std::vector<const sem::Expression*>& args,
+ const sem::CallTarget* target);
+
+ /// @returns true if an argument of an abstract numeric type, passed to a parameter of type
+ /// `parameter_ty` should be materialized.
+ bool ShouldMaterializeArgument(const sem::Type* parameter_ty) const;
+
// Statement resolving methods
// Each return true on success, false on failure.
sem::Statement* AssignmentStatement(const ast::AssignmentStatement*);
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 9f698e6..fdd74e9 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -57,6 +57,7 @@
#include "src/tint/sem/function.h"
#include "src/tint/sem/if_statement.h"
#include "src/tint/sem/loop_statement.h"
+#include "src/tint/sem/materialize.h"
#include "src/tint/sem/member_accessor_expression.h"
#include "src/tint/sem/multisampled_texture.h"
#include "src/tint/sem/pointer.h"
@@ -276,6 +277,19 @@
return true;
}
+bool Validator::Materialize(const sem::Materialize* m) const {
+ auto* from = m->Expr()->Type();
+ auto* to = m->Type();
+
+ if (sem::Type::ConversionRank(from, to) == sem::Type::kNoConversion) {
+ AddError("cannot convert value of type '" + sem_.TypeNameOf(from) + "' to type '" +
+ sem_.TypeNameOf(to) + "'",
+ m->Expr()->Declaration()->source);
+ return false;
+ }
+ return true;
+}
+
bool Validator::VariableConstructorOrCast(const ast::Variable* var,
ast::StorageClass storage_class,
const sem::Type* storage_ty,
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index a8c18d5..b30fdc7 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -54,6 +54,7 @@
class ForLoopStatement;
class IfStatement;
class LoopStatement;
+class Materialize;
class Statement;
class SwitchStatement;
class TypeConstructor;
@@ -275,6 +276,11 @@
/// @returns true on success, false otherwise.
bool LoopStatement(const sem::LoopStatement* stmt) const;
+ /// Validates a materialize of an abstract numeric value
+ /// @param m the materialize to validate
+ /// @returns true on success, false otherwise
+ bool Materialize(const sem::Materialize* m) const;
+
/// Validates a matrix
/// @param ty the matrix to validate
/// @param source the source of the matrix
diff --git a/src/tint/sem/info.h b/src/tint/sem/info.h
index 66b2cd5..41321cf 100644
--- a/src/tint/sem/info.h
+++ b/src/tint/sem/info.h
@@ -70,8 +70,7 @@
return As<RESULT>(it->second);
}
- /// Add registers the semantic node `sem_node` for the AST or type node
- /// `node`.
+ /// Add registers the semantic node `sem_node` for the AST or type node `node`.
/// @param node the AST or type node
/// @param sem_node the semantic node
template <typename AST_OR_TYPE>
@@ -81,6 +80,14 @@
map_.emplace(node, sem_node);
}
+ /// Replace replaces any existing semantic node `sem_node` for the AST or type node `node`.
+ /// @param node the AST or type node
+ /// @param sem_node the new semantic node
+ template <typename AST_OR_TYPE>
+ void Replace(const AST_OR_TYPE* node, const SemanticNodeTypeFor<AST_OR_TYPE>* sem_node) {
+ map_[node] = sem_node;
+ }
+
/// Wrap returns a new Info created with the contents of `inner`.
/// The Info returned by Wrap is intended to temporarily extend the contents
/// of an existing immutable Info.
diff --git a/test/tint/BUILD.gn b/test/tint/BUILD.gn
index 2ef1202..4f1619f 100644
--- a/test/tint/BUILD.gn
+++ b/test/tint/BUILD.gn
@@ -257,6 +257,7 @@
"../../src/tint/resolver/intrinsic_table_test.cc",
"../../src/tint/resolver/is_host_shareable_test.cc",
"../../src/tint/resolver/is_storeable_test.cc",
+ "../../src/tint/resolver/materialize_test.cc",
"../../src/tint/resolver/pipeline_overridable_constant_test.cc",
"../../src/tint/resolver/ptr_ref_test.cc",
"../../src/tint/resolver/ptr_ref_validation_test.cc",