tint: Implement const-eval of frexp()

Also add abstract overloads.

Bug: tint:1581
Fixed: tint:1768
Change-Id: Icda465e0cfe960b77823c2135f0cfe8f82ed394f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/111441
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton
2022-11-23 18:21:38 +00:00
committed by Dawn LUCI CQ
parent d743778ed5
commit 69c2c34326
218 changed files with 6625 additions and 719 deletions

View File

@@ -477,8 +477,8 @@ fn fma<T: f32_f16>(T, T, T) -> T
fn fma<N: num, T: f32_f16>(vec<N, T>, vec<N, T>, vec<N, T>) -> vec<N, T>
fn fract<T: f32_f16>(T) -> T
fn fract<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
fn frexp<T: f32_f16>(T) -> __frexp_result<T>
fn frexp<N: num, T: f32_f16>(vec<N, T>) -> __frexp_result_vec<N, T>
@const fn frexp<T: fa_f32_f16>(T) -> __frexp_result<T>
@const fn frexp<N: num, T: fa_f32_f16>(vec<N, T>) -> __frexp_result_vec<N, T>
@stage("fragment") fn fwidth(f32) -> f32
@stage("fragment") fn fwidth<N: num>(vec<N, f32>) -> vec<N, f32>
@stage("fragment") fn fwidthCoarse(f32) -> f32

View File

@@ -1079,54 +1079,8 @@ TEST_F(ResolverBuiltinFloatTest, Frexp_Error_FirstParamInt) {
R"(error: no matching call to frexp(i32, ptr<workgroup, i32, read_write>)
2 candidate functions:
frexp(T) -> __frexp_result_T where: T is f32 or f16
frexp(vecN<T>) -> __frexp_result_vecN_T where: T is f32 or f16
)");
}
TEST_F(ResolverBuiltinFloatTest, Frexp_Error_SecondParamFloatPtr) {
GlobalVar("v", ty.f32(), ast::AddressSpace::kWorkgroup);
auto* call = Call("frexp", 1_f, AddressOf("v"));
WrapInFunction(call);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(error: no matching call to frexp(f32, ptr<workgroup, f32, read_write>)
2 candidate functions:
frexp(T) -> __frexp_result_T where: T is f32 or f16
frexp(vecN<T>) -> __frexp_result_vecN_T where: T is f32 or f16
)");
}
TEST_F(ResolverBuiltinFloatTest, Frexp_Error_SecondParamNotAPointer) {
auto* call = Call("frexp", 1_f, 1_i);
WrapInFunction(call);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(error: no matching call to frexp(f32, i32)
2 candidate functions:
frexp(T) -> __frexp_result_T where: T is f32 or f16
frexp(vecN<T>) -> __frexp_result_vecN_T where: T is f32 or f16
)");
}
TEST_F(ResolverBuiltinFloatTest, Frexp_Error_VectorSizesDontMatch) {
GlobalVar("v", ty.vec4<i32>(), ast::AddressSpace::kWorkgroup);
auto* call = Call("frexp", vec2<f32>(1_f, 2_f), AddressOf("v"));
WrapInFunction(call);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(error: no matching call to frexp(vec2<f32>, ptr<workgroup, vec4<i32>, read_write>)
2 candidate functions:
frexp(T) -> __frexp_result_T where: T is f32 or f16
frexp(vecN<T>) -> __frexp_result_vecN_T where: T is f32 or f16
frexp(T) -> __frexp_result_T where: T is abstract-float, f32 or f16
frexp(vecN<T>) -> __frexp_result_vecN_T where: T is abstract-float, f32 or f16
)");
}

View File

@@ -2219,6 +2219,79 @@ ConstEval::Result ConstEval::floor(const sem::Type* ty,
return TransformElements(builder, ty, transform, args[0]);
}
ConstEval::Result ConstEval::frexp(const sem::Type* ty,
utils::VectorRef<const sem::Constant*> args,
const Source& source) {
auto* arg = args[0];
struct FractExp {
ImplResult fract;
ImplResult exp;
};
auto scalar = [&](const sem::Constant* s) {
int exp = 0;
double fract = std::frexp(s->As<AFloat>(), &exp);
return Switch(
s->Type(),
[&](const sem::F32*) {
return FractExp{
CreateElement(builder, source, builder.create<sem::F32>(), f32(fract)),
CreateElement(builder, source, builder.create<sem::I32>(), i32(exp)),
};
},
[&](const sem::F16*) {
return FractExp{
CreateElement(builder, source, builder.create<sem::F16>(), f16(fract)),
CreateElement(builder, source, builder.create<sem::I32>(), i32(exp)),
};
},
[&](const sem::AbstractFloat*) {
return FractExp{
CreateElement(builder, source, builder.create<sem::AbstractFloat>(),
AFloat(fract)),
CreateElement(builder, source, builder.create<sem::AbstractInt>(), AInt(exp)),
};
},
[&](Default) {
TINT_ICE(Resolver, builder.Diagnostics())
<< "unhandled element type for frexp() const-eval: "
<< builder.FriendlyName(s->Type());
return FractExp{utils::Failure, utils::Failure};
});
};
if (auto* vec = arg->Type()->As<sem::Vector>()) {
utils::Vector<const sem::Constant*, 4> fract_els;
utils::Vector<const sem::Constant*, 4> exp_els;
for (uint32_t i = 0; i < vec->Width(); i++) {
auto fe = scalar(arg->Index(i));
if (!fe.fract || !fe.exp) {
return utils::Failure;
}
fract_els.Push(fe.fract.Get());
exp_els.Push(fe.exp.Get());
}
auto fract_ty = builder.create<sem::Vector>(fract_els[0]->Type(), vec->Width());
auto exp_ty = builder.create<sem::Vector>(exp_els[0]->Type(), vec->Width());
return CreateComposite(builder, ty,
utils::Vector<const sem::Constant*, 2>{
CreateComposite(builder, fract_ty, std::move(fract_els)),
CreateComposite(builder, exp_ty, std::move(exp_els)),
});
} else {
auto fe = scalar(arg);
if (!fe.fract || !fe.exp) {
return utils::Failure;
}
return CreateComposite(builder, ty,
utils::Vector<const sem::Constant*, 2>{
fe.fract.Get(),
fe.exp.Get(),
});
}
}
ConstEval::Result ConstEval::insertBits(const sem::Type* ty,
utils::VectorRef<const sem::Constant*> args,
const Source& source) {

View File

@@ -610,6 +610,15 @@ class ConstEval {
utils::VectorRef<const sem::Constant*> args,
const Source& source);
/// frexp builtin
/// @param ty the expression type
/// @param args the input arguments
/// @param source the source location
/// @return the result value, or null if the value cannot be calculated
Result frexp(const sem::Type* ty,
utils::VectorRef<const sem::Constant*> args,
const Source& source);
/// insertBits builtin
/// @param ty the expression type
/// @param args the input arguments

View File

@@ -968,6 +968,62 @@ INSTANTIATE_TEST_SUITE_P( //
testing::ValuesIn(Concat(FloorCases<AFloat>(), //
FloorCases<f32>(),
FloorCases<f16>()))));
template <typename T>
std::vector<Case> FrexpCases() {
using F = T; // fract type
using E = std::conditional_t<std::is_same_v<T, AFloat>, AInt, i32>; // exp type
auto cases = std::vector<Case>{
// Scalar tests
// in fract exp
C({T(-3.5)}, {F(-0.875), E(2)}), //
C({T(-3.0)}, {F(-0.750), E(2)}), //
C({T(-2.5)}, {F(-0.625), E(2)}), //
C({T(-2.0)}, {F(-0.500), E(2)}), //
C({T(-1.5)}, {F(-0.750), E(1)}), //
C({T(-1.0)}, {F(-0.500), E(1)}), //
C({T(+0.0)}, {F(+0.000), E(0)}), //
C({T(+1.0)}, {F(+0.500), E(1)}), //
C({T(+1.5)}, {F(+0.750), E(1)}), //
C({T(+2.0)}, {F(+0.500), E(2)}), //
C({T(+2.5)}, {F(+0.625), E(2)}), //
C({T(+3.0)}, {F(+0.750), E(2)}), //
C({T(+3.5)}, {F(+0.875), E(2)}), //
// Vector tests
// in fract exp
C({Vec(T(-2.5), T(+1.0))}, {Vec(F(-0.625), F(+0.500)), Vec(E(2), E(1))}),
C({Vec(T(+3.5), T(-2.5))}, {Vec(F(+0.875), F(-0.625)), Vec(E(2), E(2))}),
};
ConcatIntoIf<std::is_same_v<T, f16>>(cases, std::vector<Case>{
C({T::Highest()}, {F(0x0.ffep0), E(16)}), //
C({T::Lowest()}, {F(-0x0.ffep0), E(16)}), //
C({T::Smallest()}, {F(0.5), E(-13)}), //
});
ConcatIntoIf<std::is_same_v<T, f32>>(cases,
std::vector<Case>{
C({T::Highest()}, {F(0x0.ffffffp0), E(128)}), //
C({T::Lowest()}, {F(-0x0.ffffffp0), E(128)}), //
C({T::Smallest()}, {F(0.5), E(-125)}), //
});
ConcatIntoIf<std::is_same_v<T, AFloat>>(
cases, std::vector<Case>{
C({T::Highest()}, {F(0x0.fffffffffffff8p0), E(1024)}), //
C({T::Lowest()}, {F(-0x0.fffffffffffff8p0), E(1024)}), //
C({T::Smallest()}, {F(0.5), E(-1021)}), //
});
return cases;
}
INSTANTIATE_TEST_SUITE_P( //
Frexp,
ResolverConstEvalBuiltinTest,
testing::Combine(testing::Values(sem::BuiltinType::kFrexp),
testing::ValuesIn(Concat(FrexpCases<AFloat>(), //
FrexpCases<f32>(), //
FrexpCases<f16>()))));
template <typename T>
std::vector<Case> InsertBitsCases() {

View File

@@ -855,6 +855,7 @@ const sem::Struct* build_modf_result(MatchState& state, const sem::Type* el) {
return nullptr;
});
}
const sem::Struct* build_modf_result_vec(MatchState& state, Number& n, const sem::Type* el) {
auto prefix = "__modf_result_vec" + std::to_string(n.Value());
auto build_f32 = [&] {
@@ -883,27 +884,70 @@ const sem::Struct* build_modf_result_vec(MatchState& state, Number& n, const sem
return nullptr;
});
}
const sem::Struct* build_frexp_result(MatchState& state, const sem::Type* el) {
std::string display_name;
if (el->Is<sem::F16>()) {
display_name = "__frexp_result_f16";
} else {
display_name = "__frexp_result";
}
auto* i32 = state.builder.create<sem::I32>();
return build_struct(state.builder, display_name, {{"fract", el}, {"exp", i32}});
auto build_f32 = [&] {
auto* f = state.builder.create<sem::F32>();
auto* i = state.builder.create<sem::I32>();
return build_struct(state.builder, "__frexp_result", {{"fract", f}, {"exp", i}});
};
auto build_f16 = [&] {
auto* f = state.builder.create<sem::F16>();
auto* i = state.builder.create<sem::I32>();
return build_struct(state.builder, "__frexp_result_f16", {{"fract", f}, {"exp", i}});
};
return Switch(
el, //
[&](const sem::F32*) { return build_f32(); }, //
[&](const sem::F16*) { return build_f16(); }, //
[&](const sem::AbstractFloat*) {
auto* i = state.builder.create<sem::AbstractInt>();
auto* abstract =
build_struct(state.builder, "__frexp_result_abstract", {{"fract", el}, {"exp", i}});
abstract->SetConcreteTypes(utils::Vector{build_f32(), build_f16()});
return abstract;
},
[&](Default) {
TINT_ICE(Resolver, state.builder.Diagnostics())
<< "unhandled frexp type: " << state.builder.FriendlyName(el);
return nullptr;
});
}
const sem::Struct* build_frexp_result_vec(MatchState& state, Number& n, const sem::Type* el) {
std::string display_name;
if (el->Is<sem::F16>()) {
display_name = "__frexp_result_vec" + std::to_string(n.Value()) + "_f16";
} else {
display_name = "__frexp_result_vec" + std::to_string(n.Value());
}
auto* vec = state.builder.create<sem::Vector>(el, n.Value());
auto* vec_i32 = state.builder.create<sem::Vector>(state.builder.create<sem::I32>(), n.Value());
return build_struct(state.builder, display_name, {{"fract", vec}, {"exp", vec_i32}});
auto prefix = "__frexp_result_vec" + std::to_string(n.Value());
auto build_f32 = [&] {
auto* f = state.builder.create<sem::Vector>(state.builder.create<sem::F32>(), n.Value());
auto* e = state.builder.create<sem::Vector>(state.builder.create<sem::I32>(), n.Value());
return build_struct(state.builder, prefix, {{"fract", f}, {"exp", e}});
};
auto build_f16 = [&] {
auto* f = state.builder.create<sem::Vector>(state.builder.create<sem::F16>(), n.Value());
auto* e = state.builder.create<sem::Vector>(state.builder.create<sem::I32>(), n.Value());
return build_struct(state.builder, prefix + "_f16", {{"fract", f}, {"exp", e}});
};
return Switch(
el, //
[&](const sem::F32*) { return build_f32(); }, //
[&](const sem::F16*) { return build_f16(); }, //
[&](const sem::AbstractFloat*) {
auto* f = state.builder.create<sem::Vector>(el, n.Value());
auto* e = state.builder.create<sem::Vector>(state.builder.create<sem::AbstractInt>(),
n.Value());
auto* abstract =
build_struct(state.builder, prefix + "_abstract", {{"fract", f}, {"exp", e}});
abstract->SetConcreteTypes(utils::Vector{build_f32(), build_f16()});
return abstract;
},
[&](Default) {
TINT_ICE(Resolver, state.builder.Diagnostics())
<< "unhandled frexp type: " << state.builder.FriendlyName(el);
return nullptr;
});
}
const sem::Struct* build_atomic_compare_exchange_result(MatchState& state, const sem::Type* ty) {
return build_struct(
state.builder,

View File

@@ -12558,24 +12558,24 @@ constexpr OverloadInfo kOverloads[] = {
/* num parameters */ 1,
/* num template types */ 1,
/* num template numbers */ 0,
/* template types */ &kTemplateTypes[26],
/* template types */ &kTemplateTypes[23],
/* template numbers */ &kTemplateNumbers[10],
/* parameters */ &kParameters[862],
/* return matcher indices */ &kMatcherIndices[104],
/* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
/* const eval */ nullptr,
/* const eval */ &ConstEval::frexp,
},
{
/* [354] */
/* num parameters */ 1,
/* num template types */ 1,
/* num template numbers */ 1,
/* template types */ &kTemplateTypes[26],
/* template types */ &kTemplateTypes[23],
/* template numbers */ &kTemplateNumbers[4],
/* parameters */ &kParameters[863],
/* return matcher indices */ &kMatcherIndices[39],
/* flags */ OverloadFlags(OverloadFlag::kIsBuiltin, OverloadFlag::kSupportsVertexPipeline, OverloadFlag::kSupportsFragmentPipeline, OverloadFlag::kSupportsComputePipeline),
/* const eval */ nullptr,
/* const eval */ &ConstEval::frexp,
},
{
/* [355] */
@@ -14259,8 +14259,8 @@ constexpr IntrinsicInfo kBuiltins[] = {
},
{
/* [40] */
/* fn frexp<T : f32_f16>(T) -> __frexp_result<T> */
/* fn frexp<N : num, T : f32_f16>(vec<N, T>) -> __frexp_result_vec<N, T> */
/* fn frexp<T : fa_f32_f16>(T) -> __frexp_result<T> */
/* fn frexp<N : num, T : fa_f32_f16>(vec<N, T>) -> __frexp_result_vec<N, T> */
/* num overloads */ 2,
/* overloads */ &kOverloads[353],
},

View File

@@ -1320,6 +1320,94 @@ TEST_F(MaterializeAbstractStructure, Modf_Vector_ExplicitType) {
abstract_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractFloat>());
}
TEST_F(MaterializeAbstractStructure, Frexp_Scalar_DefaultType) {
// var v = frexp(1);
auto* call = Call("frexp", 1_a);
WrapInFunction(Decl(Var("v", call)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(call);
ASSERT_TRUE(sem->Is<sem::Materialize>());
auto* materialize = sem->As<sem::Materialize>();
ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
auto* concrete_str = materialize->Type()->As<sem::Struct>();
ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::F32>());
ASSERT_TRUE(concrete_str->Members()[1]->Type()->Is<sem::I32>());
ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::AbstractFloat>());
ASSERT_TRUE(abstract_str->Members()[1]->Type()->Is<sem::AbstractInt>());
}
TEST_F(MaterializeAbstractStructure, Frexp_Vector_DefaultType) {
// var v = frexp(vec2(1));
auto* call = Call("frexp", Construct(ty.vec2(nullptr), 1_a));
WrapInFunction(Decl(Var("v", call)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(call);
ASSERT_TRUE(sem->Is<sem::Materialize>());
auto* materialize = sem->As<sem::Materialize>();
ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
auto* concrete_str = materialize->Type()->As<sem::Struct>();
ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::Vector>());
ASSERT_TRUE(concrete_str->Members()[1]->Type()->Is<sem::Vector>());
ASSERT_TRUE(concrete_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
ASSERT_TRUE(concrete_str->Members()[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::Vector>());
ASSERT_TRUE(
abstract_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractFloat>());
ASSERT_TRUE(
abstract_str->Members()[1]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractInt>());
}
TEST_F(MaterializeAbstractStructure, Frexp_Scalar_ExplicitType) {
// var v = frexp(1_h); // v is __frexp_result_f16
// v = frexp(1); // __frexp_result_f16 <- __frexp_result_abstract
Enable(ast::Extension::kF16);
auto* call = Call("frexp", 1_a);
WrapInFunction(Decl(Var("v", Call("frexp", 1_h))), //
Assign("v", call));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(call);
ASSERT_TRUE(sem->Is<sem::Materialize>());
auto* materialize = sem->As<sem::Materialize>();
ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
auto* concrete_str = materialize->Type()->As<sem::Struct>();
ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::F16>());
ASSERT_TRUE(concrete_str->Members()[1]->Type()->Is<sem::I32>());
ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::AbstractFloat>());
ASSERT_TRUE(abstract_str->Members()[1]->Type()->Is<sem::AbstractInt>());
}
TEST_F(MaterializeAbstractStructure, Frexp_Vector_ExplicitType) {
// var v = frexp(vec2(1_h)); // v is __frexp_result_vec2_f16
// v = frexp(vec2(1)); // __frexp_result_vec2_f16 <- __frexp_result_vec2_abstract
Enable(ast::Extension::kF16);
auto* call = Call("frexp", Construct(ty.vec2(nullptr), 1_a));
WrapInFunction(Decl(Var("v", Call("frexp", Construct(ty.vec2(nullptr), 1_h)))),
Assign("v", call));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(call);
ASSERT_TRUE(sem->Is<sem::Materialize>());
auto* materialize = sem->As<sem::Materialize>();
ASSERT_TRUE(materialize->Type()->Is<sem::Struct>());
auto* concrete_str = materialize->Type()->As<sem::Struct>();
ASSERT_TRUE(concrete_str->Members()[0]->Type()->Is<sem::Vector>());
ASSERT_TRUE(concrete_str->Members()[1]->Type()->Is<sem::Vector>());
ASSERT_TRUE(concrete_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::F16>());
ASSERT_TRUE(concrete_str->Members()[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
ASSERT_TRUE(materialize->Expr()->Type()->Is<sem::Struct>());
auto* abstract_str = materialize->Expr()->Type()->As<sem::Struct>();
ASSERT_TRUE(abstract_str->Members()[0]->Type()->Is<sem::Vector>());
ASSERT_TRUE(
abstract_str->Members()[0]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractFloat>());
ASSERT_TRUE(
abstract_str->Members()[1]->Type()->As<sem::Vector>()->type()->Is<sem::AbstractInt>());
}
} // namespace materialize_abstract_structure
} // namespace

View File

@@ -668,9 +668,9 @@ void main() {
)");
}
TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Scalar_f32) {
auto* call = Call("frexp", 1_f);
WrapInFunction(CallStmt(call));
TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Frexp_Scalar_f32) {
WrapInFunction(Var("f", Expr(1_f)), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -690,7 +690,8 @@ frexp_result tint_frexp(float param_0) {
void test_function() {
tint_frexp(1.0f);
float f = 1.0f;
frexp_result v = tint_frexp(f);
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -701,11 +702,11 @@ void main() {
)");
}
TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Scalar_f16) {
TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Frexp_Scalar_f16) {
Enable(ast::Extension::kF16);
auto* call = Call("frexp", 1_h);
WrapInFunction(CallStmt(call));
WrapInFunction(Var("f", Expr(1_h)), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -726,7 +727,8 @@ frexp_result_f16 tint_frexp(float16_t param_0) {
void test_function() {
tint_frexp(1.0hf);
float16_t f = 1.0hf;
frexp_result_f16 v = tint_frexp(f);
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -737,9 +739,9 @@ void main() {
)");
}
TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Vector_f32) {
auto* call = Call("frexp", vec3<f32>());
WrapInFunction(CallStmt(call));
TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Frexp_Vector_f32) {
WrapInFunction(Var("f", Expr(vec3<f32>())), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -759,7 +761,8 @@ frexp_result_vec3 tint_frexp(vec3 param_0) {
void test_function() {
tint_frexp(vec3(0.0f));
vec3 f = vec3(0.0f);
frexp_result_vec3 v = tint_frexp(f);
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -770,11 +773,11 @@ void main() {
)");
}
TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Vector_f16) {
TEST_F(GlslGeneratorImplTest_Builtin, Runtime_Frexp_Vector_f16) {
Enable(ast::Extension::kF16);
auto* call = Call("frexp", vec3<f16>());
WrapInFunction(CallStmt(call));
WrapInFunction(Var("f", Expr(vec3<f16>())), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -795,7 +798,118 @@ frexp_result_vec3_f16 tint_frexp(f16vec3 param_0) {
void test_function() {
tint_frexp(f16vec3(0.0hf));
f16vec3 f = f16vec3(0.0hf);
frexp_result_vec3_f16 v = tint_frexp(f);
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main() {
test_function();
return;
}
)");
}
TEST_F(GlslGeneratorImplTest_Builtin, Const_Frexp_Scalar_f32) {
WrapInFunction(Decl(Let("v", Call("frexp", 1_f))));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
struct frexp_result {
float fract;
int exp;
};
void test_function() {
frexp_result v = frexp_result(0.5f, 1);
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main() {
test_function();
return;
}
)");
}
TEST_F(GlslGeneratorImplTest_Builtin, Const_Frexp_Scalar_f16) {
Enable(ast::Extension::kF16);
WrapInFunction(Decl(Let("v", Call("frexp", 1_h))));
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 frexp_result_f16 {
float16_t fract;
int exp;
};
void test_function() {
frexp_result_f16 v = frexp_result_f16(0.5hf, 1);
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main() {
test_function();
return;
}
)");
}
TEST_F(GlslGeneratorImplTest_Builtin, Const_Frexp_Vector_f32) {
WrapInFunction(Decl(Let("v", Call("frexp", vec3<f32>()))));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
struct frexp_result_vec3 {
vec3 fract;
ivec3 exp;
};
void test_function() {
frexp_result_vec3 v = frexp_result_vec3(vec3(0.0f), ivec3(0));
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main() {
test_function();
return;
}
)");
}
TEST_F(GlslGeneratorImplTest_Builtin, Const_Frexp_Vector_f16) {
Enable(ast::Extension::kF16);
WrapInFunction(Decl(Let("v", Call("frexp", vec3<f16>()))));
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 frexp_result_vec3_f16 {
f16vec3 fract;
ivec3 exp;
};
void test_function() {
frexp_result_vec3_f16 v = frexp_result_vec3_f16(f16vec3(0.0hf), ivec3(0));
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
@@ -815,20 +929,8 @@ TEST_F(GlslGeneratorImplTest_Builtin, Frexp_Sig_Deprecation) {
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#version 310 es
struct frexp_result {
float fract;
int exp;
};
frexp_result tint_frexp(float param_0) {
frexp_result result;
result.fract = frexp(param_0, result.exp);
return result;
}
void test_function() {
float tint_symbol = tint_frexp(1.0f).fract;
float tint_symbol = 0.5f;
}
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

View File

@@ -572,7 +572,7 @@ TEST_F(HlslGeneratorImplTest_Builtin, NonInitializer_Modf_Vector_f32) {
Decl(Var("v", Call("modf", vec3<f32>(1.5_f, 2.5_f, 3.5_f)))),
// Now assign 'v' again with another modf call.
// This requires generating a temporary variable for the struct initializer.
Assign("v", Call("modf", vec3<f32>(4.5_f, 5.5_f, 6.5_f))));
Assign("v", Call("modf", vec3<f32>(4.5_a, 5.5_a, 6.5_a))));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -591,9 +591,9 @@ void test_function() {
)");
}
TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Scalar_f32) {
auto* call = Call("frexp", 1_f);
WrapInFunction(CallStmt(call));
TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Frexp_Scalar_f32) {
WrapInFunction(Var("f", Expr(1_f)), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -611,17 +611,18 @@ frexp_result tint_frexp(float param_0) {
[numthreads(1, 1, 1)]
void test_function() {
tint_frexp(1.0f);
float f = 1.0f;
frexp_result v = tint_frexp(f);
return;
}
)");
}
TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Scalar_f16) {
TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Frexp_Scalar_f16) {
Enable(ast::Extension::kF16);
auto* call = Call("frexp", 1_h);
WrapInFunction(CallStmt(call));
WrapInFunction(Var("f", Expr(1_h)), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -639,15 +640,16 @@ frexp_result_f16 tint_frexp(float16_t param_0) {
[numthreads(1, 1, 1)]
void test_function() {
tint_frexp(float16_t(1.0h));
float16_t f = float16_t(1.0h);
frexp_result_f16 v = tint_frexp(f);
return;
}
)");
}
TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Vector_f32) {
auto* call = Call("frexp", vec3<f32>());
WrapInFunction(CallStmt(call));
TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Frexp_Vector_f32) {
WrapInFunction(Var("f", Expr(vec3<f32>())), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -665,17 +667,18 @@ frexp_result_vec3 tint_frexp(float3 param_0) {
[numthreads(1, 1, 1)]
void test_function() {
tint_frexp((0.0f).xxx);
float3 f = (0.0f).xxx;
frexp_result_vec3 v = tint_frexp(f);
return;
}
)");
}
TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Vector_f16) {
TEST_F(HlslGeneratorImplTest_Builtin, Runtime_Frexp_Vector_f16) {
Enable(ast::Extension::kF16);
auto* call = Call("frexp", vec3<f16>());
WrapInFunction(CallStmt(call));
WrapInFunction(Var("f", Expr(vec3<f16>())), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -693,15 +696,15 @@ frexp_result_vec3_f16 tint_frexp(vector<float16_t, 3> param_0) {
[numthreads(1, 1, 1)]
void test_function() {
tint_frexp((float16_t(0.0h)).xxx);
vector<float16_t, 3> f = (float16_t(0.0h)).xxx;
frexp_result_vec3_f16 v = tint_frexp(f);
return;
}
)");
}
// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Sig_Deprecation) {
WrapInFunction(MemberAccessor(Call("frexp", 1_f), "sig"));
TEST_F(HlslGeneratorImplTest_Builtin, Const_Frexp_Scalar_f32) {
WrapInFunction(Decl(Let("v", Call("frexp", 1_f))));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -710,16 +713,114 @@ TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Sig_Deprecation) {
float fract;
int exp;
};
frexp_result tint_frexp(float param_0) {
float exp;
float fract = frexp(param_0, exp);
frexp_result result = {fract, int(exp)};
return result;
}
[numthreads(1, 1, 1)]
void test_function() {
const float tint_symbol = tint_frexp(1.0f).fract;
const frexp_result v = {0.5f, 1};
return;
}
)");
}
TEST_F(HlslGeneratorImplTest_Builtin, Const_Frexp_Scalar_f16) {
Enable(ast::Extension::kF16);
WrapInFunction(Decl(Let("v", Call("frexp", 1_h))));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(struct frexp_result_f16 {
float16_t fract;
int exp;
};
[numthreads(1, 1, 1)]
void test_function() {
const frexp_result_f16 v = {float16_t(0.5h), 1};
return;
}
)");
}
TEST_F(HlslGeneratorImplTest_Builtin, Const_Frexp_Vector_f32) {
WrapInFunction(Decl(Let("v", Call("frexp", vec3<f32>()))));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3 {
float3 fract;
int3 exp;
};
[numthreads(1, 1, 1)]
void test_function() {
const frexp_result_vec3 v = (frexp_result_vec3)0;
return;
}
)");
}
TEST_F(HlslGeneratorImplTest_Builtin, Const_Frexp_Vector_f16) {
Enable(ast::Extension::kF16);
WrapInFunction(Decl(Let("v", Call("frexp", vec3<f16>()))));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3_f16 {
vector<float16_t, 3> fract;
int3 exp;
};
[numthreads(1, 1, 1)]
void test_function() {
const frexp_result_vec3_f16 v = (frexp_result_vec3_f16)0;
return;
}
)");
}
TEST_F(HlslGeneratorImplTest_Builtin, NonInitializer_Frexp_Vector_f32) {
WrapInFunction(
// Declare a variable with the result of a frexp call.
// This is required to infer the 'var' type.
Decl(Var("v", Call("frexp", vec3<f32>(1.5_f, 2.5_f, 3.5_f)))),
// Now assign 'v' again with another frexp call.
// This requires generating a temporary variable for the struct initializer.
Assign("v", Call("frexp", vec3<f32>(4.5_a, 5.5_a, 6.5_a))));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(struct frexp_result_vec3 {
float3 fract;
int3 exp;
};
[numthreads(1, 1, 1)]
void test_function() {
frexp_result_vec3 v = {float3(0.75f, 0.625f, 0.875f), int3(1, 2, 2)};
const frexp_result_vec3 c = {float3(0.5625f, 0.6875f, 0.8125f), (3).xxx};
v = c;
return;
}
)");
}
// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
TEST_F(HlslGeneratorImplTest_Builtin, Frexp_Sig_Deprecation) {
WrapInFunction(Var("v", Call("frexp", 1_f)), //
MemberAccessor("v", "sig"));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(struct frexp_result {
float fract;
int exp;
};
[numthreads(1, 1, 1)]
void test_function() {
frexp_result v = {0.5f, 1};
const float tint_symbol = v.fract;
return;
}
)");

View File

@@ -621,9 +621,9 @@ kernel void test_function() {
)");
}
TEST_F(MslGeneratorImplTest, Frexp_Scalar_f32) {
auto* call = Call("frexp", 1_f);
WrapInFunction(CallStmt(call));
TEST_F(MslGeneratorImplTest, Runtime_Frexp_Scalar_f32) {
WrapInFunction(Var("f", Expr(1_f)), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -643,18 +643,19 @@ frexp_result tint_frexp(float param_0) {
}
kernel void test_function() {
tint_frexp(1.0f);
float f = 1.0f;
frexp_result v = tint_frexp(f);
return;
}
)");
}
TEST_F(MslGeneratorImplTest, Frexp_Scalar_f16) {
TEST_F(MslGeneratorImplTest, Runtime_Frexp_Scalar_f16) {
Enable(ast::Extension::kF16);
auto* call = Call("frexp", 1_h);
WrapInFunction(CallStmt(call));
WrapInFunction(Var("f", Expr(1_h)), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -674,16 +675,17 @@ frexp_result_f16 tint_frexp(half param_0) {
}
kernel void test_function() {
tint_frexp(1.0h);
half f = 1.0h;
frexp_result_f16 v = tint_frexp(f);
return;
}
)");
}
TEST_F(MslGeneratorImplTest, Frexp_Vector_f32) {
auto* call = Call("frexp", vec3<f32>());
WrapInFunction(CallStmt(call));
TEST_F(MslGeneratorImplTest, Runtime_Frexp_Vector_f32) {
WrapInFunction(Var("f", Expr(vec3<f32>())), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -703,18 +705,19 @@ frexp_result_vec3 tint_frexp(float3 param_0) {
}
kernel void test_function() {
tint_frexp(float3(0.0f));
float3 f = float3(0.0f);
frexp_result_vec3 v = tint_frexp(f);
return;
}
)");
}
TEST_F(MslGeneratorImplTest, Frexp_Vector_f16) {
TEST_F(MslGeneratorImplTest, Runtime_Frexp_Vector_f16) {
Enable(ast::Extension::kF16);
auto* call = Call("frexp", vec3<f16>());
WrapInFunction(CallStmt(call));
WrapInFunction(Var("f", Expr(vec3<f16>())), //
Var("v", Call("frexp", "f")));
GeneratorImpl& gen = SanitizeAndBuild();
@@ -734,7 +737,100 @@ frexp_result_vec3_f16 tint_frexp(half3 param_0) {
}
kernel void test_function() {
tint_frexp(half3(0.0h));
half3 f = half3(0.0h);
frexp_result_vec3_f16 v = tint_frexp(f);
return;
}
)");
}
TEST_F(MslGeneratorImplTest, Const_Frexp_Scalar_f32) {
WrapInFunction(Decl(Let("v", Call("frexp", 1_f))));
GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
using namespace metal;
struct frexp_result {
float fract;
int exp;
};
kernel void test_function() {
frexp_result const v = frexp_result{.fract=0.5f, .exp=1};
return;
}
)");
}
TEST_F(MslGeneratorImplTest, Const_Frexp_Scalar_f16) {
Enable(ast::Extension::kF16);
WrapInFunction(Decl(Let("v", Call("frexp", 1_h))));
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 fract;
int exp;
};
kernel void test_function() {
frexp_result_f16 const v = frexp_result_f16{.fract=0.5h, .exp=1};
return;
}
)");
}
TEST_F(MslGeneratorImplTest, Const_Frexp_Vector_f32) {
WrapInFunction(Decl(Let("v", Call("frexp", vec3<f32>()))));
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 fract;
int3 exp;
};
kernel void test_function() {
frexp_result_vec3 const v = frexp_result_vec3{};
return;
}
)");
}
TEST_F(MslGeneratorImplTest, Const_Frexp_Vector_f16) {
Enable(ast::Extension::kF16);
WrapInFunction(Decl(Let("v", Call("frexp", vec3<f16>()))));
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 fract;
int3 exp;
};
kernel void test_function() {
frexp_result_vec3_f16 const v = frexp_result_vec3_f16{};
return;
}
@@ -751,19 +847,8 @@ TEST_F(MslGeneratorImplTest, Frexp_Sig_Deprecation) {
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
using namespace metal;
struct frexp_result {
float fract;
int exp;
};
frexp_result tint_frexp(float param_0) {
frexp_result result;
result.fract = frexp(param_0, result.exp);
return result;
}
kernel void test_function() {
float const tint_symbol = tint_frexp(1.0f).fract;
float const tint_symbol = 0.5f;
return;
}

View File

@@ -1834,7 +1834,7 @@ OpFunctionEnd
Validate(b);
}
TEST_F(BuiltinBuilderTest, Call_Frexp_f32) {
TEST_F(BuiltinBuilderTest, Runtime_Call_Frexp_f32) {
auto* vec = Var("vec", vec2<f32>(1_f, 2_f));
auto* expr = Call("frexp", vec);
Func("a_func", utils::Empty, ty.void_(),
@@ -1888,7 +1888,7 @@ OpFunctionEnd
Validate(b);
}
TEST_F(BuiltinBuilderTest, Call_Frexp_f16) {
TEST_F(BuiltinBuilderTest, Runtime_Call_Frexp_f16) {
Enable(ast::Extension::kF16);
auto* vec = Var("vec", vec2<f16>(1_h, 2_h));
@@ -1948,15 +1948,10 @@ OpFunctionEnd
Validate(b);
}
// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
TEST_F(BuiltinBuilderTest, Frexp_Sig_Deprecation) {
WrapInFunction(MemberAccessor(Call("frexp", 1_f), "sig"));
auto* vec = Var("vec", vec2<f32>(1_f, 2_f));
TEST_F(BuiltinBuilderTest, Const_Call_Frexp_f32) {
Func("a_func", utils::Empty, ty.void_(),
utils::Vector{
Decl(vec),
Decl(Let("s", MemberAccessor(Call("frexp", vec), "sig"))),
CallStmt(Call("frexp", vec2<f32>(1_f, 2_f))),
},
utils::Vector{
Stage(ast::PipelineStage::kFragment),
@@ -1967,51 +1962,134 @@ TEST_F(BuiltinBuilderTest, Frexp_Sig_Deprecation) {
ASSERT_TRUE(b.Build()) << b.error();
auto got = DumpBuilder(b);
auto* expect = R"(OpCapability Shader
%9 = OpExtInstImport "GLSL.std.450"
%11 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %3 "test_function"
OpEntryPoint Fragment %12 "a_func"
OpExecutionMode %3 LocalSize 1 1 1
OpExecutionMode %12 OriginUpperLeft
OpName %3 "test_function"
OpName %6 "__frexp_result"
OpEntryPoint Fragment %3 "a_func"
OpExecutionMode %3 OriginUpperLeft
OpName %3 "a_func"
OpName %6 "__frexp_result_vec2"
OpMemberName %6 0 "fract"
OpMemberName %6 1 "exp"
OpName %12 "a_func"
OpName %17 "vec"
OpName %21 "__frexp_result_vec2"
OpMemberName %21 0 "fract"
OpMemberName %21 1 "exp"
OpMemberDecorate %6 0 Offset 0
OpMemberDecorate %6 1 Offset 4
OpMemberDecorate %21 0 Offset 0
OpMemberDecorate %21 1 Offset 8
OpMemberDecorate %6 1 Offset 8
%2 = OpTypeVoid
%1 = OpTypeFunction %2
%7 = OpTypeFloat 32
%8 = OpTypeInt 32 1
%6 = OpTypeStruct %7 %8
%10 = OpConstant %7 1
%14 = OpTypeVector %7 2
%15 = OpConstant %7 2
%16 = OpConstantComposite %14 %10 %15
%18 = OpTypePointer Function %14
%19 = OpConstantNull %14
%22 = OpTypeVector %8 2
%21 = OpTypeStruct %14 %22
%8 = OpTypeFloat 32
%7 = OpTypeVector %8 2
%10 = OpTypeInt 32 1
%9 = OpTypeVector %10 2
%6 = OpTypeStruct %7 %9
%12 = OpConstant %8 1
%13 = OpConstant %8 2
%14 = OpConstantComposite %7 %12 %13
%3 = OpFunction %2 None %1
%4 = OpLabel
%5 = OpExtInst %6 %9 FrexpStruct %10
%11 = OpCompositeExtract %7 %5 0
%5 = OpExtInst %6 %11 FrexpStruct %14
OpReturn
OpFunctionEnd
%12 = OpFunction %2 None %1
%13 = OpLabel
%17 = OpVariable %18 Function %19
OpStore %17 %16
%23 = OpLoad %14 %17
%20 = OpExtInst %21 %9 FrexpStruct %23
%24 = OpCompositeExtract %14 %20 0
)";
EXPECT_EQ(expect, got);
Validate(b);
}
TEST_F(BuiltinBuilderTest, Const_Call_Frexp_f16) {
Enable(ast::Extension::kF16);
Func("a_func", utils::Empty, ty.void_(),
utils::Vector{
CallStmt(Call("frexp", vec2<f16>(1_h, 2_h))),
},
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
%11 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %3 "a_func"
OpExecutionMode %3 OriginUpperLeft
OpName %3 "a_func"
OpName %6 "__frexp_result_vec2_f16"
OpMemberName %6 0 "fract"
OpMemberName %6 1 "exp"
OpMemberDecorate %6 0 Offset 0
OpMemberDecorate %6 1 Offset 8
%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 16
%7 = OpTypeVector %8 2
%10 = OpTypeInt 32 1
%9 = OpTypeVector %10 2
%6 = OpTypeStruct %7 %9
%12 = OpConstant %8 0x1p+0
%13 = OpConstant %8 0x1p+1
%14 = OpConstantComposite %7 %12 %13
%3 = OpFunction %2 None %1
%4 = OpLabel
%5 = OpExtInst %6 %11 FrexpStruct %14
OpReturn
OpFunctionEnd
)";
EXPECT_EQ(expect, got);
Validate(b);
}
// TODO(crbug.com/tint/1757): Remove once deprecation period for `frexp().sig` is over
TEST_F(BuiltinBuilderTest, Frexp_Sig_Deprecation) {
Func("a_func", utils::Empty, ty.void_(),
utils::Vector{
Decl(Var("vec", vec2<f32>(1_f, 2_f))),
Decl(Let("s", MemberAccessor(Call("frexp", "vec"), "sig"))),
},
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
%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"
OpMemberName %14 0 "fract"
OpMemberName %14 1 "exp"
OpMemberDecorate %14 0 Offset 0
OpMemberDecorate %14 1 Offset 8
%2 = OpTypeVoid
%1 = OpTypeFunction %2
%6 = OpTypeFloat 32
%5 = OpTypeVector %6 2
%7 = OpConstant %6 1
%8 = OpConstant %6 2
%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
%19 = OpCompositeExtract %5 %13 0
OpReturn
OpFunctionEnd
)";