amd x86-64 기준, 프로그램의 기계수준 표현에 대해서 알아본다.
이 글에서 정리한 것은 word, 정수 레지스터, att형식 operand 읽는 법, 그리고 mov 인스트럭션이다.
1. C 자료형의 길이
C declaration | Intel data types | Assembly-code suffix | Size |
---|---|---|---|
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 |
x86 프로세서는 처음에 16비트로 시작해서 32비트, 64비트로 확장했기 때문에 word는 16비트 데이터 타입을 말한다.
그래서 32비트 데이터 타입은 double word, 64비트는 quad word라고 부른다.
윈도우 프로그래밍에서 DWORD 타입이 그래서 32비트인가?
2. 정수 레지스터 16개
64bit | 32bit | 16bit | 8bit | |
---|---|---|---|---|
%rax | %eax | %ax | %al | Return value |
%rbx | %ebx | %bx | %bl | Callee saved |
%rcx | %ecx | %cx | %cl | 4th argument |
%rdx | %edx | %dx | %dl | 3th argument |
%rsi | %esi | %si | %sil | 2nd argument |
%rdi | %edi | %di | %dil | 1st argument |
%rbp | %ebp | %bp | %bpl | Callee saved |
%rsp | %esp | %sp | %spl | Stack pointer |
%r8 | %r8d | %r8w | %r8b | 5th argument |
%r9 | %r9d | %r9w | %r9b | 6th argument |
%r10 | %r10d | %r10w | %r10b | Caller saved |
%r11 | %r11d | %r11w | %r11b | Caller saved |
%r12 | %r12d | %r12w | %r12b | Callee saved |
%r13 | %r13d | %r13w | %r13b | Callee saved |
%r14 | %r14d | %r14w | %r14b | Callee saved |
%r15 | %r15d | %r15w | %r15b | Callee saved |
x86이 64비트로 확장되면서 정수 레지스터를 8개 더 늘렸다.
함수 인자가 4개를 넘어가면 레지스터로 커버가 안되서 느려진다고 들었었는데… 이 표대로라면 64비트에선 인자를 6개까지 해도 괜찮은 걸까?
3. 오퍼랜드(Operand) 형식
Type | Form | Operand value | Name |
---|---|---|---|
Immediate | $Imm | Imm | Immediate |
Register | r | R[r] | Register |
Memory | Imm | M[Imm] | Absolute |
Memory | (r) | M[R[r]] | Indirect |
Memory | Imm(r) | M[Imm + R[r]] | Base + displacement |
Memory | (r1, r2) | M[R[r1] + R[r2]] | Indexed |
Memory | Imm(r1, r2) | M[Imm + R[r1] + R[r2]] | Indexed |
Memory | (, r, s) | M[R[r] * s] | Scaled indexed |
Memory | Imm(, r, s) | M[Imm + R[r] * s] | Scaled indexed |
Memory | (r1, r2, s) | M[R[r1] + R[r2] * s] | Scaled indexed |
Memory | Imm(r1, r2, s) | M[Imm + R[r1] + R[r2] * s] | Scaled indexed |
ATT 형식의 어셈블리 코드 읽는 법.. 야매로 작성해서 틀린 게 있을 수 있다.
이제 gdb에서 intel 형식으로 바꿔주지 않아도 읽을 수 있다… ㅜㅜ
4. 데이터 이동 인스트럭션(Instruction)
간단한 데이터 이동 인스트럭션
Instruction | Effect | Description |
---|---|---|
mov S, D | D <- S | Move |
movb | Move byte | |
movw | Move word | |
movl | Move double word | |
movq | Move quad word | |
movabsq I, R | R <- I | Move absolute quad word |
Source에는 상수, 레지스터 저장 값, 메모리 저장 값이 올 수 있다.
Destination에는 레지스터, 메모리 주소가 올 수 있다.
단, Source와 Destination가 동시에 메모리를 가리킬 수 없다.
movb, movw는 상위바이트에 관여하지 않으나, movl은 상위 4바이트를 0으로 설정한다.
movabsq는 64비트 상수를 위한 인스트럭션이다. Source로 상수(I)만을, Destination으로 레지스터(R)만을 가질 수 있다.
0 확장 데이터 이동 인스트럭션
Instruction | Effect | Description |
---|---|---|
movz S, R | R <- ZeroExtend(S) | Move with zero extension |
movzbw | Move zero-extended byte to word | |
movzbl | Move zero-extended byte to double word | |
movzwl | Move zero-extended word to double word | |
movzbq | Move zero-extended byte to quad word | |
movzwq | Move zero-extended word to quad word |
상위 바이트를 모두 0으로 채우는 효과가 있다. unsigned형에서 많이 쓰일 것 같다.
Destination으로 레지스터(R)만을 가질 수 있다.
movzlq가 없는데, movl은 이미 이러한 효과를 가지고 있기 때문이다.
부호 확장 데이터 이동 인스트럭션
Instruction | Effect | Description |
---|---|---|
movs S, R | R <- SignExtend(S) | Move with sign extension |
movsbw | Move sign-extended byte to word | |
movsbl | Move sign-extended byte to double word | |
movswl | Move sign-extended word to double word | |
movsbq | Move sign-extended byte to quad word | |
movswq | Move sign-extended word to quad word | |
movslq | Move sign-extended double word to quad word | |
cltq | %rax <- SignExtend(%eax) | Sign-extend %eax to %rax |
signed형에서 많이 쓰일 것 같다.
movslq %eax, %rax와 같은 효과를 가진 cltq가 따로 있다. 자주 쓰여서 짧은 인코딩을 위해 만들었나보다.
5. 데이터 이동 예제
long exchange(long *xp, long y) {
long x = *xp;
*xp = y;
return x;
}
xp 포인터가 가리키는 위치에 y를 대입하고, 기존 값은 반환하는 함수다.
gcc -Og -S exchange.c
cat exchange.s
위 명령어로 어셈블리 코드를 보자
exchange:
.LFB0:
.cfi_startproc
movq (%rdi), %rax
movq %rsi, (%rdi)
ret
.cfi_endproc
책에 따르면 이 예제에서 주목할 점이 두 가지 있다.
첫째는 C언어에서 포인터라고 부르는 것이 어셈블리어에서는 주소라는 점이고, 둘째로 x 같은 지역변수들은 종종 레지스터에 저장된다는 점이다.
개인적으로 둘째가 프로그램 최적화의 핵심이라고 생각한다.
6. PUSH, POP 인스트럭션
Instruction | Effect | Description |
---|---|---|
pushq S | R[%rsp] <- R[%rsp] - 8;M[R[%rsp]] <- S | Push quad word |
popq D | D <- M[R[%rsp]];R[%rsp] <- R[%rsp] + 8 | Pop quad word |
7. 마무리
시간이 없어서 사흘 정도 글을 작성하지 못했다. 이 글도 내가 나간 진도보다 많이 늦다. 그래서 급하게 작성하느라 정리도 제대로 안했다.
출처
‘Computer Systems A Programmer’s Perspective (3rd Edition)’