MIPS는 조건이 참일 때만 특정 라벨로 분기(이동)하고, 그렇지 않으면 그 다음 줄로 계속 실행한다.
beq rs, rt, L1 # rs==rt일 때 L1으로 분기
bne rs, rt, L1 # rs!=rt일 때 L1으로 분기
j L1 # 무조건 L1으로 점프
if문 예제
if(i == j) f = g + h;
else f = g - h;
Compiled MIPS code:
bne $s3, $s4, Else # i != j 이면 Else로 점프
add $s0, $s1, $s2 # f = g + h
j Exit
Else: sub $s0, $s1, $s2 # f = g - h
Exit: ...
while (save[i] == k) i += 1;
Loop: sll $t1, $s3, 2 # i * 4 (byte 주소)
add $t1, $t1, $s6 # save[i] 주소
lw $t0, 0($t1) # save[i] 값 로드
bne $t0, $s5, Exit # 같지 않으면 루프 종료
addi $s3, $s3, 1 # i++
j Loop
Exit: ...
Basic Block은 중간에 분기(branch)가 없고, 오직 처음(진입점), 끝에만 분기가 있는 코드 덩어리를 의미한다. 컴파일러가 최적화할 때 단위로 사용한다.

slt $t0, $s1, $s2 # if ($s1 < $s2)
bne $t0, $zero, L # $t0 != 0 이면 L로 점프
beq나 bne과 조합하여 <, >, >= 같은 조건 구현이 가능하다.
비교 명령어가 제한적인 이유 : 왜 blt, bge 같은 명령어가 기본으로 없을까?
Signed vs Unsigned
$s0 = -1 (0xFFFFFFFF)
$s1 = 1
slt $t0, $s0, $s1 # true → $t0 = 1
sltu $t0, $s0, $s1 # false → $t0 = 0

jal ProcedureLabel
jr $ra
함수 안에서 더 이상 다른 함수를 호출하지 않는 함수
int leaf_example (int g, h, i, j) {
int f;
f = (g + h) - (i + j);
return f;
}
leaf_example:
addi $sp, $sp, -4 # 스택 공간 확보
sw $s0, 0($sp) # $s0 저장
add $t0, $a0, $a1 # g + h
add $t1, $a2, $a3 # i + j
sub $s0, $t0, $t1 # f = 결과
add $v0, $s0, $zero # 반환값 설정
lw $s0, 0($sp) # $s0 복원
addi $sp, $sp, 4 # 스택 복구
jr $ra # 복귀
함수 안에서 다른 함수를 호출하는 함수(또는 재귀)
int fact(int n) {
if (n < 1) return 1;
else return n * fact(n - 1);
}
fact:
addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slti $t0, $a0, 1
beq $t0, $zero, L1
addi $v0, $zero, 1
addi $sp, $sp, 8
jr $ra
L1: addi $a0, $a0, -1
jal fact # 재귀 호출
lw $a0, 0($sp) # n 복원
lw $ra, 4($sp) # $ra 복원
addi $sp, $sp, 8
mul $v0, $a0, $v0 # n * fact(n-1)
jr $ra


void strcpy (char x[], char y[]) {
int i = 0;
while ((x[i] = y[i]) != '\0') i++;
}
// Adresses of x, y in $a0, $a1
// i in $s0
strcpy:
addi $sp, $sp, -4 # 스택 공간 확보
sw $s0, 0($sp) # $s0 저장
add $s0, $zero, $zero # i = 0
L1:
add $t1, $s0, $a1 # y[i] 주소 계산
lbu $t2, 0($t1) # y[i] = $t2
add $t3, $s0, $a0 # x[i] 주소 계산
sb $t2, 0($t3) # x[i] = y[i]
beq $t2, $zero, L2 # y[i] == '\0'이면 종료
addi $s0, $s0, 1 # i++
j L1 # 반복
L2:
lw $s0, 0($sp) # $s0 복원
addi $sp, $sp, 4 # 스택 복구
jr $ra # 복귀

lui $s0, 61 # 상위 16비트 = 61 (0x3D)
ori $s0, $s0, 2304 # 하위 16비트 = 2304 (0x0900)

분기(Branch) 명령어 형식

op : 명령어(opcode), 예: beq, bne
rs, rt : 비교할 두 개의 레지스터
offset : 분기할 주소까지의 거리(단위: 명령어 수)
Branch 명령어는 PC-relative 방식을 사용한다.
MIPS의 분기 명령어는 절대 주소를 직접 저장하지 않고, 현재 명령어 위치(PC)를 기준으로 offset만 저장한다.
Target Address = (PC + 4) + (offset × 4)
대부분의 분기(branch) 타겟은 near branch이다.
offset 필드는 16비트 -> 표현 가능한 값의 범위
-2^15 ~ 2^15 - 1 = -32768 ~ 32767 (word 단위)
→ 실제 주소 단위로는 -131072 ~ +131068 (byte 단위)
따라서 분기 가능한 범위는 현재 PC 기준 -128KB ~ +128KB정도로, 비교적 가까운 거리(near)에만 분기가 가능하다.
멀리 떨어진 곳은 jump 명령어(j, jal) 또는 어셈블러의 자동 분기 우회 방식을 사용한다.
// C code
if (a == b) {
// do something
}
# MIPS code
beq $t0, $t1, Label # $t0 == $t1이면 Label로 분기

Target Address = (PC[31:28] << 28) | (address << 2)
1. Jump 명령어 안에는 26비트 주소가 들어있고,
2. 이는 외쪽으로 2비트 시프트되어 하위 28비트를 구성한다.
3. 상위 4비트는 현재 PC의 상위 4비트(PC[31 : 28])를 그대로 사용한다.
Target Address = 0x00400000[31:28] : (0x0003FF0 << 2)
= 0x00400000 | 0x000FFC0
= 0x0043FF00 (점프할 최종 주소)
Branch 명령어는 16비트 offset만 가능했지만, jump는 26비트 address * 4 = 약 228바이트 범위가 가능하다. 즉, 코드가 멀리 떨어져 있어도 접근이 가능하다.
MIPS의 branch 명령어는 16비트 offset만 사용이 가능하다. 즉 실제 주소 범위는 ±128KB이다. 하지만 현실에서 분기할 타겟이 너무 멀리 떨어진 경우가 있다. 예를 들어 beq $s0, $s1, L1에서 L1이 500KB 떨어진 곳에 있다면, branch 명령어 하나로 도달할 수 없다.
해결책으로 어셈블러가 Jump와 함께 분기 코드를 자동 재작성을 한다.
beq $s0, $s1, L1 # 원래 의도
↓
bne $s0, $s1, L2 # 조건 반전: 다르면 L2로 가고
j L1 # Jump로 멀리 있는 L1으로 점프
L2: # 조건 안 맞으면 여기로 떨어짐
beq는 가까운 거리만 가능해서 조건을 뒤집은 다음, 가까운 곳인 L2로 분기 시킨다. j L1로 멀리 있는 진짜 목표 L1으로 이동시킨다. 조건을 뒤집는 이유는 jump는 무조건 실행되므로, 조건 실패 시 jump를 피하기 위해 조건을 반전시킨다.(어셈블러가 자동으로 처리, 프로그래머는 몰라도 된다.)
아래 예제는 MIPS의 분기와 점프 명령어가 실제로 어떻게 주소를 계산해서 흐름을 제어하는지 보여주는 예제이다.
Loop:
sll $t1, $s3, 2 # $t1 = $s3 * 4
add $t1, $t1, $s6 # $t1 = base + offset
lw $t0, 0($t1) # 메모리에서 데이터 로드
bne $t0, $s5, Exit # 다르면 Exit으로 분기
addi $s3, $s3, 1 # 인덱스 증가
j Loop # 다시 Loop로 점프
Exit:

Target = (PC + 4) + (offset × 4)



try:
add $t0, $zero, $s4 # 교환할 값
ll $t0, 0($s1) # 주소의 값 읽기
addi $t0, $t0, 1 # 값 수정
sc $t0, 0($s1) # 조건부 저장
beq $t0, $zero, try # 실패 시 반복
실제 기계어로 1:1 대응되지 않지만, 편의를 위해 제공되는 명령어이다.

slt $at, $t0, $t1
bne $at, $zero, L
컴파일러 또는 어셈블러가 생성하는 출력물: