Skip to content

Commit 4b3931c

Browse files
authored
Merge pull request #76734 from jckarter/atomic-layout-runtime
Runtime: Set 'is not bitwise borrowable' bit for raw layout types.
2 parents 3803e91 + 6e565b5 commit 4b3931c

File tree

5 files changed

+138
-24
lines changed

5 files changed

+138
-24
lines changed

include/swift/ABI/Metadata.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2275,6 +2275,24 @@ struct TargetExtendedExistentialTypeShape
22752275
if (!hasGeneralizationSignature()) return 0;
22762276
return getGenSigHeader()->getArgumentLayoutSizeInWords();
22772277
}
2278+
2279+
bool isCopyable() const {
2280+
if (!hasGeneralizationSignature()) {
2281+
return true;
2282+
}
2283+
auto *reqts = getGenSigRequirements();
2284+
for (unsigned i = 0, e = getNumGenSigRequirements(); i < e; ++i) {
2285+
auto &reqt = reqts[i];
2286+
if (reqt.getKind() != GenericRequirementKind::InvertedProtocols) {
2287+
continue;
2288+
}
2289+
if (reqt.getInvertedProtocols()
2290+
.contains(InvertibleProtocolKind::Copyable)) {
2291+
return false;
2292+
}
2293+
}
2294+
return true;
2295+
}
22782296
};
22792297
using ExtendedExistentialTypeShape
22802298
= TargetExtendedExistentialTypeShape<InProcess>;

include/swift/ABI/MetadataValues.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,12 @@ enum class RawLayoutFlags : uintptr_t {
15581558

15591559
/// Whether or not this raw layout type was declared 'movesAsLike'.
15601560
MovesAsLike = 0x2,
1561+
1562+
/// Whether this raw layout type is bitwise borrowable.
1563+
///
1564+
/// No raw layout types are yet, but should we change our mind about that in the future,
1565+
/// this flag is here.
1566+
BitwiseBorrowable = 0x4,
15611567
};
15621568
static inline RawLayoutFlags operator|(RawLayoutFlags lhs,
15631569
RawLayoutFlags rhs) {
@@ -1573,6 +1579,9 @@ static inline bool isRawLayoutArray(RawLayoutFlags flags) {
15731579
static inline bool shouldRawLayoutMoveAsLike(RawLayoutFlags flags) {
15741580
return uintptr_t(flags) & uintptr_t(RawLayoutFlags::MovesAsLike);
15751581
}
1582+
static inline bool isRawLayoutBitwiseBorrowable(RawLayoutFlags flags) {
1583+
return uintptr_t(flags) & uintptr_t(RawLayoutFlags::BitwiseBorrowable);
1584+
}
15761585

15771586
namespace SpecialPointerAuthDiscriminators {
15781587
// All of these values are the stable string hash of the corresponding

stdlib/public/runtime/Enum.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,14 +382,15 @@ swift::swift_initEnumMetadataMultiPayload(EnumMetadata *enumType,
382382
const TypeLayout * const *payloadLayouts) {
383383
// Accumulate the layout requirements of the payloads.
384384
size_t payloadSize = 0, alignMask = 0;
385-
bool isPOD = true, isBT = true;
385+
bool isPOD = true, isBT = true, isBB = true;
386386
for (unsigned i = 0; i < numPayloads; ++i) {
387387
const TypeLayout *payloadLayout = payloadLayouts[i];
388388
payloadSize
389389
= std::max(payloadSize, (size_t)payloadLayout->size);
390390
alignMask |= payloadLayout->flags.getAlignmentMask();
391391
isPOD &= payloadLayout->flags.isPOD();
392392
isBT &= payloadLayout->flags.isBitwiseTakable();
393+
isBB &= payloadLayout->flags.isBitwiseBorrowable();
393394
}
394395

395396
// Store the max payload size in the metadata.
@@ -418,6 +419,7 @@ swift::swift_initEnumMetadataMultiPayload(EnumMetadata *enumType,
418419
.withAlignmentMask(alignMask)
419420
.withPOD(isPOD)
420421
.withBitwiseTakable(isBT)
422+
.withBitwiseBorrowable(isBB)
421423
.withEnumWitnesses(true)
422424
.withInlineStorage(ValueWitnessTable::isValueInline(
423425
isBT, totalSize, alignMask + 1)),

stdlib/public/runtime/Metadata.cpp

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,6 +2142,7 @@ static void performBasicLayout(TypeLayout &layout,
21422142
size_t alignMask = layout.flags.getAlignmentMask();
21432143
bool isPOD = layout.flags.isPOD();
21442144
bool isBitwiseTakable = layout.flags.isBitwiseTakable();
2145+
bool isBitwiseBorrowable = layout.flags.isBitwiseBorrowable();
21452146
for (unsigned i = 0; i != numElements; ++i) {
21462147
auto &elt = elements[i];
21472148

@@ -2157,6 +2158,7 @@ static void performBasicLayout(TypeLayout &layout,
21572158
alignMask = std::max(alignMask, eltLayout->flags.getAlignmentMask());
21582159
if (!eltLayout->flags.isPOD()) isPOD = false;
21592160
if (!eltLayout->flags.isBitwiseTakable()) isBitwiseTakable = false;
2161+
if (!eltLayout->flags.isBitwiseBorrowable()) isBitwiseBorrowable = false;
21602162
}
21612163
bool isInline =
21622164
ValueWitnessTable::isValueInline(isBitwiseTakable, size, alignMask + 1);
@@ -2166,6 +2168,7 @@ static void performBasicLayout(TypeLayout &layout,
21662168
.withAlignmentMask(alignMask)
21672169
.withPOD(isPOD)
21682170
.withBitwiseTakable(isBitwiseTakable)
2171+
.withBitwiseBorrowable(isBitwiseBorrowable)
21692172
.withInlineStorage(isInline);
21702173
layout.extraInhabitantCount = 0;
21712174
layout.stride = std::max(size_t(1), roundUpToAlignMask(size, alignMask));
@@ -3050,7 +3053,9 @@ void swift::swift_initRawStructMetadata(StructMetadata *structType,
30503053
vwtable->stride = stride;
30513054
vwtable->flags = ValueWitnessFlags()
30523055
.withAlignmentMask(alignMask)
3053-
.withCopyable(false);
3056+
.withCopyable(false)
3057+
.withBitwiseTakable(true)
3058+
.withBitwiseBorrowable(false);
30543059
vwtable->extraInhabitantCount = extraInhabitantCount;
30553060
}
30563061

@@ -3090,6 +3095,9 @@ void swift::swift_initRawStructMetadata2(StructMetadata *structType,
30903095
vwtable->flags = vwtable->flags
30913096
.withBitwiseTakable(likeTypeLayout->flags.isBitwiseTakable());
30923097
}
3098+
3099+
vwtable->flags = vwtable->flags
3100+
.withBitwiseBorrowable(isRawLayoutBitwiseBorrowable(rawLayoutFlags));
30933101

30943102
// If the calculated size of this raw layout type is available to be put in
30953103
// value buffers, then set the inline storage bit if our like type is also
@@ -4454,27 +4462,44 @@ class ExistentialCacheEntry {
44544462

44554463
class OpaqueExistentialValueWitnessTableCacheEntry {
44564464
public:
4465+
struct Key {
4466+
unsigned numWitnessTables : 31;
4467+
unsigned copyable : 1;
4468+
4469+
bool operator==(struct Key k) {
4470+
return k.numWitnessTables == numWitnessTables
4471+
&& k.copyable == copyable;
4472+
}
4473+
};
4474+
44574475
ValueWitnessTable Data;
44584476

4459-
OpaqueExistentialValueWitnessTableCacheEntry(unsigned numTables);
4477+
OpaqueExistentialValueWitnessTableCacheEntry(Key key);
44604478

44614479
unsigned getNumWitnessTables() const {
44624480
return (Data.size - sizeof(OpaqueExistentialContainer))
44634481
/ sizeof(const WitnessTable *);
44644482
}
4483+
4484+
bool isCopyable() const {
4485+
return Data.flags.isCopyable();
4486+
}
44654487

44664488
intptr_t getKeyIntValueForDump() {
44674489
return getNumWitnessTables();
44684490
}
44694491

4470-
bool matchesKey(unsigned key) const { return key == getNumWitnessTables(); }
4492+
bool matchesKey(Key key) const {
4493+
return key == Key{getNumWitnessTables(), isCopyable()};
4494+
}
44714495

44724496
friend llvm::hash_code
44734497
hash_value(const OpaqueExistentialValueWitnessTableCacheEntry &value) {
4474-
return llvm::hash_value(value.getNumWitnessTables());
4498+
return llvm::hash_value(
4499+
std::make_pair(value.getNumWitnessTables(), value.isCopyable()));
44754500
}
44764501

4477-
static size_t getExtraAllocationSize(unsigned numTables) {
4502+
static size_t getExtraAllocationSize(Key key) {
44784503
return 0;
44794504
}
44804505
size_t getExtraAllocationSize() const {
@@ -4562,19 +4587,24 @@ OpaqueExistentialValueWitnessTables;
45624587
/// Instantiate a value witness table for an opaque existential container with
45634588
/// the given number of witness table pointers.
45644589
static const ValueWitnessTable *
4565-
getOpaqueExistentialValueWitnesses(unsigned numWitnessTables) {
4590+
getOpaqueExistentialValueWitnesses(unsigned numWitnessTables,
4591+
bool copyable) {
45664592
// We pre-allocate a couple of important cases.
4567-
if (numWitnessTables == 0)
4568-
return &OpaqueExistentialValueWitnesses_0;
4569-
if (numWitnessTables == 1)
4570-
return &OpaqueExistentialValueWitnesses_1;
4593+
if (copyable) {
4594+
if (numWitnessTables == 0)
4595+
return &OpaqueExistentialValueWitnesses_0;
4596+
if (numWitnessTables == 1)
4597+
return &OpaqueExistentialValueWitnesses_1;
4598+
}
45714599

4572-
return &OpaqueExistentialValueWitnessTables.getOrInsert(numWitnessTables)
4573-
.first->Data;
4600+
return &OpaqueExistentialValueWitnessTables
4601+
.getOrInsert(OpaqueExistentialValueWitnessTableCacheEntry::Key{
4602+
numWitnessTables, copyable})
4603+
.first->Data;
45744604
}
45754605

45764606
OpaqueExistentialValueWitnessTableCacheEntry::
4577-
OpaqueExistentialValueWitnessTableCacheEntry(unsigned numWitnessTables) {
4607+
OpaqueExistentialValueWitnessTableCacheEntry(Key key) {
45784608
using Box = NonFixedOpaqueExistentialBox;
45794609
using Witnesses = NonFixedValueWitnesses<Box, /*known allocated*/ true>;
45804610

@@ -4584,16 +4614,24 @@ OpaqueExistentialValueWitnessTableCacheEntry(unsigned numWitnessTables) {
45844614
#define DATA_VALUE_WITNESS(LOWER_ID, UPPER_ID, TYPE)
45854615
#include "swift/ABI/ValueWitness.def"
45864616

4587-
Data.size = Box::Container::getSize(numWitnessTables);
4617+
Data.size = Box::Container::getSize(key.numWitnessTables);
45884618
Data.flags = ValueWitnessFlags()
4589-
.withAlignment(Box::Container::getAlignment(numWitnessTables))
4619+
.withAlignment(Box::Container::getAlignment(key.numWitnessTables))
45904620
.withPOD(false)
45914621
.withBitwiseTakable(true)
4592-
.withInlineStorage(false);
4622+
.withInlineStorage(false)
4623+
.withCopyable(key.copyable)
4624+
// Non-bitwise-takable values are always stored out-of-line in existentials,
4625+
// so the existential representation itself is always bitwise-takable.
4626+
// Noncopyable values however can be bitwise-takable without being
4627+
// bitwise-borrowable, so noncopyable existentials are not bitwise-borrowable
4628+
// in the general case.
4629+
.withBitwiseBorrowable(key.copyable);
45934630
Data.extraInhabitantCount = Witnesses::numExtraInhabitants;
4594-
Data.stride = Box::Container::getStride(numWitnessTables);
4631+
Data.stride = Box::Container::getStride(key.numWitnessTables);
45954632

4596-
assert(getNumWitnessTables() == numWitnessTables);
4633+
assert(getNumWitnessTables() == key.numWitnessTables);
4634+
assert(isCopyable() == key.copyable);
45974635
}
45984636

45994637
static const ValueWitnessTable ClassExistentialValueWitnesses_1 =
@@ -4662,7 +4700,8 @@ static const ValueWitnessTable *
46624700
getExistentialValueWitnesses(ProtocolClassConstraint classConstraint,
46634701
const Metadata *superclassConstraint,
46644702
unsigned numWitnessTables,
4665-
SpecialProtocol special) {
4703+
SpecialProtocol special,
4704+
bool copyable) {
46664705
// Use special representation for special protocols.
46674706
switch (special) {
46684707
case SpecialProtocol::Error:
@@ -4685,7 +4724,7 @@ getExistentialValueWitnesses(ProtocolClassConstraint classConstraint,
46854724
numWitnessTables);
46864725
case ProtocolClassConstraint::Any:
46874726
assert(superclassConstraint == nullptr);
4688-
return getOpaqueExistentialValueWitnesses(numWitnessTables);
4727+
return getOpaqueExistentialValueWitnesses(numWitnessTables, copyable);
46894728
}
46904729

46914730
swift_unreachable("Unhandled ProtocolClassConstraint in switch.");
@@ -4928,7 +4967,8 @@ ExistentialCacheEntry::ExistentialCacheEntry(Key key) {
49284967
Data.ValueWitnesses = getExistentialValueWitnesses(key.ClassConstraint,
49294968
key.SuperclassConstraint,
49304969
numWitnessTables,
4931-
special);
4970+
special,
4971+
/*copyable*/ true);
49324972
Data.Flags = ExistentialTypeFlags()
49334973
.withNumWitnessTables(numWitnessTables)
49344974
.withClassConstraint(key.ClassConstraint)
@@ -5164,6 +5204,7 @@ class ExtendedExistentialTypeCacheEntry {
51645204
const ValueWitnessTable *
51655205
ExtendedExistentialTypeCacheEntry::getOrCreateVWT(Key key) {
51665206
auto shape = key.Shape;
5207+
bool copyable = shape->isCopyable();
51675208

51685209
if (auto witnesses = shape->getSuggestedValueWitnesses())
51695210
return witnesses;
@@ -5202,7 +5243,8 @@ ExtendedExistentialTypeCacheEntry::getOrCreateVWT(Key key) {
52025243
return getExistentialValueWitnesses(ProtocolClassConstraint::Any,
52035244
/*superclass*/ nullptr,
52045245
wtableStorageSizeInWords,
5205-
SpecialProtocol::None);
5246+
SpecialProtocol::None,
5247+
copyable);
52065248

52075249
case SpecialKind::ExplicitLayout:
52085250
swift_unreachable("shape with explicit layout but no suggested VWT");
@@ -5214,7 +5256,8 @@ ExtendedExistentialTypeCacheEntry::getOrCreateVWT(Key key) {
52145256
return getExistentialValueWitnesses(ProtocolClassConstraint::Class,
52155257
/*superclass*/ nullptr,
52165258
wtableStorageSizeInWords,
5217-
SpecialProtocol::None);
5259+
SpecialProtocol::None,
5260+
/*copyable*/ true);
52185261

52195262
case SpecialKind::Metatype:
52205263
// Existential metatypes don't store type metadata.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %target-run-simple-swift(-enable-experimental-feature RawLayout)
2+
// REQUIRES: executable_test
3+
// UNSUPPORTED: use_os_stdlib
4+
// UNSUPPORTED: back_deployment_runtime
5+
6+
import Synchronization
7+
8+
struct NC<T>: ~Copyable { var x: T }
9+
10+
@_rawLayout(like: T, movesAsLike)
11+
struct Test<T>: ~Copyable {}
12+
13+
func isBitwiseBorrowable(_ type: any (~Copyable.Type)) -> Bool {
14+
let metadataPtr = unsafeBitCast(type,
15+
to: UnsafePointer<UnsafePointer<UInt>>.self)
16+
let flags = metadataPtr[-1][10]
17+
return flags & 0x0110_0000 == 0
18+
}
19+
20+
func test(_ type: any (~Copyable.Type)) {
21+
print("\(isBitwiseBorrowable(type))")
22+
}
23+
24+
protocol P: ~Copyable {}
25+
26+
// CHECK: begin
27+
print("begin")
28+
29+
// CHECK-NEXT: true
30+
test(Int.self)
31+
// CHECK-NEXT: true
32+
test(Any.self)
33+
// CHECK-NEXT: true
34+
test(P.self)
35+
// CHECK-NEXT: true
36+
test(NC<Int>.self)
37+
// CHECK-NEXT: true
38+
test(NC<Any>.self)
39+
// CHECK-NEXT: false
40+
test(Test<Int>.self)
41+
// CHECK-NEXT: false
42+
test(Test<Any>.self)

0 commit comments

Comments
 (0)