spirv-writer: support isNormal
Fixed: tint:158 Change-Id: Iabe7c1afe7dea87e62277bacb2086ee6d2964e78 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/52460 Auto-Submit: David Neto <dneto@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: David Neto <dneto@google.com> Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
parent
5ed3f68eb5
commit
5c0820c76b
|
@ -32,6 +32,7 @@
|
|||
#include "src/sem/sampled_texture_type.h"
|
||||
#include "src/sem/struct.h"
|
||||
#include "src/sem/variable.h"
|
||||
#include "src/sem/vector_type.h"
|
||||
#include "src/utils/get_or_create.h"
|
||||
#include "src/writer/append_vector.h"
|
||||
|
||||
|
@ -2251,6 +2252,83 @@ uint32_t Builder::GenerateIntrinsic(ast::CallExpression* call,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
case IntrinsicType::kIsNormal: {
|
||||
// A normal number is finite, non-zero, and not subnormal.
|
||||
// Its exponent is neither of the extreme possible values.
|
||||
// Implemented as:
|
||||
// exponent_bits = bitcast<u32>(f);
|
||||
// clamped = uclamp(1,254,exponent_bits);
|
||||
// result = (clamped == exponent_bits);
|
||||
//
|
||||
auto val_id = get_param_as_value_id(0);
|
||||
if (!val_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// These parameters are valid for IEEE 754 binary32
|
||||
const uint32_t kExponentMask = 0x7f80000;
|
||||
const uint32_t kMinNormalExponent = 0x0080000;
|
||||
const uint32_t kMaxNormalExponent = 0x7f00000;
|
||||
|
||||
auto set_id = GetGLSLstd450Import();
|
||||
sem::U32 u32;
|
||||
auto unsigned_id = GenerateTypeIfNeeded(&u32);
|
||||
auto exponent_mask_id =
|
||||
GenerateConstantIfNeeded(ScalarConstant::U32(kExponentMask));
|
||||
auto min_exponent_id =
|
||||
GenerateConstantIfNeeded(ScalarConstant::U32(kMinNormalExponent));
|
||||
auto max_exponent_id =
|
||||
GenerateConstantIfNeeded(ScalarConstant::U32(kMaxNormalExponent));
|
||||
if (auto* fvec_ty = intrinsic->ReturnType()->As<sem::Vector>()) {
|
||||
// In the vector case, update the unsigned type to a vector type of the
|
||||
// same size, and create vector constants by replicating the scalars.
|
||||
// I expect backend compilers to fold these into unique constants, so
|
||||
// there is no loss of efficiency.
|
||||
sem::Vector uvec_ty(&u32, fvec_ty->size());
|
||||
unsigned_id = GenerateTypeIfNeeded(&uvec_ty);
|
||||
auto splat = [&](uint32_t scalar_id) -> uint32_t {
|
||||
auto splat_result = result_op();
|
||||
OperandList splat_params{Operand::Int(unsigned_id), splat_result};
|
||||
for (size_t i = 0; i < fvec_ty->size(); i++) {
|
||||
splat_params.emplace_back(Operand::Int(scalar_id));
|
||||
}
|
||||
if (!push_function_inst(spv::Op::OpCompositeConstruct,
|
||||
std::move(splat_params))) {
|
||||
return 0;
|
||||
}
|
||||
return splat_result.to_i();
|
||||
};
|
||||
exponent_mask_id = splat(exponent_mask_id);
|
||||
min_exponent_id = splat(min_exponent_id);
|
||||
max_exponent_id = splat(max_exponent_id);
|
||||
}
|
||||
auto cast_result = result_op();
|
||||
auto exponent_bits_result = result_op();
|
||||
auto clamp_result = result_op();
|
||||
|
||||
if (set_id && unsigned_id && exponent_mask_id && min_exponent_id &&
|
||||
max_exponent_id &&
|
||||
push_function_inst(
|
||||
spv::Op::OpBitcast,
|
||||
{Operand::Int(unsigned_id), cast_result, Operand::Int(val_id)}) &&
|
||||
push_function_inst(spv::Op::OpBitwiseAnd,
|
||||
{Operand::Int(unsigned_id), exponent_bits_result,
|
||||
Operand::Int(cast_result.to_i()),
|
||||
Operand::Int(exponent_mask_id)}) &&
|
||||
push_function_inst(
|
||||
spv::Op::OpExtInst,
|
||||
{Operand::Int(unsigned_id), clamp_result, Operand::Int(set_id),
|
||||
Operand::Int(GLSLstd450UClamp),
|
||||
Operand::Int(exponent_bits_result.to_i()),
|
||||
Operand::Int(min_exponent_id), Operand::Int(max_exponent_id)}) &&
|
||||
push_function_inst(spv::Op::OpIEqual,
|
||||
{Operand::Int(result_type_id), result,
|
||||
Operand::Int(exponent_bits_result.to_i()),
|
||||
Operand::Int(clamp_result.to_i())})) {
|
||||
return result_id;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case IntrinsicType::kReverseBits:
|
||||
op = spv::Op::OpBitReverse;
|
||||
break;
|
||||
|
|
|
@ -184,6 +184,97 @@ TEST_F(IntrinsicBuilderTest, IsFinite_Vector) {
|
|||
)");
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicBuilderTest, IsNormal_Scalar) {
|
||||
auto* var = Global("v", ty.f32(), ast::StorageClass::kPrivate);
|
||||
|
||||
auto* expr = Call("isNormal", "v");
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{}, ast::DecorationList{});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
|
||||
ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
|
||||
|
||||
EXPECT_EQ(b.GenerateCallExpression(expr), 9u) << b.error();
|
||||
auto got = DumpBuilder(b);
|
||||
EXPECT_EQ(got, R"(%12 = OpExtInstImport "GLSL.std.450"
|
||||
OpName %1 "v"
|
||||
OpName %7 "a_func"
|
||||
%3 = OpTypeFloat 32
|
||||
%2 = OpTypePointer Private %3
|
||||
%4 = OpConstantNull %3
|
||||
%1 = OpVariable %2 Private %4
|
||||
%6 = OpTypeVoid
|
||||
%5 = OpTypeFunction %6
|
||||
%10 = OpTypeBool
|
||||
%13 = OpTypeInt 32 0
|
||||
%14 = OpConstant %13 133693440
|
||||
%15 = OpConstant %13 524288
|
||||
%16 = OpConstant %13 133169152
|
||||
%7 = OpFunction %6 None %5
|
||||
%8 = OpLabel
|
||||
%11 = OpLoad %3 %1
|
||||
%17 = OpBitcast %13 %11
|
||||
%18 = OpBitwiseAnd %13 %17 %14
|
||||
%19 = OpExtInst %13 %12 UClamp %18 %15 %16
|
||||
%9 = OpIEqual %10 %18 %19
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_F(IntrinsicBuilderTest, IsNormal_Vector) {
|
||||
auto* var = Global("v", ty.vec2<f32>(), ast::StorageClass::kPrivate);
|
||||
|
||||
auto* expr = Call("isNormal", "v");
|
||||
WrapInFunction(expr);
|
||||
|
||||
auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
|
||||
ast::StatementList{}, ast::DecorationList{});
|
||||
|
||||
spirv::Builder& b = Build();
|
||||
|
||||
ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
|
||||
ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
|
||||
|
||||
EXPECT_EQ(b.GenerateCallExpression(expr), 10u) << b.error();
|
||||
auto got = DumpBuilder(b);
|
||||
std::cout << got << std::endl;
|
||||
EXPECT_EQ(got, R"(%14 = OpExtInstImport "GLSL.std.450"
|
||||
OpName %1 "v"
|
||||
OpName %8 "a_func"
|
||||
%4 = OpTypeFloat 32
|
||||
%3 = OpTypeVector %4 2
|
||||
%2 = OpTypePointer Private %3
|
||||
%5 = OpConstantNull %3
|
||||
%1 = OpVariable %2 Private %5
|
||||
%7 = OpTypeVoid
|
||||
%6 = OpTypeFunction %7
|
||||
%12 = OpTypeBool
|
||||
%11 = OpTypeVector %12 2
|
||||
%15 = OpTypeInt 32 0
|
||||
%16 = OpConstant %15 133693440
|
||||
%17 = OpConstant %15 524288
|
||||
%18 = OpConstant %15 133169152
|
||||
%19 = OpTypeVector %15 2
|
||||
%8 = OpFunction %7 None %6
|
||||
%9 = OpLabel
|
||||
%13 = OpLoad %3 %1
|
||||
%20 = OpCompositeConstruct %19 %16 %16
|
||||
%21 = OpCompositeConstruct %19 %17 %17
|
||||
%22 = OpCompositeConstruct %19 %18 %18
|
||||
%23 = OpBitcast %19 %13
|
||||
%24 = OpBitwiseAnd %19 %23 %20
|
||||
%25 = OpExtInst %19 %14 UClamp %24 %21 %22
|
||||
%10 = OpIEqual %11 %24 %25
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)");
|
||||
}
|
||||
|
||||
using IntrinsicIntTest = IntrinsicBuilderTestWithParam<IntrinsicData>;
|
||||
TEST_P(IntrinsicIntTest, Call_SInt_Scalar) {
|
||||
auto param = GetParam();
|
||||
|
|
Loading…
Reference in New Issue