Merge pull request #5044 from ethereum/updateAssembly

[DOCS] Update assembly.
This commit is contained in:
chriseth 2018-10-04 17:33:14 +02:00 committed by GitHub
commit c07e5f0b16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -47,13 +47,22 @@ these curly braces, you can use the following (see the later sections for more d
- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
- opcodes in functional style, e.g. ``add(1, mlod(0))``
- labels, e.g. ``name:``
- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of empty (0) is assigned)
- identifiers (labels or assembly-local variables and externals if used as inline assembly), e.g. ``jump(name)``, ``3 x add``
- assignments (in "instruction style"), e.g. ``3 =: x``
- assignments in functional style, e.g. ``x := add(y, 3)``
- identifiers (assembly-local variables and externals if used as inline assembly), e.g. ``add(3, x)``, ``sstore(x_slot, 2)``
- assignments, e.g. ``x := add(y, 3)``
- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }``
The following features are only available for standalone assembly:
- direct stack control via ``dup1``, ``swap1``, ...
- direct stack assignments (in "instruction style"), e.g. ``3 =: x``
- labels, e.g. ``name:``
- jump opcodes
.. note::
Standalone assembly is supported for backwards compatibility but is not documented
here anymore.
At the end of the ``assembly { ... }`` block, the stack must be balanced,
unless you require it otherwise. If it is not balanced, the compiler generates
a warning.
@ -156,7 +165,7 @@ Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Hom
Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception.
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
(excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
but not including position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
The opcodes ``pushi`` and ``jumpdest`` cannot be used directly.
@ -338,38 +347,39 @@ Literals
You can use integer constants by typing them in decimal or hexadecimal notation and an
appropriate ``PUSHi`` instruction will automatically be generated. The following creates code
to add 2 and 3 resulting in 5 and then computes the bitwise and with the string "abc".
The final value is assigned to a local variable called ``x``.
Strings are stored left-aligned and cannot be longer than 32 bytes.
.. code::
assembly { 2 3 add "abc" and }
assembly { let x := and("abc", add(3, 2)) }
Functional Style
-----------------
You can type opcode after opcode in the same way they will end up in bytecode. For example
adding ``3`` to the contents in memory at position ``0x80`` would be
For a sequence of opcodes, it is often hard to see what the actual
arguments for certain opcodes are. In the following example,
``3`` is added to the contents in memory at position ``0x80``.
.. code::
3 0x80 mload add 0x80 mstore
As it is often hard to see what the actual arguments for certain opcodes are,
Solidity inline assembly also provides a "functional style" notation where the same code
would be written as follows
Solidity inline assembly has a "functional style" notation where the same code
would be written as follows:
.. code::
mstore(0x80, add(mload(0x80), 3))
Functional style expressions cannot use instructional style internally, i.e.
``1 2 mstore(0x80, add)`` is not valid assembly, it has to be written as
``mstore(0x80, add(2, 1))``. For opcodes that do not take arguments, the
parentheses can be omitted.
Note that the order of arguments is reversed in functional-style as opposed to the instruction-style
way. If you use functional-style, the first argument will end up on the stack top.
If you read the code from right to left, you end up with exactly the same
sequence of constants and opcodes, but it is much clearer where the
values end up.
If you care about the exact stack layout, just note that the
syntactically first argument for a function or opcode will be put at the
top of the stack.
Access to External Variables, Functions and Libraries
-----------------------------------------------------
@ -396,7 +406,7 @@ Local Solidity variables are available for assignments, for example:
}
}
.. note::
.. warning::
If you access variables of a type that spans less than 256 bits
(for example ``uint64``, ``address``, ``bytes16`` or ``byte``),
you cannot make any assumptions about bits not part of the
@ -448,26 +458,19 @@ Assignments are possible to assembly-local variables and to function-local
variables. Take care that when you assign to variables that point to
memory or storage, you will only change the pointer and not the data.
There are two kinds of assignments: functional-style and instruction-style.
For functional-style assignments (``variable := value``), you need to provide a value in a
functional-style expression that results in exactly one stack value
and for instruction-style (``=: variable``), the value is just taken from the stack top.
For both ways, the colon points to the name of the variable. The assignment
is performed by replacing the variable's value on the stack by the new value.
Variables can only be assigned expressions that result in exactly one value.
If you want to assign the values returned from a function that has
multiple return parameters, you have to provide multiple variables.
.. code::
{
let v := 0 // functional-style assignment as part of variable declaration
let v := 0
let g := add(v, 2)
sload(10)
=: v // instruction style assignment, puts the result of sload(10) into v
function f() -> a, b { }
let c, d := f()
}
.. note::
Instruction-style assignment is deprecated.
If
--
@ -584,12 +587,9 @@ Things to Avoid
Inline assembly might have a quite high-level look, but it actually is extremely
low-level. Function calls, loops, ifs and switches are converted by simple
rewriting rules and after that, the only thing the assembler does for you is re-arranging
functional-style opcodes, managing jump labels, counting stack height for
functional-style opcodes, counting stack height for
variable access and removing stack slots for assembly-local variables when the end
of their block is reached. Especially for those two last cases, it is important
to know that the assembler only counts stack height from top to bottom, not
necessarily following control flow. Furthermore, operations like swap will only
swap the contents of the stack but not the location of variables.
of their block is reached.
Conventions in Solidity
-----------------------
@ -605,6 +605,8 @@ first.
Solidity manages memory in a very simple way: There is a "free memory pointer"
at position ``0x40`` in memory. If you want to allocate memory, just use the memory
starting from where this pointer points at and update it accordingly.
There is no guarantee that the memory has not been used before and thus
you cannot assume that its contents are zero bytes.
There is no built-in mechanism to release or free allocated memory.
Here is an assembly snippet that can be used for allocating memory::
@ -623,10 +625,10 @@ of the free memory pointer.
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (yes, this is
even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory
arrays are pointers to memory arrays. The length of a dynamic array is stored at the
first slot of the array and then only the array elements follow.
first slot of the array and followed by the array elements.
.. warning::
Statically-sized memory arrays do not have a length field, but it will be added soon
Statically-sized memory arrays do not have a length field, but it might be added later
to allow better convertibility between statically- and dynamically-sized arrays, so
please do not rely on that.
@ -661,7 +663,7 @@ Scoping: An identifier that is declared (label, variable, function, assembly)
is only visible in the block where it was declared (including nested blocks
inside the current block). It is not legal to access local variables across
function borders, even if they would be in scope. Shadowing is not allowed.
Local variables cannot be accessed before they were declared, but labels,
Local variables cannot be accessed before they were declared, but
functions and assemblies can. Assemblies are special blocks that are used
for e.g. returning runtime code or creating contracts. No identifier from an
outer assembly is visible in a sub-assembly.
@ -672,7 +674,7 @@ Whenever a local variable is referenced, the code generator needs
to know its current relative position in the stack and thus it needs to
keep track of the current so-called stack height. Since all local variables
are removed at the end of a block, the stack height before and after the block
should be the same. If this is not the case, a warning is issued.
should be the same. If this is not the case, compilation fails.
Using ``switch``, ``for`` and functions, it should be possible to write
complex code without using ``jump`` or ``jumpi`` manually. This makes it much