Import Tint changes from Dawn
Changes:
- e9c5070348abd0f33529b225dfcc2cc4703bc3ea tint/writer/msl: Support for F16 type, constructor, and c... by Zhaoming Jiang <zhaoming.jiang@intel.com>
- 0d3d3210d4ff86ecd3b2e288e091246f8b47af90 tint/writer/glsl: Support for F16 type, constructor, and ... by Zhaoming Jiang <zhaoming.jiang@intel.com>
- 9c71174f381d35160e67f1c897e28e63c68a0074 tint/resolver: Temporally ban f16 in buffer, pipeline IO ... by Zhaoming Jiang <zhaoming.jiang@intel.com>
- d31838aff1176b8bfbfdeec2c4f9c7f17360342c tint: Fix x86 build by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: e9c5070348abd0f33529b225dfcc2cc4703bc3ea
Change-Id: Ic4a220ec9a6c0f9ec87bbb9b8c9fa0303a3a0938
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/95882
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/resolver/entry_point_validation_test.cc b/src/tint/resolver/entry_point_validation_test.cc
index 5e5df15..b0495c2 100644
--- a/src/tint/resolver/entry_point_validation_test.cc
+++ b/src/tint/resolver/entry_point_validation_test.cc
@@ -334,12 +334,26 @@
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 @@
// @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 @@
// 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 @@
// 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)});
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index d50057c..1bd9ed7 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -107,7 +107,7 @@
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 @@
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));
diff --git a/src/tint/resolver/pipeline_overridable_constant_test.cc b/src/tint/resolver/pipeline_overridable_constant_test.cc
index 583cc16..3614500 100644
--- a/src/tint/resolver/pipeline_overridable_constant_test.cc
+++ b/src/tint/resolver/pipeline_overridable_constant_test.cc
@@ -103,5 +103,15 @@
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
diff --git a/src/tint/resolver/storage_class_validation_test.cc b/src/tint/resolver/storage_class_validation_test.cc
index 3b98fdb..cb3ab16 100644
--- a/src/tint/resolver/storage_class_validation_test.cc
+++ b/src/tint/resolver/storage_class_validation_test.cc
@@ -23,6 +23,8 @@
namespace tint::resolver {
namespace {
+using ::testing::HasSubstr;
+
using ResolverStorageClassValidationTest = ResolverTest;
TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
@@ -99,6 +101,139 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) {
+TEST_F(ResolverStorageClassValidationTest, UniformBufferArrayF32) {
// struct S {
// @size(16) f : f32;
// }
@@ -326,25 +577,7 @@
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 @@
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;
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index ee98d03..d99d35b 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -392,6 +392,15 @@
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 @@
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 @@
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;
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index fdf4557..01cbba0 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -159,6 +159,17 @@
}
}
+bool PrintF16(std::ostream& out, float value) {
+ // Note: Currently inf and nan should not be constructable, and there is no solid way to
+ // generate constant/literal f16 Inf or NaN.
+ if (std::isinf(value) || std::isnan(value)) {
+ return false;
+ } else {
+ out << FloatToString(value) << "hf";
+ return true;
+ }
+}
+
} // namespace
SanitizedResult::SanitizedResult() = default;
@@ -313,6 +324,10 @@
extensions.Append("#extension GL_OES_sample_variables : require");
}
+ if (requires_f16_extension_) {
+ extensions.Append("#extension GL_AMD_gpu_shader_half_float : require");
+ }
+
auto indent = current_buffer_->current_indent;
if (!extensions.lines.empty()) {
@@ -333,17 +348,12 @@
return true;
}
-bool GeneratorImpl::RecordExtension(const ast::Enable*) {
- /*
- Deal with extension node here, recording it within the generator for
- later emition.
- For example:
- ```
- if (ext->kind == ast::Enable::ExtensionKind::kF16) {
- require_fp16_ = true;
- }
- ```
- */
+bool GeneratorImpl::RecordExtension(const ast::Enable* ext) {
+ // Deal with extension node here, recording it within the generator for later emition.
+
+ if (ext->extension == ast::Extension::kF16) {
+ requires_f16_extension_ = true;
+ }
return true;
}
@@ -2225,6 +2235,7 @@
PrintF32(out, constant->As<float>());
return true;
},
+ [&](const sem::F16*) { return PrintF16(out, constant->As<float>()); },
[&](const sem::I32*) {
out << constant->As<AInt>();
return true;
@@ -2305,6 +2316,9 @@
return true;
},
[&](const ast::FloatLiteralExpression* l) {
+ if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
+ return PrintF16(out, static_cast<float>(l->value));
+ }
PrintF32(out, static_cast<float>(l->value));
return true;
},
@@ -2326,6 +2340,8 @@
out << "false";
} else if (type->Is<sem::F32>()) {
out << "0.0f";
+ } else if (type->Is<sem::F16>()) {
+ out << "0.0hf";
} else if (type->Is<sem::I32>()) {
out << "0";
} else if (type->Is<sem::U32>()) {
@@ -2751,12 +2767,14 @@
} else if (type->Is<sem::F32>()) {
out << "float";
} else if (type->Is<sem::F16>()) {
- diagnostics_.add_error(diag::System::Writer, "Type f16 is not completely implemented yet.");
- return false;
+ out << "float16_t";
} else if (type->Is<sem::I32>()) {
out << "int";
} else if (auto* mat = type->As<sem::Matrix>()) {
- TINT_ASSERT(Writer, mat->type()->Is<sem::F32>());
+ TINT_ASSERT(Writer, (mat->type()->IsAnyOf<sem::F32, sem::F16>()));
+ if (mat->type()->Is<sem::F16>()) {
+ out << "f16";
+ }
out << "mat" << mat->columns();
if (mat->rows() != mat->columns()) {
out << "x" << mat->rows();
@@ -2835,6 +2853,8 @@
auto width = vec->Width();
if (vec->type()->Is<sem::F32>() && width >= 1 && width <= 4) {
out << "vec" << width;
+ } else if (vec->type()->Is<sem::F16>() && width >= 1 && width <= 4) {
+ out << "f16vec" << width;
} else if (vec->type()->Is<sem::I32>() && width >= 1 && width <= 4) {
out << "ivec" << width;
} else if (vec->type()->Is<sem::U32>() && width >= 1 && width <= 4) {
diff --git a/src/tint/writer/glsl/generator_impl.h b/src/tint/writer/glsl/generator_impl.h
index 15b7ee1..153e316 100644
--- a/src/tint/writer/glsl/generator_impl.h
+++ b/src/tint/writer/glsl/generator_impl.h
@@ -527,6 +527,7 @@
std::unordered_set<const sem::Struct*> emitted_structs_;
bool requires_oes_sample_variables_ = false;
bool requires_default_precision_qualifier_ = false;
+ bool requires_f16_extension_ = false;
Version version_;
};
diff --git a/src/tint/writer/glsl/generator_impl_constructor_test.cc b/src/tint/writer/glsl/generator_impl_constructor_test.cc
index 9a937e8..20d6b07 100644
--- a/src/tint/writer/glsl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/glsl/generator_impl_constructor_test.cc
@@ -61,6 +61,18 @@
EXPECT_THAT(gen.result(), HasSubstr("1073741824.0f"));
}
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_F16) {
+ Enable(ast::Extension::kF16);
+
+ // Use a number close to 1<<16 but whose decimal representation ends in 0.
+ WrapInFunction(Expr(f16((1 << 15) - 8)));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("32752.0hf"));
+}
+
TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Float) {
WrapInFunction(Construct<f32>(-1.2e-5_f));
@@ -70,6 +82,17 @@
EXPECT_THAT(gen.result(), HasSubstr("-0.000012f"));
}
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(Construct<f16>(-1.2e-3_h));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("-0.00119972229hf"));
+}
+
TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Bool) {
WrapInFunction(Construct<bool>(true));
@@ -97,7 +120,7 @@
EXPECT_THAT(gen.result(), HasSubstr("12345u"));
}
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_F32) {
WrapInFunction(vec3<f32>(1_f, 2_f, 3_f));
GeneratorImpl& gen = Build();
@@ -106,7 +129,18 @@
EXPECT_THAT(gen.result(), HasSubstr("vec3(1.0f, 2.0f, 3.0f)"));
}
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_Empty) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(vec3<f16>(1_h, 2_h, 3_h));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("f16vec3(1.0hf, 2.0hf, 3.0hf)"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_Empty_F32) {
WrapInFunction(vec3<f32>());
GeneratorImpl& gen = Build();
@@ -115,7 +149,18 @@
EXPECT_THAT(gen.result(), HasSubstr("vec3(0.0f)"));
}
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_Float) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_Empty_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(vec3<f16>());
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("f16vec3(0.0hf)"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_F32_Literal) {
WrapInFunction(vec3<f32>(2_f));
GeneratorImpl& gen = Build();
@@ -124,6 +169,43 @@
EXPECT_THAT(gen.result(), HasSubstr("vec3(2.0f)"));
}
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_F16_Literal) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(vec3<f16>(2_h));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("f16vec3(2.0hf)"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_F32_Var) {
+ auto* var = Var("v", nullptr, Expr(2_f));
+ auto* cast = vec3<f32>(var);
+ WrapInFunction(var, cast);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr(R"(float v = 2.0f;
+ vec3 tint_symbol = vec3(v);)"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_F16_Var) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = Var("v", nullptr, Expr(2_h));
+ auto* cast = vec3<f16>(var);
+ WrapInFunction(var, cast);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr(R"(float16_t v = 2.0hf;
+ f16vec3 tint_symbol = f16vec3(v);)"));
+}
+
TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_SingleScalar_Bool) {
WrapInFunction(vec3<bool>(true));
@@ -151,7 +233,7 @@
EXPECT_THAT(gen.result(), HasSubstr("uvec3(2u)"));
}
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_F32) {
WrapInFunction(mat2x3<f32>(vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(3_f, 4_f, 5_f)));
GeneratorImpl& gen = Build();
@@ -161,14 +243,135 @@
EXPECT_THAT(gen.result(), HasSubstr("mat2x3(vec3(1.0f, 2.0f, 3.0f), vec3(3.0f, 4.0f, 5.0f))"));
}
-TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Empty) {
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(mat2x3<f16>(vec3<f16>(1_h, 2_h, 3_h), vec3<f16>(3_h, 4_h, 5_h)));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(),
+ HasSubstr("f16mat2x3(f16vec3(1.0hf, 2.0hf, 3.0hf), f16vec3(3.0hf, 4.0hf, 5.0hf))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Complex_F32) {
+ // mat4x4<f32>(
+ // vec4<f32>(2.0f, 3.0f, 4.0f, 8.0f),
+ // vec4<f32>(),
+ // vec4<f32>(7.0f),
+ // vec4<f32>(vec4<f32>(42.0f, 21.0f, 6.0f, -5.0f)),
+ // );
+ auto* vector_literal =
+ vec4<f32>(Expr(f32(2.0)), Expr(f32(3.0)), Expr(f32(4.0)), Expr(f32(8.0)));
+ auto* vector_zero_ctor = vec4<f32>();
+ auto* vector_single_scalar_ctor = vec4<f32>(Expr(f32(7.0)));
+ auto* vector_identical_ctor =
+ vec4<f32>(vec4<f32>(Expr(f32(42.0)), Expr(f32(21.0)), Expr(f32(6.0)), Expr(f32(-5.0))));
+
+ auto* constructor = mat4x4<f32>(vector_literal, vector_zero_ctor, vector_single_scalar_ctor,
+ vector_identical_ctor);
+
+ WrapInFunction(constructor);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(), HasSubstr("mat4(vec4(2.0f, 3.0f, 4.0f, 8.0f), vec4(0.0f), "
+ "vec4(7.0f), vec4(42.0f, 21.0f, 6.0f, -5.0f))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Complex_F16) {
+ // mat4x4<f16>(
+ // vec4<f16>(2.0h, 3.0h, 4.0h, 8.0h),
+ // vec4<f16>(),
+ // vec4<f16>(7.0h),
+ // vec4<f16>(vec4<f16>(42.0h, 21.0h, 6.0h, -5.0h)),
+ // );
+ Enable(ast::Extension::kF16);
+
+ auto* vector_literal =
+ vec4<f16>(Expr(f16(2.0)), Expr(f16(3.0)), Expr(f16(4.0)), Expr(f16(8.0)));
+ auto* vector_zero_ctor = vec4<f16>();
+ auto* vector_single_scalar_ctor = vec4<f16>(Expr(f16(7.0)));
+ auto* vector_identical_ctor =
+ vec4<f16>(vec4<f16>(Expr(f16(42.0)), Expr(f16(21.0)), Expr(f16(6.0)), Expr(f16(-5.0))));
+
+ auto* constructor = mat4x4<f16>(vector_literal, vector_zero_ctor, vector_single_scalar_ctor,
+ vector_identical_ctor);
+
+ WrapInFunction(constructor);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(),
+ HasSubstr("f16mat4(f16vec4(2.0hf, 3.0hf, 4.0hf, 8.0hf), f16vec4(0.0hf), "
+ "f16vec4(7.0hf), f16vec4(42.0hf, 21.0hf, 6.0hf, -5.0hf))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Empty_F32) {
WrapInFunction(mat2x3<f32>());
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
- EXPECT_THAT(gen.result(), HasSubstr("mat2x3(vec3(0.0f), vec3(0.0f)"));
+ EXPECT_THAT(gen.result(), HasSubstr("mat2x3 tint_symbol = mat2x3(vec3(0.0f), vec3(0.0f))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Empty_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(mat2x3<f16>());
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(),
+ HasSubstr("f16mat2x3 tint_symbol = f16mat2x3(f16vec3(0.0hf), f16vec3(0.0hf))"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Identity_F32) {
+ // fn f() {
+ // var m_1: mat4x4<f32> = mat4x4<f32>();
+ // var m_2: mat4x4<f32> = mat4x4<f32>(m_1);
+ // }
+
+ auto* m_1 = Var("m_1", ty.mat4x4(ty.f32()), mat4x4<f32>());
+ auto* m_2 = Var("m_2", ty.mat4x4(ty.f32()), mat4x4<f32>(m_1));
+
+ WrapInFunction(m_1, m_2);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(), HasSubstr("mat4 m_2 = mat4(m_1);"));
+}
+
+TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Mat_Identity_F16) {
+ // fn f() {
+ // var m_1: mat4x4<f16> = mat4x4<f16>();
+ // var m_2: mat4x4<f16> = mat4x4<f16>(m_1);
+ // }
+
+ Enable(ast::Extension::kF16);
+
+ auto* m_1 = Var("m_1", ty.mat4x4(ty.f16()), mat4x4<f16>());
+ auto* m_2 = Var("m_2", ty.mat4x4(ty.f16()), mat4x4<f16>(m_1));
+
+ WrapInFunction(m_1, m_2);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(), HasSubstr("f16mat4 m_2 = f16mat4(m_1);"));
}
TEST_F(GlslGeneratorImplTest_Constructor, EmitConstructor_Type_Array) {
diff --git a/src/tint/writer/glsl/generator_impl_module_constant_test.cc b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
index b483511..fa269da 100644
--- a/src/tint/writer/glsl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/glsl/generator_impl_module_constant_test.cc
@@ -117,6 +117,26 @@
)");
}
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = GlobalConst("G", nullptr, Expr(1_h));
+ Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+ float16_t l = 1.0hf;
+}
+
+)");
+}
+
TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_AInt) {
auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
@@ -168,6 +188,26 @@
)");
}
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_vec3_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = GlobalConst("G", nullptr, vec3<f16>(1_h, 2_h, 3_h));
+ Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+ f16vec3 l = f16vec3(1.0hf, 2.0hf, 3.0hf);
+}
+
+)");
+}
+
TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_AFloat) {
auto* var = GlobalConst("G", nullptr,
Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
@@ -203,6 +243,26 @@
)");
}
+TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_mat2x3_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = GlobalConst("G", nullptr, mat2x3<f16>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
+ Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+ f16mat2x3 l = f16mat2x3(f16vec3(1.0hf, 2.0hf, 3.0hf), f16vec3(4.0hf, 5.0hf, 6.0hf));
+}
+
+)");
+}
+
TEST_F(GlslGeneratorImplTest_ModuleConstant, Emit_GlobalConst_arr_f32) {
auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
diff --git a/src/tint/writer/glsl/generator_impl_type_test.cc b/src/tint/writer/glsl/generator_impl_type_test.cc
index acf28ff..1c4cb03 100644
--- a/src/tint/writer/glsl/generator_impl_type_test.cc
+++ b/src/tint/writer/glsl/generator_impl_type_test.cc
@@ -105,6 +105,19 @@
EXPECT_EQ(out.str(), "float");
}
+TEST_F(GlslGeneratorImplTest_Type, EmitType_F16) {
+ Enable(ast::Extension::kF16);
+
+ auto* f16 = create<sem::F16>();
+
+ GeneratorImpl& gen = Build();
+
+ std::stringstream out;
+ ASSERT_TRUE(gen.EmitType(out, f16, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+ << gen.error();
+ EXPECT_EQ(out.str(), "float16_t");
+}
+
TEST_F(GlslGeneratorImplTest_Type, EmitType_I32) {
auto* i32 = create<sem::I32>();
@@ -116,7 +129,7 @@
EXPECT_EQ(out.str(), "int");
}
-TEST_F(GlslGeneratorImplTest_Type, EmitType_Matrix) {
+TEST_F(GlslGeneratorImplTest_Type, EmitType_Matrix_F32) {
auto* f32 = create<sem::F32>();
auto* vec3 = create<sem::Vector>(f32, 3u);
auto* mat2x3 = create<sem::Matrix>(vec3, 2u);
@@ -129,6 +142,21 @@
EXPECT_EQ(out.str(), "mat2x3");
}
+TEST_F(GlslGeneratorImplTest_Type, EmitType_Matrix_F16) {
+ Enable(ast::Extension::kF16);
+
+ auto* f16 = create<sem::F16>();
+ auto* vec3 = create<sem::Vector>(f16, 3u);
+ auto* mat2x3 = create<sem::Matrix>(vec3, 2u);
+
+ GeneratorImpl& gen = Build();
+
+ std::stringstream out;
+ ASSERT_TRUE(gen.EmitType(out, mat2x3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+ << gen.error();
+ EXPECT_EQ(out.str(), "f16mat2x3");
+}
+
TEST_F(GlslGeneratorImplTest_Type, EmitType_StructDecl) {
auto* s = Structure("S", {
Member("a", ty.i32()),
@@ -213,7 +241,7 @@
EXPECT_EQ(out.str(), "uint");
}
-TEST_F(GlslGeneratorImplTest_Type, EmitType_Vector) {
+TEST_F(GlslGeneratorImplTest_Type, EmitType_Vector_F32) {
auto* f32 = create<sem::F32>();
auto* vec3 = create<sem::Vector>(f32, 3u);
@@ -225,6 +253,20 @@
EXPECT_EQ(out.str(), "vec3");
}
+TEST_F(GlslGeneratorImplTest_Type, EmitType_Vector_F16) {
+ Enable(ast::Extension::kF16);
+
+ auto* f16 = create<sem::F16>();
+ auto* vec3 = create<sem::Vector>(f16, 3u);
+
+ GeneratorImpl& gen = Build();
+
+ std::stringstream out;
+ ASSERT_TRUE(gen.EmitType(out, vec3, ast::StorageClass::kNone, ast::Access::kReadWrite, ""))
+ << gen.error();
+ EXPECT_EQ(out.str(), "f16vec3");
+}
+
TEST_F(GlslGeneratorImplTest_Type, EmitType_Void) {
auto* void_ = create<sem::Void>();
diff --git a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
index daa664a..873afc1 100644
--- a/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/glsl/generator_impl_variable_decl_statement_test.cc
@@ -149,6 +149,26 @@
)");
}
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* C = Const("C", nullptr, Expr(1_h));
+ Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+ float16_t l = 1.0hf;
+}
+
+)");
+}
+
TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_AInt) {
auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
@@ -200,6 +220,26 @@
)");
}
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_vec3_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* C = Const("C", nullptr, vec3<f16>(1_h, 2_h, 3_h));
+ Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+ f16vec3 l = f16vec3(1.0hf, 2.0hf, 3.0hf);
+}
+
+)");
+}
+
TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
auto* C =
Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
@@ -235,6 +275,26 @@
)");
}
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_mat2x3_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* C = Const("C", nullptr, mat2x3<f16>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
+ Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+void f() {
+ f16mat2x3 l = f16mat2x3(f16vec3(1.0hf, 2.0hf, 3.0hf), f16vec3(4.0hf, 5.0hf, 6.0hf));
+}
+
+)");
+}
+
TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const_arr_f32) {
auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
@@ -300,7 +360,7 @@
EXPECT_THAT(gen.result(), HasSubstr(" float a = 0.0f;\n"));
}
-TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec) {
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec_f32) {
auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, vec3<f32>());
auto* stmt = Decl(var);
@@ -313,7 +373,22 @@
)");
}
-TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroMat) {
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroVec_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = Var("a", ty.vec3<f16>(), ast::StorageClass::kNone, vec3<f16>());
+
+ auto* stmt = Decl(var);
+ WrapInFunction(stmt);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+ EXPECT_EQ(gen.result(), R"(f16vec3 a = f16vec3(0.0hf);
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroMat_f32) {
auto* var = Var("a", ty.mat2x3<f32>(), ast::StorageClass::kNone, mat2x3<f32>());
auto* stmt = Decl(var);
@@ -327,5 +402,21 @@
)");
}
+TEST_F(GlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Initializer_ZeroMat_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = Var("a", ty.mat2x3<f16>(), ast::StorageClass::kNone, mat2x3<f16>());
+
+ auto* stmt = Decl(var);
+ WrapInFunction(stmt);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+ EXPECT_EQ(gen.result(),
+ R"(f16mat2x3 a = f16mat2x3(f16vec3(0.0hf), f16vec3(0.0hf));
+)");
+}
+
} // namespace
} // namespace tint::writer::glsl
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 43324bb..e2b14a6 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -39,6 +39,7 @@
#include "src/tint/sem/constant.h"
#include "src/tint/sem/depth_multisampled_texture.h"
#include "src/tint/sem/depth_texture.h"
+#include "src/tint/sem/f16.h"
#include "src/tint/sem/f32.h"
#include "src/tint/sem/function.h"
#include "src/tint/sem/i32.h"
@@ -97,6 +98,20 @@
}
}
+void PrintF16(std::ostream& out, float value) {
+ // Note: Currently inf and nan should not be constructable, but this is implemented for the day
+ // we support them.
+ if (std::isinf(value)) {
+ // HUGE_VALH evaluates to +infinity.
+ out << (value >= 0 ? "HUGE_VALH" : "-HUGE_VALH");
+ } else if (std::isnan(value)) {
+ // There is no NaN expr for half in MSL, "NAN" is of float type.
+ out << "NAN";
+ } else {
+ out << FloatToString(value) << "h";
+ }
+}
+
void PrintI32(std::ostream& out, int32_t value) {
// MSL (and C++) parse `-2147483648` as a `long` because it parses unary minus and `2147483648`
// as separate tokens, and the latter doesn't fit into an (32-bit) `int`.
@@ -1551,10 +1566,8 @@
return true;
},
[&](const sem::F16*) {
- // Placeholder for emitting f16 zero value
- diagnostics_.add_error(diag::System::Writer,
- "Type f16 is not completely implemented yet");
- return false;
+ out << "0.0h";
+ return true;
},
[&](const sem::F32*) {
out << "0.0f";
@@ -1605,6 +1618,10 @@
PrintF32(out, constant->As<float>());
return true;
},
+ [&](const sem::F16*) {
+ PrintF16(out, constant->As<float>());
+ return true;
+ },
[&](const sem::I32*) {
PrintI32(out, constant->As<int32_t>());
return true;
@@ -1694,7 +1711,11 @@
return true;
},
[&](const ast::FloatLiteralExpression* l) {
- PrintF32(out, static_cast<float>(l->value));
+ if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
+ PrintF16(out, static_cast<float>(l->value));
+ } else {
+ PrintF32(out, static_cast<float>(l->value));
+ }
return true;
},
[&](const ast::IntLiteralExpression* i) {
@@ -2459,9 +2480,8 @@
return true;
},
[&](const sem::F16*) {
- diagnostics_.add_error(diag::System::Writer,
- "Type f16 is not completely implemented yet");
- return false;
+ out << "half";
+ return true;
},
[&](const sem::F32*) {
out << "float";
@@ -3043,20 +3063,25 @@
[&](const sem::F32*) {
return SizeAndAlign{4, 4};
},
+ [&](const sem::F16*) {
+ return SizeAndAlign{2, 2};
+ },
[&](const sem::Vector* vec) {
auto num_els = vec->Width();
auto* el_ty = vec->type();
- if (el_ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) {
+ SizeAndAlign el_size_align = MslPackedTypeSizeAndAlign(el_ty);
+ if (el_ty->IsAnyOf<sem::U32, sem::I32, sem::F32, sem::F16>()) {
// Use a packed_vec type for 3-element vectors only.
if (num_els == 3) {
// https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
// 2.2.3 Packed Vector Types
- return SizeAndAlign{num_els * 4, 4};
+ return SizeAndAlign{num_els * el_size_align.size, el_size_align.align};
} else {
// https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
// 2.2 Vector Data Types
- return SizeAndAlign{num_els * 4, num_els * 4};
+ // Vector data types are aligned to their size.
+ return SizeAndAlign{num_els * el_size_align.size, num_els * el_size_align.size};
}
}
TINT_UNREACHABLE(Writer, diagnostics_)
@@ -3070,8 +3095,9 @@
auto cols = mat->columns();
auto rows = mat->rows();
auto* el_ty = mat->type();
- if (el_ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) {
- static constexpr SizeAndAlign table[] = {
+ // Metal only support half and float matrix.
+ if (el_ty->IsAnyOf<sem::F32, sem::F16>()) {
+ static constexpr SizeAndAlign table_f32[] = {
/* float2x2 */ {16, 8},
/* float2x3 */ {32, 16},
/* float2x4 */ {32, 16},
@@ -3082,8 +3108,23 @@
/* float4x3 */ {64, 16},
/* float4x4 */ {64, 16},
};
+ static constexpr SizeAndAlign table_f16[] = {
+ /* half2x2 */ {8, 4},
+ /* half2x3 */ {16, 8},
+ /* half2x4 */ {16, 8},
+ /* half3x2 */ {12, 4},
+ /* half3x3 */ {24, 8},
+ /* half3x4 */ {24, 8},
+ /* half4x2 */ {16, 4},
+ /* half4x3 */ {32, 8},
+ /* half4x4 */ {32, 8},
+ };
if (cols >= 2 && cols <= 4 && rows >= 2 && rows <= 4) {
- return table[(3 * (cols - 2)) + (rows - 2)];
+ if (el_ty->Is<sem::F32>()) {
+ return table_f32[(3 * (cols - 2)) + (rows - 2)];
+ } else {
+ return table_f16[(3 * (cols - 2)) + (rows - 2)];
+ }
}
}
diff --git a/src/tint/writer/msl/generator_impl_constructor_test.cc b/src/tint/writer/msl/generator_impl_constructor_test.cc
index 2fa85f0..14fc4a5 100644
--- a/src/tint/writer/msl/generator_impl_constructor_test.cc
+++ b/src/tint/writer/msl/generator_impl_constructor_test.cc
@@ -61,6 +61,18 @@
EXPECT_THAT(gen.result(), HasSubstr("1073741824.0f"));
}
+TEST_F(MslGeneratorImplTest, EmitConstructor_F16) {
+ Enable(ast::Extension::kF16);
+
+ // Use a number close to 1<<16 but whose decimal representation ends in 0.
+ WrapInFunction(Expr(f16((1 << 15) - 8)));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("32752.0h"));
+}
+
TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Float) {
WrapInFunction(Construct<f32>(-1.2e-5_f));
@@ -70,6 +82,17 @@
EXPECT_THAT(gen.result(), HasSubstr("-0.000012f"));
}
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(Construct<f16>(-1.2e-3_h));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("-0.00119972229h"));
+}
+
TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Bool) {
WrapInFunction(Construct<bool>(true));
@@ -97,7 +120,7 @@
EXPECT_THAT(gen.result(), HasSubstr("12345u"));
}
-TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec) {
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_F32) {
WrapInFunction(vec3<f32>(1_f, 2_f, 3_f));
GeneratorImpl& gen = Build();
@@ -106,7 +129,18 @@
EXPECT_THAT(gen.result(), HasSubstr("float3(1.0f, 2.0f, 3.0f)"));
}
-TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_Empty) {
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(vec3<f16>(1_h, 2_h, 3_h));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("half3(1.0h, 2.0h, 3.0h)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_Empty_F32) {
WrapInFunction(vec3<f32>());
GeneratorImpl& gen = Build();
@@ -115,26 +149,230 @@
EXPECT_THAT(gen.result(), HasSubstr("float3(0.0f)"));
}
-TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat) {
- WrapInFunction(Construct(ty.mat2x3<f32>(), vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(3_f, 4_f, 5_f)));
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_Empty_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(vec3<f16>());
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("half3(0.0h)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_F32_Literal) {
+ WrapInFunction(vec3<f32>(2_f));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("float3(2.0f)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_F16_Literal) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(vec3<f16>(2_h));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("half3(2.0h)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_F32_Var) {
+ auto* var = Var("v", nullptr, Expr(2_f));
+ auto* cast = vec3<f32>(var);
+ WrapInFunction(var, cast);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr(R"(float v = 2.0f;
+ float3 const tint_symbol = float3(v);)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_F16_Var) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = Var("v", nullptr, Expr(2_h));
+ auto* cast = vec3<f16>(var);
+ WrapInFunction(var, cast);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr(R"(half v = 2.0h;
+ half3 const tint_symbol = half3(v);)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_Bool) {
+ WrapInFunction(vec3<bool>(true));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("bool3(true)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_Int) {
+ WrapInFunction(vec3<i32>(2_i));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("int3(2)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_SingleScalar_UInt) {
+ WrapInFunction(vec3<u32>(2_u));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr("uint3(2u)"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_F32) {
+ WrapInFunction(mat2x3<f32>(vec3<f32>(1_f, 2_f, 3_f), vec3<f32>(3_f, 4_f, 5_f)));
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
- // A matrix of type T with n columns and m rows can also be constructed from
- // n vectors of type T with m components.
EXPECT_THAT(gen.result(),
HasSubstr("float2x3(float3(1.0f, 2.0f, 3.0f), float3(3.0f, 4.0f, 5.0f))"));
}
-TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Empty) {
- WrapInFunction(mat4x4<f32>());
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(mat2x3<f16>(vec3<f16>(1_h, 2_h, 3_h), vec3<f16>(3_h, 4_h, 5_h)));
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
- EXPECT_THAT(gen.result(), HasSubstr("float4x4(float4(0.0f), float4(0.0f)"));
+
+ EXPECT_THAT(gen.result(),
+ HasSubstr("half2x3(half3(1.0h, 2.0h, 3.0h), half3(3.0h, 4.0h, 5.0h))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Complex_F32) {
+ // mat4x4<f32>(
+ // vec4<f32>(2.0f, 3.0f, 4.0f, 8.0f),
+ // vec4<f32>(),
+ // vec4<f32>(7.0f),
+ // vec4<f32>(vec4<f32>(42.0f, 21.0f, 6.0f, -5.0f)),
+ // );
+ auto* vector_literal =
+ vec4<f32>(Expr(f32(2.0)), Expr(f32(3.0)), Expr(f32(4.0)), Expr(f32(8.0)));
+ auto* vector_zero_ctor = vec4<f32>();
+ auto* vector_single_scalar_ctor = vec4<f32>(Expr(f32(7.0)));
+ auto* vector_identical_ctor =
+ vec4<f32>(vec4<f32>(Expr(f32(42.0)), Expr(f32(21.0)), Expr(f32(6.0)), Expr(f32(-5.0))));
+
+ auto* constructor = mat4x4<f32>(vector_literal, vector_zero_ctor, vector_single_scalar_ctor,
+ vector_identical_ctor);
+
+ WrapInFunction(constructor);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(), HasSubstr("float4x4(float4(2.0f, 3.0f, 4.0f, 8.0f), float4(0.0f), "
+ "float4(7.0f), float4(42.0f, 21.0f, 6.0f, -5.0f))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Complex_F16) {
+ // mat4x4<f16>(
+ // vec4<f16>(2.0h, 3.0h, 4.0h, 8.0h),
+ // vec4<f16>(),
+ // vec4<f16>(7.0h),
+ // vec4<f16>(vec4<f16>(42.0h, 21.0h, 6.0h, -5.0h)),
+ // );
+ Enable(ast::Extension::kF16);
+
+ auto* vector_literal =
+ vec4<f16>(Expr(f16(2.0)), Expr(f16(3.0)), Expr(f16(4.0)), Expr(f16(8.0)));
+ auto* vector_zero_ctor = vec4<f16>();
+ auto* vector_single_scalar_ctor = vec4<f16>(Expr(f16(7.0)));
+ auto* vector_identical_ctor =
+ vec4<f16>(vec4<f16>(Expr(f16(42.0)), Expr(f16(21.0)), Expr(f16(6.0)), Expr(f16(-5.0))));
+
+ auto* constructor = mat4x4<f16>(vector_literal, vector_zero_ctor, vector_single_scalar_ctor,
+ vector_identical_ctor);
+
+ WrapInFunction(constructor);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(), HasSubstr("half4x4(half4(2.0h, 3.0h, 4.0h, 8.0h), half4(0.0h), "
+ "half4(7.0h), half4(42.0h, 21.0h, 6.0h, -5.0h))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Empty_F32) {
+ WrapInFunction(mat2x3<f32>());
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(),
+ HasSubstr("float2x3 const tint_symbol = float2x3(float3(0.0f), float3(0.0f))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Empty_F16) {
+ Enable(ast::Extension::kF16);
+
+ WrapInFunction(mat2x3<f16>());
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(),
+ HasSubstr("half2x3 const tint_symbol = half2x3(half3(0.0h), half3(0.0h))"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Identity_F32) {
+ // fn f() {
+ // var m_1: mat4x4<f32> = mat4x4<f32>();
+ // var m_2: mat4x4<f32> = mat4x4<f32>(m_1);
+ // }
+
+ auto* m_1 = Var("m_1", ty.mat4x4(ty.f32()), mat4x4<f32>());
+ auto* m_2 = Var("m_2", ty.mat4x4(ty.f32()), mat4x4<f32>(m_1));
+
+ WrapInFunction(m_1, m_2);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(), HasSubstr("float4x4 m_2 = float4x4(m_1);"));
+}
+
+TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Mat_Identity_F16) {
+ // fn f() {
+ // var m_1: mat4x4<f16> = mat4x4<f16>();
+ // var m_2: mat4x4<f16> = mat4x4<f16>(m_1);
+ // }
+
+ Enable(ast::Extension::kF16);
+
+ auto* m_1 = Var("m_1", ty.mat4x4(ty.f16()), mat4x4<f16>());
+ auto* m_2 = Var("m_2", ty.mat4x4(ty.f16()), mat4x4<f16>(m_1));
+
+ WrapInFunction(m_1, m_2);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_THAT(gen.result(), HasSubstr("half4x4 m_2 = half4x4(m_1);"));
}
TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Array) {
diff --git a/src/tint/writer/msl/generator_impl_module_constant_test.cc b/src/tint/writer/msl/generator_impl_module_constant_test.cc
index c204686..4834b3d 100644
--- a/src/tint/writer/msl/generator_impl_module_constant_test.cc
+++ b/src/tint/writer/msl/generator_impl_module_constant_test.cc
@@ -112,6 +112,26 @@
)");
}
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = GlobalConst("G", nullptr, Expr(1_h));
+ Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+ half const l = 1.0h;
+}
+
+)");
+}
+
TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_AInt) {
auto* var = GlobalConst("G", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
@@ -166,6 +186,26 @@
)");
}
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_vec3_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = GlobalConst("G", nullptr, vec3<f16>(1_h, 2_h, 3_h));
+ Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+ half3 const l = half3(1.0h, 2.0h, 3.0h);
+}
+
+)");
+}
+
TEST_F(MslGeneratorImplTest, Emit_GlobalConst_mat2x3_AFloat) {
auto* var = GlobalConst("G", nullptr,
Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
@@ -203,6 +243,26 @@
)");
}
+TEST_F(MslGeneratorImplTest, Emit_GlobalConst_mat2x3_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = GlobalConst("G", nullptr, mat2x3<f16>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
+ Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+ half2x3 const l = half2x3(half3(1.0h, 2.0h, 3.0h), half3(4.0h, 5.0h, 6.0h));
+}
+
+)");
+}
+
TEST_F(MslGeneratorImplTest, Emit_GlobalConst_arr_f32) {
auto* var = GlobalConst("G", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
Func("f", {}, ty.void_(), {Decl(Let("l", nullptr, Expr(var)))});
diff --git a/src/tint/writer/msl/generator_impl_type_test.cc b/src/tint/writer/msl/generator_impl_type_test.cc
index 070b1bb..4ed5a76 100644
--- a/src/tint/writer/msl/generator_impl_type_test.cc
+++ b/src/tint/writer/msl/generator_impl_type_test.cc
@@ -71,6 +71,18 @@
DECLARE_TYPE(float4x2, 32, 8);
DECLARE_TYPE(float4x3, 64, 16);
DECLARE_TYPE(float4x4, 64, 16);
+DECLARE_TYPE(half2, 4, 4);
+DECLARE_TYPE(packed_half3, 6, 2);
+DECLARE_TYPE(half4, 8, 8);
+DECLARE_TYPE(half2x2, 8, 4);
+DECLARE_TYPE(half2x3, 16, 8);
+DECLARE_TYPE(half2x4, 16, 8);
+DECLARE_TYPE(half3x2, 12, 4);
+DECLARE_TYPE(half3x3, 24, 8);
+DECLARE_TYPE(half3x4, 24, 8);
+DECLARE_TYPE(half4x2, 16, 4);
+DECLARE_TYPE(half4x3, 32, 8);
+DECLARE_TYPE(half4x4, 32, 8);
using uint = unsigned int;
using MslGeneratorImplTest = TestHelper;
@@ -153,6 +165,16 @@
EXPECT_EQ(out.str(), "float");
}
+TEST_F(MslGeneratorImplTest, EmitType_F16) {
+ auto* f16 = create<sem::F16>();
+
+ GeneratorImpl& gen = Build();
+
+ std::stringstream out;
+ ASSERT_TRUE(gen.EmitType(out, f16, "")) << gen.error();
+ EXPECT_EQ(out.str(), "half");
+}
+
TEST_F(MslGeneratorImplTest, EmitType_I32) {
auto* i32 = create<sem::I32>();
@@ -163,7 +185,7 @@
EXPECT_EQ(out.str(), "int");
}
-TEST_F(MslGeneratorImplTest, EmitType_Matrix) {
+TEST_F(MslGeneratorImplTest, EmitType_Matrix_F32) {
auto* f32 = create<sem::F32>();
auto* vec3 = create<sem::Vector>(f32, 3u);
auto* mat2x3 = create<sem::Matrix>(vec3, 2u);
@@ -175,6 +197,18 @@
EXPECT_EQ(out.str(), "float2x3");
}
+TEST_F(MslGeneratorImplTest, EmitType_Matrix_F16) {
+ auto* f16 = create<sem::F16>();
+ auto* vec3 = create<sem::Vector>(f16, 3u);
+ auto* mat2x3 = create<sem::Matrix>(vec3, 2u);
+
+ GeneratorImpl& gen = Build();
+
+ std::stringstream out;
+ ASSERT_TRUE(gen.EmitType(out, mat2x3, "")) << gen.error();
+ EXPECT_EQ(out.str(), "half2x3");
+}
+
TEST_F(MslGeneratorImplTest, EmitType_Pointer) {
auto* f32 = create<sem::F32>();
auto* p = create<sem::Pointer>(f32, ast::StorageClass::kWorkgroup, ast::Access::kReadWrite);
diff --git a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
index 46becb0..10c5b3d 100644
--- a/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/tint/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -154,6 +154,26 @@
)");
}
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* C = Const("C", nullptr, Expr(1_h));
+ Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+ half const l = 1.0h;
+}
+
+)");
+}
+
TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_AInt) {
auto* C = Const("C", nullptr, Construct(ty.vec3(nullptr), 1_a, 2_a, 3_a));
Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
@@ -208,6 +228,26 @@
)");
}
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_vec3_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* C = Const("C", nullptr, vec3<f16>(1_h, 2_h, 3_h));
+ Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+ half3 const l = half3(1.0h, 2.0h, 3.0h);
+}
+
+)");
+}
+
TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_AFloat) {
auto* C =
Const("C", nullptr, Construct(ty.mat(nullptr, 2, 3), 1._a, 2._a, 3._a, 4._a, 5._a, 6._a));
@@ -245,6 +285,26 @@
)");
}
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_mat2x3_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* C = Const("C", nullptr, mat2x3<f16>(1_h, 2_h, 3_h, 4_h, 5_h, 6_h));
+ Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+void f() {
+ half2x3 const l = half2x3(half3(1.0h, 2.0h, 3.0h), half3(4.0h, 5.0h, 6.0h));
+}
+
+)");
+}
+
TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const_arr_f32) {
auto* C = Const("C", nullptr, Construct(ty.array<f32, 3>(), 1_f, 2_f, 3_f));
Func("f", {}, ty.void_(), {Decl(C), Decl(Let("l", nullptr, Expr(C)))});
@@ -343,7 +403,7 @@
)");
}
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Vector) {
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Vector_f32) {
auto* var = Var("a", ty.vec2<f32>());
auto* stmt = Decl(var);
WrapInFunction(stmt);
@@ -356,7 +416,22 @@
EXPECT_EQ(gen.result(), " float2 a = 0.0f;\n");
}
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Matrix) {
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Vector_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = Var("a", ty.vec2<f16>());
+ auto* stmt = Decl(var);
+ WrapInFunction(stmt);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+ EXPECT_EQ(gen.result(), " half2 a = 0.0h;\n");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Matrix_f32) {
auto* var = Var("a", ty.mat3x2<f32>());
auto* stmt = Decl(var);
@@ -370,6 +445,80 @@
EXPECT_EQ(gen.result(), " float3x2 a = float3x2(0.0f);\n");
}
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Matrix_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = Var("a", ty.mat3x2<f16>());
+
+ auto* stmt = Decl(var);
+ WrapInFunction(stmt);
+
+ GeneratorImpl& gen = Build();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+ EXPECT_EQ(gen.result(), " half3x2 a = half3x2(0.0h);\n");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroVec_f32) {
+ auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, vec3<f32>());
+
+ auto* stmt = Decl(var);
+ WrapInFunction(stmt);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+ EXPECT_EQ(gen.result(), R"(float3 a = float3(0.0f);
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroVec_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = Var("a", ty.vec3<f16>(), ast::StorageClass::kNone, vec3<f16>());
+
+ auto* stmt = Decl(var);
+ WrapInFunction(stmt);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+ EXPECT_EQ(gen.result(), R"(half3 a = half3(0.0h);
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroMat_f32) {
+ auto* var = Var("a", ty.mat2x3<f32>(), ast::StorageClass::kNone, mat2x3<f32>());
+
+ auto* stmt = Decl(var);
+ WrapInFunction(stmt);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+ EXPECT_EQ(gen.result(),
+ R"(float2x3 a = float2x3(float3(0.0f), float3(0.0f));
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroMat_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* var = Var("a", ty.mat2x3<f16>(), ast::StorageClass::kNone, mat2x3<f16>());
+
+ auto* stmt = Decl(var);
+ WrapInFunction(stmt);
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
+ EXPECT_EQ(gen.result(),
+ R"(half2x3 a = half2x3(half3(0.0h), half3(0.0h));
+)");
+}
+
TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Private) {
GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate);
@@ -396,19 +545,5 @@
EXPECT_THAT(gen.result(), HasSubstr("threadgroup float tint_symbol_2;\n"));
}
-TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroVec) {
- auto* zero_vec = vec3<f32>();
-
- auto* var = Var("a", ty.vec3<f32>(), ast::StorageClass::kNone, zero_vec);
- auto* stmt = Decl(var);
- WrapInFunction(stmt);
-
- GeneratorImpl& gen = Build();
-
- ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
- EXPECT_EQ(gen.result(), R"(float3 a = float3(0.0f);
-)");
-}
-
} // namespace
} // namespace tint::writer::msl
diff --git a/src/tint/writer/text_generator.cc b/src/tint/writer/text_generator.cc
index 1a1fa5a..3dabb5c 100644
--- a/src/tint/writer/text_generator.cc
+++ b/src/tint/writer/text_generator.cc
@@ -84,12 +84,13 @@
<< " lines.size(): " << lines.size();
return;
}
- lines.insert(lines.begin() + static_cast<int64_t>(before), Line{indent, line});
+ using DT = decltype(lines)::difference_type;
+ lines.insert(lines.begin() + static_cast<DT>(before), Line{indent, line});
}
void TextGenerator::TextBuffer::Append(const TextBuffer& tb) {
for (auto& line : tb.lines) {
- // TODO(bclayton): inefficent, consider optimizing
+ // TODO(bclayton): inefficient, consider optimizing
lines.emplace_back(Line{current_indent + line.indent, line.content});
}
}
@@ -104,8 +105,9 @@
}
size_t idx = 0;
for (auto& line : tb.lines) {
- // TODO(bclayton): inefficent, consider optimizing
- lines.insert(lines.begin() + static_cast<int64_t>(before + idx),
+ // TODO(bclayton): inefficient, consider optimizing
+ using DT = decltype(lines)::difference_type;
+ lines.insert(lines.begin() + static_cast<DT>(before + idx),
Line{indent + line.indent, line.content});
idx++;
}