| //===--- BitPatternBuilder.h - Create masks for composite types -----------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2019 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #pragma once |
| |
| #include "swift/Basic/ClusteredBitVector.h" |
| |
| #include "llvm/ADT/APInt.h" |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/ADT/SmallVector.h" |
| |
| namespace swift { |
| namespace irgen { |
| |
| /// BitPatternBuilder is a class to help with the construction of |
| /// bit masks for composite types. The class should be constructed |
| /// using the byte order of the target platform. Elements added |
| /// to the class must mask an entire element in a composite type. |
| /// These elements must be byte aligned. For example, if you want |
| /// to add a 64-bit integer to the bit pattern with the low 32 |
| /// bits set to 1 and the high 32 bits set to 0 then you must create |
| /// a 64-bit APInt and append it in its entirety rather than calling |
| /// the appendSetBits and appendClearBits helper functions which |
| /// would not be portable across architectures using different byte |
| /// orders. |
| /// |
| /// Example construction of a mask for a struct: |
| /// |
| /// // Type T that we are generating a mask for: |
| /// struct T { |
| /// uint8_t a; |
| /// uint8_t b; |
| /// uint16_t c; |
| /// }; |
| /// |
| /// // Code to generate the mask: |
| /// auto mask = BitPatternBuilder(isLittleEndian()); |
| /// mask.appendSetBits(8); // mask T.a with 0xff |
| /// mask.appendClearBits(8); // mask T.b with 0x00 |
| /// mask.append(APInt(16, 0x1177ULL)); // mask T.c with 0x1177 |
| /// |
| /// // Little-endian result: |
| /// mask.build(); // 0x117700ff [ ff 00 77 11 ] |
| /// |
| /// // Big-endian result: |
| /// mask.build(); // 0xff001177 [ ff 00 11 77 ] |
| /// |
| class BitPatternBuilder { |
| using APInt = llvm::APInt; |
| |
| // An array of masks that, when combined, will form the mask for a |
| // composite value. Generally these correspond to elements in a |
| // struct (or class, tuple etc.). |
| llvm::SmallVector<APInt, 8> Elements; |
| |
| // Little-endian byte order implies that elements should be |
| // appended to the most significant bit. If this flag is false |
| // then elements should be appended to the least signficant |
| // bit (big-endian byte order). |
| bool LittleEndian; |
| |
| // The combined size of the elements added so far in bits. |
| unsigned Size = 0; |
| public: |
| /// Create a new BitPatternBuilder with either a little-endian |
| /// (true) or big-endian (false) byte order. |
| BitPatternBuilder(bool littleEndian) : LittleEndian(littleEndian) {} |
| |
| /// Append the given mask to the bit pattern. The mask should mask |
| /// an entire element type and be byte aligned. |
| void append(const APInt &value) { |
| assert(value.getBitWidth() % 8 == 0); |
| Size += value.getBitWidth(); |
| Elements.push_back(value); |
| } |
| |
| /// Append the given mask to the bit pattern. The mask should mask |
| /// an entire element type and be byte aligned. |
| void append(APInt &&value) { |
| assert(value.getBitWidth() % 8 == 0); |
| Size += value.getBitWidth(); |
| Elements.push_back(std::move(value)); |
| } |
| |
| /// Append the given mask to the bit pattern. The mask should mask |
| /// an entire element type and be byte aligned. |
| void append(const ClusteredBitVector &value) { |
| assert(value.size() % 8 == 0); |
| if (!value.empty()) { |
| Size += value.size(); |
| Elements.push_back(value.asAPInt()); |
| } |
| } |
| |
| /// Append the given number of set (1) bits to the bit pattern. The |
| /// number of bits must be a multiple of 8 and mask a whole number |
| /// of element types. |
| void appendSetBits(unsigned numBits) { |
| assert(numBits % 8 == 0); |
| if (numBits) { |
| Size += numBits; |
| Elements.push_back(APInt::getAllOnesValue(numBits)); |
| } |
| } |
| |
| /// Append the given number of clear (0) bits to the bit pattern. The |
| /// number of bits must be a multiple of 8 and mask a whole number |
| /// of element types. |
| void appendClearBits(unsigned numBits) { |
| assert(numBits % 8 == 0); |
| if (numBits) { |
| Size += numBits; |
| Elements.push_back(APInt::getNullValue(numBits)); |
| } |
| } |
| |
| /// Append set (1) bits to the bit pattern until it reaches the |
| /// given size in bits. The total number of bits must be a |
| /// multiple of 8. |
| void padWithSetBitsTo(unsigned totalSizeInBits) { |
| assert(totalSizeInBits % 8 == 0); |
| assert(totalSizeInBits >= Size); |
| appendSetBits(totalSizeInBits - Size); |
| } |
| |
| /// Append clear (0) bits to the bit pattern until it reaches the |
| /// given size in bits. The total number of bits must be a |
| /// multiple of 8. |
| void padWithClearBitsTo(unsigned totalSizeInBits) { |
| assert(totalSizeInBits % 8 == 0); |
| assert(totalSizeInBits >= Size); |
| appendClearBits(totalSizeInBits - Size); |
| } |
| |
| /// Build the complete mask for the composite type. If the mask has a |
| /// length of 0 then the optional will not contain a value. Otherwise |
| /// the option will contain a value that is the combined length of |
| /// the elements appended to the builder. The mask represents is an |
| /// integer in the target byte order. |
| llvm::Optional<APInt> build() const { |
| if (Size == 0) { |
| return llvm::Optional<APInt>(); |
| } |
| auto result = APInt::getNullValue(Size); |
| unsigned offset = 0; |
| for (const auto &e : Elements) { |
| unsigned index = offset; |
| if (!LittleEndian) { |
| index = Size - offset - e.getBitWidth(); |
| } |
| result.insertBits(e, index); |
| offset += e.getBitWidth(); |
| } |
| assert(offset == Size); |
| return result; |
| } |
| }; |
| |
| } // end namespace irgen |
| } // end namespace swift |
| |