[llvm] [SelectionDAG] Add expansion for llvm.convert.to.arbitrary.fp (PR #193595)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 9 01:27:23 PDT 2026
================
@@ -9046,6 +9046,418 @@ SDValue TargetLowering::expandFCANONICALIZE(SDNode *Node,
return Mul;
}
+SDValue TargetLowering::expandCONVERT_TO_ARBITRARY_FP(SDNode *Node,
+ SelectionDAG &DAG) const {
+ // Expand conversion from a native IEEE float type to an arbitrary FP format
+ // returning the result as an integer using bit manipulation.
+ EVT ResVT = Node->getValueType(0);
+ SDLoc dl(Node);
+
+ SDValue FloatVal = Node->getOperand(0);
+ const uint64_t SemEnum = Node->getConstantOperandVal(1);
+ const auto Sem = static_cast<APFloatBase::Semantics>(SemEnum);
+ const auto RoundMode =
+ static_cast<RoundingMode>(Node->getConstantOperandVal(2));
+ const bool Saturate = Node->getConstantOperandVal(3) != 0;
+
+ // Supported destination formats.
+ switch (Sem) {
+ case APFloatBase::S_Float8E5M2:
+ case APFloatBase::S_Float8E4M3FN:
+ case APFloatBase::S_Float6E3M2FN:
+ case APFloatBase::S_Float6E2M3FN:
+ case APFloatBase::S_Float4E2M1FN:
+ break;
+ default:
+ DAG.getContext()->emitError("CONVERT_TO_ARBITRARY_FP: not implemented "
+ "destination format (semantics enum " +
+ Twine(SemEnum) + ")");
+ return SDValue();
+ }
+
+ // Supported rounding modes.
+ switch (RoundMode) {
+ case RoundingMode::NearestTiesToEven:
+ case RoundingMode::TowardZero:
+ case RoundingMode::TowardPositive:
+ case RoundingMode::TowardNegative:
+ case RoundingMode::NearestTiesToAway:
+ break;
+ default:
+ DAG.getContext()->emitError(
+ "CONVERT_TO_ARBITRARY_FP: unsupported rounding mode (enum " +
+ Twine(static_cast<int>(RoundMode)) + ")");
+ return SDValue();
+ }
+
+ // Destination format parameters.
+ const fltSemantics &DstSem = APFloatBase::EnumToSemantics(Sem);
+ const unsigned DstBits = APFloat::getSizeInBits(DstSem);
+ const unsigned DstPrecision = APFloat::semanticsPrecision(DstSem);
+ const unsigned DstMant = DstPrecision - 1;
+ const unsigned DstExpBits = DstBits - DstMant - 1;
+ const int DstBias = 1 - APFloat::semanticsMinExponent(DstSem);
+ const unsigned DstExpMax = (1U << DstExpBits) - 1;
+ const uint64_t DstMantMask = (DstMant > 0) ? ((1ULL << DstMant) - 1) : 0;
+ const fltNonfiniteBehavior DstNFBehavior = DstSem.nonFiniteBehavior;
+ const fltNanEncoding DstNanEnc = DstSem.nanEncoding;
+
+ // Compute the maximum normal exponent for the destination format.
+ const unsigned DstExpMaxNormal =
+ DstNFBehavior == fltNonfiniteBehavior::IEEE754 ? DstExpMax - 1
+ : DstExpMax;
+
+ // For NanOnly formats the max exponent field for finite values
+ // is DstExpMax, but the encoding with exp = DstExpMax and
+ // mant = all-ones is NaN. So DstExpMaxNormal = DstExpMax, but max
+ // mantissa at that exponent is DstMantMask - 1 (if NanEnc == AllOnes) to
+ // avoid the NaN encoding.
+ uint64_t DstMaxMantAtMaxExp = DstMantMask;
+ if (DstNFBehavior == fltNonfiniteBehavior::NanOnly &&
+ DstNanEnc == fltNanEncoding::AllOnes)
+ DstMaxMantAtMaxExp = DstMantMask - 1;
+
+ // Source format parameters.
+ EVT SrcVT = FloatVal.getValueType();
+ const fltSemantics &SrcSem = SrcVT.getScalarType().getFltSemantics();
+ const unsigned SrcBits = APFloat::getSizeInBits(SrcSem);
+ const unsigned SrcPrecision = APFloat::semanticsPrecision(SrcSem);
+ const unsigned SrcMant = SrcPrecision - 1;
+ const uint64_t SrcMantMask = (1ULL << SrcMant) - 1;
+
+ // Work in the source integer type. Match the destination shape so the
+ // expansion stays vector when ResVT is a vector.
+ EVT IntScalarVT = EVT::getIntegerVT(*DAG.getContext(), SrcBits);
+ EVT IntVT = ResVT.isVector()
+ ? EVT::getVectorVT(*DAG.getContext(), IntScalarVT,
+ ResVT.getVectorElementCount())
+ : IntScalarVT;
+ EVT SetCCVT =
+ getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), IntVT);
+ EVT FPSetCCVT =
+ getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), SrcVT);
+
+ SDValue Zero = DAG.getConstant(0, dl, IntVT);
+ SDValue One = DAG.getConstant(1, dl, IntVT);
+
+ // Bitcast source float to integer to extract the sign bit.
+ SDValue Src = DAG.getNode(ISD::BITCAST, dl, IntVT, FloatVal);
+ SDValue SignBit =
+ DAG.getNode(ISD::SRL, dl, IntVT, Src,
+ DAG.getShiftAmountConstant(SrcBits - 1, IntVT, dl));
+
+ // Classify the input.
+ SDValue FPZero = DAG.getConstantFP(0.0, dl, SrcVT);
+ SDValue FPInf = DAG.getConstantFP(APFloat::getInf(SrcSem), dl, SrcVT);
+ SDValue AbsVal = DAG.getNode(ISD::FABS, dl, SrcVT, FloatVal);
+ SDValue IsNaN = DAG.getSetCC(dl, FPSetCCVT, FloatVal, FPZero, ISD::SETUO);
+ SDValue IsInf = DAG.getSetCC(dl, FPSetCCVT, AbsVal, FPInf, ISD::SETOEQ);
+ SDValue IsZero = DAG.getSetCC(dl, FPSetCCVT, FloatVal, FPZero, ISD::SETOEQ);
+
+ // Split into a normalized fraction and unbiased exponent. FFREXP normalizes
+ // source denormals automatically. The result is unspecified for Inf/NaN, but
+ // those inputs are detected above and override the final result.
+ EVT FrexpExpScalarVT =
+ getValueType(DAG.getDataLayout(), Type::getInt32Ty(*DAG.getContext()));
+ EVT FrexpExpVT = SrcVT.changeElementType(*DAG.getContext(), FrexpExpScalarVT);
+ SDValue Frexp =
+ DAG.getNode(ISD::FFREXP, dl, DAG.getVTList(SrcVT, FrexpExpVT), FloatVal);
+ SDValue FrexpFrac = Frexp.getValue(0);
+ SDValue FrexpExp = Frexp.getValue(1);
+
+ SDValue FrexpFracInt = DAG.getNode(ISD::BITCAST, dl, IntVT, FrexpFrac);
+ SDValue EffSrcMant = DAG.getNode(ISD::AND, dl, IntVT, FrexpFracInt,
+ DAG.getConstant(SrcMantMask, dl, IntVT));
+
+ SDValue FrexpExpExt = DAG.getSExtOrTrunc(FrexpExp, dl, IntVT);
+ SDValue NewExp = DAG.getNode(
+ ISD::ADD, dl, IntVT, FrexpExpExt,
+ DAG.getConstant(APInt(SrcBits, DstBias - 1, true), dl, IntVT));
----------------
arsenm wrote:
```suggestion
DAG.getConstant(DstBias - 1, dl, IntVT));
```
https://github.com/llvm/llvm-project/pull/193595
More information about the llvm-commits
mailing list