Introduction to Solidity inline assembly (Yul)
Yul is the low-level language used in Solidity inline assembly. (official documentation)
In a Solidity smart contract, Yul is always written within an inline assembly block.
- There are no semicolons.
- There's only one data-type in Yul -- the 32 byte word.
- New variables are declared within the assembly block via the
letkeyword. - Values are assigned to variables via the
:=colon-equals symbol.
contract YulExample {
function add() external {
assembly {
let x := 1
let y := 2
let result := add(x,y)
}
}
}
You can reference variables declared in the function's arguments, return values and body.
contract YulExample {
function add(uint256 value) external view returns (uint256 ret) {
assembly {
let x := 1
ret := add(x,value)
}
}
}
You can reference variables declared in the contract storage with the .slot or .offset suffixes. These are used to determine the storage location of the value.
contract YulExample {
// Slot-0: 0x-[A-32-bytes]
uint256 public A;
// Slot-1: 0x-[empty-1-byte][E-1-byte][D-2-bytes][C-12-bytes][B-16-bytes]
uint128 public B;
uint96 public C;
uint16 public D;
uint8 public E;
function add_D(uint256 val) external returns (uint256 ret) {
assembly {
let slot := D.slot // slot-1 packs B,C,D,E
// 0x-[empty-1-byte][E-1-byte][D-2-bytes][C-12-bytes][B-16-bytes]
let current := sload(slot)
// `D.offset` is 28-bytes (B.len + C.len)
let dOffsetBits := mul(D.offset, 8)
// 0x-[empty-2-bytes][full-2-bytes][empty-28-bytes]
let dMask := shl(dOffsetBits, 0xffff)
// 0x-[empty-1-byte][full-1-byte][empty-2-bytes][full-28-bytes]
let notDMask := not(dMask)
// 0x-[empty-2-bytes][D-2-bytes][empty-28-bytes]
let d := and(dMask, current)
// 0x-[empty-1-byte][E-1-byte][empty-2-bytes][C-12-bytes][B-16-bytes]
let notd := and(notDMask, current)
// 0x-[empty-30-bytes][New-D-2-bytes]
ret := and(add(shr(dOffsetBits, d), val), 0xffff)
// 0x-[empty-1-byte][E-1-byte][New-D-2-bytes][C-12-bytes][B-16-bytes]
let newVal := or(notd, shl(dOffsetBits, ret))
sstore(slot, newVal)
}
}
}
You can write if-statements (no else-statements) and for-loops.
contract YulExample {
function isPrime(uint256 num) external pure returns (bool ret) {
assert(num > 0);
ret = true;
assembly {
let x := num
let halfX := add(div(x, 2), 1)
for { let i := 2 } lt(i, halfX) { i := add(i,1) }
{
if iszero(mod(x, i)) {
ret := 0
break
}
}
}
}
}
Within assembly blocks, value assignments via := colon-equals manipulate the stack, and you manipulate memory and storage by invoking opcodes directly.