본문 바로가기
컴퓨터시스템구조

[컴퓨터시스템구조] 09. MIPS 프로시저 호출(jal, jr)

by 파스텔코랄 2023. 10. 14.

프로시저 호출 단계

  1. 레지스터에 매개변수 배치
  2. 제어권을 프로시저에게 줌
  3. 프로시저(callee)를 위한 저장공간 확보
  4. 프로시저의 작업 수행
  5. caller를 위한 레지스터 결과 배치
  6. 호출 장소로 돌아가기

 


 

레지스터

$v0~1 결과값
64비트일수도 있기때문에 2개 존재
(레지스터 2~3)
$a0~3 아규먼트 (레지스터 4~7)
$t0~9 임시값
callee가 덮어쓸수 있다.
(레지스터 8~15)
$s0~7 saved
callee에 의해 저장/복원
(레지스터 16~23)
$gp 정적 데이터를 위한 글로벌 포인터 (레지스터 28)
$sp 스택 포인터 (레지스터 29)
$fp 프레임 포인터 (레지스터 30)
$ra 리턴 주소 (레지스터 31)

 


 

프로시저 호출 명령어

  • 프로시저 호출: jump & link
jal ProcedureLabel
  • 1. $ra다음 명령어의 주소를 넣는다.
  • 2. 타겟 주소로 점프

 

  • 프로시저 리턴: jump register
jr $ra
  • 1. $ra프로그램 카운터를 복사한다.
  • 2. 계산된 점프에도 사용할 수 있다.
    • 예를 들어, Case/Switch 명령문

 


 

말단/비말단 프로시저

  • caller : 상수를 호출하는 부분
  • callee : 상수를 호출 당하는 부분
    • 스택에 $s0~1와 같은 saved 레지스터 값을 저장했다가 복구한다.

 


 

말단(Leaf) 프로시저

  • 말단 프로시저(Leaf Procedures) : 함수가 더이상 다른 함수를 호출하지 않는 경우

 

말단(Leaf) 프로시저 예제

  • C 코드
int leaf_example (int g, h, i, j) {
    int f;
    f = (g + h) - (i + j);
    return f;
}
  • 아규먼트 g, h, i, j : $a0, $a1, $a2, $a3
  • f : $s0 (따라서 스택에 $s0를 저장)
  • 결과 : $v0

 

  • MIPS 코드
leaf_example:
    addi $sp, $sp, -4                          # callee가 overwrite하지 않도록
    sw $s0, 0($sp)                             # 스택에 기존 변수 $s0을 저장
    add $t0, $a0, $a1                         # 프로시저 바디
    add $t1, $a2, $a3                         # 프로시저 바디
    sub $s0, $t0, $t1                           # 프로시저 바디
    add $v0, $s0, $zero                      # 결과
    lw $s0, 0($sp)                              # 스택의 $s0를 불러오고
    addi $sp, $sp, 4                           # 스택 오프셋 조정
    jr $ra                                             # 리턴하여 다음 명령어 수행

 


 

비말단(Non-Leaf) 프로시저

  • 비말단 프로시저(Non-Leaf Procedures) : 다른 프로시저를 호출하는 프로시저
  • 중첩 호출의 경우 caller는 스택에 저장
    • 리턴 주소
    • 호출 후 필요한 모든 인수 및 임시값
  • 호출 후 스택에서 복원

 

비말단(Non-Leaf) 프로시저 예제

  • C 코드
    • 팩토리얼 재귀함수
int fact (int n) {
    if (n < 1) return 1;
    else return n * fact(n - 1);
}
  • 인수 n : $a0
  • 결과값 : $v0

 

  • MIPS 코드
fact:
        addi $sp, $sp, -8                      # callee가 overwrite하지 않도록
        sw $ra, 4($sp)                           # 스택에 $ra 저장
        sw $a0, 0($sp)                          # 스택에 $a0 저장
        slti $t0, $a0, 1                            # 만약 n<1 참이면 $t0 = 1, 거짓이면 $t0 = 0
        beq $t0, $zero, L1                      # 거짓이면 L1으로 이동
        addi $v0, $zero, 1                      # 참이면 $v0 = 1
        addi $sp, $sp, 8                          # 스택 오프셋 복원
        jr $ra                                          # 리턴
L1:
        addi $a0, $a0, -1                        # n을 감소시켜
        jal fact                                       # fact(n-1) 재귀 함수 호출
        lw $a0, 0($sp)                           # 스택에 저장했던 원래 n값($a0) 불러오기
        lw $ra, 4($sp)                            # 스택에 저장했던 원래 리턴주소($ra) 불러오기
        addi $sp, $sp, 8                        # 스택 오프셋 복원
        mul $v0, $a0, $v0                      # $v0 = n * $v0
        jr $ra                                          # 리턴

 


 

스택의 로컬 데이터

  • callee가 할당한 로컬 데이터
    • 예: C 자동 변수
  • 프로시저 프레임(활성화 레코드)
    • 일부 컴파일러에서 스택 저장소를 관리하는 데 사용

 


 

메모리 레이아웃

  • 텍스트(Text) : 프로그램 코드
  • 정적 데이터(Static data) : 전역 변수
    • C의 정적 변수 : 상수 배열, 문자열
    • $gp는 이 세그먼트에 ±오프셋을 허용하는 주소로 초기화
  • 동적 데이터(Dynamic data) : 힙
    • C : malloc, Java : new
  • 스택(Stack) : 자동 저장

  • 스택이랑 힙이랑 만났을때 스택오버플로우 발생

댓글