

프로시저... 맛있는 시저...
죄송합니다.
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의 시작 주소로 점프해, 제어권 전달retP로 복귀%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바이트 증가
선생님 진도가 너무 빠ㅣㄹ러요