@@ -1567,86 +1567,152 @@ struct TargetForeignTypeMetadata : public TargetMetadata<Runtime> {
1567
1567
using StoredPointer = typename Runtime::StoredPointer;
1568
1568
using StoredSize = typename Runtime::StoredSize;
1569
1569
using InitializationFunction_t =
1570
- void (*)( TargetForeignTypeMetadata<Runtime> *selectedMetadata);
1570
+ void (TargetForeignTypeMetadata<Runtime> *selectedMetadata);
1571
1571
using RuntimeMetadataPointer =
1572
1572
ConstTargetMetadataPointer<Runtime, swift::TargetForeignTypeMetadata>;
1573
1573
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
+
1574
1638
// / Foreign type metadata may have extra header fields depending on
1575
1639
// / the flags.
1576
1640
struct HeaderPrefix {
1577
1641
// / An optional callback performed when a particular metadata object
1578
1642
// / is chosen as the unique structure.
1643
+ // /
1579
1644
// / If there is no initialization function, this metadata record can be
1580
1645
// / 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;
1583
1649
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;
1592
1654
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;
1600
1656
};
1601
1657
1602
1658
struct HeaderType : HeaderPrefix, TypeMetadataHeader {};
1603
1659
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);
1606
1662
1607
1663
TargetPointer<Runtime, const char > getName () const {
1608
1664
return reinterpret_cast <TargetPointer<Runtime, const char >>(
1609
- asFullMetadata (this )->Name );
1665
+ asFullMetadata (this )->Name . get () );
1610
1666
}
1611
1667
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);
1622
1680
}
1623
1681
1624
1682
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);
1629
1694
1630
1695
// 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);
1633
1698
1634
1699
// Otherwise, we need a release store to publish the result of
1635
- // initialization
1700
+ // initialization.
1636
1701
else
1637
- asFullMetadata (this )->Unique .store (unique , std::memory_order_release);
1702
+ asFullMetadata (this )->Cache .store (newCache , std::memory_order_release);
1638
1703
}
1639
1704
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
1647
1715
1648
- InitializationFunction_t getInitializationFunction () const {
1649
- assert (hasInitializationFunction ());
1650
1716
return asFullMetadata (this )->InitializationFunction ;
1651
1717
}
1652
1718
};
0 commit comments