-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[sil] Add some small docs for copy_value, destroy_value, end_borrow, load_borrow #5453
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2166,6 +2166,48 @@ requires ``release`` or other cleanup, that value must be loaded before being | |
stored over and cleaned up. It is undefined behavior to store to an address | ||
that points to deallocated storage. | ||
|
||
load_borrow | ||
``````````` | ||
|
||
:: | ||
|
||
sil-instruction ::= 'load_borrow' sil-value | ||
|
||
%1 = load_borrow %0 : $*T | ||
// $T must be a loadable type | ||
|
||
Loads the value ``%1`` from the memory location ``%0``. The ``load_borrow`` | ||
instruction creates a borrowed scope in which a read-only borrow value ``%1`` | ||
can be used to read the value stored in ``%0``. The end of scope is deliminated | ||
by an ``end_borrow`` instruction. All ``load_borrow`` instructions must be | ||
paired with exactly one ``end_borrow`` instruction along any path through the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the "exactly one" really necessary? I have the feeling that it restricts the optimizer too much. Can't we have multiple end_borrow for a single load_borrow, like we do for alloc_stack-dealloc_stack? loop_head: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Notice how I said exactly one along any path through the program. That is the same condition as alloc-stack/dealloc-stack. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right, I missed that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No worries = ). |
||
program. Until ``end_borrow``, it is illegal to invalidate or store to ``%0``. | ||
|
||
end_borrow | ||
`````````` | ||
|
||
:: | ||
|
||
sil-instruction ::= 'end_borrow' sil-value 'from' sil-value : sil-type, sil-type | ||
|
||
end_borrow %1 from %0 : $T, $T | ||
end_borrow %1 from %0 : $T, $*T | ||
end_borrow %1 from %0 : $*T, $T | ||
end_borrow %1 from %0 : $*T, $*T | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If both operands are addresses, do we require them to be the same address? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea is one could do something like this:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep in mind, that I think we would only do this if we wanted to ensure that all borrowed addresses did not alias. Beyond that I can not see a good reason to have a "borrow" copy like operation that moves the borrowed value to a new memory location (but perhaps I am missing something). I wrote this instruction purposely as generally as possible so that we could restrict it over time as we get a better idea of the exact constraints that we want here. |
||
// We allow for end_borrow to be specified in between values and addresses | ||
// all of the same type T. | ||
|
||
Ends the scope for which the SILValue ``%1`` is borrowed from the SILValue | ||
``%0``. Must be paired with at most 1 borrowing instruction (like | ||
``load_borrow``) along any path through the program. In the region in between | ||
the borrow instruction and the ``end_borrow``, the original SILValue can not be | ||
modified. This means that: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What requirements do we have for the end_borrow operands? %1 = load_borrow %0 : $*T Example 2: %1 = load_borrow %0 : $*T There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I imagine we will have some form of forwarding. Since borrows are in the type system, it should all just work. |
||
|
||
1. If ``%0`` is an address, ``%0`` can not be written to. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently we only have load_borrow to begin a borrowing scope, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No you are not missing anything. I was implementing a very general instruction that could be used for any of the borrowing cases that are interesting to us. |
||
2. If ``%0`` is a non-trivial value, ``%0`` can not be destroyed. | ||
|
||
We require that ``%1`` and ``%0`` have the same type ignoring SILValueCategory. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You shouldn't use the term SILValueCategory, it's an implementation class. Maybe you can use "formal type" instead? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could also have a borrow of an object from an object for instance. Or potentially an address type from an address type. Or even potentially an address from a value (for instance in the case of a re-abstraction thunk). |
||
|
||
assign | ||
`````` | ||
:: | ||
|
@@ -3191,6 +3233,32 @@ For aggregate types, especially enums, it is typically both easier | |
and more efficient to reason about aggregate copies than it is to | ||
reason about copies of the subobjects. | ||
|
||
copy_value | ||
`````````` | ||
|
||
:: | ||
|
||
sil-instruction ::= 'copy_value' sil-operand | ||
|
||
%1 = copy_value %0 : $A | ||
|
||
Performs a copy of a loadable value as if by the value's type lowering and | ||
returns the copy. The returned copy semantically is a value that is completely | ||
independent of the operand. In terms of specific types: | ||
|
||
1. For trivial types, this is equivalent to just propagating through the trivial | ||
value. | ||
2. For reference types, this is equivalent to performing a ``strong_retain`` | ||
operation and returning the reference. | ||
3. For ``@unowned`` types, this is equivalent to performing an | ||
``unowned_retain`` and returning the operand. | ||
4. For aggregate types, this is equivalent to recursively performing a | ||
``copy_value`` on its components, forming a new aggregate from the copied | ||
components, and then returning the new aggregate. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This list is a very good description. It would help if you could write something similar for load_borrow and end_borrow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I think doing such a thing will provide the clarity that you want above. |
||
|
||
In ownership qualified functions, a ``copy_value`` produces a +1 value that must | ||
be consumed at most once along any path through the program. | ||
|
||
release_value | ||
````````````` | ||
|
||
|
@@ -3214,6 +3282,29 @@ For aggregate types, especially enums, it is typically both easier | |
and more efficient to reason about aggregate destroys than it is to | ||
reason about destroys of the subobjects. | ||
|
||
destroy_value | ||
````````````` | ||
|
||
:: | ||
|
||
sil-instruction ::= 'destroy_value' sil-operand | ||
|
||
destroy_value %0 : $A | ||
|
||
Destroys a loadable value, by releasing any retainable pointers within it. | ||
|
||
This is defined to be equivalent to storing the operand into a stack | ||
allocation and using 'destroy_addr' to destroy the object there. | ||
|
||
For trivial types, this is a no-op. For reference types, this is | ||
equivalent to a ``strong_release``. For ``@unowned`` types, this is | ||
equivalent to an ``unowned_release``. In each of these cases, those | ||
are the preferred forms. | ||
|
||
For aggregate types, especially enums, it is typically both easier | ||
and more efficient to reason about aggregate destroys than it is to | ||
reason about destroys of the subobjects. | ||
|
||
autorelease_value | ||
````````````````` | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should define what's a read-only borrow value is. I assume it's the same as for end_borrow (except %1 instead of %0):
%1
is an address,%1
can not be written to.%1
is a non-trivial value,%1
can not be destroyed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, we can't load an address so 1. is not an option.
Somehow I'm missing information what a load_borrow actually "does". For example:
For non-trivial types it loads the value without retaining the value.
For trivial types... well, can we use it for trivial types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if I elaborate specifically on what this instruction does for the various categories of instructions (as you requested below), your question will be answered