Skip to content

Commit 23781df

Browse files
authored
Merge pull request #18342 from rjmccall/tuple-layout-from-element-layouts
Add runtime functions to compute tuple layouts from element layouts
2 parents b124154 + 436a8b2 commit 23781df

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)