함수(프로시저)는 프로그램을 이해하기 쉽고 코드를 재사용할 수 있도록 만들어준다. 함수 호출 과정에서는 호출자와 호출 대상간의 관계가 형성된다. 호출자는 함수를 호출하는 프로그램, 호출 대상은 함수를 실행하는 프로시저이다. 호출 대상이 다른 함수를 호출하면 호출 대상은 호출자가 된다.
1) 호출자는 매개변수를 호출 대상이 접근할 수 있는 장소에 둔다.
2) 제어가 호출 대상으로 전달된다.
3) 호출 대상은 프로시저에게 필요한 저장 공간 자원을 할당한다.
4) 호출 대상은 필요한 작업을 수행한다.
5) 호출 대상은 결과 값을 호출자가 접근할 수 있는 장소에 놓는다.
6) 제어가 호출자로 반환된다.
1~3번은 함수 호출, 4번은 함수 실행, 5~6번은 함수 반환을 뜻한다.
함수 관련 레지스터는 다음과 같다.
$a0 ~ $a3 : 함수에 전달할 인자를 전달하는데 사용됨
$v0 ~ $v1 : 함수에서 반환할 값을 저장하는데 사용됨
$ra : 함수에서 호출한 지점으로 돌아가기 위한 반환 주소 저장
함수 관련 명령어는 다음과 같다.
jal : 함수 호출을 위한 점프 명령어, 함수의 시작 주소로 분기하고 반환 주소를 $ra 레지스터에 저장
jr : 레지스터에 저장된 주소로 점프하는 명령어, 함수의 반환처리를 위해 $ra 레지스터에 저장된 주소로 돌아감
따라서 함수의 수행 과정은 다음과 같다.
1) 호출자는 $a0 ~ $a3 레지스터에 매개변수를 넣고 jal X로 프로시저 X로 점프
2) 피호출자는 계산을 수행하고 $v0 ~ $v1 레지스터에 저장하고 jr $ra를 사용해서 호출자로 되돌아감
CPU는 현재 실행중인 명령어의 주소를 저장하는 프로그램 카운터(PC)를 갖고 있다. (MIPS에서, PC는 32개의 레지스터에 속해있진 않다) 명령어가 실행될 때마다 PC는 4씩 증가한다.
jal 명령어는 $ra에 PC+4를 저장한다. 프로시저 반환 이후 되돌아올 곳이 프로시저 호출의 다음 명령어이기 때문이다.
만약 프로시저에서 호출자가 사용하는 $s나 $t를 사용한다면 문제가 생길 수 있고, $a0~a3, $v0~$v1은 프로시저가 작동하는데 개수가 충분하지 않을 수 있다.
이의 해결책으로, 이러한 레지스터의 값은 스택이라는 메모리 영역에 저장된다. 스택 포인터는 $sp에 저장돼있고 push할 때 4만큼 감소, pop할 때 4만큼 증가한다.
만약 위와 같이 작성하면, 호출자가 사용하던 $t와 $s의 값이 달라져 문제가 발생한다.
따라서 호출자에서 $t0, $t1, $s0을 사용하고 있었다고 가정할 때 $sp (스택 포인터)를 3칸(12)만큼 뒤로 보내고, 스택에 $t0, $t1, $s0을 저장해둔다. 그리고 프로시저가 종료되면 스택에서 꺼내와 다시 원래 자리에 저장해준다.
사실 MIPS에서 모든 레지스터를 저장하고 복원하지는 않는다. 필요 없는 저장-복원 단계를 없애기 위해, 임시 레지스터인 $t 는 데이터가 보존되지 않고 저장된 레지스터인 $s를 저장하고 복원한다.
'학교강의필기장 > 컴퓨터구조' 카테고리의 다른 글
컴퓨터구조[7]: 프로시저 프레임과 메모리 공간 (0) | 2023.04.09 |
---|---|
컴퓨터구조[6]: 함수(프로시저)와 스택 포인터 2 - 재귀함수에서의 어셈블리 (0) | 2023.04.09 |
컴퓨터구조[4] : shift 연산과 조건문, 반복문 (0) | 2023.04.06 |
컴퓨터구조[3] : R-type & I-type (0) | 2023.04.06 |
컴퓨터구조[2] : instruction set, add, sub, addi, lw, sw (0) | 2023.04.06 |