Skip to content

Commit 5e12567

Browse files
authored
Merge pull request #73624 from tbkka/tbkka-mpe-sparebits-6.0
[6.0] Compute spare bit mask from first principles
2 parents a1f9a6b + bb3b89f commit 5e12567

File tree

6 files changed

+693
-422
lines changed

6 files changed

+693
-422
lines changed
Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
//===--- Bitmask.h - Swift Bitmask type for Reflection ----*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Used by TypeLowering logic to compute masks for in-memory representations
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_REFLECTION_BITMASK_H
18+
#define SWIFT_REFLECTION_BITMASK_H
19+
20+
#include "swift/Remote/MemoryReader.h"
21+
#include <sstream>
22+
23+
namespace swift {
24+
namespace reflection {
25+
26+
// A variable-length bitmap used to track "spare bits" for general multi-payload
27+
// enums. Note: These are not arbitrary-sized! They are always a multiple
28+
// of 8 bits in size, and always aligned on an 8-bit boundary.
29+
class BitMask {
30+
static constexpr unsigned maxSize = 128 * 1024 * 1024; // 128MB
31+
32+
unsigned size; // Size of mask _in bytes_
33+
uint8_t *mask;
34+
public:
35+
~BitMask() {
36+
free(mask);
37+
}
38+
private:
39+
// Construct a bitmask of the appropriate number of bytes
40+
// initialized to all bits set
41+
BitMask(unsigned sizeInBytes = 0): size(sizeInBytes) {
42+
assert(size < maxSize && "Trying to build a too-large bitmask");
43+
if (size > maxSize || size == 0) {
44+
size = 0;
45+
mask = nullptr;
46+
return;
47+
}
48+
49+
mask = (uint8_t *)malloc(size);
50+
51+
if (!mask) {
52+
// Malloc might fail if size is large due to some bad data. Assert in
53+
// asserts builds, and fail gracefully in non-asserts builds by
54+
// constructing an empty BitMask.
55+
assert(false && "Failed to allocate BitMask");
56+
size = 0;
57+
return;
58+
}
59+
60+
memset(mask, 0xff, size);
61+
}
62+
63+
public:
64+
static BitMask zeroMask(unsigned sizeInBytes) {
65+
auto mask = BitMask(sizeInBytes);
66+
mask.makeZero();
67+
return mask;
68+
}
69+
70+
static BitMask oneMask(unsigned sizeInBytes) {
71+
auto mask = BitMask(sizeInBytes);
72+
return mask;
73+
}
74+
75+
BitMask(unsigned sizeInBytes, uint64_t sourceMask): size(sizeInBytes) {
76+
mask = (uint8_t *)calloc(1, sizeInBytes);
77+
memcpy(mask, &sourceMask, sizeInBytes);
78+
}
79+
80+
// Construct a bitmask of the appropriate number of bytes
81+
// initialized with bits from the specified buffer
82+
BitMask(unsigned sizeInBytes, const uint8_t *initialValue,
83+
unsigned initialValueBytes, unsigned offset)
84+
: size(sizeInBytes) {
85+
// Gracefully fail by constructing an empty mask if we exceed the size
86+
// limit.
87+
if (size > maxSize) {
88+
size = 0;
89+
mask = nullptr;
90+
return;
91+
}
92+
93+
// Bad data could cause the initial value location to be off the end of our
94+
// size. If initialValueBytes + offset is beyond sizeInBytes (or overflows),
95+
// assert in asserts builds, and fail gracefully in non-asserts builds by
96+
// constructing an empty BitMask.
97+
bool overflowed = false;
98+
unsigned initialValueEnd =
99+
llvm::SaturatingAdd(initialValueBytes, offset, &overflowed);
100+
if (overflowed) {
101+
assert(false && "initialValueBytes + offset overflowed");
102+
size = 0;
103+
mask = nullptr;
104+
return;
105+
}
106+
assert(initialValueEnd <= sizeInBytes);
107+
if (initialValueEnd > size) {
108+
assert(false && "initialValueBytes + offset is greater than size");
109+
size = 0;
110+
mask = nullptr;
111+
return;
112+
}
113+
114+
mask = (uint8_t *)calloc(1, size);
115+
116+
if (!mask) {
117+
// Malloc might fail if size is large due to some bad data. Assert in
118+
// asserts builds, and fail gracefully in non-asserts builds by
119+
// constructing an empty BitMask.
120+
assert(false && "Failed to allocate BitMask");
121+
size = 0;
122+
return;
123+
}
124+
125+
memcpy(mask + offset, initialValue, initialValueBytes);
126+
}
127+
// Move constructor moves ownership and zeros the src
128+
BitMask(BitMask&& src) noexcept: size(src.size), mask(std::move(src.mask)) {
129+
src.size = 0;
130+
src.mask = nullptr;
131+
}
132+
// Copy constructor makes a copy of the mask storage
133+
BitMask(const BitMask& src) noexcept: size(src.size), mask(nullptr) {
134+
mask = (uint8_t *)malloc(size);
135+
memcpy(mask, src.mask, size);
136+
}
137+
138+
std::string str() const {
139+
std::ostringstream buff;
140+
buff << size << ":0x";
141+
for (unsigned i = 0; i < size; i++) {
142+
buff << std::hex << ((mask[i] >> 4) & 0x0f) << (mask[i] & 0x0f);
143+
}
144+
return buff.str();
145+
}
146+
147+
bool operator==(const BitMask& rhs) const {
148+
// The two masks may be of different sizes.
149+
// The common prefix must be identical.
150+
size_t common = std::min(size, rhs.size);
151+
if (memcmp(mask, rhs.mask, common) != 0)
152+
return false;
153+
// The remainder of the longer mask must be
154+
// all zero bits.
155+
unsigned mustBeZeroSize = std::max(size, rhs.size) - common;
156+
uint8_t *mustBeZero;
157+
if (size < rhs.size) {
158+
mustBeZero = rhs.mask + size;
159+
} else if (size > rhs.size) {
160+
mustBeZero = mask + rhs.size;
161+
}
162+
for (unsigned i = 0; i < mustBeZeroSize; ++i) {
163+
if (mustBeZero[i] != 0) {
164+
return false;
165+
}
166+
}
167+
return true;
168+
}
169+
170+
bool operator!=(const BitMask& rhs) const {
171+
return !(*this == rhs);
172+
}
173+
174+
bool isNonZero() const { return !isZero(); }
175+
176+
bool isZero() const {
177+
for (unsigned i = 0; i < size; ++i) {
178+
if (mask[i] != 0) {
179+
return false;
180+
}
181+
}
182+
return true;
183+
}
184+
185+
void makeZero() {
186+
memset(mask, 0, size * sizeof(mask[0]));
187+
}
188+
189+
void complement() {
190+
for (unsigned i = 0; i < size; ++i) {
191+
mask[i] = ~mask[i];
192+
}
193+
}
194+
195+
int countSetBits() const {
196+
static const int counter[] =
197+
{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
198+
int bits = 0;
199+
for (unsigned i = 0; i < size; ++i) {
200+
bits += counter[mask[i] >> 4] + counter[mask[i] & 15];
201+
}
202+
return bits;
203+
}
204+
205+
int countZeroBits() const {
206+
return (size * 8) - countSetBits();
207+
}
208+
209+
// Treat the provided value as a mask, `and` it with
210+
// the part of the mask at the provided byte offset.
211+
// Bits outside the specified area are unchanged.
212+
template<typename IntegerType>
213+
void andMask(IntegerType value, unsigned byteOffset) {
214+
andMask((void *)&value, sizeof(value), byteOffset);
215+
}
216+
217+
// As above, but using the provided bitmask instead
218+
// of an integer.
219+
void andMask(BitMask mask, unsigned offset) {
220+
andMask(mask.mask, mask.size, offset);
221+
}
222+
223+
// As above, but using the complement of the
224+
// provided mask.
225+
void andNotMask(BitMask mask, unsigned offset) {
226+
if (offset < size) {
227+
andNotMask(mask.mask, mask.size, offset);
228+
}
229+
}
230+
231+
// Zero all bits except for the `n` most significant ones.
232+
void keepOnlyMostSignificantBits(unsigned n) {
233+
if (size < 1) {
234+
return;
235+
}
236+
#if defined(__BIG_ENDIAN__)
237+
assert(false && "Big endian not supported for readMaskedInteger");
238+
#else
239+
unsigned count = 0;
240+
unsigned i = size;
241+
while (i > 0) {
242+
i -= 1;
243+
if (count < n) {
244+
for (int b = 128; b > 0; b >>= 1) {
245+
if (count >= n) {
246+
mask[i] &= ~b;
247+
} else if ((mask[i] & b) != 0) {
248+
++count;
249+
}
250+
}
251+
} else {
252+
mask[i] = 0;
253+
}
254+
}
255+
#endif
256+
}
257+
258+
void keepOnlyLeastSignificantBytes(unsigned n) {
259+
if (size > n) {
260+
size = n;
261+
}
262+
}
263+
264+
unsigned numBits() const {
265+
return size * 8;
266+
}
267+
268+
unsigned numSetBits() const {
269+
unsigned count = 0;
270+
for (unsigned i = 0; i < size; ++i) {
271+
if (mask[i] != 0) {
272+
for (unsigned b = 1; b < 256; b <<= 1) {
273+
if ((mask[i] & b) != 0) {
274+
++count;
275+
}
276+
}
277+
}
278+
}
279+
return count;
280+
}
281+
282+
// Read a mask-sized area from the target and collect
283+
// the masked bits into a single integer.
284+
template<typename IntegerType>
285+
bool readMaskedInteger(remote::MemoryReader &reader,
286+
remote::RemoteAddress address,
287+
IntegerType *dest) const {
288+
auto data = reader.readBytes(address, size);
289+
if (!data) {
290+
return false;
291+
}
292+
#if defined(__BIG_ENDIAN__)
293+
assert(false && "Big endian not supported for readMaskedInteger");
294+
#else
295+
IntegerType result = 0;
296+
IntegerType resultBit = 1; // Start from least-significant bit
297+
auto bytes = static_cast<const uint8_t *>(data.get());
298+
for (unsigned i = 0; i < size; ++i) {
299+
for (unsigned b = 1; b < 256; b <<= 1) {
300+
if ((mask[i] & b) != 0) {
301+
if ((bytes[i] & b) != 0) {
302+
result |= resultBit;
303+
}
304+
resultBit <<= 1;
305+
}
306+
}
307+
}
308+
*dest = result;
309+
return true;
310+
#endif
311+
}
312+
313+
private:
314+
void andMask(void *maskData, unsigned len, unsigned offset) {
315+
if (offset < size) {
316+
unsigned common = std::min(len, size - offset);
317+
uint8_t *maskBytes = (uint8_t *)maskData;
318+
for (unsigned i = 0; i < common; ++i) {
319+
mask[i + offset] &= maskBytes[i];
320+
}
321+
}
322+
}
323+
324+
void andNotMask(void *maskData, unsigned len, unsigned offset) {
325+
assert(offset < size);
326+
if (offset < size) {
327+
unsigned common = std::min(len, size - offset);
328+
uint8_t *maskBytes = (uint8_t *)maskData;
329+
for (unsigned i = 0; i < common; ++i) {
330+
mask[i + offset] &= ~maskBytes[i];
331+
}
332+
}
333+
}
334+
};
335+
336+
} // namespace reflection
337+
} // namespace swift
338+
339+
#endif

include/swift/RemoteInspection/TypeLowering.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/Support/Casting.h"
2424
#include "swift/Remote/MetadataReader.h"
2525
#include "swift/Remote/TypeInfoProvider.h"
26+
#include "swift/RemoteInspection/BitMask.h"
2627
#include "swift/RemoteInspection/DescriptorFinder.h"
2728

2829
#include <memory>
@@ -34,6 +35,7 @@ using llvm::cast;
3435
using llvm::dyn_cast;
3536
using remote::RemoteRef;
3637

38+
class TypeConverter;
3739
class TypeRef;
3840
class TypeRefBuilder;
3941
class BuiltinTypeDescriptor;
@@ -158,6 +160,11 @@ class TypeInfo {
158160
return false;
159161
}
160162

163+
// Calculate and return the spare bit mask for this type
164+
virtual BitMask getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const {
165+
return BitMask::zeroMask(getSize());
166+
}
167+
161168
virtual ~TypeInfo() { }
162169
};
163170

@@ -195,6 +202,8 @@ class BuiltinTypeInfo : public TypeInfo {
195202
remote::RemoteAddress address,
196203
int *extraInhabitantIndex) const override;
197204

205+
BitMask getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const override;
206+
198207
static bool classof(const TypeInfo *TI) {
199208
return TI->getKind() == TypeInfoKind::Builtin;
200209
}
@@ -222,6 +231,8 @@ class RecordTypeInfo : public TypeInfo {
222231
remote::RemoteAddress address,
223232
int *index) const override;
224233

234+
BitMask getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const override;
235+
225236
static bool classof(const TypeInfo *TI) {
226237
return TI->getKind() == TypeInfoKind::Record;
227238
}
@@ -330,6 +341,8 @@ class ReferenceTypeInfo : public TypeInfo {
330341
return reader.readHeapObjectExtraInhabitantIndex(address, extraInhabitantIndex);
331342
}
332343

344+
BitMask getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const override;
345+
333346
static bool classof(const TypeInfo *TI) {
334347
return TI->getKind() == TypeInfoKind::Reference;
335348
}

0 commit comments

Comments
 (0)