diff --git a/lgc/builder/ArithBuilder.cpp b/lgc/builder/ArithBuilder.cpp index 69dc6e41d6..68ca7279bb 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 definitions: + // + // 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; }