Skip to content

Commit f6a73ff

Browse files
committed
IRGen: Unwrap one-element tuple metadata in emitDynamicTupleTypeMetadataRef()
This matches the static behavior of Type::subst() and the SIL optimizer.
1 parent 2ad1589 commit f6a73ff

File tree

4 files changed

+148
-33
lines changed

4 files changed

+148
-33
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/MetadataRequest.cpp

Lines changed: 94 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,8 @@ 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) {
@@ -1179,32 +1181,105 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF,
11791181

11801182
CanPackType packType = CanPackType::get(IGF.IGM.Context, types);
11811183

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

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

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-
};
1198+
// Test if the runtime length of the pack type is exactly 1.
1199+
auto *one = llvm::ConstantInt::get(IGF.IGM.SizeTy, 1);
1200+
auto *isOne = IGF.Builder.CreateICmpEQ(shapeExpression, one);
11951201

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

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

1205-
cleanupTypeMetadataPack(IGF, addr, elementCount);
1207+
IGF.Builder.emitBlock(trueBB);
12061208

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

12101285
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)