P4C
The P4 Compiler
Loading...
Searching...
No Matches
bitrange.h
1/*
2Copyright 2013-present Barefoot Networks, Inc.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#ifndef LIB_BITRANGE_H_
18#define LIB_BITRANGE_H_
19
20#include <algorithm>
21#include <iosfwd>
22#include <limits>
23#include <optional>
24#include <utility>
25
26#include "absl/numeric/bits.h"
27#include "bitvec.h"
28#include "exceptions.h"
29#include "hash.h"
30
31namespace P4 {
32
33/* iterate over ranges of contiguous bits in a bitvector */
34class bitranges {
35 bitvec tmp;
36 const bitvec &bits;
37 struct iter {
38 bool valid = true;
40 std::pair<int, int> range;
41
42 iter &operator++() {
43 if (ptr) {
44 range.first = range.second = ptr.index();
45 while (++ptr && range.second + 1 == ptr.index()) ++range.second;
46 } else {
47 valid = false;
48 }
49 return *this;
50 }
51 std::pair<int, int> operator*() { return range; }
52 bool operator==(iter &a) const { return valid == a.valid && ptr == a.ptr; }
53 bool operator!=(iter &a) const { return !(*this == a); }
54 explicit iter(bitvec::const_bitref p) : ptr(p) { ++*this; }
55 };
56
57 public:
58 explicit bitranges(const bitvec &b) : bits(b) {}
59 explicit bitranges(bitvec &&b) : tmp(b), bits(tmp) {}
60 explicit bitranges(uintptr_t b) : tmp(b), bits(tmp) {}
61 iter begin() const { return iter(bits.begin()); }
62 iter end() const { return iter(bits.end()); }
63};
64
65class JSONGenerator;
66class JSONLoader;
67
68namespace BitRange {
69
70namespace Detail {
77constexpr inline int divideFloor(int dividend, int divisor) {
78#if defined(__GNUC__) || defined(__clang__)
79 // Code to enable compiler folding when the divisor is a power-of-two compile-time constant
80 // In this case, compiler should fold to a right-shift
81 // FIXME: Replace absl with std after moving to C++20
82 unsigned u_divisor = static_cast<unsigned>(divisor);
83 if (__builtin_constant_p(u_divisor) && absl::has_single_bit(u_divisor))
84 return dividend >> (absl::bit_width(u_divisor) - 1);
85#endif // defined(__GNUC__) || defined(__clang__)
86
87 const int quotient = dividend / divisor;
88 const int remainder = dividend % divisor;
89 if ((remainder != 0) && ((remainder < 0) != (divisor < 0))) return quotient - 1;
90 return quotient;
91}
92
100constexpr int modulo(int dividend, int divisor) {
101 return (dividend % divisor) * ((dividend < 0) != (divisor < 0) ? -1 : 1);
102}
103
117constexpr inline int moduloFloor(const int dividend, const int divisor) {
118#if defined(__GNUC__) || defined(__clang__)
119 // Code to enable compiler folding when the divisor is a power-of-two compile-time constant
120 // In this case, compiler should fold to a bitwise-and
121 // FIXME: Replace absl with std after moving to C++20
122 if (__builtin_constant_p(divisor) && absl::has_single_bit(static_cast<unsigned>(divisor)))
123 return dividend & (divisor - 1);
124#endif // defined(__GNUC__) || defined(__clang__)
125
126 const int remainder = modulo(dividend, divisor);
127 if (remainder == 0 || dividend >= 0) return remainder;
128 return divisor - remainder;
129}
130} // namespace Detail
131
141struct FromTo {
142 FromTo(int from, int to) : from(from), to(to) {}
143 FromTo(const FromTo &) = delete;
144 FromTo &operator=(const FromTo &) = delete;
145 const int from;
146 const int to;
147};
148
158struct StartLen {
159 StartLen(int start, int len) : start(start), len(len) {}
160 StartLen(const StartLen &) = delete;
161 StartLen &operator=(const StartLen &) = delete;
162 const int start;
163 const int len;
164};
165
190struct ZeroToMax {};
191
209struct MinToMax {};
210
212void rangeToJSON(JSONGenerator &json, int lo, int hi);
213std::pair<int, int> rangeFromJSON(JSONLoader &json);
214
215} // namespace BitRange
216
218enum class RangeUnit : uint8_t {
219 Bit = 0,
220 Byte = 1
221};
222
224enum class Endian : uint8_t {
225 Network = 0,
226 Little = 1,
227};
228
252template <RangeUnit Unit, Endian Order>
254 static constexpr RangeUnit unit = Unit;
255 static constexpr Endian order = Order;
256
257 using FromTo = BitRange::FromTo;
261
262 HalfOpenRange() : lo(0), hi(0) {}
263 HalfOpenRange(int lo, int hi) : lo(lo), hi(hi) {}
264 HalfOpenRange(FromTo &&fromTo) // NOLINT(runtime/explicit)
265 : lo(fromTo.from), hi(fromTo.to + 1) {}
266 HalfOpenRange(StartLen &&startLen) // NOLINT(runtime/explicit)
267 : lo(startLen.start), hi(startLen.start + startLen.len) {}
268 HalfOpenRange(ZeroToMax &&) // NOLINT(runtime/explicit)
269 : lo(0), hi(std::numeric_limits<int>::max()) {}
270 HalfOpenRange(MinToMax &&) // NOLINT(runtime/explicit)
271 : lo(std::numeric_limits<int>::min()), hi(std::numeric_limits<int>::max()) {}
272 explicit HalfOpenRange(std::pair<int, int> range) : lo(range.first), hi(range.second) {}
273
275 ssize_t size() const { return ssize_t(hi) - ssize_t(lo); }
276
280 if (empty()) return HalfOpenRange(0, 0);
281 return *this;
282 }
283
289 auto asBits = toUnit<RangeUnit::Bit>();
290 return {asBits.lo, asBits.lo + size};
291 }
292
296 const int resizedLo = empty() ? 0 : lo;
297 if (Unit == RangeUnit::Byte) return {resizedLo, resizedLo + size};
298 return {resizedLo, resizedLo + size * 8};
299 }
300
306 auto asBits = toUnit<RangeUnit::Bit>();
307 return {asBits.lo + offset, asBits.hi + offset};
308 }
309
314 if (empty()) return HalfOpenRange();
315 if (Unit == RangeUnit::Byte) return {lo + offset, hi + offset};
316 return {lo + offset * 8, hi + offset * 8};
317 }
318
320 int loByte() const {
321 if (empty()) return 0;
322 return Unit == RangeUnit::Byte ? lo : BitRange::Detail::divideFloor(lo, 8);
323 }
324
326 int hiByte() const {
327 if (empty()) return 0;
328 return Unit == RangeUnit::Byte ? hi - 1 : BitRange::Detail::divideFloor(hi - 1, 8);
329 }
330
334 int nextByte() const { return empty() ? 0 : hiByte() + 1; }
335
338 bool isLoAligned() const {
339 return (empty() || Unit == RangeUnit::Byte) ? true
340 : BitRange::Detail::moduloFloor(lo, 8) == 0;
341 }
342
346 bool isHiAligned() const {
347 return (empty() || Unit == RangeUnit::Byte) ? true
348 : BitRange::Detail::moduloFloor(hi, 8) == 0;
349 }
350
351 bool operator==(HalfOpenRange other) const {
352 if (empty()) return other.empty();
353 return other.lo == lo && other.hi == hi;
354 }
355 bool operator!=(HalfOpenRange other) const { return !(*this == other); }
356
358 bool empty() const { return lo == hi; }
359
361 bool contains(int index) const { return index >= lo && index < hi; }
362
365 bool contains(HalfOpenRange other) const { return intersectWith(other) == other; }
366
370 bool overlaps(HalfOpenRange a) const { return !intersectWith(a).empty(); }
371 bool overlaps(int l, int h) const { return !intersectWith(l, h).empty(); }
372
377 HalfOpenRange intersectWith(int l, int h) const {
378 HalfOpenRange rv = {std::max(lo, l), std::min(hi, h)};
379 if (rv.hi <= rv.lo) return {0, 0};
380 return rv;
381 }
382 HalfOpenRange operator&(HalfOpenRange a) const { return intersectWith(a); }
383 HalfOpenRange operator&=(HalfOpenRange a) {
384 *this = intersectWith(a);
385 return *this;
386 }
387
393 HalfOpenRange unionWith(int l, int h) const {
394 if (empty()) return {l, h};
395 if (l == h) return *this;
396 return HalfOpenRange(std::min(lo, l), std::max(hi, h));
397 }
398 HalfOpenRange operator|(HalfOpenRange a) const { return unionWith(a); }
399 HalfOpenRange operator|=(HalfOpenRange a) {
400 *this = unionWith(a);
401 return *this;
402 }
403
426 template <Endian DestOrder>
428 if (DestOrder == Order) return HalfOpenRange<Unit, DestOrder>(lo, hi);
429 switch (DestOrder) {
430 case Endian::Network:
431 case Endian::Little:
432 return HalfOpenRange<Unit, DestOrder>(spaceSize - hi, spaceSize - lo);
433 }
434 BUG("Unexpected ordering");
435 }
436
452 template <RangeUnit DestUnit>
454 if (DestUnit == Unit) return HalfOpenRange<DestUnit, Order>(lo, hi);
456 switch (DestUnit) {
457 case RangeUnit::Bit:
458 return HalfOpenRange<DestUnit, Order>(lo * 8, hi * 8);
459 case RangeUnit::Byte:
461 }
462 BUG("Unexpected unit");
463 }
464
466 void toJSON(JSONGenerator &json) const { BitRange::rangeToJSON(json, lo, hi); }
467 static HalfOpenRange fromJSON(JSONLoader &json) {
468 return HalfOpenRange(BitRange::rangeFromJSON(json));
469 }
470
472 bool operator<(const HalfOpenRange &other) const {
473 if (lo != other.lo) return lo < other.lo;
474 return hi < other.hi;
475 }
476
477 friend size_t hash_value(const HalfOpenRange &r) { return Util::Hash{}(r.lo, r.hi); }
478
482 int lo;
483
488 int hi;
489};
490
512template <RangeUnit Unit, Endian Order>
514 static constexpr RangeUnit unit = Unit;
515 static constexpr Endian order = Order;
516
517 using FromTo = BitRange::FromTo;
521
522 ClosedRange() : lo(0), hi(0) {} // FIXME: default is [0,0]? This is just wrong ...
523 constexpr ClosedRange(int lo, int hi) : lo(lo), hi(hi) {}
524 ClosedRange(FromTo &&fromTo) // NOLINT(runtime/explicit)
525 : lo(fromTo.from), hi(fromTo.to) {}
526 ClosedRange(StartLen &&startLen) // NOLINT(runtime/explicit)
527 : lo(startLen.start), hi(startLen.start + startLen.len - 1) {}
528 ClosedRange(ZeroToMax &&) // NOLINT(runtime/explicit)
529 : lo(0), hi(std::numeric_limits<int>::max() - 1) {}
530 ClosedRange(MinToMax &&) // NOLINT(runtime/explicit)
531 : lo(std::numeric_limits<int>::min()), hi(std::numeric_limits<int>::max() - 1) {}
532 explicit ClosedRange(std::pair<int, int> range) : lo(range.first), hi(range.second) {}
533 ClosedRange(const HalfOpenRange<Unit, Order> &r) // NOLINT(runtime/explicit)
534 : lo(r.lo), hi(r.hi - 1) {
535 BUG_CHECK(!r.empty(), "can't convert empty range to Closed");
536 }
537
539 ssize_t size() const { return ssize_t(hi) - ssize_t(lo) + 1; }
540
543 BUG_CHECK(size != 0, "Resizing ClosedRange to zero size");
544 auto asBits = toUnit<RangeUnit::Bit>();
545 return {asBits.lo, asBits.lo + size - 1};
546 }
547
550 BUG_CHECK(size != 0, "Resizing ClosedRange to zero size");
551 if (Unit == RangeUnit::Byte) return {lo, lo + size - 1};
552 return {lo, lo + size * 8 - 1};
553 }
554
557 auto asBits = toUnit<RangeUnit::Bit>();
558 return {asBits.lo + offset, asBits.hi + offset};
559 }
560
562 ClosedRange shiftedByBytes(int offset) const {
563 if (Unit == RangeUnit::Byte) return {lo + offset, hi + offset};
564 return {lo + offset * 8, hi + offset * 8};
565 }
566
568 int loByte() const {
569 return Unit == RangeUnit::Byte ? lo : BitRange::Detail::divideFloor(lo, 8);
570 }
571
573 int hiByte() const {
574 return Unit == RangeUnit::Byte ? hi : BitRange::Detail::divideFloor(hi, 8);
575 }
576
578 int nextByte() const { return hiByte() + 1; }
579
581 bool isLoAligned() const {
582 return Unit == RangeUnit::Byte ? true : BitRange::Detail::moduloFloor(lo, 8) == 0;
583 }
584
586 bool isHiAligned() const {
587 return Unit == RangeUnit::Byte ? true : BitRange::Detail::moduloFloor(hi + 1, 8) == 0;
588 }
589
590 bool operator==(ClosedRange other) const { return (other.lo == lo) && (other.hi == hi); }
591 bool operator!=(ClosedRange other) const { return !(*this == other); }
592
594 bool contains(int index) const { return (index >= lo) && (index <= hi); }
595
597 bool contains(ClosedRange other) const {
598 auto intersection = intersectWith(other);
599 return intersection.lo == other.lo && intersection.size() == other.size();
600 }
601
603 bool overlaps(ClosedRange a) const { return !intersectWith(a).empty(); }
604 bool overlaps(int l, int h) const { return !intersectWith(l, h).empty(); }
605
613 HalfOpenRange<Unit, Order> intersectWith(int l, int h) const {
614 return HalfOpenRange<Unit, Order>(lo, hi + 1)
616 }
617 HalfOpenRange<Unit, Order> operator&(ClosedRange a) const { return intersectWith(a); }
618 HalfOpenRange<Unit, Order> operator&=(ClosedRange a) {
619 *this = intersectWith(a);
620 return *this;
621 }
622
624 ClosedRange unionWith(ClosedRange a) const { return unionWith(a.lo, a.hi); }
625 ClosedRange unionWith(int l, int h) const {
626 return ClosedRange(std::min(lo, l), std::max(hi, h));
627 }
628 ClosedRange operator|(ClosedRange a) const { return unionWith(a); }
629 ClosedRange operator|=(ClosedRange a) {
630 *this = unionWith(a);
631 return *this;
632 }
633
635 template <Endian DestOrder>
637 BUG_CHECK(spaceSize > 0, "Can't represent an empty range");
638 if (DestOrder == Order) return ClosedRange<Unit, DestOrder>(lo, hi);
639 switch (DestOrder) {
640 case Endian::Network:
641 case Endian::Little:
642 return ClosedRange<Unit, DestOrder>((spaceSize - 1) - hi, (spaceSize - 1) - lo);
643 }
644 BUG("Unexpected ordering");
645 }
646
648 template <RangeUnit DestUnit>
650 if (DestUnit == Unit) return ClosedRange<DestUnit, Order>(lo, hi);
651 switch (DestUnit) {
652 case RangeUnit::Bit:
653 return ClosedRange<DestUnit, Order>(lo * 8, hi * 8 + 7);
654 case RangeUnit::Byte:
656 }
657 BUG("Unexpected unit");
658 }
659
661 void toJSON(JSONGenerator &json) const { BitRange::rangeToJSON(json, lo, hi); }
662 static ClosedRange fromJSON(JSONLoader &json) {
663 return ClosedRange(BitRange::rangeFromJSON(json));
664 }
665
667 bool operator<(const ClosedRange &other) const {
668 if (lo != other.lo) return lo < other.lo;
669 return hi < other.hi;
670 }
671
672 friend size_t hash_value(const ClosedRange &r) { return Util::Hash{}(r.lo, r.hi); }
673
676 cstring formatAsSlice(int spaceSize) const {
677 auto r = toOrder<Endian::Little>(spaceSize);
678 auto hi = r.hi;
679 auto lo = r.lo;
680 if (Unit == RangeUnit::Byte) {
681 lo = lo * 8;
682 hi = hi * 8 + 7;
683 } else {
684 BUG_CHECK(Unit == RangeUnit::Bit, "mismatch range units");
685 }
686 std::stringstream out;
687 out << "[" << hi << ":" << lo << "]";
688 return cstring(out.str());
689 }
690
694 int lo;
695
700 int hi;
701};
702
715template <RangeUnit Unit, Endian Order>
716std::pair<HalfOpenRange<Unit, Order>, HalfOpenRange<Unit, Order>> operator-(
718 HalfOpenRange<Unit, Order> empty = {0, 0};
719 HalfOpenRange<Unit, Order> intersect = left.intersectWith(right);
720 if (intersect.empty())
721 return left.lo < right.lo ? std::make_pair(left, empty) : std::make_pair(empty, left);
722
724 left.lo == intersect.lo ? empty : BitRange::FromTo(left.lo, intersect.lo - 1);
726 left.hi == intersect.hi ? empty : BitRange::FromTo(intersect.hi, left.hi - 1);
727
728 return {lower, upper};
729}
730
733template <RangeUnit Unit, Endian Order>
734std::pair<HalfOpenRange<Unit, Order>, HalfOpenRange<Unit, Order>> operator-(
736 return toHalfOpenRange(left) - toHalfOpenRange(right);
737}
738
741template <RangeUnit Unit, Endian Order>
743 return HalfOpenRange<Unit, Order>(closedRange.lo, closedRange.hi + 1);
744}
745
748template <RangeUnit Unit, Endian Order>
749std::optional<ClosedRange<Unit, Order>> toClosedRange(HalfOpenRange<Unit, Order> halfOpenRange) {
750 if (halfOpenRange.empty()) return std::nullopt;
751 return ClosedRange<Unit, Order>(halfOpenRange.lo, halfOpenRange.hi - 1);
752}
753
757
761
765
769
770std::ostream &toStream(std::ostream &out, RangeUnit unit, Endian order, int lo, int hi,
771 bool closed);
772
773template <RangeUnit Unit, Endian Order>
774std::ostream &operator<<(std::ostream &out, const HalfOpenRange<Unit, Order> &range) {
775 return toStream(out, Unit, Order, range.lo, range.hi, false);
776}
777
778template <RangeUnit Unit, Endian Order>
779std::ostream &operator<<(std::ostream &out, const ClosedRange<Unit, Order> &range) {
780 return toStream(out, Unit, Order, range.lo, range.hi, true);
781}
782
783} // namespace P4
784
785// Hashing specializations
786namespace std {
787template <P4::RangeUnit Unit, P4::Endian Order>
788struct hash<P4::HalfOpenRange<Unit, Order>> {
789 std::size_t operator()(const P4::HalfOpenRange<Unit, Order> &r) const {
790 return P4::Util::Hash{}(r.lo, r.hi);
791 }
792};
793
794template <P4::RangeUnit Unit, P4::Endian Order>
795struct hash<P4::ClosedRange<Unit, Order>> {
796 std::size_t operator()(const P4::ClosedRange<Unit, Order> &r) const {
797 return P4::Util::Hash{}(r.lo, r.hi);
798 }
799};
800} // namespace std
801
802namespace P4::Util {
803template <RangeUnit Unit, Endian Order>
804struct Hasher<HalfOpenRange<Unit, Order>> {
805 size_t operator()(const HalfOpenRange<Unit, Order> &r) const {
806 return Util::Hash{}(r.lo, r.hi);
807 }
808};
809
810template <RangeUnit Unit, Endian Order>
811struct Hasher<ClosedRange<Unit, Order>> {
812 size_t operator()(const ClosedRange<Unit, Order> &r) const { return Util::Hash{}(r.lo, r.hi); }
813};
814} // namespace P4::Util
815
816#endif /* LIB_BITRANGE_H_ */
Definition json_generator.h:36
Definition json_loader.h:38
Definition bitrange.h:34
Definition bitvec.h:201
Definition bitvec.h:120
Definition cstring.h:85
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
Endian
An ordering for bits or bytes.
Definition bitrange.h:224
@ Little
Most significant bit/byte first.
std::optional< ClosedRange< Unit, Order > > toClosedRange(HalfOpenRange< Unit, Order > halfOpenRange)
Definition bitrange.h:749
RangeUnit
Units in which a range can be specified.
Definition bitrange.h:218
std::pair< HalfOpenRange< Unit, Order >, HalfOpenRange< Unit, Order > > operator-(HalfOpenRange< Unit, Order > left, HalfOpenRange< Unit, Order > right)
Definition bitrange.h:716
HalfOpenRange< Unit, Order > toHalfOpenRange(ClosedRange< Unit, Order > closedRange)
Definition bitrange.h:742
STL namespace.
Definition bitrange.h:141
Definition bitrange.h:209
Definition bitrange.h:158
Definition bitrange.h:190
Definition bitrange.h:513
bool isHiAligned() const
Definition bitrange.h:586
bool contains(ClosedRange other) const
Definition bitrange.h:597
ClosedRange< DestUnit, Order > toUnit() const
Definition bitrange.h:649
bool overlaps(ClosedRange a) const
Definition bitrange.h:603
ClosedRange< Unit, DestOrder > toOrder(int spaceSize) const
Definition bitrange.h:636
int loByte() const
Definition bitrange.h:568
ClosedRange resizedToBytes(int size) const
Definition bitrange.h:549
bool isLoAligned() const
Definition bitrange.h:581
int hiByte() const
Definition bitrange.h:573
HalfOpenRange< Unit, Order > intersectWith(ClosedRange a) const
Definition bitrange.h:610
void toJSON(JSONGenerator &json) const
JSON serialization/deserialization.
Definition bitrange.h:661
ClosedRange< RangeUnit::Bit, Order > shiftedByBits(int offset) const
Definition bitrange.h:556
ClosedRange< RangeUnit::Bit, Order > resizedToBits(int size) const
Definition bitrange.h:542
cstring formatAsSlice(int spaceSize) const
Definition bitrange.h:676
ClosedRange shiftedByBytes(int offset) const
Definition bitrange.h:562
int lo
Definition bitrange.h:694
ssize_t size() const
Definition bitrange.h:539
bool operator<(const ClosedRange &other) const
Definition bitrange.h:667
int hi
Definition bitrange.h:700
bool contains(int index) const
Definition bitrange.h:594
ClosedRange unionWith(ClosedRange a) const
Definition bitrange.h:624
int nextByte() const
Definition bitrange.h:578
Definition bitrange.h:253
bool contains(int index) const
Definition bitrange.h:361
int hi
Definition bitrange.h:488
bool operator<(const HalfOpenRange &other) const
Total ordering, first by lo, then by hi.
Definition bitrange.h:472
int hiByte() const
Definition bitrange.h:326
HalfOpenRange< Unit, DestOrder > toOrder(int spaceSize) const
Definition bitrange.h:427
bool isHiAligned() const
Definition bitrange.h:346
bool overlaps(HalfOpenRange a) const
Definition bitrange.h:370
int nextByte() const
Definition bitrange.h:334
HalfOpenRange< Unit, Order > shiftedByBytes(int offset) const
Definition bitrange.h:313
bool contains(HalfOpenRange other) const
Definition bitrange.h:365
bool isLoAligned() const
Definition bitrange.h:338
HalfOpenRange< DestUnit, Order > toUnit() const
Definition bitrange.h:453
int loByte() const
Definition bitrange.h:320
ssize_t size() const
Definition bitrange.h:275
void toJSON(JSONGenerator &json) const
JSON serialization/deserialization.
Definition bitrange.h:466
HalfOpenRange< RangeUnit::Bit, Order > resizedToBits(int size) const
Definition bitrange.h:287
bool empty() const
Definition bitrange.h:358
HalfOpenRange unionWith(HalfOpenRange a) const
Definition bitrange.h:392
HalfOpenRange canonicalize() const
Definition bitrange.h:279
int lo
Definition bitrange.h:482
HalfOpenRange< RangeUnit::Bit, Order > shiftedByBits(int offset) const
Definition bitrange.h:304
HalfOpenRange resizedToBytes(int size) const
Definition bitrange.h:295
HalfOpenRange intersectWith(HalfOpenRange a) const
Definition bitrange.h:376
Definition hash.h:125
Definition hash.h:123