* bit指定について
-(by [[K]], 2014.05.22)
** (0)
-OSECPU-VMのrev2には、アセンブラにbitというよくわからないものが標準で出てくるようになりました。それについての説明です。
-関連するページ:
--[[page0077]]: 32bitまでしかOSECPU-VMはサポートしないという話
--[[page0078]]: VMを改造したい人、移植したい人向けのbitの話
** (1)
-パソコン向けCPUの歴史を考えてみると、8ビットCPUから始まって、16ビット、32ビット、64ビット、と発展してきました。将来はどうなるのでしょうか。128ビットとか256ビットになるのでしょうか。
-こうしてみると、まるでビット数が増えることが技術の進歩のように思うかもしれません。実際64ビットのレジスタはかなり広い範囲の整数を扱うことができます。それまで32ビット演算で収まらなかった場合、複数の整数演算を組み合わせて64ビット計算をしてきたので、プログラムは単純化されて分かりやすくなり高速化されます。
-しかし一方で、デメリットもあります。たとえば1+2+3+...+9+10の計算をすることを考えてみた時に、こんな計算は8ビットどころか、6ビットもあれば十分な計算なのですが、それにもかかわらず64ビットのCPUはこれを64ビットで計算します。その分余計に電力を消費していますし、回路内のゲートも多くなって、高クロック化の妨げになっています。・・・100メートル先のお店に行くために、超高速飛行機を飛ばすようなものです。そんなの徒歩か自転車くらいで十分なのです。演算対象によって適切なビット数というものがあって、それより大きいのは結局は無駄なのです。だから一番大きなビット数に合わせてすべてを設計してしまうと、無駄の多いシステムになってしまいます。
-もし64ビット演算が時々しか必要ないのなら、結局は32ビットCPUが一番良いのかもしれません。いやそれどころか、もしかしたら16ビットCPUくらいが一番いいのかもしれません。
** (2)
-もし全ての分野に共通な最適なビット数が分かれば話は単純です。たとえばそれは32ビットだとしましょう。それならOSECPU-VMは32ビットのVMとして設計してしまえばいいのです。これでOSECPU-VMは全ての分野に適合できるすばらしいVMになります。
-最適なビット数がわからない以上は、この演算は○○ビットでやってください、とプログラム内に記述できるようにすることが一番いいと僕は考えました。そのためにすべての整数演算にbitという定数を記述する部分があります。
-VMは、もしCPUのアーキテクチャが16ビットで、それにもかかわらず32ビットの演算を要求されれば、多倍長演算アルゴリズムを使って32ビット演算をします。したがってプログラマは本当に必要なビット数を書けばいいのです。我慢して小さな数を書く必要はないですし、必要もないのに無駄に大きな数を書く必要もありません。これにより、16ビットや8ビットの組み込みCPUなどでも、無駄に32ビット演算のエミュレーションなどをする必要はなくなります。8ビット演算で十分な時は8ビット演算しかしないからです。逆にもし本当に必要なら遠慮なく256ビットでも4096ビットでも書くべきです。それをわざわざ多倍長演算する必要はありません。それはVMが自動でやってくれることだからです。
** (3)
-一方で、世界最小サイズを目指してコードを書きたい場合は、bit=32とすることをお勧めします。もちろんそれでは無駄な演算になってしまうこともあるでしょう。しかし32ビットはデフォルトになっているので、これをよく使うことはサイズの上では有利です。
-なぜ32ビットをデフォルトにしたかです。正直、多くのケースでは16ビットで十分だと感じてはいました。しかしゲームなどを作る際に、スコアなどは3万以上の数値を使いたいかもしれないと思いました。また、カラーコードでも24ビット以上がほしくなるのではないかと思いました。ということで、デフォルトは32ビットとしました。ちなみにAPIの引数では、16ビットしか要求しないものがかなりあります。
--註:16ビットしか要求していない引数に対して32ビットで渡すことは何の問題もありません。上位ビットが無視されるだけです。
** (4) 具体例
-(4-1) LIMM(16, R00, 0x123);
--R00レジスタに0x123が代入されます。この代入操作には16ビットかそれ以上で行われます。実行後のR00の精度は16ビットになります。
~
-(4-2) ADD(8, R01, R02, R03); // R01 = R02 + R03;
--実行前の状態は次の通りだったとします。
|レジスタ|データ|ビット|
|R01|RIGHT:0x2|RIGHT:4|
|R02|RIGHT:0x1234|RIGHT:16|
|R03|RIGHT:0x56789a|RIGHT:32|
--上記ADD命令を実行するとこうなります。
|レジスタ|データ|ビット|
|R01|RIGHT:0xce|RIGHT:8|
|R02|RIGHT:0x1234|RIGHT:16|
|R03|RIGHT:0x56789a|RIGHT:32|
--[解説] この命令は8ビット以上の結果を要求しています。入力値であるR02とR03の両方とも、8ビット以上の精度を持っていたので、この演算は問題なく実行されて、結果がR01に格納されます。結果は無条件で上書きされるので、R01がそれまで4ビットの精度だったことは全く関係ありません。
--[Q] R02とR03の精度の最低値は16ビットなのだから、R01の精度は16ビットにできるのではないの?
---[A] その通りです。その気になれば0x8aceを結果として格納することはできます。しかし、VMやJITコンパイラによっては、高速化のために「要求された8ビット演算だけ」をやるかもしれません。そうするとやはり上位ビットは計算されていないので不定になります。したがってR01の精度は8ビットとして扱うべきです。
--なお、このADD命令の実行に当たっては、プリフィクス2F-0が必要です。そうでないと、中間結果0x568aceが8ビットを超えてしまっているので、「もしかして間違いではないですか?」と警告のためにセキュリティ例外になってしまいます。
** (5) [[impressions]]より転機:参考になりそうなやり取りなので
-あと、小さいことですが、execStep_checkBitsRange()内で、prefix[0]がセットされている時はレジスタ値(といってもプログラムから見えない部分だと思いますが)をセットしているので、checkという名前にはすこし違和感があります。(ちなみにairJOsecpu Rev.2ではarrangeRegisterUpperBitsという名前にしています) -- [[ttwilb]] SIZE(10){2014-06-03 (火) 23:25:30}
-質問ばかりで申し訳ないですが、VisualStudio君によればosecpu108aではRxxにBIT_DISABLE_REGを設定しているコードはないそうです。しかしinteger.cのいたるところで、Rxxがこの定数でないことを確認するIF文が見られます。では、この定数には一体どんな役割があるのでしょうか。 -- [[ttwilb]] SIZE(10){2014-06-03 (火) 23:59:59}
->checkという名前にはすこし違和感があります。 ・・・なるほど、それは一理あります。まあprefix[0]による修飾はかなり「まれ」なので、ほとんどの場合は名前のとおりcheckなのです。だから関数名はそのままにしますが、コメントは入れておくことにします。正しい名前はcheck-or-fixくらいでしょうかねえ・・・。 -- [[K]] SIZE(10){2014-06-04 (水) 08:31:03}
-BIT_DISABLE_REGについて。ご指摘のとおり、今のVMではRxxのbitがBIT_DISABLE_REGになることはありません。ではこれがセットされるのはどういう状態かというと、高速モードなどの「bitを持たないVM」が実行していた状態をいったん停止して、bitなどのフィールドを付与して安全モードで再開した直後の場合のみに生じる状態です。もともとフィールドがなかったので値が分かりません。仕方ないのでBIT_DISABLE_REGにすることになっています。 -- ''K'' SIZE(10){2014-06-04 (水) 08:59:23}
-なるほど、そういうことでしたか。bitが無い環境のことも考えられているのですね。そうすると、bitが無い環境だと、prefix2f[0]が立っている時に違う動作をしてしまうということで、VMを作る側もプログラムを書く側も気を付けた方がよいですね。 -- [[ttwilb]] SIZE(10){2014-06-04 (水) 23:02:50}
->bitが無い環境だと、prefix2f[0]が立っている時に違う動作をしてしまうということで、VMを作る側もプログラムを書く側も気を付けた方がよいですね。 ・・・この部分についてもう少し理解してもらえるように説明します。 -- [[K]] SIZE(10){2014-06-05 (木) 06:14:03}
--2F-0プリフィクスがあると、安全モード・高速モードで結果が変わるのではないか?・・・これはYesです。しかしNoでもあります。
--高速モードでは、bit[]がなく、したがってレンジチェックもされず、レジスタの上位ビットにゴミが混ざっていても放置されます。高速モードでは2F-0プリフィクスがあっても無視されて値の補正はありません。ゴミがあっても問題ないからです。
--安全モード時に上位ビットのゴミを常に取り除いてきれいにしておくのは、レンジチェックのためだけなのです。
--上位ビットのゴミを明示的に取り除くためには、SBXという命令を実行します。いつでもこれでゴミのない結果に修正できます。これは安全モードでも高速モードでもどちらでも確実に動作します。
--ということで2F-0プリフィクスがあってもなくても、ゴミ部分以外の意味を持つビットだけに注目すれば結果は変わっていないはずです。しかしゴミ部分については結果は変わっているでしょう。先のYes・Noはそういう意味です。
--2F-0プリフィクスの意味: この演算結果は一時的にどうしても上位ビットにゴミを生じてしまうので、たとえ安全モードであってもレンジチェックをするな、プログラマはわかってやっているのであって、ミスではないぞ。
たとえばこんな状況:
R00に0x12、R01に0x34が入っている。R00とR01はどちらもbit=32。これらを足し合わせて、下位4ビットだけがほしい。
これには2つの書き方がある。
(1) ADD(32, R02, R00, R01); LIMM(4, R3F, 0x0f); AND(4, R02, R02, R3F);
(2) PREFIX_2F(0); ADD(4, R02, R00, R01); LIMM(4, R3F, 0x0f); AND(4, R02, R02, R3F);
1のやり方はプリフィクスがいらないけど、ADDは32ビットで演算している。これは必要のない精度の計算だといえる。
無駄な書き方を「強要」しなければいけないとしたら、それは本当に「いい仕様」といえるだろうか?
それに対して2は不要な演算は全くない。
-なるほど、ということはVM側のbit[]は、オーバーフローのチェックと各オペレーションの引数bitのチェックをするためのもので、それが必要ない(エラーのない)コードであればVM側にbit[]が無くても正しく処理できる、ということですね。そう考えるといろいろな仕様がつながってくるように感じます。ありがとうございます。 -- [[ttwilb]] SIZE(10){2014-06-06 (金) 11:59:53}
-ttwilbさんへ。bit[]について分かっていただけたようでうれしいです! -- ''K'' SIZE(10){2014-06-13 (金) 09:32:57}
** (6) プリフィクスについて
-OR(10)~MOD(1B)の整数演算系の命令について、以下の2つのプリフィクスが有効です。
--2F-0プリフィクス:演算結果に本質的にゴミが生じるので、上位ビットのゴミを無視して、レンジオーバーのセキュリティ例外を抑制します。
--2F-1プリフィクス:SHLI(32, R00, R01, 16); を例に説明します。この場合、R01が16ビットの精度しかなくても、結果は32ビットの精度が実現可能です。このようにr1やr2の精度よりもr0の精度の方が大きくできる場合、2F-1プリフィクスを付けます。そうすれば不要なSBX命令を避けられます。osecpu112d以降で有効です。
* こめんと欄
-LIMMではレジスタのbitの値は書き換えられ、ORなどの数値演算ではレジスタのbitは書き換えられない(同じかどうかチェックするだけ)だと思うのですが、ではCP命令のときは、値のみをコピーするのでしょうか?それとも、レジスタのbitも含めた全体をコピーするのでしょうか? -- [[hikarupsp]] SIZE(10){2014-06-08 (日) 15:42:21}
-他の例で言えば、まだ値の代入されたことのない未定義のレジスタ(bit==0)に数値をCPするには、先にLIMMやSBXを実行する必要があるのか、ということです。 -- ''hikarupsp'' SIZE(10){2014-06-08 (日) 15:45:28}
-ORでr0に値を代入する時は確かOR命令の引数bitがr0のbitに適用されるはず。すなわち、SBXを実行する必要はない、ということ。ついでに言うと、r1とr2に関してもbitが等しいことは必須条件でなく、双方のbitがOR命令の引数bit以上あればOK。 -- [[ttwilb]] SIZE(10){2014-06-09 (月) 08:58:42}
-ttwilbさん・Kさん、ありがとうございます。よくわかりました。本当は自分で実行して確かめてみるべきだったと思うのですが、Macで動作させるのに少し手こずってしまって…。 -- [[hikarupsp]] SIZE(10){2014-06-09 (月) 14:20:49}
-計算の途中結果は必ずオーバーフローやアンダーフローが発生しないことをVMが保証する、と考えて良いのでしょうか?現在のVMの実装だと中間結果がオーバーフローしてしまう場合があると思うのですが、これは現在の実装がそうなだけで、将来的には改善されるということでしょうか?VMの能力を超えた計算をしようとしたときにもエラーを発生させる必要があると思います。 -- [[hikarupsp]] SIZE(10){2014-06-10 (火) 14:22:02}
-それとも、中間結果はソースオペランドのレジスタのbitの最大値で計算される、ということになるのでしょうか… -- ''hikarupsp'' SIZE(10){2014-06-10 (火) 14:27:32}
-計算の途中結果は、オーバーフローやアンダーフローがあれば極力検出して知らせる(=セキュリティ例外を発生させて止まる)、というのが現在のスタンスです。もし現在のVM実装で中間結果がオーバーフローしているのに検出できない場合がありそうでしたら、ぜひ教えてください。どうするべきかを検討します! -- [[K]] SIZE(10){2014-06-10 (火) 14:32:43}
-ああなるほど、bit=32のときに 0x7fffffff * 2 とかをやったらどうなんだ、ということですね。これは今は検出できません。検出するための判定がややこしくなるのでやめてしまいました。64ビット版のVMなら比較的容易に検出できます。・・・ということで、16ビット以下についてはほぼ確実に判定できるものの、それ以上については努力目標程度で考えています。 -- ''K'' SIZE(10){2014-06-10 (火) 14:41:14}
-完全に検出させるとなると、結構面倒です。しかしだからといって容易に検出できるケースすら見つけられないというのは、もったいないと思います。ということで、容易に見つけられるものについて見つけて報告するという仕様にしています。しかし厳密にやってくれるVMがあっても構いません。 -- ''K'' SIZE(10){2014-06-10 (火) 14:44:01}
-数値比較命令のbit1に関してもう少し詳細な説明が欲しいです。111dの実装では、比較する二つのレジスタのbitがbit1より大きいことを期待していますが、実際の比較にはbit1は影響していません。bit1の精度で比較を行う、ということならまだわかるのですが、いまいちbit1の目的がわからないのです…。 -- [[hikarupsp]] SIZE(10){2014-06-22 (日) 20:31:37}
-これはいい質問ですね!これは、プログラマが両方のレジスタが少なくとも何ビットの精度を持っているのかを把握するために明記させています。16ビットあると思ったのに8ビットしかなかったとか、そういうことがあっては困るわけです。・・・と、ここまで書いて、ちょっと工夫できることを思いついたので、もう少し研究してからお返事します! -- [[K]] SIZE(10){2014-06-23 (月) 07:34:43}
-CMPcc命令でのbit1ですが、これはこういうことにしました。「2つのレジスタの差を符号付整数で表現するために必要なビット数」。CMPcc命令というのは、本質的にはレジスタの値の差を求めて、その符号によってr0の値を決めるものです。だから差が何ビットになるのかというのは本質的なことです。1万ビットのレジスタがあったとしても、その差が常にプラスマイナス10くらいなら、差を1万ビットも計算する必要はなくて、5ビット程度で十分なはずです。そういうことを表現可能なのです。osecpu112d以降で採用します。 -- ''K'' SIZE(10){2014-06-23 (月) 14:58:45}
-説明ありがとうございます。差が何ビットになるかというのは、つまりSUB命令のbitと同じ扱いと考えてよいでしょうか?差が何ビットになるかを指定したところで、計算すべきビット数が減るわけではない(0x2-0x1と0xffffff2-0xffffff1がどちらも差が1であるように)と思うのですが…。 -- [[hikarupsp]] SIZE(10){2014-06-23 (月) 22:49:58}
-もしCMPccのbit1がSUBのbitと同じような扱いになるとしたら、2F-0プリフィックスも付加可能になるのでしょうか。 -- ''hikarupsp'' SIZE(10){2014-06-23 (月) 22:51:42}
-また、このタイミングになって思ったのですが、2F-0によって、レンジオーバーした数値が暗黙的に丸められる際の数値丸めの手法について、仕様を定義する必要があると思います(どのように値を丸めればよいのでしょうか)。現状の実装を採用するということであれば、それでまあ構わないのですが、確実に定義されていた方が良いと思うのです…。 -- ''hikarupsp'' SIZE(10){2014-06-23 (月) 22:56:00}
-差が何ビットになるか規定すれば、計算すべきビット数は減らせますよー。 0x1234 から 0x1235 を引くことを考えましょう。もし結果が4ビット以下になると分かっていれば、双方から下位4ビットを取ってきて、 0x4 - 0x5 を計算することもできます。ほら、結果は 0xffffffff と 0xf になりますが、下位4ビットは同じです。0xffffffffは32ビットの-1ですが、0xfは4ビットの-1です。つまり0xfをSBXで4→32ビット拡張すると0xffffffffになります。 -- [[K]] SIZE(10){2014-06-24 (火) 06:33:30}
-2F-0は数値を丸めているのではなくて、現在のbitよりも上位にある「ゴミ」を符号ビットで埋めているだけです。つまり仕様はきわめて明白なのです。・・・うーん、やっぱりいまどきの人たちは32ビットとか64ビットに慣れていて、多倍長演算とかをやらないから、この辺のことを説明なしで分かってもらうのは無理があるかなあ。じゃあ、そういう基礎を説明するページを書きます。 -- ''K'' SIZE(10){2014-06-24 (火) 06:43:07}
-CMPcc命令に2F-0プリフィクスはつきません。そもそもCMPcc命令は、「引き算をしてその結果に応じて0か-1をセットするものの、引き算した結果そのものは捨てる」というものです。つまり、捨ててしまうもののために2F-0はつけません。 -- ''K'' SIZE(10){2014-06-24 (火) 06:51:15}
-参考:[[page0094]] -- ''K'' SIZE(10){2014-06-24 (火) 12:39:20}
-この言葉ものすごく分かりやすいです! > 2F-0は数値を丸めているのではなくて、現在のbitよりも上位にある「ゴミ」を符号ビットで埋めているだけです。 -- ''hikarupsp'' SIZE(10){2014-06-24 (火) 16:32:37}
-2F-0プリフィクスの働きについては十分理解できました。 -- ''hikarupsp'' SIZE(10){2014-06-24 (火) 16:44:38}
-ただ、「計算すべきbitが減る訳ではない」と言ったのは、ほかの整数演算命令ではbitを指定していても、そのbit分だけ演算するのではなく、一度完全な中間結果を出して、それが収まりきらないbitだったらエラーにする、という仕様だったので、CMPcc命令ではその部分を統一しなくていいのだろうか、と疑問に思ったからなのです。 -- ''hikarupsp'' SIZE(10){2014-06-24 (火) 16:49:28}
-これもいい質問ですね。まず他の整数演算では指定されたbitではなくて32ビット以上で計算しています。それはなぜかというと、プログラマのミスを探すためです。プログラマは10ビットで十分だと言っているけど、本当は十分ではないかもしれません。それを見つけてあげるのがOSECPU-VMの安全モードの仕事です。これをクリアできていれば、bitの指定は間違ってなくて、だから16ビットマシンや8ビットマシンで無駄なく実行できると安心できるわけです。高速モードでは、指定されたbit以上の演算をすることは保証しますが、チェックはしません。CMPccでも安全モードでは32ビット以上で差を計算して、bit1の範囲に収まっているかどうかを確認します。高速モードではbit1以上で比較することは保証しますが、32ビットではないかもしれません。・・・ほら、同じですよね? -- [[K]] SIZE(10){2014-06-24 (火) 17:29:57}
-あーなるほど…やっとKさんのbitの考え方がわかった気がします。色々誤解していたのは、OSECPUにある複数のモードの差異をよくわかっていなかったからだと思います。すごくいい考え方ですね、これ…。色々教えていただいてありがとうございます。 -- ''hikarupsp'' SIZE(10){2014-06-24 (火) 19:20:28}
-bitはややこしいので、きっと分かってもらえないだろうなと思って(というか興味すら持ってもらえないだろうなと思って)、あまり詳しくは説明しない方針でした。最初は。・・・でもttwilbさんやhikarupspさんが興味を持ってくれて、詳しく説明する機会ができました。しかも良さまで理解してもらえてうれしいです。 -- [[K]] SIZE(10){2014-06-24 (火) 20:43:43}
-将来的に、主要な商用CPUにも同じ仕様が導入されればいいですね。OSECPUのbitの仕様にはそんなポテンシャルがあると思います。 -- [[ttwilb]] SIZE(10){2014-06-24 (火) 21:14:35}
-どうもありがとうございます。とりあえず、ビット数が多ければ多いほどいいとか、ビット数が上がるたびに命令セットが変わって○○ビット対応版を作らなきゃいけないとか、そういうのは終わってほしいですね・・・。 -- [[K]] SIZE(10){2014-06-25 (水) 05:14:40}
#comment