got sv.bc working for pospopcount
[openpower-isa.git] / src / openpower / decoder / fp_working_format.py
1 # SPDX-License-Identifier: LGPLv3+
2 # Funded by NLnet https://nlnet.nl/
3
4 """ implementation of binary floating-point working format as used in:
5 PowerISA v3.1B section 7.6.2.2
6 e.g. bfp_CONVERT_FROM_BFP32() on page 589(615)
7 """
8
9 from openpower.decoder.selectable_int import SelectableInt
10 import operator
11 import math
12 from fractions import Fraction
13
14 # in this file, everything uses properties instead of plain attributes because
15 # we need to convert most SelectableInts we get to Python int
16
17
18 class BFPStateClass:
19 def __init__(self, value=None):
20 self.__snan = 0
21 self.__qnan = 0
22 self.__infinity = 0
23 self.__zero = 0
24 self.__denormal = 0
25 self.__normal = 0
26 if value is not None:
27 self.eq(value)
28
29 def eq(self, rhs):
30 self.SNaN = rhs.SNaN
31 self.QNaN = rhs.QNaN
32 self.Infinity = rhs.Infinity
33 self.Zero = rhs.Zero
34 self.Denormal = rhs.Denormal
35 self.Normal = rhs.Normal
36
37 @property
38 def SNaN(self):
39 return self.__snan
40
41 @SNaN.setter
42 def SNaN(self, value):
43 self.__snan = int(value)
44
45 @property
46 def QNaN(self):
47 return self.__qnan
48
49 @QNaN.setter
50 def QNaN(self, value):
51 self.__qnan = int(value)
52
53 @property
54 def Infinity(self):
55 return self.__infinity
56
57 @Infinity.setter
58 def Infinity(self, value):
59 self.__infinity = int(value)
60
61 @property
62 def Zero(self):
63 return self.__zero
64
65 @Zero.setter
66 def Zero(self, value):
67 self.__zero = int(value)
68
69 @property
70 def Denormal(self):
71 return self.__denormal
72
73 @Denormal.setter
74 def Denormal(self, value):
75 self.__denormal = int(value)
76
77 @property
78 def Normal(self):
79 return self.__normal
80
81 @Normal.setter
82 def Normal(self, value):
83 self.__normal = int(value)
84
85 def __eq__(self, other):
86 if isinstance(other, BFPStateClass):
87 return (self.SNaN == other.SNaN and
88 self.QNaN == other.QNaN and
89 self.Infinity == other.Infinity and
90 self.Zero == other.Zero and
91 self.Denormal == other.Denormal and
92 self.Normal == other.Normal)
93 return NotImplemented
94
95 def _bfp_state_fields(self):
96 return (f"class_.SNaN: {self.SNaN}",
97 f"class_.QNaN: {self.QNaN}",
98 f"class_.Infinity: {self.Infinity}",
99 f"class_.Zero: {self.Zero}",
100 f"class_.Denormal: {self.Denormal}",
101 f"class_.Normal: {self.Normal}")
102
103 def __repr__(self):
104 fields = self._bfp_state_fields()
105 return f"<BFPStateClass {fields}>"
106
107
108 class SelectableMSB0Fraction:
109 """a MSB0 infinite bit string that is a real number generally between 0
110 and 2, but we approximate it using a Fraction.
111
112 bit 0 is the lsb of the integer part,
113 bit 1 is the msb of the fraction part,
114 bit 2 is the next-to-msb of the fraction part, etc.
115
116 this is not SelectableInt because we need more bits and because this isn't
117 an integer -- it can represent unlimited values, both really small and
118 really large -- corresponding to having bits be able to be set in bit
119 indexes 0, -1, -2, -3, etc.
120 """
121
122 def __init__(self, value=None):
123 self.__value = Fraction()
124 if value is not None:
125 self.eq(value)
126
127 @property
128 def value(self):
129 return self.__value
130
131 @value.setter
132 def value(self, v):
133 self.__value = Fraction(v)
134
135 @staticmethod
136 def __get_slice_dimensions(index):
137 if isinstance(index, slice):
138 if index.stop is None or index.step is not None:
139 raise ValueError("unsupported slice kind")
140 # use int() to convert from
141 start = int(0 if index.start is None else index.start)
142 stop = int(index.stop)
143 # pseudo-code compiler converts from inclusive to
144 # standard Python slices
145 length = stop - start
146 else:
147 start = int(index)
148 length = 1
149 return start, length
150
151 def __slice_as_int(self, start, length):
152 if start < 0 or length < 0:
153 raise ValueError("slice out of range")
154 if length == 0:
155 return 0
156 last = start + length - 1
157 # shift so bits we want are the lsb bits of the integer part
158 v = math.floor(self.value * (1 << last))
159 return v & ~(~0 << length) # mask off unwanted bits
160
161 def __set_slice(self, start, length, value):
162 if start < 0 or length < 0:
163 raise ValueError("slice out of range")
164 if length == 0:
165 return
166 last = start + length - 1
167 shift_factor = 1 << last
168 # shift so bits we want to replace are the lsb bits of the integer part
169 v = self.value * shift_factor
170 mask = ~(~0 << length)
171 # convert any SelectableInts to int and mask
172 value = int(value) & mask
173 # compute how much we need to add
174 offset = value - (math.floor(v) & mask)
175 # shift offset back into position
176 offset = Fraction(offset, shift_factor)
177 self.value += offset
178
179 def __getitem__(self, index):
180 start, length = self.__get_slice_dimensions(index)
181 return SelectableInt(self.__slice_as_int(start, length), length)
182
183 def __setitem__(self, index, value):
184 start, length = self.__get_slice_dimensions(index)
185 self.__set_slice(start, length, value)
186
187 def __str__(self, *,
188 max_int_digits=4, # don't need much since this is generally
189 # supposed to be in [0, 1]
190 max_fraction_digits=17, # u64 plus 1
191 fraction_sep_period=4, # how many fraction digits between `_`s
192 ):
193 """ convert to a string of the form: `0x3a.bc` or
194 `0x...face.face_face_face_face... (0xa8ef0000 / 0x5555)`"""
195 if max_int_digits <= 0 or max_fraction_digits <= 0:
196 raise ValueError("invalid digit limit")
197 approx = False
198 int_part = math.floor(self.value)
199 int_part_limit = 0x10 ** max_int_digits
200 if 0 <= int_part < int_part_limit:
201 int_str = hex(int_part)
202 else:
203 approx = True
204 int_part %= int_part_limit
205 int_str = f"0x...{int_part:0{max_int_digits}x}"
206
207 factor = 0x10 ** max_fraction_digits
208 fraction_part_exact = (self.value - math.floor(self.value)) * factor
209 fraction_part = math.floor(fraction_part_exact)
210
211 if fraction_part == fraction_part_exact:
212 # extract least-significant set bit of fraction_part
213 fraction_part_lsb = fraction_part & -fraction_part
214 log2_fraction_part_lsb = fraction_part_lsb.bit_length() - 1
215 zero_fraction_digits = max(0, log2_fraction_part_lsb // 4)
216 fraction_part >>= 4 * zero_fraction_digits
217 fraction_digits = max_fraction_digits - zero_fraction_digits
218 suffix = ""
219 else:
220 suffix = "..."
221 approx = True
222 fraction_digits = max_fraction_digits
223 fraction_str = f"{fraction_part:0{fraction_digits}x}"
224 fraction_parts = []
225 if fraction_sep_period is not None and fraction_sep_period > 0:
226 for i in range(0, len(fraction_str), fraction_sep_period):
227 fraction_parts.append(fraction_str[i:i + fraction_sep_period])
228 fraction_str = "_".join(fraction_parts)
229 fraction_str = "." + fraction_str + suffix
230 retval = int_str
231 if self.value.denominator != 1:
232 retval += fraction_str
233 else:
234 retval += ".0"
235 if approx:
236 n = self.value.numerator
237 d = self.value.denominator
238 fraction = f" ({n:#x} / {d:#x})"
239
240 # is the denominator a power of 2?
241 if (self.value.denominator & (self.value.denominator - 1)) == 0:
242 log2_d = self.value.denominator.bit_length() - 1
243
244 if self.value.denominator == 1:
245 fraction = f" ({n:#x})"
246
247 # extract least-significant set bit of n
248 n_lsb = n & -n
249 n //= n_lsb
250 log2_n_lsb = n_lsb.bit_length() - 1
251 exponent = log2_n_lsb - log2_d
252 if exponent < -8 or exponent > 8:
253 fraction = f" ({n:#x} * 2**{exponent})"
254 retval += fraction
255 return retval
256
257 def __repr__(self):
258 return "SelectableMSB0Fraction(" + str(self) + ")"
259
260 def eq(self, value):
261 if isinstance(value, (int, Fraction)):
262 self.value = Fraction(value)
263 elif isinstance(value, SelectableMSB0Fraction):
264 self.value = value.value
265 else:
266 raise ValueError("unsupported assignment type")
267
268 def __bool__(self):
269 return self.value != 0
270
271 def __neg__(self):
272 return SelectableMSB0Fraction(-self.value)
273
274 def __pos__(self):
275 return SelectableMSB0Fraction(self)
276
277 def __floor__(self):
278 return SelectableMSB0Fraction(math.floor(self.value))
279
280 def __ceil__(self):
281 return SelectableMSB0Fraction(math.ceil(self.value))
282
283 def __trunc__(self):
284 return SelectableMSB0Fraction(math.trunc(self.value))
285
286 def __round__(self):
287 return SelectableMSB0Fraction(round(self.value))
288
289 @staticmethod
290 def __arith_op(lhs, rhs, op):
291 lhs = SelectableMSB0Fraction(lhs)
292 rhs = SelectableMSB0Fraction(rhs)
293 return SelectableMSB0Fraction(op(lhs.value, rhs.value))
294
295 def __add__(self, other):
296 return self.__arith_op(self, other, operator.add)
297
298 __radd__ = __add__
299
300 def __mul__(self, other):
301 return self.__arith_op(self, other, operator.mul)
302
303 __rmul__ = __mul__
304
305 def __sub__(self, other):
306 return self.__arith_op(self, other, operator.sub)
307
308 def __rsub__(self, other):
309 return self.__arith_op(other, self, operator.sub)
310
311 def __truediv__(self, other):
312 return self.__arith_op(self, other, operator.truediv)
313
314 def __rtruediv__(self, other):
315 return self.__arith_op(other, self, operator.truediv)
316
317 def __floordiv__(self, other):
318 return self.__arith_op(self, other, operator.floordiv)
319
320 def __rfloordiv__(self, other):
321 return self.__arith_op(other, self, operator.floordiv)
322
323 def __mod__(self, other):
324 return self.__arith_op(self, other, operator.mod)
325
326 def __rmod__(self, other):
327 return self.__arith_op(other, self, operator.mod)
328
329 def __lshift__(self, amount):
330 if not isinstance(amount, int):
331 raise TypeError("can't shift by non-int")
332 if amount < 0:
333 return SelectableMSB0Fraction(self.value / (1 << -amount))
334 return SelectableMSB0Fraction(self.value * (1 << amount))
335
336 def __rlshift__(self, other):
337 raise TypeError("can't shift by non-int")
338
339 def __rshift__(self, amount):
340 if not isinstance(amount, int):
341 raise TypeError("can't shift by non-int")
342 return self << -amount
343
344 def __rrshift__(self, other):
345 raise TypeError("can't shift by non-int")
346
347 def __cmp_op(self, other, op):
348 if isinstance(other, (int, Fraction)):
349 pass
350 elif isinstance(other, SelectableMSB0Fraction):
351 other = other.value
352 else:
353 return NotImplemented
354 return op(self.value, other)
355
356 def __eq__(self, other):
357 return self.__cmp_op(other, operator.eq)
358
359 def __ne__(self, other):
360 return self.__cmp_op(other, operator.ne)
361
362 def __lt__(self, other):
363 return self.__cmp_op(other, operator.lt)
364
365 def __le__(self, other):
366 return self.__cmp_op(other, operator.le)
367
368 def __gt__(self, other):
369 return self.__cmp_op(other, operator.gt)
370
371 def __ge__(self, other):
372 return self.__cmp_op(other, operator.ge)
373
374
375 class BFPState:
376 """ implementation of binary floating-point working format as used in:
377 PowerISA v3.1B section 7.6.2.2
378 e.g. bfp_CONVERT_FROM_BFP32() on page 589(615)
379 """
380
381 def __init__(self, value=None):
382 self.__sign = 0
383 self.__exponent = 0
384 self.__significand = SelectableMSB0Fraction()
385 self.__class = BFPStateClass()
386 if value is not None:
387 self.eq(value)
388
389 def eq(self, rhs):
390 self.sign = rhs.sign
391 self.exponent = rhs.exponent
392 self.significand = rhs.significand
393 self.class_ = rhs.class_
394
395 @property
396 def sign(self):
397 return self.__sign
398
399 @sign.setter
400 def sign(self, value):
401 self.__sign = int(value)
402
403 @property
404 def exponent(self):
405 return self.__exponent
406
407 @exponent.setter
408 def exponent(self, value):
409 self.__exponent = int(value)
410
411 @property
412 def significand(self):
413 return self.__significand
414
415 @significand.setter
416 def significand(self, value):
417 self.__significand.eq(value)
418
419 @property
420 def class_(self):
421 return self.__class
422
423 @class_.setter
424 def class_(self, value):
425 self.__class.eq(value)
426
427 def __eq__(self, other):
428 if isinstance(other, BFPStateClass):
429 return self._bfp_state_fields() == other._bfp_state_fields()
430 return NotImplemented
431
432 def _bfp_state_fields(self):
433 class_fields = self.class_._bfp_state_fields()
434 return (f"sign: {self.sign}",
435 f"exponent: {self.exponent}",
436 f"significand: {self.significand}",
437 *self.class_._bfp_state_fields())
438
439 def __str__(self):
440 if self.class_.SNaN:
441 return "SNaN"
442 if self.class_.QNaN:
443 return "QNaN"
444 if self.class_.Infinity:
445 return "-Inf" if self.sign else "Inf"
446 retval = self.significand
447 if self.sign:
448 retval = -retval
449 retval <<= self.exponent
450 sign = ""
451 if retval < 0:
452 sign = "-"
453 retval = -retval
454 return sign + retval.__str__(max_int_digits=16)
455
456 def __repr__(self):
457 fields = self._bfp_state_fields()
458 return f"<BFPState {self} {fields}>"
459
460
461 # TODO: add tests