GCC Code Coverage Report
Directory: . Exec Total Coverage
File: build-coverage/deps/include/symfpu/core/operations.h Lines: 187 187 100.0 %
Date: 2021-08-01 Branches: 354 677 52.3 %

Line Exec Source
1
/*
2
** Copyright (C) 2018 Martin Brain
3
**
4
** See the file LICENSE for licensing information.
5
*/
6
7
/*
8
** operations.h
9
**
10
** Martin Brain
11
** martin.brain@cs.ox.ac.uk
12
** 15/11/14
13
**
14
** A number of compound operations on bit-vectors.  These are default
15
** implementations to reduce the amount of code that is needed for a
16
** back-end (and the risk of making a mistake).  Back-ends can provide
17
** their own implementations of these if they can handle them in a
18
** smarter way than the default.
19
**
20
*/
21
22
#include <cassert>
23
#include <map>
24
25
#include "../utils/common.h"
26
27
28
#ifndef SYMFPU_OPERATIONS
29
#define SYMFPU_OPERATIONS
30
31
namespace symfpu {
32
33
  /*** Size matching ***/
34
  template <class t, class bv>
35
  std::pair<bv, bv> sameWidth(const bv &op1, const bv &op2) {
36
    typename t::bwt w1(op1.getWidth());
37
    typename t::bwt w2(op2.getWidth());
38
39
    if (w1 == w2) {
40
      return std::make_pair(op1, op2);
41
    } else if (w1 < w2) {
42
      return std::make_pair(op1.matchWidth(op2), op2);
43
    } else {
44
      INVARIANT(w1 > w2);
45
      return std::make_pair(op1.matchWidth(op2), op2);
46
    }
47
  }
48
49
50
51
  /*** Expanding operations ***/
52
  template <class t, class bv>
53
4
  bv expandingAdd (const bv &op1, const bv &op2) {
54
4
    PRECONDITION(op1.getWidth() == op2.getWidth());
55
56
8
    bv x(op1.extend(1));
57
8
    bv y(op2.extend(1));
58
59
8
    return x + y;
60
  }
61
62
  template <class t, class bv, class prop>
63
158
    bv expandingAddWithCarryIn (const bv &op1, const bv &op2, const prop &cin) {
64
158
    PRECONDITION(op1.getWidth() == op2.getWidth());
65
66
316
    bv x(op1.extend(1));
67
316
    bv y(op2.extend(1));
68
69
316
    bv sum(x + y);
70
71
158
    typename t::bwt w(sum.getWidth());
72
316
    bv carry(ITE(cin, bv::one(w), bv::zero(w)));
73
158
    bv res(sum.modularAdd(carry)); // Modular is safe due to the extension
74
                                   // (2^n - 1) + (2^n - 1) + 1 == 2^(n+1) - 1
75
                                   // -(2^n) + -(2^n) + 1 > 2^(n+1)
76
316
    return res;
77
  }
78
79
  template <class t, class bv>
80
1940
  bv expandingSubtract (const bv &op1, const bv &op2) {
81
1940
    PRECONDITION(op1.getWidth() == op2.getWidth());
82
83
3880
    bv x(op1.extend(1));
84
3880
    bv y(op2.extend(1));
85
86
3880
    return x - y;
87
  }
88
89
  template <class t, class bv>
90
530
  bv expandingMultiply (const bv &op1, const bv &op2) {
91
530
    typename t::bwt width = op1.getWidth();
92
530
    PRECONDITION(width == op2.getWidth());
93
94
1060
    bv x(op1.extend(width));
95
1060
    bv y(op2.extend(width));
96
97
1060
    return x * y;
98
  }
99
100
101
  /*** Conditional Operations ***/
102
  template <class t, class bv, class prop>
103
1219
  bv conditionalIncrement (const prop &p, const bv &b) {
104
1219
    PRECONDITION(IMPLIES(p, b <  bv::maxValue(b.getWidth())));
105
106
1219
    typename t::bwt w(b.getWidth());
107
2438
    bv inc(ITE(p, bv::one(w), bv::zero(w)));
108
109
2438
    return b + inc;
110
  }
111
112
  template <class t, class bv, class prop>
113
202
  bv conditionalDecrement (const prop &p, const bv &b) {
114
202
    PRECONDITION(IMPLIES(p, bv::minValue(b.getWidth()) < b));
115
116
202
    typename t::bwt w(b.getWidth());
117
404
    bv inc(ITE(p, bv::one(w), bv::zero(w)));
118
119
404
    return b - inc;
120
  }
121
122
  template <class t, class bv, class prop>
123
1019
  bv conditionalLeftShiftOne (const prop &p, const bv &b) {
124
1019
    typename t::bwt w(b.getWidth());
125
1019
    PRECONDITION(IMPLIES(p, (b.extract(w - 1, w - 1).isAllZeros())));
126
127
2038
    bv shifted(b.modularLeftShift(bv::one(w)));
128
2038
    return bv(ITE(p, shifted, b));
129
  }
130
131
  template <class t, class bv, class prop>
132
628
  bv conditionalRightShiftOne (const prop &p, const bv &b) {
133
628
    typename t::bwt w(b.getWidth());
134
    // PRECONDITION(IMPLIES(p, (b.extract(0, 0).isAllZeros())));  // Adder uses and compensates for this case.
135
136
1256
    bv shifted(b.modularRightShift(bv::one(w)));
137
1256
    return bv(ITE(p, shifted, b));
138
  }
139
140
  template <class t, class bv, class prop>
141
640
  bv conditionalNegate (const prop &p, const bv &b) {
142
640
    typename t::bwt w(b.getWidth());
143
640
    PRECONDITION(w >= 2);
144
640
    PRECONDITION(IMPLIES(p, !(b.extract(w - 1, w - 1).isAllOnes() &&
145
			      b.extract(w - 2,     0).isAllZeros())));
146
147
640
    return bv(ITE(p, -b, b));
148
  }
149
150
  template <class t, class bv>
151
8
  bv abs(const bv &b) {
152
8
    return conditionalNegate<t, bv, typename t::prop>(b < bv::zero(b.getWidth()), b);
153
  }
154
155
156
157
  /*** Probability Annotations ***/
158
  enum probability {
159
    VERYLIKELY = 100,
160
    LIKELY = 50,
161
    NEUTRAL = 0,
162
    UNLIKELY = -50,
163
    VERYUNLIKELY = -100
164
  };
165
166
  template <class t, class prop>
167
33404
  void probabilityAnnotation (const prop &, const probability &) {
168
    // Back-ends can make use of this information if they want
169
33404
    return;
170
  }
171
172
173
  /*** Max and min ***/
174
  template <class t, class bv>
175
29519
  bv max (const bv &op1, const bv &op2) {
176
29519
    return ITE(op1 <= op2, op2, op1);
177
  }
178
179
  template <class t, class bv>
180
  bv min (const bv &op1, const bv &op2) {
181
    return ITE(op1 <= op2, op1, op2);
182
  }
183
184
  template <class t, class bv>
185
1219
  bv collar(const bv &op, const bv &lower, const bv &upper) {
186
2280
    return ITE(op < lower,
187
	       lower,
188
2280
	       ITE(upper < op,
189
		   upper,
190
4639
		   op));
191
  }
192
193
194
  /*** Unary/Binary operations ***/
195
  template <class t, class bv, class prop, class bwt>
196
  bv countLeadingZerosRec (const bv &op, const bwt position, const prop &allPreceedingZeros) {
197
    typename t::bwt w(op.getWidth());
198
199
    PRECONDITION(0 <= position && position < w);
200
201
    bv bit(op.extract(position, position));
202
203
    prop isLeadingOne(allPreceedingZeros && (bit.isAllOnes()));
204
    prop continuingZero(allPreceedingZeros && (bit.isAllZeros()));
205
206
    if (position == 0) {
207
      return ITE(isLeadingOne, bv(w, w - 1), bv(w, w));
208
    } else {
209
      return ITE(isLeadingOne,
210
		 bv(w, w - (position + 1)),
211
		 countLeadingZerosRec<t>(op, position - 1, continuingZero));
212
    }
213
  }
214
215
  template <class t, class bv>
216
  bv countLeadingZeros (const bv &op) {
217
    typedef typename t::bwt bwt;
218
    typedef typename t::prop prop;
219
    bwt w(op.getWidth());
220
221
    return countLeadingZerosRec<t>(op, w - 1, prop(true));
222
  }
223
224
  // This is sort of the opposite of count trailing 1's (a.k.a. clz(reverse(not(x))) )
225
  template <class t, class bv>
226
20094
  bv orderEncode (const bv &op) {
227
20094
    typename t::bwt w(op.getWidth());
228
229
    //PRECONDITION(bv::zero(w) <= op && op <= bv(w, w)); // Not needed as using modular shift
230
231
20094
    bv tmp((bv::one(w + 1).modularLeftShift(op.resize(w + 1))).modularDecrement().extract(w-1,0));
232
20094
    return tmp;
233
  }
234
235
236
  // The comparison we need to do is
237
  //   op.extract(relevantBits - 1, 0) == bv(relevantBits, position + 1)
238
  // bits can be shared between instances of this.
239
  // The following is a dynamic programming style solution.
240
  // HOWEVER : it may not actually give a net saving depending on your application
241
  template <class t, class bv>
242
4816
  struct fragmentMap {
243
    typedef typename t::bwt bwt;
244
    typedef typename t::prop prop;
245
246
    typedef std::pair<bwt, bwt> fragment;
247
    typedef std::map<fragment, prop> map;
248
249
  protected :
250
    const bv &op;
251
    map m;
252
253
    prop getComparitorRec(bwt length, bwt value)
254
    {
255
      PRECONDITION(length > 0);
256
      PRECONDITION(bitsToRepresent(value) <= length);
257
      typename map::const_iterator it = m.find(std::make_pair(length, value));
258
259
      if (it != m.end()) {
260
	return it->second;
261
      } else {
262
	bwt leadingBit = bwt(1) << (length - 1);
263
	prop leadingBitIsOne(op.extract(length - 1, length - 1).isAllOnes());
264
	prop correctComparison((value & leadingBit) ?
265
			       leadingBitIsOne : !leadingBitIsOne);
266
	prop *step = NULL;
267
268
	if (length == 1) {
269
	  step = new prop(correctComparison);
270
	} else {
271
	  prop rec(getComparitorRec(length - 1, value & (~leadingBit)));
272
	  step = new prop(correctComparison && rec);
273
	}
274
275
	prop res(*step);
276
	delete step;
277
278
	m.insert(std::make_pair(std::make_pair(length, value), res));
279
	return res;
280
      }
281
    }
282
283
  public :
284
4816
    fragmentMap(const bv &_op) : op(_op) {}
285
286
    prop getComparitor(bwt length, bwt value)
287
    {
288
      PRECONDITION(length > 0);
289
      PRECONDITION(bitsToRepresent(value) <= length);
290
291
      prop res(getComparitorRec(length, value));
292
293
      POSTCONDITION(bv(res) == (op.extract(length - 1, 0) == bv(length, value)));
294
      return res;
295
    }
296
  };
297
298
299
  // A more compact, bitwise implementation of orderEncode for SAT encoding
300
  // Intended to be used in specialisation of orderEncode
301
  template <class t, class bv>
302
4816
  bv orderEncodeBitwise (const bv &op) {
303
    typedef typename t::bwt bwt;
304
4816
    bwt w(op.getWidth());
305
306
9632
    fragmentMap<t,bv> m(op);
307
308
    // If op is too large, then set everything to 1
309
9632
    bv outOfRange(op >= bv(w, w));
310
311
    // SAND to fill in the remaining bits
312
4816
    bv * working = new bv(outOfRange);
313
64455
    for (bwt i = w; i > 0; --i) {
314
59639
      bwt position = i - 1;    // Position in the output bitvectors
315
59639
      bwt relevantBits = bitsToRepresent(position + 1);
316
59639
      INVARIANT(relevantBits > 0);
317
318
      //bv activateBit(m.getComparitor(relevantBits, position + 1));  // No more compact and slower
319
119278
      bv activateBit(op.extract(relevantBits - 1, 0) == bv(relevantBits, position + 1));
320
119278
      bv nextBit(working->extract(0,0) | activateBit);
321
322
59639
      bv * tmp = working;
323
59639
      working = new bv(working->append(nextBit));
324
59639
      delete tmp;
325
    }
326
327
4816
    bv output(working->extract(w - 1,0));
328
4816
    delete working;
329
330
4816
    POSTCONDITION(output == (bv::one(w + 1).modularLeftShift(op.resize(w + 1))).modularDecrement().extract(w-1,0));
331
332
9632
    return output;
333
  }
334
335
336
  /*** Custom shifts ***/
337
  // 1 if and only if the right shift moves at least one 1 out of the word
338
  template <class t, class bv>
339
628
  bv rightShiftStickyBit (const bv &op, const bv &shift) {
340
1884
    bv stickyBit(ITE((orderEncode<t>(shift) & op).isAllZeros(),
341
1256
		     bv::zero(op.getWidth()),
342
1256
		     bv::one(op.getWidth())));
343
344
628
    return stickyBit;
345
  }
346
347
348
  // It is easier to compute along with the shift
349
  template <class t>
350
628
  struct stickyRightShiftResult {
351
    typedef typename t::ubv ubv;
352
353
    ubv signExtendedResult;
354
    ubv stickyBit;
355
356
628
    stickyRightShiftResult(const ubv &ser, const ubv &sb) : signExtendedResult(ser), stickyBit(sb) {}
357
  stickyRightShiftResult(const stickyRightShiftResult &old) : signExtendedResult(old.signExtendedResult), stickyBit(old.stickyBit) {}
358
  };
359
360
361
  template <class t>
362
593
  stickyRightShiftResult<t> stickyRightShift (const typename t::ubv &input, const typename t::ubv &shiftAmount) {
363
593
    stickyRightShiftResult<t> res(input.signExtendRightShift(shiftAmount), rightShiftStickyBit<t>(input, shiftAmount));
364
365
593
    return res;
366
  }
367
368
369
  template <class t>
370
35
  stickyRightShiftResult<t> stickyRightShiftBitwise (const typename t::ubv &input, const typename t::ubv &shiftAmount) {
371
    typedef typename t::bwt bwt;
372
    typedef typename t::prop prop;
373
    typedef typename t::ubv ubv;
374
375
35
    bwt width(input.getWidth());
376
35
    bwt startingPosition(positionOfLeadingOne(width));
377
35
    INVARIANT(0 < startingPosition && startingPosition < width);
378
379
    // Catch the out of bounds case
380
35
    PRECONDITION(shiftAmount.getWidth() == width);
381
70
    prop fullShift(shiftAmount >= ubv(width, width));
382
    // Note the shiftAmount is treated as unsigned...
383
384
35
    ubv *working = new ubv(input);
385
35
    prop *stickyBit = new prop(ITE(fullShift, !input.isAllZeros(), prop(false)));
386
387
208
    for (bwt i = startingPosition + 1; i > 0; --i)
388
    {
389
173
      bwt shiftAmountPosition = i - 1;
390
391
346
      prop shiftEnabled = fullShift || shiftAmount.extract(shiftAmountPosition, shiftAmountPosition).isAllOnes();
392
393
346
      prop stickyAccumulate(shiftEnabled && !(working->extract((1ULL << shiftAmountPosition) - 1, 0).isAllZeros())); // Would this loose data?
394
395
173
      prop * tmp = stickyBit;
396
173
      stickyBit = new prop(*stickyBit || stickyAccumulate);
397
173
      delete tmp;
398
399
400
      // Note the slightly unexpected sign extension
401
346
      ubv shifted(working->signExtendRightShift(ubv::one(width) << ubv(width, shiftAmountPosition)));
402
403
173
      ubv * workingTmp = working;
404
173
      working = new ubv(ITE(shiftEnabled, shifted, *working));
405
173
      delete workingTmp;
406
    }
407
408
35
    stickyRightShiftResult<t> res(*working, ubv(*stickyBit).extend(width - 1));
409
410
35
    delete working;
411
35
    delete stickyBit;
412
413
35
    POSTCONDITION(res.signExtendedResult == input.signExtendRightShift(shiftAmount));
414
35
    POSTCONDITION(res.stickyBit == rightShiftStickyBit<t>(input, shiftAmount));
415
416
70
    return res;
417
  }
418
419
420
421
  template <class t>
422
1166
  struct normaliseShiftResult {
423
    typedef typename t::ubv ubv;
424
    typedef typename t::prop prop;
425
426
    ubv normalised;
427
    ubv shiftAmount;
428
    prop isZero;
429
430
1166
  normaliseShiftResult(const ubv &n, const ubv &s, const prop &z) : normalised(n), shiftAmount(s), isZero(z) {}
431
  normaliseShiftResult(const normaliseShiftResult<t> &old) :  normalised(old.normalised), shiftAmount(old.shiftAmount), isZero(old.isZero) {}
432
  };
433
434
  template <class t>
435
1166
  normaliseShiftResult<t> normaliseShift (const typename t::ubv input) {
436
    typedef typename t::bwt bwt;
437
    typedef typename t::prop prop;
438
    typedef typename t::ubv ubv;
439
440
1166
    bwt width(input.getWidth());
441
1166
    bwt startingMask(previousPowerOfTwo(width));
442
1166
    INVARIANT(startingMask < width);
443
444
    // Catch the zero case
445
1237
    prop zeroCase(input.isAllZeros());
446
447
1166
    ubv *working = new ubv(input);
448
1166
    ubv *shiftAmount = NULL;
449
1166
    prop *deactivateShifts = new prop(zeroCase);
450
451
5590
    for (bwt i = startingMask; i > 0; i >>= 1) {
452
4752
      prop newDeactivateShifts = *deactivateShifts || working->extract(width-1,width-1).isAllOnes();
453
4424
      delete deactivateShifts;
454
4424
      deactivateShifts = new prop(newDeactivateShifts);
455
456
8848
      ubv mask(ubv::allOnes(i).append(ubv::zero(width - i)));
457
4752
      prop shiftNeeded(!(*deactivateShifts) && (mask & *working).isAllZeros());
458
459
      // Modular is safe because of the mask comparison
460
8848
      ubv shifted(ITE(shiftNeeded, working->modularLeftShift(ubv(width, i)), *working));
461
4424
      delete working;
462
4424
      working = new ubv(shifted);
463
464
4424
      if (shiftAmount == NULL) {
465
1166
	shiftAmount = new ubv(shiftNeeded);
466
      } else {
467
6516
	ubv newShiftAmount = shiftAmount->append(ubv(shiftNeeded));
468
3258
	delete shiftAmount;
469
3258
	shiftAmount = new ubv(newShiftAmount);
470
      }
471
    }
472
473
1166
    normaliseShiftResult<t> res(*working, *shiftAmount, zeroCase);
474
475
1166
    delete deactivateShifts;
476
1166
    delete working;
477
1166
    delete shiftAmount;
478
479
1166
    POSTCONDITION(res.normalised.extract(width-1,width-1).isAllZeros() == res.isZero);
480
1166
    POSTCONDITION(IMPLIES(res.isZero, res.shiftAmount.isAllZeros()));
481
482
1166
    bwt shiftAmountWidth(res.shiftAmount.getWidth());
483
1166
    bwt widthBits(bitsToRepresent(width));
484
1166
    POSTCONDITION(shiftAmountWidth == widthBits ||
485
		  shiftAmountWidth == widthBits - 1); // If width is an exact power of 2
486
2332
    ubv widthBV(widthBits, width);
487
1166
    POSTCONDITION(res.shiftAmount.matchWidth(widthBV) < widthBV);
488
489
2332
    return res;
490
  }
491
492
493
  /*** Dividers ***/
494
  template <class t>
495
5349
  struct resultWithRemainderBit {
496
    typedef typename t::ubv ubv;
497
    typedef typename t::prop prop;
498
499
    ubv result;
500
    prop remainderBit;
501
502
5349
  resultWithRemainderBit(const ubv &o, const prop &r) : result(o), remainderBit(r) {}
503
  resultWithRemainderBit(const resultWithRemainderBit<t> &old) : result(old.result), remainderBit(old.remainderBit) {}
504
  };
505
506
  // x and y are fixed-point numbers in the range [1,2)
507
  // Compute o \in [0.5,2), r \in [0,\delta) such that:  x = o*y + r
508
  // Return (o, r != 0)
509
  template <class t>
510
202
  resultWithRemainderBit<t> fixedPointDivide (const typename t::ubv &x, const typename t::ubv &y) {
511
202
    typename t::bwt w(x.getWidth());
512
513
    // Same width and both have MSB ones
514
202
    PRECONDITION(y.getWidth() == w);
515
202
    PRECONDITION(x.extract(w - 1, w - 1).isAllOnes());
516
202
    PRECONDITION(y.extract(w - 1, w - 1).isAllOnes());
517
518
    typedef typename t::ubv ubv;
519
520
    // Not the best way of doing this but pretty universal
521
404
    ubv ex(x.append(ubv::zero(w - 1)));
522
404
    ubv ey(y.extend(w - 1));
523
524
404
    ubv div(ex / ey);
525
404
    ubv rem(ex % ey);
526
527
404
    return resultWithRemainderBit<t>(div.extract(w - 1, 0), !(rem.isAllZeros()));
528
  }
529
530
531
  // x is a fixed-point number in the range [1,4) with 2/p bits
532
  // Compute o \in [1,sqrt(2)), r \in [0,o*2 + 1) such that x = o*o + r with 1/p bits
533
  // Return (o, r != 0)
534
  template <class t>
535
31
  resultWithRemainderBit<t> fixedPointSqrt (const typename t::ubv &x) {
536
    typedef typename t::bwt bwt;
537
    typedef typename t::ubv ubv;
538
    typedef typename t::prop prop;
539
540
    // The default algorithm given here isn't a great one
541
    // However it is simple and has a very simple termination criteria.
542
    // Plus most symbolic back ends will prefer
543
    // o = nondet(), r = nondet(), assert(r < o*2 + 1), assert(x = o*o + r)
544
545
31
    bwt inputWidth(x.getWidth());
546
31
    bwt outputWidth(inputWidth - 1);
547
548
    // To compare against, we need to pad x to 2/2p
549
62
    ubv xcomp(x.append(ubv::zero(inputWidth - 2)));
550
551
    // Start at 1
552
62
    ubv working(ubv::one(outputWidth) << ubv(outputWidth, outputWidth - 1));
553
554
    bwt location;
555
372
    for (location = outputWidth - 1; location > 0; --location) { // Offset by 1 for easy termination
556
682
      ubv shift(ubv(outputWidth, location - 1));
557
558
682
      ubv candidate(working | (ubv::one(outputWidth) << shift));
559
560
352
      prop addBit(expandingMultiply<t, ubv>(candidate, candidate) <= xcomp);
561
562
341
      working = working | (ubv(addBit).extend(outputWidth - 1) << shift);
563
    }
564
565
62
    return resultWithRemainderBit<t>(working, !(expandingMultiply<t, ubv>(working, working) == xcomp));
566
  }
567
568
  // One step of a divider
569
  // Here the "remainder bit" is actual the result bit and
570
  // The result is the remainder
571
  template <class t>
572
5116
  resultWithRemainderBit<t> divideStep (const typename t::ubv &x, const typename t::ubv &y) {
573
    typedef typename t::bwt bwt;
574
    typedef typename t::ubv ubv;
575
    typedef typename t::prop prop;
576
577
5116
    bwt xWidth(x.getWidth());
578
5116
    bwt yWidth(y.getWidth());
579
580
5116
    PRECONDITION(xWidth == yWidth);
581
5116
    PRECONDITION(yWidth >= 2);
582
5116
    PRECONDITION(y.extract(yWidth - 2, yWidth - 2).isAllOnes());  // Assume y is aligned
583
584
5763
    prop canSubtract(x >= y);
585
10232
    ubv sub(x.modularAdd(y.modularNegate())); // TODO : modular subtract or better
586
10232
    ubv step(ITE(canSubtract, sub, x));
587
588
10232
    return resultWithRemainderBit<t>(step << ubv::one(xWidth), canSubtract);
589
  }
590
}
591
592
#endif