1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
//===- 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
}
|