---
title: "compressed instruction: 짧은 덧셈에도 레지스터 조건이 붙는 이유"
author: "브레이브브라우저쓰자"
published_at: "2026-10-16T13:25:04.541Z"
updated_at: "2026-10-06T00:47:08.972Z"
tags: ["compressed instruction"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/compressed-instruction-%EC%A7%A7%EC%9D%80-%EB%8D%A7%EC%85%88%EC%97%90%EB%8F%84-%EB%A0%88%EC%A7%80%EC%8A%A4%ED%84%B0-%EC%A1%B0%EA%B1%B4%EC%9D%B4-%EB%B6%99%EB%8A%94-%EC%9D%B4%EC%9C%A0"
ai_assisted: true
---

# compressed instruction: 짧은 덧셈에도 레지스터 조건이 붙는 이유

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

[원문](https://ariser.io/@writer_a9817d5a8de0/compressed-instruction-%EC%A7%A7%EC%9D%80-%EB%8D%A7%EC%85%88%EC%97%90%EB%8F%84-%EB%A0%88%EC%A7%80%EC%8A%A4%ED%84%B0-%EC%A1%B0%EA%B1%B4%EC%9D%B4-%EB%B6%99%EB%8A%94-%EC%9D%B4%EC%9C%A0)

명령어가 16비트가 되면 CPU가 할 수 있는 일도 절반으로 줄어들까. compressed instruction의 핵심은 표현 가능한 연산을 모두 짧게 만드는 데 있지 않다. 자주 나타나는 피연산자 조합에 짧은 표현을 주고, 맞지 않는 경우에는 기존 표현을 함께 쓰는 데 있다.

## 같은 덧셈을 짧게 표현할 수 있는 조건

RISC-V Zca의 C.ADDI를 예로 보자. 다음 두 표기는 같은 레지스터 갱신을 나타낸다.

```text
c.addi x9, 5      # 16비트 표현
addi   x9, x9, 5  # 대응하는 32비트 명령
```

실행 전 x9가 100이면 실행 뒤에는 105가 된다. 짧은 표현이 덧셈의 정밀도를 절반으로 줄이지 않는다. 레지스터의 연산 폭은 해당 기본 ISA의 XLEN을 따른다. 줄어든 것은 명령어를 저장하고 가져오는 비트 수다.

이를 가능하게 하는 제약도 있다. 일반적인 C.ADDI 연산은 목적지와 첫 입력이 같은 레지스터이고, 즉시값은 0이 아닌 signed 6비트 값이다. 따라서 x9에 40을 더하는 동작은 이 C.ADDI 한 개로 표현할 수 없다. `addi x9, x10, 5`처럼 입력과 목적지가 다른 경우도 이 형식에 맞지 않는다. 즉시값이 0인 일부 encoding은 HINT이며, 목적지 x0인 경우도 별도 규정을 가진다. “작은 ADDI면 아무 조합이나 압축된다”는 규칙으로 decode해서는 안 된다. [RISC-V Zca, Integer Register-Immediate Operations](https://github.com/riscv/riscv-isa-manual/blob/main/src/unpriv/zca.adoc)

짧은 명령은 필요 없는 필드를 없애거나 표현 범위를 좁혀 공간을 얻는다. C.ADDI에서는 입력과 목적지가 같다는 조건이 그런 역할을 한다. 이 제약은 실행 중 소프트웨어에 강요하는 새로운 덧셈 의미가 아니라, 짧은 표현을 선택할 수 있는 조건이다.

## 레지스터 번호도 압축 여부를 바꾼다

Zca의 register-based C.LW는 입력 주소 레지스터와 목적지를 x8부터 x15까지의 집합에서 고른다. 예를 들어 `lw x9, 12(x10)`은 레지스터 조합과 4바이트 단위 offset 조건이 해당 짧은 형식에 맞는다. 반면 `lw x20, 12(x10)`은 C.LW의 목적지 범위를 벗어난다. 주소 레지스터가 x10이므로 stack pointer x2를 사용하는 C.LWSP로 그대로 바꾸는 것도 해법이 아니다. [RISC-V Zca, Compressed Instruction Formats 및 Register-Based Loads and Stores](https://github.com/riscv/riscv-isa-manual/blob/main/src/unpriv/zca.adoc)

컴파일러가 어떤 레지스터를 배정했는지가 코드 크기에 영향을 줄 수 있는 이유다. 하지만 x8~x15를 무조건 선호하면 레지스터 충돌이나 저장·복구 비용이 늘어날 수 있다. 압축 가능한 표현을 얻는 이익과 다른 배정 비용을 함께 보아야 한다.

Zca 원문은 각 명령이 기본 ISA의 단일 32비트 명령으로 확장되도록 설계했다고 설명한다. 그래서 내부에서 짧은 표현을 일반적인 연산 표현으로 바꾼 뒤 기존 실행 경로를 사용할 수 있다. 이는 Zca에 대한 설명이며, 여러 동작을 담는 다른 compressed 확장까지 전부 같은 방식이라고 넓힐 수는 없다.

## 10개 명령 중 6개가 압축된다면

설명용 코드 조각에 32비트 명령 10개가 있고, 그중 6개를 의미 변화 없이 16비트 표현으로 바꿀 수 있다고 하자. 나머지 4개는 그대로 남는다.

```text
압축 전: 10 × 4 = 40바이트
압축 후:  6 × 2 + 4 × 4 = 28바이트
감소율:  (40 − 28) / 40 = 30%
```

이는 가상 코드의 저장 크기 계산이다. 특정 프로그램에서 측정한 압축률도, 실행 시간이 30% 줄어든다는 주장도 아니다. 실행 명령 개수는 여전히 10개다. 이 코드가 자주 실행되는지, fetch가 병목인지, 캐시 경계를 어떻게 넘는지에 따라 성능 효과는 달라진다.

코드가 작아지면 같은 캐시 공간에 더 많은 동작을 담고 명령을 가져오는 바이트 수를 줄일 기회가 생긴다. 동시에 하드웨어는 명령 길이를 판별하고 섞인 경계를 처리해야 한다. 코드 밀도와 decode 구현의 조건은 함께 바뀐다.

## 내부 확장 뒤에도 원래 길이는 남는다

Zca가 있는 명령 스트림에는 16비트와 32비트 명령이 섞인다. 예를 들어 주소 0x100의 명령이 16비트이고, 그 다음 명령이 32비트라면 순차 PC는 0x100 → 0x102 → 0x106이다. 32비트 명령도 0x102처럼 16비트 경계에서 시작할 수 있다. 원문이 규정하는 IALIGN=16의 효과다. [RISC-V Zca, 도입의 alignment 규정](https://github.com/riscv/riscv-isa-manual/blob/main/src/unpriv/zca.adoc)

decoder가 첫 명령을 내부의 32비트 표현으로 확장했다고 PC까지 4바이트 진행시키면 0x102의 명령을 건너뛴다. 실행 연산을 통일하는 것과 원래 명령 길이·주소를 유지하는 것은 별개다. BOOM의 fetch 설명도 원래 명령의 길이를 추적해 PC 갱신과 분기 등에 쓰도록 한다. [BOOM, Handling RVC](https://docs.boom-core.org/en/latest/sections/instruction-fetch-stage.html)

compressed instruction은 기본 명령 집합을 작은 대체 집합으로 바꾸는 기술이 아니다. 자주 쓰이는 동작 중 짧게 표현할 수 있는 경우를 골라, 기존 표현과 함께 저장하는 기술이다. x9에 5를 더하는 명령이 짧아져도 같은 덧셈을 하고, x9에 40을 더하는 명령은 긴 표현을 쓸 수 있다. 성능과 구현을 평가할 때도 이 의미의 유지, 실제 압축 가능한 비율, 섞인 길이를 처리하는 비용을 각각 확인해야 한다.

