프로시저... 맛있는 시저...
죄송합니다.
push
로 데이터를 추가, pop
으로 데이터를 제거pop
할 땐 제일 최근에 push
한 데이터가 먼저 제거됨push
한 원소는 최상단에 위치%rsp
로 나타냄%rbp
에 저장된 8바이트 값을 푸시subq $8, %rsp
: 스택 포인터의 주소를 8 감소. 여기서는 8바이트 값이므로 8을 감소시킨 것movq %rbp, (%rsp)
: 레지스터 %rbp
에 저장된 값을, 스택포인터 (%rsp)
가 가리키는 메모리 주소로 복사pushq %rbp
로 한꺼번에 수행 가능%rax
에 저장movq (%rsp), %rax
: 스택포인터 (%rsp)
가 가리키는 메모리 주소의 값을, 레지스터 %rax
로 복사addq $8, %rsp
: 스택포인터의 주소를 8 증가. 여기선 8바이트 값이므로, 8을 증가시킨 것popq %rax
로 한꺼번에 수행 가능⚠️ 런타임 스택은 전에 배웠듯이 주소가 낮을수록 상단, 높을수록 하단입니다. 스택의 데이터가 입출력되는 쪽이 상단입니다. 헷갈리지 않으시길 바랍니다.
call Q
:P
의 call
명령어 다음 명령어의 주소Q
의 시작 주소로 점프해, 제어권 전달ret
P
로 복귀%rip
)에 저장된 주소가 변경됨0x400563
에서 callq 400540
을 실행0x400540
에 다른 프로시저가 있다고 가정0x400568
를 스택에 푸시 (callq
다음 명령어의 주소)0x400540
으로 점프ret
실행0x400568
를 팝ret
명령어 이후 레지스터 %rax
를 통해 전달됨%rdi
-> %rsi
-> %rdx
-> %rcx
-> %r8
-> %r9
순으로 레지스터로 전달됨%rsp
의 주소에 8
의 단위를 더해 접근 (e.g., %rsp + 8
)call
명령어 실행 전에 레지스터로 전달되고 스택에 푸시됨call
명령어 실행 시, 리턴 주소가 푸시됨void proc(long a1, long *a1p, int a2, int *a2p, short a3, short *a3p, char a4, char *a4p){
// 내부 코드는 생략
}
long
: 8바이트, int
: 4바이트, short
: 2바이트, char
: 1바이트*
붙은 변수들): 모두 8바이트a4
, 8번째 인수 *a4p
는 스택에 저장⚠️ 위 그림은 8바이트 데이터를 한 줄로, 8바이트 내 더 작은 데이터는 각 줄 안의 칸으로 표시했음에 유의하세요.
&
)를 사용해 메모리 주소가 필요하거나🤔 주소... 연산자요? 그게 뭔 소리에요?
&변수
와 같이 사용하면 해당 변수의 주소를 반환합니다.&변수
사용 전 변수를 선언할 때, 레지스터가 아닌 메모리에 저장시켜 놓아야 합니다.long call_proc(){
long x1 = 1; // 8바이트
int x2 = 2; // 4바이트
short x3 = 3; // 2바이트
char x4 = 4; // 1바이트
proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4)
// 주소 (&x1, &x2, &x3, &x4)는 모두 8바이트
return (x1 * x2 + x3 * x4)
}
x1
, x2
, x3
, x4
를 스택에 푸시proc
호출 직전, 인수 중 1~6번째 인수는 레지스터로 전달, 7번째 인수인 x4
와 8번째 인수인 &x4
는 스택에 푸시proc
이 호출되지 않아 리턴 주소는 아직 푸시되지 않았음에 유의할 것%rbx
, %rbp
, %r12
-%r15
6개의 범용 레지스터%rsp
(스택 포인터)를 제외한 범용 레지스터long P(long x, long y){
long u = Q(y);
long v = Q(x);
return u + v
}
Q(y)
가 호출되는 동안 P
의 매개변수 x
의 값을 유지시켜야 함
Q(x)
가 호출되는 동안 Q(y)
의 반환값 u
의 값을 유지시켜야 함
따라서 두 값은 비호출자 저장 레지스터에 저장
실제 과정
P
시작 시 피호출자 저장 레지스터 %rbp
, %rbx
의 이전 값을 pushq %rbp
, pushq %rbx
로 푸시Q(y)
호출 전 x
를 %rbp
에 복사Q(x)
호출 전 u
를 %rbx
에 복사P
종료 시 %rbp
, %rbx
의 이전 값을 popq %rbx
, popq %rbp
로 팝해 원상태로 복귀call
) 다음 순서대로 값이 푸시됨ret
)T A[N]
와 같이 배열을 선언할 때i
번째 원소는 주소 에 저장됨int A[6]
의 시작 위치가 일 대int
는 4바이트이므로 총 24바이트가 할당됨i
번째 원소의 주소는 A
의 시작 주소가 %rdx
, i
가 %rcx
에 저장되어 있을 때A[i]
의 주소는 (%rdx, %rcx, 4)
로 계산 -> rdx + 4 * %rcx
와 동일movl (%rdx, %rcx, 4), %eax
: A[i]
의 값을 레지스터 %eax
로 복사p
가 크기의 자료형 데이터에 대한 포인터이고, 값이 일 때p + i
는 로 계산됨&Expr
: 객체 Expr
의 주소를 갖는 포인터 생성*AExpr
: 주소 AExpr
에 대해 주소에 위치한 값을 줌A[i]
는 *(A + i)
와 동일A
는 주소처럼 동작해, 포인터 연산 가능A + i
는 (A의 시작 주소) + (자료형 크기) * i
로 연산됨int A[5][3]
과 같이 이차원 배열을 생성했을 때A
는 5개의 배열을 원소로 가짐int
를 저장하기 위해 12바이트를 필요로 함A[0]
) 이후 행 1(A[1]
), 행 2(A[2]
).... 순으로 저장됨D[R][C]
의 원소 D[i][j]
의 주소D
의 시작 주소를 로 둘 때i
행 j
열의 원소는 C \times i + j
번째 (0으로 시작)로 저장된다는 점에 유의int A[3][4]
로 선언했을 때, A[i][j]
에 접근)leaq (%rsi, %rsi, 2), %rax
; %rsi * 3 → 3i 계산 (즉, %rax = 3 × i)
leaq (%rdi, %rax, 4), %rax
; A + 4 × (3i) → %rax = A + 12 × i
movl (%rax, %rdx, 4), %eax
; %rax + 4 × j → A + 12i + 4j 위치의 값 → %eax에 저장
🤔 왜 첫 두 명령어는 leaq
, 마지막은 movl
이 사용되었나요?
leaq
가 사용되었습니다. movl
를 사용해야겠죠.// 최적화 전
int A[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
printf("%d", A[i]);
}
i
대신 포인터로 배열에 접근하게끔 연산이 변경됨int A[5] = {1, 2, 3, 4, 5};
int* p;
for (p = A; p < A + 5; p++) {
printf("%d", *p);
}
p
의 초깃값은 배열의 시작 주소 (첫 원소의 주소, &A[0]
)*p
로 현재 포인터에 저장된 주소의 값에 접근한 뒤, p++
로 포인터 연산 수행int
형은 4바이트이므로, p++
로 포인터는 4바이트 증가
선생님 진도가 너무 빠ㅣㄹ러요