프로시저 호출은 소프트웨어에서 중요한 추상화이다. 이들은 지정된 인자들과 리턴 값으로 특정 기능을 구현하는 코드를 감싸주는 방법을 제공한다. 프로시저에 대한 기계어수준 지원을 제공할 때 처리되어야 하는 여러 가지 많은 특성들이 존재한다. 프로시저 P가 프로시저 Q를 호출하고, Q가 실행한 후에 다시 P로 리턴한다고 가정하자. 이러한 동작들은 다음과 같은 하나 이상의 메커니즘이 연관된다
- 제어권 전달: PC(프로그램 카운터) 는 진입 할 때 Q에 대한 코드의 시작주소로 설정되고, 리턴할 때는 P에서 Q를 호출하는 명령어 다음의 명령어로 설정되어야 한다.
- 데이터 전달: P는 하나 이상의 매개변수를 Q에 제공할 수 있어야 하며, Q는 다시 P로 하나의 값을 리턴할 수 있어야 함.
- 메모리 할당과 반납: Q는 시작할 때 지역변수들을 위한 공간을 할당할 수도 있고, 리턴할 때 이 저장소를 반납할 수 있다.
위에서 말한 메커니즘을 이해하기 위해 재귀 프로시저를 예시로 보겠다.
그 전에 프로시저에 대해 기억하면 좋을 것들에 대해 정리해 보겠다.
- 프로시저는 최대 6개의 정수 값들을 레지스터로 전송할 수 있지만, 만약 호출되는 프로시저가 더 많은 인자를 요구한다면 이들은 호출하기 전에 자신의 스택 프레임 내에 P에 의해 저장될 수 있다. 그래서 만약 인자가 수자 적다면 굳이 스택프레임을 요청하지 않는다.
- 제어의 이동에과 관련해서 예제 코드 실행 결과를 보자
여기서 %rsp 즉 스택 포인터와 PC값을 주목하면 좋겠다.
- 데이터 전송 부분에서 알아둬야 할 것은 인자 6개를 받을때 operand 사이즈에 따라 다르게 받는다
예를 들자면 첫번째 long 타입 인자 같은 경우 rdi로 받고, 그 뒤 int형 인자가 2번째라면 %edx로 받는다.
- 스택에서의 지역 저장공간이 필요한 이유는 위에서 언급한 것처럼 레지스터 수가 부족하고, 변수의 주소를 생성 할 수 있어야 하거나, 일부 지역변수들이 배열 또는 구조체여서 이들이 배열이나 구조체 참조로 접근되야 한다.
- 레지스터들은 모든 프로시저들이 공유하는 단일 자원의 역할을 한다. 비록 어떤 한순간에는 오직 하나의 프로시저만이 활성화될 수 있지만, 하나의 호출자프로시저가 다른 피호출자를 호출할 때, 피호출자는 호출자가 나중에 사용할 계획인 일부 레지스터 값은 덮어 쓰지 않는다. 관습적으로 레지스터 %rbx, %rpb, %r12- %r15는 피호출자-저장 레지스터로 구분한다.
이제 재귀 프로시저를 한번 보자
아래는 c코드다
long rfact(long n)
{
long result;
if(n<=1)
result =1;
else
result =n * rfact(n-1);
return result
다음은 생성된 어셈블리 코드다
먼저 스택에 기존 값을 보관한 후에 레지스터 %rbx를 매개변수 n을 저장하기 위해 사용한다. 이건 나중에 재귀적으로 rfact함수가 호출 된 뒤 결과값과 다시 계산하기 위해 사용된다.
'CS' 카테고리의 다른 글
CPU 가상화 (0) | 2021.10.04 |
---|---|
커널 레벨 쓰레드와 유저 레벨 쓰레드 (0) | 2021.10.02 |
전통적 동기화 문제와 데드락 (0) | 2021.09.26 |
동시성 이슈 (0) | 2021.09.25 |
Nonlocal jumps (0) | 2021.09.24 |