Skip to content

Commit 62a997a

Browse files
committed
Documentation.
1 parent dfb7bf2 commit 62a997a

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

docs/assembly.rst

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ of their block is reached.
228228
Conventions in Solidity
229229
-----------------------
230230

231+
.. _assembly-typed-variables:
232+
233+
Values of Typed Variables
234+
=========================
235+
231236
In contrast to EVM assembly, Solidity has types which are narrower than 256 bits,
232237
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that
233238
types can be shorter than 256
@@ -237,6 +242,11 @@ This means that if you access such a variable
237242
from within inline assembly, you might have to manually clean the higher-order bits
238243
first.
239244

245+
.. _assembly-memory-management:
246+
247+
Memory Management
248+
=================
249+
240250
Solidity manages memory in the following way. There is a "free memory pointer"
241251
at position ``0x40`` in memory. If you want to allocate memory, use the memory
242252
starting from where this pointer points at and update it.
@@ -268,3 +278,89 @@ first slot of the array and followed by the array elements.
268278
Statically-sized memory arrays do not have a length field, but it might be added later
269279
to allow better convertibility between statically- and dynamically-sized arrays, so
270280
do not rely on this.
281+
282+
Memory Safety
283+
=============
284+
285+
Without the use of inline assembly, the compiler can rely on memory to remain in a well-defined
286+
state at all times. This is especially relevant for :ref:`the new code generation pipeline via Yul IR <ir-breaking-changes>`:
287+
this code generation path can move local variables from stack to memory to avoid stack-too-deep errors and
288+
perform additional memory optimizations, if it can rely on certain assumptions about memory use.
289+
290+
While we recommend to always respect Solidity's memory model, inline assembly allows you to use memory
291+
in an incompatible way. Therefore, moving stack variables to memory and additional memory optimizations are,
292+
by default, disabled in the presence of any inline assembly block that contains a memory operation or assigns
293+
to solidity variables in memory.
294+
295+
However, you can specifically annotate an assembly block to indicate that it in fact respects Solidity's memory
296+
model as follows:
297+
298+
.. code-block:: solidity
299+
300+
/// @solidity memory-safe-assembly
301+
assembly {
302+
...
303+
}
304+
305+
In particular, a memory-safe assembly block may only access the following memory ranges:
306+
307+
- Memory allocated by yourself using a mechanism like the ``allocate`` function described above.
308+
- Memory allocated by Solidity, e.g. memory within the bounds of a memory array you reference.
309+
- The scratch space between memory offset 0 and 64 mentioned above.
310+
- Temporary memory that is located *after* the value of the free memory pointer at the beginning of the assembly block,
311+
i.e. memory that is "allocated" at the free memory pointer without updating the free memory pointer.
312+
313+
Furthermore, if the assembly block assigns to Solidity variables in memory, you need to assure that accesses to
314+
the Solidity variables only access these memory ranges.
315+
316+
Since this is mainly about the optimizer, these restrictions still need to be followed, even if the assembly block
317+
reverts or terminates. As an example, the following assembly snippet is not memory safe:
318+
319+
.. code-block:: solidity
320+
321+
assembly {
322+
returndatacopy(0, 0, returndatasize())
323+
revert(0, returndatasize())
324+
}
325+
326+
But the following is:
327+
328+
.. code-block:: solidity
329+
330+
/// @solidity memory-safe-assembly
331+
assembly {
332+
let p := mload(0x40)
333+
returndatacopy(p, 0, returndatasize())
334+
revert(p, returndatasize())
335+
}
336+
337+
Note that you do not need to update the free memory pointer if there is no following allocation,
338+
but you can only use memory starting from the current offset given by the free memory pointer.
339+
340+
If the memory operations use a length of zero, it is also fine to just use any offset (not only if it falls into the scratch space):
341+
342+
.. code-block:: solidity
343+
344+
/// @solidity memory-safe-assembly
345+
assembly {
346+
revert(0, 0)
347+
}
348+
349+
Note that not only memory operations in inline assembly itself can be memory-unsafe, but also assignments to
350+
solidity variables of reference type in memory. For example the following is not memory-safe:
351+
352+
.. code-block:: solidity
353+
354+
bytes memory x;
355+
assembly {
356+
x := 0x40
357+
}
358+
x[0x20] = 0x42;
359+
360+
Inline assembly that neither involves any operations that access memory nor assigns to any solidity variables
361+
in memory is automatically considered memory-safe and does not need to be annotated.
362+
363+
.. warning::
364+
It is your responsibility to make sure that the assembly actually satisfies the memory model. If you annotate
365+
an assembly block as memory-safe, but violate one of the memory assumptions, this **will** lead to incorrect and
366+
undefined behaviour that cannot easily be discovered by testing.

docs/ir-breaking-changes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
.. index: ir breaking changes
33
4+
.. _ir-breaking-changes:
5+
46
*********************************
57
Solidity IR-based Codegen Changes
68
*********************************

0 commit comments

Comments
 (0)