From 2dc262fe23389f6b916a86d37624acd3789b7712 Mon Sep 17 00:00:00 2001 From: Rex Xu Date: Wed, 8 Nov 2023 15:37:44 +0800 Subject: [PATCH] Fix issues of fract(x) The problem is similar to modf(x) when x=-0.0 or +INF/-INF. Although SPIR-V spec says nothing about such cases, the OpenCL spec does define the results of those operations as follow: fract(-0.0) = -0.0 fract(+INF) = 0.0 fract(-INF) = -0.0 Hence, we follow what have been done for modf. --- lgc/builder/ArithBuilder.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lgc/builder/ArithBuilder.cpp b/lgc/builder/ArithBuilder.cpp index 69dc6e41d6..a8935a3dd7 100644 --- a/lgc/builder/ArithBuilder.cpp +++ b/lgc/builder/ArithBuilder.cpp @@ -700,6 +700,32 @@ Value *BuilderImpl::CreateSSign(Value *x, const Twine &instName) { Value *BuilderImpl::CreateFract(Value *x, const Twine &instName) { // We need to scalarize this ourselves. Value *result = scalarize(x, [this](Value *x) { return CreateIntrinsic(Intrinsic::amdgcn_fract, x->getType(), x); }); + + // NOTE: Although SPIR-V spec says nothing about such cases: fract(-0.0), fract(+INF), fract(-INF), OpenCL spec does + // have following defintions: + // + // fract(-0.0) = -0.0 + // fract(+INF) = +0.0 + // fract(-INF) = -0.0 + // + // When we follow it, we have two issues that are similar to modf(x): + // + // 1. When we input x=+INF/-INF to above formula, we finally get the computation of (-INF) + INF or INF - INF. + // The result is NaN returned by HW. + // 2. When we input x=-0.0 to above formula, we finally get the addition of (-0.0) + 0.0. The result is +0.0 + // returned by HW. + // + // Hence, we have to manually check those special cases: + // + // y = fract(x) + // y = x == -0.0 || x == INF ? copysign(0.0, x) : y, when either NSZ or NoInfs is not present + if (!getFastMathFlags().noSignedZeros() || !getFastMathFlags().noInfs()) { + Value *isNegZeroOrInf = + createIsFPClass(x, CmpClass::NegativeZero | CmpClass::NegativeInfinity | CmpClass::PositiveInfinity); + Value *signedZero = CreateCopySign(ConstantFP::getNullValue(x->getType()), x); + result = CreateSelect(isNegZeroOrInf, signedZero, result); + } + result->setName(instName); return result; }