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) | 용도 및 설명 |
|---|---|---|---|
| x0 | zero | - | 하드웨어 0 (항상 0이며 쓰기 무시) |
| x1 | ra | Caller | Return Address (함수 복귀 주소) |
| x2 | sp | Callee | Stack Pointer (스택 최상단 포인터) |
| x3 | gp | - | Global Pointer (전역 데이터 접근 기준) |
| x4 | tp | - | Thread Pointer (스레드별 로컬 데이터) |
| x5 - x7 | t0 - t2 | Caller | 임시 레지스터 (호출 후 값 보존 안 됨) |
| x8 / x9 | s0 / s1 (fp) | Callee | 보존 레지스터 / 프레임 포인터 |
| x10 - x11 | a0 - a1 | Caller | 함수 인자 1~2 / 반환값 1~2 |
| x12 - x17 | a2 - a7 | Caller | 함수 인자 3~8 |
| x18 - x27 | s2 - s11 | Callee | 보존 레지스터 (함수 종료 시 복원 필수) |
| x28 - x31 | t3 - t6 | Caller | 추가 임시 레지스터 |
2. Caller-Saved vs Callee-Saved 보존 규칙
- Caller-Saved (호출자 보존 - t0~t6, a0~a7, ra): 함수를 호출하는 쪽(
Caller)에서 이 레지스터의 값이 유지되어야 한다면, 함수 호출(jal) 전에 자신의 스택에 저장해야 합니다. 피호출자는 이 레지스터를 자유롭게 덮어쓸 수 있습니다. - Callee-Saved (피호출자 보존 - s0~s11, sp): 호출을 당한 함수(
Callee)는 이 레지스터를 사용하려면 진입 시 스택에 백업하고, 반환 직전에 원래 값으로 복원해 두어야 합니다.
3. 표준 함수 프롤로그 및 에필로그 어셈블리
# 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)를 제공하여 대부분의 함수 호출이 메모리 접근 없이 순수 레지스터로 완료됩니다.