Skip to content

Commit 32ca567

Browse files
authored
Merge pull request #75356 from Azoy/movesaslikes-but-for-vectors
[IRGen] Support movesAsLike for the array variant of @_rawLayout
2 parents ea1baed + dd37c1d commit 32ca567

File tree

7 files changed

+259
-72
lines changed

7 files changed

+259
-72
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,15 +1033,17 @@ forms are currently accepted:
10331033

10341034
- `@_rawLayout(size: N, alignment: M)` specifies the type's size and alignment
10351035
in bytes.
1036-
- `@_rawLayout(like: T)` specifies the type's size and alignment should be
1037-
equal to the type `T`'s.
1038-
- `@_rawLayout(likeArrayOf: T, count: N)` specifies the type's size should be
1039-
`MemoryLayout<T>.stride * N` and alignment should match `T`'s, like an
1040-
array of N contiguous elements of `T` in memory.
1041-
- `@_rawLayout(like: T, movesAsLike)` specifies the type's size and alignment
1042-
should be equal to the type `T`'s. It also guarantees that moving a value of
1043-
this raw layout type will have the same move semantics as the type it's like.
1044-
This is important for things like ObjC weak references and non-trivial move
1036+
- `@_rawLayout(like: T(, movesAsLike))` specifies the type's size and alignment
1037+
should be equal to the type `T`'s. An optional `movesAsLike` parameter can be
1038+
passed to guarantee that moving a value of this raw layout type will have the
1039+
same move semantics as the type it's like. This is important for things like
1040+
ObjC weak references and non-trivial move constructors in C++.
1041+
- `@_rawLayout(likeArrayOf: T, count: N(, movesAsLike))` specifies the type's
1042+
size should be `MemoryLayout<T>.stride * N` and alignment should match `T`'s,
1043+
like an array of N contiguous elements of `T` in memory. An optional
1044+
`movesAsLike` parameter can be passed to guarantee that moving a value of this
1045+
raw layout type will have the same move semantics as the type it's like. This
1046+
is important for things like ObjC weak references and non-trivial move
10451047
constructors in C++.
10461048

10471049
A notable difference between `@_rawLayout(like: T)` and

include/swift/AST/Attr.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,11 +2553,12 @@ class RawLayoutAttr final : public DeclAttribute {
25532553
MovesAsLike(movesAsLike) {}
25542554

25552555
/// Construct a `@_rawLayout(likeArrayOf: T, count: N)` attribute.
2556-
RawLayoutAttr(TypeRepr *LikeType, unsigned Count, SourceLoc AtLoc,
2557-
SourceRange Range)
2556+
RawLayoutAttr(TypeRepr *LikeType, unsigned Count, bool movesAsLike,
2557+
SourceLoc AtLoc, SourceRange Range)
25582558
: DeclAttribute(DeclAttrKind::RawLayout, AtLoc, Range,
25592559
/*implicit*/ false),
2560-
LikeType(LikeType), SizeOrCount(Count), Alignment(0) {}
2560+
LikeType(LikeType), SizeOrCount(Count), Alignment(0),
2561+
MovesAsLike(movesAsLike) {}
25612562

25622563
/// Construct a `@_rawLayout(size: N, alignment: M)` attribute.
25632564
RawLayoutAttr(unsigned Size, unsigned Alignment, SourceLoc AtLoc,

lib/IRGen/GenRecord.h

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -203,21 +203,9 @@ class RecordTypeInfoImpl : public Base,
203203
}
204204

205205
if (auto rawLayout = T.getRawLayout()) {
206-
// Because we have a rawlayout attribute, we know this has to be a struct.
207-
auto structDecl = T.getStructOrBoundGenericStruct();
208-
209-
if (auto likeType = rawLayout->getResolvedScalarLikeType(structDecl)) {
210-
if (rawLayout->shouldMoveAsLikeType()) {
211-
auto astT = T.getASTType();
212-
auto subs = astT->getContextSubstitutionMap();
213-
auto loweredLikeType = IGF.IGM.getLoweredType(likeType->subst(subs));
214-
auto &likeTypeInfo = IGF.IGM.getTypeInfo(loweredLikeType);
215-
216-
likeTypeInfo.assignWithTake(IGF, dest, src, loweredLikeType,
217-
isOutlined);
218-
return;
219-
}
220-
}
206+
return takeRawLayout(IGF, dest, src, T, isOutlined,
207+
/* zeroizeIfSensitive */ false, rawLayout,
208+
/* isInit */ false);
221209
}
222210

223211
if (isOutlined || T.hasParameterizedExistential()) {
@@ -280,20 +268,8 @@ class RecordTypeInfoImpl : public Base,
280268
// If the fields are not ABI-accessible, use the value witness table.
281269
return emitInitializeWithTakeCall(IGF, T, dest, src);
282270
} else if (auto rawLayout = T.getRawLayout()) {
283-
// Because we have a rawlayout attribute, we know this has to be a struct.
284-
auto structDecl = T.getStructOrBoundGenericStruct();
285-
286-
if (auto likeType = rawLayout->getResolvedScalarLikeType(structDecl)) {
287-
if (rawLayout->shouldMoveAsLikeType()) {
288-
auto astT = T.getASTType();
289-
auto subs = astT->getContextSubstitutionMap();
290-
auto loweredLikeType = IGF.IGM.getLoweredType(likeType->subst(subs));
291-
auto &likeTypeInfo = IGF.IGM.getTypeInfo(loweredLikeType);
292-
293-
likeTypeInfo.initializeWithTake(IGF, dest, src, loweredLikeType,
294-
isOutlined, zeroizeIfSensitive);
295-
}
296-
}
271+
return takeRawLayout(IGF, dest, src, T, isOutlined, zeroizeIfSensitive,
272+
rawLayout, /* isInit */ true);
297273
} else if (isOutlined || T.hasParameterizedExistential()) {
298274
auto offsets = asImpl().getNonFixedOffsets(IGF, T);
299275
for (auto &field : getFields()) {
@@ -313,6 +289,90 @@ class RecordTypeInfoImpl : public Base,
313289
fillWithZerosIfSensitive(IGF, src, T);
314290
}
315291

292+
void takeRawLayout(IRGenFunction &IGF, Address dest, Address src, SILType T,
293+
bool isOutlined, bool zeroizeIfSensitive,
294+
RawLayoutAttr *rawLayout, bool isInit) const {
295+
if (rawLayout->shouldMoveAsLikeType()) {
296+
// Because we have a rawlayout attribute, we know this has to be a struct.
297+
auto structDecl = T.getStructOrBoundGenericStruct();
298+
299+
if (auto likeType = rawLayout->getResolvedScalarLikeType(structDecl)) {
300+
auto astT = T.getASTType();
301+
auto subs = astT->getContextSubstitutionMap();
302+
auto loweredLikeType = IGF.IGM.getLoweredType(likeType->subst(subs));
303+
auto &likeTypeInfo = IGF.IGM.getTypeInfo(loweredLikeType);
304+
305+
if (isInit) {
306+
likeTypeInfo.initializeWithTake(IGF, dest, src, loweredLikeType,
307+
isOutlined, zeroizeIfSensitive);
308+
} else {
309+
likeTypeInfo.assignWithTake(IGF, dest, src, loweredLikeType,
310+
isOutlined);
311+
}
312+
}
313+
314+
if (auto likeArray = rawLayout->getResolvedArrayLikeTypeAndCount(structDecl)) {
315+
auto likeType = likeArray->first;
316+
unsigned count = likeArray->second;
317+
318+
auto astT = T.getASTType();
319+
auto subs = astT->getContextSubstitutionMap();
320+
auto loweredLikeType = IGF.IGM.getLoweredType(likeType.subst(subs));
321+
auto &likeTypeInfo = IGF.IGM.getTypeInfo(loweredLikeType);
322+
323+
for (unsigned i = 0; i != count; i += 1) {
324+
auto index = llvm::ConstantInt::get(IGF.IGM.SizeTy, i);
325+
326+
Address srcEltAddr;
327+
Address destEltAddr;
328+
329+
// If we have a fixed type, we can use a typed GEP to index into the
330+
// array raw layout. Otherwise, we need to advance by bytes given the
331+
// stride from the VWT of the like type.
332+
if (auto fixedLikeType = dyn_cast<FixedTypeInfo>(&likeTypeInfo)) {
333+
srcEltAddr = Address(IGF.Builder.CreateInBoundsGEP(
334+
fixedLikeType->getStorageType(),
335+
src.getAddress(),
336+
index),
337+
fixedLikeType->getStorageType(),
338+
src.getAlignment());
339+
destEltAddr = Address(IGF.Builder.CreateInBoundsGEP(
340+
fixedLikeType->getStorageType(),
341+
dest.getAddress(),
342+
index),
343+
fixedLikeType->getStorageType(),
344+
dest.getAlignment());
345+
} else {
346+
auto eltSize = likeTypeInfo.getStride(IGF, loweredLikeType);
347+
auto offset = IGF.Builder.CreateMul(index, eltSize);
348+
349+
srcEltAddr = Address(IGF.Builder.CreateInBoundsGEP(
350+
IGF.IGM.Int8Ty,
351+
src.getAddress(),
352+
offset),
353+
IGF.IGM.Int8Ty,
354+
src.getAlignment());
355+
destEltAddr = Address(IGF.Builder.CreateInBoundsGEP(
356+
IGF.IGM.Int8Ty,
357+
dest.getAddress(),
358+
offset),
359+
IGF.IGM.Int8Ty,
360+
dest.getAlignment());
361+
}
362+
363+
if (isInit) {
364+
likeTypeInfo.initializeWithTake(IGF, destEltAddr, srcEltAddr,
365+
loweredLikeType, isOutlined,
366+
zeroizeIfSensitive);
367+
} else {
368+
likeTypeInfo.assignWithTake(IGF, destEltAddr, srcEltAddr,
369+
loweredLikeType, isOutlined);
370+
}
371+
}
372+
}
373+
}
374+
}
375+
316376
void destroy(IRGenFunction &IGF, Address addr, SILType T,
317377
bool isOutlined) const override {
318378
// If the fields are not ABI-accessible, use the value witness table.

lib/IRGen/StructLayout.cpp

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,19 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
111111
SpareBits.extendWithClearBits(MinimumSize.getValueInBits());
112112
IsFixedLayout = true;
113113
IsKnownAlwaysFixedSize = IsFixedSize;
114-
} else if (auto likeType = rawLayout->getResolvedScalarLikeType(sd)) {
114+
} else {
115+
std::optional<Type> likeType = std::nullopt;
116+
unsigned count = 0;
117+
118+
if (auto like = rawLayout->getResolvedScalarLikeType(sd)) {
119+
likeType = like;
120+
}
121+
122+
if (auto like = rawLayout->getResolvedArrayLikeTypeAndCount(sd)) {
123+
likeType = like->first;
124+
count = like->second;
125+
}
126+
115127
// If our likeType is dependent, then all calls to try and lay it out will
116128
// be non-fixed, but in a concrete case we want a fixed layout, so try to
117129
// substitute it out.
@@ -121,15 +133,21 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
121133

122134
// Take layout attributes from the like type.
123135
if (const FixedTypeInfo *likeFixedType = dyn_cast<FixedTypeInfo>(&likeTypeInfo)) {
124-
MinimumSize = likeFixedType->getFixedSize();
136+
// If we have no count, treat this as a scalar.
137+
if (count == 0) {
138+
MinimumSize = likeFixedType->getFixedSize();
139+
} else {
140+
MinimumSize = likeFixedType->getFixedStride() * count;
141+
}
142+
125143
SpareBits.extendWithClearBits(MinimumSize.getValueInBits());
126144
MinimumAlign = likeFixedType->getFixedAlignment();
127145
IsFixedLayout = true;
128146
IsKnownAlwaysFixedSize = IsFixedSize;
129147

130-
// @_rawLayout(like: T) has an optional `movesAsLike` which enforces that
131-
// a value of this raw layout type should have the same move semantics
132-
// as the like its like.
148+
// @_rawLayout has an optional `movesAsLike` which enforces that a value
149+
// of this raw layout type should have the same move semantics as the
150+
// type its like.
133151
if (rawLayout->shouldMoveAsLikeType()) {
134152
IsKnownTriviallyDestroyable = likeFixedType->isTriviallyDestroyable(ResilienceExpansion::Maximal);
135153
IsKnownBitwiseTakable = likeFixedType->isBitwiseTakable(ResilienceExpansion::Maximal);
@@ -147,29 +165,6 @@ StructLayout::StructLayout(IRGenModule &IGM, std::optional<CanType> type,
147165
IsKnownBitwiseTakable = IsNotBitwiseTakable;
148166
}
149167
}
150-
} else if (auto likeArray = rawLayout->getResolvedArrayLikeTypeAndCount(sd)) {
151-
auto elementType = likeArray->first;
152-
unsigned count = likeArray->second;
153-
154-
auto subs = (*type)->getContextSubstitutionMap();
155-
auto loweredElementType = IGM.getLoweredType(elementType.subst(subs));
156-
const TypeInfo &likeTypeInfo = IGM.getTypeInfo(loweredElementType);
157-
158-
// Take layout attributes from the like type.
159-
if (const FixedTypeInfo *likeFixedType = dyn_cast<FixedTypeInfo>(&likeTypeInfo)) {
160-
MinimumSize = likeFixedType->getFixedStride() * count;
161-
SpareBits.extendWithClearBits(MinimumSize.getValueInBits());
162-
MinimumAlign = likeFixedType->getFixedAlignment();
163-
IsFixedLayout = true;
164-
IsKnownAlwaysFixedSize = IsFixedSize;
165-
} else {
166-
MinimumSize = Size(0);
167-
MinimumAlign = Alignment(1);
168-
IsFixedLayout = false;
169-
IsKnownAlwaysFixedSize = IsNotFixedSize;
170-
}
171-
} else {
172-
llvm_unreachable("unhandled raw layout variant?");
173168
}
174169

175170
// Set the LLVM struct type for a fixed layout according to the stride and

lib/Parse/ParseDecl.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4039,14 +4039,23 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
40394039
return makeParserSuccess();
40404040
}
40414041

4042+
bool movesAsLike = false;
4043+
4044+
// @_rawLayout(likeArrayOf: T, count: N, movesAsLike)
4045+
if (consumeIf(tok::comma) && Tok.isAny(tok::identifier) &&
4046+
!parseSpecificIdentifier("movesAsLike",
4047+
diag::attr_rawlayout_expected_label, "movesAsLike")) {
4048+
movesAsLike = true;
4049+
}
4050+
40424051
SourceLoc rParenLoc;
40434052
if (!consumeIf(tok::r_paren, rParenLoc)) {
40444053
diagnose(Tok.getLoc(), diag::attr_expected_rparen,
40454054
AttrName, /*isModifier*/false);
40464055
return makeParserSuccess();
40474056
}
40484057

4049-
attr = new (Context) RawLayoutAttr(likeType.get(), count,
4058+
attr = new (Context) RawLayoutAttr(likeType.get(), count, movesAsLike,
40504059
AtLoc, SourceRange(Loc, rParenLoc));
40514060
} else {
40524061
diagnose(Loc, diag::attr_rawlayout_expected_label,

lib/Serialization/Deserialization.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6354,7 +6354,7 @@ llvm::Error DeclDeserializer::deserializeDeclCommon() {
63546354
SourceRange());
63556355
break;
63566356
} else {
6357-
Attr = new (ctx) RawLayoutAttr(typeRepr, rawSize,
6357+
Attr = new (ctx) RawLayoutAttr(typeRepr, rawSize, movesAsLike,
63586358
SourceLoc(),
63596359
SourceRange());
63606360
break;

0 commit comments

Comments
 (0)