Call Stack 이란?
- 실행중인 함수에 관한 정보를 담아 놓은 스택구조의 데이터 구조이다
예시
예를 들어 call_incr() 함수 내부에서 incr() 함수를 호출하는 상황에 대한 assembly과 call stack을 보자
먼저 용어에 대한 정리가 필요하다. A 함수 내부에서 B 함수를 호출할 때, A 함수를 Caller, B 함수를 Callee라고 부른다.
아래 예시에선 call_incr() 함수가 Caller, incr() 함수가 Callee 이다
그냥 단순한 incr 함수 내부 어셈블리다. 아래에서 call stack을 살펴보자
1. 먼저 call_incr() 함수 내부의 지역변수를 v1을 스택에 저장하는 모습이다
2. incr() 함수 호출에 필요한 인자를 레지스터로 넘겨주고 있다.
사실 인자를 넘겨줄 용도, return address를 저장할 용도 등, 레지스터마다의 용도가 정해져 있다. 아래 그림을 보자
위의 표대로 1번째, 2번째 인자를 %rdi, %esi로 넘겨주고 있다(접두사가 r이냐 e이냐는 레지스터가 64비트 크기이냐 32비트이냐 차이이다)
3. 맨위의 incr 함수 내부에서 보았듯이, x + val 결과값을 지역 변수 y에 담아서 리턴하므로, 스택에 리턴값을 저장하는 것을 볼 수 있다
4. 위의 레지스터 표에서 볼 수 있었듯이, return value를 %rax 레지스터에 저장하고 있다. 그리고 스택 포인터를 저장하는 %rsp 레지스터를 더해서 스택에서 더 이상 필요없는 공간을 정리한다.
5. ret instruction을 통해서 call_incr()를 호출해줬던 또 다른 caller가 스택에 저장해둔 return address로 jump한다.
참고로 x86/64 기준으로, 6개 이상의 인자를 넘겨야해서 스택으로 넘긴 인자들은 caller가 stack에서 정리한다고 한다
추가적으로 caller-saved/callee saved register에 대해 정리하고자 한다
caller가 callee를 호출하고 난 뒤에 값이 overwritten되는 것을 방지하기 위해 caller나 callee가 값을 백업해두고 함수가 종료되면 백업해둔 값으로 복원하는데, 이때 백업에 사용되는 레지스터중에 Caller가 백업할 때 쓰는 레지스터를 Caller-saved register라고 부르고, Callee가 백업에 사용하는데 쓰는 레지스터를 Callee-saved register라고 부른다.
중요한건, Callee saved register는 무조건 복원을 해줘야하는 것이다(Caller saved register는 필요할 때만 한다)
위에서 볼 수 있었듯이, %rbx, %rsp는 callee saved register 였다. 그래서 callee가 종료도기 전에 원래 값으로 복원해주는 것을 확인할 수 있다.
'공부 > System Programming & Computer Structure' 카테고리의 다른 글
Instruction Pipeline, Hazard 그리고 Branch Prediction (0) | 2022.09.10 |
---|---|
CPU구조와 내부 구성요소들의 역할 (0) | 2022.01.21 |