tint/resolver: Temporally ban f16 in buffer, pipeline IO and override

This patch make resolver reject using f16 types in uniform or storage
buffer, pipeline IO or overridable variable, since these are not
implemented yet. This can help prevent hitting invalid path in writers.

Bug: tint:1473, tint:1502
Change-Id: I5ea753e4254276a6d141d7012a6d0987423a61cf
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/95827
Reviewed-by: Ben Clayton <bclayton@google.com>
Auto-Submit: Zhaoming Jiang <zhaoming.jiang@intel.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.com>
This commit is contained in:
Zhaoming Jiang 2022-07-08 19:35:05 +00:00 committed by Dawn LUCI CQ
parent 03d9835d14
commit 9c71174f38
5 changed files with 334 additions and 46 deletions

View File

@ -334,12 +334,26 @@ static constexpr Params cases[] = {
ParamsFor<alias<i32>>(true), //
ParamsFor<alias<u32>>(true), //
ParamsFor<alias<bool>>(false), //
// Currently entry point IO of f16 types are not implemented yet.
// TODO(tint:1473, tint:1502): Change f16 and vecN<f16> cases to valid after f16 is supported in
// entry point IO.
ParamsFor<f16>(false), //
ParamsFor<vec2<f16>>(false), //
ParamsFor<vec3<f16>>(false), //
ParamsFor<vec4<f16>>(false), //
ParamsFor<mat2x2<f16>>(false), //
ParamsFor<mat3x3<f16>>(false), //
ParamsFor<mat4x4<f16>>(false), //
ParamsFor<alias<f16>>(false), //
};
TEST_P(TypeValidationTest, BareInputs) {
// @fragment
// fn main(@location(0) @interpolate(flat) a : *) {}
auto params = GetParam();
Enable(ast::Extension::kF16);
auto* a = Param("a", params.create_ast_type(*this), {Location(0), Flat()});
Func(Source{{12, 34}}, "main", {a}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
@ -357,6 +371,9 @@ TEST_P(TypeValidationTest, StructInputs) {
// @fragment
// fn main(a : Input) {}
auto params = GetParam();
Enable(ast::Extension::kF16);
auto* input =
Structure("Input", {Member("a", params.create_ast_type(*this), {Location(0), Flat()})});
auto* a = Param("a", ty.Of(input), {});
@ -375,6 +392,9 @@ TEST_P(TypeValidationTest, BareOutputs) {
// return *();
// }
auto params = GetParam();
Enable(ast::Extension::kF16);
Func(Source{{12, 34}}, "main", {}, params.create_ast_type(*this),
{Return(Construct(params.create_ast_type(*this)))}, {Stage(ast::PipelineStage::kFragment)},
{Location(0)});
@ -395,6 +415,9 @@ TEST_P(TypeValidationTest, StructOutputs) {
// return Output();
// }
auto params = GetParam();
Enable(ast::Extension::kF16);
auto* output = Structure("Output", {Member("a", params.create_ast_type(*this), {Location(0)})});
Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
{Stage(ast::PipelineStage::kFragment)});

View File

@ -107,7 +107,7 @@ TEST_F(ResolverHostShareableValidationTest, NoError) {
Enable(ast::Extension::kF16);
auto* i1 = Structure("I1", {
Member(Source{{1, 1}}, "w1", ty.f16()),
Member(Source{{1, 1}}, "w1", ty.f32()),
Member(Source{{2, 1}}, "x1", ty.f32()),
Member(Source{{3, 1}}, "y1", ty.vec3<f32>()),
Member(Source{{4, 1}}, "z1", ty.array<i32, 4>()),
@ -115,7 +115,7 @@ TEST_F(ResolverHostShareableValidationTest, NoError) {
auto* a1 = Alias("a1", ty.Of(i1));
auto* i2 = Structure("I2", {
Member(Source{{5, 1}}, "x2", ty.mat2x2<f32>()),
Member(Source{{6, 1}}, "w2", ty.mat3x4<f16>()),
Member(Source{{6, 1}}, "w2", ty.mat3x4<f32>()),
Member(Source{{7, 1}}, "z2", ty.Of(i1)),
});
auto* a2 = Alias("a2", ty.Of(i2));

View File

@ -103,5 +103,15 @@ TEST_F(ResolverPipelineOverridableConstantTest, IdTooLarge) {
EXPECT_EQ(r()->error(), "12:34 error: pipeline constant IDs must be between 0 and 65535");
}
TEST_F(ResolverPipelineOverridableConstantTest, F16_TemporallyBan) {
Enable(ast::Extension::kF16);
Override(Source{{12, 34}}, "a", ty.f16(), Expr(1_h), {Id(1u)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: 'override' of type f16 is not implemented yet");
}
} // namespace
} // namespace tint::resolver

View File

@ -23,6 +23,8 @@ using namespace tint::number_suffixes; // NOLINT
namespace tint::resolver {
namespace {
using ::testing::HasSubstr;
using ResolverStorageClassValidationTest = ResolverTest;
TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
@ -99,6 +101,139 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferBool) {
56:78 note: while instantiating 'var' g)");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
// type a = bool;
// var<storage, read> g : a;
auto* a = Alias("a", ty.bool_());
GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
// F16 types in storage and uniform buffer is not implemented yet.
// TODO(tint:1473, tint:1502): make these testcases valid after f16 is supported.
TEST_F(ResolverStorageClassValidationTest, StorageBufferF16_TemporallyBan) {
// var<storage> g : f16;
Enable(ast::Extension::kF16);
GlobalVar("g", ty.f16(Source{{56, 78}}), ast::StorageClass::kStorage,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
"implemented yet");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferF16Alias_TemporallyBan) {
// type a = f16;
// var<storage, read> g : a;
Enable(ast::Extension::kF16);
auto* a = Alias("a", ty.f16());
GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::StorageClass::kStorage,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
"implemented yet");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferVectorF16_TemporallyBan) {
// var<storage> g : vec4<f16>;
Enable(ast::Extension::kF16);
GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::StorageClass::kStorage,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
"implemented yet");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferArrayF16_TemporallyBan) {
// struct S { a : f16 };
// var<storage, read> g : array<S, 3u>;
Enable(ast::Extension::kF16);
auto* s = Structure("S", {Member("a", ty.f16(Source{{56, 78}}))});
auto* a = ty.array(ty.Of(s), 3_u);
GlobalVar("g", a, ast::StorageClass::kStorage, ast::Access::kRead,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_THAT(r()->error(),
HasSubstr("56:78 error: using f16 types in 'uniform' or 'storage' storage "
"class is not implemented yet"));
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferStructF16_TemporallyBan) {
// struct S { x : f16 };
// var<storage, read> g : S;
Enable(ast::Extension::kF16);
auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
GlobalVar("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_THAT(r()->error(),
HasSubstr("12:34 error: using f16 types in 'uniform' or 'storage' storage "
"class is not implemented yet"));
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoErrorStructF16Aliases_TemporallyBan) {
// struct S { x : f16 };
// type a1 = S;
// var<storage, read> g : a1;
Enable(ast::Extension::kF16);
auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
auto* a1 = Alias("a1", ty.Of(s));
auto* a2 = Alias("a2", ty.Of(a1));
GlobalVar("g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_THAT(r()->error(),
HasSubstr("12:34 error: using f16 types in 'uniform' or 'storage' storage "
"class is not implemented yet"));
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
// var<storage> g : ptr<private, f32>;
GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
@ -127,7 +262,7 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferIntScalar) {
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferVector) {
TEST_F(ResolverStorageClassValidationTest, StorageBufferVectorF32) {
// var<storage> g : vec4<f32>;
GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kStorage,
ast::AttributeList{
@ -138,7 +273,7 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferVector) {
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) {
TEST_F(ResolverStorageClassValidationTest, StorageBufferArrayF32) {
// var<storage, read> g : array<S, 3u>;
auto* s = Structure("S", {Member("a", ty.f32())});
auto* a = ty.array(ty.Of(s), 3_u);
@ -151,24 +286,6 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) {
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
// type a = bool;
// var<storage, read> g : a;
auto* a = Alias("a", ty.bool_());
GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
TEST_F(ResolverStorageClassValidationTest, NotStorage_AccessMode) {
// var<private, read> g : a;
GlobalVar(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kPrivate, ast::Access::kRead);
@ -207,7 +324,7 @@ TEST_F(ResolverStorageClassValidationTest, Storage_WriteAccessMode) {
R"(56:78 error: access mode 'write' is not valid for the 'storage' address space)");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
TEST_F(ResolverStorageClassValidationTest, StorageBufferStructI32) {
// struct S { x : i32 };
// var<storage, read> g : S;
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
@ -220,7 +337,7 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
ASSERT_TRUE(r()->Resolve());
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Aliases) {
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoErrorStructI32Aliases) {
// struct S { x : i32 };
// type a1 = S;
// var<storage, read> g : a1;
@ -271,6 +388,140 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferBool) {
56:78 note: while instantiating 'var' g)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
// type a = bool;
// var<uniform> g : a;
auto* a = Alias("a", ty.bool_());
GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
// F16 types in storage and uniform buffer is not implemented yet.
// TODO(tint:1473, tint:1502): make these testcases valid after f16 is supported.
TEST_F(ResolverStorageClassValidationTest, UniformBufferF16_TemporallyBan) {
// var<uniform> g : f16;
Enable(ast::Extension::kF16);
GlobalVar("g", ty.f16(Source{{56, 78}}), ast::StorageClass::kUniform,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
"implemented yet");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferF16Alias_TemporallyBan) {
// type a = f16;
// var<uniform> g : a;
Enable(ast::Extension::kF16);
auto* a = Alias("a", ty.f16());
GlobalVar("g", ty.type_name(Source{{56, 78}}, a->name), ast::StorageClass::kUniform,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"56:78 error: using f16 types in 'uniform' or 'storage' storage class is not "
"implemented yet");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferVectorF16_TemporallyBan) {
// var<uniform> g : vec4<f16>;
Enable(ast::Extension::kF16);
GlobalVar("g", ty.vec(Source{{56, 78}}, ty.Of<f16>(), 4u), ast::StorageClass::kUniform,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_THAT(r()->error(),
HasSubstr("56:78 error: using f16 types in 'uniform' or 'storage' storage "
"class is not implemented yet"));
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferArrayF16_TemporallyBan) {
// struct S {
// @size(16) f : f16;
// }
// var<uniform> g : array<S, 3u>;
Enable(ast::Extension::kF16);
auto* s = Structure("S", {Member("a", ty.f16(Source{{56, 78}}), {MemberSize(16)})});
auto* a = ty.array(ty.Of(s), 3_u);
GlobalVar("g", a, ast::StorageClass::kUniform,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_THAT(r()->error(),
HasSubstr("56:78 error: using f16 types in 'uniform' or 'storage' storage "
"class is not implemented yet"));
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferStructF16_TemporallyBan) {
// struct S { x : f16 };
// var<uniform> g : S;
Enable(ast::Extension::kF16);
auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
GlobalVar("g", ty.Of(s), ast::StorageClass::kUniform,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_THAT(r()->error(),
HasSubstr("12:34 error: using f16 types in 'uniform' or 'storage' storage "
"class is not implemented yet"));
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferStructF16Aliases_TemporallyBan) {
// struct S { x : f16 };
// type a1 = S;
// var<uniform> g : a1;
Enable(ast::Extension::kF16);
auto* s = Structure("S", {Member("x", ty.f16(Source{{12, 34}}))});
auto* a1 = Alias("a1", ty.Of(s));
GlobalVar("g", ty.Of(a1), ast::StorageClass::kUniform,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_THAT(r()->error(),
HasSubstr("12:34 error: using f16 types in 'uniform' or 'storage' storage "
"class is not implemented yet"));
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferPointer) {
// var<uniform> g : ptr<private, f32>;
GlobalVar(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
@ -299,7 +550,7 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferIntScalar) {
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferVector) {
TEST_F(ResolverStorageClassValidationTest, UniformBufferVectorF32) {
// var<uniform> g : vec4<f32>;
GlobalVar(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kUniform,
ast::AttributeList{
@ -310,7 +561,7 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferVector) {
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) {
TEST_F(ResolverStorageClassValidationTest, UniformBufferArrayF32) {
// struct S {
// @size(16) f : f32;
// }
@ -326,25 +577,7 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) {
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
// type a = bool;
// var<uniform> g : a;
auto* a = Alias("a", ty.bool_());
GlobalVar(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
ast::AttributeList{
create<ast::BindingAttribute>(0u),
create<ast::GroupAttribute>(0u),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
56:78 note: while instantiating 'var' g)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
TEST_F(ResolverStorageClassValidationTest, UniformBufferStructI32) {
// struct S { x : i32 };
// var<uniform> g : S;
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
@ -357,7 +590,7 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Aliases) {
TEST_F(ResolverStorageClassValidationTest, UniformBufferStructI32Aliases) {
// struct S { x : i32 };
// type a1 = S;
// var<uniform> g : a1;

View File

@ -392,6 +392,15 @@ bool Validator::StorageClassLayout(const sem::Type* store_ty,
return true;
}
// Temporally forbid using f16 types in "uniform" and "storage" storage class.
// TODO(tint:1473, tint:1502): Remove this error after f16 is supported in "uniform" and
// "storage" storage class.
if (Is<sem::F16>(sem::Type::DeepestElementOf(store_ty))) {
AddError("using f16 types in 'uniform' or 'storage' storage class is not implemented yet",
source);
return false;
}
if (auto* str = store_ty->As<sem::Struct>()) {
for (size_t i = 0; i < str->Members().size(); ++i) {
auto* const m = str->Members()[i];
@ -801,6 +810,12 @@ bool Validator::Override(const sem::Variable* v) const {
decl->source);
return false;
}
if (storage_ty->Is<sem::F16>()) {
AddError("'override' of type f16 is not implemented yet", decl->source);
return false;
}
return true;
}
@ -1102,6 +1117,13 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
const sem::Type* ty, Source source,
ParamOrRetType param_or_ret,
bool is_struct_member) {
// Temporally forbid using f16 types in entry point IO.
// TODO(tint:1473, tint:1502): Remove this error after f16 is supported in entry point IO.
if (Is<sem::F16>(sem::Type::DeepestElementOf(ty))) {
AddError("entry point IO of f16 types is not implemented yet", source);
return false;
}
// Scan attributes for pipeline IO attributes.
// Check for overlap with attributes that have been seen previously.
const ast::Attribute* pipeline_io_attribute = nullptr;