[llvm] [X86] Respect denormal mode in f32-to-bf16 conversions (PR #221052)

Simon Pilgrim via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 14 01:42:35 PDT 2026


================
@@ -22831,6 +22831,57 @@ SDValue X86TargetLowering::LowerFP_EXTEND(SDValue Op, SelectionDAG &DAG) const {
   return DAG.getNode(X86ISD::VFPEXT, DL, VT, Res);
 }
 
+static bool hasCVTNEPS2BF16(const X86Subtarget &Subtarget) {
+  return (Subtarget.hasBF16() && Subtarget.hasVLX()) ||
+         Subtarget.hasAVXNECONVERT();
+}
+
+/// VCVTNEPS2BF16 always flushes input and output denormals to zero. Since f32
+/// and bf16 have the same exponent range, a normal f32 cannot produce a bf16
+/// denormal. The instruction is therefore valid when f32 input denormals are
+/// flushed while preserving their sign.
+static bool canUseCVTNEPS2BF16(const X86Subtarget &Subtarget,
+                               const SelectionDAG &DAG) {
+  return hasCVTNEPS2BF16(Subtarget) &&
+         DAG.getDenormalMode(MVT::f32).Input == DenormalMode::PreserveSign;
+}
+
+/// Round f32 values (scalar or vector) to bf16 using integer arithmetic,
+/// producing the bf16 bit pattern as i16 (or vXi16). This is round to nearest
+/// even, quiets NaNs and, unlike VCVTNEPS2BF16, handles denormals exactly. It
+/// mirrors the bf16 expansion in TargetLowering::expandFP_ROUND.
+static SDValue expandF32ToBF16Bits(SDValue Src, const SDLoc &DL,
+                                   SelectionDAG &DAG,
+                                   const X86TargetLowering &TLI) {
+  EVT SrcVT = Src.getValueType();
+  assert(SrcVT.getScalarType() == MVT::f32 && "Expected f32 source");
+  EVT I32VT = SrcVT.changeTypeToInteger();
+  EVT I16VT = I32VT.changeElementType(*DAG.getContext(), MVT::i16);
+
+  SDValue Bits = DAG.getBitcast(I32VT, Src);
+  SDValue IsNaN = DAG.getSetCC(
+      DL, TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), SrcVT),
+      Src, Src, ISD::SETUO);
+
+  // Round to nearest even: add 0x7fff plus the lsb of the result.
+  SDValue Lsb = DAG.getNode(ISD::SRL, DL, I32VT, Bits,
+                            DAG.getShiftAmountConstant(16, I32VT, DL));
+  Lsb = DAG.getNode(ISD::AND, DL, I32VT, Lsb, DAG.getConstant(1, DL, I32VT));
+  SDValue Bias =
+      DAG.getNode(ISD::ADD, DL, I32VT, Lsb, DAG.getConstant(0x7fff, DL, I32VT));
+  SDValue Rounded = DAG.getNode(ISD::ADD, DL, I32VT, Bits, Bias);
+
+  // Set the quiet bit on NaNs instead of rounding, which could turn a NaN
+  // into an infinity.
+  SDValue NaN = DAG.getNode(ISD::OR, DL, I32VT, Bits,
+                            DAG.getConstant(0x400000, DL, I32VT));
+
+  SDValue Res = DAG.getSelect(DL, I32VT, IsNaN, NaN, Rounded);
+  Res = DAG.getNode(ISD::SRL, DL, I32VT, Res,
+                    DAG.getShiftAmountConstant(16, I32VT, DL));
+  return DAG.getNode(ISD::TRUNCATE, DL, I16VT, Res);
----------------
RKSimon wrote:

Nothing in here is x86 specific - move to TargetLowering

https://github.com/llvm/llvm-project/pull/221052


More information about the llvm-commits mailing list