tint/writers: Polyfill integer clamp()

...to `min(max(e, low), high)` as defined by the WGSL spec.

Fixed: tint:1479
Fixed: tint:1539
Change-Id: I39406d5256a155a781e44bd9d6081ce7a9bf5a68
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/107640
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
Ben Clayton
2022-10-31 17:54:49 +00:00
committed by Dawn LUCI CQ
parent be83128031
commit 6dbb463f1d
42 changed files with 822 additions and 367 deletions

View File

@@ -137,6 +137,27 @@ struct BuiltinPolyfill::State {
return name;
}
/// Builds the polyfill function for the `clamp` builtin when called with integer arguments
/// (scalar or vector)
/// @param ty the parameter and return type for the function
/// @return the polyfill function name
Symbol clampInteger(const sem::Type* ty) {
auto name = b.Symbols().New("tint_clamp");
b.Func(name,
utils::Vector{
b.Param("e", T(ty)),
b.Param("low", T(ty)),
b.Param("high", T(ty)),
},
T(ty),
utils::Vector{
// return min(max(e, low), high);
b.Return(b.Call("min", b.Call("max", "e", "low"), "high")),
});
return name;
}
/// Builds the polyfill function for the `countLeadingZeros` builtin
/// @param ty the parameter and return type for the function
/// @return the polyfill function name
@@ -588,6 +609,12 @@ bool BuiltinPolyfill::ShouldRun(const Program* program, const DataMap& data) con
return true;
}
break;
case sem::BuiltinType::kClamp:
if (builtins.clamp_int) {
auto& sig = builtin->Signature();
return sig.parameters[0]->Type()->is_integer_scalar_or_vector();
}
break;
case sem::BuiltinType::kCountLeadingZeros:
if (builtins.count_leading_zeros) {
return true;
@@ -679,6 +706,16 @@ void BuiltinPolyfill::Run(CloneContext& ctx, const DataMap& data, DataMap&) cons
polyfills, builtin, [&] { return s.atanh(builtin->ReturnType()); });
}
break;
case sem::BuiltinType::kClamp:
if (builtins.clamp_int) {
auto& sig = builtin->Signature();
if (sig.parameters[0]->Type()->is_integer_scalar_or_vector()) {
polyfill = utils::GetOrCreate(polyfills, builtin, [&] {
return s.clampInteger(builtin->ReturnType());
});
}
}
break;
case sem::BuiltinType::kCountLeadingZeros:
if (builtins.count_leading_zeros) {
polyfill = utils::GetOrCreate(polyfills, builtin, [&] {

View File

@@ -47,6 +47,8 @@ class BuiltinPolyfill final : public Castable<BuiltinPolyfill, Transform> {
bool asinh = false;
/// What level should `atanh` be polyfilled?
Level atanh = Level::kNone;
/// Should `clamp()` be polyfilled for integer values (scalar or vector)?
bool clamp_int = false;
/// Should `countLeadingZeros()` be polyfilled?
bool count_leading_zeros = false;
/// Should `countTrailingZeros()` be polyfilled?

View File

@@ -398,6 +398,173 @@ fn f() {
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// clampInteger
////////////////////////////////////////////////////////////////////////////////
DataMap polyfillClampInteger() {
BuiltinPolyfill::Builtins builtins;
builtins.clamp_int = true;
DataMap data;
data.Add<BuiltinPolyfill::Config>(builtins);
return data;
}
TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_i32) {
auto* src = R"(
fn f() {
let v = 1i;
clamp(v, 2i, 3i);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
}
TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_u32) {
auto* src = R"(
fn f() {
let v = 1u;
clamp(v, 2u, 3u);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
}
TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_f32) {
auto* src = R"(
fn f() {
let v = 1f;
clamp(v, 2f, 3f);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
}
TEST_F(BuiltinPolyfillTest, ShouldRunClampInteger_f16) {
auto* src = R"(
enable f16;
fn f() {
let v = 1h;
clamp(v, 2h, 3h);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillClampInteger()));
}
TEST_F(BuiltinPolyfillTest, ClampInteger_ConstantExpression) {
auto* src = R"(
fn f() {
let r : i32 = clamp(1i, 2i, 3i);
}
)";
auto* expect = src;
auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, ClampInteger_i32) {
auto* src = R"(
fn f() {
let v = 1i;
let r : i32 = clamp(v, 2i, 3i);
}
)";
auto* expect = R"(
fn tint_clamp(e : i32, low : i32, high : i32) -> i32 {
return min(max(e, low), high);
}
fn f() {
let v = 1i;
let r : i32 = tint_clamp(v, 2i, 3i);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, ClampInteger_vec3_i32) {
auto* src = R"(
fn f() {
let v = 1i;
let r : vec3<i32> = clamp(vec3(v), vec3(2i), vec3(3i));
}
)";
auto* expect =
R"(
fn tint_clamp(e : vec3<i32>, low : vec3<i32>, high : vec3<i32>) -> vec3<i32> {
return min(max(e, low), high);
}
fn f() {
let v = 1i;
let r : vec3<i32> = tint_clamp(vec3(v), vec3(2i), vec3(3i));
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, ClampInteger_u32) {
auto* src = R"(
fn f() {
let r : u32 = clamp(1u, 2u, 3u);
}
)";
auto* expect = R"(
fn f() {
let r : u32 = clamp(1u, 2u, 3u);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, ClampInteger_vec3_u32) {
auto* src = R"(
fn f() {
let v = 1u;
let r : vec3<u32> = clamp(vec3(v), vec3(2u), vec3(3u));
}
)";
auto* expect =
R"(
fn tint_clamp(e : vec3<u32>, low : vec3<u32>, high : vec3<u32>) -> vec3<u32> {
return min(max(e, low), high);
}
fn f() {
let v = 1u;
let r : vec3<u32> = tint_clamp(vec3(v), vec3(2u), vec3(3u));
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillClampInteger());
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// countLeadingZeros
////////////////////////////////////////////////////////////////////////////////

View File

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

View File

@@ -171,6 +171,7 @@ SanitizedResult Sanitize(const Program* in, const Options& options) {
transform::BuiltinPolyfill::Builtins polyfills;
polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.clamp_int = true;
polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
polyfills.first_leading_bit = true;
polyfills.first_trailing_bit = true;

View File

@@ -52,6 +52,7 @@ SanitizedResult Sanitize(const Program* in, const Options& options) {
transform::BuiltinPolyfill::Builtins polyfills;
polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.clamp_int = true;
polyfills.count_leading_zeros = true;
polyfills.count_trailing_zeros = true;
polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;