副作用についていろいろ
計算機の中の副作用について。
副作用 (プログラム)(Wikipedia)
用語が違ってるかもしれないけど、以下の定義で考えていく。
関数 ある入力を与えると出力が一意に決まる。
手続き 関数とは異なり他のグローバル変数などを参照して出力が決まる。他のグローバル変数を書き換えることもある。
(1)論理回路のレベル
組み合わせ回路は状態を持たないので、組み合わせ回路のみでできたモジュールの入口と出口で見れば関数である。現代の加減算器、乗算器、バレルシフタなど。
演算器の中に状態を持つものもある。中間結果を保持する加減算器、乗算器、ビット数分シフトするシフタ、除算器など。この中間結果が外部から触れない場合は関数である。
演算命令は入力が与えられれば出力が決まる。除算などは内部的には中間結果をレジスタを使用して演算を繰り返しているが、命令の発行から完了までを見ればサイクル数が長くなるだけで、ある入力で出力が決まる点は変わらない。回路規模を無視すればすべて組み合わせ回路(テーブル)で表現することも可能。
(2)命令セットのレベル
多倍長演算でキャリーやボローを利用する場合は条件フラグが見えてしまう。
初期のRISC系にあるステップ乗算命令、ステップ除算命令は中間結果を保持するレジスタがアクセス可能。(SPARCだと%yレジスタ、MIPSはHI/LOレジスタ)
これらを参照したり書き換えたりすると関数ではなくなる。多倍長演算や乗除算が完了するまで、中間結果を外部からアクセスさせない約束でコードを書けば関数となる。
だが、完全に保護する手段はない。プロセッサの汎用レジスタはどこからでも触れるグローバルなものなのでコーディング時のミスでついうっかり上書きしてしまうことがあり得る。サブルーチンコールでも限られた数のグローバル変数をサブルーチン側で他と重ならないように使いまわしている。
サブルーチン内で使われる汎用レジスタが外部から変更されないなら関数と言える。通常はプログラム側で参照や変更をされないよう気をつけなければならないが、レジスタ・ウィンドウのようにサブルーチン内だけで使用するレジスタを割り当て・開放するしくみがある。
ちょっと脱線:SPARCのレジスタ・ウィンドウについて
サブルーチンを呼び出す側は%o0~%o7に引数を用意し、サブルーチンコールする。ここでレジスタウィンドウをsave(CWP+1)すると、先ほど%o0~%o7だったレジスタは%i0~%i7としてアクセスでき、新たにローカルで使用する%l0~%l7と新しい%o0~%o7が割り当てられる。これらは呼び出し元からは見えないので、ローカルの作業領域と次のサブルーチンコールの引数用として使用できる。サブルーチンの処理が終わり値を返す時は%i0~%i7に書き込みrestore(CWP-1)して戻ると、呼び出し元から%o0~%o7をサブルーチンの結果として取り出すことができる。
※が、これも完璧ではない。CWPレジスタをいじれば隠したレジスタも触れる。
スタックというしくみは一時的に汎用レジスタの中身を退避して、ある用途に使い終われば元に戻すことができる。ハードウェアスタックはソフトから途中の部分を参照できないよう隠してある。メモリ上にスタック領域があるものは意図せず壊される可能性がある。
(3)命令セット:特権モード
プロセッサの動作モードを変更するようなレジスタは保護されていることがある。ユーザモードと特権モードを持つプロセッサでは、特権モードでないと全体に影響があるようなレジスタの変更はできないようになっている。
(4)メモリ
特権モードによって割り当てられたメモリの範囲内だけをアクセスし、外部とのやりとりは引数(特定の汎用レジスタを引数用に決め、結果もレジスタで返す)のみで行うプログラムは関数といえる。
このメモリ割り当て/解放がプログラム側できちんと管理されていないとおかしくなる。
(5)I/O
I/Oアクセスするプログラムは関数ではない。
関数のつもりで書いたコードが意図しない動作をするケースとして、中間結果を保持する部分が参照されたり破壊されたりして手続き的になってしまった場合を考える。
ソフトから触れない部分(1)は保護されている。(2)の演算レベルだとより慎重に作られ検証もしっかりされているライブラリで提供されると考えればまあ大丈夫だろうけど、これ以降はコーディングの約束で守られているような感じもある。関数として実行しているサブルーチンでは局所的にしかアクセスできないリソースを割り当てて外部からはアクセスできない、内部からは外部を参照できないように保護するしくみは考えられないだろうか。
あんまりやりすぎると高級言語志向のプロセッサになってしまいそうだ。NS32032やiAPX432あたりはその方向の機能があると聞く。実行時により安全なプロセッサというのは流行らんのだろうか。
副作用 (プログラム)(Wikipedia)
用語が違ってるかもしれないけど、以下の定義で考えていく。
関数 ある入力を与えると出力が一意に決まる。
手続き 関数とは異なり他のグローバル変数などを参照して出力が決まる。他のグローバル変数を書き換えることもある。
(1)論理回路のレベル
組み合わせ回路は状態を持たないので、組み合わせ回路のみでできたモジュールの入口と出口で見れば関数である。現代の加減算器、乗算器、バレルシフタなど。
演算器の中に状態を持つものもある。中間結果を保持する加減算器、乗算器、ビット数分シフトするシフタ、除算器など。この中間結果が外部から触れない場合は関数である。
演算命令は入力が与えられれば出力が決まる。除算などは内部的には中間結果をレジスタを使用して演算を繰り返しているが、命令の発行から完了までを見ればサイクル数が長くなるだけで、ある入力で出力が決まる点は変わらない。回路規模を無視すればすべて組み合わせ回路(テーブル)で表現することも可能。
(2)命令セットのレベル
多倍長演算でキャリーやボローを利用する場合は条件フラグが見えてしまう。
初期のRISC系にあるステップ乗算命令、ステップ除算命令は中間結果を保持するレジスタがアクセス可能。(SPARCだと%yレジスタ、MIPSはHI/LOレジスタ)
これらを参照したり書き換えたりすると関数ではなくなる。多倍長演算や乗除算が完了するまで、中間結果を外部からアクセスさせない約束でコードを書けば関数となる。
だが、完全に保護する手段はない。プロセッサの汎用レジスタはどこからでも触れるグローバルなものなのでコーディング時のミスでついうっかり上書きしてしまうことがあり得る。サブルーチンコールでも限られた数のグローバル変数をサブルーチン側で他と重ならないように使いまわしている。
サブルーチン内で使われる汎用レジスタが外部から変更されないなら関数と言える。通常はプログラム側で参照や変更をされないよう気をつけなければならないが、レジスタ・ウィンドウのようにサブルーチン内だけで使用するレジスタを割り当て・開放するしくみがある。
ちょっと脱線:SPARCのレジスタ・ウィンドウについて
サブルーチンを呼び出す側は%o0~%o7に引数を用意し、サブルーチンコールする。ここでレジスタウィンドウをsave(CWP+1)すると、先ほど%o0~%o7だったレジスタは%i0~%i7としてアクセスでき、新たにローカルで使用する%l0~%l7と新しい%o0~%o7が割り当てられる。これらは呼び出し元からは見えないので、ローカルの作業領域と次のサブルーチンコールの引数用として使用できる。サブルーチンの処理が終わり値を返す時は%i0~%i7に書き込みrestore(CWP-1)して戻ると、呼び出し元から%o0~%o7をサブルーチンの結果として取り出すことができる。
※が、これも完璧ではない。CWPレジスタをいじれば隠したレジスタも触れる。
スタックというしくみは一時的に汎用レジスタの中身を退避して、ある用途に使い終われば元に戻すことができる。ハードウェアスタックはソフトから途中の部分を参照できないよう隠してある。メモリ上にスタック領域があるものは意図せず壊される可能性がある。
(3)命令セット:特権モード
プロセッサの動作モードを変更するようなレジスタは保護されていることがある。ユーザモードと特権モードを持つプロセッサでは、特権モードでないと全体に影響があるようなレジスタの変更はできないようになっている。
(4)メモリ
特権モードによって割り当てられたメモリの範囲内だけをアクセスし、外部とのやりとりは引数(特定の汎用レジスタを引数用に決め、結果もレジスタで返す)のみで行うプログラムは関数といえる。
このメモリ割り当て/解放がプログラム側できちんと管理されていないとおかしくなる。
(5)I/O
I/Oアクセスするプログラムは関数ではない。
関数のつもりで書いたコードが意図しない動作をするケースとして、中間結果を保持する部分が参照されたり破壊されたりして手続き的になってしまった場合を考える。
ソフトから触れない部分(1)は保護されている。(2)の演算レベルだとより慎重に作られ検証もしっかりされているライブラリで提供されると考えればまあ大丈夫だろうけど、これ以降はコーディングの約束で守られているような感じもある。関数として実行しているサブルーチンでは局所的にしかアクセスできないリソースを割り当てて外部からはアクセスできない、内部からは外部を参照できないように保護するしくみは考えられないだろうか。
あんまりやりすぎると高級言語志向のプロセッサになってしまいそうだ。NS32032やiAPX432あたりはその方向の機能があると聞く。実行時により安全なプロセッサというのは流行らんのだろうか。
この記事へのコメント