Skip to content

Commit f5a8f60

Browse files
committed
SIL: new instructions for copy-on-write support
* a new [immutable] attribute on ref_element_addr and ref_tail_addr * new instructions: begin_cow_mutation and end_cow_mutation These new instructions are intended to be used for the stdlib's COW containers, e.g. Array. They allow more aggressive optimizations, especially for Array.
1 parent 63167fa commit f5a8f60

24 files changed

+473
-26
lines changed

docs/SIL.rst

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3199,6 +3199,56 @@ strong reference count is greater than 1.
31993199
A discussion of the semantics can be found here:
32003200
:ref:`arcopts.is_unique`.
32013201

3202+
begin_cow_mutation
3203+
``````````````````
3204+
3205+
::
3206+
3207+
sil-instruction ::= 'begin_cow_mutation' '[native]'? sil-operand
3208+
3209+
(%1, %2) = begin_cow_mutation %0 : $C
3210+
// $C must be a reference-counted type
3211+
// %1 will be of type Builtin.Int1
3212+
// %2 will be of type C
3213+
3214+
Checks whether %0 is a unique reference to a memory object. Returns 1 in the
3215+
first result if the strong reference count is 1, and 0 if the strong reference
3216+
count is greater than 1.
3217+
3218+
Returns the reference operand in the second result. The returned reference can
3219+
be used to mutate the object. Technically, the returned reference is the same
3220+
as the operand. But it's important that optimizations see the result as a
3221+
different SSA value than the operand. This is important to ensure the
3222+
correctness of ``ref_element_addr [immutable]``.
3223+
3224+
The operand is consumed and the second result is returned as owned.
3225+
3226+
The optional ``native`` attribute specifies that the operand has native Swift
3227+
reference counting.
3228+
3229+
end_cow_mutation
3230+
````````````````
3231+
3232+
::
3233+
3234+
sil-instruction ::= 'end_cow_mutation' '[keep_unique]'? sil-operand
3235+
3236+
%1 = end_cow_mutation %0 : $C
3237+
// $C must be a reference-counted type
3238+
// %1 will be of type C
3239+
3240+
Marks the end of the mutation of a reference counted object.
3241+
Returns the reference operand. Technically, the returned reference is the same
3242+
as the operand. But it's important that optimizations see the result as a
3243+
different SSA value than the operand. This is important to ensure the
3244+
correctness of ``ref_element_addr [immutable]``.
3245+
3246+
The operand is consumed and the result is returned as owned. The result is
3247+
guaranteed to be uniquely referenced.
3248+
3249+
The optional ``keep_unique`` attribute indicates that the optimizer must not
3250+
replace this reference with a not uniquely reference object.
3251+
32023252
is_escaping_closure
32033253
```````````````````
32043254

@@ -4193,7 +4243,7 @@ ref_element_addr
41934243
````````````````
41944244
::
41954245

4196-
sil-instruction ::= 'ref_element_addr' sil-operand ',' sil-decl-ref
4246+
sil-instruction ::= 'ref_element_addr' '[immutable]'? sil-operand ',' sil-decl-ref
41974247

41984248
%1 = ref_element_addr %0 : $C, #C.field
41994249
// %0 must be a value of class type $C
@@ -4205,11 +4255,15 @@ Given an instance of a class, derives the address of a physical instance
42054255
variable inside the instance. It is undefined behavior if the class value
42064256
is null.
42074257

4258+
The ``immutable`` attribute specifies that all loads of the same instance
4259+
variable from the same class reference operand are guaranteed to yield the
4260+
same value.
4261+
42084262
ref_tail_addr
42094263
`````````````
42104264
::
42114265

4212-
sil-instruction ::= 'ref_tail_addr' sil-operand ',' sil-type
4266+
sil-instruction ::= 'ref_tail_addr' '[immutable]'? sil-operand ',' sil-type
42134267

42144268
%1 = ref_tail_addr %0 : $C, $E
42154269
// %0 must be a value of class type $C with tail-allocated elements $E
@@ -4222,6 +4276,10 @@ object which is created by an ``alloc_ref`` with ``tail_elems``.
42224276
It is undefined behavior if the class instance does not have tail-allocated
42234277
arrays or if the element-types do not match.
42244278

4279+
The ``immutable`` attribute specifies that all loads of the same instance
4280+
variable from the same class reference operand are guaranteed to yield the
4281+
same value.
4282+
42254283
Enums
42264284
~~~~~
42274285

include/swift/SIL/SILBuilder.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,9 +1422,10 @@ class SILBuilder {
14221422
}
14231423

14241424
RefElementAddrInst *createRefElementAddr(SILLocation Loc, SILValue Operand,
1425-
VarDecl *Field, SILType ResultTy) {
1425+
VarDecl *Field, SILType ResultTy,
1426+
bool IsImmutable = false) {
14261427
return insert(new (getModule()) RefElementAddrInst(
1427-
getSILDebugLocation(Loc), Operand, Field, ResultTy));
1428+
getSILDebugLocation(Loc), Operand, Field, ResultTy, IsImmutable));
14281429
}
14291430
RefElementAddrInst *createRefElementAddr(SILLocation Loc, SILValue Operand,
14301431
VarDecl *Field) {
@@ -1434,9 +1435,10 @@ class SILBuilder {
14341435
}
14351436

14361437
RefTailAddrInst *createRefTailAddr(SILLocation Loc, SILValue Ref,
1437-
SILType ResultTy) {
1438+
SILType ResultTy,
1439+
bool IsImmutable = false) {
14381440
return insert(new (getModule()) RefTailAddrInst(getSILDebugLocation(Loc),
1439-
Ref, ResultTy));
1441+
Ref, ResultTy, IsImmutable));
14401442
}
14411443

14421444
DestructureStructInst *createDestructureStruct(SILLocation Loc,
@@ -1722,6 +1724,17 @@ class SILBuilder {
17221724
return insert(new (getModule()) IsUniqueInst(getSILDebugLocation(Loc),
17231725
operand, Int1Ty));
17241726
}
1727+
BeginCOWMutationInst *createBeginCOWMutation(SILLocation Loc,
1728+
SILValue operand, bool isNative) {
1729+
auto Int1Ty = SILType::getBuiltinIntegerType(1, getASTContext());
1730+
return insert(BeginCOWMutationInst::create(getSILDebugLocation(Loc), operand,
1731+
Int1Ty, getFunction(), isNative));
1732+
}
1733+
EndCOWMutationInst *createEndCOWMutation(SILLocation Loc, SILValue operand,
1734+
bool keepUnique = false) {
1735+
return insert(new (getModule()) EndCOWMutationInst(getSILDebugLocation(Loc),
1736+
operand, keepUnique));
1737+
}
17251738
IsEscapingClosureInst *createIsEscapingClosure(SILLocation Loc,
17261739
SILValue operand,
17271740
unsigned VerificationType) {

include/swift/SIL/SILCloner.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,8 +1966,8 @@ SILCloner<ImplClass>::visitRefElementAddrInst(RefElementAddrInst *Inst) {
19661966
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
19671967
recordClonedInstruction(
19681968
Inst, getBuilder().createRefElementAddr(
1969-
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()),
1970-
Inst->getField(), getOpType(Inst->getType())));
1969+
getOpLocation(Inst->getLoc()), getOpValue(Inst->getOperand()),
1970+
Inst->getField(), getOpType(Inst->getType()), Inst->isImmutable()));
19711971
}
19721972

19731973
template<typename ImplClass>
@@ -1977,7 +1977,8 @@ SILCloner<ImplClass>::visitRefTailAddrInst(RefTailAddrInst *Inst) {
19771977
recordClonedInstruction(
19781978
Inst, getBuilder().createRefTailAddr(getOpLocation(Inst->getLoc()),
19791979
getOpValue(Inst->getOperand()),
1980-
getOpType(Inst->getType())));
1980+
getOpType(Inst->getType()),
1981+
Inst->isImmutable()));
19811982
}
19821983

19831984
template <typename ImplClass>
@@ -2370,6 +2371,20 @@ void SILCloner<ImplClass>::visitIsUniqueInst(IsUniqueInst *Inst) {
23702371
Inst, getBuilder().createIsUnique(getOpLocation(Inst->getLoc()),
23712372
getOpValue(Inst->getOperand())));
23722373
}
2374+
template<typename ImplClass>
2375+
void SILCloner<ImplClass>::visitBeginCOWMutationInst(BeginCOWMutationInst *Inst) {
2376+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
2377+
recordClonedInstruction(
2378+
Inst, getBuilder().createBeginCOWMutation(getOpLocation(Inst->getLoc()),
2379+
getOpValue(Inst->getOperand()), Inst->isNative()));
2380+
}
2381+
template<typename ImplClass>
2382+
void SILCloner<ImplClass>::visitEndCOWMutationInst(EndCOWMutationInst *Inst) {
2383+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
2384+
recordClonedInstruction(
2385+
Inst, getBuilder().createEndCOWMutation(getOpLocation(Inst->getLoc()),
2386+
getOpValue(Inst->getOperand()), Inst->doKeepUnique()));
2387+
}
23732388
template <typename ImplClass>
23742389
void SILCloner<ImplClass>::visitIsEscapingClosureInst(
23752390
IsEscapingClosureInst *Inst) {

include/swift/SIL/SILInstruction.h

Lines changed: 131 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5806,11 +5806,24 @@ class RefElementAddrInst
58065806
friend SILBuilder;
58075807

58085808
RefElementAddrInst(SILDebugLocation DebugLoc, SILValue Operand,
5809-
VarDecl *Field, SILType ResultTy)
5810-
: UnaryInstructionBase(DebugLoc, Operand, ResultTy, Field) {}
5809+
VarDecl *Field, SILType ResultTy, bool IsImmutable)
5810+
: UnaryInstructionBase(DebugLoc, Operand, ResultTy, Field) {
5811+
setImmutable(IsImmutable);
5812+
}
58115813

58125814
public:
58135815
ClassDecl *getClassDecl() const { return cast<ClassDecl>(getParentDecl()); }
5816+
5817+
/// Returns true if all loads of the same instance variable from the same
5818+
/// class reference operand are guaranteed to yield the same value.
5819+
bool isImmutable() const {
5820+
return SILInstruction::Bits.RefElementAddrInst.Immutable;
5821+
}
5822+
5823+
/// Sets the immutable flag.
5824+
void setImmutable(bool immutable = true) {
5825+
SILInstruction::Bits.RefElementAddrInst.Immutable = immutable;
5826+
}
58145827
};
58155828

58165829
/// RefTailAddrInst - Derive the address of the first element of the first
@@ -5821,8 +5834,11 @@ class RefTailAddrInst
58215834
{
58225835
friend SILBuilder;
58235836

5824-
RefTailAddrInst(SILDebugLocation DebugLoc, SILValue Operand, SILType ResultTy)
5825-
: UnaryInstructionBase(DebugLoc, Operand, ResultTy) {}
5837+
RefTailAddrInst(SILDebugLocation DebugLoc, SILValue Operand, SILType ResultTy,
5838+
bool IsImmutable)
5839+
: UnaryInstructionBase(DebugLoc, Operand, ResultTy) {
5840+
setImmutable(IsImmutable);
5841+
}
58265842

58275843
public:
58285844
ClassDecl *getClassDecl() const {
@@ -5832,6 +5848,17 @@ class RefTailAddrInst
58325848
}
58335849

58345850
SILType getTailType() const { return getType().getObjectType(); }
5851+
5852+
/// Returns true if all loads of the same instance variable from the same
5853+
/// class reference operand are guaranteed to yield the same value.
5854+
bool isImmutable() const {
5855+
return SILInstruction::Bits.RefTailAddrInst.Immutable;
5856+
}
5857+
5858+
/// Sets the immutable flag.
5859+
void setImmutable(bool immutable = true) {
5860+
SILInstruction::Bits.RefTailAddrInst.Immutable = immutable;
5861+
}
58355862
};
58365863

58375864
/// MethodInst - Abstract base for instructions that implement dynamic
@@ -6562,6 +6589,106 @@ class IsUniqueInst
65626589
: UnaryInstructionBase(DebugLoc, Operand, BoolTy) {}
65636590
};
65646591

6592+
class BeginCOWMutationInst;
6593+
6594+
/// A result for the begin_cow_mutation instruction. See documentation for
6595+
/// begin_cow_mutation for more information.
6596+
class BeginCOWMutationResult final : public MultipleValueInstructionResult {
6597+
public:
6598+
BeginCOWMutationResult(unsigned index, SILType type,
6599+
ValueOwnershipKind ownershipKind)
6600+
: MultipleValueInstructionResult(ValueKind::BeginCOWMutationResult,
6601+
index, type, ownershipKind) {}
6602+
6603+
BeginCOWMutationInst *getParent(); // inline below
6604+
const BeginCOWMutationInst *getParent() const {
6605+
return const_cast<BeginCOWMutationResult *>(this)->getParent();
6606+
}
6607+
6608+
static bool classof(const SILNode *N) {
6609+
return N->getKind() == SILNodeKind::BeginCOWMutationResult;
6610+
}
6611+
};
6612+
6613+
/// Performs a uniqueness check of the operand for the purpose of modifying
6614+
/// a copy-on-write object.
6615+
///
6616+
/// Returns two results: the first result is an Int1 which is the result of the
6617+
/// uniqueness check. The second result is the class reference operand, which
6618+
/// can be used for mutation.
6619+
class BeginCOWMutationInst final
6620+
: public UnaryInstructionBase<SILInstructionKind::BeginCOWMutationInst,
6621+
MultipleValueInstruction>,
6622+
public MultipleValueInstructionTrailingObjects<
6623+
BeginCOWMutationInst, BeginCOWMutationResult>
6624+
{
6625+
friend SILBuilder;
6626+
friend TrailingObjects;
6627+
6628+
bool native;
6629+
6630+
BeginCOWMutationInst(SILDebugLocation loc, SILValue operand,
6631+
ArrayRef<SILType> resultTypes,
6632+
ArrayRef<ValueOwnershipKind> resultOwnerships,
6633+
bool isNative);
6634+
6635+
static BeginCOWMutationInst *
6636+
create(SILDebugLocation loc, SILValue operand, SILType BoolTy, SILFunction &F,
6637+
bool isNative);
6638+
6639+
public:
6640+
using MultipleValueInstructionTrailingObjects::totalSizeToAlloc;
6641+
6642+
/// Returns the result of the uniqueness check.
6643+
SILValue getUniquenessResult() const {
6644+
return &getAllResultsBuffer()[0];
6645+
}
6646+
6647+
/// Returns the class reference which can be used for mutation.
6648+
SILValue getBufferResult() const {
6649+
return &getAllResultsBuffer()[1];
6650+
}
6651+
6652+
bool isNative() const {
6653+
return SILInstruction::Bits.BeginCOWMutationInst.Native;
6654+
}
6655+
6656+
void setNative(bool native = true) {
6657+
SILInstruction::Bits.BeginCOWMutationInst.Native = native;
6658+
}
6659+
};
6660+
6661+
// Out of line to work around forward declaration issues.
6662+
inline BeginCOWMutationInst *BeginCOWMutationResult::getParent() {
6663+
auto *Parent = MultipleValueInstructionResult::getParent();
6664+
return cast<BeginCOWMutationInst>(Parent);
6665+
}
6666+
6667+
/// Marks the end of the mutation of a reference counted object.
6668+
class EndCOWMutationInst
6669+
: public UnaryInstructionBase<SILInstructionKind::EndCOWMutationInst,
6670+
SingleValueInstruction>
6671+
{
6672+
friend SILBuilder;
6673+
6674+
bool keepUnique;
6675+
6676+
EndCOWMutationInst(SILDebugLocation DebugLoc, SILValue Operand,
6677+
bool keepUnique)
6678+
: UnaryInstructionBase(DebugLoc, Operand, Operand->getType()) {
6679+
setKeepUnique(keepUnique);
6680+
}
6681+
6682+
public:
6683+
bool doKeepUnique() const {
6684+
return SILInstruction::Bits.EndCOWMutationInst.KeepUnique;
6685+
}
6686+
6687+
void setKeepUnique(bool keepUnique = true) {
6688+
SILInstruction::Bits.EndCOWMutationInst.KeepUnique = keepUnique;
6689+
}
6690+
};
6691+
65656692
/// Given an escaping closure return true iff it has a non-nil context and the
65666693
/// context has a strong reference count greater than 1.
65676694
class IsEscapingClosureInst

include/swift/SIL/SILNode.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,18 @@ class alignas(8) SILNode {
304304
FieldNo : 32
305305
);
306306

307+
SWIFT_INLINE_BITFIELD(RefElementAddrInst, SingleValueInstruction, 1,
308+
Immutable : 1
309+
);
310+
311+
SWIFT_INLINE_BITFIELD(RefTailAddrInst, SingleValueInstruction, 1,
312+
Immutable : 1
313+
);
314+
315+
SWIFT_INLINE_BITFIELD(EndCOWMutationInst, NonValueInstruction, 1,
316+
KeepUnique : 1
317+
);
318+
307319
SWIFT_INLINE_BITFIELD_FULL(FieldIndexCacheBase, SingleValueInstruction, 32,
308320
: NumPadBits,
309321
FieldIndex : 32);
@@ -355,6 +367,12 @@ class alignas(8) SILNode {
355367
NumCases : 32
356368
);
357369

370+
SWIFT_INLINE_BITFIELD_EMPTY(MultipleValueInstruction, SILInstruction);
371+
372+
SWIFT_INLINE_BITFIELD(BeginCOWMutationInst, MultipleValueInstruction, 1,
373+
Native : 1
374+
);
375+
358376
} Bits;
359377

360378
enum class SILNodeStorageLocation : uint8_t { Value, Instruction };

0 commit comments

Comments
 (0)