---
title: "명령 경계 정렬: 다음 fetch에 걸친 32비트 명령을 잃지 않는 법"
author: "브레이브브라우저쓰자"
published_at: "2026-10-14T06:58:00.935Z"
updated_at: "2026-10-06T00:30:59.116Z"
tags: ["명령 경계 정렬"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/%EB%AA%85%EB%A0%B9-%EA%B2%BD%EA%B3%84-%EC%A0%95%EB%A0%AC-%EB%8B%A4%EC%9D%8C-fetch%EC%97%90-%EA%B1%B8%EC%B9%9C-32%EB%B9%84%ED%8A%B8-%EB%AA%85%EB%A0%B9%EC%9D%84-%EC%9E%83%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%B2%95"
ai_assisted: true
---

# 명령 경계 정렬: 다음 fetch에 걸친 32비트 명령을 잃지 않는 법

이 글은 AI가 활용됐습니다.

[원문](https://ariser.io/@writer_a9817d5a8de0/%EB%AA%85%EB%A0%B9-%EA%B2%BD%EA%B3%84-%EC%A0%95%EB%A0%AC-%EB%8B%A4%EC%9D%8C-fetch%EC%97%90-%EA%B1%B8%EC%B9%9C-32%EB%B9%84%ED%8A%B8-%EB%AA%85%EB%A0%B9%EC%9D%84-%EC%9E%83%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%B2%95)

명령 캐시는 주소가 맞춰진 넓은 덩어리를 읽어 준다. CPU가 실행할 명령은 꼭 그 덩어리의 경계에서 시작하지 않는다. 16비트와 32비트 명령이 섞이면, 마지막 2바이트가 다음 명령의 앞 절반일 수도 있다. 명령 경계 정렬은 읽어 온 비트들을 단순히 일정 간격으로 자르는 일이 아니라, 각 명령의 시작과 길이를 이어 붙이는 작업이다.

RISC-V의 compressed 정수 명령 정의는 16비트와 32비트 명령을 섞을 수 있고, 32비트 명령도 16비트 경계에서 시작할 수 있다고 명시한다. 따라서 4바이트마다 한 명령이라는 가정은 이 구성에서 성립하지 않는다. [RISC-V Zca 원문, Introduction](https://github.com/riscv/riscv-isa-manual/blob/main/src/unpriv/zca.adoc)

## fetch 경계와 명령 경계가 어긋난다

가상의 프런트엔드가 한 번에 8바이트를 가져온다고 하자. 0x100~0x107의 묶음에 먼저 0x100의 16비트 명령, 0x102의 32비트 명령, 0x106의 32비트 명령이 놓여 있다. 첫 명령은 0x100~0x101, 두 번째는 0x102~0x105다. 세 번째 명령의 앞 16비트는 0x106~0x107에 있지만 나머지는 다음 묶음의 0x108~0x109에 있다.

첫 묶음의 네 16비트 조각을 네 명령으로 내보내면 32비트 명령의 뒤 절반을 독립 명령처럼 해독한다. 반대로 무조건 32비트씩 자르면 0x100의 짧은 명령과 다음 명령의 앞 절반을 하나로 묶는다. 둘 다 비트들은 정상적으로 읽었어도 명령의 의미를 바꾼다.

RISC-V의 길이 부호화에서 32비트 기본 명령의 하위 두 비트는 11이고, 16비트 compressed 명령은 00·01·10이다. 16/32비트의 지원되는 명령들을 처리하는 경우 이 차이를 이용해 현재 조각이 다음 조각을 필요로 하는지 판단할 수 있다. 더 긴 형식이나 지원하지 않는 부호화까지 모두 32비트로 간주하는 규칙은 아니다. [RISC-V 원문, Instruction-Length Encoding](https://github.com/riscv/riscv-isa-manual/blob/main/src/unpriv/intro.adoc)

## 앞 절반과 그 주소를 함께 남긴다

경계에 남은 앞 절반을 보관하는 작은 상태가 필요하다. 0x106에서 시작하는 32비트 명령의 하위 절반이 0x006F이고, 다음 묶음의 첫 절반이 0x0080이라면 합친 명령 비트열은 0x0080006F다. 낮은 주소의 조각이 명령의 하위 비트라는 순서를 지킨 계산이다.

이때 명령의 PC는 0x108이 아니라 0x106이다. 뒤 절반을 가져온 묶음의 시작 주소를 명령 PC로 붙이면, PC에 상대 offset을 더하는 분기나 예외 보고가 틀릴 수 있다. 보관할 정보는 앞 절반의 값뿐 아니라 시작 PC, 아직 이어 붙일 명령이 있다는 유효 상태와 관련된 요청 정보다.

BOOM의 fetch 문서는 이전 묶음의 끝 16비트와 PC, 명령 경계를 추적하고 다음 묶음과 결합하는 방식을 설명한다. 해당 구현은 경계를 가로지른 명령을 뒤 절반이 들어 있는 묶음에 소속시켜 관리하면서 원래 시작 PC를 따로 주의해야 한다고 명시한다. 묶음 소속과 명령 주소가 항상 같은 위치를 뜻하지 않는 예다. [BOOM Instruction Fetch, Fetching Compressed Instructions](https://docs.boom-core.org/en/latest/sections/instruction-fetch-stage.html#fetching-compressed-instructions)

대기 상황도 중요하다. 다음 묶음이 캐시 miss 때문에 늦게 오면 앞 절반은 계속 살아 있어야 한다. 소비 쪽이 멈추었다고 같은 완성 명령을 두 번 보내거나, 이전 절반을 먼저 지워서 잃어버리면 안 된다. 어느 입력이 받아들여졌고 어느 출력이 소비되었는지를 실제 handshake와 연결해야 한다.

## 길이를 잃으면 정상 비트열도 오답이 된다

해독 편의를 위해 16비트 명령을 대응하는 32비트 내부 형식으로 확장할 수 있다. 하지만 원래 명령 길이는 계속 필요하다. 다음 순차 PC는 짧은 명령이면 PC+2, 긴 명령이면 PC+4다. compressed call의 link 주소 역시 원래 길이를 반영해야 한다. 내부 비트 폭만 보고 늘 PC+4를 쓰면 압축 형식의 의미가 달라진다.

경로가 바뀌는 경우에는 보관한 절반의 수명도 끝날 수 있다. 0x106의 앞 절반을 저장한 상태에서 분기 복구가 새 PC로 redirect했다면, 새 경로의 첫 16비트와 옛 절반을 연결해서는 안 된다. 메모리 응답이 늦게 돌아올 수 있는 인터페이스라면 응답이 현재 경로에 속하는지도 확인해야 한다. reset과 flush는 정렬 상태의 유효성을 포함하여 처리해야 한다.

주소 변환 경계나 캐시 라인 경계를 가로지르는 명령에는 추가 조건이 있다. 두 조각의 요청이 같은 사이클에 끝난다고 가정할 수 없고, 한쪽 접근에서 fault가 날 수도 있다. 이때 잘못된 조각을 정상 명령으로 내보내는 대신 해당 구현의 예외 계약을 따라야 한다. 앞의 8바이트 예는 그런 fault가 없는 정상 흐름만 보여 준다.

명령 경계 정렬의 검증은 길이가 다른 명령을 많이 실행하는 데서 끝나지 않는다. 경계에서 남는 절반, 응답 지연, 출력 대기와 경로 취소를 조합하고 시작 PC와 원래 길이까지 확인해야 한다. fetch가 가져온 덩어리와 실행할 명령의 경계를 구분하면, 정상적인 바이트들이 어디서 다른 명령으로 오해되는지 찾을 수 있다.

