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
|
//===--- 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/SmallVector.h"
#include <optional>
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::getAllOnes(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::getZero(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.
std::optional<APInt> build() const {
if (Size == 0) {
return std::optional<APInt>();
}
auto result = APInt::getZero(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
|