tint: Implement `f16` keyword in Tint frontend
This patch:
1. Add the `f16` WGSL extension.
2. Add `f16` as keyword, and remove it from reserved word list.
3. Add ast::f16 and sem::f16, and implement validation that using `f16`
type must be with `f16` extension enabled.
4. Add `Number<NumberKindF16>` for f16 literal and constant, and add
`ast::FloatLiteralExpression::Suffix::kH`.
5. Add placeholder in all writer which report error when try to emit f16
type.
Bugs: tint:1473, tint:1502
Change-Id: Ifb363beeb2699ed7cac57e07227d1b2cfa8050b4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/89922
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 4a22a67..9a78107 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -228,6 +228,8 @@
"ast/expression.h",
"ast/external_texture.cc",
"ast/external_texture.h",
+ "ast/f16.cc",
+ "ast/f16.h",
"ast/f32.cc",
"ast/f32.h",
"ast/fallthrough_statement.cc",
@@ -342,7 +344,6 @@
"debug.h",
"demangler.cc",
"demangler.h",
- "number.h",
"diagnostic/diagnostic.cc",
"diagnostic/diagnostic.h",
"diagnostic/formatter.cc",
@@ -357,6 +358,7 @@
"inspector/resource_binding.h",
"inspector/scalar.cc",
"inspector/scalar.h",
+ "number.h",
"program.cc",
"program.h",
"program_builder.cc",
@@ -394,6 +396,7 @@
"sem/depth_texture.h",
"sem/expression.h",
"sem/external_texture.h",
+ "sem/f16.h",
"sem/f32.h",
"sem/for_loop_statement.h",
"sem/i32.h",
@@ -591,6 +594,8 @@
"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",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index d2273fd..d492595 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -116,6 +116,8 @@
ast/expression.h
ast/external_texture.cc
ast/external_texture.h
+ ast/f16.cc
+ ast/f16.h
ast/f32.cc
ast/f32.h
ast/fallthrough_statement.cc
@@ -403,6 +405,8 @@
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
@@ -686,6 +690,7 @@
ast/discard_statement_test.cc
ast/enable_test.cc
ast/external_texture_test.cc
+ ast/f16_test.cc
ast/f32_test.cc
ast/fallthrough_statement_test.cc
ast/float_literal_expression_test.cc
@@ -790,6 +795,7 @@
sem/depth_multisampled_texture_test.cc
sem/depth_texture_test.cc
sem/external_texture_test.cc
+ sem/f16_test.cc
sem/f32_test.cc
sem/i32_test.cc
sem/matrix_test.cc
diff --git a/src/tint/ast/enable.cc b/src/tint/ast/enable.cc
index a611cfb..200c2be 100644
--- a/src/tint/ast/enable.cc
+++ b/src/tint/ast/enable.cc
@@ -28,6 +28,9 @@
if (name == "chromium_disable_uniformity_analysis") {
return Enable::ExtensionKind::kChromiumDisableUniformityAnalysis;
}
+ if (name == "f16") {
+ return Enable::ExtensionKind::kF16;
+ }
// The reserved internal extension name for testing
if (name == "InternalExtensionForTesting") {
@@ -43,7 +46,8 @@
return "chromium_experimental_dp4a";
case ExtensionKind::kChromiumDisableUniformityAnalysis:
return "chromium_disable_uniformity_analysis";
-
+ case ExtensionKind::kF16:
+ return "f16";
// The reserved internal extension for testing
case ExtensionKind::kInternalExtensionForTesting:
return "InternalExtensionForTesting";
diff --git a/src/tint/ast/enable.h b/src/tint/ast/enable.h
index 69622e0..f190b0a 100644
--- a/src/tint/ast/enable.h
+++ b/src/tint/ast/enable.h
@@ -35,6 +35,8 @@
/// An internal reserved extension for test, named
/// "InternalExtensionForTesting".
kInternalExtensionForTesting,
+ /// WGSL Extension "f16"
+ kF16,
/// An extension for the experimental feature
/// "chromium_experimental_dp4a".
diff --git a/src/tint/ast/f16.cc b/src/tint/ast/f16.cc
new file mode 100644
index 0000000..0eb1be5
--- /dev/null
+++ b/src/tint/ast/f16.cc
@@ -0,0 +1,38 @@
+// 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/ast/f16.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::F16);
+
+namespace tint::ast {
+
+F16::F16(ProgramID pid, const Source& src) : Base(pid, src) {}
+
+F16::F16(F16&&) = default;
+
+F16::~F16() = default;
+
+std::string F16::FriendlyName(const SymbolTable&) const {
+ return "f16";
+}
+
+const F16* F16::Clone(CloneContext* ctx) const {
+ auto src = ctx->Clone(source);
+ return ctx->dst->create<F16>(src);
+}
+
+} // namespace tint::ast
diff --git a/src/tint/ast/f16.h b/src/tint/ast/f16.h
new file mode 100644
index 0000000..1b84f09
--- /dev/null
+++ b/src/tint/ast/f16.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef SRC_TINT_AST_F16_H_
+#define SRC_TINT_AST_F16_H_
+
+#include <string>
+
+#include "src/tint/ast/type.h"
+
+namespace tint::ast {
+
+/// A float 16 type
+class F16 : public Castable<F16, Type> {
+ public:
+ /// Constructor
+ /// @param pid the identifier of the program that owns this node
+ /// @param src the source of this node
+ F16(ProgramID pid, const Source& src);
+ /// Move constructor
+ F16(F16&&);
+ ~F16() override;
+
+ /// @param symbols the program's symbol table
+ /// @returns the name for this type that closely resembles how it would be
+ /// declared in WGSL.
+ std::string FriendlyName(const SymbolTable& symbols) const override;
+
+ /// Clones this type and all transitive types using the `CloneContext` `ctx`.
+ /// @param ctx the clone context
+ /// @return the newly cloned type
+ const F16* Clone(CloneContext* ctx) const override;
+};
+
+} // namespace tint::ast
+
+#endif // SRC_TINT_AST_F16_H_
diff --git a/src/tint/ast/f16_test.cc b/src/tint/ast/f16_test.cc
new file mode 100644
index 0000000..48ab284
--- /dev/null
+++ b/src/tint/ast/f16_test.cc
@@ -0,0 +1,30 @@
+// 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/ast/f16.h"
+
+#include "src/tint/ast/test_helper.h"
+
+namespace tint::ast {
+namespace {
+
+using AstF16Test = TestHelper;
+
+TEST_F(AstF16Test, FriendlyName) {
+ auto* f = create<F16>();
+ EXPECT_EQ(f->FriendlyName(Symbols()), "f16");
+}
+
+} // namespace
+} // namespace tint::ast
diff --git a/src/tint/ast/float_literal_expression.h b/src/tint/ast/float_literal_expression.h
index 321efc8..72a395f 100644
--- a/src/tint/ast/float_literal_expression.h
+++ b/src/tint/ast/float_literal_expression.h
@@ -30,6 +30,8 @@
kNone,
/// 'f' suffix (f32)
kF,
+ /// 'h' suffix (f16)
+ kH,
};
/// Constructor
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index 36fcfb7..2bfe815 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -27,6 +27,7 @@
#include "src/tint/sem/array.h"
#include "src/tint/sem/call.h"
#include "src/tint/sem/depth_multisampled_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"
diff --git a/src/tint/number.h b/src/tint/number.h
index a4bd369..95a07e6 100644
--- a/src/tint/number.h
+++ b/src/tint/number.h
@@ -18,6 +18,12 @@
#include <stdint.h>
#include <functional>
+namespace tint::detail {
+/// An empty structure used as a unique template type for Number when
+/// specializing for the f16 type.
+struct NumberKindF16 {};
+} // namespace tint::detail
+
namespace tint {
/// Number wraps a integer or floating point number, enforcing explicit casting.
@@ -72,6 +78,43 @@
return Number<A>(a) == b;
}
+/// The partial specification of Number for f16 type, storing the f16 value as float,
+/// and enforcing proper explicit casting.
+template <>
+struct Number<detail::NumberKindF16> {
+ /// Constructor. The value is zero-initialized.
+ Number() = default;
+
+ /// Constructor.
+ /// @param v the value to initialize this Number to
+ template <typename U>
+ explicit Number(U v) : value(static_cast<float>(v)) {}
+
+ /// Constructor.
+ /// @param v the value to initialize this Number to
+ template <typename U>
+ explicit Number(Number<U> v) : value(static_cast<float>(v.value)) {}
+
+ /// Conversion operator
+ /// @returns the value as the internal representation type of F16
+ operator float() const { return value; }
+
+ /// Negation operator
+ /// @returns the negative value of the number
+ Number operator-() const { return Number<detail::NumberKindF16>(-value); }
+
+ /// Assignment operator with parameter as native floating point type
+ /// @param v the new value
+ /// @returns this Number so calls can be chained
+ Number& operator=(float v) {
+ value = v;
+ return *this;
+ }
+
+ /// The number value, stored as float
+ float value = {};
+};
+
/// `AInt` is a type alias to `Number<int64_t>`.
using AInt = Number<int64_t>;
/// `AFloat` is a type alias to `Number<double>`.
@@ -83,6 +126,9 @@
using u32 = Number<uint32_t>;
/// `f32` is a type alias to `Number<float>`
using f32 = Number<float>;
+/// `f16` is a type alias to `Number<detail::NumberKindF16>`, which should be IEEE 754 binary16.
+/// However since C++ don't have native binary16 type, the value is stored as float.
+using f16 = Number<detail::NumberKindF16>;
} // namespace tint
@@ -118,6 +164,16 @@
return f32(static_cast<double>(value));
}
+/// Literal suffix for f16 literals
+inline f16 operator"" _h(long double value) { // NOLINT
+ return f16(static_cast<double>(value));
+}
+
+/// Literal suffix for f16 literals
+inline f16 operator"" _h(unsigned long long int value) { // NOLINT
+ return f16(static_cast<double>(value));
+}
+
} // namespace tint::number_suffixes
#endif // SRC_TINT_NUMBER_H_
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 7dd2c81..a91d87c 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -40,6 +40,7 @@
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/enable.h"
#include "src/tint/ast/external_texture.h"
+#include "src/tint/ast/f16.h"
#include "src/tint/ast/f32.h"
#include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/float_literal_expression.h"
@@ -82,6 +83,7 @@
#include "src/tint/sem/bool.h"
#include "src/tint/sem/depth_texture.h"
#include "src/tint/sem/external_texture.h"
+#include "src/tint/sem/f16.h"
#include "src/tint/sem/f32.h"
#include "src/tint/sem/i32.h"
#include "src/tint/sem/matrix.h"
@@ -385,6 +387,15 @@
return builder->create<ast::Bool>(source);
}
+ /// @returns a f16 type
+ const ast::F16* f16() const { return builder->create<ast::F16>(); }
+
+ /// @param source the Source of the node
+ /// @returns a f16 type
+ const ast::F16* f16(const Source& source) const {
+ return builder->create<ast::F16>(source);
+ }
+
/// @returns a f32 type
const ast::F32* f32() const { return builder->create<ast::F32>(); }
@@ -1005,6 +1016,21 @@
}
/// @param source the source information
+ /// @param value the float value
+ /// @return a 'h'-suffixed FloatLiteralExpression for the f16 value
+ const ast::FloatLiteralExpression* Expr(const Source& source, f16 value) {
+ return create<ast::FloatLiteralExpression>(source, static_cast<double>(value.value),
+ ast::FloatLiteralExpression::Suffix::kH);
+ }
+
+ /// @param value the float value
+ /// @return a 'h'-suffixed FloatLiteralExpression for the f16 value
+ const ast::FloatLiteralExpression* Expr(f16 value) {
+ return create<ast::FloatLiteralExpression>(static_cast<double>(value.value),
+ ast::FloatLiteralExpression::Suffix::kH);
+ }
+
+ /// @param source the source information
/// @param value the integer value
/// @return an unsuffixed IntLiteralExpression for the AInt value
const ast::IntLiteralExpression* Expr(const Source& source, AInt value) {
@@ -2675,6 +2701,10 @@
static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->f32(); }
};
template <>
+struct ProgramBuilder::TypesBuilder::CToAST<f16> {
+ static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->f16(); }
+};
+template <>
struct ProgramBuilder::TypesBuilder::CToAST<bool> {
static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->bool_(); }
};
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index db75613..4b5f955 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -1067,6 +1067,8 @@
return {Token::Type::kElse, source, "else"};
if (str == "enable")
return {Token::Type::kEnable, source, "enable"};
+ if (str == "f16")
+ return {Token::Type::kF16, source, "f16"};
if (str == "f32")
return {Token::Type::kF32, source, "f32"};
if (str == "fallthrough")
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index e684790..abd97e5 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -124,8 +124,8 @@
// https://gpuweb.github.io/gpuweb/wgsl.html#reserved-keywords
bool is_reserved(Token t) {
- return t == "asm" || t == "bf16" || t == "const" || t == "do" || t == "enum" || t == "f16" ||
- t == "f64" || t == "handle" || t == "i8" || t == "i16" || t == "i64" || t == "mat" ||
+ return t == "asm" || t == "bf16" || t == "const" || t == "do" || t == "enum" || t == "f64" ||
+ t == "handle" || t == "i8" || t == "i16" || t == "i64" || t == "mat" ||
t == "premerge" || t == "regardless" || t == "typedef" || t == "u8" || t == "u16" ||
t == "u64" || t == "unless" || t == "using" || t == "vec" || t == "void" || t == "while";
}
@@ -310,6 +310,9 @@
if (after_global_decl) {
add_error(p, "enable directives must come before all global declarations");
}
+ } else if (ed.errored) {
+ // Found a invalid enable directive.
+ continue;
} else {
auto gd = global_decl();
@@ -345,6 +348,11 @@
synchronized_ = true;
next();
name = {t.to_str(), t.source()};
+ } else if (t.Is(Token::Type::kF16)) {
+ // `f16` is a valid extension name and also a keyword
+ synchronized_ = true;
+ next();
+ name = {"f16", t.source()};
} else if (handle_error(t)) {
// The token might itself be an error.
return Failure::kErrored;
@@ -976,6 +984,9 @@
if (match(Token::Type::kBool, &source))
return builder_.ty.bool_(source);
+ if (match(Token::Type::kF16, &source))
+ return builder_.ty.f16(source);
+
if (match(Token::Type::kF32, &source))
return builder_.ty.f32(source);
diff --git a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
index 3226ef5..e840af7 100644
--- a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
@@ -88,7 +88,6 @@
"const",
"do",
"enum",
- "f16",
"f64",
"handle",
"i8",
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
index a721d77..bc2bfe5 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
@@ -55,6 +55,17 @@
EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 5u}}));
}
+TEST_F(ParserImplTest, TypeDecl_F16) {
+ auto p = parser("f16");
+
+ auto t = p->type_decl();
+ EXPECT_TRUE(t.matched);
+ EXPECT_FALSE(t.errored);
+ ASSERT_NE(t.value, nullptr) << p->error();
+ ASSERT_TRUE(t.value->Is<ast::F16>());
+ EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 4u}}));
+}
+
TEST_F(ParserImplTest, TypeDecl_F32) {
auto p = parser("f32");
diff --git a/src/tint/reader/wgsl/token.cc b/src/tint/reader/wgsl/token.cc
index 06eb6fb..4680eee 100644
--- a/src/tint/reader/wgsl/token.cc
+++ b/src/tint/reader/wgsl/token.cc
@@ -151,6 +151,8 @@
return "else";
case Token::Type::kEnable:
return "enable";
+ case Token::Type::kF16:
+ return "f16";
case Token::Type::kF32:
return "f32";
case Token::Type::kFallthrough:
diff --git a/src/tint/reader/wgsl/token.h b/src/tint/reader/wgsl/token.h
index 82b3fab..0a68f9b0 100644
--- a/src/tint/reader/wgsl/token.h
+++ b/src/tint/reader/wgsl/token.h
@@ -162,6 +162,8 @@
kElse,
/// A 'enable'
kEnable,
+ /// A 'f16'
+ kF16,
/// A 'f32'
kF32,
/// A 'fallthrough'
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 94efafb..7e66899 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -342,7 +342,7 @@
TraverseType(tex->type);
},
[&](Default) {
- if (!ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F32,
+ if (!ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F16, ast::F32,
ast::DepthTexture, ast::DepthMultisampledTexture,
ast::StorageTexture, ast::ExternalTexture, ast::Sampler>()) {
UnhandledNode(diagnostics_, ty);
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 962dd02..38a5025 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -172,6 +172,14 @@
[&](const ast::Bool*) { return builder_->create<sem::Bool>(); },
[&](const ast::I32*) { return builder_->create<sem::I32>(); },
[&](const ast::U32*) { return builder_->create<sem::U32>(); },
+ [&](const ast::F16* t) -> sem::F16* {
+ // Validate if f16 type is allowed.
+ if (builder_->AST().Extensions().count(ast::Enable::ExtensionKind::kF16) == 0) {
+ AddError("f16 used without 'f16' extension enabled", t->source);
+ return nullptr;
+ }
+ return builder_->create<sem::F16>();
+ },
[&](const ast::F32*) { return builder_->create<sem::F32>(); },
[&](const ast::Vector* t) -> sem::Vector* {
if (!t->type) {
diff --git a/src/tint/resolver/resolver_constants.cc b/src/tint/resolver/resolver_constants.cc
index 2a8a1d1..3ec6f94 100644
--- a/src/tint/resolver/resolver_constants.cc
+++ b/src/tint/resolver/resolver_constants.cc
@@ -70,6 +70,10 @@
if (elem_type->Is<sem::U32>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, 0_u));
}
+ // Add f16 zero scalar here
+ if (elem_type->Is<sem::F16>()) {
+ return sem::Constant(type, sem::Constant::Scalars(result_size, f16{0.f}));
+ }
if (elem_type->Is<sem::F32>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, 0_f));
}
@@ -120,6 +124,11 @@
return u32(static_cast<uint32_t>(s));
});
},
+ [&](const sem::F16*) {
+ return value.WithScalarAt(i, [](auto&& s) { //
+ return f16{static_cast<float>(s)};
+ });
+ },
[&](const sem::F32*) {
return value.WithScalarAt(i, [](auto&& s) { //
return static_cast<f32>(s);
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 90e18e1..4c2f56d 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -662,6 +662,24 @@
EXPECT_EQ(r()->error(), "error: cannot use builtin 'max' as type");
}
+TEST_F(ResolverTypeValidationTest, F16TypeUsedWithExtension) {
+ // enable f16;
+ // var<private> v : f16;
+ auto* ext = create<ast::Enable>("f16");
+ AST().AddEnable(ext);
+ Global("v", ty.f16(), ast::StorageClass::kPrivate);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverTypeValidationTest, F16TypeUsedWithoutExtension) {
+ // var<private> v : f16;
+ Global("v", ty.f16(), ast::StorageClass::kPrivate);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "error: f16 used without 'f16' extension enabled");
+}
+
namespace GetCanonicalTests {
struct Params {
builder::ast_type_func_ptr create_ast_type;
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 7a66ec4..df9c7c0 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -194,7 +194,7 @@
// https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
bool Validator::IsHostShareable(const sem::Type* type) const {
- if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
+ if (type->IsAnyOf<sem::I32, sem::U32, sem::F32, sem::F16>()) {
return true;
}
return Switch(
@@ -1890,11 +1890,13 @@
using Bool = sem::Bool;
using I32 = sem::I32;
using U32 = sem::U32;
+ using F16 = sem::F16;
using F32 = sem::F32;
const bool is_valid =
(ty->Is<Bool>() && value_ty->is_scalar()) || (ty->Is<I32>() && value_ty->is_scalar()) ||
- (ty->Is<U32>() && value_ty->is_scalar()) || (ty->Is<F32>() && value_ty->is_scalar());
+ (ty->Is<U32>() && value_ty->is_scalar()) || (ty->Is<F16>() && value_ty->is_scalar()) ||
+ (ty->Is<F32>() && value_ty->is_scalar());
if (!is_valid) {
AddError("cannot construct '" + sem_.TypeNameOf(ty) + "' with a value of type '" +
sem_.TypeNameOf(value_ty) + "'",
diff --git a/src/tint/sem/constant.h b/src/tint/sem/constant.h
index 673446f..2e8258d 100644
--- a/src/tint/sem/constant.h
+++ b/src/tint/sem/constant.h
@@ -35,6 +35,8 @@
tint::u32 u32;
/// The scalar value as a f32
tint::f32 f32;
+ /// The scalar value as a f16, internally stored as float
+ tint::f16 f16;
/// The scalar value as a bool
bool bool_;
@@ -50,6 +52,10 @@
/// @param v the value of the Scalar
Scalar(tint::f32 v) : f32(v) {} // NOLINT
+ /// Constructs the scalar with the f16 value `v`
+ /// @param v the value of the Scalar
+ Scalar(tint::f16 v) : f16({v}) {} // NOLINT
+
/// Constructs the scalar with the bool value `v`
/// @param v the value of the Scalar
Scalar(bool v) : bool_(v) {} // NOLINT
@@ -106,6 +112,7 @@
ElementType(), //
[&](const I32*) { return func(elems_[index].i32); },
[&](const U32*) { return func(elems_[index].u32); },
+ [&](const F16*) { return func(elems_[index].f16); },
[&](const F32*) { return func(elems_[index].f32); },
[&](const Bool*) { return func(elems_[index].bool_); },
[&](Default) {
diff --git a/src/tint/sem/f16.cc b/src/tint/sem/f16.cc
new file mode 100644
index 0000000..7da65fa
--- /dev/null
+++ b/src/tint/sem/f16.cc
@@ -0,0 +1,55 @@
+// 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/f16.h"
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::sem::F16);
+
+namespace tint {
+namespace sem {
+
+F16::F16() = default;
+
+F16::F16(F16&&) = default;
+
+F16::~F16() = default;
+
+size_t F16::Hash() const {
+ return static_cast<size_t>(TypeInfo::Of<F16>().full_hashcode);
+}
+
+bool F16::Equals(const Type& other) const {
+ return other.Is<F16>();
+}
+
+std::string F16::FriendlyName(const SymbolTable&) const {
+ return "f16";
+}
+
+bool F16::IsConstructible() const {
+ return true;
+}
+
+uint32_t F16::Size() const {
+ return 2;
+}
+
+uint32_t F16::Align() const {
+ return 2;
+}
+
+} // namespace sem
+} // namespace tint
diff --git a/src/tint/sem/f16.h b/src/tint/sem/f16.h
new file mode 100644
index 0000000..72984c1
--- /dev/null
+++ b/src/tint/sem/f16.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef SRC_TINT_SEM_F16_H_
+#define SRC_TINT_SEM_F16_H_
+
+#include <string>
+
+#include "src/tint/sem/type.h"
+
+namespace tint::sem {
+
+/// A float 16 type
+class F16 : public Castable<F16, Type> {
+ public:
+ /// Constructor
+ F16();
+ /// Move constructor
+ F16(F16&&);
+ ~F16() override;
+
+ /// @returns a hash of the type.
+ size_t Hash() const override;
+
+ /// @param other the other type to compare against
+ /// @returns true if the this type is equal to the given type
+ bool Equals(const Type& other) const override;
+
+ /// @param symbols the program's symbol table
+ /// @returns the name for this type that closely resembles how it would be
+ /// declared in WGSL.
+ std::string FriendlyName(const SymbolTable& symbols) const override;
+
+ /// @returns true if constructible as per
+ /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
+ bool IsConstructible() const override;
+
+ /// @returns the size in bytes of the type.
+ uint32_t Size() const override;
+
+ /// @returns the alignment in bytes of the type.
+ uint32_t Align() const override;
+};
+
+} // namespace tint::sem
+
+#endif // SRC_TINT_SEM_F16_H_
diff --git a/src/tint/sem/f16_test.cc b/src/tint/sem/f16_test.cc
new file mode 100644
index 0000000..28fd0da
--- /dev/null
+++ b/src/tint/sem/f16_test.cc
@@ -0,0 +1,48 @@
+// 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/test_helper.h"
+#include "src/tint/sem/texture.h"
+
+namespace tint::sem {
+namespace {
+
+using F16Test = TestHelper;
+
+TEST_F(F16Test, Creation) {
+ auto* a = create<F16>();
+ auto* b = create<F16>();
+ EXPECT_EQ(a, b);
+}
+
+TEST_F(F16Test, Hash) {
+ auto* a = create<F16>();
+ auto* b = create<F16>();
+ EXPECT_EQ(a->Hash(), b->Hash());
+}
+
+TEST_F(F16Test, Equals) {
+ auto* a = create<F16>();
+ auto* b = create<F16>();
+ EXPECT_TRUE(a->Equals(*b));
+ EXPECT_FALSE(a->Equals(Void{}));
+}
+
+TEST_F(F16Test, FriendlyName) {
+ F16 f;
+ EXPECT_EQ(f.FriendlyName(Symbols()), "f16");
+}
+
+} // namespace
+} // namespace tint::sem
diff --git a/src/tint/sem/type.cc b/src/tint/sem/type.cc
index 5776ff4..0ee138d 100644
--- a/src/tint/sem/type.cc
+++ b/src/tint/sem/type.cc
@@ -15,6 +15,7 @@
#include "src/tint/sem/type.h"
#include "src/tint/sem/bool.h"
+#include "src/tint/sem/f16.h"
#include "src/tint/sem/f32.h"
#include "src/tint/sem/i32.h"
#include "src/tint/sem/matrix.h"
@@ -64,15 +65,15 @@
}
bool Type::is_scalar() const {
- return IsAnyOf<F32, U32, I32, Bool>();
+ return IsAnyOf<F16, F32, U32, I32, Bool>();
}
bool Type::is_numeric_scalar() const {
- return IsAnyOf<F32, U32, I32>();
+ return IsAnyOf<F16, F32, U32, I32>();
}
bool Type::is_float_scalar() const {
- return Is<F32>();
+ return IsAnyOf<F16, F32>();
}
bool Type::is_float_matrix() const {
diff --git a/src/tint/transform/transform.cc b/src/tint/transform/transform.cc
index e3d0ea9..f3ab173 100644
--- a/src/tint/transform/transform.cc
+++ b/src/tint/transform/transform.cc
@@ -87,6 +87,9 @@
if (ty->Is<sem::U32>()) {
return ctx.dst->create<ast::U32>();
}
+ if (ty->Is<sem::F16>()) {
+ return ctx.dst->create<ast::F16>();
+ }
if (ty->Is<sem::F32>()) {
return ctx.dst->create<ast::F32>();
}
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 98066d2..9745079 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -2565,6 +2565,9 @@
out << "bool";
} 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;
} else if (type->Is<sem::I32>()) {
out << "int";
} else if (auto* mat = type->As<sem::Matrix>()) {
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 1a16079..fb4efb6 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -3513,6 +3513,11 @@
out << "float";
return true;
},
+ [&](const sem::F16*) {
+ diagnostics_.add_error(diag::System::Writer,
+ "Type f16 is not completely implemented yet.");
+ return false;
+ },
[&](const sem::I32*) {
out << "int";
return true;
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 8e8dc7a..2f87cb2 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -1467,6 +1467,12 @@
out << "false";
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;
+ },
[&](const sem::F32*) {
out << "0.0f";
return true;
@@ -2239,6 +2245,11 @@
out << "bool";
return true;
},
+ [&](const sem::F16*) {
+ diagnostics_.add_error(diag::System::Writer,
+ "Type f16 is not completely implemented yet");
+ return false;
+ },
[&](const sem::F32*) {
out << "float";
return true;
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 7d232c0..aaa64ce 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -1628,6 +1628,8 @@
constant.kind = ScalarConstant::Kind::kF32;
constant.value.f32 = static_cast<float>(f->value);
return;
+ case ast::FloatLiteralExpression::Suffix::kH:
+ error_ = "Type f16 is not completely implemented yet";
}
},
[&](Default) { error_ = "unknown literal type"; });
@@ -3672,6 +3674,11 @@
push_type(spv::Op::OpTypeFloat, {result, Operand(32u)});
return true;
},
+ [&](const sem::F16*) {
+ // Should be `push_type(spv::Op::OpTypeFloat, {result, Operand(16u)});`
+ error_ = "Type f16 is not completely implemented yet.";
+ return false;
+ },
[&](const sem::I32*) {
push_type(spv::Op::OpTypeInt, {result, Operand(32u), Operand(1u)});
return true;
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 3ef6269..121d904 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -405,6 +405,11 @@
out << "f32";
return true;
},
+ [&](const ast::F16*) {
+ diagnostics_.add_error(diag::System::Writer,
+ "Type f16 is not completely implemented yet.");
+ return false;
+ },
[&](const ast::I32*) {
out << "i32";
return true;
diff --git a/test/tint/BUILD.gn b/test/tint/BUILD.gn
index e17c6c7..87e26f2 100644
--- a/test/tint/BUILD.gn
+++ b/test/tint/BUILD.gn
@@ -165,6 +165,7 @@
"../../src/tint/ast/discard_statement_test.cc",
"../../src/tint/ast/enable_test.cc",
"../../src/tint/ast/external_texture_test.cc",
+ "../../src/tint/ast/f16_test.cc",
"../../src/tint/ast/f32_test.cc",
"../../src/tint/ast/fallthrough_statement_test.cc",
"../../src/tint/ast/float_literal_expression_test.cc",
@@ -289,6 +290,7 @@
"../../src/tint/sem/depth_multisampled_texture_test.cc",
"../../src/tint/sem/depth_texture_test.cc",
"../../src/tint/sem/external_texture_test.cc",
+ "../../src/tint/sem/f16_test.cc",
"../../src/tint/sem/f32_test.cc",
"../../src/tint/sem/i32_test.cc",
"../../src/tint/sem/matrix_test.cc",