tint/writer/hlsl: Support for F16 type, constructor, and convertor

This patch make HLSL writer support emitting f16 types, f16 literals,
f16 constructor and convertor. Unittests are also implemented.

The HLSL writer will emit f16 literal as `float16_t(1.23h)`, making the
type explicit, and map f16 types as follow. The generated code require
DXC with SM6.0 or higher, and `-enable-16bit-types`.
WGSL type   -> HLSL type
f16         -> float16_t
vec2<f16>   -> vector<float16_t, 2>
vec3<f16>   -> vector<float16_t, 3>
vec4<f16>   -> vector<float16_t, 4>
mat2x2<f16> -> matrix<float16_t, 2, 2>
mat2x3<f16> -> matrix<float16_t, 2, 3>
mat2x4<f16> -> matrix<float16_t, 2, 4>
mat3x2<f16> -> matrix<float16_t, 3, 2>
mat3x3<f16> -> matrix<float16_t, 3, 3>
mat3x4<f16> -> matrix<float16_t, 3, 4>
mat4x2<f16> -> matrix<float16_t, 4, 2>
mat4x3<f16> -> matrix<float16_t, 4, 3>
mat4x4<f16> -> matrix<float16_t, 4, 4>

Bug: tint:1473, tint:1502
Change-Id: Iaf564f3ce29ace2984cef19d7df5a7dfb0fab2ef
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/95685
Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index b307c8d..c9861a1 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -122,6 +122,16 @@
     }
 }
 
+bool PrintF16(std::ostream& out, float value) {
+    // Note: Currently inf and nan should not be constructable, don't emit them.
+    if (std::isinf(value) || std::isnan(value)) {
+        return false;
+    } else {
+        out << FloatToString(value) << "h";
+        return true;
+    }
+}
+
 // Helper for writing " : register(RX, spaceY)", where R is the register, X is
 // the binding point binding value, and Y is the binding point group value.
 struct RegisterAndSpace {
@@ -3122,6 +3132,13 @@
             PrintF32(out, constant->As<float>());
             return true;
         },
+        [&](const sem::F16*) {
+            // emit a f16 scalar with explicit float16_t type declaration.
+            out << "float16_t(";
+            bool valid = PrintF16(out, constant->As<float>());
+            out << ")";
+            return valid;
+        },
         [&](const sem::I32*) {
             out << constant->As<AInt>();
             return true;
@@ -3218,6 +3235,13 @@
             return true;
         },
         [&](const ast::FloatLiteralExpression* l) {
+            if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
+                // Emit f16 literal with explicit float16_t type declaration.
+                out << "float16_t(";
+                bool valid = PrintF16(out, static_cast<float>(l->value));
+                out << ")";
+                return valid;
+            }
             PrintF32(out, static_cast<float>(l->value));
             return true;
         },
@@ -3251,6 +3275,10 @@
             out << value << ".0f";
             return true;
         },
+        [&](const sem::F16*) {
+            out << "float16_t(" << value << ".0h)";
+            return true;
+        },
         [&](const sem::I32*) {
             out << value;
             return true;
@@ -3723,15 +3751,23 @@
             return true;
         },
         [&](const sem::F16*) {
-            diagnostics_.add_error(diag::System::Writer,
-                                   "Type f16 is not completely implemented yet.");
-            return false;
+            out << "float16_t";
+            return true;
         },
         [&](const sem::I32*) {
             out << "int";
             return true;
         },
         [&](const sem::Matrix* mat) {
+            if (mat->type()->Is<sem::F16>()) {
+                // Use matrix<type, N, M> for f16 matrix
+                out << "matrix<";
+                if (!EmitType(out, mat->type(), storage_class, access, "")) {
+                    return false;
+                }
+                out << ", " << mat->columns() << ", " << mat->rows() << ">";
+                return true;
+            }
             if (!EmitType(out, mat->type(), storage_class, access, "")) {
                 return false;
             }
@@ -3847,6 +3883,7 @@
             } else if (vec->type()->Is<sem::Bool>() && width >= 1 && width <= 4) {
                 out << "bool" << width;
             } else {
+                // For example, use "vector<float16_t, N>" for f16 vector.
                 out << "vector<";
                 if (!EmitType(out, vec->type(), storage_class, access, "")) {
                     return false;