tint/transform: Polyfill f32 conversion to i32 or u32

And vectors of those types.

Fixed: tint:1866
Change-Id: Ic0b5061232c5eb5a67d43dde1cd9599c580f4388
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/123201
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Kokoro: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
Ben Clayton
2023-03-09 21:26:12 +00:00
committed by Dawn LUCI CQ
parent 905b63b90f
commit cc3f851ad8
132 changed files with 2954 additions and 1755 deletions

View File

@@ -23,6 +23,7 @@
#include "src/tint/sem/builtin.h"
#include "src/tint/sem/call.h"
#include "src/tint/sem/type_expression.h"
#include "src/tint/sem/value_conversion.h"
#include "src/tint/switch.h"
#include "src/tint/type/storage_texture.h"
#include "src/tint/type/texture_dimension.h"
@@ -135,6 +136,28 @@ struct BuiltinPolyfill::State {
return Program(std::move(b));
}
private:
/// The source program
Program const* const src;
/// The transform config
const Config& cfg;
/// The destination program builder
ProgramBuilder b;
/// The clone context
CloneContext ctx{&b, src};
/// The source clone context
const sem::Info& sem = src->Sem();
/// Polyfill functions for binary operators.
utils::Hashmap<BinaryOpSignature, Symbol, 8> binary_op_polyfills;
/// Polyfill builtins.
utils::Hashmap<const sem::Builtin*, Symbol, 8> builtin_polyfills;
/// Polyfill f32 conversion to i32 or u32 (or vectors of)
utils::Hashmap<const type::Type*, Symbol, 2> f32_conv_polyfills;
// Tracks whether the chromium_experimental_full_ptr_parameters extension has been enabled.
bool has_full_ptr_params = false;
/// True if the transform has made changes (i.e. the program needs cloning)
bool made_changes = false;
////////////////////////////////////////////////////////////////////////////
// Function polyfills
////////////////////////////////////////////////////////////////////////////
@@ -802,6 +825,52 @@ struct BuiltinPolyfill::State {
return name;
}
/// Builds the polyfill function to value convert a scalar or vector of f32 to an i32 or u32 (or
/// vector of).
/// @param source the type of the value being converted
/// @param target the target conversion type
/// @return the polyfill function name
Symbol ConvF32ToIU32(const type::Type* source, const type::Type* target) {
struct Limits {
AFloat low_condition;
AInt low_limit;
AFloat high_condition;
AInt high_limit;
};
const bool is_signed = target->is_signed_integer_scalar_or_vector();
const Limits limits = is_signed ? Limits{
/* low_condition */ -AFloat(0x80000000),
/* low_limit */ -AInt(0x80000000),
/* high_condition */ AFloat(0x7fffff80),
/* high_limit */ AInt(0x7fffffff),
}
: Limits{
/* low_condition */ AFloat(0),
/* low_limit */ AInt(0),
/* high_condition */ AFloat(0xffffff00),
/* high_limit */ AInt(0xffffffff),
};
const uint32_t width = WidthOf(target);
// select(target(v), low_limit, v < low_condition)
auto* select_low = b.Call(builtin::Function::kSelect, //
b.Call(T(target), "v"), //
ScalarOrVector(width, limits.low_limit), //
b.LessThan("v", ScalarOrVector(width, limits.low_condition)));
// select(high_limit, select_low, v < high_condition)
auto* select_high = b.Call(builtin::Function::kSelect, //
ScalarOrVector(width, limits.high_limit), //
select_low, //
b.LessThan("v", ScalarOrVector(width, limits.high_condition)));
auto name = b.Symbols().New(is_signed ? "tint_ftoi" : "tint_ftou");
b.Func(name, utils::Vector{b.Param("v", T(source))}, T(target),
utils::Vector{b.Return(select_high)});
return name;
}
////////////////////////////////////////////////////////////////////////////
// Inline polyfills
////////////////////////////////////////////////////////////////////////////
@@ -971,26 +1040,6 @@ struct BuiltinPolyfill::State {
return b.Call(fn, lhs, rhs);
}
private:
/// The source program
Program const* const src;
/// The transform config
const Config& cfg;
/// The destination program builder
ProgramBuilder b;
/// The clone context
CloneContext ctx{&b, src};
/// The source clone context
const sem::Info& sem = src->Sem();
/// Polyfill functions for binary operators.
utils::Hashmap<BinaryOpSignature, Symbol, 8> binary_op_polyfills;
/// Polyfill builtins.
utils::Hashmap<const sem::Builtin*, Symbol, 8> builtin_polyfills;
// Tracks whether the chromium_experimental_full_ptr_parameters extension has been enabled.
bool has_full_ptr_params = false;
/// True if the transform has made changes (i.e. the program needs cloning)
bool made_changes = false;
/// @returns the AST type for the given sem type
ast::Type T(const type::Type* ty) { return CreateASTTypeFor(ctx, ty); }
@@ -1195,6 +1244,20 @@ struct BuiltinPolyfill::State {
default:
return Symbol{};
}
},
[&](const sem::ValueConversion* conv) {
if (cfg.builtins.conv_f32_to_iu32) {
auto* src_ty = conv->Source();
if (tint::Is<type::F32>(type::Type::ElementOf(src_ty))) {
auto* dst_ty = conv->Target();
if (tint::IsAnyOf<type::I32, type::U32>(type::Type::ElementOf(dst_ty))) {
return f32_conv_polyfills.GetOrCreate(dst_ty, [&] { //
return ConvF32ToIU32(src_ty, dst_ty);
});
}
}
}
return Symbol{};
});
if (fn.IsValid()) {

View File

@@ -57,6 +57,8 @@ class BuiltinPolyfill final : public Castable<BuiltinPolyfill, Transform> {
bool count_leading_zeros = false;
/// Should `countTrailingZeros()` be polyfilled?
bool count_trailing_zeros = false;
/// Should converting f32 to i32 or u32 be polyfilled?
bool conv_f32_to_iu32 = false;
/// What level should `extractBits()` be polyfilled?
Level extract_bits = Level::kNone;
/// Should `firstLeadingBit()` be polyfilled?

View File

@@ -814,6 +814,157 @@ fn f() {
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// conv_f32_to_iu32
////////////////////////////////////////////////////////////////////////////////
DataMap polyfillConvF32ToIU32() {
BuiltinPolyfill::Builtins builtins;
builtins.conv_f32_to_iu32 = true;
DataMap data;
data.Add<BuiltinPolyfill::Config>(builtins);
return data;
}
TEST_F(BuiltinPolyfillTest, ShouldRunConvF32ToI32) {
auto* src = R"(
fn f() {
let f = 42.0;
_ = i32(f);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
}
TEST_F(BuiltinPolyfillTest, ShouldRunConvF32ToU32) {
auto* src = R"(
fn f() {
let f = 42.0;
_ = u32(f);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
}
TEST_F(BuiltinPolyfillTest, ShouldRunConvVec3F32ToVec3I32) {
auto* src = R"(
fn f() {
let f = vec3(42.0);
_ = vec3<i32>(f);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
}
TEST_F(BuiltinPolyfillTest, ShouldRunConvVec3F32ToVec3U32) {
auto* src = R"(
fn f() {
let f = vec3(42.0);
_ = vec3<u32>(f);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillConvF32ToIU32()));
}
TEST_F(BuiltinPolyfillTest, ConvF32ToI32) {
auto* src = R"(
fn f() {
let f = 42.0;
_ = i32(f);
}
)";
auto* expect = R"(
fn tint_ftoi(v : f32) -> i32 {
return select(2147483647, select(i32(v), -2147483648, (v < -2147483648.0)), (v < 2147483520.0));
}
fn f() {
let f = 42.0;
_ = tint_ftoi(f);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, ConvF32ToU32) {
auto* src = R"(
fn f() {
let f = 42.0;
_ = u32(f);
}
)";
auto* expect = R"(
fn tint_ftou(v : f32) -> u32 {
return select(4294967295, select(u32(v), 0, (v < 0.0)), (v < 4294967040.0));
}
fn f() {
let f = 42.0;
_ = tint_ftou(f);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, ConvVec3F32ToVec3I32) {
auto* src = R"(
fn f() {
let f = vec3(42.0);
_ = vec3<i32>(f);
}
)";
auto* expect = R"(
fn tint_ftoi(v : vec3<f32>) -> vec3<i32> {
return select(vec3(2147483647), select(vec3<i32>(v), vec3(-2147483648), (v < vec3(-2147483648.0))), (v < vec3(2147483520.0)));
}
fn f() {
let f = vec3(42.0);
_ = tint_ftoi(f);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, ConvVec3F32ToVec3U32) {
auto* src = R"(
fn f() {
let f = vec3(42.0);
_ = vec3<u32>(f);
}
)";
auto* expect = R"(
fn tint_ftou(v : vec3<f32>) -> vec3<u32> {
return select(vec3(4294967295), select(vec3<u32>(v), vec3(0), (v < vec3(0.0))), (v < vec3(4294967040.0)));
}
fn f() {
let f = vec3(42.0);
_ = tint_ftou(f);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillConvF32ToIU32());
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// countLeadingZeros
////////////////////////////////////////////////////////////////////////////////

View File

@@ -193,6 +193,7 @@ SanitizedResult Sanitize(const Program* in,
polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.bgra8unorm = true;
polyfills.bitshift_modulo = true;
polyfills.conv_f32_to_iu32 = true;
polyfills.count_leading_zeros = true;
polyfills.count_trailing_zeros = true;
polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;

View File

@@ -204,6 +204,7 @@ SanitizedResult Sanitize(const Program* in, const Options& options) {
polyfills.clamp_int = true;
// TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
// and `firstbithigh`.
polyfills.conv_f32_to_iu32 = true;
polyfills.count_leading_zeros = true;
polyfills.count_trailing_zeros = true;
polyfills.extract_bits = transform::BuiltinPolyfill::Level::kFull;

View File

@@ -219,6 +219,7 @@ SanitizedResult Sanitize(const Program* in, const Options& options) {
polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.bitshift_modulo = true; // crbug.com/tint/1543
polyfills.clamp_int = true;
polyfills.conv_f32_to_iu32 = true;
polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
polyfills.first_leading_bit = true;
polyfills.first_trailing_bit = true;

View File

@@ -89,6 +89,7 @@ SanitizedResult Sanitize(const Program* in, const Options& options) {
polyfills.bgra8unorm = true;
polyfills.bitshift_modulo = true;
polyfills.clamp_int = true;
polyfills.conv_f32_to_iu32 = true;
polyfills.count_leading_zeros = true;
polyfills.count_trailing_zeros = true;
polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;