tint: Implement modf and frexp built-ins for f16 types
This patch implement modf and frexp built-ins for f16 types, and also
simplify their implementation for f32 in MSL and HLSL, and clean up
deprecated code in GLSL writer. Corresponding unittests are also
implemented, but end-to-end tests for f16 are not implemented yet.
Bug: tint:1473, tint:1502
Change-Id: I12887ae5303c6dc032a51f619e1afeb19b4603b6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/98102
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index b7a3be6..c66e174 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -1205,97 +1205,51 @@
bool GeneratorImpl::EmitModfCall(std::ostream& out,
const ast::CallExpression* expr,
const sem::Builtin* builtin) {
- if (expr->args.Length() == 1) {
- return CallBuiltinHelper(
- out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
- // Emit the builtin return type unique to this overload. This does not
- // exist in the AST, so it will not be generated in Generate().
- if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
+ TINT_ASSERT(Writer, expr->args.Length() == 1);
+ return CallBuiltinHelper(
+ out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
+ // Emit the builtin return type unique to this overload. This does not
+ // exist in the AST, so it will not be generated in Generate().
+ if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
+ return false;
+ }
+
+ {
+ auto l = line(b);
+ if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
+ ast::Access::kUndefined, "")) {
return false;
}
-
- {
- auto l = line(b);
- if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
- ast::Access::kUndefined, "")) {
- return false;
- }
- l << " result;";
- }
- line(b) << "result.fract = modf(" << params[0] << ", result.whole);";
- line(b) << "return result;";
- return true;
- });
- }
-
- // DEPRECATED
- out << "modf";
- ScopedParen sp(out);
- if (!EmitExpression(out, expr->args[0])) {
- return false;
- }
- out << ", ";
- if (!EmitExpression(out, expr->args[1])) {
- return false;
- }
- return true;
+ l << " result;";
+ }
+ line(b) << "result.fract = modf(" << params[0] << ", result.whole);";
+ line(b) << "return result;";
+ return true;
+ });
}
bool GeneratorImpl::EmitFrexpCall(std::ostream& out,
const ast::CallExpression* expr,
const sem::Builtin* builtin) {
- if (expr->args.Length() == 1) {
- return CallBuiltinHelper(
- out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
- // Emit the builtin return type unique to this overload. This does not
- // exist in the AST, so it will not be generated in Generate().
- if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
- return false;
- }
-
- {
- auto l = line(b);
- if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
- ast::Access::kUndefined, "")) {
- return false;
- }
- l << " result;";
- }
- line(b) << "result.sig = frexp(" << params[0] << ", result.exp);";
- line(b) << "return result;";
- return true;
- });
- }
- // DEPRECATED
- // Exponent is an integer in WGSL, but HLSL wants a float.
- // We need to make the call with a temporary float, and then cast.
+ TINT_ASSERT(Writer, expr->args.Length() == 1);
return CallBuiltinHelper(
out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
- auto* significand_ty = builtin->Parameters()[0]->Type();
- auto significand = params[0];
- auto* exponent_ty = builtin->Parameters()[1]->Type();
- auto exponent = params[1];
-
- std::string width;
- if (auto* vec = significand_ty->As<sem::Vector>()) {
- width = std::to_string(vec->Width());
+ // Emit the builtin return type unique to this overload. This does not
+ // exist in the AST, so it will not be generated in Generate().
+ if (!EmitStructType(&helpers_, builtin->ReturnType()->As<sem::Struct>())) {
+ return false;
}
- // Exponent is an integer, which HLSL does not have an overload for.
- // We need to cast from a float.
- line(b) << "float" << width << " float_exp;";
- line(b) << "float" << width << " significand = frexp(" << significand
- << ", float_exp);";
{
auto l = line(b);
- l << exponent << " = ";
- if (!EmitType(l, exponent_ty->UnwrapPtr(), ast::StorageClass::kNone,
+ if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
ast::Access::kUndefined, "")) {
return false;
}
- l << "(float_exp);";
+ l << " result;";
}
- line(b) << "return significand;";
+ line(b) << "result.sig = frexp(" << params[0] << ", result.exp);";
+ line(b) << "return result;";
return true;
});
}
diff --git a/src/tint/writer/glsl/generator_impl_builtin_test.cc b/src/tint/writer/glsl/generator_impl_builtin_test.cc
index c7c8d53..b8161e2 100644
--- a/src/tint/writer/glsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/glsl/generator_impl_builtin_test.cc
@@ -412,7 +412,7 @@
EXPECT_EQ(out.str(), "((a) * (b) + (c))");
}
-TEST_F(GlslGeneratorImplTest_Builtin, Modf_Scalar) {
+TEST_F(GlslGeneratorImplTest_Builtin, Modf_Scalar_f32) {
auto* call = Call("modf", 1_f);
WrapInFunction(CallStmt(call));
@@ -445,7 +445,43 @@
)");
}
-TEST_F(GlslGeneratorImplTest_Builtin, Modf_Vector) {
+TEST_F(GlslGeneratorImplTest_Builtin, Modf_Scalar_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("modf", 1_h);
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+struct modf_result_f16 {
+ float16_t fract;
+ float16_t whole;
+};
+
+modf_result_f16 tint_modf(float16_t param_0) {
+ modf_result_f16 result;
+ result.fract = modf(param_0, result.whole);
+ return result;
+}
+
+
+void test_function() {
+ tint_modf(1.0hf);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+ test_function();
+ return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Modf_Vector_f32) {
auto* call = Call("modf", vec3<f32>());
WrapInFunction(CallStmt(call));
@@ -478,7 +514,43 @@
)");
}
-TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Scalar_i32) {
+TEST_F(GlslGeneratorImplTest_Builtin, Modf_Vector_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("modf", vec3<f16>());
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+struct modf_result_vec3_f16 {
+ f16vec3 fract;
+ f16vec3 whole;
+};
+
+modf_result_vec3_f16 tint_modf(f16vec3 param_0) {
+ modf_result_vec3_f16 result;
+ result.fract = modf(param_0, result.whole);
+ return result;
+}
+
+
+void test_function() {
+ tint_modf(f16vec3(0.0hf));
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+ test_function();
+ return;
+}
+)");
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Scalar_f32) {
auto* call = Call("frexp", 1_f);
WrapInFunction(CallStmt(call));
@@ -505,7 +577,42 @@
)"));
}
-TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Vector_i32) {
+TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Scalar_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("frexp", 1_h);
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr(R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+struct frexp_result_f16 {
+ float16_t sig;
+ int exp;
+};
+
+frexp_result_f16 tint_frexp(float16_t param_0) {
+ frexp_result_f16 result;
+ result.sig = frexp(param_0, result.exp);
+ return result;
+}
+
+
+void test_function() {
+ tint_frexp(1.0hf);
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+ test_function();
+ return;
+)"));
+}
+
+TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Vector_f32) {
auto* call = Call("frexp", vec3<f32>());
WrapInFunction(CallStmt(call));
@@ -537,6 +644,42 @@
)"));
}
+TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Vector_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("frexp", vec3<f16>());
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(), HasSubstr(R"(#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+
+struct frexp_result_vec3_f16 {
+ f16vec3 sig;
+ ivec3 exp;
+};
+
+frexp_result_vec3_f16 tint_frexp(f16vec3 param_0) {
+ frexp_result_vec3_f16 result;
+ result.sig = frexp(param_0, result.exp);
+ return result;
+}
+
+
+void test_function() {
+ tint_frexp(f16vec3(0.0hf));
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+ test_function();
+ return;
+}
+)"));
+}
+
TEST_F(GlslGeneratorImplTest_Builtin, Degrees_Scalar_f32) {
auto* val = Var("val", ty.f32());
auto* call = Call("degrees", val);
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 726a459..444746c 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -1855,16 +1855,15 @@
return false;
}
- line(b) << "float" << width << " whole;";
- line(b) << "float" << width << " fract = modf(" << in << ", whole);";
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
ast::Access::kUndefined, "")) {
return false;
}
- l << " result = {fract, whole};";
+ l << " result;";
}
+ line(b) << "result.fract = modf(" << params[0] << ", result.whole);";
line(b) << "return result;";
return true;
});
@@ -1889,8 +1888,15 @@
return false;
}
- line(b) << "float" << width << " exp;";
- line(b) << "float" << width << " sig = frexp(" << in << ", exp);";
+ std::string member_type;
+ if (Is<sem::F16>(sem::Type::DeepestElementOf(ty))) {
+ member_type = width.empty() ? "float16_t" : ("vector<float16_t, " + width + ">");
+ } else {
+ member_type = "float" + width;
+ }
+
+ line(b) << member_type << " exp;";
+ line(b) << member_type << " sig = frexp(" << in << ", exp);";
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::StorageClass::kNone,
diff --git a/src/tint/writer/hlsl/generator_impl_builtin_test.cc b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
index 82c7e7e..3f9cfa7 100644
--- a/src/tint/writer/hlsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
@@ -377,7 +377,7 @@
EXPECT_EQ(out.str(), "(bool2(true, false) ? int2(3, 4) : int2(1, 2))");
}
-TEST_F(HlslGeneratorImplTest_Builtin, Modf_Scalar) {
+TEST_F(HlslGeneratorImplTest_Builtin, Modf_Scalar_f32) {
auto* call = Call("modf", 1_f);
WrapInFunction(CallStmt(call));
@@ -389,9 +389,8 @@
float whole;
};
modf_result tint_modf(float param_0) {
- float whole;
- float fract = modf(param_0, whole);
- modf_result result = {fract, whole};
+ modf_result result;
+ result.fract = modf(param_0, result.whole);
return result;
}
@@ -403,7 +402,34 @@
)");
}
-TEST_F(HlslGeneratorImplTest_Builtin, Modf_Vector) {
+TEST_F(HlslGeneratorImplTest_Builtin, Modf_Scalar_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("modf", 1_h);
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(struct modf_result_f16 {
+ float16_t fract;
+ float16_t whole;
+};
+modf_result_f16 tint_modf(float16_t param_0) {
+ modf_result_f16 result;
+ result.fract = modf(param_0, result.whole);
+ return result;
+}
+
+[numthreads(1, 1, 1)]
+void test_function() {
+ tint_modf(float16_t(1.0h));
+ return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Modf_Vector_f32) {
auto* call = Call("modf", vec3<f32>());
WrapInFunction(CallStmt(call));
@@ -415,9 +441,8 @@
float3 whole;
};
modf_result_vec3 tint_modf(float3 param_0) {
- float3 whole;
- float3 fract = modf(param_0, whole);
- modf_result_vec3 result = {fract, whole};
+ modf_result_vec3 result;
+ result.fract = modf(param_0, result.whole);
return result;
}
@@ -429,7 +454,34 @@
)");
}
-TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Scalar_i32) {
+TEST_F(HlslGeneratorImplTest_Builtin, Modf_Vector_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("modf", vec3<f16>());
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(struct modf_result_vec3_f16 {
+ vector<float16_t, 3> fract;
+ vector<float16_t, 3> whole;
+};
+modf_result_vec3_f16 tint_modf(vector<float16_t, 3> param_0) {
+ modf_result_vec3_f16 result;
+ result.fract = modf(param_0, result.whole);
+ return result;
+}
+
+[numthreads(1, 1, 1)]
+void test_function() {
+ tint_modf((float16_t(0.0h)).xxx);
+ return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Scalar_f32) {
auto* call = Call("frexp", 1_f);
WrapInFunction(CallStmt(call));
@@ -455,7 +507,35 @@
)");
}
-TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Vector_i32) {
+TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Scalar_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("frexp", 1_h);
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(struct frexp_result_f16 {
+ float16_t sig;
+ int exp;
+};
+frexp_result_f16 tint_frexp(float16_t param_0) {
+ float16_t exp;
+ float16_t sig = frexp(param_0, exp);
+ frexp_result_f16 result = {sig, int(exp)};
+ return result;
+}
+
+[numthreads(1, 1, 1)]
+void test_function() {
+ tint_frexp(float16_t(1.0h));
+ return;
+}
+)");
+}
+
+TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Vector_f32) {
auto* call = Call("frexp", vec3<f32>());
WrapInFunction(CallStmt(call));
@@ -481,6 +561,34 @@
)");
}
+TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Vector_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("frexp", vec3<f16>());
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f16 {
+ vector<float16_t, 3> sig;
+ int3 exp;
+};
+frexp_result_vec3_f16 tint_frexp(vector<float16_t, 3> param_0) {
+ vector<float16_t, 3> exp;
+ vector<float16_t, 3> sig = frexp(param_0, exp);
+ frexp_result_vec3_f16 result = {sig, int3(exp)};
+ return result;
+}
+
+[numthreads(1, 1, 1)]
+void test_function() {
+ tint_frexp((float16_t(0.0h)).xxx);
+ return;
+}
+)");
+}
+
TEST_F(HlslGeneratorImplTest_Builtin, Degrees_Scalar_f32) {
auto* val = Var("val", ty.f32());
auto* call = Call("degrees", val);
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 577ecd7..28d2c10 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -1308,9 +1308,9 @@
return false;
}
- line(b) << "float" << width << " whole;";
- line(b) << "float" << width << " fract = modf(" << in << ", whole);";
- line(b) << "return {fract, whole};";
+ line(b) << StructName(builtin->ReturnType()->As<sem::Struct>()) << " result;";
+ line(b) << "result.fract = modf(" << in << ", result.whole);";
+ line(b) << "return result;";
return true;
});
}
@@ -1334,9 +1334,9 @@
return false;
}
- line(b) << "int" << width << " exp;";
- line(b) << "float" << width << " sig = frexp(" << in << ", exp);";
- line(b) << "return {sig, exp};";
+ line(b) << StructName(builtin->ReturnType()->As<sem::Struct>()) << " result;";
+ line(b) << "result.sig = frexp(" << in << ", result.exp);";
+ line(b) << "return result;";
return true;
});
}
diff --git a/src/tint/writer/msl/generator_impl_builtin_test.cc b/src/tint/writer/msl/generator_impl_builtin_test.cc
index b97a108..9f75691 100644
--- a/src/tint/writer/msl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/msl/generator_impl_builtin_test.cc
@@ -405,6 +405,246 @@
EXPECT_EQ(out.str(), "threadgroup_barrier(mem_flags::mem_threadgroup)");
}
+TEST_F(MslGeneratorImplTest, Modf_Scalar_f32) {
+ auto* call = Call("modf", 1_f);
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct modf_result {
+ float fract;
+ float whole;
+};
+modf_result tint_modf(float param_0) {
+ modf_result result;
+ result.fract = modf(param_0, result.whole);
+ return result;
+}
+
+kernel void test_function() {
+ tint_modf(1.0f);
+ return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Modf_Scalar_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("modf", 1_h);
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct modf_result_f16 {
+ half fract;
+ half whole;
+};
+modf_result_f16 tint_modf(half param_0) {
+ modf_result_f16 result;
+ result.fract = modf(param_0, result.whole);
+ return result;
+}
+
+kernel void test_function() {
+ tint_modf(1.0h);
+ return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Modf_Vector_f32) {
+ auto* call = Call("modf", vec3<f32>());
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct modf_result_vec3 {
+ float3 fract;
+ float3 whole;
+};
+modf_result_vec3 tint_modf(float3 param_0) {
+ modf_result_vec3 result;
+ result.fract = modf(param_0, result.whole);
+ return result;
+}
+
+kernel void test_function() {
+ tint_modf(float3(0.0f));
+ return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Modf_Vector_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("modf", vec3<f16>());
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct modf_result_vec3_f16 {
+ half3 fract;
+ half3 whole;
+};
+modf_result_vec3_f16 tint_modf(half3 param_0) {
+ modf_result_vec3_f16 result;
+ result.fract = modf(param_0, result.whole);
+ return result;
+}
+
+kernel void test_function() {
+ tint_modf(half3(0.0h));
+ return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Frexp_Scalar_f32) {
+ auto* call = Call("frexp", 1_f);
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct frexp_result {
+ float sig;
+ int exp;
+};
+frexp_result tint_frexp(float param_0) {
+ frexp_result result;
+ result.sig = frexp(param_0, result.exp);
+ return result;
+}
+
+kernel void test_function() {
+ tint_frexp(1.0f);
+ return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Frexp_Scalar_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("frexp", 1_h);
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct frexp_result_f16 {
+ half sig;
+ int exp;
+};
+frexp_result_f16 tint_frexp(half param_0) {
+ frexp_result_f16 result;
+ result.sig = frexp(param_0, result.exp);
+ return result;
+}
+
+kernel void test_function() {
+ tint_frexp(1.0h);
+ return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Frexp_Vector_f32) {
+ auto* call = Call("frexp", vec3<f32>());
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct frexp_result_vec3 {
+ float3 sig;
+ int3 exp;
+};
+frexp_result_vec3 tint_frexp(float3 param_0) {
+ frexp_result_vec3 result;
+ result.sig = frexp(param_0, result.exp);
+ return result;
+}
+
+kernel void test_function() {
+ tint_frexp(float3(0.0f));
+ return;
+}
+
+)");
+}
+
+TEST_F(MslGeneratorImplTest, Frexp_Vector_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* call = Call("frexp", vec3<f16>());
+ WrapInFunction(CallStmt(call));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
+
+using namespace metal;
+
+struct frexp_result_vec3_f16 {
+ half3 sig;
+ int3 exp;
+};
+frexp_result_vec3_f16 tint_frexp(half3 param_0) {
+ frexp_result_vec3_f16 result;
+ result.sig = frexp(param_0, result.exp);
+ return result;
+}
+
+kernel void test_function() {
+ tint_frexp(half3(0.0h));
+ return;
+}
+
+)");
+}
+
TEST_F(MslGeneratorImplTest, Degrees_Scalar_f32) {
auto* val = Var("val", ty.f32());
auto* call = Call("degrees", val);
diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc
index 1faf474..52254d2 100644
--- a/src/tint/writer/spirv/builder_builtin_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_test.cc
@@ -1651,7 +1651,7 @@
EXPECT_EQ(got, expect);
}
-TEST_F(BuiltinBuilderTest, Call_Modf) {
+TEST_F(BuiltinBuilderTest, Call_Modf_f32) {
auto* vec = Var("vec", nullptr, vec2<f32>(1_f, 2_f));
auto* expr = Call("modf", vec);
Func("a_func", utils::Empty, ty.void_(),
@@ -1703,7 +1703,65 @@
Validate(b);
}
-TEST_F(BuiltinBuilderTest, Call_Frexp) {
+TEST_F(BuiltinBuilderTest, Call_Modf_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* vec = Var("vec", nullptr, vec2<f16>(1_h, 2_h));
+ auto* expr = Call("modf", vec);
+ Func("a_func", utils::Empty, ty.void_(),
+ utils::Vector{
+ Decl(vec),
+ CallStmt(expr),
+ },
+ utils::Vector{
+ Stage(ast::PipelineStage::kFragment),
+ });
+
+ spirv::Builder& b = Build();
+
+ ASSERT_TRUE(b.Build()) << b.error();
+ auto got = DumpBuilder(b);
+ auto* expect = R"(OpCapability Shader
+OpCapability Float16
+OpCapability UniformAndStorageBuffer16BitAccess
+OpCapability StorageBuffer16BitAccess
+OpCapability StorageInputOutput16
+%15 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %3 "a_func"
+OpExecutionMode %3 OriginUpperLeft
+OpName %3 "a_func"
+OpName %10 "vec"
+OpName %14 "__modf_result_vec2_f16"
+OpMemberName %14 0 "fract"
+OpMemberName %14 1 "whole"
+OpMemberDecorate %14 0 Offset 0
+OpMemberDecorate %14 1 Offset 4
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 0x1p+0
+%8 = OpConstant %6 0x1p+1
+%9 = OpConstantComposite %5 %7 %8
+%11 = OpTypePointer Function %5
+%12 = OpConstantNull %5
+%14 = OpTypeStruct %5 %5
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%10 = OpVariable %11 Function %12
+OpStore %10 %9
+%16 = OpLoad %5 %10
+%13 = OpExtInst %14 %15 ModfStruct %16
+OpReturn
+OpFunctionEnd
+)";
+ EXPECT_EQ(expect, got);
+
+ Validate(b);
+}
+
+TEST_F(BuiltinBuilderTest, Call_Frexp_f32) {
auto* vec = Var("vec", nullptr, vec2<f32>(1_f, 2_f));
auto* expr = Call("frexp", vec);
Func("a_func", utils::Empty, ty.void_(),
@@ -1757,6 +1815,66 @@
Validate(b);
}
+TEST_F(BuiltinBuilderTest, Call_Frexp_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* vec = Var("vec", nullptr, vec2<f16>(1_h, 2_h));
+ auto* expr = Call("frexp", vec);
+ Func("a_func", utils::Empty, ty.void_(),
+ utils::Vector{
+ Decl(vec),
+ CallStmt(expr),
+ },
+ utils::Vector{
+ Stage(ast::PipelineStage::kFragment),
+ });
+
+ spirv::Builder& b = Build();
+
+ ASSERT_TRUE(b.Build()) << b.error();
+ auto got = DumpBuilder(b);
+ auto* expect = R"(OpCapability Shader
+OpCapability Float16
+OpCapability UniformAndStorageBuffer16BitAccess
+OpCapability StorageBuffer16BitAccess
+OpCapability StorageInputOutput16
+%17 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %3 "a_func"
+OpExecutionMode %3 OriginUpperLeft
+OpName %3 "a_func"
+OpName %10 "vec"
+OpName %14 "__frexp_result_vec2_f16"
+OpMemberName %14 0 "sig"
+OpMemberName %14 1 "exp"
+OpMemberDecorate %14 0 Offset 0
+OpMemberDecorate %14 1 Offset 8
+%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%6 = OpTypeFloat 16
+%5 = OpTypeVector %6 2
+%7 = OpConstant %6 0x1p+0
+%8 = OpConstant %6 0x1p+1
+%9 = OpConstantComposite %5 %7 %8
+%11 = OpTypePointer Function %5
+%12 = OpConstantNull %5
+%16 = OpTypeInt 32 1
+%15 = OpTypeVector %16 2
+%14 = OpTypeStruct %5 %15
+%3 = OpFunction %2 None %1
+%4 = OpLabel
+%10 = OpVariable %11 Function %12
+OpStore %10 %9
+%18 = OpLoad %5 %10
+%13 = OpExtInst %14 %17 FrexpStruct %18
+OpReturn
+OpFunctionEnd
+)";
+ EXPECT_EQ(expect, got);
+
+ Validate(b);
+}
+
} // namespace float_builtin_tests
// Tests for Numeric builtins with all integer parameter