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

add dst, src

dst += src

sub dst, src

dst -= src

inc dst, dec dst

++ / --; do not touch CF

neg dst

two’s-complement negation

mul src (unsigned)

rdx:rax = rax * src

imul src / imul dst, src, imm

signed multiply; the three-operand form fits a 32-bit immediate, useful for index math

div src (unsigned)

rax = rdx:rax / src, rdx = rdx:rax % src

idiv src

signed; the operator zeros (xor edx, edx) or sign-extends (cqo) rdx before calling

adc, sbb

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

and dst, src

bitwise AND

or dst, src

bitwise OR

xor dst, src

bitwise XOR

not dst

bitwise NOT

shl dst, n / sal

logical / arithmetic left shift

shr dst, n

logical right shift (zero-fill)

sar dst, n

arithmetic right shift (sign-extend)

rol, ror

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#