Skip to content

Commit eb25a37

Browse files
authored
Merge pull request #1001 from swiftwasm/master
[pull] swiftwasm from master
2 parents 171e819 + ec0b560 commit eb25a37

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+806
-395
lines changed

docs/SIL.rst

+148-2
Original file line numberDiff line numberDiff line change
@@ -2107,6 +2107,94 @@ make the use of such types more convenient; it does not shift the
21072107
ultimate responsibility for assuring the safety of unsafe
21082108
language/library features away from the user.
21092109

2110+
Copy-on-Write Representation
2111+
----------------------------
2112+
2113+
Copy-on-Write (COW) data structures are implemented by a reference to an object
2114+
which is copied on mutation in case it's not uniquely referenced.
2115+
2116+
A COW mutation sequence in SIL typically looks like::
2117+
2118+
(%uniq, %buffer) = begin_cow_mutation %immutable_buffer : $BufferClass
2119+
cond_br %uniq, bb_uniq, bb_not_unique
2120+
bb_uniq:
2121+
br bb_mutate(%buffer : $BufferClass)
2122+
bb_not_unique:
2123+
%copied_buffer = apply %copy_buffer_function(%buffer) : ...
2124+
br bb_mutate(%copied_buffer : $BufferClass)
2125+
bb_mutate(%mutable_buffer : $BufferClass):
2126+
%field = ref_element_addr %mutable_buffer : $BufferClass, #BufferClass.Field
2127+
store %value to %field : $ValueType
2128+
%new_immutable_buffer = end_cow_mutation %buffer : $BufferClass
2129+
2130+
Loading from a COW data structure looks like::
2131+
2132+
%field1 = ref_element_addr [immutable] %immutable_buffer : $BufferClass, #BufferClass.Field
2133+
%value1 = load %field1 : $*FieldType
2134+
...
2135+
%field2 = ref_element_addr [immutable] %immutable_buffer : $BufferClass, #BufferClass.Field
2136+
%value2 = load %field2 : $*FieldType
2137+
2138+
The ``immutable`` attribute means that loading values from ``ref_element_addr``
2139+
and ``ref_tail_addr`` instructions, which have the *same* operand, are
2140+
equivalent.
2141+
In other words, it's guaranteed that a buffer's properties are not mutated
2142+
between two ``ref_element/tail_addr [immutable]`` as long as they have the
2143+
same buffer reference as operand.
2144+
This is even true if e.g. the buffer 'escapes' to an unknown function.
2145+
2146+
2147+
In the example above, ``%value2`` is equal to ``%value1`` because the operand
2148+
of both ``ref_element_addr`` instructions is the same ``%immutable_buffer``.
2149+
Conceptually, the content of a COW buffer object can be seen as part of
2150+
the same *static* (immutable) SSA value as the buffer reference.
2151+
2152+
The lifetime of a COW value is strictly separated into *mutable* and
2153+
*immutable* regions by ``begin_cow_mutation`` and
2154+
``end_cow_mutation`` instructions::
2155+
2156+
%b1 = alloc_ref $BufferClass
2157+
// The buffer %b1 is mutable
2158+
%b2 = end_cow_mutation %b1 : $BufferClass
2159+
// The buffer %b2 is immutable
2160+
(%u1, %b3) = begin_cow_mutation %b1 : $BufferClass
2161+
// The buffer %b3 is mutable
2162+
%b4 = end_cow_mutation %b3 : $BufferClass
2163+
// The buffer %b4 is immutable
2164+
...
2165+
2166+
Both, ``begin_cow_mutation`` and ``end_cow_mutation``, consume their operand
2167+
and return the new buffer as an *owned* value.
2168+
The ``begin_cow_mutation`` will compile down to a uniqueness check and
2169+
``end_cow_mutation`` will compile to a no-op.
2170+
2171+
Although the physical pointer value of the returned buffer reference is the
2172+
same as the operand, it's important to generate a *new* buffer reference in
2173+
SIL. It prevents the optimizer from moving buffer accesses from a *mutable* into
2174+
a *immutable* region and vice versa.
2175+
2176+
Because the buffer *content* is conceptually part of the
2177+
buffer *reference* SSA value, there must be a new buffer reference every time
2178+
the buffer content is mutated.
2179+
2180+
To illustrate this, let's look at an example, where a COW value is mutated in
2181+
a loop. As with a scalar SSA value, also mutating a COW buffer will enforce a
2182+
phi-argument in the loop header block (for simplicity the code for copying a
2183+
non-unique buffer is not shown)::
2184+
2185+
header_block(%b_phi : $BufferClass):
2186+
(%u, %b_mutate) = begin_cow_mutation %b_phi : $BufferClass
2187+
// Store something to %b_mutate
2188+
%b_immutable = end_cow_mutation %b_mutate : $BufferClass
2189+
cond_br %loop_cond, exit_block, backedge_block
2190+
backedge_block:
2191+
br header_block(b_immutable : $BufferClass)
2192+
exit_block:
2193+
2194+
Two adjacent ``begin_cow_mutation`` and ``end_cow_mutation`` instructions
2195+
don't need to be in the same function.
2196+
2197+
21102198
Instruction Set
21112199
---------------
21122200

@@ -3199,6 +3287,56 @@ strong reference count is greater than 1.
31993287
A discussion of the semantics can be found here:
32003288
:ref:`arcopts.is_unique`.
32013289

3290+
begin_cow_mutation
3291+
``````````````````
3292+
3293+
::
3294+
3295+
sil-instruction ::= 'begin_cow_mutation' '[native]'? sil-operand
3296+
3297+
(%1, %2) = begin_cow_mutation %0 : $C
3298+
// $C must be a reference-counted type
3299+
// %1 will be of type Builtin.Int1
3300+
// %2 will be of type C
3301+
3302+
Checks whether %0 is a unique reference to a memory object. Returns 1 in the
3303+
first result if the strong reference count is 1, and 0 if the strong reference
3304+
count is greater than 1.
3305+
3306+
Returns the reference operand in the second result. The returned reference can
3307+
be used to mutate the object. Technically, the returned reference is the same
3308+
as the operand. But it's important that optimizations see the result as a
3309+
different SSA value than the operand. This is important to ensure the
3310+
correctness of ``ref_element_addr [immutable]``.
3311+
3312+
The operand is consumed and the second result is returned as owned.
3313+
3314+
The optional ``native`` attribute specifies that the operand has native Swift
3315+
reference counting.
3316+
3317+
end_cow_mutation
3318+
````````````````
3319+
3320+
::
3321+
3322+
sil-instruction ::= 'end_cow_mutation' '[keep_unique]'? sil-operand
3323+
3324+
%1 = end_cow_mutation %0 : $C
3325+
// $C must be a reference-counted type
3326+
// %1 will be of type C
3327+
3328+
Marks the end of the mutation of a reference counted object.
3329+
Returns the reference operand. Technically, the returned reference is the same
3330+
as the operand. But it's important that optimizations see the result as a
3331+
different SSA value than the operand. This is important to ensure the
3332+
correctness of ``ref_element_addr [immutable]``.
3333+
3334+
The operand is consumed and the result is returned as owned. The result is
3335+
guaranteed to be uniquely referenced.
3336+
3337+
The optional ``keep_unique`` attribute indicates that the optimizer must not
3338+
replace this reference with a not uniquely reference object.
3339+
32023340
is_escaping_closure
32033341
```````````````````
32043342

@@ -4193,7 +4331,7 @@ ref_element_addr
41934331
````````````````
41944332
::
41954333

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

41984336
%1 = ref_element_addr %0 : $C, #C.field
41994337
// %0 must be a value of class type $C
@@ -4205,11 +4343,15 @@ Given an instance of a class, derives the address of a physical instance
42054343
variable inside the instance. It is undefined behavior if the class value
42064344
is null.
42074345

4346+
The ``immutable`` attribute specifies that all loads of the same instance
4347+
variable from the same class reference operand are guaranteed to yield the
4348+
same value.
4349+
42084350
ref_tail_addr
42094351
`````````````
42104352
::
42114353

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

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

4367+
The ``immutable`` attribute specifies that all loads of the same instance
4368+
variable from the same class reference operand are guaranteed to yield the
4369+
same value.
4370+
42254371
Enums
42264372
~~~~~
42274373

include/swift/AST/Builtins.def

+29
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,29 @@ BUILTIN_SIL_OPERATION(IsUnique, "isUnique", Special)
429429
/// BridgeObject to be treated as a native object by the runtime.
430430
BUILTIN_SIL_OPERATION(IsUnique_native, "isUnique_native", Special)
431431

432+
/// beginCOWMutation<T : AnyObject>(inout T) -> Int1
433+
///
434+
/// Begins a copy-on-write mutation for a buffer reference which is passed as
435+
/// inout argument. It returns a true if the buffer is uniquely referenced.
436+
/// In this case the buffer may be mutated after calling this builtin.
437+
///
438+
/// The beginCOWMutation builtin is very similar to isUnique. It just translates
439+
/// to a different SIL instruction (begin_cow_mutation), which is the preferred
440+
/// representation of COW in SIL.
441+
BUILTIN_SIL_OPERATION(BeginCOWMutation, "beginCOWMutation", Special)
442+
443+
/// beginCOWMutation_native<T : AnyObject>(inout T) -> Int1
444+
///
445+
/// Like beginCOWMutation, but it's assumed that T has native Swift reference
446+
/// counting.
447+
BUILTIN_SIL_OPERATION(BeginCOWMutation_native, "beginCOWMutation_native", Special)
448+
449+
/// endCOWMutation<T : AnyObject>(inout T)
450+
///
451+
/// Ends a copy-on-write mutation for a buffer reference which is passed as
452+
/// inout argument. After calling this builtin, the buffer must not be mutated.
453+
BUILTIN_SIL_OPERATION(EndCOWMutation, "endCOWMutation", Special)
454+
432455
/// bindMemory : <T> (Builtin.RawPointer, Builtin.Word, T.Type) -> ()
433456
BUILTIN_SIL_OPERATION(BindMemory, "bindMemory", Special)
434457

@@ -651,6 +674,12 @@ BUILTIN_MISC_OPERATION(AssignCopyArrayFrontToBack, "assignCopyArrayFrontToBack",
651674
BUILTIN_MISC_OPERATION(AssignCopyArrayBackToFront, "assignCopyArrayBackToFront", "", Special)
652675
BUILTIN_MISC_OPERATION(AssignTakeArray, "assignTakeArray", "", Special)
653676

677+
/// COWBufferForReading has type <T: AnyObject> T -> T
678+
///
679+
/// Returns the buffer reference which is passed as argument.
680+
/// This builtin indicates to the optimizer that the buffer is not mutable.
681+
BUILTIN_MISC_OPERATION(COWBufferForReading, "COWBufferForReading", "n", Special)
682+
654683
// unsafeGuaranteed has type <T: AnyObject> T -> (T, Builtin.Int8)
655684
BUILTIN_MISC_OPERATION(UnsafeGuaranteed, "unsafeGuaranteed", "", Special)
656685

include/swift/AST/Types.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -2696,10 +2696,10 @@ enum class FunctionTypeRepresentation : uint8_t {
26962696
/// A "thin" function that needs no context.
26972697
Thin,
26982698

2699-
/// A C function pointer (or reference), which is thin and also uses the C
2700-
/// calling convention.
2699+
/// A C function pointer, which is thin and also uses the C calling
2700+
/// convention.
27012701
CFunctionPointer,
2702-
2702+
27032703
/// The value of the greatest AST function representation.
27042704
Last = CFunctionPointer,
27052705
};
@@ -2980,8 +2980,8 @@ class AnyFunctionType : public TypeBase {
29802980
// We preserve a full clang::Type *, not a clang::FunctionType * as:
29812981
// 1. We need to keep sugar in case we need to present an error to the user.
29822982
// 2. The actual type being stored is [ignoring sugar] either a
2983-
// clang::PointerType, a clang::BlockPointerType, or a
2984-
// clang::ReferenceType which points to a clang::FunctionType.
2983+
// clang::PointerType or a clang::BlockPointerType which points to a
2984+
// clang::FunctionType.
29852985
const clang::Type *ClangFunctionType;
29862986

29872987
bool empty() const { return !ClangFunctionType; }

include/swift/SIL/SILBuilder.h

+17-4
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

+18-3
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) {

0 commit comments

Comments
 (0)