//===- 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(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(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(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(inSemantics)); const llvm::fltSemantics &outSem = llvm::APFloatBase::EnumToSemantics( static_cast(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(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(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(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(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(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(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(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(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(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(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(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(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 }