Skip to content

Commit 8dfeeeb

Browse files
committed
IRGen: Support tuple labels in emitDynamicTupleTypeMetadataRef()
1 parent 999b790 commit 8dfeeeb

File tree

6 files changed

+242
-27
lines changed

6 files changed

+242
-27
lines changed

lib/IRGen/GenPack.cpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/SIL/SILType.h"
2727
#include "llvm/IR/DerivedTypes.h"
2828

29+
#include "GenTuple.h"
2930
#include "GenType.h"
3031
#include "IRGenFunction.h"
3132
#include "IRGenModule.h"
@@ -1184,3 +1185,167 @@ void irgen::deallocatePack(IRGenFunction &IGF, StackAddress addr, CanSILPackType
11841185
IGF.Builder.CreateLifetimeEnd(addr.getAddress(),
11851186
elementSize * elementCount);
11861187
}
1188+
1189+
static unsigned getConstantLabelsLength(CanTupleType type) {
1190+
unsigned total = 0;
1191+
1192+
for (auto elt : type->getElements()) {
1193+
if (elt.getType()->is<PackExpansionType>()) {
1194+
assert(!elt.hasName());
1195+
continue;
1196+
}
1197+
1198+
if (elt.hasName()) {
1199+
assert(!elt.getType()->is<PackExpansionType>());
1200+
total += elt.getName().getLength();
1201+
}
1202+
1203+
++total;
1204+
}
1205+
1206+
return total;
1207+
}
1208+
1209+
/// Emit the dynamic label string for a tuple type containing pack
1210+
/// expansions.
1211+
///
1212+
/// The basic idea is that the static label string is "stretched out".
1213+
/// Pack expansion elements are unlabeled, so they appear as a single
1214+
/// blank space in the static label string. We replace this with the
1215+
/// appropriate number of blank spaces, given the dynamic length of
1216+
/// the pack.
1217+
llvm::Optional<StackAddress>
1218+
irgen::emitDynamicTupleTypeLabels(IRGenFunction &IGF,
1219+
CanTupleType type,
1220+
CanPackType packType,
1221+
llvm::Value *shapeExpression) {
1222+
bool hasLabels = false;
1223+
for (auto elt : type->getElements()) {
1224+
hasLabels |= elt.hasName();
1225+
}
1226+
1227+
if (!hasLabels)
1228+
return llvm::None;
1229+
1230+
// Elements of pack expansion type are unlabeled, so the length of
1231+
// the label string is the number of elements in the pack, plus the
1232+
// sum of the lengths of the labels.
1233+
llvm::Value *labelLength = llvm::ConstantInt::get(
1234+
IGF.IGM.SizeTy, getConstantLabelsLength(type));
1235+
labelLength = IGF.Builder.CreateAdd(shapeExpression, labelLength);
1236+
1237+
// Leave root for a null byte at the end.
1238+
labelLength = IGF.Builder.CreateAdd(labelLength,
1239+
llvm::ConstantInt::get(IGF.IGM.SizeTy, 1));
1240+
1241+
// Allocate space for the label string; we fill it in below.
1242+
StackAddress labelString = IGF.emitDynamicAlloca(
1243+
IGF.IGM.Int8Ty, labelLength,
1244+
IGF.IGM.getPointerAlignment(),
1245+
/*allowTaskAlloc=*/true);
1246+
1247+
// Get the static label string, where each pack expansion is one element.
1248+
auto *staticLabelString = getTupleLabelsString(IGF.IGM, type);
1249+
1250+
// The position in the static label string for to the current element.
1251+
unsigned staticPosition = 0;
1252+
1253+
// The position in the dynamic label string for to the current element.
1254+
llvm::Value *dynamicPosition = llvm::ConstantInt::get(IGF.IGM.SizeTy, 0);
1255+
1256+
// Number of expansions we've seen so far.
1257+
unsigned numExpansions = 0;
1258+
1259+
// Was there at least one label?
1260+
bool sawLabel = false;
1261+
1262+
auto visitFn = [&](CanType eltTy,
1263+
unsigned scalarIndex,
1264+
llvm::Value *dynamicIndex,
1265+
llvm::Value *dynamicLength) {
1266+
auto elt = type->getElements()[scalarIndex + numExpansions];
1267+
assert(eltTy == CanType(elt.getType()));
1268+
1269+
// The destination address, where we put the current element's label.
1270+
auto eltAddr = IGF.Builder.CreateArrayGEP(labelString.getAddress(),
1271+
dynamicPosition, Size(1));
1272+
1273+
// If we're looking at a pack expansion, insert the appropriate
1274+
// number of blank spaces in the dynamic label string.
1275+
if (isa<PackExpansionType>(eltTy)) {
1276+
assert(!elt.hasName() && "Pack expansions cannot have labels");
1277+
// Fill the dynamic label string with a blank label for each
1278+
// dynamic element.
1279+
IGF.Builder.CreateMemSet(
1280+
eltAddr, llvm::ConstantInt::get(IGF.IGM.Int8Ty, ' '),
1281+
dynamicLength);
1282+
1283+
// We consumed one static label.
1284+
staticPosition += 1;
1285+
1286+
// We produced some number of dynamic labels.
1287+
dynamicPosition = IGF.Builder.CreateAdd(dynamicPosition, dynamicLength);
1288+
1289+
// We consumed an expansion.
1290+
numExpansions += 1;
1291+
1292+
return;
1293+
}
1294+
1295+
// Otherwise, we have a single scalar element, which deposits a single
1296+
// label in the dynamic label string.
1297+
unsigned length = 0;
1298+
1299+
// Scalar elements may have labels.
1300+
if (elt.hasName()) {
1301+
// Index into the static label string.
1302+
llvm::Constant *indices[] = {
1303+
llvm::ConstantInt::get(IGF.IGM.SizeTy, staticPosition)
1304+
};
1305+
1306+
// The source address in the static label string.
1307+
Address srcAddr(
1308+
llvm::ConstantExpr::getInBoundsGetElementPtr(
1309+
IGF.IGM.Int8Ty, staticLabelString,
1310+
indices),
1311+
IGF.IGM.Int8Ty, Alignment(1));
1312+
1313+
// The number of bytes to copy; add one for the space at the end.
1314+
length = elt.getName().getLength() + 1;
1315+
1316+
// Desposit the label for this element in the dynamic label string.
1317+
IGF.Builder.CreateMemCpy(eltAddr, srcAddr, Size(length));
1318+
1319+
sawLabel = true;
1320+
} else {
1321+
length = 1;
1322+
1323+
// There is no label. The static label string stores a blank space,
1324+
// and we need to update the dynamic string for the same.
1325+
IGF.Builder.CreateStore(
1326+
llvm::ConstantInt::get(IGF.IGM.Int8Ty, ' '),
1327+
eltAddr);
1328+
}
1329+
1330+
// We consumed one static label.
1331+
staticPosition += length;
1332+
1333+
// We produced one dynamic label.
1334+
auto *constant = llvm::ConstantInt::get(IGF.IGM.SizeTy, length);
1335+
accumulateSum(IGF, dynamicPosition, constant);
1336+
};
1337+
1338+
(void) visitPackExplosion(IGF, packType, visitFn);
1339+
1340+
// Null-terminate the dynamic label string.
1341+
auto eltAddr = IGF.Builder.CreateArrayGEP(labelString.getAddress(),
1342+
dynamicPosition, Size(1));
1343+
IGF.Builder.CreateStore(
1344+
llvm::ConstantInt::get(IGF.IGM.Int8Ty, '\0'),
1345+
eltAddr);
1346+
1347+
assert(sawLabel);
1348+
(void) sawLabel;
1349+
1350+
return labelString;
1351+
}

lib/IRGen/GenPack.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ StackAddress allocatePack(IRGenFunction &IGF, CanSILPackType packType);
114114

115115
void deallocatePack(IRGenFunction &IGF, StackAddress addr, CanSILPackType packType);
116116

117+
llvm::Optional<StackAddress>
118+
emitDynamicTupleTypeLabels(IRGenFunction &IGF,
119+
CanTupleType tupleType,
120+
CanPackType packType,
121+
llvm::Value *shapeExpression);
122+
117123
} // end namespace irgen
118124
} // end namespace swift
119125

lib/IRGen/GenTuple.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,3 +643,28 @@ irgen::getPhysicalTupleElementStructIndex(IRGenModule &IGM, SILType tupleType,
643643
unsigned fieldNo) {
644644
FOR_TUPLE_IMPL(IGM, tupleType, getElementStructIndex, fieldNo);
645645
}
646+
647+
/// Emit a string encoding the labels in the given tuple type.
648+
llvm::Constant *irgen::getTupleLabelsString(IRGenModule &IGM,
649+
CanTupleType type) {
650+
bool hasLabels = false;
651+
llvm::SmallString<128> buffer;
652+
for (auto &elt : type->getElements()) {
653+
if (elt.hasName()) {
654+
hasLabels = true;
655+
buffer.append(elt.getName().str());
656+
}
657+
658+
// Each label is space-terminated.
659+
buffer += ' ';
660+
}
661+
662+
// If there are no labels, use a null pointer.
663+
if (!hasLabels) {
664+
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
665+
}
666+
667+
// Otherwise, create a new string literal.
668+
// This method implicitly adds a null terminator.
669+
return IGM.getAddrOfGlobalString(buffer);
670+
}

lib/IRGen/GenTuple.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ namespace irgen {
6767
llvm::Optional<unsigned> getPhysicalTupleElementStructIndex(IRGenModule &IGM,
6868
SILType tupleType,
6969
unsigned fieldNo);
70+
71+
/// Emit a string encoding the labels in the given tuple type.
72+
llvm::Constant *getTupleLabelsString(IRGenModule &IGM,
73+
CanTupleType type);
74+
7075
} // end namespace irgen
7176
} // end namespace swift
7277

lib/IRGen/MetadataRequest.cpp

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "GenPack.h"
2828
#include "GenPointerAuth.h"
2929
#include "GenProto.h"
30+
#include "GenTuple.h"
3031
#include "GenType.h"
3132
#include "GenericArguments.h"
3233
#include "GenericRequirement.h"
@@ -1135,31 +1136,6 @@ MetadataAccessStrategy irgen::getTypeMetadataAccessStrategy(CanType type) {
11351136
return MetadataAccessStrategy::NonUniqueAccessor;
11361137
}
11371138

1138-
/// Emit a string encoding the labels in the given tuple type.
1139-
static llvm::Constant *getTupleLabelsString(IRGenModule &IGM,
1140-
CanTupleType type) {
1141-
bool hasLabels = false;
1142-
llvm::SmallString<128> buffer;
1143-
for (auto &elt : type->getElements()) {
1144-
if (elt.hasName()) {
1145-
hasLabels = true;
1146-
buffer.append(elt.getName().str());
1147-
}
1148-
1149-
// Each label is space-terminated.
1150-
buffer += ' ';
1151-
}
1152-
1153-
// If there are no labels, use a null pointer.
1154-
if (!hasLabels) {
1155-
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
1156-
}
1157-
1158-
// Otherwise, create a new string literal.
1159-
// This method implicitly adds a null terminator.
1160-
return IGM.getAddrOfGlobalString(buffer);
1161-
}
1162-
11631139
static llvm::Constant *emitEmptyTupleTypeMetadataRef(IRGenModule &IGM) {
11641140
llvm::Constant *fullMetadata = IGM.getEmptyTupleMetadata();
11651141
llvm::Constant *indices[] = {
@@ -1188,6 +1164,8 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF,
11881164
// "vanishes" and gets unwrapped. This behavior is implemented in both
11891165
// compile-time type substitution, and runtime type metadata instantiation,
11901166
// ensuring consistent behavior.
1167+
//
1168+
// FIXME: Inconsistent behavior with one-element labeled tuples.
11911169
if (type->getNumScalarElements() <= 1) {
11921170
ConditionalDominanceScope scope(IGF);
11931171

@@ -1231,6 +1209,9 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF,
12311209
{
12321210
ConditionalDominanceScope scope(IGF);
12331211

1212+
llvm::Optional<StackAddress> labelString = emitDynamicTupleTypeLabels(
1213+
IGF, type, packType, shapeExpression);
1214+
12341215
// Otherwise, we know that either statically or dynamically, we have more than
12351216
// one element. Emit the pack.
12361217
llvm::Value *shape;
@@ -1241,12 +1222,20 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF,
12411222
auto *pointerToFirst = IGF.Builder.CreatePointerCast(
12421223
addr.getAddressPointer(), IGF.IGM.TypeMetadataPtrPtrTy);
12431224

1225+
auto *flags = shapeExpression;
1226+
if (labelString) {
1227+
flags = IGF.Builder.CreateOr(flags, llvm::ConstantInt::get(IGF.IGM.SizeTy,
1228+
TupleTypeFlags().withNonConstantLabels(true).getIntValue()));
1229+
}
1230+
12441231
// Call swift_getTupleMetadata().
12451232
llvm::Value *args[] = {
12461233
request.get(IGF),
1247-
shapeExpression,
1234+
flags,
12481235
pointerToFirst,
1249-
getTupleLabelsString(IGF.IGM, type),
1236+
(labelString
1237+
? labelString->getAddress().getAddress()
1238+
: llvm::ConstantPointerNull::get(IGF.IGM.Int8PtrTy)),
12501239
llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrTy) // proposed
12511240
};
12521241

@@ -1256,6 +1245,9 @@ static MetadataResponse emitDynamicTupleTypeMetadataRef(IRGenFunction &IGF,
12561245
call->setDoesNotThrow();
12571246

12581247
cleanupTypeMetadataPack(IGF, addr, shape);
1248+
1249+
if (labelString)
1250+
IGF.emitDeallocateDynamicAlloca(*labelString);
12591251
}
12601252

12611253
// Control flow join with the one-element case.

test/Interpreter/variadic_generic_tuples.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,26 @@ tuples.test("expandTuple") {
6464
expandTupleElements(1, "hello", true)
6565
}
6666

67+
func tupleLabelMix<each T, each U>(t: repeat (each T).Type, u: repeat (each U).Type) -> Any.Type {
68+
return (Float, hello: Int, repeat each T, swift: String, repeat each U, Bool, world: UInt8).self
69+
}
70+
71+
func oneElementLabeledTuple<each T>(t: repeat (each T).Type) -> Any.Type {
72+
return (label: Int, repeat each T).self
73+
}
74+
75+
tuples.test("labels") {
76+
expectEqual("(Swift.Float, hello: Swift.Int, swift: Swift.String, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix()))
77+
expectEqual("(Swift.Float, hello: Swift.Int, swift: Swift.String, Swift.Double, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix(u: Double.self)))
78+
expectEqual("(Swift.Float, hello: Swift.Int, swift: Swift.String, Swift.Double, Swift.Int32, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix(u: Double.self, Int32.self)))
79+
expectEqual("(Swift.Float, hello: Swift.Int, Swift.Character, swift: Swift.String, Swift.Double, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix(t: Character.self, u: Double.self)))
80+
expectEqual("(Swift.Float, hello: Swift.Int, Swift.Character, Swift.Substring, swift: Swift.String, Swift.Double, Swift.Int32, Swift.Bool, world: Swift.UInt8)", _typeName(tupleLabelMix(t: Character.self, Substring.self, u: Double.self, Int32.self)))
81+
82+
// FIXME: One-element labeled tuples
83+
expectEqual("Swift.Int", _typeName(oneElementLabeledTuple()))
84+
85+
expectEqual("(label: Swift.Int, Swift.String)", _typeName(oneElementLabeledTuple(t: String.self)))
86+
}
87+
88+
6789
runAllTests()

0 commit comments

Comments
 (0)