MIPS 아키텍처의 명령어를 GAS 문법 기준으로 종합 정리합니다. 전통 RISC 설계 철학과 고정 32-bit 명령어, 32개 범용 레지스터와 HI/LO 특수 레지스터, 지연 슬롯 메커니즘, MIPS32/64 Release 2와 Release 6의 차이, R/I/J 3가지 인코딩 타입, 데이터 전송·산술·논리·분기·스택·시스템·원자적·MSA SIMD 명령어 카테고리 표, 인코딩 다이어그램, 커널 핵심 명령어(SYSCALL, ERET, LL/SC, MFC0/MTC0, TLBWI) 심화까지 Linux 커널 개발에 필요한 MIPS ISA 전체를 다룹니다.
전제 조건:어셈블리 종합을 먼저 읽으세요.
MIPS 명령어 레퍼런스는 RISC Load/Store 모델, 지연 슬롯 개념, CP0 레지스터에 대한 기본 이해가 필요합니다.
일상 비유: MIPS는 교과서적 RISC 설계의 대표입니다.
컴퓨터 구조 교육의 표준으로 사용될 만큼 깔끔한 명령어 세트를 가지고 있으며, 파이프라인 설계의 교본과 같습니다. 지연 슬롯은 파이프라인 효율을 위한 독특한 설계 결정입니다.
핵심 요약
전통 RISC — 고정 32-bit 명령어, Load/Store 아키텍처, 균일한 레지스터 파일.
지연 슬롯 — 분기/점프 다음 명령어가 항상 실행됨 (R6에서 compact branch로 개선).
HI/LO 레지스터 — 곱셈/나눗셈 결과 저장 (R6에서 제거, GPR 직접 사용).
R2 → R6 진화 — Release 6에서 지연 슬롯 없는 분기, HI/LO 제거, 새 인코딩 도입.
단계별 이해
레지스터 구조 파악 $0-$31의 ABI 이름($zero, $at, $v0-$v1, $a0-$a3, $t0-$t9, $s0-$s7 등)을 먼저 익힙니다.
지연 슬롯 이해 분기/점프 직후 명령어가 항상 실행되는 MIPS 고유 특성을 반드시 이해합니다.
R/I/J 인코딩 3가지 고정 인코딩 포맷의 필드 배치를 학습합니다.
CP0와 특권 명령어 MFC0/MTC0으로 접근하는 CP0 레지스터와 SYSCALL/ERET를 학습합니다.
아키텍처 개요
MIPS(Microprocessor without Interlocked Pipelined Stages)는 1981년 Stanford에서 John Hennessy가 설계한 RISC 아키텍처입니다. MIPS Computer Systems(1984) → SGI(1992) → Imagination Technologies(2012) → Wave Computing(2018) → MIPS(2021)를 거쳤습니다.
특성
MIPS
설계 철학
전통 RISC (교과서적 파이프라인 설계)
명령어 길이
고정 32-bit
엔디언
바이-엔디언 (Big 또는 Little)
범용 레지스터
32개 ($0=hardwired zero)
특수 레지스터
HI/LO (곱셈/나눗셈, R2), PC
지연 슬롯
분기/점프 다음 1개 명령어 항상 실행 (R6: compact branch 도입)
주요 릴리스
MIPS32/64 R2 (2003), R5 (2012), R6 (2014)
동작 모드
커널 모드, 슈퍼바이저 모드, 유저 모드
주소 공간
32-bit (MIPS32) 또는 64-bit (MIPS64)
R2 vs R6: Release 6(2014)에서 대폭 변경되었습니다. HI/LO 제거(곱셈/나눗셈 결과 GPR 직접), 지연 슬롯 없는 compact branch 도입, 일부 명령어 재인코딩. Linux 커널은 R2와 R6 모두 지원합니다.
MIPS 클래식 5단계 파이프라인
파이프라인 해저드와 지연 슬롯: MIPS의 이름(Microprocessor without Interlocked Pipelined Stages)이 암시하듯, 초기 MIPS는 하드웨어 인터록 없이 파이프라인 해저드를 소프트웨어가 관리하도록 설계되었습니다. 분기 명령어가 ID 단계에서 결과를 결정하지만, 그 시점에 다음 명령어는 이미 IF 단계에 진입해 있습니다. 하드웨어로 이를 취소(flush)하는 대신, 항상 실행하도록 하여 1 사이클의 파이프라인 버블을 제거한 것이 지연 슬롯의 핵심 설계 결정입니다. 현대 프로세서에서는 이 결정이 오히려 복잡성을 증가시키기 때문에, R6에서 지연 슬롯 없는 compact branch가 도입되었습니다.
MIPS 가상 주소 공간 (32-bit)
레지스터 셋
범용 레지스터 ($0-$31)
번호
ABI 이름
용도
보존
$0
$zero
하드와이어 제로 (항상 0)
—
$1
$at
어셈블러 임시 (Assembler Temporary)
—
$2-$3
$v0-$v1
함수 반환값
Caller
$4-$7
$a0-$a3
함수 인자 (o32 ABI)
Caller
$8-$15
$t0-$t7
임시 레지스터
Caller
$16-$23
$s0-$s7
Saved 레지스터
Callee
$24-$25
$t8-$t9
임시 레지스터
Caller
$26-$27
$k0-$k1
커널 전용 (예외 핸들러용)
—
$28
$gp
전역 포인터 (Global Pointer)
Caller
$29
$sp
스택 포인터
Callee
$30
$fp ($s8)
프레임 포인터 / Saved 레지스터
Callee
$31
$ra
복귀 주소 (Return Address)
Caller
$k0/$k1이 커널 전용인 이유: MIPS에서 예외가 발생하면 프로세서는 예외 벡터로 즉시 점프하지만, 레지스터를 자동으로 저장하지 않습니다. 예외 핸들러는 레지스터를 메모리에 저장하기 위해 최소 2개의 "스크래치" 레지스터가 필요합니다. $k0/$k1은 이 목적으로 예약되어 있으며, 예외가 언제든 발생할 수 있으므로 이 레지스터의 값은 예고 없이 덮어쓰여질 수 있습니다. 따라서 유저 코드나 일반 커널 코드에서 $k0/$k1을 사용하면 안 됩니다. Linux 커널의 TLB refill 핸들러에서 $k0으로 Context 레지스터를, $k1으로 PTE 값을 임시 저장하는 패턴이 대표적입니다.
특수 레지스터
레지스터
설명
HI / LO
곱셈 상위/하위, 나눗셈 나머지/몫 (R2, R6에서 제거)
PC
프로그램 카운터 (직접 접근 불가)
CP0 (Coprocessor 0) 레지스터 — 주요
번호
이름
설명
$8
BadVAddr
가장 최근 주소 오류의 가상 주소
$9
Count
카운터 (타이머)
$11
Compare
카운터 비교값 (타이머 인터럽트)
$12
Status
프로세서 상태 (IE, EXL, ERL, KSU, IM 등)
$13
Cause
예외 원인 (ExcCode, IP 등)
$14
EPC
예외 PC (복귀 주소)
$15
PRId
프로세서 ID
$16
Config
구성 레지스터
$0
Index
TLB 인덱스
$1
Random
TLB 랜덤 인덱스
$2-$3
EntryLo0/1
TLB 엔트리 하위 (짝수/홀수 페이지)
$4
Context
TLB 미스 컨텍스트
$5
PageMask
TLB 페이지 마스크
$6
Wired
고정 TLB 엔트리 수
$10
EntryHi
TLB 엔트리 상위 (VPN2, ASID)
$15.1
EBase
예외 베이스 주소 (R2+)
$30
ErrorEPC
에러 예외 PC
CP0 Status 레지스터 비트 필드 ($12)
CP0 Cause 레지스터 비트 필드 ($13)
MIPS64 n64 ABI 차이
n64 ABI: $a0-$a7 (8개 인자 레지스터), $v0-$v1 반환. o32 ABI는 $a0-$a3 (4개)만 사용하고 나머지는 스택으로 전달.
주소 지정 모드
MIPS는 매우 제한적인 주소 모드를 사용합니다: 베이스 + 16-bit 부호 확장 오프셋만 지원합니다.
패턴
문법
설명
예제
베이스+오프셋
offset(base)
[base + sign_ext(offset)]
lw $t0, 8($sp)
32-bit 즉시값
lui + ori
상위 16 + 하위 16 조합
lui $t0, %hi(sym); ori $t0, $t0, %lo(sym)
의사 명령어 li
li $t0, imm
어셈블러가 lui+ori로 확장
li $t0, 0x12345678
의사 명령어 la
la $t0, symbol
주소 로드
la $t0, my_var
지연 슬롯 (Branch Delay Slot): MIPS R2에서 모든 분기/점프 명령어 다음 1개 명령어는 분기 결과와 무관하게 항상 실행됩니다. 이는 파이프라인 효율을 위한 설계입니다. NOP으로 채우거나 유용한 명령어를 배치합니다. R6의 compact branch(BC, BALC 등)는 지연 슬롯이 없습니다.
지연 슬롯 구체적 예제: 아래 코드에서 addiu는 분기 결과와 무관하게 항상 실행됩니다:
beq $t0, $t1, target /* $t0 == $t1이면 target으로 분기 */addiu $v0, $v0, 1/* ← 지연 슬롯: 항상 실행됨! *//* 여기는 분기하지 않았을 때만 실행 */
...
target:
/* $v0는 이미 +1된 상태 (지연 슬롯이 실행되었으므로) */
주의: 지연 슬롯에서 분기 조건의 소스 레지스터를 수정하면 안 됩니다. 또한 지연 슬롯에 또 다른 분기 명령어를 넣는 것은 미정의 동작입니다.
.set noreorder / .set reorder 지시자: 커널 어셈블리에서는 지연 슬롯을 직접 관리하기 위해 GAS 지시자를 사용합니다:
.set noreorder — 어셈블러가 명령어 재배치/NOP 삽입을 하지 않음. 프로그래머가 지연 슬롯을 직접 채워야 함. 커널 핵심 경로(예외 핸들러, TLB refill)에서 사용.
.set reorder — 어셈블러가 필요 시 NOP을 자동 삽입하고 명령어 순서를 최적화. 일반 C 호출 함수에서 사용.
.set noreorder
beq $t0, $zero, done
addiu $v0, $v0, 1/* 유용한 명령어를 지연 슬롯에 배치 */.set reorder
Linux 커널의 arch/mips/ 어셈블리 코드는 대부분 .set noreorder 블록 내에서 작성됩니다.
데이터 전송 명령어
명령어
문법
설명
비고
LB / LBU
lb $t0, 0($a0)
바이트 로드 (부호/제로 확장)
LH / LHU
lh $t0, 0($a0)
하프워드 로드
LW
lw $t0, 0($a0)
워드 로드
LWU
lwu $t0, 0($a0)
워드 제로 확장 로드
MIPS64
LD
ld $t0, 0($a0)
더블워드 로드
MIPS64
SB / SH / SW / SD
sw $t0, 0($a0)
바이트/하프/워드/더블 저장
LUI
lui $t0, 0x1234
상위 16-bit 즉시값 로드
LWL / LWR
lwl $t0, 3($a0)
비정렬 워드 로드 (좌/우)
R2 전용
MFC0 / MTC0
mfc0 $t0, $12
CP0 레지스터 읽기/쓰기
특권
MFHI / MFLO
mfhi $t0
HI/LO 레지스터 읽기
R2
MTHI / MTLO
mthi $t0
HI/LO 레지스터 쓰기
R2
MFC1 / MTC1
mfc1 $t0, $f0
FPU 레지스터 ↔ GPR
LWC1 / LDC1
lwc1 $f0, 0($a0)
FPU 메모리 로드
SWC1 / SDC1
swc1 $f0, 0($a0)
FPU 메모리 저장
비정렬 메모리 접근 (Unaligned Access): MIPS R2에서 LW/SW는 4바이트 정렬을 요구합니다. 비정렬 주소에서 워드를 읽으려면 LWL(Load Word Left)과 LWR(Load Word Right)을 쌍으로 사용해야 합니다. R6에서는 이 문제가 해결되어 LW/SW가 비정렬 접근을 하드웨어적으로 지원하며, LWL/LWR은 제거되었습니다.
/* R2: 비정렬 32-bit 로드 (LWL/LWR 패턴) *//* $a0에 비정렬 주소가 있다고 가정 (예: 0x10003) *//* Big-Endian 기준 */lwl $t0, 0($a0) /* 주소의 워드 좌측 바이트들 로드 */lwr $t0, 3($a0) /* 주소의 워드 우측 바이트들 로드 *//* 이제 $t0에 4바이트가 올바르게 로드됨 *//* R2: 비정렬 32-bit 저장 (SWL/SWR 패턴) */swl $t0, 0($a0) /* 워드 좌측 바이트들 저장 */swr $t0, 3($a0) /* 워드 우측 바이트들 저장 *//* R6: 단순화됨 — LW/SW가 비정렬 지원 */lw $t0, 0($a0) /* R6에서는 비정렬이어도 정상 동작 */
커널에서의 비정렬 접근: Linux 커널의 arch/mips/kernel/unaligned.c는 비정렬 접근 예외(AdEL/AdES)를 에뮬레이션합니다. R2 커널 코드에서 비정렬 접근이 필요한 경우 get_unaligned() / put_unaligned() 매크로를 사용하며, 이 매크로는 내부적으로 LWL/LWR 패턴을 사용합니다.
산술 명령어
명령어
문법
설명
비고
ADD / ADDU
addu $t0, $t1, $t2
덧셈 (ADD=오버플로 트랩, ADDU=무시)
ADDI / ADDIU
addiu $t0, $t1, 42
즉시값 덧셈
SUB / SUBU
subu $t0, $t1, $t2
뺄셈
MULT / MULTU
mult $t0, $t1
곱셈 → HI:LO
R2
DIV / DIVU
div $t0, $t1
나눗셈 → LO=몫, HI=나머지
R2
MUL (R6)
mul $t0, $t1, $t2
곱셈 → GPR (하위)
R6
MUH / MULU / MUHU (R6)
muh $t0, $t1, $t2
곱셈 상위 / 부호없는
R6
DIV / MOD (R6)
div $t0, $t1, $t2
나눗셈 / 나머지 → GPR
R6
MADD / MADDU
madd $t0, $t1
곱셈-누적: HI:LO += t0*t1
R2
CLO / CLZ
clz $t0, $t1
선행 1/0 카운트
R2+
DADD / DADDU / DSUB
daddu $t0, $t1, $t2
64-bit 덧셈/뺄셈
MIPS64
DMULT / DDIV
dmult $t0, $t1
64-bit 곱셈/나눗셈
MIPS64
R2 vs R6 곱셈/나눗셈 비교:
R2 방식:MULT rs, rt → 결과가 HI:LO에 저장됨 → MFHI/MFLO로 읽어야 함. 3개 명령어 필요.
Branch Likely 명령어 (R2, R6에서 제거): BEQL, BNEL, BGTZL, BLEZL 등의 "branch likely" 변형은 분기가 taken일 때만 지연 슬롯을 실행합니다 (not-taken이면 지연 슬롯 명령어를 무효화). 이는 컴파일러가 taken 경로에 유용한 명령어를 배치하기 쉽게 해주었지만, 하드웨어 구현이 복잡해져 R6에서 완전히 제거되었습니다. Linux 커널에서는 R2 TLB 핸들러 등 성능 크리티컬한 코드에서 여전히 사용됩니다.
지연 슬롯 최적화 예제: NOP 대신 유용한 명령어를 지연 슬롯에 배치하여 성능을 향상시킵니다:
/* 비최적화: NOP 낭비 */addiu $v0, $v0, 1bne $v0, $a0, loop
nop/* 낭비되는 사이클 *//* 최적화: 유용한 명령어를 지연 슬롯에 이동 */bne $v0, $a0, loop
addiu $v0, $v0, 1/* 지연 슬롯에서 실행 — 1 사이클 절약 *//* 함수 호출 최적화: 인자 설정을 지연 슬롯에 */jal some_func
move $a0, $s0 /* 지연 슬롯: 인자를 $a0에 설정 */
/* 함수 프롤로그/에필로그 (o32 ABI) */my_func:
addiu $sp, $sp, -32/* 스택 프레임 할당 */sw $ra, 28($sp) /* 복귀 주소 저장 */sw $fp, 24($sp) /* 프레임 포인터 저장 */move $fp, $sp /* 프레임 포인터 설정 *//* ... 함수 본문 ... */move $sp, $fp /* 스택 복원 */lw $fp, 24($sp) /* 프레임 포인터 복원 */lw $ra, 28($sp) /* 복귀 주소 복원 */addiu $sp, $sp, 32/* 스택 프레임 해제 */jr $ra /* 복귀 (지연 슬롯) */nop/* 지연 슬롯 */
o32 ABI 호출 규약:
인자: $a0-$a3 (4개, 나머지 스택)
반환값: $v0 (+ $v1)
Callee-saved: $s0-$s7, $fp, $sp
Caller-saved: $t0-$t9, $a0-$a3, $v0-$v1, $ra
n64 ABI: $a0-$a7 (8개 인자 레지스터)
시스템/특권 명령어
명령어
설명
비고
SYSCALL
시스템 콜 트랩
BREAK
디버그 브레이크포인트 트랩
ERET
예외 복귀 (EPC → PC, Status 복원)
MFC0 / MTC0
CP0 레지스터 읽기/쓰기
DMFC0 / DMTC0
64-bit CP0 접근
MIPS64
WAIT
저전력 대기 (인터럽트까지)
EI / DI
인터럽트 활성화/비활성화
R2+
TLBR
TLB 읽기 (Index → EntryHi/Lo)
TLBWI
TLB 쓰기 (Index 지정)
TLBWR
TLB 쓰기 (Random 인덱스)
TLBP
TLB 탐색 (EntryHi → Index)
CACHE
캐시 조작 (I-cache/D-cache)
SYNC
메모리 배리어
RDHWR
하드웨어 레지스터 읽기 (ULR 등)
R2+
GINVI / GINVT (R6)
전역 TLB 무효화
R6
예외 벡터 레이아웃
EI/DI 인터럽트 관리 (R2+): Release 2부터 도입된 EI(Enable Interrupts)과 DI(Disable Interrupts)는 CP0 Status의 IE 비트를 원자적으로 설정/해제합니다. 이전 R1에서는 MFC0/MTC0으로 Status 레지스터를 읽고-수정-쓰기 해야 했으며, 이 사이에 인터럽트가 발생할 수 있는 경쟁 조건이 있었습니다.
/* R2+: 원자적 인터럽트 비활성화 */di $t0 /* $t0 = 이전 Status, IE←0 */ehb/* Execution Hazard Barrier *//* ... 크리티컬 섹션 ... */ei/* IE←1, 인터럽트 재활성화 *//* R1 (구형): MFC0/MTC0 패턴 — 경쟁 조건 가능 */mfc0 $t0, $12 /* Status 읽기 */ori $t1, $t0, 1/* IE 비트 세트 */mtc0 $t1, $12 /* Status 쓰기 — 여기서 인터럽트 경쟁 가능 */
원자적/동기화 명령어
명령어
문법
설명
LL
ll $t0, 0($a0)
Load Linked (32-bit 독점 로드)
SC
sc $t1, 0($a0)
Store Conditional ($t1=0이면 실패, 1이면 성공)
LLD
lld $t0, 0($a0)
64-bit Load Linked (MIPS64)
SCD
scd $t1, 0($a0)
64-bit Store Conditional (MIPS64)
SYNC
sync 0
메모리 배리어 (0=full barrier)
LL/SC CAS 루프 패턴
/* compare_and_swap(addr=$a0, expected=$a1, desired=$a2) → old in $v0 */cas_loop:
ll $v0, 0($a0) /* Load Linked */bne $v0, $a1, 1f /* expected와 다르면 실패 */nop/* 지연 슬롯 */move $t0, $a2 /* desired 값 복사 */sc $t0, 0($a0) /* Store Conditional */beqz $t0, cas_loop /* SC 실패(0) 시 재시도 */nop/* 지연 슬롯 */1:
jr $ra /* $v0에 이전 값 반환 */nop/* 지연 슬롯 */
LL/SC 전진 보장 제약 조건: LL과 SC 사이의 코드는 매우 엄격한 제약을 따라야 합니다:
LL과 SC 사이에 다른 메모리 접근(load/store) 금지 — SC 실패를 유발할 수 있음
LL과 SC 사이의 명령어 수를 최소화 (구현에 따라 제한 있음, 일반적으로 수십 개 이내)
/* Status.EXL=1 → EPC에서 복귀, EXL=0으로 복원 */mfc0 $k0, $14 /* EPC 읽기 */mfc0 $k1, $12 /* Status 읽기 *//* Status 복원 처리 ... */mtc0 $k1, $12 /* Status 쓰기 */eret/* PC←EPC, EXL←0 */
TLB 조작 패턴
/* TLB Refill 핸들러 (0x80000000) */tlb_refill:
mfc0 $k0, $4 /* Context 레지스터 */lw $k1, 0($k0) /* PTE 로드 */mtc0 $k1, $2 /* EntryLo0 */lw $k1, 4($k0) /* 다음 PTE */mtc0 $k1, $3 /* EntryLo1 */tlbwr/* Random 인덱스로 TLB 쓰기 */eret
MIPS는 소프트웨어 관리 TLB를 사용합니다. TLB 미스 시 CPU가 자동으로 페이지 테이블을 검색하는 x86/ARM64와 달리, MIPS는 TLB Refill 예외를 발생시키고 커널이 직접 TLB 엔트리를 채워야 합니다. 이를 위해 EntryHi, EntryLo0/1, PageMask, Index CP0 레지스터와 TLBWR/TLBWI/TLBR/TLBP 명령어를 사용합니다.
/* arch/mips/mm/tlbex.c — TLB Refill 핸들러 (빌드 시 동적 생성) *//* 가장 성능 크리티컬한 커널 코드 — ~20 명령어로 최적화 *//* 개념적 TLB Refill 핸들러 (R2/R6 공통 원리) */
tlb_refill_handler:
/* 1. k0/k1은 예외에서 자유롭게 사용 가능 (커널 전용) */
mfc0 k1, C0_CONTEXT /* Context.PTEBase + BadVPN2 → PTE 주소 */
lw k0, 0(k1) /* EntryLo0 = PTE[even] */
lw k1, 4(k1) /* EntryLo1 = PTE[odd] */
mtc0 k0, C0_ENTRYLO0 /* EntryLo0 설정 */
mtc0 k1, C0_ENTRYLO1 /* EntryLo1 설정 */
ehb /* Execution Hazard Barrier */
tlbwr /* Random 위치에 TLB 쓰기 */
eret /* 예외 복귀 (EPC로 점프) *//* TLB 프로빙과 무효화 */
mtc0 t0, C0_ENTRYHI /* 검색할 VPN2|ASID 설정 */
ehb
tlbp /* TLB 검색 → Index에 결과 */
ehb
mfc0 t1, C0_INDEX /* Index.P(bit31)=1이면 미스 */
bltz t1, not_found /* 음수 → TLB에 없음 *//* Index >= 0 → 해당 위치에 무효 엔트리 쓰기 */
mtc0 zero, C0_ENTRYLO0
mtc0 zero, C0_ENTRYLO1
ehb
tlbwi /* Index 위치에 쓰기 (무효화) */
CP0 레지스터 비트 필드 상세
CP0 (Coprocessor 0)는 MIPS의 시스템 제어 레지스터 집합입니다. x86의 CR0-CR4/MSR, ARM64의 시스템 레지스터에 대응합니다. 커널은 mfc0/mtc0로 CP0에 접근하며, 예외 처리·TLB 관리·캐시 설정·인터럽트 제어의 핵심입니다.
CP0 레지스터
번호
핵심 필드
커널 사용
Index
$0
P(probe result), Index[5:0]
TLBWI/TLBP 대상
Random
$1
Random[5:0]
TLBWR 자동 감소 카운터
EntryLo0/1
$2/$3
PFN, C, D, V, G
TLB 쓰기 데이터
Context
$4
PTEBase, BadVPN2
TLB Refill 빠른 PTE 조회
PageMask
$5
Mask[28:13]
가변 페이지 크기 (4KB-256MB)
Wired
$6
Wired[5:0]
고정 TLB 엔트리 수
BadVAddr
$8
전체 VA
주소 예외/TLB 미스 주소
Count
$9
32비트 카운터
타이머 (CPU 클럭/2)
EntryHi
$10
VPN2, ASID
TLB 매칭 키
Compare
$11
32비트 비교값
Count==Compare → IP7 인터럽트
Status
$12
IE, EXL, ERL, KSU, IM, CU
인터럽트/모드 제어
Cause
$13
ExcCode, IP, BD
예외 원인 판별
EPC
$14
복귀 주소
예외 복귀 (eret)
PRId
$15,0
Company, Processor
CPU 식별
EBase
$15,1
Exception Base
예외 벡터 베이스 재배치
Config
$16
K0, AT, AR, MT
아키텍처 설정
⚠️
Hazard Barrier 필수: MIPS에서 CP0 레지스터를 mtc0로 수정한 후 해당 값이 실제로 반영되기까지 실행 해저드(Execution Hazard)가 있습니다. R2에서는 ehb(sll $0,$0,3) 명령어를, R6에서는 ehb 또는 충분한 nop 삽입이 필요합니다. TLBWI/TLBWR 전에 ehb를 빠뜨리면 이전 CP0 값이 반영되지 않아 잘못된 TLB 엔트리가 기록될 수 있습니다.