[Mlir-commits] [llvm] [mlir] [mlir][arith] Expand narrow-float truncf/extf with one generic engine (PR #236002)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Mon Oct 26 22:04:54 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-arith
Author: yifeihe07
<details>
<summary>Changes</summary>
arith-expand has one hand-written truncf/extf converter per narrow format (f4E2M1FN, f8E5M2, f8E4M3FN). This does not scale: every format adds code that redoes rounding and special-value handling by hand, which is error-prone and hard to verify.
Replace them with one generic expansion that reads each format from llvm::fltSemantics and emits integer arith ops. It supports every narrow APFloat format and is bit-exact with APFloat::convert in all five rounding modes, so results change where the old converters were inexact. New formats need a table entry instead of a converter; the existing options and populate functions are kept.
---
Patch is 252.78 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/236002.diff
16 Files Affected:
- (modified) mlir/include/mlir/Dialect/Arith/Transforms/Passes.h (+14)
- (modified) mlir/include/mlir/Dialect/Arith/Transforms/Passes.td (+19)
- (added) mlir/include/mlir/Dialect/Arith/Utils/NarrowFloatExpansion.h (+84)
- (modified) mlir/lib/Dialect/Arith/IR/ArithOps.cpp (+5)
- (modified) mlir/lib/Dialect/Arith/Transforms/CMakeLists.txt (+1)
- (modified) mlir/lib/Dialect/Arith/Transforms/ExpandOps.cpp (+443-511)
- (modified) mlir/lib/Dialect/Arith/Utils/CMakeLists.txt (+1)
- (added) mlir/lib/Dialect/Arith/Utils/NarrowFloatExpansion.cpp (+570)
- (modified) mlir/test/Dialect/Arith/canonicalize.mlir (+41)
- (added) mlir/test/Dialect/Arith/expand-narrow-float.mlir (+827)
- (modified) mlir/test/Dialect/Arith/expand-ops.mlir (+308-102)
- (added) mlir/test/Integration/Dialect/Arith/CPU/test-arith-expand-narrow-float.mlir (+1166)
- (modified) mlir/test/Integration/Dialect/Arith/CPU/test-arith-expand-truncf-extf.mlir (+33-3)
- (modified) mlir/unittests/Dialect/Arith/CMakeLists.txt (+5)
- (added) mlir/unittests/Dialect/Arith/NarrowFloatExpansionTest.cpp (+556)
- (modified) utils/bazel/llvm-project-overlay/mlir/BUILD.bazel (+4-1)
``````````diff
diff --git a/mlir/include/mlir/Dialect/Arith/Transforms/Passes.h b/mlir/include/mlir/Dialect/Arith/Transforms/Passes.h
index 7d7909c5f8231f..9708435832ff8e 100644
--- a/mlir/include/mlir/Dialect/Arith/Transforms/Passes.h
+++ b/mlir/include/mlir/Dialect/Arith/Transforms/Passes.h
@@ -56,6 +56,18 @@ void populateEmulateUnsupportedFloatsLegality(ConversionTarget &target,
/// Add patterns to expand Arith bf16 patterns to lower level bitcasts/shifts.
void populateExpandBFloat16Patterns(RewritePatternSet &patterns);
+/// Add patterns to expand `arith.truncf` and `arith.extf` on the narrow float
+/// types in `narrowTypes` (at most 8 bits, with a zero and subnormals or, as a
+/// truncf destination with a rounding mode, an unsigned precision-1 format with
+/// neither; others are ignored) into integer arith ops plus, for some pairs,
+/// arith.extf or arith.truncf bridges. Results are APFloat::convert's in the
+/// op's rounding mode, rounded once (from f80 or f128 via f64 rounded to odd);
+/// a format without Inf and NaN saturates (NaN gives +max-finite), and an
+/// unsigned destination gets the input rounded with its sign, the sign then
+/// dropped. Dynamic shapes are left alone.
+void populateExpandNarrowFloatPatterns(RewritePatternSet &patterns,
+ ArrayRef<TypeID> narrowTypes);
+
/// Add patterns to expand Arith f4e2m1 patterns to lower level bitcasts/shifts.
void populateExpandF4E2M1Patterns(RewritePatternSet &patterns);
@@ -67,6 +79,8 @@ void populateExpandF8E5M2Patterns(RewritePatternSet &patterns);
void populateExpandF8E4M3FNPatterns(RewritePatternSet &patterns);
/// Add patterns to expand Arith f8e8m0 patterns to lower level bitcasts/shifts.
+/// Without a rounding mode a truncf into f8E8M0FNU takes the exponent (toward
+/// zero); with one it is expanded as by populateExpandNarrowFloatPatterns.
void populateExpandF8E8M0Patterns(RewritePatternSet &patterns);
/// Add patterns to expand scaling ExtF/TruncF ops to equivalent arith ops
diff --git a/mlir/include/mlir/Dialect/Arith/Transforms/Passes.td b/mlir/include/mlir/Dialect/Arith/Transforms/Passes.td
index 98e549f53040a4..9a0a4484154389 100644
--- a/mlir/include/mlir/Dialect/Arith/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/Arith/Transforms/Passes.td
@@ -25,6 +25,25 @@ def ArithExpandOpsPass : Pass<"arith-expand"> {
"Enable the F8E5M2 expansion patterns">,
Option<"includeF8E4M3FN", "include-f8e4m3fn", "bool", /*default=*/"false",
"Enable the F8E4M3FN expansion patterns">,
+ Option<"includeF8E4M3", "include-f8e4m3", "bool", /*default=*/"false",
+ "Enable the F8E4M3 expansion patterns">,
+ Option<"includeF8E4M3FNUZ", "include-f8e4m3fnuz", "bool",
+ /*default=*/"false",
+ "Enable the F8E4M3FNUZ expansion patterns">,
+ Option<"includeF8E4M3B11FNUZ", "include-f8e4m3b11fnuz", "bool",
+ /*default=*/"false",
+ "Enable the F8E4M3B11FNUZ expansion patterns">,
+ Option<"includeF8E5M2FNUZ", "include-f8e5m2fnuz", "bool",
+ /*default=*/"false",
+ "Enable the F8E5M2FNUZ expansion patterns">,
+ Option<"includeF8E3M4", "include-f8e3m4", "bool", /*default=*/"false",
+ "Enable the F8E3M4 expansion patterns">,
+ Option<"includeF6E2M3FN", "include-f6e2m3fn", "bool",
+ /*default=*/"false",
+ "Enable the F6E2M3FN expansion patterns">,
+ Option<"includeF6E3M2FN", "include-f6e3m2fn", "bool",
+ /*default=*/"false",
+ "Enable the F6E3M2FN expansion patterns">,
Option<"includeFlushDenormals", "include-flush-denormals", "bool",
/*default=*/"false",
"Enable expansion of `arith.flush_denormals` on IEEE-like "
diff --git a/mlir/include/mlir/Dialect/Arith/Utils/NarrowFloatExpansion.h b/mlir/include/mlir/Dialect/Arith/Utils/NarrowFloatExpansion.h
new file mode 100644
index 00000000000000..dc10b9270f12eb
--- /dev/null
+++ b/mlir/include/mlir/Dialect/Arith/Utils/NarrowFloatExpansion.h
@@ -0,0 +1,84 @@
+//===- NarrowFloatExpansion.h - Narrow float truncf/extf --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_ARITH_UTILS_NARROWFLOATEXPANSION_H
+#define MLIR_DIALECT_ARITH_UTILS_NARROWFLOATEXPANSION_H
+
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/IR/Builders.h"
+#include <cstdint>
+
+namespace llvm {
+struct fltSemantics;
+} // namespace llvm
+
+namespace mlir {
+namespace arith {
+/// The encoding of a float format, as floatFormatSpecFromSemantics derives it.
+struct FloatFormatSpec {
+ unsigned sizeInBits = 0;
+ unsigned mantBits = 0;
+ unsigned expBits = 0;
+ /// UINT_MAX for a format without a sign.
+ unsigned signBitPos = 0;
+ /// 1 - the minimum exponent.
+ int bias = 0;
+ unsigned maxStoredExp = 0;
+ /// APFloat::getNaN's encoding; max-finite for a format without a NaN.
+ uint64_t quietNaNBits = 0;
+ uint64_t maxFiniteBitsAbs = 0;
+ bool hasInf = false;
+ bool isSigned = true;
+ bool hasAllOnesNaN = false;
+ bool isFNUZ = false;
+ /// Without a zero (unsigned, precision 1 only), exponent field 0 is a normal
+ /// binade: code c is what code c + 1 would be with a zero, 2^(c + 1 - bias).
+ bool hasZero = true;
+
+ bool hasNaN() const { return hasInf || hasAllOnesNaN || isFNUZ; }
+};
+
+/// Returns the spec of `sem`, one of APFloat's semantics, which must have a
+/// zero and subnormals (or, if unsigned with precision 1, neither), an implicit
+/// integer bit and at most 64 bits (asserted).
+FloatFormatSpec floatFormatSpecFromSemantics(const llvm::fltSemantics &sem);
+
+/// Returns floatFormatSpecFromSemantics of `floatTy`'s semantics.
+FloatFormatSpec floatFormatSpecFromFloatType(FloatType floatTy);
+
+/// Whether `a` and `b` are signed IEEE-encoded formats with the same exponent
+/// width and bias (e.g. f8E5M2 and f16), so the encodings differ by a shift.
+bool haveSameExponentLayout(const FloatFormatSpec &a, const FloatFormatSpec &b);
+
+// Both emitters take a carrier `srcBits` of i32 or i64 (or a static or scalable
+// shaped type of it) with the source encoding in its low bits, higher bits
+// ignored, and return the destination encoding zero-extended in the same type.
+// The truncation needs src.mantBits + dst.mantBits + 4 carrier bits.
+
+/// Emits the truncation of `src` (f16, bf16, f32 or f64) to the narrower `dst`,
+/// rounded once in `rnd`, as APFloat::convert does (NaN sign and payload
+/// included); a `dst` without Inf and NaN saturates (NaN gives +max-finite).
+/// An unsigned `dst` gets x rounded with its sign, the sign then dropped, as
+/// APFloat does (it asserts where that is a negative NaN; here that is the
+/// NaN, or max-finite downward). `dst`'s exponent range must lie inside
+/// `src`'s (asserted).
+Value emitNarrowFloatTrunc(ImplicitLocOpBuilder &b, Value srcBits,
+ const FloatFormatSpec &src,
+ const FloatFormatSpec &dst, RoundingMode rnd);
+
+/// Emits the exact extension of the narrow `src` (which must have a zero) to
+/// `dst` (f16, bf16, f32 or f64), which must have `src`'s precision and
+/// smallest subnormal (asserted); a value above its range gives +-Inf, and NaNs
+/// are quieted as by APFloat.
+Value emitNarrowFloatExt(ImplicitLocOpBuilder &b, Value srcBits,
+ const FloatFormatSpec &src,
+ const FloatFormatSpec &dst);
+} // namespace arith
+} // namespace mlir
+
+#endif // MLIR_DIALECT_ARITH_UTILS_NARROWFLOATEXPANSION_H
diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index 19e9caca6567a6..54c2ce8471be09 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -1839,6 +1839,11 @@ convertFloatValue(APFloat sourceValue,
if (sourceValue.isNaN() &&
targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly)
return failure();
+ // A format without a sign (e.g. f8E8M0FNU) has no negative values, and
+ // APFloat::convert reaches llvm_unreachable on a negative NaN or overflow.
+ if (sourceValue.isNegative() &&
+ !APFloat::semanticsHasSignedRepr(targetSemantics))
+ return failure();
bool losesInfo = false;
auto status = sourceValue.convert(targetSemantics, roundingMode, &losesInfo);
diff --git a/mlir/lib/Dialect/Arith/Transforms/CMakeLists.txt b/mlir/lib/Dialect/Arith/Transforms/CMakeLists.txt
index 8e84edc05bb857..bda5a50d86f250 100644
--- a/mlir/lib/Dialect/Arith/Transforms/CMakeLists.txt
+++ b/mlir/lib/Dialect/Arith/Transforms/CMakeLists.txt
@@ -21,6 +21,7 @@ add_mlir_dialect_library(MLIRArithTransforms
MLIRAnalysisDataFlow
MLIRArithCommonConversion
MLIRArithDialect
+ MLIRArithUtils
MLIRBufferizationDialect
MLIRBufferizationTransforms
MLIRFuncDialect
diff --git a/mlir/lib/Dialect/Arith/Transforms/ExpandOps.cpp b/mlir/lib/Dialect/Arith/Transforms/ExpandOps.cpp
index fd8e8e8368f6cc..98df6236a07e79 100644
--- a/mlir/lib/Dialect/Arith/Transforms/ExpandOps.cpp
+++ b/mlir/lib/Dialect/Arith/Transforms/ExpandOps.cpp
@@ -9,6 +9,7 @@
#include "mlir/Conversion/ArithCommon/CeilFloorDivExpansion.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/Arith/Transforms/Passes.h"
+#include "mlir/Dialect/Arith/Utils/NarrowFloatExpansion.h"
#include "mlir/Dialect/Vector/IR/VectorDialect.h"
#include "mlir/IR/BuiltinTypeInterfaces.h"
#include "mlir/IR/Location.h"
@@ -46,18 +47,6 @@ static Value createAPIntConst(Location loc, Type type, const APInt &value,
return arith::ConstantOp::create(rewriter, loc, attr);
}
-/// Create a float constant.
-static Value createFloatConst(Location loc, Type type, const APFloat &value,
- PatternRewriter &rewriter) {
- auto attr = rewriter.getFloatAttr(getElementTypeOrSelf(type), value);
- if (auto shapedTy = dyn_cast<ShapedType>(type)) {
- return arith::ConstantOp::create(rewriter, loc,
- DenseElementsAttr::get(shapedTy, attr));
- }
-
- return arith::ConstantOp::create(rewriter, loc, attr);
-}
-
/// Creates shapedType using shape from cloneFrom and base type from cloneTo
static Type cloneToShapedType(Type cloneFrom, Type cloneTo) {
if (auto shapedTy = dyn_cast<ShapedType>(cloneFrom)) {
@@ -66,6 +55,332 @@ static Type cloneToShapedType(Type cloneFrom, Type cloneTo) {
return cloneTo;
}
+/// Returns the exact f32 bits of the f8E8M0FNU `code`: code << 23, except 0x00
+/// (2^-127, 0x00400000) and, if `withNaN`, the NaN (0x7FC00000).
+static Value emitF8E8M0FNUToF32Bits(ImplicitLocOpBuilder &b,
+ PatternRewriter &rewriter, Value code,
+ bool withNaN) {
+ Location loc = b.getLoc();
+ Type i8Ty = code.getType();
+ Type i32Ty = cloneToShapedType(i8Ty, b.getI32Type());
+ Value cF32MantissaWidth = createConst(loc, i32Ty, 23, rewriter);
+ Value exti = arith::ExtUIOp::create(b, i32Ty, code);
+ Value f32Bits = arith::ShLIOp::create(b, exti, cF32MantissaWidth);
+ Value cF8Min = createConst(loc, i8Ty, 0, rewriter);
+ Value cF32Min = createConst(loc, i32Ty, 0x00400000, rewriter);
+ Value min = arith::CmpIOp::create(b, arith::CmpIPredicate::eq, code, cF8Min);
+ f32Bits = arith::SelectOp::create(b, min, cF32Min, f32Bits);
+ if (!withNaN)
+ return f32Bits;
+ Value cF8NaN = createConst(loc, i8Ty, 0xff, rewriter);
+ Value cF32NaN = createConst(loc, i32Ty, 0x7fc00000, rewriter);
+ Value isNan =
+ arith::CmpIOp::create(b, arith::CmpIPredicate::eq, code, cF8NaN);
+ return arith::SelectOp::create(b, isNan, cF32NaN, f32Bits);
+}
+
+static bool isEmitterWideType(Type ty) {
+ return ty.isF16() || ty.isBF16() || ty.isF32() || ty.isF64();
+}
+
+/// A float the emitters take: at most 8 bits, with a zero and subnormals or,
+/// as a truncation destination only, an unsigned precision-1 format with
+/// neither.
+static bool isNarrowFloat(Type ty, bool asTruncDest = false) {
+ auto floatTy = dyn_cast<FloatType>(ty);
+ if (!floatTy || floatTy.getWidth() > 8)
+ return false;
+ const llvm::fltSemantics &sem = floatTy.getFloatSemantics();
+ if (sem.hasZero && sem.hasDenormals)
+ return true;
+ return asTruncDest && !sem.hasZero && !sem.hasDenormals &&
+ !sem.hasSignedRepr && APFloat::semanticsPrecision(sem) == 1;
+}
+
+/// Whether every finite `from` value is exact in `to` (or, if `allowOverflow`,
+/// above its range); given precision and sign, the extremes decide.
+static bool isExactIn(FloatType from, FloatType to,
+ bool allowOverflow = false) {
+ const llvm::fltSemantics &fromSem = from.getFloatSemantics();
+ const llvm::fltSemantics &toSem = to.getFloatSemantics();
+ if (APFloat::semanticsPrecision(fromSem) >
+ APFloat::semanticsPrecision(toSem) ||
+ (APFloat::semanticsHasSignedRepr(fromSem) &&
+ !APFloat::semanticsHasSignedRepr(toSem)))
+ return false;
+ SmallVector<APFloat, 2> values = {APFloat::getSmallest(fromSem)};
+ if (!allowOverflow)
+ values.push_back(APFloat::getLargest(fromSem));
+ for (APFloat &value : values) {
+ bool losesInfo = false;
+ (void)value.convert(toSem, APFloat::rmNearestTiesToEven, &losesInfo);
+ if (losesInfo)
+ return false;
+ }
+ return true;
+}
+
+static bool isExactNarrowPair(FloatType src, FloatType dst) {
+ return isNarrowFloat(dst) && isExactIn(src, dst);
+}
+
+/// Whether emitNarrowFloatTrunc's exponent-range and carrier asserts hold.
+static bool canRoundFrom(FloatType src, FloatType dst) {
+ const llvm::fltSemantics &srcSem = src.getFloatSemantics();
+ const llvm::fltSemantics &dstSem = dst.getFloatSemantics();
+ return APFloat::semanticsMinExponent(dstSem) >=
+ APFloat::semanticsMinExponent(srcSem) &&
+ APFloat::semanticsMaxExponent(dstSem) <=
+ APFloat::semanticsMaxExponent(srcSem) &&
+ APFloat::semanticsPrecision(srcSem) +
+ APFloat::semanticsPrecision(dstSem) + 2 <=
+ 64;
+}
+
+/// Returns the type emitNarrowFloatTrunc rounds from: `src`; f64 for f80 and
+/// f128 (see emitRoundToOddF64Bits); else f32, or f64 if f32's range is too
+/// small, reached exactly (extf, or a decode); or null.
+static FloatType getTruncSourceType(FloatType src, FloatType dst) {
+ if (isEmitterWideType(src) && canRoundFrom(src, dst))
+ return src;
+ FloatType f64 = Float64Type::get(src.getContext());
+ if (isa<Float80Type, Float128Type>(src))
+ return canRoundFrom(f64, dst) ? f64 : FloatType();
+ FloatType f32 = Float32Type::get(src.getContext());
+ if (isa<Float8E8M0FNUType>(src))
+ return canRoundFrom(f32, dst) ? f32 : FloatType();
+ FloatType wide = canRoundFrom(f32, dst) ? f32 : f64;
+ if (!canRoundFrom(wide, dst))
+ return FloatType();
+ bool decodable = isNarrowFloat(src);
+ bool extendable = src.getWidth() > 8 && src.getWidth() < wide.getWidth();
+ return (decodable || extendable) && isExactIn(src, wide) ? wide : FloatType();
+}
+
+/// Returns the type emitNarrowFloatExt writes for `src` to `dst`, or null: f16
+/// if `src` has its exponent layout and `dst` is wider; else `dst`; f32 (then a
+/// truncf) for a `dst` of at most 8 bits without a zero, subnormals or `src`'s
+/// sign; else the widest of f64, f32 and f16 holding `src`, exact in `dst`.
+static FloatType getExtResultType(FloatType src, FloatType dst) {
+ Builder builder(dst.getContext());
+ FloatType f16 = builder.getF16Type();
+ if (dst.getWidth() > 16 && isExactIn(f16, dst) &&
+ arith::haveSameExponentLayout(arith::floatFormatSpecFromFloatType(src),
+ arith::floatFormatSpecFromFloatType(f16)))
+ return f16;
+ if (isEmitterWideType(dst))
+ return isExactIn(src, dst, /*allowOverflow=*/true) ? dst : FloatType();
+ if (dst.getWidth() <= 8) {
+ bool losesSign = APFloat::semanticsHasSignedRepr(src.getFloatSemantics()) &&
+ !APFloat::semanticsHasSignedRepr(dst.getFloatSemantics());
+ FloatType f32 = builder.getF32Type();
+ return (!isNarrowFloat(dst) || losesSign) && isExactIn(src, f32)
+ ? f32
+ : FloatType();
+ }
+ for (FloatType wide :
+ {builder.getF64Type(), builder.getF32Type(), builder.getF16Type()})
+ if (wide.getWidth() < dst.getWidth() && isExactIn(wide, dst) &&
+ isExactIn(src, wide))
+ return wide;
+ return FloatType();
+}
+
+/// The gate shared by the converters and the pass legality. A truncf into a
+/// listed type (which may lack a zero), or an exact extf between narrow floats
+/// one of them listed, is a rounding; any other extf from a listed type an
+/// extension. Dynamic shapes are left alone (no splat constants).
+static bool canExpandNarrowFloat(bool isExt, Type srcTy, Type dstTy, Type opTy,
+ ArrayRef<TypeID> narrowTypes) {
+ if (auto shapedTy = dyn_cast<ShapedType>(opTy);
+ shapedTy && !shapedTy.hasStaticShape())
+ return false;
+ auto src = dyn_cast<FloatType>(srcTy);
+ auto dst = dyn_cast<FloatType>(dstTy);
+ if (!src || !dst)
+ return false;
+ auto isListed = [&](FloatType ty, bool asTruncDest = false) {
+ return isNarrowFloat(ty, asTruncDest) &&
+ llvm::is_contained(narrowTypes, ty.getTypeID());
+ };
+ if (!isExt)
+ return isListed(dst, /*asTruncDest=*/true) && getTruncSourceType(src, dst);
+ if (isExactNarrowPair(src, dst))
+ return (isListed(dst) || isListed(src)) && getTruncSourceType(src, dst);
+ return isListed(src) && getExtResultType(src, dst);
+}
+
+/// i32, or i64 if a format or the truncation's rounding needs more bits.
+static IntegerType getCarrierIntType(Builder &b,
+ const arith::FloatFormatSpec &src,
+ const arith::FloatFormatSpec &dst) {
+ bool needsI64 = src.sizeInBits > 32 || dst.sizeInBits > 32 ||
+ src.mantBits + dst.mantBits + 4 > 32;
+ return b.getIntegerType(needsI64 ? 64 : 32);
+}
+
+/// Returns the op's nnan flag alone, or null: the only flag of the float ops
+/// created before the rounding. The f64 value of an f128 can be Inf or -0
+/// where the f128 is neither (ninf, nsz), and with contract ExtFOp::fold would
+/// fold the widening of a contract truncf back to the truncf's operand.
+static arith::FastMathFlagsAttr getNNaNOnly(MLIRContext *ctx,
+ arith::FastMathFlagsAttr fastmath) {
+ if (fastmath && arith::bitEnumContainsAll(fastmath.getValue(),
+ arith::FastMathFlags::nnan))
+ return arith::FastMathFlagsAttr::get(ctx, arith::FastMathFlags::nnan);
+ return {};
+}
+
+/// Returns the f64 bits of the f128 or f80 `operand` rounded to odd: toward
+/// zero, with the LSB set if inexact. f64 has at least two more bits than any
+/// precision p the emitter writes (53 >= p + 2) and a wider range, so rounding
+/// this value once more in any mode equals rounding `operand` directly. An f80
+/// is extended to f128 first (exact for canonical x87 encodings; a
+/// non-canonical one is read as the lowered arith.extf reads it). A NaN gives
+/// APFloat's f64 NaN; `nnan` (the op's nnan flag or null) drops that step.
+static Value emitRoundToOddF64Bits(ImplicitLocOpBuilder &b,
+ PatternRewriter &rewriter, Value operand,
+ arith::FastMathFlagsAttr nnan) {
+ Location loc = b.getLoc();
+ Type operandTy = operand.getType();
+ Type f128Ty = cloneToShapedType(operandTy, b.getF128Type());
+ Type i128Ty = cloneToShapedType(operandTy, b.getIntegerType(128));
+ Type f64Ty = cloneToShapedType(operandTy, b.getF64Type());
+ Type i64Ty = cloneToShapedType(operandTy, b.getI64Type());
+ Value x = operand;
+ if (operandTy != f128Ty)
+ x = arith::ExtFOp::create(b, f128Ty, operand, nnan);
+ Value r =
+ arith::TruncFOp::create(b, f64Ty, x, /*roundingmode=*/nullptr, nnan);
+ Value back = arith::ExtFOp::create(b, f128Ty, r, nnan);
+ // `back` has the sign of `x`, so their bits order by magnitude.
+ Value xBits = ari...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/236002
More information about the Mlir-commits
mailing list