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.
assembly { let x :=2}assembly {let y := x // Error}// DeclarationError: identifier not found// let y := x// ^
Basic Example
functionaddition(uint x,uint y) publicpurereturns (uint) {assembly {// Create a new variable `result`// -> calculate the sum of `x + y` with the `add` opcode// -> assign the value to `result`let result :=add(x, y) // x + y// Use the `mstore` opcode, to:// -> store `result` in memory// -> at memory address 0x0mstore(0x0, result) // store result in memory// return 32 bytes from memory address 0x0return(0x0,32) }}
In the inner working of the EVM, let performs the following:
Creates a new stack slot
The new slot is reserved for the variable.
The slot is then automatically removed again when the end of the block is reached.
Variables declared with the let keyword are not visible outside the assembly block.
Literals
Literals are also written in the same way than in Solidity. However, string literals can be up to 32 characters:
assembly {let a :=0x123// Hexadecimallet b :=42// Decimallet c :="hello world"// Stringlet d :="very long string more than 32 bytes"// Error} // TypeError: String literal too long (35 < 32)// let d := "really long string more than 32 bytes"// ^------------------------------------^
Blocks and Scope
Variables follow the standard rule of block scoping. A block scope is defined by adding code between two curly braces { ... }.
In the example below, y and z are only visible in the scope they are defined in. So Scope 1 for y and Scope 2 for z:
assembly { let x :=3// x is visible everywhere// Scope 1 { let y := x // ok } // y is "deallocated" here// Scope 2 {let z := y // Error } // x is "deallocated" here}// DeclarationError: identifier not found// let x:= y// ^
Loops
This is a for loop in Solidity:
functionfor_loop_solidity(uint n,uint value) publicpurereturns(uint) {for ( uint i =0; i < n; i++ ) { value =2* value; }return value;}
This is its assembly equivalent:
functionfor_loop_assembly(uint n,uint value) publicpurereturns (uint) {assembly {for { let i :=0 } lt(i, n) { i :=add(i,1) } { value :=mul(2, value) }mstore(0x0, value)return(0x0,32) }}
Note that lt(i, n) is a functional-style expression. In fact, everything in Solidity assembly must be written in functional.
There is no while loop in Solidity assembly, but similar effect can be achieved by for loop:
assembly {let x :=0let i :=0for { } lt(i,0x100) { } { // while(i < 256), 100 (hex) = 256 x :=add(x,mload(i)) i :=add(i,0x20) }}
Conditional Statements
Solidity inline assembly supports if statement to check conditions before executing code. However, there is no else part:
Curly braces for the body of the if statement are required.
EVM Assembly also includes switch statement. A switch statement takes the value of an expression and compares it to several constants. The branch corresponding to the matching constant is taken. You can also have a default case if none of the cases match:
assembly {let x :=0 switch calldataload(4) case 0 { x :=calldataload(0x24) } default { x :=calldataload(0x44) }sstore(0,div(x,2))}