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>
This commit is contained in:
Zhaoming Jiang 2022-05-13 12:01:11 +00:00 committed by Dawn LUCI CQ
parent e221ddd30e
commit 62bfd318ae
34 changed files with 502 additions and 11 deletions

View File

@ -228,6 +228,8 @@ libtint_source_set("libtint_core_all_src") {
"ast/expression.h", "ast/expression.h",
"ast/external_texture.cc", "ast/external_texture.cc",
"ast/external_texture.h", "ast/external_texture.h",
"ast/f16.cc",
"ast/f16.h",
"ast/f32.cc", "ast/f32.cc",
"ast/f32.h", "ast/f32.h",
"ast/fallthrough_statement.cc", "ast/fallthrough_statement.cc",
@ -342,7 +344,6 @@ libtint_source_set("libtint_core_all_src") {
"debug.h", "debug.h",
"demangler.cc", "demangler.cc",
"demangler.h", "demangler.h",
"number.h",
"diagnostic/diagnostic.cc", "diagnostic/diagnostic.cc",
"diagnostic/diagnostic.h", "diagnostic/diagnostic.h",
"diagnostic/formatter.cc", "diagnostic/formatter.cc",
@ -357,6 +358,7 @@ libtint_source_set("libtint_core_all_src") {
"inspector/resource_binding.h", "inspector/resource_binding.h",
"inspector/scalar.cc", "inspector/scalar.cc",
"inspector/scalar.h", "inspector/scalar.h",
"number.h",
"program.cc", "program.cc",
"program.h", "program.h",
"program_builder.cc", "program_builder.cc",
@ -394,6 +396,7 @@ libtint_source_set("libtint_core_all_src") {
"sem/depth_texture.h", "sem/depth_texture.h",
"sem/expression.h", "sem/expression.h",
"sem/external_texture.h", "sem/external_texture.h",
"sem/f16.h",
"sem/f32.h", "sem/f32.h",
"sem/for_loop_statement.h", "sem/for_loop_statement.h",
"sem/i32.h", "sem/i32.h",
@ -591,6 +594,8 @@ libtint_source_set("libtint_sem_src") {
"sem/expression.h", "sem/expression.h",
"sem/external_texture.cc", "sem/external_texture.cc",
"sem/external_texture.h", "sem/external_texture.h",
"sem/f16.cc",
"sem/f16.h",
"sem/f32.cc", "sem/f32.cc",
"sem/f32.h", "sem/f32.h",
"sem/for_loop_statement.cc", "sem/for_loop_statement.cc",

View File

@ -116,6 +116,8 @@ set(TINT_LIB_SRCS
ast/expression.h ast/expression.h
ast/external_texture.cc ast/external_texture.cc
ast/external_texture.h ast/external_texture.h
ast/f16.cc
ast/f16.h
ast/f32.cc ast/f32.cc
ast/f32.h ast/f32.h
ast/fallthrough_statement.cc ast/fallthrough_statement.cc
@ -403,6 +405,8 @@ set(TINT_LIB_SRCS
sem/depth_texture.h sem/depth_texture.h
sem/external_texture.cc sem/external_texture.cc
sem/external_texture.h sem/external_texture.h
sem/f16.cc
sem/f16.h
sem/f32.cc sem/f32.cc
sem/f32.h sem/f32.h
sem/for_loop_statement.cc sem/for_loop_statement.cc
@ -686,6 +690,7 @@ if(TINT_BUILD_TESTS)
ast/discard_statement_test.cc ast/discard_statement_test.cc
ast/enable_test.cc ast/enable_test.cc
ast/external_texture_test.cc ast/external_texture_test.cc
ast/f16_test.cc
ast/f32_test.cc ast/f32_test.cc
ast/fallthrough_statement_test.cc ast/fallthrough_statement_test.cc
ast/float_literal_expression_test.cc ast/float_literal_expression_test.cc
@ -790,6 +795,7 @@ if(TINT_BUILD_TESTS)
sem/depth_multisampled_texture_test.cc sem/depth_multisampled_texture_test.cc
sem/depth_texture_test.cc sem/depth_texture_test.cc
sem/external_texture_test.cc sem/external_texture_test.cc
sem/f16_test.cc
sem/f32_test.cc sem/f32_test.cc
sem/i32_test.cc sem/i32_test.cc
sem/matrix_test.cc sem/matrix_test.cc

View File

@ -28,6 +28,9 @@ Enable::ExtensionKind Enable::NameToKind(const std::string& name) {
if (name == "chromium_disable_uniformity_analysis") { if (name == "chromium_disable_uniformity_analysis") {
return Enable::ExtensionKind::kChromiumDisableUniformityAnalysis; return Enable::ExtensionKind::kChromiumDisableUniformityAnalysis;
} }
if (name == "f16") {
return Enable::ExtensionKind::kF16;
}
// The reserved internal extension name for testing // The reserved internal extension name for testing
if (name == "InternalExtensionForTesting") { if (name == "InternalExtensionForTesting") {
@ -43,7 +46,8 @@ std::string Enable::KindToName(ExtensionKind kind) {
return "chromium_experimental_dp4a"; return "chromium_experimental_dp4a";
case ExtensionKind::kChromiumDisableUniformityAnalysis: case ExtensionKind::kChromiumDisableUniformityAnalysis:
return "chromium_disable_uniformity_analysis"; return "chromium_disable_uniformity_analysis";
case ExtensionKind::kF16:
return "f16";
// The reserved internal extension for testing // The reserved internal extension for testing
case ExtensionKind::kInternalExtensionForTesting: case ExtensionKind::kInternalExtensionForTesting:
return "InternalExtensionForTesting"; return "InternalExtensionForTesting";

View File

@ -35,6 +35,8 @@ class Enable : public Castable<Enable, Node> {
/// An internal reserved extension for test, named /// An internal reserved extension for test, named
/// "InternalExtensionForTesting". /// "InternalExtensionForTesting".
kInternalExtensionForTesting, kInternalExtensionForTesting,
/// WGSL Extension "f16"
kF16,
/// An extension for the experimental feature /// An extension for the experimental feature
/// "chromium_experimental_dp4a". /// "chromium_experimental_dp4a".

38
src/tint/ast/f16.cc Normal file
View File

@ -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

48
src/tint/ast/f16.h Normal file
View File

@ -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_

30
src/tint/ast/f16_test.cc Normal file
View File

@ -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

View File

@ -30,6 +30,8 @@ class FloatLiteralExpression final : public Castable<FloatLiteralExpression, Lit
kNone, kNone,
/// 'f' suffix (f32) /// 'f' suffix (f32)
kF, kF,
/// 'h' suffix (f16)
kH,
}; };
/// Constructor /// Constructor

View File

@ -27,6 +27,7 @@
#include "src/tint/sem/array.h" #include "src/tint/sem/array.h"
#include "src/tint/sem/call.h" #include "src/tint/sem/call.h"
#include "src/tint/sem/depth_multisampled_texture.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/f32.h"
#include "src/tint/sem/function.h" #include "src/tint/sem/function.h"
#include "src/tint/sem/i32.h" #include "src/tint/sem/i32.h"

View File

@ -18,6 +18,12 @@
#include <stdint.h> #include <stdint.h>
#include <functional> #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 { namespace tint {
/// Number wraps a integer or floating point number, enforcing explicit casting. /// Number wraps a integer or floating point number, enforcing explicit casting.
@ -72,6 +78,43 @@ bool operator==(A a, Number<B> b) {
return Number<A>(a) == b; 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>`. /// `AInt` is a type alias to `Number<int64_t>`.
using AInt = Number<int64_t>; using AInt = Number<int64_t>;
/// `AFloat` is a type alias to `Number<double>`. /// `AFloat` is a type alias to `Number<double>`.
@ -83,6 +126,9 @@ using i32 = Number<int32_t>;
using u32 = Number<uint32_t>; using u32 = Number<uint32_t>;
/// `f32` is a type alias to `Number<float>` /// `f32` is a type alias to `Number<float>`
using f32 = 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 } // namespace tint
@ -118,6 +164,16 @@ inline f32 operator"" _f(unsigned long long int value) { // NOLINT
return f32(static_cast<double>(value)); 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 } // namespace tint::number_suffixes
#endif // SRC_TINT_NUMBER_H_ #endif // SRC_TINT_NUMBER_H_

View File

@ -40,6 +40,7 @@
#include "src/tint/ast/discard_statement.h" #include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/enable.h" #include "src/tint/ast/enable.h"
#include "src/tint/ast/external_texture.h" #include "src/tint/ast/external_texture.h"
#include "src/tint/ast/f16.h"
#include "src/tint/ast/f32.h" #include "src/tint/ast/f32.h"
#include "src/tint/ast/fallthrough_statement.h" #include "src/tint/ast/fallthrough_statement.h"
#include "src/tint/ast/float_literal_expression.h" #include "src/tint/ast/float_literal_expression.h"
@ -82,6 +83,7 @@
#include "src/tint/sem/bool.h" #include "src/tint/sem/bool.h"
#include "src/tint/sem/depth_texture.h" #include "src/tint/sem/depth_texture.h"
#include "src/tint/sem/external_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/f32.h"
#include "src/tint/sem/i32.h" #include "src/tint/sem/i32.h"
#include "src/tint/sem/matrix.h" #include "src/tint/sem/matrix.h"
@ -385,6 +387,15 @@ class ProgramBuilder {
return builder->create<ast::Bool>(source); 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 /// @returns a f32 type
const ast::F32* f32() const { return builder->create<ast::F32>(); } const ast::F32* f32() const { return builder->create<ast::F32>(); }
@ -1004,6 +1015,21 @@ class ProgramBuilder {
ast::FloatLiteralExpression::Suffix::kF); ast::FloatLiteralExpression::Suffix::kF);
} }
/// @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 source the source information
/// @param value the integer value /// @param value the integer value
/// @return an unsuffixed IntLiteralExpression for the AInt value /// @return an unsuffixed IntLiteralExpression for the AInt value
@ -2675,6 +2701,10 @@ struct ProgramBuilder::TypesBuilder::CToAST<f32> {
static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->f32(); } static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->f32(); }
}; };
template <> template <>
struct ProgramBuilder::TypesBuilder::CToAST<f16> {
static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->f16(); }
};
template <>
struct ProgramBuilder::TypesBuilder::CToAST<bool> { struct ProgramBuilder::TypesBuilder::CToAST<bool> {
static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->bool_(); } static const ast::Type* get(const ProgramBuilder::TypesBuilder* t) { return t->bool_(); }
}; };

View File

@ -1067,6 +1067,8 @@ Token Lexer::check_keyword(const Source& source, std::string_view str) {
return {Token::Type::kElse, source, "else"}; return {Token::Type::kElse, source, "else"};
if (str == "enable") if (str == "enable")
return {Token::Type::kEnable, source, "enable"}; return {Token::Type::kEnable, source, "enable"};
if (str == "f16")
return {Token::Type::kF16, source, "f16"};
if (str == "f32") if (str == "f32")
return {Token::Type::kF32, source, "f32"}; return {Token::Type::kF32, source, "f32"};
if (str == "fallthrough") if (str == "fallthrough")

View File

@ -124,8 +124,8 @@ const char kWorkgroupSizeAttribute[] = "workgroup_size";
// https://gpuweb.github.io/gpuweb/wgsl.html#reserved-keywords // https://gpuweb.github.io/gpuweb/wgsl.html#reserved-keywords
bool is_reserved(Token t) { bool is_reserved(Token t) {
return t == "asm" || t == "bf16" || t == "const" || t == "do" || t == "enum" || t == "f16" || return t == "asm" || t == "bf16" || t == "const" || t == "do" || t == "enum" || t == "f64" ||
t == "f64" || t == "handle" || t == "i8" || t == "i16" || t == "i64" || t == "mat" || t == "handle" || t == "i8" || t == "i16" || t == "i64" || t == "mat" ||
t == "premerge" || t == "regardless" || t == "typedef" || t == "u8" || t == "u16" || t == "premerge" || t == "regardless" || t == "typedef" || t == "u8" || t == "u16" ||
t == "u64" || t == "unless" || t == "using" || t == "vec" || t == "void" || t == "while"; t == "u64" || t == "unless" || t == "using" || t == "vec" || t == "void" || t == "while";
} }
@ -310,6 +310,9 @@ void ParserImpl::translation_unit() {
if (after_global_decl) { if (after_global_decl) {
add_error(p, "enable directives must come before all global declarations"); add_error(p, "enable directives must come before all global declarations");
} }
} else if (ed.errored) {
// Found a invalid enable directive.
continue;
} else { } else {
auto gd = global_decl(); auto gd = global_decl();
@ -345,6 +348,11 @@ Maybe<bool> ParserImpl::enable_directive() {
synchronized_ = true; synchronized_ = true;
next(); next();
name = {t.to_str(), t.source()}; 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)) { } else if (handle_error(t)) {
// The token might itself be an error. // The token might itself be an error.
return Failure::kErrored; return Failure::kErrored;
@ -976,6 +984,9 @@ Maybe<const ast::Type*> ParserImpl::type_decl() {
if (match(Token::Type::kBool, &source)) if (match(Token::Type::kBool, &source))
return builder_.ty.bool_(source); return builder_.ty.bool_(source);
if (match(Token::Type::kF16, &source))
return builder_.ty.f16(source);
if (match(Token::Type::kF32, &source)) if (match(Token::Type::kF32, &source))
return builder_.ty.f32(source); return builder_.ty.f32(source);

View File

@ -88,7 +88,6 @@ INSTANTIATE_TEST_SUITE_P(ParserImplReservedKeywordTest,
"const", "const",
"do", "do",
"enum", "enum",
"f16",
"f64", "f64",
"handle", "handle",
"i8", "i8",

View File

@ -55,6 +55,17 @@ TEST_F(ParserImplTest, TypeDecl_Bool) {
EXPECT_EQ(t.value->source.range, (Source::Range{{1u, 1u}, {1u, 5u}})); 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) { TEST_F(ParserImplTest, TypeDecl_F32) {
auto p = parser("f32"); auto p = parser("f32");

View File

@ -151,6 +151,8 @@ std::string_view Token::TypeToName(Type type) {
return "else"; return "else";
case Token::Type::kEnable: case Token::Type::kEnable:
return "enable"; return "enable";
case Token::Type::kF16:
return "f16";
case Token::Type::kF32: case Token::Type::kF32:
return "f32"; return "f32";
case Token::Type::kFallthrough: case Token::Type::kFallthrough:

View File

@ -162,6 +162,8 @@ class Token {
kElse, kElse,
/// A 'enable' /// A 'enable'
kEnable, kEnable,
/// A 'f16'
kF16,
/// A 'f32' /// A 'f32'
kF32, kF32,
/// A 'fallthrough' /// A 'fallthrough'

View File

@ -342,7 +342,7 @@ class DependencyScanner {
TraverseType(tex->type); TraverseType(tex->type);
}, },
[&](Default) { [&](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::DepthTexture, ast::DepthMultisampledTexture,
ast::StorageTexture, ast::ExternalTexture, ast::Sampler>()) { ast::StorageTexture, ast::ExternalTexture, ast::Sampler>()) {
UnhandledNode(diagnostics_, ty); UnhandledNode(diagnostics_, ty);

View File

@ -172,6 +172,14 @@ sem::Type* Resolver::Type(const ast::Type* ty) {
[&](const ast::Bool*) { return builder_->create<sem::Bool>(); }, [&](const ast::Bool*) { return builder_->create<sem::Bool>(); },
[&](const ast::I32*) { return builder_->create<sem::I32>(); }, [&](const ast::I32*) { return builder_->create<sem::I32>(); },
[&](const ast::U32*) { return builder_->create<sem::U32>(); }, [&](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::F32*) { return builder_->create<sem::F32>(); },
[&](const ast::Vector* t) -> sem::Vector* { [&](const ast::Vector* t) -> sem::Vector* {
if (!t->type) { if (!t->type) {

View File

@ -70,6 +70,10 @@ sem::Constant Resolver::EvaluateConstantValue(const ast::CallExpression* call,
if (elem_type->Is<sem::U32>()) { if (elem_type->Is<sem::U32>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, 0_u)); 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>()) { if (elem_type->Is<sem::F32>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, 0_f)); return sem::Constant(type, sem::Constant::Scalars(result_size, 0_f));
} }
@ -120,6 +124,11 @@ sem::Constant Resolver::ConstantCast(const sem::Constant& value,
return u32(static_cast<uint32_t>(s)); 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*) { [&](const sem::F32*) {
return value.WithScalarAt(i, [](auto&& s) { // return value.WithScalarAt(i, [](auto&& s) { //
return static_cast<f32>(s); return static_cast<f32>(s);

View File

@ -662,6 +662,24 @@ TEST_F(ResolverTypeValidationTest, BuiltinAsType) {
EXPECT_EQ(r()->error(), "error: cannot use builtin 'max' as type"); 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 { namespace GetCanonicalTests {
struct Params { struct Params {
builder::ast_type_func_ptr create_ast_type; builder::ast_type_func_ptr create_ast_type;

View File

@ -194,7 +194,7 @@ bool Validator::IsFixedFootprint(const sem::Type* type) const {
// https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types // https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
bool Validator::IsHostShareable(const sem::Type* type) const { 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 true;
} }
return Switch( return Switch(
@ -1890,11 +1890,13 @@ bool Validator::ScalarConstructorOrCast(const ast::CallExpression* ctor,
using Bool = sem::Bool; using Bool = sem::Bool;
using I32 = sem::I32; using I32 = sem::I32;
using U32 = sem::U32; using U32 = sem::U32;
using F16 = sem::F16;
using F32 = sem::F32; using F32 = sem::F32;
const bool is_valid = const bool is_valid =
(ty->Is<Bool>() && value_ty->is_scalar()) || (ty->Is<I32>() && value_ty->is_scalar()) || (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) { if (!is_valid) {
AddError("cannot construct '" + sem_.TypeNameOf(ty) + "' with a value of type '" + AddError("cannot construct '" + sem_.TypeNameOf(ty) + "' with a value of type '" +
sem_.TypeNameOf(value_ty) + "'", sem_.TypeNameOf(value_ty) + "'",

View File

@ -35,6 +35,8 @@ class Constant {
tint::u32 u32; tint::u32 u32;
/// The scalar value as a f32 /// The scalar value as a f32
tint::f32 f32; tint::f32 f32;
/// The scalar value as a f16, internally stored as float
tint::f16 f16;
/// The scalar value as a bool /// The scalar value as a bool
bool bool_; bool bool_;
@ -50,6 +52,10 @@ class Constant {
/// @param v the value of the Scalar /// @param v the value of the Scalar
Scalar(tint::f32 v) : f32(v) {} // NOLINT 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` /// Constructs the scalar with the bool value `v`
/// @param v the value of the Scalar /// @param v the value of the Scalar
Scalar(bool v) : bool_(v) {} // NOLINT Scalar(bool v) : bool_(v) {} // NOLINT
@ -106,6 +112,7 @@ class Constant {
ElementType(), // ElementType(), //
[&](const I32*) { return func(elems_[index].i32); }, [&](const I32*) { return func(elems_[index].i32); },
[&](const U32*) { return func(elems_[index].u32); }, [&](const U32*) { return func(elems_[index].u32); },
[&](const F16*) { return func(elems_[index].f16); },
[&](const F32*) { return func(elems_[index].f32); }, [&](const F32*) { return func(elems_[index].f32); },
[&](const Bool*) { return func(elems_[index].bool_); }, [&](const Bool*) { return func(elems_[index].bool_); },
[&](Default) { [&](Default) {

55
src/tint/sem/f16.cc Normal file
View File

@ -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

58
src/tint/sem/f16.h Normal file
View File

@ -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_

48
src/tint/sem/f16_test.cc Normal file
View File

@ -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

View File

@ -15,6 +15,7 @@
#include "src/tint/sem/type.h" #include "src/tint/sem/type.h"
#include "src/tint/sem/bool.h" #include "src/tint/sem/bool.h"
#include "src/tint/sem/f16.h"
#include "src/tint/sem/f32.h" #include "src/tint/sem/f32.h"
#include "src/tint/sem/i32.h" #include "src/tint/sem/i32.h"
#include "src/tint/sem/matrix.h" #include "src/tint/sem/matrix.h"
@ -64,15 +65,15 @@ bool Type::IsConstructible() const {
} }
bool Type::is_scalar() const { bool Type::is_scalar() const {
return IsAnyOf<F32, U32, I32, Bool>(); return IsAnyOf<F16, F32, U32, I32, Bool>();
} }
bool Type::is_numeric_scalar() const { bool Type::is_numeric_scalar() const {
return IsAnyOf<F32, U32, I32>(); return IsAnyOf<F16, F32, U32, I32>();
} }
bool Type::is_float_scalar() const { bool Type::is_float_scalar() const {
return Is<F32>(); return IsAnyOf<F16, F32>();
} }
bool Type::is_float_matrix() const { bool Type::is_float_matrix() const {

View File

@ -87,6 +87,9 @@ const ast::Type* Transform::CreateASTTypeFor(CloneContext& ctx, const sem::Type*
if (ty->Is<sem::U32>()) { if (ty->Is<sem::U32>()) {
return ctx.dst->create<ast::U32>(); return ctx.dst->create<ast::U32>();
} }
if (ty->Is<sem::F16>()) {
return ctx.dst->create<ast::F16>();
}
if (ty->Is<sem::F32>()) { if (ty->Is<sem::F32>()) {
return ctx.dst->create<ast::F32>(); return ctx.dst->create<ast::F32>();
} }

View File

@ -2565,6 +2565,9 @@ bool GeneratorImpl::EmitType(std::ostream& out,
out << "bool"; out << "bool";
} else if (type->Is<sem::F32>()) { } else if (type->Is<sem::F32>()) {
out << "float"; 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>()) { } else if (type->Is<sem::I32>()) {
out << "int"; out << "int";
} else if (auto* mat = type->As<sem::Matrix>()) { } else if (auto* mat = type->As<sem::Matrix>()) {

View File

@ -3513,6 +3513,11 @@ bool GeneratorImpl::EmitType(std::ostream& out,
out << "float"; out << "float";
return true; return true;
}, },
[&](const sem::F16*) {
diagnostics_.add_error(diag::System::Writer,
"Type f16 is not completely implemented yet.");
return false;
},
[&](const sem::I32*) { [&](const sem::I32*) {
out << "int"; out << "int";
return true; return true;

View File

@ -1467,6 +1467,12 @@ bool GeneratorImpl::EmitZeroValue(std::ostream& out, const sem::Type* type) {
out << "false"; out << "false";
return true; 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*) { [&](const sem::F32*) {
out << "0.0f"; out << "0.0f";
return true; return true;
@ -2239,6 +2245,11 @@ bool GeneratorImpl::EmitType(std::ostream& out,
out << "bool"; out << "bool";
return true; return true;
}, },
[&](const sem::F16*) {
diagnostics_.add_error(diag::System::Writer,
"Type f16 is not completely implemented yet");
return false;
},
[&](const sem::F32*) { [&](const sem::F32*) {
out << "float"; out << "float";
return true; return true;

View File

@ -1628,6 +1628,8 @@ uint32_t Builder::GenerateLiteralIfNeeded(const ast::Variable* var,
constant.kind = ScalarConstant::Kind::kF32; constant.kind = ScalarConstant::Kind::kF32;
constant.value.f32 = static_cast<float>(f->value); constant.value.f32 = static_cast<float>(f->value);
return; return;
case ast::FloatLiteralExpression::Suffix::kH:
error_ = "Type f16 is not completely implemented yet";
} }
}, },
[&](Default) { error_ = "unknown literal type"; }); [&](Default) { error_ = "unknown literal type"; });
@ -3672,6 +3674,11 @@ uint32_t Builder::GenerateTypeIfNeeded(const sem::Type* type) {
push_type(spv::Op::OpTypeFloat, {result, Operand(32u)}); push_type(spv::Op::OpTypeFloat, {result, Operand(32u)});
return true; 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*) { [&](const sem::I32*) {
push_type(spv::Op::OpTypeInt, {result, Operand(32u), Operand(1u)}); push_type(spv::Op::OpTypeInt, {result, Operand(32u), Operand(1u)});
return true; return true;

View File

@ -405,6 +405,11 @@ bool GeneratorImpl::EmitType(std::ostream& out, const ast::Type* ty) {
out << "f32"; out << "f32";
return true; return true;
}, },
[&](const ast::F16*) {
diagnostics_.add_error(diag::System::Writer,
"Type f16 is not completely implemented yet.");
return false;
},
[&](const ast::I32*) { [&](const ast::I32*) {
out << "i32"; out << "i32";
return true; return true;

View File

@ -165,6 +165,7 @@ tint_unittests_source_set("tint_unittests_ast_src") {
"../../src/tint/ast/discard_statement_test.cc", "../../src/tint/ast/discard_statement_test.cc",
"../../src/tint/ast/enable_test.cc", "../../src/tint/ast/enable_test.cc",
"../../src/tint/ast/external_texture_test.cc", "../../src/tint/ast/external_texture_test.cc",
"../../src/tint/ast/f16_test.cc",
"../../src/tint/ast/f32_test.cc", "../../src/tint/ast/f32_test.cc",
"../../src/tint/ast/fallthrough_statement_test.cc", "../../src/tint/ast/fallthrough_statement_test.cc",
"../../src/tint/ast/float_literal_expression_test.cc", "../../src/tint/ast/float_literal_expression_test.cc",
@ -289,6 +290,7 @@ tint_unittests_source_set("tint_unittests_sem_src") {
"../../src/tint/sem/depth_multisampled_texture_test.cc", "../../src/tint/sem/depth_multisampled_texture_test.cc",
"../../src/tint/sem/depth_texture_test.cc", "../../src/tint/sem/depth_texture_test.cc",
"../../src/tint/sem/external_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/f32_test.cc",
"../../src/tint/sem/i32_test.cc", "../../src/tint/sem/i32_test.cc",
"../../src/tint/sem/matrix_test.cc", "../../src/tint/sem/matrix_test.cc",