Busy being born.

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.

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.