Skip to content

Commit 9c68aaa

Browse files
authored
Merge pull request #65466 from slavapestov/irgen-vanishing-tuple
IRGen: Unwrap one-element tuple metadata in emitDynamicTupleTypeMetadataRef()
2 parents d445a18 + 75cea74 commit 9c68aaa

File tree

5 files changed

+208
-40
lines changed

5 files changed

+208
-40
lines changed

lib/IRGen/GenPack.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -899,11 +899,11 @@ llvm::Value *irgen::emitTypeMetadataPackElementRef(
899899
wtables.push_back(wtable);
900900
}
901901
}
902-
metadataPhi->addIncoming(metadata, materialize);
902+
metadataPhi->addIncoming(metadata, IGF.Builder.GetInsertBlock());
903903
for (auto i : indices(wtables)) {
904904
auto *wtable = wtables[i];
905905
auto *wtablePhi = wtablePhis[i];
906-
wtablePhi->addIncoming(wtable, materialize);
906+
wtablePhi->addIncoming(wtable, IGF.Builder.GetInsertBlock());
907907
}
908908
IGF.Builder.CreateBr(exit);
909909
// }} Finished emitting emit_i.

lib/IRGen/GenTuple.cpp

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,9 +562,66 @@ Address irgen::projectTupleElementAddressByDynamicIndex(IRGenFunction &IGF,
562562
SILType elementType) {
563563
auto *metadata = IGF.emitTypeMetadataRefForLayout(tupleType);
564564

565-
llvm::Value *offset = loadTupleOffsetFromMetadata(IGF, metadata, index);
565+
566+
llvm::BasicBlock *trueBB = nullptr, *falseBB = nullptr, *restBB = nullptr;
567+
llvm::BasicBlock *unwrappedBB = nullptr;
568+
llvm::Value *unwrappedOffset = nullptr;
569+
570+
auto loweredTupleType = tupleType.castTo<TupleType>();
571+
if (loweredTupleType->getNumScalarElements() <= 1) {
572+
ConditionalDominanceScope scope(IGF);
573+
574+
// Test if the runtime length of the pack type is exactly 1.
575+
CanPackType packType = loweredTupleType.getInducedPackType();
576+
auto *shapeExpression = IGF.emitPackShapeExpression(packType);
577+
578+
auto *one = llvm::ConstantInt::get(IGF.IGM.SizeTy, 1);
579+
auto *isOne = IGF.Builder.CreateICmpEQ(shapeExpression, one);
580+
581+
trueBB = IGF.createBasicBlock("vanishing-tuple");
582+
falseBB = IGF.createBasicBlock("actual-tuple");
583+
584+
IGF.Builder.CreateCondBr(isOne, trueBB, falseBB);
585+
586+
IGF.Builder.emitBlock(trueBB);
587+
588+
// If the length is 1, the offset is just zero.
589+
unwrappedBB = IGF.Builder.GetInsertBlock();
590+
unwrappedOffset = llvm::ConstantInt::get(IGF.IGM.Int32Ty, 0);
591+
592+
restBB = IGF.createBasicBlock("tuple-rest");
593+
IGF.Builder.CreateBr(restBB);
594+
595+
IGF.Builder.emitBlock(falseBB);
596+
}
597+
598+
llvm::Value *tupleOffset = nullptr;
599+
llvm::BasicBlock *tupleBB = nullptr;
600+
601+
{
602+
ConditionalDominanceScope scope(IGF);
603+
tupleOffset = loadTupleOffsetFromMetadata(IGF, metadata, index);
604+
605+
tupleBB = IGF.Builder.GetInsertBlock();
606+
}
607+
608+
// Control flow join with the one-element case.
609+
llvm::Value *result = nullptr;
610+
if (unwrappedOffset != nullptr) {
611+
IGF.Builder.CreateBr(restBB);
612+
IGF.Builder.emitBlock(restBB);
613+
614+
auto *phi = IGF.Builder.CreatePHI(IGF.IGM.Int32Ty, 2);
615+
phi->addIncoming(unwrappedOffset, unwrappedBB);
616+
phi->addIncoming(tupleOffset, tupleBB);
617+
618+
result = phi;
619+
} else {
620+
result = tupleOffset;
621+
}
622+
566623
auto *gep =
567-
IGF.emitByteOffsetGEP(tuple.getAddress(), offset, IGF.IGM.OpaqueTy);
624+
IGF.emitByteOffsetGEP(tuple.getAddress(), result, IGF.IGM.OpaqueTy);
568625
auto elementAddress = Address(gep, IGF.IGM.OpaqueTy,
569626
IGF.IGM.getPointerAlignment());
570627
return IGF.Builder.CreateElementBitCast(elementAddress,

lib/IRGen/MetadataRequest.cpp

Lines changed: 95 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,41 +1170,112 @@ static llvm::Constant *emitEmptyTupleTypeMetadataRef(IRGenModule &IGM) {
11701170
IGM.FullExistentialTypeMetadataStructTy, fullMetadata, indices);
11711171
}
11721172

1173+
/// Emit metadata for a tuple type containing one or more pack expansions, eg
1174+
/// (T, repeat each U, v: V, repeat each W).
11731175
static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF,
11741176
CanTupleType type,
11751177
DynamicMetadataRequest request) {
1176-
SmallVector<CanType, 2> types;
1177-
types.append(type.getElementTypes().begin(),
1178-
type.getElementTypes().end());
1179-
1180-
CanPackType packType = CanPackType::get(IGF.IGM.Context, types);
1178+
CanPackType packType = type.getInducedPackType();
11811179

1180+
// Begin by computing the number of elements in the tuple type.
11821181
auto *shapeExpression = IGF.emitPackShapeExpression(packType);
1183-
auto addr = emitTypeMetadataPack(IGF, packType, MetadataState::Abstract);
1182+
llvm::BasicBlock *trueBB = nullptr, *falseBB = nullptr, *restBB = nullptr;
1183+
llvm::BasicBlock *unwrappedBB = nullptr;
1184+
llvm::Value *unwrapped = nullptr;
11841185

1185-
auto *pointerToFirst = IGF.Builder.CreatePointerCast(
1186-
addr.getAddressPointer(), IGF.IGM.TypeMetadataPtrPtrTy);
1186+
// A tuple type containing zero or one non-pack-expansions might contain
1187+
// exactly one element after substitution, in which case the tuple
1188+
// "vanishes" and gets unwrapped. This behavior is implemented in both
1189+
// compile-time type substitution, and runtime type metadata instantiation,
1190+
// ensuring consistent behavior.
1191+
if (type->getNumScalarElements() <= 1) {
1192+
ConditionalDominanceScope scope(IGF);
11871193

1188-
llvm::Value *args[] = {
1189-
request.get(IGF),
1190-
shapeExpression,
1191-
pointerToFirst,
1192-
getTupleLabelsString(IGF.IGM, type),
1193-
llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrTy) // proposed
1194-
};
1194+
// Test if the runtime length of the pack type is exactly 1.
1195+
auto *one = llvm::ConstantInt::get(IGF.IGM.SizeTy, 1);
1196+
auto *isOne = IGF.Builder.CreateICmpEQ(shapeExpression, one);
11951197

1196-
auto call = IGF.Builder.CreateCall(
1197-
IGF.IGM.getGetTupleMetadataFunctionPointer(), args);
1198-
call->setCallingConv(IGF.IGM.SwiftCC);
1199-
call->setDoesNotThrow();
1198+
trueBB = IGF.createBasicBlock("vanishing-tuple");
1199+
falseBB = IGF.createBasicBlock("actual-tuple");
12001200

1201-
Optional<unsigned> elementCount = 0;
1202-
if (auto *constant = dyn_cast<llvm::ConstantInt>(shapeExpression))
1203-
elementCount = constant->getValue().getZExtValue();
1201+
IGF.Builder.CreateCondBr(isOne, trueBB, falseBB);
12041202

1205-
cleanupTypeMetadataPack(IGF, addr, elementCount);
1203+
IGF.Builder.emitBlock(trueBB);
12061204

1207-
return MetadataResponse::handle(IGF, request, call);
1205+
// If the length is 1, directly emit the metadata for the first pack element.
1206+
ArrayRef<ProtocolConformanceRef> conformances;
1207+
llvm::SmallVector<llvm::Value *, 2> wtables;
1208+
1209+
auto *index = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);
1210+
auto *value = emitTypeMetadataPackElementRef(
1211+
IGF, packType, conformances, index, request, wtables);
1212+
1213+
// FIXME: Should emitTypeMetadataPackElementRef() preserve the dynamic state?
1214+
auto response = MetadataResponse::forBounded(
1215+
value, request.getStaticLowerBoundOnResponseState());
1216+
response.ensureDynamicState(IGF);
1217+
1218+
unwrapped = response.combine(IGF);
1219+
unwrappedBB = IGF.Builder.GetInsertBlock();
1220+
1221+
assert(wtables.empty());
1222+
1223+
restBB = IGF.createBasicBlock("tuple-rest");
1224+
IGF.Builder.CreateBr(restBB);
1225+
1226+
IGF.Builder.emitBlock(falseBB);
1227+
}
1228+
1229+
llvm::CallInst *call = nullptr;
1230+
1231+
{
1232+
ConditionalDominanceScope scope(IGF);
1233+
1234+
// Otherwise, we know that either statically or dynamically, we have more than
1235+
// one element. Emit the pack.
1236+
auto addr = emitTypeMetadataPack(IGF, packType, MetadataState::Abstract);
1237+
1238+
auto *pointerToFirst = IGF.Builder.CreatePointerCast(
1239+
addr.getAddressPointer(), IGF.IGM.TypeMetadataPtrPtrTy);
1240+
1241+
// Call swift_getTupleMetadata().
1242+
llvm::Value *args[] = {
1243+
request.get(IGF),
1244+
shapeExpression,
1245+
pointerToFirst,
1246+
getTupleLabelsString(IGF.IGM, type),
1247+
llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrTy) // proposed
1248+
};
1249+
1250+
call = IGF.Builder.CreateCall(
1251+
IGF.IGM.getGetTupleMetadataFunctionPointer(), args);
1252+
call->setCallingConv(IGF.IGM.SwiftCC);
1253+
call->setDoesNotThrow();
1254+
1255+
// Clean up the pack.
1256+
Optional<unsigned> elementCount = 0;
1257+
if (auto *constant = dyn_cast<llvm::ConstantInt>(shapeExpression))
1258+
elementCount = constant->getValue().getZExtValue();
1259+
1260+
cleanupTypeMetadataPack(IGF, addr, elementCount);
1261+
}
1262+
1263+
// Control flow join with the one-element case.
1264+
llvm::Value *result = nullptr;
1265+
if (unwrapped != nullptr) {
1266+
IGF.Builder.CreateBr(restBB);
1267+
IGF.Builder.emitBlock(restBB);
1268+
1269+
auto *phi = IGF.Builder.CreatePHI(IGF.IGM.TypeMetadataResponseTy, 2);
1270+
phi->addIncoming(unwrapped, unwrappedBB);
1271+
phi->addIncoming(call, call->getParent());
1272+
1273+
result = phi;
1274+
} else {
1275+
result = call;
1276+
}
1277+
1278+
return MetadataResponse::handle(IGF, request, result);
12081279
}
12091280

12101281
static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s -DINT=i%target-ptrsize
2+
3+
public func takesMetatype<T>(_: T.Type) {}
4+
5+
public func makeTuple<each T>(_ t: repeat each T) {
6+
takesMetatype((repeat each T).self)
7+
}
8+
9+
// CHECK-LABEL: define {{(protected )?}}{{(dllexport )?}}swiftcc void @"$s24variadic_vanishing_tuple9makeTupleyyxxQpRvzlF"(%swift.opaque** noalias nocapture %0, {{i32|i64}} %1, %swift.type** %"each T")
10+
// CHECK: [[CMP:%.*]] = icmp eq [[INT]] %1, 1
11+
// CHECK: br i1 [[CMP]], label %vanishing-tuple, label %actual-tuple
12+
13+
// CHECK: vanishing-tuple:
14+
// CHECK: [[PACK_ADDR:%.*]] = ptrtoint %swift.type** %"each T" to [[INT]]
15+
// CHECK: [[PACK_ADDR_MASKED:%.*]] = and [[INT]] [[PACK_ADDR]], -2
16+
// CHECK: [[PACK_PTR:%.*]] = inttoptr [[INT]] [[PACK_ADDR_MASKED]] to %swift.type**
17+
// CHECK: [[ELT_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[PACK_PTR]], [[INT]] 0
18+
// CHECK: [[ELT:%.*]] = load %swift.type*, %swift.type** [[ELT_PTR]]
19+
// CHECK: [[RESULT:%.*]] = insertvalue %swift.metadata_response undef, %swift.type* [[ELT]], 0
20+
// CHECK: [[RESULT2:%.*]] = insertvalue %swift.metadata_response [[RESULT]], [[INT]] 0, 1
21+
// CHECK: br label %tuple-rest
22+
23+
// CHECK: actual-tuple:
24+
// CHECK: [[PACK:%.*]] = alloca %swift.type*, [[INT]] %1
25+
// CHECK: br label %pack-expansion-check
26+
27+
// CHECK: pack-expansion-check:
28+
// CHECK: br i1 {{%.*}}, label %pack-expansion-loop, label %pack-expansion-rest
29+
30+
// CHECK: pack-expansion-loop:
31+
// CHECK: br label %pack-expansion-check
32+
33+
// CHECK: pack-expansion-rest:
34+
// CHECK: [[TUPLE:%.*]] = call swiftcc %swift.metadata_response @swift_getTupleTypeMetadata([[INT]] 0, [[INT]] %1, %swift.type** [[PACK:%.*]], i8* null, i8** null)
35+
// CHECK: br label %tuple-rest
36+
37+
// CHECK: tuple-rest:
38+
// CHECK: [[PHI:%.*]] = phi %swift.metadata_response [ [[RESULT2]], %vanishing-tuple ], [ [[TUPLE]], %pack-expansion-rest ]
39+
// CHECK: [[METADATA:%.*]] = extractvalue %swift.metadata_response [[PHI]], 0
40+
// CHECK: call swiftcc void @"$s24variadic_vanishing_tuple13takesMetatypeyyxmlF"(%swift.type* [[METADATA]], %swift.type* [[METADATA]])
41+
// CHECK: ret void
42+
43+
public func makeTuple2<each T, each U, each V: Hashable>(t: repeat each T, u: repeat each U, v: repeat each V) {
44+
takesMetatype((repeat each T, repeat Array<each U>, repeat Set<each V>).self)
45+
}

test/Interpreter/variadic_generic_tuples.swift

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
// RUN: %target-run-simple-swift
22

3-
// FIXME: Fix the optimizer
4-
// REQUIRES: swift_test_mode_optimize_none
5-
63
// REQUIRES: executable_test
74

85
import StdlibUnittest
@@ -18,8 +15,8 @@ func makeTuple<each T>(_: repeat (each T).Type) -> Any.Type {
1815
tuples.test("makeTuple") {
1916
expectEqual("()", _typeName(makeTuple()))
2017

21-
// FIXME: This should unwrap the one-element tuple!
22-
expectEqual("(Swift.Array<Swift.Int>)", _typeName(makeTuple(Int.self)))
18+
// Note that we unwrap the one-element tuple!
19+
expectEqual("Swift.Array<Swift.Int>", _typeName(makeTuple(Int.self)))
2320

2421
expectEqual("(Swift.Array<Swift.Int>, Swift.Array<Swift.String>)", _typeName(makeTuple(Int.self, String.self)))
2522
expectEqual("(Swift.Array<Swift.Int>, Swift.Array<Swift.String>, Swift.Array<Swift.Float>)", _typeName(makeTuple(Int.self, String.self, Float.self)))
@@ -30,8 +27,8 @@ func makeTuple2<each T>(_: repeat (each T).Type) -> Any.Type {
3027
}
3128

3229
tuples.test("makeTuple2") {
33-
// FIXME: This should unwrap the one-element tuple!
34-
expectEqual("(Swift.Int)", _typeName(makeTuple2()))
30+
// Note that we unwrap the one-element tuple!
31+
expectEqual("Swift.Int", _typeName(makeTuple2()))
3532

3633
expectEqual("(Swift.Int, Swift.Array<Swift.Bool>)", _typeName(makeTuple2(Bool.self)))
3734
expectEqual("(Swift.Int, Swift.Array<Swift.Bool>, Swift.Array<Swift.Character>)", _typeName(makeTuple2(Bool.self, Character.self)))
@@ -45,11 +42,9 @@ func makeTuple3<each T, each U>(t: repeat (each T).Type, u: repeat (each U).Type
4542
tuples.test("makeTuple3") {
4643
expectEqual("()", _typeName(makeTuple3()))
4744

48-
// FIXME: This should unwrap the one-element tuple!
49-
expectEqual("(Swift.Int)", _typeName(makeTuple3(t: Int.self)))
50-
51-
// FIXME: This should unwrap the one-element tuple!
52-
expectEqual("(Swift.Int)", _typeName(makeTuple3(u: Int.self)))
45+
// Note that we unwrap the one-element tuple!
46+
expectEqual("Swift.Int", _typeName(makeTuple3(t: Int.self)))
47+
expectEqual("Swift.Int", _typeName(makeTuple3(u: Int.self)))
5348

5449
expectEqual("(Swift.Int, Swift.Float)", _typeName(makeTuple3(t: Int.self, u: Float.self)))
5550
}

0 commit comments

Comments
 (0)