自己書き換えコード

自己書き換えコード（じこかきかえコード、self-modifying code)とは、目的を問わず実行時に自分自身の命令を書き換えるコードを指す。
自己書き換えコードはアセンブリ言語を使用すると簡単に記述できる（CPUのキャッシュを考慮する必要がある）。 また、SNOBOL4やLISPのようなインタプリタ型の高級言語でもサポートされている。また、COBOLには ALTER という命令が存在していた。 コンパイラで実装するのは難しいが、CLIPPERとSPITBOLではその試みが行われている。 バッチスクリプトも自己書き換えコードを頻繁に使用する。
再構成可能コンピューティングは、言ってみれば「自己書き換えハードウェア」である。 再構成可能コンピューティングはソフトウェアとハードウェアの境界を曖昧にする概念である。

状態依存ループを最適化する自己書き換えコード

仮想コードの例は以下の通りである。

repeat N 回 {
  if STATE == 1
     A = A + 1
  else
     A = A - 1

  A に関して処理をする}
自己書き換えコードをこの場合に当てはめると、単純にループを以下のように書き換える。

 repeat N 回 {

  A = A + 1
  A に関して処理をする }
 
 when STATEが変化した時 {
    上記の + 命令を -命令に書き換える。
 }
ふたつの状態に対応した命令コードの書き換えは、XOR交換アルゴリズムを使えば簡単に記述できる。
この手法をとるかどうかは N（ループ回数）が大きいかどうかと、状態変化が頻繁かどうかによる。

自己書き換えコードに対する態度

他にも有効な選択肢がある場合は自己書き換えコードはお勧めできないという人もいる。 というのは、自己書き換えコードは理解しにくいし、後でメンテナンスが困難になるからである。
また他の人は、自己書き換えコードは単にコーディング時にやっていることを実行時にやるだけじゃないかと言う。
自己書き換えコードは初期のコンピュータで限りあるメモリ空間を節約するために使われていた。 また、単純な分岐しかないシステムでサブルーチンを実装するために自己書き換えコードを使用する場合もあった。ドナルド・クヌースのMIXアーキテクチャでもサブルーチン呼び出しを実現するために自己書き換えコードを使用していた。
また、未来の高度に進化した人工知能は本質的に自己書き換えを行うはずだと主張する者もいる。未来のソフトウェアがユーザーとのやり取りから学習し、ほとんど無限のパーソナライゼーションを提供するだろうという見方もある。

偽装のための自己書き換えコード

自己書き換えコードは1980年代のMS-DOS上のゲームで、コピープロテクションを隠すのに使われた。 フロッピーディスクドライブアクセス命令「int 0x13」は、実行プログラムのイメージには存在しないのだが、実行プログラムがメモリにロードされると自己書き換えを実施し、コピープロテクトのためのフロッピーディスクアクセス命令が書き込まれるようになっていた。
自己書き換えコードは自身の存在を隠したいプログラムにも使われることがある。 すなわち、コンピュータウイルスなどである。 自己書き換えコードを使用するウイルスの多くは、同時にポリモルフィックコードを使っている。 ポリモルフィック（多様）なウイルスは、ある意味で自分を突然変異させるプログラムとも言える。 動作中のコードを書き換えることはある種の攻撃（たとえばバッファオーバーラン）にも使われる。

自己参照型機械学習システムでの自己書き換えコード

機械学習システムは一般に学習アルゴリズムは事前に用意され固定であり、パラメータを変化させることで学習を行う。しかし、Jürgen Schmidhuber は1980年代から自己書き換え式の学習アルゴリズムを自ら変更できるシステムをいくつか発表している。機能不全に陥るような自己書き換えに陥らないように、ユーザー指定のフィットネス関数などを使って、有効な書き換えのみが生き残るようにしている。

オペレーティングシステムと自己書き換えコード

自己書き換えコードはセキュリティ上問題があるため、いくつかのオペレーティングシステムではそれを禁止している。懸念されているのは、そのプログラム自身が自分のコードを書き換えることではなく、他者が悪意を持ってコードを改変することである。たとえばOpenBSDの最近のバージョンは W^X（"write XOR execute"）という機能を持っており、あるメモリページについてプログラムは書き込むことができるか「あるいは」実行することができるが、書き込んで実行することはできないというものである。W^X 機能を持った OpenBSDでは自己書き換えコードは通常は動作できない。自己書き換えが必要なプログラムは、mmap で PROT_EXEC | PROT_WRITE 属性でページをマッピングしてそこにコードを書き込まなければならない。
メタなレベルで考えれば、適切なデータ構造を使うことで振る舞いを変化させるプログラムは一種の自己書き換えとも言える（メタプログラミング参照）。

Just-in-time コンパイラ

Javaなどのプログラミング言語には Just-in-time コンパイラがあり、小さなプログラムを機械語に変換して即座に実行する。

キャッシュと自己書き換えコードの問題

最近のプロセッサでは自己書き換えコードは実行速度が遅くなる。 実行コードを書き換えると、命令キャッシュに保持していた筈の命令が使えなくなるので、メモリからキャッシュにロードし直さなければならなくなり、遅くなるのである。
つまり自己書き換えコードで性能改善が図れるのは、書き換えがごくまれにしか発生しない、ループ内のスイッチ切り替え（前述の状態依存ループ）のような場合だけである。 コード書き換えは一瞬で終わるわけではないから、これは何も命令キャッシュに限った問題ではない。
最近のプロセッサは命令を実行前に内部に取り込むので、プログラムカウンタに近い箇所を書き換えるとプロセッサがそれに気づかない可能性があり、書き換え前のコードを実行してしまうことがある。これについては命令プリフェッチキューを参照されたい。

例

NASM文法の自己書き換えx86アセンブラコード：命令プリフェッチキューのサイズ測定

code_starts_here:
   xor cx, cx                  ; レジスタ cx をゼロクリア   xor ax, ax                  ; レジスタ ax をゼロクリアaround:
   cmp ax, 1                   ; ax が変化したかチェック   je found_size

   mov [nop_field+cx], 0x90    ; 0x90 = "nop" (NO oPeration)命令のオペコード
   inc cx

   jmp short flush_queue
flush_queue:
 
   mov [nop_field+cx], 0x40    ; 0x40 = "inc ax" (INCrease ax)命令のオペコードnop_field:
   nop times 256
   jmp around
found_size:

   ;
   ;    これで、レジスタ cx は命令プリフェッチキューのサイズを保持している   ;
このコードは処理の流れを変更して力ずくで命令プリフェッチキューの大きさを調べるものである。 コードを順次書き換えていき、どれだけ書き換えたらプロセッサが書き換え後の命令をフェッチするかを調べることでキューの長さがわかる。 これをプロテクトモードで実行する際にはコンテキストスイッチが発生しないようにしなければならない。 さもなくば、このプログラムは間違った値を返すだろう。