| //===- APFloatWrappers.cpp - Software Implementation of FP Arithmetics --- ===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file exposes the APFloat infrastructure to MLIR programs as a runtime | |
| // library. APFloat is a software implementation of floating point arithmetics. | |
| // | |
| // On the MLIR side, floating-point values must be bitcasted to 64-bit integers | |
| // before calling a runtime function. If a floating-point type has less than | |
| // 64 bits, it must be zero-extended to 64 bits after bitcasting it to an | |
| // integer. | |
| // | |
| // Runtime functions receive the floating-point operands of the arithmeic | |
| // operation in the form of 64-bit integers, along with the APFloat semantics | |
| // in the form of a 32-bit integer, which will be interpreted as an | |
| // APFloatBase::Semantics enum value. | |
| // | |
| #include "llvm/ADT/APFloat.h" | |
| #include "llvm/ADT/APSInt.h" | |
| #include "llvm/Support/Debug.h" | |
| #ifdef _WIN32 | |
| #ifndef MLIR_APFLOAT_WRAPPERS_EXPORT | |
| #ifdef mlir_apfloat_wrappers_EXPORTS | |
| // We are building this library | |
| #define MLIR_APFLOAT_WRAPPERS_EXPORT __declspec(dllexport) | |
| #else | |
| // We are using this library | |
| #define MLIR_APFLOAT_WRAPPERS_EXPORT __declspec(dllimport) | |
| #endif // mlir_apfloat_wrappers_EXPORTS | |
| #endif // MLIR_APFLOAT_WRAPPERS_EXPORT | |
| #else | |
| // Non-windows: use visibility attributes. | |
| #define MLIR_APFLOAT_WRAPPERS_EXPORT __attribute__((visibility("default"))) | |
| #endif // _WIN32 | |
| /// Binary operations without rounding mode. | |
| #define APFLOAT_BINARY_OP(OP) \ | |
| MLIR_APFLOAT_WRAPPERS_EXPORT int64_t _mlir_apfloat_##OP( \ | |
| int32_t semantics, uint64_t a, uint64_t b) { \ | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( \ | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); \ | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); \ | |
| llvm::APFloat lhs(sem, llvm::APInt(bitWidth, a)); \ | |
| llvm::APFloat rhs(sem, llvm::APInt(bitWidth, b)); \ | |
| lhs.OP(rhs); \ | |
| return lhs.bitcastToAPInt().getZExtValue(); \ | |
| } | |
| /// Binary operations with rounding mode. | |
| #define APFLOAT_BINARY_OP_ROUNDING_MODE(OP, ROUNDING_MODE) \ | |
| MLIR_APFLOAT_WRAPPERS_EXPORT uint64_t _mlir_apfloat_##OP( \ | |
| int32_t semantics, uint64_t a, uint64_t b) { \ | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( \ | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); \ | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); \ | |
| llvm::APFloat lhs(sem, llvm::APInt(bitWidth, a)); \ | |
| llvm::APFloat rhs(sem, llvm::APInt(bitWidth, b)); \ | |
| lhs.OP(rhs, ROUNDING_MODE); \ | |
| return lhs.bitcastToAPInt().getZExtValue(); \ | |
| } | |
| extern "C" { | |
| #define BIN_OPS_WITH_ROUNDING(X) \ | |
| X(add, llvm::RoundingMode::NearestTiesToEven) \ | |
| X(subtract, llvm::RoundingMode::NearestTiesToEven) \ | |
| X(multiply, llvm::RoundingMode::NearestTiesToEven) \ | |
| X(divide, llvm::RoundingMode::NearestTiesToEven) | |
| BIN_OPS_WITH_ROUNDING(APFLOAT_BINARY_OP_ROUNDING_MODE) | |
| #undef BIN_OPS_WITH_ROUNDING | |
| #undef APFLOAT_BINARY_OP_ROUNDING_MODE | |
| APFLOAT_BINARY_OP(remainder) | |
| #undef APFLOAT_BINARY_OP | |
| MLIR_APFLOAT_WRAPPERS_EXPORT void printApFloat(int32_t semantics, uint64_t a) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat x(sem, llvm::APInt(bitWidth, a)); | |
| double d = x.convertToDouble(); | |
| fprintf(stdout, "%lg", d); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT uint64_t | |
| _mlir_apfloat_convert(int32_t inSemantics, int32_t outSemantics, uint64_t a) { | |
| const llvm::fltSemantics &inSem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(inSemantics)); | |
| const llvm::fltSemantics &outSem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(outSemantics)); | |
| unsigned bitWidthIn = llvm::APFloatBase::semanticsSizeInBits(inSem); | |
| llvm::APFloat val(inSem, llvm::APInt(bitWidthIn, a)); | |
| // TODO: Custom rounding modes are not supported yet. | |
| bool losesInfo; | |
| val.convert(outSem, llvm::RoundingMode::NearestTiesToEven, &losesInfo); | |
| llvm::APInt result = val.bitcastToAPInt(); | |
| return result.getZExtValue(); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT uint64_t _mlir_apfloat_convert_to_int( | |
| int32_t semantics, int32_t resultWidth, bool isUnsigned, uint64_t a) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned inputWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat val(sem, llvm::APInt(inputWidth, a)); | |
| llvm::APSInt result(resultWidth, isUnsigned); | |
| bool isExact; | |
| // TODO: Custom rounding modes are not supported yet. | |
| val.convertToInteger(result, llvm::RoundingMode::NearestTiesToEven, &isExact); | |
| // This function always returns uint64_t, regardless of the desired result | |
| // width. It does not matter whether we zero-extend or sign-extend the APSInt | |
| // to 64 bits because the generated IR in arith-to-apfloat will truncate the | |
| // result to the desired result width. | |
| return result.getZExtValue(); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT uint64_t _mlir_apfloat_convert_from_int( | |
| int32_t semantics, int32_t inputWidth, bool isUnsigned, uint64_t a) { | |
| llvm::APInt val(inputWidth, a, /*isSigned=*/!isUnsigned); | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| llvm::APFloat result(sem); | |
| // TODO: Custom rounding modes are not supported yet. | |
| result.convertFromAPInt(val, /*IsSigned=*/!isUnsigned, | |
| llvm::RoundingMode::NearestTiesToEven); | |
| return result.bitcastToAPInt().getZExtValue(); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT int8_t _mlir_apfloat_compare(int32_t semantics, | |
| uint64_t a, | |
| uint64_t b) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat x(sem, llvm::APInt(bitWidth, a)); | |
| llvm::APFloat y(sem, llvm::APInt(bitWidth, b)); | |
| return static_cast<int8_t>(x.compare(y)); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT uint64_t _mlir_apfloat_neg(int32_t semantics, | |
| uint64_t a) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat x(sem, llvm::APInt(bitWidth, a)); | |
| x.changeSign(); | |
| return x.bitcastToAPInt().getZExtValue(); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT uint64_t _mlir_apfloat_abs(int32_t semantics, | |
| uint64_t a) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat x(sem, llvm::APInt(bitWidth, a)); | |
| return abs(x).bitcastToAPInt().getZExtValue(); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT bool _mlir_apfloat_isfinite(int32_t semantics, | |
| uint64_t a) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat x(sem, llvm::APInt(bitWidth, a)); | |
| return x.isFinite(); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT bool _mlir_apfloat_isinfinite(int32_t semantics, | |
| uint64_t a) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat x(sem, llvm::APInt(bitWidth, a)); | |
| return x.isInfinity(); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT bool _mlir_apfloat_isnormal(int32_t semantics, | |
| uint64_t a) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat x(sem, llvm::APInt(bitWidth, a)); | |
| return x.isNormal(); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT bool _mlir_apfloat_isnan(int32_t semantics, | |
| uint64_t a) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat x(sem, llvm::APInt(bitWidth, a)); | |
| return x.isNaN(); | |
| } | |
| MLIR_APFLOAT_WRAPPERS_EXPORT uint64_t | |
| _mlir_apfloat_fused_multiply_add(int32_t semantics, uint64_t operand, | |
| uint64_t multiplicand, uint64_t addend) { | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); | |
| llvm::APFloat operand_(sem, llvm::APInt(bitWidth, operand)); | |
| llvm::APFloat multiplicand_(sem, llvm::APInt(bitWidth, multiplicand)); | |
| llvm::APFloat addend_(sem, llvm::APInt(bitWidth, addend)); | |
| llvm::detail::opStatus stat = operand_.fusedMultiplyAdd( | |
| multiplicand_, addend_, llvm::RoundingMode::NearestTiesToEven); | |
| assert(stat == llvm::APFloatBase::opOK && | |
| "expected fusedMultiplyAdd status to be OK"); | |
| (void)stat; | |
| return operand_.bitcastToAPInt().getZExtValue(); | |
| } | |
| /// Min/max operations. | |
| #define APFLOAT_MIN_MAX_OP(OP) \ | |
| MLIR_APFLOAT_WRAPPERS_EXPORT uint64_t _mlir_apfloat_##OP( \ | |
| int32_t semantics, uint64_t a, uint64_t b) { \ | |
| const llvm::fltSemantics &sem = llvm::APFloatBase::EnumToSemantics( \ | |
| static_cast<llvm::APFloatBase::Semantics>(semantics)); \ | |
| unsigned bitWidth = llvm::APFloatBase::semanticsSizeInBits(sem); \ | |
| llvm::APFloat lhs(sem, llvm::APInt(bitWidth, a)); \ | |
| llvm::APFloat rhs(sem, llvm::APInt(bitWidth, b)); \ | |
| llvm::APFloat result = llvm::OP(lhs, rhs); \ | |
| return result.bitcastToAPInt().getZExtValue(); \ | |
| } | |
| APFLOAT_MIN_MAX_OP(minimum) | |
| APFLOAT_MIN_MAX_OP(maximum) | |
| APFLOAT_MIN_MAX_OP(minnum) | |
| APFLOAT_MIN_MAX_OP(maxnum) | |
| #undef APFLOAT_MIN_MAX_OP | |
| } |