Skip to content

Commit a5c948a

Browse files
committed
Merge pull request swiftlang#73491 from tbkka/tbkka-mpe-sparebits
Calculate spare bits for multi-payload enums from first principles This adds a getSpareBits method to all the TypeInfo classes that returns a suitable bitmask indicating the spare bits available in values of this type. This gives us a way to recursively explore the type tree and build up a full spare bit mask for an arbitrary type. We mostly have enough information to do this calculation entirely from first principles, without requiring additional reflection information. So once this is stable, we should remove my earlier incomplete effort to publish spare bit mask info in the reflection data, as that adds unnecessary metadata to every binary. TODO: Resilience forces some enums to not use spare bits even though they otherwise would be able to. We should have the compiler add a single bit to the reflectio data indicating whether or not spare bits were used and then rely on this code to actually compute the spare bits. This doubtless still has plenty of holes, but seems sufficient to handle a few basic enum types, including the stdlib DecodingError which was used as an example to work out some key issues. Resolves rdar://126563813
1 parent a17d360 commit a5c948a

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)