gcc -O1 p1.c p2.c -o p
#include
로 명시된 파일을 삽입, #define
으로 선언된 매크로 확장.C program (p1 p2) <- Text
Asm program (p1.s p2.s) <- Text
Object program (p1.o p2.o) <- Binary
Excutable program (p) <- Binary
// code.c
int sum(int x, int y) {
int t = x + y;
return t;
}
# input
gcc -O -m64 -S code.c
# output
sum:
.LFB0:
.cfi_startproc
endbr64
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
# input
gcc -O1 -c code.c
# output viewed by gdb
0x0 <sum>: 0xf3 0x0f 0x1e 0xfa 0x8d 0x04 0x37 0xc3
0x8 <sum+8>: 0x04
# output viewed by objdump
0000000000000000 <sum>:
0: f3 0f 1e fa endbr64
4: 8d 04 37 lea (%rdi,%rsi,1),%eax
7: c3 retq
// main.c
#include <stdio.h>
int sum(int x, int y);
int main() {
sum(1, 3);
}
#input
gcc -O1 -o prog code.o main.c
C declaration | Intel data type | Assembly-code suffix | Size (bytes) |
---|---|---|---|
char | Byte | b | 1 |
short | Word | w | 2 |
int | Double word | l | 4 |
long | Quad word | q | 8 |
char * | Quad word | q | 8 |
float | Single precision | s | 4 |
double | Double precision | l | 8 |
Sizes of C data types in x86-64. (64비트 머신에서 포인터는 8바이트를 가짐)
Integer registers
Operand forms. scaling factor s는 1, 2, 4, 8 중에 하나이다.
Simple data movement instructions(MOV 클래스)
Extending data movement instructions
Zero-extending data movement instructions(MOVZ 클래스)
Sign-extending data movement instructions(MOVS 클래스)
movslq %eax, %rax
와 동일한 효과지만 더 압축적인 인코딩을 가짐long exchange(long *xp, long y) {
long x = *xp;
*xp = y;
return x;
}
# long exchange(long *xp, long y)
# xp in %rdi, y in %rsi
exchange:
movq (%rdi), %rax # Get x at xp. Set as return value.
movq %rsi, (%rdi) # Store y at xp.
ret # Return.
스택(stack): 후입선출(Last-In, First-Out) 형태로만 추가(push), 제거(pop) 가능
# pushq %rbp. equivalent to the following pair of instructions:
subq $8,%rsp # Decrement stack pointer
movq %rbp,(%rsp) # Store %rbp on stack
# popq %rax. equivalent to the following pair of instructions:
movq (%rsp),%rax # Read %rax from stack
addq $8,%rsp # Increment stack pointer
movq 8(%rsp), %rdx
로 스택의 두 번째(top의 바로 다음) 원소에 접근
long scale(long x, long y, long z) {
long t = x + 4 * y + 12 * z;
return t;
}
# long scale(long x, long y, long z)
# x in %rdi, y in %rsi, z in %rdx
scale:
leaq (%rdi,%rsi,4), %rax # x + 4*y
leaq (%rdx,%rdx,2), %rdx # z + 2*z = 3*z
leaq (%rax,%rdx,4), %rax # (x+4*y) + 4*(3*z) = x + 4*y + 12*z
ret
oct word: x86-64 인스트럭션 set은 128-bit(16-byte) 숫자 관련 연산을 제한적으로 지원
콜론(:)으로 두 레지스터가 연결되어 함께 하나의 더 큰 레지스터로 취급. 연산 결과의 상위 64-bit를 앞에, 하위 64-bit를 뒤에 있는 레지스터에 저장.
imulq: signed 정수 곱셈 연산으로, 두 가지 형식 가능
1. 두 개의 operand를 사용하여 64-bit 결과를 생성(*u_64, *t_64)
2. src operand 한 개만 사용
- src와 %rax의 곱을 수행하여 128-bit의 결과를 %rdx:%rax에 저장.
mulq: unsigned 정수 곱셈 연산
- src operand 한 개만 사용
- src와 %rax의 곱을 수행하여 128-bit의 결과를 %rdx:%rax에 저장.
# void store_uprod(uint128_t *dest, uint64_t x, uint64_t y)
# dest in %rdi, x in %rsi, y in %rdx
store_uprod:
movq %rsi, %rax # Copy x to multiplicand
mulq %rdx # Multiply by y
movq %rax, (%rdi) # Store lower 8 bytes at dest
movq %rdx, 8(%rdi) # Store upper 8 bytes at dest+8
ret
cqto: %rax 레지스터의 부호를 확장
- operand가 없음
- %rax의 부호 비트(MSB)를 확장하여 %rdx 전체에 복사
- 주로 idivq 인스트럭션을 수행하기 전에 사용
idivq, divq: 정수 나눗셈 연산
- 각각 signed/unsigned 128-bit 피제수(dividened)로, %rdx:%rax에 저장
- src로 %rdx:%rax를 나누어 몫은 %rax, 나머지는 %rdx에 저장
- 수행 전에 %rdx의 모든 비트들은 %rax의 부호 비트로 설정되어 있어야 함
# void remdiv(long x, long y, long *qp, long *rp)
# x in %rdi, y in %rsi, qp in %rdx, rp in %rcx
remdiv:
movq %rdx, %r8 # Copy qp
movq %rdi, %rax # Move x to lower 8 bytes of dividend
cqto # Sign-extend to upper 8 bytes of dividend
idivq %rsi # Divide by y
movq %rax, (%r8) # Store quotient at qp
movq %rdx, (%rcx) # Store remainder at rp
ret