Operators#
Assembly’s “operators” are the instruction set. Each instruction moves data, computes arithmetic / logic, sets flags, or transfers control. There is no operator overloading; the operator picks the mnemonic that matches the data width and the intent.
For the conditional
jumps that read the flags these instructions set, see
Control flow. For call / ret and the function
surface, see Functions.
Move#
x86-64 mov copies between register, memory, and immediate.
mov rax, 10 ; immediate -> register
mov rax, rbx ; register -> register
mov rax, qword [rsi] ; memory -> register
mov qword [rdi], rax ; register -> memory
mov cannot copy memory-to-memory in a single instruction;
use an intermediate register.
ARM64 splits move (mov) from load (ldr) / store
(str).
mov x0, #10 // immediate -> register
mov x0, x1 // register -> register
ldr x0, [x1] // memory -> register
str x0, [x1] // register -> memory
Arithmetic#
Instruction |
Effect |
|---|---|
|
|
|
|
|
|
|
two’s-complement negation |
|
|
|
signed multiply; the three-operand form fits a 32-bit immediate, useful for index math |
|
|
|
signed; the operator zeros ( |
|
add / subtract with carry, for multi-precision |
add rax, rcx ; rax += rcx
sub rax, 8 ; rax -= 8
; unsigned divide rdi by rsi
mov rax, rdi
xor edx, edx
div rsi
; rax = quotient, rdx = remainder
; signed divide
mov rax, rdi
cqo ; sign-extend rax into rdx
idiv rsi
Logical#
Instruction |
Effect |
|---|---|
|
bitwise AND |
|
bitwise OR |
|
bitwise XOR |
|
bitwise NOT |
|
logical / arithmetic left shift |
|
logical right shift (zero-fill) |
|
arithmetic right shift (sign-extend) |
|
rotate |
xor eax, eax ; idiomatic "rax = 0"
and rax, 0xFFF ; mask off everything except low 12 bits
shl rax, 3 ; rax *= 8
shr rax, 1 ; rax /= 2 (unsigned)
Comparison and tests#
cmp and test set flags but do not write any register.
The conditional jumps (see Control flow) read the flags.
cmp rax, 10 ; sets flags as if (rax - 10) was computed
je .equal
jl .less
test rax, rax ; sets ZF and SF based on rax
jz .zero ; jump if rax == 0
test rax, rax is the operator’s idiom for “is rax zero?”;
cmp is the idiom for “compare to a value”.
LEA#
lea (Load Effective Address) computes an addressing-mode
expression and stores the result; no memory access. The
operator uses it as a 3-operand arithmetic instruction.
lea rax, [rbx + rcx*4 + 8] ; rax = rbx + rcx*4 + 8
lea rax, [rax + rax*4] ; rax *= 5
lea rax, [rax*8 + rbx] ; rax = rax*8 + rbx
Stack operations#
push rax ; rsp -= 8; [rsp] = rax
pop rbx ; rbx = [rsp]; rsp += 8
pushfq ; push rflags
popfq ; pop rflags
ARM64 arithmetic#
add x0, x1, x2 // x0 = x1 + x2
sub x0, x1, #8 // x0 = x1 - 8
mul x0, x1, x2 // x0 = x1 * x2
udiv x0, x1, x2 // unsigned divide
sdiv x0, x1, x2 // signed divide
and x0, x1, #0xff // bitwise AND
orr x0, x1, x2 // bitwise OR
eor x0, x1, x2 // bitwise XOR
lsl x0, x1, #3 // logical shift left
lsr x0, x1, #1 // logical shift right
asr x0, x1, #1 // arithmetic shift right
ARM64 instructions take three operands by default; the operator does not have to overwrite an input.
SIMD#
x86-64 SIMD instructions live in the SSE / AVX / AVX-512 families. Reach for them when the loop body is hot enough to justify the verbosity.
movdqu xmm0, [rsi] ; load 16 unaligned bytes
pxor xmm1, xmm1 ; zero xmm1
pcmpeqb xmm0, xmm1 ; compare bytes
pmovmskb eax, xmm0 ; pack into a 16-bit mask in eax
ARM64 NEON.
ld1 {v0.16b}, [x0] // load 16 bytes
eor v1.16b, v1.16b, v1.16b // zero
cmeq v0.16b, v0.16b, v1.16b // compare equal
Atomics and lock prefix#
lock makes the next instruction atomic (read-modify-write).
xchg is implicitly locked. See Concurrency for the
full surface.
lock add qword [rdi], 1 ; atomic increment
lock cmpxchg qword [rdi], rcx ; CAS: if [rdi] == rax then [rdi] = rcx
References#
Types for size suffixes and addressing modes.
Control flow for the flags
cmp/testset and the conditional jumps.Functions for
call/retmechanics.Concurrency for
lock,xchg,cmpxchg.