# [EIP8013] EVMのジャンプ先を静的に読み取れるようにする仕組みを理解しよう！

> EIP8013は、EVMに以下の5つの命令を追加します。

- URL: https://cardene.dev/articles/7f7bc69dac48b12dd3b4
- 公開日: 2026-06-02
- 更新日: 2026-06-02
- トピック: Blockchain
- タグ: eip, evm, ブロックチェーン, opcode

## はじめに

初めまして。
『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください！

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://twitter.com/cardene777

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

今回は「**EVMのジャンプ先を命令内の相対オフセットで表し、実行前に制御フローを読み取りやすくするEIP8013**」についてまとめていきます。

**EIP8013**は、EVMに静的な相対ジャンプ命令を追加する提案です。
従来の `JUMP` や `JUMPI` は、ジャンプ先をスタック上の値から決めます。
これに対して **EIP8013** は、命令そのものに符号付きの相対オフセットを持たせ、ジャンプ先を実行前に検証できるようにします。

:::message
以下にまとめられている提案を解説しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-8013

## 概要

**EIP8013**は、EVMに以下の5つの命令を追加します。

| 命令 | オペコード | 役割 |
|------|------------|------|
| `RJUMP` | `0xe0` | 無条件に相対位置へ移動します。 |
| `RJUMPI` | `0xe1` | 条件が真の時だけ相対位置へ移動します。 |
| `RJUMPV` | `0xe2` | スタックから取り出した値でジャンプ表を選びます。 |
| `RJUMPSUB` | `0xe3` | サブルーチンへ相対ジャンプします。 |
| `RJUMPSUBV` | `0xe4` | ジャンプ表でサブルーチンを選びます。 |

どの命令も、ジャンプ先を命令列の中へ埋め込まれた即値から決めます。
`RJUMP`、`RJUMPI`、`RJUMPSUB` は2バイトの相対オフセットを持ちます。
`RJUMPV` と `RJUMPSUBV` は、`max_index` と複数の相対オフセットを持つジャンプ表を使います。

従来の `JUMP` / `JUMPI` と **EIP8013** の違いは以下の図です。

![EIP8013の全体像](/images/qiita-e9954f0f-c64b-499b-b89c-4800524eac22.webp)

左側の従来方式では、ジャンプ先アドレスがスタック上の値として現れます。
そのため、解析ツールや実装は、実行時のスタック値を考えながら移動先を確認する必要があります。
右側の **EIP8013** では、移動先が命令内の相対オフセットから決まるため、実行前に制御フローを読み取りやすくなります。

この提案は、既存の `JUMP` や `JUMPI` をすぐに削除するものではありません。
静的に決められる制御フローでは `RJUMP` 系を使えるようにし、動的ジャンプが必要な場面は残します。

## 動機

EVMのジャンプは非常に柔軟です。
`JUMP` と `JUMPI` はスタックからジャンプ先を取り出すため、実行時に移動先を決められます。
一方で、この柔軟さは、バイトコード解析や検証を難しくします。

たとえば、ある `JUMP` の移動先が実行時の計算結果で変わる場合、解析ツールは「どこへ飛ぶか」をすぐには決められません。
さらに、移動先として有効な位置を示すために `JUMPDEST` が必要です。
この仕組みは安全性のために重要ですが、静的に分かるジャンプまで同じ扱いにすると、コードサイズや実行コストも増えます。

実際には、すべての制御フローが動的である必要はありません。
`if`、`while`、`switch`、関数呼び出しのような多くの処理は、コンパイラが生成する時点で移動先を決められます。
**EIP8013** は、そのような制御フローを相対オフセットで表せるようにします。

目的は大きく3つあります。

- ジャンプ先の検証を実行前に済ませやすくする。
- `JUMPDEST` を減らし、コードサイズとガスを削減する。
- コンパイラ、静的解析、事前コンパイル、ZK向けの回路生成などで扱いやすい制御フローにする。

この背景には、EVMの静的制御フローを整える一連の議論があります。
特に **EIP7979** は、`CALLSUB`、`ENTERSUB`、`RETURNSUB` というサブルーチン向け命令と、検証済みコードの考え方を扱います。
**EIP8013** はこの検証の考え方を拡張し、相対ジャンプ系の命令が有効な命令位置だけを指すように確認します。

## 仕様

**EIP8013** の中心は、命令の直後の位置を基準にした相対オフセットです。
提案ではこの基準位置を `PC_post_instruction` と呼びます。
これは、オペコードと即値をすべて読み終えた直後の `PC` です。

`relative_offset` は16ビットの符号付き整数として、2の補数、ビッグエンディアンでエンコードされます。
そのため、前方にも後方にもジャンプできます。
最大の正方向は `32767` バイトです。

相対オフセットの考え方は以下の図です。

![相対オフセットの基準](/images/qiita-e16016db-814c-4f03-97a8-5d1978a514e5.webp)

基準は `RJUMP` 命令の先頭ではなく、即値を読み終えた直後です。
そこから相対オフセットを足した位置が移動先になります。
移動先は命令の先頭である必要があり、`PUSH` などの即値データ内やコード範囲外は無効です。

### 5つの命令

追加される命令の役割は以下です。

![EIP8013で追加される命令](/images/qiita-5a7e3f9c-9baf-4798-8edf-7869f50bb27a.webp)

#### `RJUMP`

`RJUMP` は無条件の相対ジャンプです。
2バイトの `relative_offset` を読み、`PC` を `PC_post_instruction + relative_offset` に設定します。

従来の `PUSH` で絶対位置を積んで `JUMP` する処理を、1つの命令と即値で表せます。
移動先は命令内に書かれているため、実行前の検証で有効な命令位置かどうかを確認できます。

#### `RJUMPI`

`RJUMPI` は条件付きの相対ジャンプです。
スタックから `condition` を1つ取り出し、`condition` が0ではない時だけ相対ジャンプします。
`condition` が0なら、ジャンプせずにそのまま後続命令へ進みます。

従来の `JUMPI` では、条件と移動先の両方がスタックに関わります。
`RJUMPI` では、条件だけをスタックから取り、移動先は命令内の相対オフセットから決めます。

#### `RJUMPV`

`RJUMPV` はジャンプ表を使う相対ジャンプです。
スタックから `case` を取り出し、その値をインデックスとして相対オフセット表を参照します。

エンコードは `max_index` と `max_index + 1` 個の `relative_offset` です。
`max_index` は8ビットの符号なし値なので、表には最大256個の移動先を入れられます。
`case > max_index` の時はジャンプせずに、そのまま後続命令へ進みます。

この挙動により、`switch` 文のような分岐を表しやすくなります。
また、`RJUMPV 0 relative_offset` は、`case` が0の時だけジャンプし、それ以外では後続命令へ進む書き方になります。
提案では、この書き方を `ISZERO RJUMPI relative_offset` の代わりに使える場面があると説明しています。

#### `RJUMPSUB`

`RJUMPSUB` はサブルーチンへ入るための相対ジャンプです。
`PC_post_instruction` をリターンスタックへ積み、`PC` を `PC_post_instruction + relative_offset` に設定します。
移動先は `ENTERSUB` でなければなりません。

ここでの `ENTERSUB` と `RETURNSUB` は、**EIP7979** が導入するサブルーチン向け命令です。
`RJUMPSUB` は、`CALLSUB` と同じ「戻り先を保存してサブルーチンへ入る」仕組みを、相対オフセットで直接使えるようにします。

#### `RJUMPSUBV`

`RJUMPSUBV` はジャンプ表でサブルーチンを選ぶ命令です。
スタックから `case` を取り出し、該当する相対オフセットを使って `ENTERSUB` へ移動します。
`case > max_index` の時はジャンプせず、後続命令へ進みます。

`RJUMPV` と同じく、表のサイズは最大256です。
違いは、移動先が通常の命令位置ではなく `ENTERSUB` に限定される点です。

### 検証ルール

**EIP8013** は、**EIP7979** の検証アルゴリズムを拡張します。
検証では、すべての `RJUMP` 系命令が有効な移動先を指しているかを確認します。

無効な移動先は以下です。

- コード範囲外。
- `PUSH` の即値データ内。
- `RJUMP`、`RJUMPI`、`RJUMPV` などの即値データ内。
- `RJUMPSUB` / `RJUMPSUBV` なのに、移動先が `ENTERSUB` ではない位置。

`RJUMP`、`RJUMPI`、`RJUMPV` の移動先は `JUMPDEST` でもよいですが、必須ではありません。
命令の先頭であれば、`JUMPDEST` 以外の命令へ移動できます。
一方で、`RJUMPSUB` と `RJUMPSUBV` は `ENTERSUB` だけを移動先にできます。

この違いにより、通常の制御フローとサブルーチン呼び出しを分けて検証できます。

### ガスコスト

提案では、ジャンプ先の検証を事前に済ませられるため、実行時に動的ジャンプと同じ確認を繰り返す必要はないとしています。
推奨されているコストは以下です。

| 命令 | 推奨コスト |
|------|------------|
| `RJUMP` | `2` |
| `RJUMPI` | `4` |
| `RJUMPV` | `4` |
| `RJUMPSUB` | `5` |
| `RJUMPSUBV` | `5` |

従来の `JUMP` や `JUMPI` と比べて、移動先の検証を静的に済ませられる点がコスト低下の理由です。
また、移動先ごとに `JUMPDEST` を置かなくてもよい場面では、`JUMPDEST` 1バイト分のデプロイコストと、実行時の `JUMPDEST` コストも削減できます。

## 設計根拠

相対アドレスを使う理由は、コード片を移動しやすくするためです。
絶対アドレスでジャンプ先を書くと、コードの前後へ命令を挿入した時に移動先を再計算しやすくなります。
相対オフセットなら、まとまったコード片を別の場所へ置いても、その内部の移動関係を保ちやすくなります。

16ビットの即値を採用する理由は、表現できる距離と命令サイズのバランスです。
8ビットでは、後方へ戻れる距離や関数間の移動には不足しやすくなります。
一方で、16ビットなら大きなコードでも多くの相対移動を表せます。

**EIP170** は、コントラクトの実行コードサイズ上限を `24,576` バイトに定めています。
**EIP3860** は、`initcode` の上限を `49,152` バイトに定めています。
提案では、これらの上限を踏まえて16ビットの相対オフセットで十分だと説明しています。

:::message
**EIP170**については以下の記事を参考にしてください。
:::

[\[EIP170\] コントラクト作成時にサイズ制限を設ける提案を理解しよう！](/articles/eef7d90d07aae21dfe6f)

:::message
**EIP3860**については以下の記事を参考にしてください。
:::

[\[EIP3860\] initcodeに対してサイズの制限とガスコストを課す提案を理解しよう！](/articles/df8d2a331482e6b7c66b)

`PUSHn JUMP` の組み合わせに割引を与えない点も重要です。
既存命令の並びへ特別な最適化ルールを入れると、ガス計算やEVM実装の内部表現が複雑になります。
**EIP8013** は、既存命令の意味を特別扱いするのではなく、新しい命令として静的ジャンプを定義します。

`JUMPDEST` を不要にできる点も設計上の大きな理由です。
従来の動的ジャンプでは、どこへ飛べるかを制限するために `JUMPDEST` が必要でした。
しかし、静的ジャンプでは移動先が即値に書かれているため、解析時に有効な移動先を直接確認できます。
そのため、すべての移動先へ `JUMPDEST` を置く必要はありません。

## 互換性

**EIP8013** は新しいオペコードを追加する提案です。
既存の `JUMP`、`JUMPI`、`JUMPDEST` の挙動を直接変更するものではありません。
そのため、既存バイトコードとの互換性リスクはないとされています。

ただし、コンセンサスルールとして新しい命令を追加するため、導入にはハードフォークが必要です。
クライアント実装は、5つの命令の実行ルールだけでなく、即値の読み取り、ジャンプ表、検証ルール、ガスコストをそろえる必要があります。

また、**EIP8013** は **EIP7979** を前提にしています。
特に `RJUMPSUB` と `RJUMPSUBV` は、`ENTERSUB` と `RETURNSUB` によるサブルーチンの仕組みを使います。
そのため、サブルーチン系の相対ジャンプだけを切り離して導入する設計ではありません。

## テストケース

提案では、検証と実行の両方でテストすべきケースを挙げています。
中心になるのは、相対オフセットが有効な命令位置を指しているか、条件や表のインデックスで正しく分岐するかです。

検証では以下を確認します。

| ケース | 期待される扱い |
|--------|----------------|
| `RJUMP` / `RJUMPI` / `RJUMPV` が `JUMPDEST` を指す | 有効です。 |
| `RJUMP` / `RJUMPI` / `RJUMPV` が `JUMPDEST` 以外の命令先頭を指す | 有効です。 |
| 正方向、負方向、0の相対オフセット | 移動先が有効なら認められます。 |
| `RJUMPV` / `RJUMPSUBV` の表サイズが1から256 | 範囲内なら有効です。 |
| 即値が途中で切れている | 無効です。 |
| 移動先がコード範囲外 | 無効です。 |
| 移動先が `PUSH` や `RJUMP` 系の即値データ内 | 無効です。 |
| `RJUMPSUB` / `RJUMPSUBV` の移動先が `ENTERSUB` ではない | 無効です。 |

実行では以下を確認します。

| 命令 | 確認内容 |
|------|----------|
| `RJUMP` | 正方向、負方向、0の相対移動。 |
| `RJUMPI` | 条件が0の時に進み、0ではない時に相対移動すること。 |
| `RJUMPV` | `case` が表内の時に該当先へ移動し、表外の時に進むこと。 |
| `RJUMPSUB` | `ENTERSUB` へ入り、`RETURNSUB` で戻れること。 |
| `RJUMPSUBV` | 表で選んだ `ENTERSUB` へ入り、表外なら進むこと。 |

`RJUMPV` と `RJUMPSUBV` では、`case > max_index` の時に例外停止するのではなく、ジャンプせずに進みます。
この挙動により、ジャンプ表の直後へデフォルト処理を置きやすくなります。

## セキュリティ

**EIP8013** の安全性で重要なのは、即値を持つ命令を正しく検証することです。
相対オフセットが `PUSH` データ内やコード範囲外を指すと、命令列の解釈が実装ごとにずれる恐れがあります。
そのため、実行前の検証で移動先を必ず弾く必要があります。

静的な相対ジャンプは、実行時に移動先を再確認しなくてよい設計です。
これは実行コストを下げる利点ですが、裏返すと、検証アルゴリズムの実装ミスがそのままコンセンサス差につながります。
特に、ジャンプ表のサイズ、即値の境界、`RJUMPSUB` 系の `ENTERSUB` 制約は慎重にそろえる必要があります。

提案では、`RJUMPV` と `RJUMPSUBV` の表は最大256エントリに制限されています。
そのため、表の読み取りそのものが大きなDoS要因になりにくい設計です。

## 引用

Greg Colvin (@gcolvin), Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0), Paweł Bylica (@chfast), "EIP-8013: Static relative jumps and calls for the EVM," Ethereum Improvement Proposals, no. 8013, August 2025. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-8013.

## 最後に

今回は「**EVMのジャンプ先を命令内の相対オフセットで表し、実行前に制御フローを読み取りやすくするEIP8013**」についてまとめてきました。

EVMの動的ジャンプは柔軟ですが、解析や検証の負担を大きくします。
**EIP8013** は、静的に決められる制御フローを `RJUMP` 系の命令で表し、ジャンプ先の検証を実行前へ移します。
これにより、コードサイズ、実行コスト、解析しやすさの面で改善を目指します。

特に重要なのは、`JUMPDEST` を必要としない相対ジャンプと、`ENTERSUB` へ入るサブルーチン向け相対ジャンプを分けている点です。
通常の分岐、ジャンプ表、サブルーチン呼び出しをそれぞれ明示できるため、コンパイラや検証ツールが制御フローを扱いやすくなります。

今後もEthereum関連の規格や技術についてまとめていきます。
