TOOLS GAMES DEVLOG DOCS ABOUT CONTACT
HOME > DOCS > Architecture / ISA > RISC-V (RV32I) 레지스터 아키텍처 및 ABI 함수 호출 규약(Calling Convention)
Architecture / ISA Intermediate RISC-V RV32I / RARS Simulator / GCC

RISC-V (RV32I) 레지스터 아키텍처 및 ABI 함수 호출 규약(Calling Convention)

모던 오픈 아키텍처인 RISC-V 32비트 기본 정수(RV32I) 레지스터 32개의 ABI 명칭과 역할, Caller-Saved vs Callee-Saved 보존 규칙, 스택 프레임 프롤로그/에필로그 어셈블리.

#RISC-V #RV32I #Assembly #ABI #Computer Architecture
핵심 개념RISC-V는 UC 버클리에서 고안된 로열티 없는 모던 오픈 표준 ISA입니다. RV32I는 32개의 32비트 범용 레지스터(x0~x31)를 가지며, 표준 ABI(Application Binary Interface)에 따라 함수 인자 전달(a0~a7), 반환 주소(ra), 스택 포인터(sp), 임시 변수(t0~t6), 보존 레지스터(s0~s11)로 명확히 역할이 분담됩니다.

1. RV32I 레지스터 매핑 및 ABI 명칭

레지스터ABI 이름보존자 (Saver)용도 및 설명
x0zero-하드웨어 0 (항상 0이며 쓰기 무시)
x1raCallerReturn Address (함수 복귀 주소)
x2spCalleeStack Pointer (스택 최상단 포인터)
x3gp-Global Pointer (전역 데이터 접근 기준)
x4tp-Thread Pointer (스레드별 로컬 데이터)
x5 - x7t0 - t2Caller임시 레지스터 (호출 후 값 보존 안 됨)
x8 / x9s0 / s1 (fp)Callee보존 레지스터 / 프레임 포인터
x10 - x11a0 - a1Caller함수 인자 1~2 / 반환값 1~2
x12 - x17a2 - a7Caller함수 인자 3~8
x18 - x27s2 - s11Callee보존 레지스터 (함수 종료 시 복원 필수)
x28 - x31t3 - t6Caller추가 임시 레지스터

2. Caller-Saved vs Callee-Saved 보존 규칙

  • Caller-Saved (호출자 보존 - t0~t6, a0~a7, ra): 함수를 호출하는 쪽(Caller)에서 이 레지스터의 값이 유지되어야 한다면, 함수 호출(jal) 전에 자신의 스택에 저장해야 합니다. 피호출자는 이 레지스터를 자유롭게 덮어쓸 수 있습니다.
  • Callee-Saved (피호출자 보존 - s0~s11, sp): 호출을 당한 함수(Callee)는 이 레지스터를 사용하려면 진입 시 스택에 백업하고, 반환 직전에 원래 값으로 복원해 두어야 합니다.

3. 표준 함수 프롤로그 및 에필로그 어셈블리

riscv_function_convention.s
# C 함수: int compute(int a, int b) {
#     return helper(a + 5) + (b * 2);
# }

.globl compute
compute:
    # --- [프롤로그 (Prologue)] ---
    # 스택은 16바이트 단위 정렬(16-byte aligned) 필수
    addi sp, sp, -16      # 스택 프레임 16바이트 할당
    sw   ra, 12(sp)       # 다른 함수(helper)를 호출하므로 ra 저장
    sw   s0, 8(sp)        # Callee-saved 레지스터 s0 백업
    sw   s1, 4(sp)        # Callee-saved 레지스터 s1 백업

    # --- [본문 (Body)] ---
    mv   s0, a0           # 인자 a 저장
    slli s1, a1, 1        # s1 = b * 2 (좌측 시프트 1비트)

    addi a0, s0, 5        # helper의 첫 번째 인자 (a + 5)
    jal  ra, helper       # helper(a + 5) 호출 (반환값은 a0에 담김)

    add  a0, a0, s1       # 결과 = helper결과(a0) + (b * 2)(s1)

    # --- [에필로그 (Epilogue)] ---
    lw   ra, 12(sp)       # ra 복원
    lw   s0, 8(sp)        # s0 복원
    lw   s1, 4(sp)        # s1 복원
    addi sp, sp, 16       # 스택 프레임 반환
    ret                   # 복귀 (jalr zero, ra, 0)

4. MIPS 대비 RISC-V의 진보된 점

  • 지연 슬롯(Delay Slot) 제거: 분기 명령어 뒤에 무의미한 NOP이나 트릭을 넣을 필요가 없어 코드가 직관적이고 분기 예측기(Branch Predictor) 하드웨어와 완벽히 융합됩니다.
  • 더 넉넉한 인자 레지스터: MIPS는 인자 레지스터가 4개($a0~$a3)뿐이라 인자가 5개 이상이면 스택 메모리를 긁어야 했으나, RISC-V는 8개(a0~a7)를 제공하여 대부분의 함수 호출이 메모리 접근 없이 순수 레지스터로 완료됩니다.