Skip to content

Commit f56af9b

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 ce23fd4 commit f56af9b

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
@@ -531,6 +531,46 @@ swift_getTupleTypeMetadata3(MetadataRequest request,
531531
const Metadata *elt2, const char *labels,
532532
const ValueWitnessTable *proposedWitnesses);
533533

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

include/swift/Runtime/RuntimeFunctions.def

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

768+
// void swift_getTupleTypeLayout(TypeLayout *result,
769+
// uint32_t offsets,
770+
// TupleTypeFlags flags,
771+
// const TypeLayout * const *elts);
772+
FUNCTION(GetTupleLayout, swift_getTupleTypeLayout, SwiftCC,
773+
RETURNS(VoidTy),
774+
ARGS(FullTypeLayoutTy->getPointerTo(0), Int32Ty->getPointerTo(0),
775+
SizeTy, Int8PtrPtrTy->getPointerTo(0)),
776+
ATTRS(NoUnwind))
777+
778+
// size_t swift_getTupleTypeLayout2(TypeLayout *layout,
779+
// const TypeLayout *elt0,
780+
// const TypeLayout *elt1);
781+
FUNCTION(GetTupleLayout2, swift_getTupleTypeLayout2, SwiftCC,
782+
RETURNS(SizeTy),
783+
ARGS(FullTypeLayoutTy->getPointerTo(0), Int8PtrPtrTy, Int8PtrPtrTy),
784+
ATTRS(NoUnwind))
785+
786+
// OffsetPair swift_getTupleTypeLayout3(TypeLayout *layout,
787+
// const TypeLayout *elt0,
788+
// const TypeLayout *elt1,
789+
// const TypeLayout *elt2);
790+
FUNCTION(GetTupleLayout3, swift_getTupleTypeLayout3, SwiftCC,
791+
RETURNS(OffsetPairTy),
792+
ARGS(FullTypeLayoutTy->getPointerTo(0),
793+
Int8PtrPtrTy, Int8PtrPtrTy, Int8PtrPtrTy),
794+
ATTRS(NoUnwind))
795+
768796
// Metadata *swift_getExistentialTypeMetadata(
769797
// ProtocolClassConstraint classConstraint,
770798
// 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
@@ -530,6 +530,8 @@ class IRGenModule {
530530
llvm::StructType *TypeMetadataResponseTy; /// { %swift.type*, iSize }
531531
llvm::StructType *TypeMetadataDependencyTy; /// { %swift.type*, iSize }
532532
};
533+
llvm::StructType *OffsetPairTy; /// { iSize, iSize }
534+
llvm::StructType *FullTypeLayoutTy; /// %swift.full_type_layout = { ... }
533535
llvm::PointerType *TupleTypeMetadataPtrTy; /// %swift.tuple_type*
534536
llvm::StructType *FullHeapMetadataStructTy; /// %swift.full_heapmetadata = type { ... }
535537
llvm::PointerType *FullHeapMetadataPtrTy;/// %swift.full_heapmetadata*

lib/IRGen/MetadataRequest.cpp

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

2129+
/// Given that the type is fixed-layout, emit the type layout by
2130+
/// emitting a global layout for it.
2131+
llvm::Value *emitFromFixedLayout(CanType t) {
2132+
auto layout = tryEmitFromFixedLayout(t);
2133+
assert(layout && "type must be fixed-size to call emitFromFixedLayout");
2134+
return layout;
2135+
}
2136+
2137+
/// If the type is fixed-layout, emit the type layout by
2138+
/// emitting a global layout for it.
2139+
llvm::Value *tryEmitFromFixedLayout(CanType t) {
2140+
auto &ti = IGF.getTypeInfo(SILType::getPrimitiveObjectType(t));
2141+
if (auto fixedTI = dyn_cast<FixedTypeInfo>(&ti))
2142+
return IGF.IGM.emitFixedTypeLayout(t, *fixedTI);
2143+
return nullptr;
2144+
}
2145+
21292146
bool hasVisibleValueWitnessTable(CanType t) const {
21302147
// Some builtin and structural types have value witnesses exported from
21312148
// the runtime.
@@ -2233,7 +2250,7 @@ namespace {
22332250
}
22342251
case MetatypeRepresentation::Thick:
22352252
if (isa<ExistentialMetatypeType>(type)) {
2236-
return emitFromTypeMetadata(type, request);
2253+
return emitFromFixedLayout(type);
22372254
}
22382255
// Otherwise, this is a metatype that looks like a pointer.
22392256
LLVM_FALLTHROUGH;
@@ -2340,6 +2357,103 @@ namespace {
23402357
type->getOwnership());
23412358
return emitFromValueWitnessTable(valueWitnessType);
23422359
}
2360+
2361+
llvm::Value *visitTupleType(CanTupleType type,
2362+
DynamicMetadataRequest request) {
2363+
// Single-element tuples have exactly the same layout as their elements.
2364+
if (type->getNumElements() == 1) {
2365+
return visit(type.getElementType(0), request);
2366+
}
2367+
2368+
// If the type is fixed-layout, use a global layout.
2369+
if (auto layout = tryEmitFromFixedLayout(type))
2370+
return layout;
2371+
2372+
// TODO: check for cached VWT / metadata for the type.
2373+
2374+
// Use swift_getTupleTypeLayout to compute a layout.
2375+
2376+
// Create a buffer to hold the result. We don't have any reasonable
2377+
// way to scope the lifetime of this.
2378+
auto resultPtr = IGF.createAlloca(IGF.IGM.FullTypeLayoutTy,
2379+
IGF.IGM.getPointerAlignment())
2380+
.getAddress();
2381+
2382+
switch (type->getNumElements()) {
2383+
case 0:
2384+
case 1:
2385+
llvm_unreachable("filtered out above");
2386+
2387+
case 2: {
2388+
auto elt0 = visit(type.getElementType(0), request);
2389+
auto elt1 = visit(type.getElementType(1), request);
2390+
2391+
// Ignore the offset.
2392+
auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayout2Fn(),
2393+
{resultPtr, elt0, elt1});
2394+
call->setDoesNotThrow();
2395+
2396+
break;
2397+
}
2398+
2399+
case 3: {
2400+
auto elt0 = visit(type.getElementType(0), request);
2401+
auto elt1 = visit(type.getElementType(1), request);
2402+
auto elt2 = visit(type.getElementType(2), request);
2403+
2404+
// Ignore the offsets.
2405+
auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayout3Fn(),
2406+
{resultPtr, elt0, elt1, elt2});
2407+
call->setDoesNotThrow();
2408+
2409+
break;
2410+
}
2411+
2412+
default: {
2413+
// Allocate a temporary array for the element layouts.
2414+
auto eltLayoutsArraySize =
2415+
IGF.IGM.getPointerSize() * type->getNumElements();
2416+
auto eltLayoutsArray =
2417+
IGF.createAlloca(IGF.IGM.Int8PtrPtrTy,
2418+
IGF.IGM.getSize(Size(type->getNumElements())),
2419+
IGF.IGM.getPointerAlignment());
2420+
IGF.Builder.CreateLifetimeStart(eltLayoutsArray, eltLayoutsArraySize);
2421+
2422+
// Emit layouts for all the elements and store them into the array.
2423+
for (auto i : indices(type.getElementTypes())) {
2424+
auto eltLayout = visit(type.getElementType(i), request);
2425+
auto eltLayoutSlot =
2426+
i == 0 ? eltLayoutsArray
2427+
: IGF.Builder.CreateConstArrayGEP(eltLayoutsArray, i,
2428+
IGF.IGM.getPointerSize());
2429+
IGF.Builder.CreateStore(eltLayout, eltLayoutSlot);
2430+
}
2431+
2432+
// Ignore the offsets.
2433+
auto offsetsPtr =
2434+
llvm::ConstantPointerNull::get(IGF.IGM.Int32Ty->getPointerTo());
2435+
2436+
// Flags.
2437+
auto flags = TupleTypeFlags().withNumElements(type->getNumElements());
2438+
auto flagsValue = IGF.IGM.getSize(Size(flags.getIntValue()));
2439+
2440+
// Compute the layout.
2441+
auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayoutFn(),
2442+
{resultPtr, offsetsPtr, flagsValue,
2443+
eltLayoutsArray.getAddress()});
2444+
call->setDoesNotThrow();
2445+
2446+
// We're done with the buffer.
2447+
IGF.Builder.CreateLifetimeEnd(eltLayoutsArray, eltLayoutsArraySize);
2448+
2449+
break;
2450+
}
2451+
}
2452+
2453+
// Cast resultPtr to i8**, our general currency type for type layouts.
2454+
resultPtr = IGF.Builder.CreateBitCast(resultPtr, IGF.IGM.Int8PtrPtrTy);
2455+
return resultPtr;
2456+
}
23432457
};
23442458

23452459
} // 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)