From 73bb454f66a07a654e88a9f4511f37fc0be9ed29 Mon Sep 17 00:00:00 2001 From: Rex Xu <rex.xu@amd.com> 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/include/lgc/state/IntrinsDefs.h | 14 --------- lgc/interface/lgc/Builder.h | 14 +++++++++ llpc/translator/lib/SPIRV/SPIRVReader.cpp | 37 +++++++++++++++++++++-- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/lgc/include/lgc/state/IntrinsDefs.h b/lgc/include/lgc/state/IntrinsDefs.h index f025fec45b..05b2920fba 100644 --- a/lgc/include/lgc/state/IntrinsDefs.h +++ b/lgc/include/lgc/state/IntrinsDefs.h @@ -466,20 +466,6 @@ enum BufDstSel { BUF_DST_SEL_W = 7, // SEL_W (W) }; -// Bits in mask supplied to v_cmp_class -enum CmpClass { - SignalingNaN = 1, - QuietNaN = 2, - NegativeInfinity = 4, - NegativeNormal = 8, - NegativeSubnormal = 0x10, - NegativeZero = 0x20, - PositiveZero = 0x40, - PositiveSubnormal = 0x80, - PositiveNormal = 0x100, - PositiveInfinity = 0x200 -}; - // Represents register fields of SPI_PS_INPUT_ADDR. union SpiPsInputAddr { struct { diff --git a/lgc/interface/lgc/Builder.h b/lgc/interface/lgc/Builder.h index b8a64f1b51..4639ed4a7e 100644 --- a/lgc/interface/lgc/Builder.h +++ b/lgc/interface/lgc/Builder.h @@ -345,6 +345,20 @@ class BuilderDefs : public BuilderCommon { ImageAtomicFMax = 12, // Atomic operation: fmax ImageAtomicFAdd = 13 // Atomic operation: fadd }; + + // Bits in mask supplied to createIsFPClass + enum CmpClass { + SignalingNaN = 1, + QuietNaN = 2, + NegativeInfinity = 4, + NegativeNormal = 8, + NegativeSubnormal = 0x10, + NegativeZero = 0x20, + PositiveZero = 0x40, + PositiveSubnormal = 0x80, + PositiveNormal = 0x100, + PositiveInfinity = 0x200 + }; }; // ===================================================================================================================== diff --git a/llpc/translator/lib/SPIRV/SPIRVReader.cpp b/llpc/translator/lib/SPIRV/SPIRVReader.cpp index 6d677c897a..8011085926 100644 --- a/llpc/translator/lib/SPIRV/SPIRVReader.cpp +++ b/llpc/translator/lib/SPIRV/SPIRVReader.cpp @@ -8808,9 +8808,42 @@ Value *SPIRVToLLVM::transGLSLExtInst(SPIRVExtInst *extInst, BasicBlock *bb) { // Round up to whole number return getBuilder()->CreateUnaryIntrinsic(Intrinsic::ceil, args[0]); - case GLSLstd450Fract: + case GLSLstd450Fract: { // Get fractional part - return getBuilder()->CreateFract(args[0]); + auto fract = getBuilder()->CreateFract(args[0]); + + // 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 + unsigned checkFlags = 0; + if (!getBuilder()->getFastMathFlags().noSignedZeros()) + checkFlags |= lgc::Builder::CmpClass::NegativeZero; + if (!getBuilder()->getFastMathFlags().noInfs()) + checkFlags |= (lgc::Builder::CmpClass::NegativeZero | lgc::Builder::CmpClass::NegativeZero); + + if (checkFlags) { + Value *isNegZeroOrInf = getBuilder()->createIsFPClass(args[0], checkFlags); + Value *signedZero = getBuilder()->CreateCopySign(ConstantFP::getNullValue(args[0]->getType()), args[0]); + fract = getBuilder()->CreateSelect(isNegZeroOrInf, signedZero, fract); + } + + return fract; + } case GLSLstd450Radians: // Convert from degrees to radians