2017-06-21 19:46:42 +00:00
|
|
|
.. _inline-assembly:
|
|
|
|
|
2020-01-09 17:35:22 +00:00
|
|
|
###############
|
2016-11-05 15:58:06 +00:00
|
|
|
Inline Assembly
|
2020-01-09 17:35:22 +00:00
|
|
|
###############
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2020-01-09 17:35:22 +00:00
|
|
|
.. index:: ! assembly, ! asm, ! evmasm
|
2018-09-17 09:42:31 +00:00
|
|
|
|
|
|
|
|
2020-01-09 17:35:22 +00:00
|
|
|
You can interleave Solidity statements with inline assembly in a language close
|
2022-07-23 15:51:28 +00:00
|
|
|
to the one of the Ethereum Virtual Machine. This gives you more fine-grained control,
|
2020-01-09 17:35:22 +00:00
|
|
|
which is especially useful when you are enhancing the language by writing libraries.
|
2019-12-16 15:26:32 +00:00
|
|
|
|
2020-03-09 08:42:03 +00:00
|
|
|
The language used for inline assembly in Solidity is called :ref:`Yul <yul>`
|
2020-01-09 17:35:22 +00:00
|
|
|
and it is documented in its own section. This section will only cover
|
|
|
|
how the inline assembly code can interface with the surrounding Solidity code.
|
2016-11-05 15:58:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
.. warning::
|
2017-01-31 22:31:25 +00:00
|
|
|
Inline assembly is a way to access the Ethereum Virtual Machine
|
2018-09-17 09:42:31 +00:00
|
|
|
at a low level. This bypasses several important safety
|
|
|
|
features and checks of Solidity. You should only use it for
|
|
|
|
tasks that need it, and only if you are confident with using it.
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2018-09-17 09:42:31 +00:00
|
|
|
|
2020-01-09 17:35:22 +00:00
|
|
|
An inline assembly block is marked by ``assembly { ... }``, where the code inside
|
2020-03-09 08:42:03 +00:00
|
|
|
the curly braces is code in the :ref:`Yul <yul>` language.
|
2018-09-17 09:42:31 +00:00
|
|
|
|
2020-01-09 17:35:22 +00:00
|
|
|
The inline assembly code can access local Solidity variables as explained below.
|
2018-09-17 09:42:31 +00:00
|
|
|
|
2020-01-09 17:35:22 +00:00
|
|
|
Different inline assembly blocks share no namespace, i.e. it is not possible
|
|
|
|
to call a Yul function or access a Yul variable defined in a different inline assembly block.
|
2017-04-03 17:17:17 +00:00
|
|
|
|
2016-11-05 15:58:06 +00:00
|
|
|
Example
|
|
|
|
-------
|
|
|
|
|
|
|
|
The following example provides library code to access the code of another contract and
|
2021-09-04 19:38:23 +00:00
|
|
|
load it into a ``bytes`` variable. This is possible with "plain Solidity" too, by using
|
|
|
|
``<address>.code``. But the point here is that reusable assembly libraries can enhance the
|
|
|
|
Solidity language without a compiler change.
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2021-06-25 10:25:29 +00:00
|
|
|
.. code-block:: solidity
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2020-05-13 15:45:58 +00:00
|
|
|
// SPDX-License-Identifier: GPL-3.0
|
2020-09-08 08:48:04 +00:00
|
|
|
pragma solidity >=0.4.16 <0.9.0;
|
2017-01-31 22:31:25 +00:00
|
|
|
|
2016-11-05 15:58:06 +00:00
|
|
|
library GetCode {
|
2022-02-16 02:59:49 +00:00
|
|
|
function at(address addr) public view returns (bytes memory code) {
|
2016-11-05 15:58:06 +00:00
|
|
|
assembly {
|
|
|
|
// retrieve the size of the code, this needs assembly
|
2022-02-16 02:59:49 +00:00
|
|
|
let size := extcodesize(addr)
|
2016-11-05 15:58:06 +00:00
|
|
|
// allocate output byte array - this could also be done without assembly
|
2022-02-16 03:24:44 +00:00
|
|
|
// by using code = new bytes(size)
|
|
|
|
code := mload(0x40)
|
2016-11-05 15:58:06 +00:00
|
|
|
// new "memory end" including padding
|
2022-02-16 03:24:44 +00:00
|
|
|
mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
|
2016-11-05 15:58:06 +00:00
|
|
|
// store length in memory
|
2022-02-16 03:24:44 +00:00
|
|
|
mstore(code, size)
|
2016-11-05 15:58:06 +00:00
|
|
|
// actually retrieve the code, this needs assembly
|
2022-02-16 02:59:49 +00:00
|
|
|
extcodecopy(addr, add(code, 0x20), 0, size)
|
2016-11-05 15:58:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-17 09:42:31 +00:00
|
|
|
Inline assembly is also beneficial in cases where the optimizer fails to produce
|
|
|
|
efficient code, for example:
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2021-06-25 10:25:29 +00:00
|
|
|
.. code-block:: solidity
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2020-05-13 15:45:58 +00:00
|
|
|
// SPDX-License-Identifier: GPL-3.0
|
2020-09-08 08:48:04 +00:00
|
|
|
pragma solidity >=0.4.16 <0.9.0;
|
2017-01-31 22:31:25 +00:00
|
|
|
|
2019-05-23 12:51:16 +00:00
|
|
|
|
2016-11-05 15:58:06 +00:00
|
|
|
library VectorSum {
|
|
|
|
// This function is less efficient because the optimizer currently fails to
|
|
|
|
// remove the bounds checks in array access.
|
2022-02-16 02:59:49 +00:00
|
|
|
function sumSolidity(uint[] memory data) public pure returns (uint sum) {
|
|
|
|
for (uint i = 0; i < data.length; ++i)
|
|
|
|
sum += data[i];
|
2016-11-05 15:58:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// We know that we only access the array in bounds, so we can avoid the check.
|
|
|
|
// 0x20 needs to be added to an array because the first slot contains the
|
|
|
|
// array length.
|
2022-02-16 02:59:49 +00:00
|
|
|
function sumAsm(uint[] memory data) public pure returns (uint sum) {
|
|
|
|
for (uint i = 0; i < data.length; ++i) {
|
2016-11-05 15:58:06 +00:00
|
|
|
assembly {
|
2022-02-16 02:59:49 +00:00
|
|
|
sum := add(sum, mload(add(add(data, 0x20), mul(i, 0x20))))
|
2016-11-05 15:58:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-07-26 14:01:17 +00:00
|
|
|
|
|
|
|
// Same as above, but accomplish the entire code within inline assembly.
|
2022-02-16 02:59:49 +00:00
|
|
|
function sumPureAsm(uint[] memory data) public pure returns (uint sum) {
|
2017-07-26 14:01:17 +00:00
|
|
|
assembly {
|
2019-05-23 12:51:16 +00:00
|
|
|
// Load the length (first 32 bytes)
|
2022-02-16 02:59:49 +00:00
|
|
|
let len := mload(data)
|
2019-05-23 12:51:16 +00:00
|
|
|
|
|
|
|
// Skip over the length field.
|
|
|
|
//
|
|
|
|
// Keep temporary variable so it can be incremented in place.
|
|
|
|
//
|
2022-02-16 02:59:49 +00:00
|
|
|
// NOTE: incrementing data would result in an unusable
|
|
|
|
// data variable after this assembly block
|
|
|
|
let dataElementLocation := add(data, 0x20)
|
2019-05-23 12:51:16 +00:00
|
|
|
|
|
|
|
// Iterate until the bound is not met.
|
|
|
|
for
|
2022-02-16 02:59:49 +00:00
|
|
|
{ let end := add(dataElementLocation, mul(len, 0x20)) }
|
|
|
|
lt(dataElementLocation, end)
|
2022-06-17 09:48:48 +00:00
|
|
|
{ dataElementLocation := add(dataElementLocation, 0x20) }
|
2019-05-23 12:51:16 +00:00
|
|
|
{
|
2022-02-16 02:59:49 +00:00
|
|
|
sum := add(sum, mload(dataElementLocation))
|
2019-05-23 12:51:16 +00:00
|
|
|
}
|
2017-07-26 14:01:17 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-05 15:58:06 +00:00
|
|
|
}
|
|
|
|
|
2022-06-14 08:37:03 +00:00
|
|
|
.. index:: selector; of a function
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2018-09-17 09:42:31 +00:00
|
|
|
Access to External Variables, Functions and Libraries
|
|
|
|
-----------------------------------------------------
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2018-09-17 09:42:31 +00:00
|
|
|
You can access Solidity variables and other identifiers by using their name.
|
2020-01-09 17:35:22 +00:00
|
|
|
|
|
|
|
Local variables of value type are directly usable in inline assembly.
|
2021-09-13 12:00:24 +00:00
|
|
|
They can both be read and assigned to.
|
2020-01-09 17:35:22 +00:00
|
|
|
|
2022-07-23 15:51:28 +00:00
|
|
|
Local variables that refer to memory evaluate to the address of the variable in memory, not the value itself.
|
2021-09-13 12:00:24 +00:00
|
|
|
Such variables can also be assigned to, but note that an assignment will only change the pointer and not the data
|
|
|
|
and that it is your responsibility to respect Solidity's memory management.
|
|
|
|
See :ref:`Conventions in Solidity <conventions-in-solidity>`.
|
|
|
|
|
|
|
|
Similarly, local variables that refer to statically-sized calldata arrays or calldata structs
|
|
|
|
evaluate to the address of the variable in calldata, not the value itself.
|
2022-07-23 15:51:28 +00:00
|
|
|
The variable can also be assigned a new offset, but note that no validation is performed to ensure that
|
|
|
|
the variable will not point beyond ``calldatasize()``.
|
2021-09-13 12:00:24 +00:00
|
|
|
|
2021-09-22 10:06:57 +00:00
|
|
|
For external function pointers the address and the function selector can be
|
|
|
|
accessed using ``x.address`` and ``x.selector``.
|
|
|
|
The selector consists of four right-aligned bytes.
|
2022-02-16 23:28:34 +00:00
|
|
|
Both values can be assigned to. For example:
|
2021-09-22 10:06:57 +00:00
|
|
|
|
|
|
|
.. code-block:: solidity
|
|
|
|
:force:
|
|
|
|
|
|
|
|
// SPDX-License-Identifier: GPL-3.0
|
|
|
|
pragma solidity >=0.8.10 <0.9.0;
|
|
|
|
|
|
|
|
contract C {
|
|
|
|
// Assigns a new selector and address to the return variable @fun
|
|
|
|
function combineToFunctionPointer(address newAddress, uint newSelector) public pure returns (function() external fun) {
|
|
|
|
assembly {
|
|
|
|
fun.selector := newSelector
|
|
|
|
fun.address := newAddress
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-13 12:00:24 +00:00
|
|
|
For dynamic calldata arrays, you can access
|
|
|
|
their calldata offset (in bytes) and length (number of elements) using ``x.offset`` and ``x.length``.
|
|
|
|
Both expressions can also be assigned to, but as for the static case, no validation will be performed
|
|
|
|
to ensure that the resulting data area is within the bounds of ``calldatasize()``.
|
2020-01-09 17:35:22 +00:00
|
|
|
|
|
|
|
For local storage variables or state variables, a single Yul identifier
|
|
|
|
is not sufficient, since they do not necessarily occupy a single full storage slot.
|
|
|
|
Therefore, their "address" is composed of a slot and a byte-offset
|
2017-04-26 09:58:36 +00:00
|
|
|
inside that slot. To retrieve the slot pointed to by the variable ``x``, you
|
2020-07-02 13:25:11 +00:00
|
|
|
use ``x.slot``, and to retrieve the byte-offset you use ``x.offset``.
|
|
|
|
Using ``x`` itself will result in an error.
|
2017-04-26 09:58:36 +00:00
|
|
|
|
2021-09-13 12:00:24 +00:00
|
|
|
You can also assign to the ``.slot`` part of a local storage variable pointer.
|
|
|
|
For these (structs, arrays or mappings), the ``.offset`` part is always zero.
|
|
|
|
It is not possible to assign to the ``.slot`` or ``.offset`` part of a state variable,
|
|
|
|
though.
|
2020-11-05 13:39:39 +00:00
|
|
|
|
2018-09-17 09:42:31 +00:00
|
|
|
Local Solidity variables are available for assignments, for example:
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2021-05-20 19:19:33 +00:00
|
|
|
.. code-block:: solidity
|
|
|
|
:force:
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2020-05-13 15:45:58 +00:00
|
|
|
// SPDX-License-Identifier: GPL-3.0
|
2020-09-29 07:53:50 +00:00
|
|
|
pragma solidity >=0.7.0 <0.9.0;
|
2017-01-31 22:31:25 +00:00
|
|
|
|
2016-11-05 15:58:06 +00:00
|
|
|
contract C {
|
|
|
|
uint b;
|
2018-08-09 13:36:00 +00:00
|
|
|
function f(uint x) public view returns (uint r) {
|
2016-11-05 15:58:06 +00:00
|
|
|
assembly {
|
2020-01-09 17:35:22 +00:00
|
|
|
// We ignore the storage slot offset, we know it is zero
|
|
|
|
// in this special case.
|
2020-07-02 13:25:11 +00:00
|
|
|
r := mul(x, sload(b.slot))
|
2016-11-05 15:58:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-06 19:17:08 +00:00
|
|
|
.. warning::
|
|
|
|
If you access variables of a type that spans less than 256 bits
|
2020-12-14 15:10:00 +00:00
|
|
|
(for example ``uint64``, ``address``, or ``bytes16``),
|
2019-06-06 19:17:08 +00:00
|
|
|
you cannot make any assumptions about bits not part of the
|
|
|
|
encoding of the type. Especially, do not assume them to be zero.
|
|
|
|
To be safe, always clear the data properly before you use it
|
|
|
|
in a context where this is important:
|
|
|
|
``uint32 x = f(); assembly { x := and(x, 0xffffffff) /* now use x */ }``
|
|
|
|
To clean signed types, you can use the ``signextend`` opcode:
|
2019-06-17 11:39:20 +00:00
|
|
|
``assembly { signextend(<num_bytes_of_x_minus_one>, x) }``
|
2019-06-06 19:17:08 +00:00
|
|
|
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2022-07-23 15:51:28 +00:00
|
|
|
Since Solidity 0.6.0, the name of a inline assembly variable may not
|
2020-07-02 13:25:11 +00:00
|
|
|
shadow any declaration visible in the scope of the inline assembly block
|
|
|
|
(including variable, contract and function declarations).
|
2019-11-07 13:43:51 +00:00
|
|
|
|
2020-07-02 13:25:11 +00:00
|
|
|
Since Solidity 0.7.0, variables and functions declared inside the
|
|
|
|
inline assembly block may not contain ``.``, but using ``.`` is
|
|
|
|
valid to access Solidity variables from outside the inline assembly block.
|
2016-11-05 15:58:06 +00:00
|
|
|
|
|
|
|
Things to Avoid
|
|
|
|
---------------
|
|
|
|
|
|
|
|
Inline assembly might have a quite high-level look, but it actually is extremely
|
2017-11-21 12:36:41 +00:00
|
|
|
low-level. Function calls, loops, ifs and switches are converted by simple
|
2017-01-04 11:34:44 +00:00
|
|
|
rewriting rules and after that, the only thing the assembler does for you is re-arranging
|
2018-09-20 20:41:14 +00:00
|
|
|
functional-style opcodes, counting stack height for
|
2016-11-05 15:58:06 +00:00
|
|
|
variable access and removing stack slots for assembly-local variables when the end
|
2018-09-20 20:41:14 +00:00
|
|
|
of their block is reached.
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2021-09-13 12:00:24 +00:00
|
|
|
.. _conventions-in-solidity:
|
|
|
|
|
2016-11-05 15:58:06 +00:00
|
|
|
Conventions in Solidity
|
|
|
|
-----------------------
|
|
|
|
|
2022-02-03 15:08:32 +00:00
|
|
|
.. _assembly-typed-variables:
|
|
|
|
|
|
|
|
Values of Typed Variables
|
|
|
|
=========================
|
|
|
|
|
2019-06-17 11:01:12 +00:00
|
|
|
In contrast to EVM assembly, Solidity has types which are narrower than 256 bits,
|
2020-01-09 17:35:22 +00:00
|
|
|
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that
|
|
|
|
types can be shorter than 256
|
2019-06-17 11:01:12 +00:00
|
|
|
bits, and the higher-order bits are cleaned when necessary,
|
|
|
|
i.e., shortly before they are written to memory or before comparisons are performed.
|
|
|
|
This means that if you access such a variable
|
|
|
|
from within inline assembly, you might have to manually clean the higher-order bits
|
2016-11-05 15:58:06 +00:00
|
|
|
first.
|
|
|
|
|
2022-02-03 15:08:32 +00:00
|
|
|
.. _assembly-memory-management:
|
|
|
|
|
|
|
|
Memory Management
|
|
|
|
=================
|
|
|
|
|
2019-06-17 11:01:12 +00:00
|
|
|
Solidity manages memory in the following way. There is a "free memory pointer"
|
|
|
|
at position ``0x40`` in memory. If you want to allocate memory, use the memory
|
|
|
|
starting from where this pointer points at and update it.
|
2018-09-20 20:41:14 +00:00
|
|
|
There is no guarantee that the memory has not been used before and thus
|
|
|
|
you cannot assume that its contents are zero bytes.
|
2018-08-07 10:26:28 +00:00
|
|
|
There is no built-in mechanism to release or free allocated memory.
|
2022-07-23 15:51:28 +00:00
|
|
|
Here is an assembly snippet you can use for allocating memory that follows the process outlined above:
|
2021-06-25 10:25:29 +00:00
|
|
|
|
|
|
|
.. code-block:: yul
|
2018-08-07 10:26:28 +00:00
|
|
|
|
|
|
|
function allocate(length) -> pos {
|
|
|
|
pos := mload(0x40)
|
|
|
|
mstore(0x40, add(pos, length))
|
|
|
|
}
|
2016-11-05 15:58:06 +00:00
|
|
|
|
2018-03-13 14:21:38 +00:00
|
|
|
The first 64 bytes of memory can be used as "scratch space" for short-term
|
2019-06-17 11:01:12 +00:00
|
|
|
allocation. The 32 bytes after the free memory pointer (i.e., starting at ``0x60``)
|
|
|
|
are meant to be zero permanently and is used as the initial value for
|
2018-03-13 14:21:38 +00:00
|
|
|
empty dynamic memory arrays.
|
2018-08-07 10:26:28 +00:00
|
|
|
This means that the allocatable memory starts at ``0x80``, which is the initial value
|
|
|
|
of the free memory pointer.
|
2018-03-13 14:21:38 +00:00
|
|
|
|
2019-06-17 11:01:12 +00:00
|
|
|
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (this is
|
2021-07-08 11:04:00 +00:00
|
|
|
even true for ``bytes1[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory
|
2016-11-05 15:58:06 +00:00
|
|
|
arrays are pointers to memory arrays. The length of a dynamic array is stored at the
|
2018-09-20 20:41:14 +00:00
|
|
|
first slot of the array and followed by the array elements.
|
2016-11-05 15:58:06 +00:00
|
|
|
|
|
|
|
.. warning::
|
2018-09-20 20:41:14 +00:00
|
|
|
Statically-sized memory arrays do not have a length field, but it might be added later
|
2022-07-23 15:51:28 +00:00
|
|
|
to allow better convertibility between statically and dynamically-sized arrays; so,
|
2019-06-17 11:01:12 +00:00
|
|
|
do not rely on this.
|
2022-02-03 15:08:32 +00:00
|
|
|
|
|
|
|
Memory Safety
|
|
|
|
=============
|
|
|
|
|
|
|
|
Without the use of inline assembly, the compiler can rely on memory to remain in a well-defined
|
|
|
|
state at all times. This is especially relevant for :ref:`the new code generation pipeline via Yul IR <ir-breaking-changes>`:
|
|
|
|
this code generation path can move local variables from stack to memory to avoid stack-too-deep errors and
|
|
|
|
perform additional memory optimizations, if it can rely on certain assumptions about memory use.
|
|
|
|
|
|
|
|
While we recommend to always respect Solidity's memory model, inline assembly allows you to use memory
|
|
|
|
in an incompatible way. Therefore, moving stack variables to memory and additional memory optimizations are,
|
2022-08-28 22:26:34 +00:00
|
|
|
by default, globally disabled in the presence of any inline assembly block that contains a memory operation
|
|
|
|
or assigns to Solidity variables in memory.
|
2022-02-03 15:08:32 +00:00
|
|
|
|
|
|
|
However, you can specifically annotate an assembly block to indicate that it in fact respects Solidity's memory
|
|
|
|
model as follows:
|
|
|
|
|
|
|
|
.. code-block:: solidity
|
|
|
|
|
2022-02-14 12:23:50 +00:00
|
|
|
assembly ("memory-safe") {
|
2022-02-03 15:08:32 +00:00
|
|
|
...
|
|
|
|
}
|
|
|
|
|
|
|
|
In particular, a memory-safe assembly block may only access the following memory ranges:
|
|
|
|
|
|
|
|
- Memory allocated by yourself using a mechanism like the ``allocate`` function described above.
|
|
|
|
- Memory allocated by Solidity, e.g. memory within the bounds of a memory array you reference.
|
|
|
|
- The scratch space between memory offset 0 and 64 mentioned above.
|
|
|
|
- Temporary memory that is located *after* the value of the free memory pointer at the beginning of the assembly block,
|
|
|
|
i.e. memory that is "allocated" at the free memory pointer without updating the free memory pointer.
|
|
|
|
|
|
|
|
Furthermore, if the assembly block assigns to Solidity variables in memory, you need to assure that accesses to
|
|
|
|
the Solidity variables only access these memory ranges.
|
|
|
|
|
|
|
|
Since this is mainly about the optimizer, these restrictions still need to be followed, even if the assembly block
|
2022-03-29 20:48:49 +00:00
|
|
|
reverts or terminates. As an example, the following assembly snippet is not memory safe, because the value of
|
|
|
|
``returndatasize()`` may exceed the 64 byte scratch space:
|
2022-02-03 15:08:32 +00:00
|
|
|
|
|
|
|
.. code-block:: solidity
|
|
|
|
|
|
|
|
assembly {
|
|
|
|
returndatacopy(0, 0, returndatasize())
|
|
|
|
revert(0, returndatasize())
|
|
|
|
}
|
|
|
|
|
2022-03-29 20:48:49 +00:00
|
|
|
On the other hand, the following code *is* memory safe, because memory beyond the location pointed to by the
|
|
|
|
free memory pointer can safely be used as temporary scratch space:
|
2022-02-03 15:08:32 +00:00
|
|
|
|
|
|
|
.. code-block:: solidity
|
|
|
|
|
2022-02-14 12:23:50 +00:00
|
|
|
assembly ("memory-safe") {
|
2022-02-03 15:08:32 +00:00
|
|
|
let p := mload(0x40)
|
|
|
|
returndatacopy(p, 0, returndatasize())
|
|
|
|
revert(p, returndatasize())
|
|
|
|
}
|
|
|
|
|
|
|
|
Note that you do not need to update the free memory pointer if there is no following allocation,
|
|
|
|
but you can only use memory starting from the current offset given by the free memory pointer.
|
|
|
|
|
|
|
|
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):
|
|
|
|
|
|
|
|
.. code-block:: solidity
|
|
|
|
|
2022-02-14 12:23:50 +00:00
|
|
|
assembly ("memory-safe") {
|
2022-02-03 15:08:32 +00:00
|
|
|
revert(0, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
Note that not only memory operations in inline assembly itself can be memory-unsafe, but also assignments to
|
2022-07-23 15:51:28 +00:00
|
|
|
Solidity variables of reference type in memory. For example the following is not memory-safe:
|
2022-02-03 15:08:32 +00:00
|
|
|
|
|
|
|
.. code-block:: solidity
|
|
|
|
|
|
|
|
bytes memory x;
|
|
|
|
assembly {
|
|
|
|
x := 0x40
|
|
|
|
}
|
|
|
|
x[0x20] = 0x42;
|
|
|
|
|
2022-07-23 15:51:28 +00:00
|
|
|
Inline assembly that neither involves any operations that access memory nor assigns to any Solidity variables
|
2022-02-03 15:08:32 +00:00
|
|
|
in memory is automatically considered memory-safe and does not need to be annotated.
|
|
|
|
|
|
|
|
.. warning::
|
|
|
|
It is your responsibility to make sure that the assembly actually satisfies the memory model. If you annotate
|
|
|
|
an assembly block as memory-safe, but violate one of the memory assumptions, this **will** lead to incorrect and
|
2023-05-30 19:49:25 +00:00
|
|
|
undefined behavior that cannot easily be discovered by testing.
|
2022-02-14 12:23:50 +00:00
|
|
|
|
|
|
|
In case you are developing a library that is meant to be compatible across multiple versions
|
2022-07-23 15:51:28 +00:00
|
|
|
of Solidity, you can use a special comment to annotate an assembly block as memory-safe:
|
2022-02-14 12:23:50 +00:00
|
|
|
|
|
|
|
.. code-block:: solidity
|
|
|
|
|
|
|
|
/// @solidity memory-safe-assembly
|
|
|
|
assembly {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
|
2022-07-23 15:51:28 +00:00
|
|
|
Note that we will disallow the annotation via comment in a future breaking release; so, if you are not concerned with
|
2023-05-30 19:49:25 +00:00
|
|
|
backward-compatibility with older compiler versions, prefer using the dialect string.
|