---
schema_version: 1
exam_id: "sose26-probeklausur"
aufgabe: 3
unit_id: "3-6"
source_group_id: "3"
official_exam: true
official_solution: true
language: "ko"
---

# 3-6 — swap 호출과 Epilog

> **학습 목표:** ABI argument register에 `swap(arr,j,j+1)`을 배치하고 call 뒤 s-register의 loop state로 반복을 진행한 뒤, 저장 때의 동일 offset에서 모든 register를 복원하고 stack frame을 해제한다.
>
> **왜 따로 배우는가:** 함수 호출은 단순한 jump가 아니라 register ownership 경계입니다. argument 준비, caller-saved invalidation, loop state 유지, stack 복원의 순서를 하나의 lifecycle로 묶어야 sort가 여러 swap 뒤에도 올바르게 계속되고 원래 caller로 돌아갑니다.
>
> **범위:** Aufgabe 3 · 8점 Aufgabe의 최소 학습 단위 · 시험 p7–8 · 공식 해설 p8–11

## 1. 문제 원문 (Deutsch)

이 unit은 공식 Teilaufgabe 또는 표의 한 행·한 access를 학습 가능한 최소 단위로 다시 나눈 것입니다. 아래 전사는 source group `3`의 공식 독일어 문제 원문이며, 페이지 이미지를 바로 뒤에 함께 두어 코드·표·도식을 대조할 수 있게 했습니다.

~~~text
Aufgabe 3: C in RISC-V übersetzen

void swap(int *arr, int i, int j)
{
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

void bubbleSort(int *arr, int n)
{
    for (int i = 0; i < n - 1; i++){
        for (int j = 0; j < n - i - 1; j++){
            if (arr[j] > arr[j + 1])
                swap(arr, j, j + 1);
        }
    }
}

Implementieren Sie die vorgestellten Funktionen in Assembler. Der von Ihnen geschriebenen Code muss die gleiche Funktionalität haben und möglichst nah an der Implementierung von C sein.

Hinweise:

• Es ist garantiert, dass das Array arr mindestens ein Element enthält.
• Variable n speichert die Länge von arr.
• Verwenden Sie innerhalb der Funktionen folgende Registerbelegung zur Speicherung der Variablen: s0 = arr, s1 = n, s2 = i, s3 = j
• Jede Funktion muss man als Unterprogramm betrachten.

Lösung:
~~~

![공식 시험 원문 — exam-p07.png](../../assets/exam-p07.png)

*공식 시험 원문: `exam-p07.png` · Probeklausur.pdf · PDF p.7*

![공식 시험 원문 — exam-p08.png](../../assets/exam-p08.png)

*공식 시험 원문: `exam-p08.png` · Probeklausur.pdf · PDF p.8*

### 이 unit에서 풀 항목

argument를 놓고 swap을 호출한 뒤 loop와 function을 끝내세요.

## 2. 쉬운 한국어 해석

문제 3: C를 RISC-V로 번역하기

void swap(int *arr, int i, int j)
{
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

void bubbleSort(int *arr, int n)
{
    for (int i = 0; i < n - 1; i++){
        for (int j = 0; j < n - i - 1; j++){
            if (arr[j] > arr[j + 1])
                swap(arr, j, j + 1);
        }
    }
}

위에 제시된 함수들을 어셈블리로 구현하시오. 작성한 코드는 C 구현과 동일한 기능을 가져야 하며, 가능한 한 C 구현에 가깝게 작성해야 한다.

주의사항:

• 배열 arr에는 적어도 한 개의 원소가 있음이 보장된다.
• 변수 n에는 배열 arr의 길이가 저장되어 있다.
• 함수 안에서 변수를 저장할 때 다음 레지스터 배치를 사용하시오: s0 = arr, s1 = n, s2 = i, s3 = j.
• 각 함수는 하나의 서브루틴으로 간주해야 한다.

답안:

### 이 unit에 해당하는 최소 작업

argument를 놓고 swap을 호출한 뒤 loop와 function을 끝내세요.

이 문제에서 최종 결과만 맞히는 것보다 중요한 것은 `3-6`에 필요한 중간 판단을 답안지에 남기는 것입니다.

## 3. 무엇을 묻는 문제인가

### 공식 문제의 요구사항

- C 함수 swap을 동일한 동작의 RISC-V 어셈블리 서브루틴으로 구현한다.
- C 함수 bubbleSort를 중첩 반복문, 조건문, swap 호출까지 보존하여 RISC-V 어셈블리로 구현한다.
- 배열 원소 주소 계산, 함수 호출, 레지스터 보존, 반환 동작을 포함해 C 의미와 RISC-V 호출 규약을 함께 만족시킨다.

### 조건과 제약

- arr에는 최소 한 원소가 있다.
- n은 arr의 길이이다.
- 함수 내부 변수 배치는 s0=arr, s1=n, s2=i, s3=j로 지정되어 있다.
- swap과 bubbleSort를 각각 독립적인 Unterprogramm으로 취급한다.
- 작성한 어셈블리의 기능은 C와 같아야 하며 가능한 한 C 구조에 가깝게 작성해야 한다.

### 원문 오탈자·도식 관련 주의

- 시험 PDF p7에 문제와 코드가 있고 p8은 'Lösung:'만 있는 빈 답안 페이지이다.
- 원문의 'Jede Funktion muss man als Unterprogramm betrachten.'는 다소 어색한 독일어 표현이지만 의미를 수정하지 않고 보존했다.
- [문제–공식 해설 차이] 원문은 'innerhalb der Funktionen'에서 s0=arr, s1=n, s2=i, s3=j를 사용하라고 읽히지만, 공식 swap 해설은 a/t-register만 사용하고 이 지정 배치를 bubbleSort의 장기 상태에 적용한다. 공식 코드는 ABI상 유효하나 문구를 문자 그대로 적용한 답과는 차이가 있다.
- 코드 들여쓰기는 PDF 시각 확인 결과에 맞춰 정리했으며 식, 조건, 레지스터 이름과 상수는 변경하지 않았다.

### 도식 시각 확인 상태

- **도식 의존 여부:** 이 최소 unit의 핵심 판정은 텍스트·수식·코드로 재현할 수 있습니다.
- **공식 페이지 상태:** exam-p07.png, exam-p08.png, solution-p08.png, solution-p11.png가 원문 대조용으로 렌더되어 연결되어 있습니다.

### 이 unit의 학습 목표

- **관찰 가능한 목표:** ABI argument register에 `swap(arr,j,j+1)`을 배치하고 call 뒤 s-register의 loop state로 반복을 진행한 뒤, 저장 때의 동일 offset에서 모든 register를 복원하고 stack frame을 해제한다.
- **출제 의도:** 함수 호출은 단순한 jump가 아니라 register ownership 경계입니다. argument 준비, caller-saved invalidation, loop state 유지, stack 복원의 순서를 하나의 lifecycle로 묶어야 sort가 여러 swap 뒤에도 올바르게 계속되고 원래 caller로 돌아갑니다.
- **공식 근거 범위:** SoSe26 Probeklausur 시험 p7–8 Aufgabe 3의 swap call·loop 종료·function 반환과 공식 해설 p11의 argument setup, 증가 label, Epilog 범위.
- **Aufgabe 공통 선수지식:** C variable을 register에 배치하고, array index를 byte offset으로 바꾸고, loop 조건을 ‘끝나면 탈출’ branch로 뒤집습니다. 다른 함수를 부르면 ra와 오래 살아야 하는 값을 stack/s-register로 보호합니다.

## 4. 선수 개념과 핵심 용어

### argument registers

- **뜻:** RISC-V ABI에서 첫 함수 인자를 전달하는 a0, a1, a2 등의 caller-saved register입니다.
- **이 문제에서:** swap의 arr, j, j+1을 각각 a0, a1, a2에 둡니다.

### jal

- **뜻:** target 함수로 이동하면서 return address `PC+4`를 ra에 기록하는 call instruction입니다.
- **이 문제에서:** `jal ra,swap`으로 swap을 호출합니다.

### caller-saved

- **뜻:** callee가 보존할 의무가 없어 call 뒤 값이 바뀔 수 있는 a/t register 분류입니다.
- **이 문제에서:** swap 뒤 temporary를 신뢰하지 않고 s0–s3의 arr,n,i,j를 계속 사용합니다.

### Epilog

- **뜻:** 저장한 register를 복원하고 stack frame을 해제한 뒤 반환하는 함수 끝 instruction 묶음입니다.
- **이 문제에서:** ra와 s0–s3를 현재 sp 기준으로 load한 후 `sp+=32; ret`합니다.

### 이 문제에 적용할 풀이 규칙

### 규칙 1. ABI argument 순서 규칙

C call `swap(arr,j,j+1)`의 첫 세 인자는 차례대로 a0, a1, a2에 놓습니다.

- **시험지에 남길 것:** call 직전 register 표에 `a0=arr, a1=j, a2=j+1`을 씁니다.

### 규칙 2. call 경계 kill 규칙

jal 이후 a/t register는 callee가 바꿀 수 있으므로 loop 지속에 필요한 arr,n,i,j는 s0–s3에서 다시 읽습니다.

- **시험지에 남길 것:** call 선을 긋고 a/t 값에 `unknown after call` 표시를 합니다.

### 규칙 3. loop 증가 위치 규칙

swap 여부와 상관없이 inner iteration 끝에서는 j++, inner loop 종료 뒤에는 i++가 정확한 label에서 실행되어야 합니다.

- **시험지에 남길 것:** `inner_inc: s3++`, `outer_inc: s2++`를 서로 다른 label 아래 둡니다.

### 규칙 4. Epilog frame-base 규칙

saved register는 저장 때 사용한 동일 offset에서, 아직 frame을 가리키는 현재 sp로 모두 load한 뒤에만 sp를 원래 값으로 되돌릴 수 있습니다. 개별 register를 load하는 순서 자체는 ABI가 강제하지 않습니다.

- **시험지에 남길 것:** `loads → addi sp,sp,32 → ret` 순서를 상자로 묶습니다.

### 공식 문제 전에 푸는 작은 유사 예제

**문제:** s0에 arr base `0x5000`, s3에 j=4가 있다. `swap(arr,4,5)` call 준비와 call 뒤 inner index 증가를 쓰세요.

**주어진 것**

- swap의 argument 순서는 arr, i, j입니다.
- s0와 s3는 callee-saved라 swap 호출 뒤에도 유지됩니다.

### 예제 단계 1. 세 argument를 ABI register에 배치합니다.

- **왜:** callee swap이 표준 위치에서 arr와 두 index를 읽습니다.
- **종이에:** `mv a0,s0  # 0x5000; mv a1,s3  # 4; addi a2,s3,1  # 5`

### 예제 단계 2. jal로 swap을 호출하고 call 경계를 표시합니다.

- **왜:** ra에는 돌아올 주소가 기록되고 a/t 값은 이후 보장되지 않습니다.
- **종이에:** `jal ra,swap; a/t after call = unknown`

### 예제 단계 3. 보존된 s3를 사용해 j를 증가합니다.

- **왜:** argument a1이 보존된다고 가정하지 않고 장기 loop state를 이어갑니다.
- **종이에:** `addi s3,s3,1  # j=5; j inner_loop`

**예제 정답:** `mv a0,s0; mv a1,s3; addi a2,s3,1; jal ra,swap; addi s3,s3,1; j inner_loop`이며 call에는 (arr,4,5)가 전달됩니다.

**독립 검산:** call 뒤 j 증가 source가 a1이 아니라 s3인지 확인합니다.

## 5. 공식 정답 요약

**공식 결론:** 공식안은 `mv a0,s0; mv a1,s3; addi a2,s3,1; jal ra,swap`; 이어 `addi s3,s3,1; j inner_loop`, `addi s2,s2,1; j outer_loop`; `done`에서는 `lw ra,0(sp); lw s0,4(sp); lw s1,8(sp); lw s2,12(sp); lw s3,16(sp); addi sp,sp,32; ret`입니다. Load의 역순은 요구되지 않으며 동일 offset과 sp 복원 전 load가 핵심입니다.

### 시험지 문장별 정답 해설

아래는 시험지의 항목 순서를 그대로 유지합니다. 각 항목에서 원문 → 한국어 번역 → 정답 → 판단 근거 → 실제로 쓸 답을 바로 이어서 읽으세요.



### 현재 소문제의 요구·판단·답을 한 흐름으로 연결

### `3-6`에서 정확히 답할 대상

- **시험지가 요구하는 답:** argument를 놓고 swap을 호출한 뒤 loop와 function을 끝내세요.
- **이 소문제의 공식 답:** 공식안은 `mv a0,s0; mv a1,s3; addi a2,s3,1; jal ra,swap`; 이어 `addi s3,s3,1; j inner_loop`, `addi s2,s2,1; j outer_loop`; `done`에서는 `lw ra,0(sp); lw s0,4(sp); lw s1,8(sp); lw s2,12(sp); lw s3,16(sp); addi sp,sp,32; ret`입니다. Load의 역순은 요구되지 않으며 동일 offset과 sp 복원 전 load가 핵심입니다.

### 판단 1. a0=arr, a1=j, a2=j+1을 만듭니다.

- **왜:** swap이 기대하는 첫 세 argument를 RISC-V ABI 순서로 전달해야 합니다.
- **이 판단에서 답안지에 남길 것:** `mv a0,s0; mv a1,s3; addi a2,s3,1`

### 판단 2. `jal ra,swap`을 실행하고 caller-saved 값을 더 이상 신뢰하지 않습니다.

- **왜:** jal은 ra를 새 link로 덮고 swap은 a/t register를 자유롭게 사용할 수 있습니다.
- **이 판단에서 답안지에 남길 것:** `jal ra,swap` 아래에 `a0–a2,t0–t6 may change`를 표시합니다.

### 판단 3. inner_inc에서 s3의 j를 증가시키고 inner_loop로 돌아갑니다.

- **왜:** swap을 했든 skip했든 현재 이웃 쌍 처리가 끝났으므로 다음 j를 검사해야 합니다.
- **이 판단에서 답안지에 남길 것:** `inner_inc: addi s3,s3,1; j inner_loop`

### 판단 4. outer_inc에서 s2의 i를 증가시키고 outer_loop로 돌아갑니다.

- **왜:** inner bound에 도달한 뒤에만 다음 pass로 넘어가고 새 pass에서 j가 다시 0으로 reset됩니다.
- **이 판단에서 답안지에 남길 것:** `outer_inc: addi s2,s2,1; j outer_loop`

### 판단 5. done에서 s0–s3와 ra를 load한 뒤 sp를 32만큼 복원하고 ret합니다.

- **왜:** 현재 sp가 frame base를 가리킬 때 saved 값을 읽어야 하며, 그 다음 caller의 stack와 return address로 돌아갈 수 있습니다.
- **이 판단에서 답안지에 남길 것:** `lw ra,0(sp); lw s0,4(sp); lw s1,8(sp); lw s2,12(sp); lw s3,16(sp); addi sp,sp,32; ret`

### 위 판단이 최종 답으로 이어지는 이유

ABI에서 첫 세 argument는 a0,a1,a2입니다. s0–s3는 호출 뒤에도 유지되어 loop 상태를 계속 쓸 수 있습니다. Epilog의 개별 load 순서는 ABI가 강제하지 않으며, 저장 때 사용한 동일 offset에서 모두 읽고 sp를 올리기 전에 끝내는 것이 핵심입니다.

### 답안 작성 시 주의

swap 호출 뒤 t-register 값은 믿지 마세요. loop에서 계속 필요한 값은 s-register에 있습니다. Epilog load 순서를 ABI 규칙으로 오해하지 마세요.

![공식 Musterlösung/Hinweise — solution-p08.png](../../assets/solution-p08.png)

*공식 Musterlösung/Hinweise: `solution-p08.png` · Probeklausur Musterlösung und Hinweise.pdf · PDF p.8*

![공식 Musterlösung/Hinweise — solution-p11.png](../../assets/solution-p11.png)

*공식 Musterlösung/Hinweise: `solution-p11.png` · Probeklausur Musterlösung und Hinweise.pdf · PDF p.11*

## 6. 풀이 전략

1. a0=arr, a1=j, a2=j+1을 만듭니다.
2. `jal ra,swap`을 실행합니다.
3. j를 증가하고 inner_loop로 돌아갑니다.
4. inner loop 종료 후 i를 증가합니다.
5. done에서 동일 offset의 s-register와 ra를 모두 복원한 뒤 sp를 되돌립니다.

## 7. 단계별 상세 풀이

### 단계 1. a0=arr, a1=j, a2=j+1을 만듭니다.

- **이유:** swap이 기대하는 첫 세 argument를 RISC-V ABI 순서로 전달해야 합니다.
- **종이에 남길 것:** `mv a0,s0; mv a1,s3; addi a2,s3,1`
- **완료 기준:** call 직전 a0, a1, a2의 의미가 각각 arr, j, j+1로 표시되어 있습니다.
- **막힐 때 힌트:** arr와 j 원본은 보존된 s0와 s3에서 복사하세요.
- **가장 흔한 첫 오류:** a1=j+1, a2=j로 두 index 순서를 바꾸거나 a0에 `&arr[j]`를 전달하는 것입니다.

### 단계 2. `jal ra,swap`을 실행하고 caller-saved 값을 더 이상 신뢰하지 않습니다.

- **이유:** jal은 ra를 새 link로 덮고 swap은 a/t register를 자유롭게 사용할 수 있습니다.
- **종이에 남길 것:** `jal ra,swap` 아래에 `a0–a2,t0–t6 may change`를 표시합니다.
- **완료 기준:** call 뒤 이어지는 loop code가 a/t의 이전 값에 의존하지 않습니다.
- **막힐 때 힌트:** loop state는 Prolog에서 s0–s3에 보존했습니다.
- **가장 흔한 첫 오류:** swap이 끝난 뒤 a1에 여전히 j가 있다고 가정해 a1을 증가시키는 것입니다.

### 단계 3. inner_inc에서 s3의 j를 증가시키고 inner_loop로 돌아갑니다.

- **이유:** swap을 했든 skip했든 현재 이웃 쌍 처리가 끝났으므로 다음 j를 검사해야 합니다.
- **종이에 남길 것:** `inner_inc: addi s3,s3,1; j inner_loop`
- **완료 기준:** swap path와 no-swap path가 모두 같은 inner_inc label로 합쳐집니다.
- **막힐 때 힌트:** ble가 taken된 target도 이 label이어야 합니다.
- **가장 흔한 첫 오류:** swap한 경우에만 j를 증가시키거나 j++ 뒤 outer_loop로 직접 가는 것입니다.

### 단계 4. outer_inc에서 s2의 i를 증가시키고 outer_loop로 돌아갑니다.

- **이유:** inner bound에 도달한 뒤에만 다음 pass로 넘어가고 새 pass에서 j가 다시 0으로 reset됩니다.
- **종이에 남길 것:** `outer_inc: addi s2,s2,1; j outer_loop`
- **완료 기준:** inner 종료 branch가 outer_inc로 오고 outer_loop에서 guard와 j reset을 다시 실행합니다.
- **막힐 때 힌트:** s3=j가 아니라 s2=i를 증가시킵니다.
- **가장 흔한 첫 오류:** inner loop의 매 iteration마다 i도 증가시키는 것입니다.

### 단계 5. done에서 s0–s3와 ra를 load한 뒤 sp를 32만큼 복원하고 ret합니다.

- **이유:** 현재 sp가 frame base를 가리킬 때 saved 값을 읽어야 하며, 그 다음 caller의 stack와 return address로 돌아갈 수 있습니다.
- **종이에 남길 것:** `lw ra,0(sp); lw s0,4(sp); lw s1,8(sp); lw s2,12(sp); lw s3,16(sp); addi sp,sp,32; ret`
- **완료 기준:** Prolog에서 저장한 다섯 register가 같은 offset으로 복원되고 sp 증가가 load 뒤, ret 앞에 있습니다.
- **막힐 때 힌트:** Prolog의 store offset 목록과 한 줄씩 대조하세요.
- **가장 흔한 첫 오류:** sp를 먼저 +32해 load가 caller frame의 엉뚱한 주소를 읽게 만드는 것입니다.

## 8. 문제와 공식 해설의 비교·검증

- **문제가 요구하는 것:** argument를 놓고 swap을 호출한 뒤 loop와 function을 끝내세요.
- **공식 해설이 제시하는 결론:** 공식안은 `mv a0,s0; mv a1,s3; addi a2,s3,1; jal ra,swap`; 이어 `addi s3,s3,1; j inner_loop`, `addi s2,s2,1; j outer_loop`; `done`에서는 `lw ra,0(sp); lw s0,4(sp); lw s1,8(sp); lw s2,12(sp); lw s3,16(sp); addi sp,sp,32; ret`입니다. Load의 역순은 요구되지 않으며 동일 offset과 sp 복원 전 load가 핵심입니다.
- **결론을 재현하는 핵심 논리:** ABI에서 첫 세 argument는 a0,a1,a2입니다. s0–s3는 호출 뒤에도 유지되어 loop 상태를 계속 쓸 수 있습니다. Epilog의 개별 load 순서는 ABI가 강제하지 않으며, 저장 때 사용한 동일 offset에서 모두 읽고 sp를 올리기 전에 끝내는 것이 핵심입니다.
- **검증 방법:** 위 micro-step의 `종이에 남길 것`을 순서대로 만들고 각 `완료 기준`을 확인합니다. 최종 산출물이 공식 결론과 같은지 대조합니다.
- **전제와 주의 범위:** swap 호출 뒤 t-register 값은 믿지 마세요. loop에서 계속 필요한 값은 s-register에 있습니다. Epilog load 순서를 ABI 규칙으로 오해하지 마세요.
- **채택할 시험 답:** 이 가이드에서는 공식 Musterlösung의 결론을 시험 답으로 유지하며, 일반 ISA 또는 다른 구현과 달라질 수 있는 부분은 위 전제와 주의 범위에서 분리합니다.

## 9. 시험장에서 쓸 최종 답안

공식안은 `mv a0,s0; mv a1,s3; addi a2,s3,1; jal ra,swap`; 이어 `addi s3,s3,1; j inner_loop`, `addi s2,s2,1; j outer_loop`; `done`에서는 `lw ra,0(sp); lw s0,4(sp); lw s1,8(sp); lw s2,12(sp); lw s3,16(sp); addi sp,sp,32; ret`입니다. Load의 역순은 요구되지 않으며 동일 offset과 sp 복원 전 load가 핵심입니다.

아래는 이 unit의 register/label 전제를 모두 포함한 완성 답안입니다. 현재 unit에서 다룬 block을 위 micro-step과 대조하세요.

```asm
swap:
    slli t0, a1, 2
    add  t0, a0, t0
    lw   t1, 0(t0)
    slli t2, a2, 2
    add  t2, a0, t2
    lw   t3, 0(t2)
    sw   t3, 0(t0)
    sw   t1, 0(t2)
    ret

bubbleSort:
    addi sp, sp, -32
    sw   ra, 0(sp)
    sw   s0, 4(sp)
    sw   s1, 8(sp)
    sw   s2, 12(sp)
    sw   s3, 16(sp)
    mv   s0, a0
    mv   s1, a1
    addi s2, zero, 0

outer_loop:
    addi t0, s1, -1
    bge  s2, t0, done
    li   s3, 0

inner_loop:
    sub  t0, s1, s2
    addi t0, t0, -1
    bge  s3, t0, outer_inc
    slli t1, s3, 2
    add  t1, s0, t1
    lw   t2, 0(t1)
    lw   t3, 4(t1)
    ble  t2, t3, inner_inc
    mv   a0, s0
    mv   a1, s3
    addi a2, s3, 1
    jal  ra, swap

inner_inc:
    addi s3, s3, 1
    j    inner_loop

outer_inc:
    addi s2, s2, 1
    j    outer_loop

done:
    lw   ra, 0(sp)
    lw   s0, 4(sp)
    lw   s1, 8(sp)
    lw   s2, 12(sp)
    lw   s3, 16(sp)
    addi sp, sp, 32
    ret
```

답안지에는 결과만 쓰지 말고, 7절의 중간 산출물 가운데 판정을 증명하는 식·bit field·register 상태·cycle·cache 상태를 함께 남기세요.

## 10. 자주 하는 실수와 검산 체크리스트

### 대표 실수

- swap 호출 뒤 t-register 값은 믿지 마세요. loop에서 계속 필요한 값은 s-register에 있습니다. Epilog load 순서를 ABI 규칙으로 오해하지 마세요.
- a1=j+1, a2=j로 두 index 순서를 바꾸거나 a0에 `&arr[j]`를 전달하는 것입니다.
- swap이 끝난 뒤 a1에 여전히 j가 있다고 가정해 a1을 증가시키는 것입니다.
- swap한 경우에만 j를 증가시키거나 j++ 뒤 outer_loop로 직접 가는 것입니다.
- inner loop의 매 iteration마다 i도 증가시키는 것입니다.
- sp를 먼저 +32해 load가 caller frame의 엉뚱한 주소를 읽게 만드는 것입니다.

### 빠른 self-check

- **회상 질문:** Epilog에서 sp를 먼저 복원한 뒤 load하면 왜 잘못되나요?
- [ ] call 직전 a0–a2와 call 직후 신뢰 가능한 s0–s3를 register table로 구분할 수 있다.
- [ ] Prolog store 목록에서 동일 offset의 Epilog load를 만들고, 모든 load를 sp 복원보다 먼저 배치할 수 있다.

## 11. 짧은 확인 문제와 전이 연습

### 개념 재구성

**문제:** 한 inner iteration의 control-flow를 ordered pair와 reversed pair 두 경로로 나눠, 두 경로가 어디서 합쳐지고 어떤 state가 증가하는지 복원하세요.

**힌트:** ble taken 경로와 swap call fall-through 경로를 그린 뒤 공통 label을 찾으세요.

<details>
<summary>정답과 채점 기준 보기</summary>

**모범 답:** ordered pair에서는 `ble left,right,inner_inc`가 taken되어 swap을 건너뜁니다. reversed pair에서는 ble가 not taken이라 argument를 놓고 `jal ra,swap`을 실행한 뒤 inner_inc로 옵니다. 두 경로는 `inner_inc`에서 합쳐져 `s3=j`를 1 증가시키고 inner_loop로 돌아갑니다.

**채점 기준**
  - ordered pair
  - ble
  - taken
  - swap
  - reversed pair
  - not taken
  - jal ra,swap
  - inner_inc
  - s3=j
  - 1 증가

</details>

### 변형 문제

**문제:** Prolog가 `ra, s0, s1`을 0,4,8(sp)에 저장하고 16-Byte frame을 만들었다. 정확한 Epilog를 작성하고 각 instruction 순서의 이유를 한 줄로 쓰세요.

**힌트:** frame address를 사용하는 load가 sp 해제보다 먼저입니다.

<details>
<summary>정답과 채점 기준 보기</summary>

**모범 답:** `lw ra,0(sp); lw s0,4(sp); lw s1,8(sp); addi sp,sp,16; ret`입니다. saved register는 현재 frame base인 sp로 먼저 load하고, 그 뒤 sp를 caller 값으로 복원한 후 복원된 ra로 ret합니다.

**채점 기준**
  - lw ra,0(sp)
  - lw s0,4(sp)
  - lw s1,8(sp)
  - addi sp,sp,16
  - ret
  - 먼저 load
  - caller
  - 복원된 ra

</details>

### 오답 진단

**문제:** 학생 Epilog는 `addi sp,sp,32`를 먼저 실행한 뒤 `lw ra,0(sp); lw s0,4(sp)`를 수행합니다. 첫 순서 오류와 예상 결과를 설명하고 수정하세요.

**힌트:** 각 load의 base가 어느 frame을 가리키는지 추적하세요.

<details>
<summary>정답과 채점 기준 보기</summary>

**모범 답:** 첫 오류는 saved register를 읽기 전에 sp를 caller frame으로 복원한 것입니다. 이후 load는 bubbleSort frame이 아니라 엉뚱한 caller 주소에서 ra와 s0를 읽어 잘못된 return이나 상태 손상을 일으킵니다. 모든 `lw`를 현재 sp에서 먼저 실행하고 마지막에 `addi sp,sp,32; ret`해야 합니다.

**채점 기준**
  - saved register
  - sp
  - caller frame
  - 엉뚱
  - ra
  - 잘못된 return
  - 모든 `lw`
  - addi sp,sp,32; ret

</details>

## 12. 근거 자료

- `[문제 출처] current:Probeklausur.pdf` — 시험 p7–8 · 공식 해설 p8–11
- `[공식 해설] current:Probeklausur Musterlösung und Hinweise.pdf` — 시험 p7–8 · 공식 해설 p8–11
- `[정확한 원문 group] 3` — 시험 p.7, p.8 · 공식 해설 p.8, p.9, p.10, p.11
- `[소문항 범위] SoSe26 Probeklausur 시험 p7–8 Aufgabe 3의 swap call·loop 종료·function 반환과 공식 해설 p11의 argument setup, 증가 label, Epilog 범위.`
- `[개념 근거 · file-level] current:Vorlesung/Rechnerorganisation - Teil 1.pdf` — RISC-V assembler와 C translation
- `[개념 근거 · file-level] current:Uebung/Übung 2 Musterlösung/Rechnerorganisation_Übung2_Lösung.pdf` — assembler programming과 array access
- `[개념 근거 · file-level] current:Uebung/Übung 3 Musterlösung.pdf` — calling convention과 stack frame
- `[개념 근거 · file-level] current:Uebung/RISC-V Reference.pdf` — instruction과 ABI register 이름

### 이 파일에서 직접 대조한 공식 이미지

- `exam-p07.png` — Probeklausur.pdf · PDF p.7
- `exam-p08.png` — Probeklausur.pdf · PDF p.8
- `solution-p08.png` — Probeklausur Musterlösung und Hinweise.pdf · PDF p.8
- `solution-p11.png` — Probeklausur Musterlösung und Hinweise.pdf · PDF p.11

정확한 강의 슬라이드 한 페이지를 확정하지 못한 개념 자료는 `file-level`로 표시했습니다. 페이지 번호를 추측해서 만들지 않았습니다.
