For Prof Tuck's CS3650, fall 2018.
This uses AT&T syntax (per norms for the GNU C compiler).
In AMD64, we have 16 registers that can each hold 64-bits (8 bytes):
Group 1:
Group 2:
Group 3:
Groups 1 and 2 are the extended form of the old 16 bit registers from the Intel 8086, while Group 3 was added specifically for AMD64.
Shorter parts of these registers are also accessible by the hisorical names:
64-bit register | Low 32-bits | Low 16 bits | Low 8 bits | Byte 7* |
---|---|---|---|---|
%rax | %eax | %ax | %al | %ah |
%rsi | %esi | %si | %sil | n/a |
%r8 | %r8d | %r8w | %r8b | n/a |
The names for the other registers follow the pattern within the same group.
For the Group 1 registers, the high and low bytes of the low 16-bits be accessed by dedicated names (%ah, %al, %ch, %cl, etc).
There are a couple of other registers worth remembering:
Instruction | Description |
---|---|
mov %rxx, %ryy | Copy data from register %rxx to %ryy |
----------------- | ------------------------------------------- |
inc %rxx | Add one to %rxx |
dec %rxx | Subtract one from rxx |
neg %rxx | Negate %rxx |
not %rxx | Bitwise NOT %rxx |
----------------- | ------------------------------------------- |
add %rxx, %ryy | %ryy += %rxx |
sub %rxx, %ryy | %ryy -= %rxx |
or %rxx, %ryy | %ryy = %rxx OR %ryy (bitwise) |
and %rxx, %ryy | %ryy = %rxx AND %ryy (bitwise) |
----------------- | ------------------------------------------- |
imul %rxx | %rdx:%rax = %rax * %rxx |
imul %rxx, %ryy | %ryy = %ryy * %rxx (truncated to 64-bits) |
idiv %rxx | %rax = %rdx:%rax / %rxx; %rdx = remainder |
Note: The argument order for "cmp" is backwards here.
## This is "%rbx > %rax"?
cmp %rax, %rbx
jg rbx_is_bigger
Instruction | Description |
---|---|
cmp %ryy, %rxx | Compares the two registers, updating the flags register (note: order seems backwards) |
---------------- | -------------------------------------------------------------- |
je label | Jump if equal (if previous cmp set equal flag; %rxx == %ryy) |
jne label | Jump if not equal (%rxx != %ryy) |
jl label | Jump if less than (%rxx < %ryy) |
jle label | <= |
jg label | > |
jge label | >= |
---------------- | -------------------------------------------------------------- |
sete %rzz | Set %rzz if %rxx == %ryy in the previous cmp, else clear it. |
setg %rzz | Set %rzz if %rxx > %ryy |
setl %rzz | Set %rzz if %rxx < %ryy |
Instruction | Description |
---|---|
push %rxx | Copy %rxx to stack @ %rsp, move down %rsp |
pop %rxx | Copy from stack @ %rsp to %rxx, move up %rsp |
enter $NN, $0 | Allocate a stack frame with NN bytes of space |
leave | Deallocate a stack frame |
call label | Push $rip and jump to the address of the "label" function |
In addition to operating on registers, many instructions can accept alternate modes that operate on data in memory or on constant values.
Example Instruction | Description |
---|---|
add (%rcx), %rdx | %rdx = %rdx + (value at address in %rcx) |
add $10, %rdx | %rdx = %rdx + 10 |
addq $10, 2(%e10, %e11, 2) | (the value at %e10+2*%e11) += 10 |
add -16(%rsp), %rax | %rax += the value 16 bytes below where %rsp points |
There's a special instruction, lea
, that calcuates an address as if it were
going to access an argument in memory but gives you the address as its output.
Example Instruction | Description |
---|---|
lea -16(%rsp), %rax | %rax = %rsp - 16 |
Instruction suffixes: Instructions can have a single letter suffix added to indicate hte size of the value operated on: b, w, l, q for 1, 2, 4, 8 bytes.
Example Instruction | Description |
---|---|
movw $10, (%rdx) | Move a 16-bit (2 byte, short) int to the address in %rdx |
movq %10, (%rdx) | Move a 64-bit (8 byte, long) int to the address in %rdx |
To call a function with the "call" instruction, you must first:
Once the function returns, your result will be in %rax. An optional second result is returned in %rdx.
These registers are callee-saved. If you want to use them, save them to the stack in your prologue and restore them in your epilogue:
The stack registers (%rsp, %rbp) are technically callee save, but this is handled by the standard use of "enter" and "leave" calls.
How to map variables / values to locations:
The caller-save strategy, where temporary registers are pushed/popped around a function call to preserve them can be used but tends to be more annoying than using safe registers.
$ gcc -no-pie -o foo foo.s
$ gcc -nostdlib -no-pie -o foo foo.s
OR
$ as -o foo.o foo.s
$ ld -o foo foo.o
break
to set breakpoints on labels or line numbers.p $rax
or p/x $rax
.info reg