x86-64 bit 컴퓨터의 CPU에는 아래 16개의 레지스터가 저장되어 있다.
8byte | 4byte | 1byte | |||||
%rax | %r8 | %eax | %r8d | %al | %r8b | ||
%rbx | %r9 | %ebx | %r9d | %bl | %r9b | ||
%rcx | %r10 | %ecx | %r10d | %cl | %r10b | ||
%rdx | %r11 | %edx | %r11d | %dl | %r11b | ||
%rsi | %r12 | %esi | %r12d | %sil | %r12b | ||
%rdi | %r13 | %edi | %r13d | %dil | %r13b | ||
%rsp | %r14 | %esp | %r14d | %spl | %r14b | ||
%rbp | %r15 | %ebp | %r15d | %bpl | %r15b |
기본적으로 64bit이며, 어셈블리의 특징인 '모든 데이터 타입은 정수형 1, 2, 4, 8 byte 이다'를 만족하기 위해 각각의 레지스터들을 1, 2, 4byte로 분해가능하다. 위 레지스터들은 %rN, %rNd로 임의 범용이 가능한데, 빨간색의 %rsp와 %esp는 stack pointer 레지스터이기 때문에 임의로 범용할 수 없다.
코드 실행 시 레지스터에 인자값이 들어가는 순서는 아래와 같이 정해져 있다.
%rdi -> %rsi -> %rdx -> %rcx -> %rN
레지스터 역시 하드웨어인데 어떻게 데이터를 주고받을까? 바로 movq 명령어를 이용하는 방법이 있다. 기본 형식은 다음과 같다.
movq SOURCE, DEST
'SOURCE에서 DEST로 데이터를 옮기겠다'라는 의미이다. movq에서 q는 quadword의 약자로 8byte만큼 옮기겠다는 뜻이다. intel 기준으로 word(2byte), double(4byte), quadword(8byte)가 있다. 위 명령조합에서 SOURCE나 DEST에 위치할 수 있는 Operand type은 Immediate, Register, Memory이다. 그렇다고 이 세가지 타입들이 모두 SOURCE와 DEST에 위치할 수는 없다. 아래 관계도를 보자.
immediate는 상수를 의미한다. 표기는 상수 앞에 $를 붙이는 것이고 $0x400, $-533 등 자유롭게 쓸 수 있다. 따라서 상수는 source에만 위치할 수 있다. register는 결국엔 저장공간이기 때문에 source, dest에 모두 위치할 수 있다. memory는 레지스터에 의해 주어진 주소가 가리키는 공간을 의미한다. 따라서 (%rN)으로 표기해야 하고 source, dest에 모두 위치할 수 있다. 단, memory는 source와 dest에 동시에 위치할 수는 없다.
예를 들어 각각의 경우에 대한 명령문과 이를 c언어로 표기했을 때를 알아보자. (%rax에 저장된 주소값은 변수 temp1의 주소, %rdx에 저장된 주소값은 변수 temp2의 주소)
명령조합 | c Analog | |
ex1) | movq $-120, %rax | temp1 = -120; |
ex2) | movq $-120, (%rax) | *temp1 = -120; |
ex3) | movq %rax, %rdx | temp2 = temp1; |
ex4) | movq %rax, (%rdx) | *temp2 = temp1; |
ex5) | movq (%rax), %rdx | temp2 = *temp1; |
아래는 두 변수에 저장된 값을 swap하는 c코드를 변환한 어셈블리 예제이다.
//c코드
void swap(long *a, long *b){
long t0 = *a; --------(1)
long t1 = *b; --------(2)
*a = t1; --------(3)
*b = t0; --------(4)
}
//assembly코드
swap: movq (%rdi), %rax --------(1)
movq (%rsi), %rdx --------(2)
movq %rdx, (%rdi) --------(3)
movq %rax, (%rsi) --------(4)
ret
swap함수가 인자로 받은 *a, *b의 값이 각각 1, 2이고, 위 코드에 대한 예상 메모리 조직도가 아래 그림과 같다고 가정해보자. (100번지에 저장된 1은 *a의 값이기 때문에 변수 a에 저장된 값은 100, 마찬가지로 b는 400이다)
assembly 코드를 보면 알 수 있듯이 변수 t0는 레지스터 %rax, t1은 %rdx, 변수a는 %rdi, b는 %rsi에 저장되었다. 이 점을 기억하고 코드를 해석해보자.
코드 라인 (1).
변수 t0에 *a를 저장했다. 즉 t0 = 1이 되었으며 %rax에 메모리(%rdi)에 접근하여 얻은 데이터 1을 저장한다.
코드 라인(2).
변수 t1에 *b를 저장했다. 즉 t1 = 2가 되었으며 %rdx에 메모리(%rsi)에 접근하여 얻은 데이터 2를 저장한다.
코드 라인(3).
변수 a가 가리키는 주소 100번지에 t1을 저장한다. (100번지에 데이터 2가 저장된다) swap!
코드 라인(4).
변수 b가 가리키는 주소 400번지에 t0를 저장한다. (400번지에 데이터 1이 저장된다) swap!
따라서 swap 함수의 최종 실행 결과는 다음과 같을 것이다.
(이 글이 도움이 됐다면 광고 한번씩만 클릭 해주시면 감사드립니다, 더 좋은 정보글 작성하도록 노력하겠습니다 :) )
'간단 지식 > System Programming' 카테고리의 다른 글
06. Arithmetic operation of register, 레지스터와 변수의 관계 (0) | 2020.11.19 |
---|---|
05. movq와 leaq의 address 계산 방식 (0) | 2020.11.06 |
03. CPU와 Memory의 관계 (0) | 2020.11.02 |
02. 시스템의 컴파일 과정 (0) | 2020.10.29 |
01. byte 지향 메모리 조직 (0) | 2020.09.10 |