tint: Implement acosh, asinh, atanh

Polyfill them completely for HLSL.

For the other backends, just add range checks for acosh and atanh.

Fixed: tint:1465
Change-Id: I3abda99b474d9f5ba09abf400381467dc28ea0bd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94380
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Auto-Submit: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
dan sinclair
2022-06-28 15:27:44 +00:00
committed by Dawn LUCI CQ
parent 6058882d4b
commit d23f296a9a
157 changed files with 11784 additions and 4686 deletions

View File

@@ -333,6 +333,8 @@ fn abs<T: fiu32>(T) -> T
fn abs<N: num, T: fiu32>(vec<N, T>) -> vec<N, T>
fn acos(f32) -> f32
fn acos<N: num>(vec<N, f32>) -> vec<N, f32>
fn acosh(f32) -> f32
fn acosh<N: num>(vec<N, f32>) -> vec<N, f32>
fn all(bool) -> bool
fn all<N: num>(vec<N, bool>) -> bool
fn any(bool) -> bool
@@ -340,10 +342,14 @@ fn any<N: num>(vec<N, bool>) -> bool
fn arrayLength<T, A: access>(ptr<storage, array<T>, A>) -> u32
fn asin(f32) -> f32
fn asin<N: num>(vec<N, f32>) -> vec<N, f32>
fn asinh(f32) -> f32
fn asinh<N: num>(vec<N, f32>) -> vec<N, f32>
fn atan(f32) -> f32
fn atan<N: num>(vec<N, f32>) -> vec<N, f32>
fn atan2(f32, f32) -> f32
fn atan2<N: num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32>
fn atanh(f32) -> f32
fn atanh<N: num>(vec<N, f32>) -> vec<N, f32>
fn ceil(f32) -> f32
fn ceil<N: num>(vec<N, f32>) -> vec<N, f32>
fn clamp<T: fiu32>(T, T, T) -> T

File diff suppressed because it is too large Load Diff

View File

@@ -35,6 +35,9 @@ BuiltinType ParseBuiltinType(const std::string& name) {
if (name == "acos") {
return BuiltinType::kAcos;
}
if (name == "acosh") {
return BuiltinType::kAcosh;
}
if (name == "all") {
return BuiltinType::kAll;
}
@@ -47,12 +50,18 @@ BuiltinType ParseBuiltinType(const std::string& name) {
if (name == "asin") {
return BuiltinType::kAsin;
}
if (name == "asinh") {
return BuiltinType::kAsinh;
}
if (name == "atan") {
return BuiltinType::kAtan;
}
if (name == "atan2") {
return BuiltinType::kAtan2;
}
if (name == "atanh") {
return BuiltinType::kAtanh;
}
if (name == "ceil") {
return BuiltinType::kCeil;
}
@@ -358,6 +367,8 @@ const char* str(BuiltinType i) {
return "abs";
case BuiltinType::kAcos:
return "acos";
case BuiltinType::kAcosh:
return "acosh";
case BuiltinType::kAll:
return "all";
case BuiltinType::kAny:
@@ -366,10 +377,14 @@ const char* str(BuiltinType i) {
return "arrayLength";
case BuiltinType::kAsin:
return "asin";
case BuiltinType::kAsinh:
return "asinh";
case BuiltinType::kAtan:
return "atan";
case BuiltinType::kAtan2:
return "atan2";
case BuiltinType::kAtanh:
return "atanh";
case BuiltinType::kCeil:
return "ceil";
case BuiltinType::kClamp:

View File

@@ -35,12 +35,15 @@ enum class BuiltinType {
kNone = -1,
kAbs,
kAcos,
kAcosh,
kAll,
kAny,
kArrayLength,
kAsin,
kAsinh,
kAtan,
kAtan2,
kAtanh,
kCeil,
kClamp,
kCos,

View File

@@ -44,6 +44,100 @@ struct BuiltinPolyfill::State {
/// The source clone context
const sem::Info& sem = ctx.src->Sem();
/// Builds the polyfill function for the `acosh` builtin
/// @param ty the parameter and return type for the function
/// @return the polyfill function name
Symbol acosh(const sem::Type* ty) {
auto name = b.Symbols().New("tint_acosh");
uint32_t width = WidthOf(ty);
auto V = [&](AFloat value) -> const ast::Expression* {
const ast::Expression* expr = b.Expr(value);
if (width == 1) {
return expr;
}
return b.Construct(T(ty), expr);
};
ast::StatementList body;
switch (polyfill.acosh) {
case Level::kFull:
// return log(x + sqrt(x*x - 1));
body.emplace_back(b.Return(
b.Call("log", b.Add("x", b.Call("sqrt", b.Sub(b.Mul("x", "x"), 1_a))))));
break;
case Level::kRangeCheck: {
// return select(acosh(x), 0, x < 1);
body.emplace_back(b.Return(
b.Call("select", b.Call("acosh", "x"), V(0.0_a), b.LessThan("x", V(1.0_a)))));
break;
}
default:
TINT_ICE(Transform, b.Diagnostics())
<< "unhandled polyfill level: " << static_cast<int>(polyfill.acosh);
return {};
}
b.Func(name, {b.Param("x", T(ty))}, T(ty), body);
return name;
}
/// Builds the polyfill function for the `asinh` builtin
/// @param ty the parameter and return type for the function
/// @return the polyfill function name
Symbol asinh(const sem::Type* ty) {
auto name = b.Symbols().New("tint_sinh");
ast::StatementList body;
// return log(x + sqrt(x*x + 1));
body.emplace_back(
b.Return(b.Call("log", b.Add("x", b.Call("sqrt", b.Add(b.Mul("x", "x"), 1_a))))));
b.Func(name, {b.Param("x", T(ty))}, T(ty), body);
return name;
}
/// Builds the polyfill function for the `atanh` builtin
/// @param ty the parameter and return type for the function
/// @return the polyfill function name
Symbol atanh(const sem::Type* ty) {
auto name = b.Symbols().New("tint_atanh");
uint32_t width = WidthOf(ty);
auto V = [&](AFloat value) -> const ast::Expression* {
const ast::Expression* expr = b.Expr(value);
if (width == 1) {
return expr;
}
return b.Construct(T(ty), expr);
};
ast::StatementList body;
switch (polyfill.atanh) {
case Level::kFull:
// return log((1+x) / (1-x)) * 0.5
body.emplace_back(
b.Return(b.Mul(b.Call("log", b.Div(b.Add(1_a, "x"), b.Sub(1_a, "x"))), 0.5_a)));
break;
case Level::kRangeCheck:
// return select(atanh(x), 0, x >= 1);
body.emplace_back(b.Return(b.Call("select", b.Call("atanh", "x"), V(0.0_a),
b.GreaterThanEqual("x", V(1.0_a)))));
break;
default:
TINT_ICE(Transform, b.Diagnostics())
<< "unhandled polyfill level: " << static_cast<int>(polyfill.acosh);
return {};
}
b.Func(name, {b.Param("x", T(ty))}, T(ty), body);
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
@@ -440,6 +534,21 @@ bool BuiltinPolyfill::ShouldRun(const Program* program, const DataMap& data) con
if (auto* call = sem.Get<sem::Call>(node)) {
if (auto* builtin = call->Target()->As<sem::Builtin>()) {
switch (builtin->Type()) {
case sem::BuiltinType::kAcosh:
if (builtins.acosh != Level::kNone) {
return true;
}
break;
case sem::BuiltinType::kAsinh:
if (builtins.asinh) {
return true;
}
break;
case sem::BuiltinType::kAtanh:
if (builtins.atanh != Level::kNone) {
return true;
}
break;
case sem::BuiltinType::kCountLeadingZeros:
if (builtins.count_leading_zeros) {
return true;
@@ -496,6 +605,24 @@ void BuiltinPolyfill::Run(CloneContext& ctx, const DataMap& data, DataMap&) cons
if (auto* builtin = call->Target()->As<sem::Builtin>()) {
Symbol polyfill;
switch (builtin->Type()) {
case sem::BuiltinType::kAcosh:
if (builtins.acosh != Level::kNone) {
polyfill = utils::GetOrCreate(
polyfills, builtin, [&] { return s.acosh(builtin->ReturnType()); });
}
break;
case sem::BuiltinType::kAsinh:
if (builtins.asinh) {
polyfill = utils::GetOrCreate(
polyfills, builtin, [&] { return s.asinh(builtin->ReturnType()); });
}
break;
case sem::BuiltinType::kAtanh:
if (builtins.atanh != Level::kNone) {
polyfill = utils::GetOrCreate(
polyfills, builtin, [&] { return s.atanh(builtin->ReturnType()); });
}
break;
case sem::BuiltinType::kCountLeadingZeros:
if (builtins.count_leading_zeros) {
polyfill = utils::GetOrCreate(polyfills, builtin, [&] {

View File

@@ -33,12 +33,20 @@ class BuiltinPolyfill final : public Castable<BuiltinPolyfill, Transform> {
kNone,
/// Clamp the parameters to the inner implementation.
kClampParameters,
/// Range check the input.
kRangeCheck,
/// Polyfill the entire function
kFull,
};
/// Specifies the builtins that should be polyfilled by the transform.
struct Builtins {
/// What level should `acosh` be polyfilled?
Level acosh = Level::kNone;
/// Should `asinh` be polyfilled?
bool asinh = false;
/// What level should `atanh` be polyfilled?
Level atanh = Level::kNone;
/// Should `countLeadingZeros()` be polyfilled?
bool count_leading_zeros = false;
/// Should `countTrailingZeros()` be polyfilled?

View File

@@ -41,6 +41,292 @@ TEST_F(BuiltinPolyfillTest, EmptyModule) {
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// acosh
////////////////////////////////////////////////////////////////////////////////
DataMap polyfillAcosh(Level level) {
BuiltinPolyfill::Builtins builtins;
builtins.acosh = level;
DataMap data;
data.Add<BuiltinPolyfill::Config>(builtins);
return data;
}
TEST_F(BuiltinPolyfillTest, ShouldRunAcosh) {
auto* src = R"(
fn f() {
acosh(1.0);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kNone)));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kClampParameters)));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull)));
}
TEST_F(BuiltinPolyfillTest, Acosh_Full_f32) {
auto* src = R"(
fn f() {
let r : f32 = acosh(1234);
}
)";
auto* expect = R"(
fn tint_acosh(x : f32) -> f32 {
return log((x + sqrt(((x * x) - 1))));
}
fn f() {
let r : f32 = tint_acosh(1234);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull));
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, Acosh_Full_vec3_f32) {
auto* src = R"(
fn f() {
let r : vec3<f32> = acosh(vec3<f32>(1234));
}
)";
auto* expect = R"(
fn tint_acosh(x : vec3<f32>) -> vec3<f32> {
return log((x + sqrt(((x * x) - 1))));
}
fn f() {
let r : vec3<f32> = tint_acosh(vec3<f32>(1234));
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kFull));
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, Acosh_Range_f32) {
auto* src = R"(
fn f() {
let r : f32 = acosh(1234);
}
)";
auto* expect = R"(
fn tint_acosh(x : f32) -> f32 {
return select(acosh(x), 0.0, (x < 1.0));
}
fn f() {
let r : f32 = tint_acosh(1234);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, Acosh_Range_vec3_f32) {
auto* src = R"(
fn f() {
let r : vec3<f32> = acosh(vec3<f32>(1234));
}
)";
auto* expect = R"(
fn tint_acosh(x : vec3<f32>) -> vec3<f32> {
return select(acosh(x), vec3<f32>(0.0), (x < vec3<f32>(1.0)));
}
fn f() {
let r : vec3<f32> = tint_acosh(vec3<f32>(1234));
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// asinh
////////////////////////////////////////////////////////////////////////////////
DataMap polyfillSinh() {
BuiltinPolyfill::Builtins builtins;
builtins.asinh = true;
DataMap data;
data.Add<BuiltinPolyfill::Config>(builtins);
return data;
}
TEST_F(BuiltinPolyfillTest, ShouldRunAsinh) {
auto* src = R"(
fn f() {
asinh(1.0);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillSinh()));
}
TEST_F(BuiltinPolyfillTest, Asinh_f32) {
auto* src = R"(
fn f() {
let r : f32 = asinh(1234);
}
)";
auto* expect = R"(
fn tint_sinh(x : f32) -> f32 {
return log((x + sqrt(((x * x) + 1))));
}
fn f() {
let r : f32 = tint_sinh(1234);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillSinh());
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, Asinh_vec3_f32) {
auto* src = R"(
fn f() {
let r : vec3<f32> = asinh(vec3<f32>(1234));
}
)";
auto* expect = R"(
fn tint_sinh(x : vec3<f32>) -> vec3<f32> {
return log((x + sqrt(((x * x) + 1))));
}
fn f() {
let r : vec3<f32> = tint_sinh(vec3<f32>(1234));
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillSinh());
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// atanh
////////////////////////////////////////////////////////////////////////////////
DataMap polyfillAtanh(Level level) {
BuiltinPolyfill::Builtins builtins;
builtins.atanh = level;
DataMap data;
data.Add<BuiltinPolyfill::Config>(builtins);
return data;
}
TEST_F(BuiltinPolyfillTest, ShouldRunAtanh) {
auto* src = R"(
fn f() {
atanh(1.0);
}
)";
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src));
EXPECT_FALSE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kNone)));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kClampParameters)));
EXPECT_TRUE(ShouldRun<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull)));
}
TEST_F(BuiltinPolyfillTest, Atanh_Full_f32) {
auto* src = R"(
fn f() {
let r : f32 = atanh(1234);
}
)";
auto* expect = R"(
fn tint_atanh(x : f32) -> f32 {
return (log(((1 + x) / (1 - x))) * 0.5);
}
fn f() {
let r : f32 = tint_atanh(1234);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull));
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, Atanh_Full_vec3_f32) {
auto* src = R"(
fn f() {
let r : vec3<f32> = atanh(vec3<f32>(1234));
}
)";
auto* expect = R"(
fn tint_atanh(x : vec3<f32>) -> vec3<f32> {
return (log(((1 + x) / (1 - x))) * 0.5);
}
fn f() {
let r : vec3<f32> = tint_atanh(vec3<f32>(1234));
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kFull));
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, Atanh_Range_f32) {
auto* src = R"(
fn f() {
let r : f32 = atanh(1234);
}
)";
auto* expect = R"(
fn tint_atanh(x : f32) -> f32 {
return select(atanh(x), 0.0, (x >= 1.0));
}
fn f() {
let r : f32 = tint_atanh(1234);
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillAtanh(Level::kRangeCheck));
EXPECT_EQ(expect, str(got));
}
TEST_F(BuiltinPolyfillTest, Atanh_Range_vec3_f32) {
auto* src = R"(
fn f() {
let r : vec3<f32> = atanh(vec3<f32>(1234));
}
)";
auto* expect = R"(
fn f() {
let r : vec3<f32> = atanh(vec3<f32>(1234));
}
)";
auto got = Run<BuiltinPolyfill>(src, polyfillAcosh(Level::kRangeCheck));
EXPECT_EQ(expect, str(got));
}
////////////////////////////////////////////////////////////////////////////////
// countLeadingZeros
////////////////////////////////////////////////////////////////////////////////

View File

@@ -175,6 +175,8 @@ SanitizedResult Sanitize(const Program* in,
{ // Builtin polyfills
transform::BuiltinPolyfill::Builtins polyfills;
polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.count_leading_zeros = true;
polyfills.count_trailing_zeros = true;
polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
@@ -1605,10 +1607,13 @@ std::string GeneratorImpl::generate_builtin_name(const sem::Builtin* builtin) {
switch (builtin->Type()) {
case sem::BuiltinType::kAbs:
case sem::BuiltinType::kAcos:
case sem::BuiltinType::kAcosh:
case sem::BuiltinType::kAll:
case sem::BuiltinType::kAny:
case sem::BuiltinType::kAsin:
case sem::BuiltinType::kAsinh:
case sem::BuiltinType::kAtan:
case sem::BuiltinType::kAtanh:
case sem::BuiltinType::kCeil:
case sem::BuiltinType::kClamp:
case sem::BuiltinType::kCos:

View File

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

View File

@@ -151,6 +151,8 @@ SanitizedResult Sanitize(const Program* in, const Options& options) {
{ // Builtin polyfills
transform::BuiltinPolyfill::Builtins polyfills;
polyfills.acosh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.atanh = transform::BuiltinPolyfill::Level::kRangeCheck;
polyfills.extract_bits = transform::BuiltinPolyfill::Level::kClampParameters;
polyfills.first_leading_bit = true;
polyfills.first_trailing_bit = true;
@@ -1351,9 +1353,12 @@ std::string GeneratorImpl::generate_builtin_name(const sem::Builtin* builtin) {
std::string out = "";
switch (builtin->Type()) {
case sem::BuiltinType::kAcos:
case sem::BuiltinType::kAcosh:
case sem::BuiltinType::kAll:
case sem::BuiltinType::kAny:
case sem::BuiltinType::kAsin:
case sem::BuiltinType::kAsinh:
case sem::BuiltinType::kAtanh:
case sem::BuiltinType::kAtan:
case sem::BuiltinType::kAtan2:
case sem::BuiltinType::kCeil:

View File

@@ -103,12 +103,18 @@ uint32_t builtin_to_glsl_method(const sem::Builtin* builtin) {
switch (builtin->Type()) {
case BuiltinType::kAcos:
return GLSLstd450Acos;
case BuiltinType::kAcosh:
return GLSLstd450Acosh;
case BuiltinType::kAsin:
return GLSLstd450Asin;
case BuiltinType::kAsinh:
return GLSLstd450Asinh;
case BuiltinType::kAtan:
return GLSLstd450Atan;
case BuiltinType::kAtan2:
return GLSLstd450Atan2;
case BuiltinType::kAtanh:
return GLSLstd450Atanh;
case BuiltinType::kCeil:
return GLSLstd450Ceil;
case BuiltinType::kClamp:

View File

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