---
title: "vector mask: 읽지 않은 주소의 자리에 0이 생기지는 않는다"
author: "브레이브브라우저쓰자"
published_at: "2026-10-17T06:47:00.223Z"
updated_at: "2026-10-06T00:49:57.553Z"
tags: ["vector mask"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/vector-mask-%EC%9D%BD%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%A3%BC%EC%86%8C%EC%9D%98-%EC%9E%90%EB%A6%AC%EC%97%90-0%EC%9D%B4-%EC%83%9D%EA%B8%B0%EC%A7%80%EB%8A%94-%EC%95%8A%EB%8A%94%EB%8B%A4"
ai_assisted: true
---

# vector mask: 읽지 않은 주소의 자리에 0이 생기지는 않는다

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

[원문](https://ariser.io/@writer_a9817d5a8de0/vector-mask-%EC%9D%BD%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%A3%BC%EC%86%8C%EC%9D%98-%EC%9E%90%EB%A6%AC%EC%97%90-0%EC%9D%B4-%EC%83%9D%EA%B8%B0%EC%A7%80%EB%8A%94-%EC%95%8A%EB%8A%94%EB%8B%A4)

조건을 만족하는 원소만 읽으면 접근할 수 없는 주소도 안전하게 건너뛸 수 있을까. vector mask는 원소마다 실행 여부를 정하지만, 건너뛴 목적지 값을 0으로 만들어 주는 장치는 아니다. 어떤 원소의 접근과 예외를 막고, 어떤 값을 남기는지 함께 읽어야 조건부 벡터 코드를 이해할 수 있다.

## 네 주소 중 두 개만 읽는 예

RISC-V 벡터의 indexed load는 기본 주소에 각 원소의 byte offset을 더해 읽을 주소를 만든다. 가상 환경에서 기본 주소 a0는 4KiB 경계에 맞고, v4의 네 offset은 [0,4096,8192,12288]이라고 하자. 이 네 주소가 놓인 영역 중 첫 번째와 세 번째만 읽기 가능하고 값은 각각 10과 30이다. 두 번째와 네 번째 주소를 실제로 읽으면 접근 예외가 발생한다.

vl=4, vstart=0, e32,m1을 지원하는 설정을 사용하며 v0의 mask 비트는 인덱스 순서대로 [1,0,1,0]이다. 명령은 다음과 같다.

```asm
vluxei32.v v8, (a0), v4, v0.t
```

여기서 32는 offset 원소의 폭이고, 읽는 데이터 폭은 설정한 SEW=32다. v0.t는 mask가 1인 원소를 활성화한다. 목적지 v8, offset v4와 mask v0가 겹치지 않게 선택했다. 주소와 보호 조건은 동작을 설명하기 위한 가정이다. [RISC-V Vector, Vector Load/Store Addressing Modes 및 Indexed Loads](https://github.com/riscv/riscv-isa-manual/blob/main/src/unpriv/vector-common.adoc)

## 비활성 원소의 예외는 발생하지 않는다

원문은 masked vector load/store가 활성 원소에 대해서만 메모리에 접근하고 예외를 일으킨다고 규정한다. 예제에서는 인덱스 0과 2만 읽으므로, 비활성인 1과 3의 접근 불가능한 주소 때문에 이 load가 예외를 내지는 않는다. [RISC-V Vector, Vector Loads and Stores](https://github.com/riscv/riscv-isa-manual/blob/main/src/unpriv/vector-common.adoc)

반대로 mask를 [1,1,1,0]으로 바꾸면 인덱스 1도 활성 원소가 되어 해당 접근 예외가 문제가 된다. mask는 잘못된 주소를 고치는 것이 아니라 그 원소의 작업을 수행할지 결정한다. 모든 mask 비트를 0으로 만든다고 미지원 명령이나 잘못된 레지스터 그룹 지정까지 합법이 되는 것도 아니다. 원소 접근의 예외 억제와 명령 자체의 유효성은 다른 경계다.

이는 scalar 코드에서 조건이 거짓인 경로의 load를 실행하지 않는다는 기능적 효과와 닮았다. 그러나 CPU가 모든 원소를 동시에 완료해야 한다거나, mask 하나가 임의의 여러 명령에 대한 분기 블록을 자동으로 만들지는 않는다. 예제의 unordered indexed load도 활성 원소의 메모리 접근 순서를 인덱스 순서로 보장하지 않는다.

## 읽지 않은 두 자리는 어떤 값일까

load 전 v8의 네 원소가 [100,200,300,400]이고 mask-undisturbed인 mu를 선택했다면 결과는 다음과 같다.

```text
mask: [1,0,1,0]
mu 결과: [10,200,30,400]
```

비활성 목적지의 이전 값은 유지된다. mask-agnostic인 ma라면 그 두 자리가 이전 값을 유지하거나 모든 비트가 1인 값으로 바뀔 수 있다. 32비트 원소에 대한 한 허용 결과는 [10,0xFFFFFFFF,30,400]이다. 앞의 10과 30은 실제 활성 load 결과이며, 뒤의 두 값은 새로 읽은 데이터가 아니다. [RISC-V Vector, Vector Masking 및 mask-agnostic policy](https://github.com/riscv/riscv-isa-manual/blob/main/src/unpriv/vector-common.adoc)

이 상태에 mask 없는 덧셈을 적용하면 네 자리가 모두 입력이 된다. mu에서 남은 200과 400도 계산에 참여하고, ma에서는 선택된 agnostic 값이 참여할 수 있다. 조건부 load만 했다고 다음 연산도 같은 조건부 연산이 되는 것은 아니다.

조건을 유지하려면 필요한 다음 연산에도 해당 mask를 사용하고, 최종 저장에서도 어떤 원소가 유효한지 지켜야 한다. 반대로 전체 원소의 합처럼 모두를 읽는 연산을 하려면 비활성 입력을 정의된 값으로 먼저 준비해야 한다. 초기화를 생략하고 mask가 자동으로 0을 넣어 주리라 기대하면 연산 의미가 바뀐다.

## mask와 작업 길이를 함께 확인한다

mask가 1이라고 언제나 활성 원소인 것도 아니다. 활성 원소는 vstart 이상이고 vl보다 작은 작업 범위에 있으면서 mask가 켜진 원소다. 인덱스가 vl 이상이면 tail이고, vstart보다 작으면 재시작 전에 이미 처리한 범위인 prestart다. 이 영역들은 같은 mask 비트가 1이어도 현재 명령의 활성 접근이 아니다.

검증에서는 mask 값뿐 아니라 vl과 vstart, 목적지 정책을 함께 기록해야 한다. 예제라면 활성 주소 0·8192의 값이 정확한지, 4096·12288의 비활성 접근이 예외를 만들지 않는지, mu에서 이전 목적지가 유지되는지를 서로 다른 조건으로 확인한다. 기능적 접근 억제 규약만으로 캐시 타이밍이나 모든 추측 실행의 물리적 흔적까지 증명했다고 확대해서도 안 된다.

vector mask는 조건별 원소 실행의 계약이다. 읽지 않는 주소의 예외를 억제할 수 있지만, 그 자리에 새 데이터를 만들어 주거나 뒤의 모든 명령을 같은 조건으로 묶지는 않는다. 활성 범위와 목적지 보존 정책을 따라 입력·계산·저장까지 연결해야, 두 주소를 안전하게 건너뛴 효과가 올바른 최종 결과로 이어진다.

