Skip to content

Commit 436a8b2

Browse files
committed
Add runtime functions to compute tuple layouts from element layouts.
Previously, when a tuple type had non-fixed layout, we would compute a layout by building the metadata for that tuple type and then extracting the layout from the VWT. This can be quite expensive because it involves constructing the exact metadata for types like arrays and functions despite those types being fixed-layout across all instantiations. It also tends to cause unnecessary recursive-type issues, especially with enums where tuples are currently used to model cases with mutliple payloads. Since we just need a layout, computing it directly from element layouts instead of constructing metadata for the formal type lets us take advantage of all the other fast paths for layout construction, e.g. for fixed types and single-field aggregates. This is a good improvement overall, but it also serves to alleviate some of the problems of rdar://40810002 / SR-7876 in a way that might be suitable for integration to 4.2.
1 parent b124154 commit 436a8b2

File tree

10 files changed

+300
-17
lines changed

10 files changed

+300
-17
lines changed

include/swift/Runtime/Metadata.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,46 @@ swift_getTupleTypeMetadata3(MetadataRequest request,
525525
const Metadata *elt2, const char *labels,
526526
const ValueWitnessTable *proposedWitnesses);
527527

528+
/// Perform layout as if for a tuple whose elements have the given layouts.
529+
///
530+
/// \param tupleLayout - A structure into which to write the tuple layout.
531+
/// Must be non-null.
532+
/// \param elementOffsets - An array into which to write the offsets of
533+
/// the elements. May be null. Must have space for all elements,
534+
/// including element 0 (which will always have offset 0).
535+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
536+
void swift_getTupleTypeLayout(TypeLayout *tupleLayout,
537+
uint32_t *elementOffsets,
538+
TupleTypeFlags flags,
539+
const TypeLayout * const *elements);
540+
541+
/// Perform layout as if for a two-element tuple whose elements have
542+
/// the given layouts.
543+
///
544+
/// \param tupleLayout - A structure into which to write the tuple layout.
545+
/// Must be non-null.
546+
/// \returns The offset of the second element.
547+
/// The first element always has offset 0.
548+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
549+
size_t swift_getTupleTypeLayout2(TypeLayout *tupleLayout,
550+
const TypeLayout *elt0,
551+
const TypeLayout *elt1);
552+
553+
struct OffsetPair { size_t First; size_t Second; };
554+
555+
/// Perform layout as if for a three-element tuple whose elements have
556+
/// the given layouts.
557+
///
558+
/// \param tupleLayout - A structure into which to write the tuple layout.
559+
/// Must be non-null.
560+
/// \returns The offsets of the second and third elements.
561+
/// The first element always has offset 0.
562+
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
563+
OffsetPair swift_getTupleTypeLayout3(TypeLayout *tupleLayout,
564+
const TypeLayout *elt0Layout,
565+
const TypeLayout *elt1Layout,
566+
const TypeLayout *elt2Layout);
567+
528568
/// Initialize the value witness table and struct field offset vector for a
529569
/// struct, using the "Universal" layout strategy.
530570
SWIFT_RUNTIME_EXPORT

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,34 @@ FUNCTION(GetTupleMetadata3, swift_getTupleTypeMetadata3, SwiftCC,
766766
Int8PtrTy, WitnessTablePtrTy),
767767
ATTRS(NoUnwind, ReadOnly))
768768

769+
// void swift_getTupleTypeLayout(TypeLayout *result,
770+
// uint32_t offsets,
771+
// TupleTypeFlags flags,
772+
// const TypeLayout * const *elts);
773+
FUNCTION(GetTupleLayout, swift_getTupleTypeLayout, SwiftCC,
774+
RETURNS(VoidTy),
775+
ARGS(FullTypeLayoutTy->getPointerTo(0), Int32Ty->getPointerTo(0),
776+
SizeTy, Int8PtrPtrTy->getPointerTo(0)),
777+
ATTRS(NoUnwind))
778+
779+
// size_t swift_getTupleTypeLayout2(TypeLayout *layout,
780+
// const TypeLayout *elt0,
781+
// const TypeLayout *elt1);
782+
FUNCTION(GetTupleLayout2, swift_getTupleTypeLayout2, SwiftCC,
783+
RETURNS(SizeTy),
784+
ARGS(FullTypeLayoutTy->getPointerTo(0), Int8PtrPtrTy, Int8PtrPtrTy),
785+
ATTRS(NoUnwind))
786+
787+
// OffsetPair swift_getTupleTypeLayout3(TypeLayout *layout,
788+
// const TypeLayout *elt0,
789+
// const TypeLayout *elt1,
790+
// const TypeLayout *elt2);
791+
FUNCTION(GetTupleLayout3, swift_getTupleTypeLayout3, SwiftCC,
792+
RETURNS(OffsetPairTy),
793+
ARGS(FullTypeLayoutTy->getPointerTo(0),
794+
Int8PtrPtrTy, Int8PtrPtrTy, Int8PtrPtrTy),
795+
ATTRS(NoUnwind))
796+
769797
// Metadata *swift_getExistentialTypeMetadata(
770798
// ProtocolClassConstraint classConstraint,
771799
// const Metadata *superclassConstraint,

lib/IRGen/IRGenModule.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
199199
SizeTy
200200
});
201201

202+
OffsetPairTy = llvm::StructType::get(getLLVMContext(), { SizeTy, SizeTy });
203+
204+
// The TypeLayout structure, including all possible trailing components.
205+
FullTypeLayoutTy = createStructType(*this, "swift.full_type_layout", {
206+
SizeTy, // size
207+
SizeTy, // flags
208+
SizeTy, // alignment
209+
SizeTy // extra inhabitant flags (optional)
210+
});
211+
202212
// A protocol descriptor describes a protocol. It is not type metadata in
203213
// and of itself, but is referenced in the structure of existential type
204214
// metadata records.

lib/IRGen/IRGenModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ class IRGenModule {
531531
llvm::StructType *TypeMetadataResponseTy; /// { %swift.type*, iSize }
532532
llvm::StructType *TypeMetadataDependencyTy; /// { %swift.type*, iSize }
533533
};
534+
llvm::StructType *OffsetPairTy; /// { iSize, iSize }
535+
llvm::StructType *FullTypeLayoutTy; /// %swift.full_type_layout = { ... }
534536
llvm::PointerType *TupleTypeMetadataPtrTy; /// %swift.tuple_type*
535537
llvm::StructType *FullHeapMetadataStructTy; /// %swift.full_heapmetadata = type { ... }
536538
llvm::PointerType *FullHeapMetadataPtrTy;/// %swift.full_heapmetadata*

lib/IRGen/MetadataRequest.cpp

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,23 @@ namespace {
21272127
return emitFromValueWitnessTablePointer(vwtable);
21282128
}
21292129

2130+
/// Given that the type is fixed-layout, emit the type layout by
2131+
/// emitting a global layout for it.
2132+
llvm::Value *emitFromFixedLayout(CanType t) {
2133+
auto layout = tryEmitFromFixedLayout(t);
2134+
assert(layout && "type must be fixed-size to call emitFromFixedLayout");
2135+
return layout;
2136+
}
2137+
2138+
/// If the type is fixed-layout, emit the type layout by
2139+
/// emitting a global layout for it.
2140+
llvm::Value *tryEmitFromFixedLayout(CanType t) {
2141+
auto &ti = IGF.getTypeInfo(SILType::getPrimitiveObjectType(t));
2142+
if (auto fixedTI = dyn_cast<FixedTypeInfo>(&ti))
2143+
return IGF.IGM.emitFixedTypeLayout(t, *fixedTI);
2144+
return nullptr;
2145+
}
2146+
21302147
bool hasVisibleValueWitnessTable(CanType t) const {
21312148
// Some builtin and structural types have value witnesses exported from
21322149
// the runtime.
@@ -2234,7 +2251,7 @@ namespace {
22342251
}
22352252
case MetatypeRepresentation::Thick:
22362253
if (isa<ExistentialMetatypeType>(type)) {
2237-
return emitFromTypeMetadata(type, request);
2254+
return emitFromFixedLayout(type);
22382255
}
22392256
// Otherwise, this is a metatype that looks like a pointer.
22402257
LLVM_FALLTHROUGH;
@@ -2277,6 +2294,103 @@ namespace {
22772294
DynamicMetadataRequest request) {
22782295
return visitAnyClassType(type->getClassOrBoundGenericClass(), request);
22792296
}
2297+
2298+
llvm::Value *visitTupleType(CanTupleType type,
2299+
DynamicMetadataRequest request) {
2300+
// Single-element tuples have exactly the same layout as their elements.
2301+
if (type->getNumElements() == 1) {
2302+
return visit(type.getElementType(0), request);
2303+
}
2304+
2305+
// If the type is fixed-layout, use a global layout.
2306+
if (auto layout = tryEmitFromFixedLayout(type))
2307+
return layout;
2308+
2309+
// TODO: check for cached VWT / metadata for the type.
2310+
2311+
// Use swift_getTupleTypeLayout to compute a layout.
2312+
2313+
// Create a buffer to hold the result. We don't have any reasonable
2314+
// way to scope the lifetime of this.
2315+
auto resultPtr = IGF.createAlloca(IGF.IGM.FullTypeLayoutTy,
2316+
IGF.IGM.getPointerAlignment())
2317+
.getAddress();
2318+
2319+
switch (type->getNumElements()) {
2320+
case 0:
2321+
case 1:
2322+
llvm_unreachable("filtered out above");
2323+
2324+
case 2: {
2325+
auto elt0 = visit(type.getElementType(0), request);
2326+
auto elt1 = visit(type.getElementType(1), request);
2327+
2328+
// Ignore the offset.
2329+
auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayout2Fn(),
2330+
{resultPtr, elt0, elt1});
2331+
call->setDoesNotThrow();
2332+
2333+
break;
2334+
}
2335+
2336+
case 3: {
2337+
auto elt0 = visit(type.getElementType(0), request);
2338+
auto elt1 = visit(type.getElementType(1), request);
2339+
auto elt2 = visit(type.getElementType(2), request);
2340+
2341+
// Ignore the offsets.
2342+
auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayout3Fn(),
2343+
{resultPtr, elt0, elt1, elt2});
2344+
call->setDoesNotThrow();
2345+
2346+
break;
2347+
}
2348+
2349+
default: {
2350+
// Allocate a temporary array for the element layouts.
2351+
auto eltLayoutsArraySize =
2352+
IGF.IGM.getPointerSize() * type->getNumElements();
2353+
auto eltLayoutsArray =
2354+
IGF.createAlloca(IGF.IGM.Int8PtrPtrTy,
2355+
IGF.IGM.getSize(Size(type->getNumElements())),
2356+
IGF.IGM.getPointerAlignment());
2357+
IGF.Builder.CreateLifetimeStart(eltLayoutsArray, eltLayoutsArraySize);
2358+
2359+
// Emit layouts for all the elements and store them into the array.
2360+
for (auto i : indices(type.getElementTypes())) {
2361+
auto eltLayout = visit(type.getElementType(i), request);
2362+
auto eltLayoutSlot =
2363+
i == 0 ? eltLayoutsArray
2364+
: IGF.Builder.CreateConstArrayGEP(eltLayoutsArray, i,
2365+
IGF.IGM.getPointerSize());
2366+
IGF.Builder.CreateStore(eltLayout, eltLayoutSlot);
2367+
}
2368+
2369+
// Ignore the offsets.
2370+
auto offsetsPtr =
2371+
llvm::ConstantPointerNull::get(IGF.IGM.Int32Ty->getPointerTo());
2372+
2373+
// Flags.
2374+
auto flags = TupleTypeFlags().withNumElements(type->getNumElements());
2375+
auto flagsValue = IGF.IGM.getSize(Size(flags.getIntValue()));
2376+
2377+
// Compute the layout.
2378+
auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayoutFn(),
2379+
{resultPtr, offsetsPtr, flagsValue,
2380+
eltLayoutsArray.getAddress()});
2381+
call->setDoesNotThrow();
2382+
2383+
// We're done with the buffer.
2384+
IGF.Builder.CreateLifetimeEnd(eltLayoutsArray, eltLayoutsArraySize);
2385+
2386+
break;
2387+
}
2388+
}
2389+
2390+
// Cast resultPtr to i8**, our general currency type for type layouts.
2391+
resultPtr = IGF.Builder.CreateBitCast(resultPtr, IGF.IGM.Int8PtrPtrTy);
2392+
return resultPtr;
2393+
}
22802394
};
22812395

22822396
} // end anonymous namespace

stdlib/public/runtime/Metadata.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,43 @@ static void performBasicLayout(TypeLayout &layout,
13781378
layout.stride = std::max(size_t(1), roundUpToAlignMask(size, alignMask));
13791379
}
13801380

1381+
1382+
size_t swift::swift_getTupleTypeLayout2(TypeLayout *result,
1383+
const TypeLayout *elt0,
1384+
const TypeLayout *elt1) {
1385+
const TypeLayout *elts[] = { elt0, elt1 };
1386+
uint32_t offsets[2];
1387+
swift_getTupleTypeLayout(result, offsets,
1388+
TupleTypeFlags().withNumElements(2), elts);
1389+
assert(offsets[0] == 0);
1390+
return offsets[1];
1391+
}
1392+
1393+
OffsetPair swift::swift_getTupleTypeLayout3(TypeLayout *result,
1394+
const TypeLayout *elt0,
1395+
const TypeLayout *elt1,
1396+
const TypeLayout *elt2) {
1397+
const TypeLayout *elts[] = { elt0, elt1, elt2 };
1398+
uint32_t offsets[3];
1399+
swift_getTupleTypeLayout(result, offsets,
1400+
TupleTypeFlags().withNumElements(3), elts);
1401+
assert(offsets[0] == 0);
1402+
return {offsets[1], offsets[2]};
1403+
}
1404+
1405+
void swift::swift_getTupleTypeLayout(TypeLayout *result,
1406+
uint32_t *elementOffsets,
1407+
TupleTypeFlags flags,
1408+
const TypeLayout * const *elements) {
1409+
*result = TypeLayout();
1410+
performBasicLayout(*result, elements, flags.getNumElements(),
1411+
[](const TypeLayout *elt) { return elt; },
1412+
[elementOffsets](size_t i, const TypeLayout *elt, size_t offset) {
1413+
if (elementOffsets)
1414+
elementOffsets[i] = uint32_t(offset);
1415+
});
1416+
}
1417+
13811418
MetadataResponse
13821419
swift::swift_getTupleTypeMetadata(MetadataRequest request,
13831420
TupleTypeFlags flags,

test/IRGen/enum_resilience.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -331,21 +331,27 @@ public func constructFullyFixed() -> FullyFixedLayout {
331331
}
332332

333333
// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$S15enum_resilience24EnumWithResilientPayloadOMr"(%swift.type*, i8*, i8**)
334+
// CHECK: [[TUPLE_LAYOUT:%.*]] = alloca %swift.full_type_layout
334335
// CHECK: [[SIZE_RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$S16resilient_struct4SizeVMa"([[INT]] 319)
335336
// CHECK-NEXT: [[SIZE_METADATA:%.*]] = extractvalue %swift.metadata_response [[SIZE_RESPONSE]], 0
336337
// CHECK-NEXT: [[SIZE_STATE:%.*]] = extractvalue %swift.metadata_response [[SIZE_RESPONSE]], 1
337338
// CHECK-NEXT: [[T0:%.*]] = icmp ule [[INT]] [[SIZE_STATE]], 63
338339
// CHECK-NEXT: br i1 [[T0]], label %[[SATISFIED1:.*]], label
339340
// CHECK: [[SATISFIED1]]:
340-
// CHECK: [[TUPLE_RESPONSE:%.*]] = call swiftcc %swift.metadata_response @swift_getTupleTypeMetadata2([[INT]] 319, %swift.type* [[SIZE_METADATA]], %swift.type* [[SIZE_METADATA]], i8* null, i8** null)
341-
// CHECK-NEXT: [[TUPLE_METADATA:%.*]] = extractvalue %swift.metadata_response [[TUPLE_RESPONSE]], 0
342-
// CHECK-NEXT: [[TUPLE_STATE:%.*]] = extractvalue %swift.metadata_response [[TUPLE_RESPONSE]], 1
343-
// CHECK-NEXT: [[T0:%.*]] = icmp ule [[INT]] [[TUPLE_STATE]], 63
344-
// CHECK-NEXT: br i1 [[T0]], label %[[SATISFIED2:.*]], label
345-
// CHECK: [[SATISFIED2]]:
341+
// CHECK-NEXT: [[T0:%.*]] = bitcast %swift.type* [[SIZE_METADATA]] to i8***
342+
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8**, i8*** [[T0]], [[INT]] -1
343+
// CHECK-NEXT: [[SIZE_VWT:%.*]] = load i8**, i8*** [[T1]],
344+
// CHECK-NEXT: [[SIZE_LAYOUT_1:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8
345+
// CHECK-NEXT: store i8** [[SIZE_LAYOUT_1]],
346+
// CHECK-NEXT: getelementptr
347+
// CHECK-NEXT: [[SIZE_LAYOUT_2:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8
348+
// CHECK-NEXT: [[SIZE_LAYOUT_3:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8
349+
// CHECK-NEXT: call swiftcc [[INT]] @swift_getTupleTypeLayout2(%swift.full_type_layout* [[TUPLE_LAYOUT]], i8** [[SIZE_LAYOUT_2]], i8** [[SIZE_LAYOUT_3]])
350+
// CHECK-NEXT: [[T0:%.*]] = bitcast %swift.full_type_layout* [[TUPLE_LAYOUT]] to i8**
351+
// CHECK-NEXT: store i8** [[T0]],
346352
// CHECK: call void @swift_initEnumMetadataMultiPayload
347-
// CHECK: phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ [[TUPLE_METADATA]], %[[SATISFIED1]] ], [ null, %[[SATISFIED2]] ]
348-
// CHECK: phi [[INT]] [ 63, %entry ], [ 63, %[[SATISFIED1]] ], [ 0, %[[SATISFIED2]] ]
353+
// CHECK: phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ null, %[[SATISFIED1]] ]
354+
// CHECK: phi [[INT]] [ 63, %entry ], [ 0, %[[SATISFIED1]] ]
349355

350356

351357
public protocol Prot {

test/IRGen/struct_resilience.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,20 +198,29 @@ public func resilientAny(s : ResilientWeakRef) {
198198

199199
// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$S17struct_resilience26StructWithResilientStorageVMr"(%swift.type*, i8*, i8**)
200200
// CHECK: [[FIELDS:%.*]] = alloca [4 x i8**]
201+
// CHECK: [[TUPLE_LAYOUT:%.*]] = alloca %swift.full_type_layout,
201202

202203
// CHECK: [[FIELDS_ADDR:%.*]] = getelementptr inbounds [4 x i8**], [4 x i8**]* [[FIELDS]], i32 0, i32 0
203204

204205
// public let s: Size
205206

206-
// CHECK: call swiftcc %swift.metadata_response @"$S16resilient_struct4SizeVMa"([[INT]] 319)
207+
// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S16resilient_struct4SizeVMa"([[INT]] 319)
208+
// CHECK: [[SIZE_METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0
209+
// CHECK: [[T0:%.*]] = bitcast %swift.type* [[SIZE_METADATA]] to i8***
210+
// CHECK: [[T1:%.*]] = getelementptr inbounds i8**, i8*** [[T0]], [[INT]] -1
211+
// CHECK: [[SIZE_VWT:%.*]] = load i8**, i8*** [[T1]],
212+
// CHECK: [[SIZE_LAYOUT_1:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8
207213
// CHECK: [[FIELD_1:%.*]] = getelementptr inbounds i8**, i8*** [[FIELDS_ADDR]], i32 0
208-
// CHECK: store i8** [[SIZE_AND_ALIGNMENT:%.*]], i8*** [[FIELD_1]]
214+
// CHECK: store i8** [[SIZE_LAYOUT_1:%.*]], i8*** [[FIELD_1]]
209215

210216
// public let ss: (Size, Size)
211217

212-
// CHECK: call swiftcc %swift.metadata_response @swift_getTupleTypeMetadata2([[INT]] 319,
218+
// CHECK: [[SIZE_LAYOUT_2:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8
219+
// CHECK: [[SIZE_LAYOUT_3:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8
220+
// CHECK: call swiftcc [[INT]] @swift_getTupleTypeLayout2(%swift.full_type_layout* [[TUPLE_LAYOUT]], i8** [[SIZE_LAYOUT_2]], i8** [[SIZE_LAYOUT_3]])
221+
// CHECK: [[T0:%.*]] = bitcast %swift.full_type_layout* [[TUPLE_LAYOUT]] to i8**
213222
// CHECK: [[FIELD_2:%.*]] = getelementptr inbounds i8**, i8*** [[FIELDS_ADDR]], i32 1
214-
// CHECK: store i8** [[SIZE_AND_ALIGNMENT:%.*]], i8*** [[FIELD_2]]
223+
// CHECK: store i8** [[T0]], i8*** [[FIELD_2]]
215224

216225
// Fixed-layout aggregate -- we can reference a static value witness table
217226
// public let n: Int

0 commit comments

Comments
 (0)