Skip to content

Commit c283a69

Browse files
authored
Merge pull request #13539 from jckarter/squeeze-foreign-metadata-header
Runtime: Compact the uniquing header for foreign metadata records.
2 parents b4da5a3 + 05fc210 commit c283a69

File tree

6 files changed

+156
-69
lines changed

6 files changed

+156
-69
lines changed

include/swift/Remote/MetadataReader.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -894,9 +894,9 @@ class MetadataReader {
894894
case MetadataKind::ForeignClass: {
895895
auto namePtrAddress =
896896
Meta.getAddress() + TargetForeignClassMetadata<Runtime>::OffsetToName;
897-
StoredPointer namePtr;
898-
if (!Reader->readInteger(RemoteAddress(namePtrAddress), &namePtr) ||
899-
namePtr == 0)
897+
898+
StoredPointer namePtr = resolveRelativeOffset<int32_t>(namePtrAddress);
899+
if (namePtr == 0)
900900
return BuiltType();
901901
std::string name;
902902
if (!Reader->readString(RemoteAddress(namePtr), name))

include/swift/Runtime/Metadata.h

Lines changed: 114 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,86 +1567,152 @@ struct TargetForeignTypeMetadata : public TargetMetadata<Runtime> {
15671567
using StoredPointer = typename Runtime::StoredPointer;
15681568
using StoredSize = typename Runtime::StoredSize;
15691569
using InitializationFunction_t =
1570-
void (*)(TargetForeignTypeMetadata<Runtime> *selectedMetadata);
1570+
void (TargetForeignTypeMetadata<Runtime> *selectedMetadata);
15711571
using RuntimeMetadataPointer =
15721572
ConstTargetMetadataPointer<Runtime, swift::TargetForeignTypeMetadata>;
15731573

1574+
/// An invasive cache for the runtime-uniqued lookup structure that is stored
1575+
/// in the header prefix of foreign metadata records.
1576+
///
1577+
/// Prior to initialization, as emitted by the compiler, this contains the
1578+
/// initialization flags.
1579+
/// After initialization, it holds a pointer to the actual, runtime-uniqued
1580+
/// metadata for this type.
1581+
struct CacheValue {
1582+
StoredSize Value;
1583+
1584+
/// Work around a bug in libstdc++'s std::atomic that requires the type to
1585+
/// be default-constructible.
1586+
CacheValue() = default;
1587+
1588+
explicit CacheValue(RuntimeMetadataPointer p)
1589+
: Value(reinterpret_cast<StoredSize>(p))
1590+
{}
1591+
1592+
/// Various flags. The largest flag bit should be less than 4096 so that
1593+
/// a flag set is distinguishable from a valid pointer.
1594+
enum : StoredSize {
1595+
/// This metadata has an initialization callback function. If
1596+
/// this flag is not set, the metadata object needn't actually
1597+
/// have a InitializationFunction field, and that field will be
1598+
/// undefined.
1599+
HasInitializationFunction = 0x1,
1600+
1601+
LargestFlagMask = 0xFFF,
1602+
};
1603+
1604+
/// True if the metadata record associated with this cache has not been
1605+
/// initialized, so contains a flag set describing parameters to the
1606+
/// initialization operation. isFlags() == !isInitialized()
1607+
bool isFlags() const {
1608+
return Value <= LargestFlagMask;
1609+
}
1610+
/// True if the metadata record associated with this cache has an
1611+
/// initialization function which must be run if it is picked as the
1612+
/// canonical metadata record for its key.
1613+
///
1614+
/// Undefined if !isFlags().
1615+
bool hasInitializationFunction() const {
1616+
assert(isFlags());
1617+
return Value & HasInitializationFunction;
1618+
}
1619+
1620+
/// True if the metadata record associated with this cache has been
1621+
/// initialized, so the cache contains an absolute pointer to the
1622+
/// canonical metadata record for its key. isInitialized() == !isFlags()
1623+
bool isInitialized() const {
1624+
return !isFlags();
1625+
}
1626+
1627+
/// Gets the cached pointer to the unique canonical metadata record for
1628+
/// this metadata record's key.
1629+
///
1630+
/// Undefined if !isInitialized().
1631+
RuntimeMetadataPointer getCachedUniqueMetadata() const {
1632+
assert(isInitialized());
1633+
return RuntimeMetadataPointer(Value);
1634+
}
1635+
};
1636+
1637+
15741638
/// Foreign type metadata may have extra header fields depending on
15751639
/// the flags.
15761640
struct HeaderPrefix {
15771641
/// An optional callback performed when a particular metadata object
15781642
/// is chosen as the unique structure.
1643+
///
15791644
/// If there is no initialization function, this metadata record can be
15801645
/// assumed to be immutable (except for the \c Unique invasive cache
1581-
/// field).
1582-
InitializationFunction_t InitializationFunction;
1646+
/// field). The field is not present unless the HasInitializationFunction
1647+
/// flag is set.
1648+
RelativeDirectPointer<InitializationFunction_t> InitializationFunction;
15831649

1584-
/// The Swift-mangled name of the type. This is the uniquing key for the
1585-
/// type.
1586-
TargetPointer<Runtime, const char> Name;
1587-
1588-
/// A pointer to the actual, runtime-uniqued metadata for this
1589-
/// type. This is essentially an invasive cache for the lookup
1590-
/// structure.
1591-
mutable std::atomic<RuntimeMetadataPointer> Unique;
1650+
/// The uniquing key for the metadata record. Metadata records with the
1651+
/// same Name string are considered equivalent by the runtime, and the
1652+
/// runtime will pick one to be canonical.
1653+
RelativeDirectPointer<const char> Name;
15921654

1593-
/// Various flags.
1594-
enum : StoredSize {
1595-
/// This metadata has an initialization callback function. If
1596-
/// this flag is not set, the metadata object needn't actually
1597-
/// have a InitializationFunction field.
1598-
HasInitializationFunction = 0x1,
1599-
} Flags;
1655+
mutable std::atomic<CacheValue> Cache;
16001656
};
16011657

16021658
struct HeaderType : HeaderPrefix, TypeMetadataHeader {};
16031659

1604-
static constexpr int OffsetToName =
1605-
(int) offsetof(HeaderType, Name) - (int) sizeof(HeaderType);
1660+
static constexpr int32_t OffsetToName =
1661+
(int32_t) offsetof(HeaderType, Name) - (int32_t) sizeof(HeaderType);
16061662

16071663
TargetPointer<Runtime, const char> getName() const {
16081664
return reinterpret_cast<TargetPointer<Runtime, const char>>(
1609-
asFullMetadata(this)->Name);
1665+
asFullMetadata(this)->Name.get());
16101666
}
16111667

1612-
RuntimeMetadataPointer getCachedUniqueMetadata() const {
1613-
#if __alpha__
1614-
// TODO: This can be a relaxed-order load if there is no initialization
1615-
// function. On platforms we care about, consume is no more expensive than
1616-
// relaxed, so there's no reason to branch here (and LLVM isn't smart
1617-
// enough to eliminate it when it's not needed).
1618-
if (!hasInitializationFunction())
1619-
return asFullMetadata(this)->Unique.load(std::memory_order_relaxed);
1620-
#endif
1621-
return asFullMetadata(this)->Unique.load(SWIFT_MEMORY_ORDER_CONSUME);
1668+
CacheValue getCacheValue() const {
1669+
/// NB: This can be a relaxed-order load if there is no initialization
1670+
/// function. On platforms Swift currently targets, consume is no more
1671+
/// expensive than relaxed, so there's no reason to branch here (and LLVM
1672+
/// isn't smart enough to eliminate it when it's not needed).
1673+
///
1674+
/// A port to a platform where relaxed is significantly less expensive than
1675+
/// consume (historically, Alpha) would probably want to preserve the
1676+
/// 'hasInitializationFunction' bit in its own word to be able to avoid
1677+
/// the consuming load when not needed.
1678+
return asFullMetadata(this)->Cache
1679+
.load(SWIFT_MEMORY_ORDER_CONSUME);
16221680
}
16231681

16241682
void setCachedUniqueMetadata(RuntimeMetadataPointer unique) const {
1625-
assert((static_cast<RuntimeMetadataPointer>(asFullMetadata(this)->Unique) ==
1626-
nullptr ||
1627-
asFullMetadata(this)->Unique == unique) &&
1628-
"already set unique metadata");
1683+
auto cache = getCacheValue();
1684+
1685+
// If the cache was already set to a pointer, we're done. We ought to
1686+
// converge on a single unique pointer.
1687+
if (cache.isInitialized()) {
1688+
assert(cache.getCachedUniqueMetadata() == unique
1689+
&& "already set unique metadata to something else");
1690+
return;
1691+
}
1692+
1693+
auto newCache = CacheValue(unique);
16291694

16301695
// If there is no initialization function, this can be a relaxed store.
1631-
if (!hasInitializationFunction())
1632-
asFullMetadata(this)->Unique.store(unique, std::memory_order_relaxed);
1696+
if (cache.hasInitializationFunction())
1697+
asFullMetadata(this)->Cache.store(newCache, std::memory_order_relaxed);
16331698

16341699
// Otherwise, we need a release store to publish the result of
1635-
// initialization
1700+
// initialization.
16361701
else
1637-
asFullMetadata(this)->Unique.store(unique, std::memory_order_release);
1702+
asFullMetadata(this)->Cache.store(newCache, std::memory_order_release);
16381703
}
16391704

1640-
StoredSize getFlags() const {
1641-
return asFullMetadata(this)->Flags;
1642-
}
1643-
1644-
bool hasInitializationFunction() const {
1645-
return getFlags() & HeaderPrefix::HasInitializationFunction;
1646-
}
1705+
/// Return the initialization function for this metadata record.
1706+
///
1707+
/// As a prerequisite, the metadata record must not have been initialized yet,
1708+
/// and must have an initialization function to begin with, otherwise the
1709+
/// result is undefined.
1710+
InitializationFunction_t *getInitializationFunction() const {
1711+
#ifndef NDEBUG
1712+
auto cache = getCacheValue();
1713+
assert(cache.hasInitializationFunction());
1714+
#endif
16471715

1648-
InitializationFunction_t getInitializationFunction() const {
1649-
assert(hasInitializationFunction());
16501716
return asFullMetadata(this)->InitializationFunction;
16511717
}
16521718
};

lib/IRGen/GenMeta.cpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5001,8 +5001,9 @@ namespace {
50015001
void layout() {
50025002
if (asImpl().requiresInitializationFunction())
50035003
asImpl().addInitializationFunction();
5004+
else
5005+
asImpl().addPaddingForInitializationFunction();
50045006
asImpl().addForeignName();
5005-
asImpl().addUniquePointer();
50065007
asImpl().addForeignFlags();
50075008
super::layout();
50085009
}
@@ -5015,11 +5016,8 @@ namespace {
50155016

50165017
void addForeignName() {
50175018
CanType targetType = asImpl().getTargetType();
5018-
B.add(getMangledTypeName(IGM, targetType));
5019-
}
5020-
5021-
void addUniquePointer() {
5022-
B.addNullPointer(IGM.TypeMetadataPtrTy);
5019+
B.addRelativeAddress(getMangledTypeName(IGM, targetType,
5020+
/*relative addressed?*/ true));
50235021
}
50245022

50255023
void addInitializationFunction() {
@@ -5045,7 +5043,23 @@ namespace {
50455043

50465044
IGF.Builder.CreateRetVoid();
50475045

5048-
B.add(fn);
5046+
B.addRelativeAddress(fn);
5047+
}
5048+
5049+
void addPaddingForInitializationFunction() {
5050+
// The initialization function field is placed at the least offset of the
5051+
// record so it can be omitted when not needed. However, the metadata
5052+
// record is still pointer-aligned, so on 64 bit platforms we need to
5053+
// occupy the space to keep the rest of the record with the right layout.
5054+
switch (IGM.getPointerSize().getValue()) {
5055+
case 4:
5056+
break;
5057+
case 8:
5058+
B.addInt32(0);
5059+
break;
5060+
default:
5061+
llvm_unreachable("unsupported word size");
5062+
}
50495063
}
50505064

50515065
void noteAddressPoint() {

stdlib/public/runtime/Metadata.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2464,14 +2464,15 @@ static Lazy<ForeignTypeState> ForeignTypes;
24642464
const ForeignTypeMetadata *
24652465
swift::swift_getForeignTypeMetadata(ForeignTypeMetadata *nonUnique) {
24662466
// Fast path: check the invasive cache.
2467-
if (auto unique = nonUnique->getCachedUniqueMetadata()) {
2468-
return unique;
2467+
auto cache = nonUnique->getCacheValue();
2468+
if (cache.isInitialized()) {
2469+
return cache.getCachedUniqueMetadata();
24692470
}
24702471

24712472
// Okay, check the global map.
24722473
auto &foreignTypes = ForeignTypes.get();
24732474
GlobalString key(nonUnique->getName());
2474-
bool hasInit = nonUnique->hasInitializationFunction();
2475+
bool hasInit = cache.hasInitializationFunction();
24752476

24762477
const ForeignTypeMetadata *uniqueMetadata;
24772478
bool inserted;

test/IRGen/cf.sil

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,18 @@
77
// CHECK: [[REFRIGERATOR:%TSo14CCRefrigeratorC]] = type
88
// CHECK: [[OBJC:%objc_object]] = type
99

10-
// CHECK: [[REFRIGERATOR_NAME:@.*]] = private unnamed_addr constant [20 x i8] c"So14CCRefrigeratorC\00"
10+
// CHECK: [[REFRIGERATOR_NAME:@.*]] = private constant [20 x i8] c"So14CCRefrigeratorC\00"
1111

12-
// CHECK-32: @_T0So14CCRefrigeratorCN = linkonce_odr hidden global <{ {{.*}} }> <{ i8* getelementptr inbounds ([20 x i8], [20 x i8]* [[REFRIGERATOR_NAME]], i32 0, i32 0), [[TYPE]]* null, i32 0, i8** @_T0BOWV, i32 16, [[TYPE]]* null, i8* null, i8* null, i8* null }>
12+
// CHECK-32: @_T0So14CCRefrigeratorCN = linkonce_odr hidden global <{ {{.*}} }> <{
13+
// CHECK-32-SAME: [[REFRIGERATOR_NAME]] to i32
14+
// CHECK-32-SAME: i32 0,
15+
// CHECK-32-SAME: i8** @_T0BOWV, i32 16, [[TYPE]]* null, i8* null, i8* null, i8* null }>
1316

14-
// CHECK-64: @_T0So14CCRefrigeratorCN = linkonce_odr hidden global <{ {{.*}} }> <{ i8* getelementptr inbounds ([20 x i8], [20 x i8]* [[REFRIGERATOR_NAME]], i64 0, i64 0), [[TYPE]]* null, i64 0, i8** @_T0BOWV, i64 16, [[TYPE]]* null, i8* null, i8* null, i8* null }>
17+
// CHECK-64: @_T0So14CCRefrigeratorCN = linkonce_odr hidden global <{ {{.*}} }> <{
18+
// CHECK-64-SAME: i32 0,
19+
// CHECK-64-SAME: i32 trunc {{.*}} [[REFRIGERATOR_NAME]] to i64
20+
// CHECK-64-SAME: i64 0,
21+
// CHECK-64-SAME: i8** @_T0BOWV, i64 16, [[TYPE]]* null, i8* null, i8* null, i8* null }>
1522

1623
sil_stage canonical
1724

@@ -35,5 +42,5 @@ bb0(%0 : $CCRefrigerator):
3542
// CHECK-NEXT: ret void
3643

3744
// CHECK: define linkonce_odr hidden [[TYPE]]* @_T0So14CCRefrigeratorCMa()
38-
// CHECK-32: call [[TYPE]]* @swift_getForeignTypeMetadata([[TYPE]]* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ({{.*}}* @_T0So14CCRefrigeratorCN to i8*), i32 16) to [[TYPE]]*))
39-
// CHECK-64: call [[TYPE]]* @swift_getForeignTypeMetadata([[TYPE]]* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ({{.*}}* @_T0So14CCRefrigeratorCN to i8*), i64 32) to [[TYPE]]*))
45+
// CHECK-32: call [[TYPE]]* @swift_getForeignTypeMetadata([[TYPE]]* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ({{.*}}* @_T0So14CCRefrigeratorCN to i8*), i32 12) to [[TYPE]]*))
46+
// CHECK-64: call [[TYPE]]* @swift_getForeignTypeMetadata([[TYPE]]* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ({{.*}}* @_T0So14CCRefrigeratorCN to i8*), i64 24) to [[TYPE]]*))

test/IRGen/foreign_types.sil

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ sil_stage canonical
44
import c_layout
55

66
// CHECK-LABEL: @_T0SC14HasNestedUnionV18__Unnamed_struct_sVN = linkonce_odr hidden global
7-
// CHECK-SAME: i8* getelementptr inbounds
8-
// CHECK-SAME: %swift.type* null,
9-
// CHECK-SAME: [[INT:i[0-9]+]] 0,
7+
// CHECK-SAME: [[INT:i[0-9]+]] sub ([[INT]] ptrtoint
8+
// CHECK-SAME: [[INT]] 0,
109
// CHECK-SAME: @_T0SC14HasNestedUnionV18__Unnamed_struct_sVWV
1110
// CHECK-SAME: [[INT]] 1,
1211
// CHECK-SAME: @_T0SC14HasNestedUnionV18__Unnamed_struct_sVMn

0 commit comments

Comments
 (0)