# fj.sys.alpha に振ってみる。Followup-To に関しては様子見というこ
# とで。
<a4snu6$2i6u$2...@news2.rim.or.jp>の記事において
doh...@hf.rim.or.jpさんは書きました。
> In article <a49c8n$30$1...@yagi.ecei.tohoku.ac.jp>
> <ka...@sra-tohoku.co.jp> writes:
>
> > #自己改変コードはインタプリタ型か?→CPUはマシン語のインタプリタである
> >
> > もちろんコンパイル型でもコンパイラを内蔵すれば同じことが
> > できますが、そういうのをコンパイラ言語と言うかどうか。
>
> WindowsNT for alpha に FX!32 とかいう (当初単なるエミュレータだと思っ
> ていたのですが) Pentium のコードを動的に alpha の命令に置換しながら実
> 行するとかいうのがあるそうなんですけど, 検索すると dynamic recompile
> とも interpret とも出てくるようですね.
>
> # これの技術的な資料ってどこかにないかな.
http://www.compaq.co.jp/products/fx32/fx32jmec.html これですよね?
# メリケンこんぱっきゅでは検索しても無しだけど、ぽんにちこんぱっ
# きゅにはあるのは何故。
これの技術資料って Compaq 内部でしか見れないとか。
で、上記の「動作メカニズム」を読むと、最初の実行時は x86 エミュレ
ータが動いて、それと同時にネイティブコード変換を行っておいて、二
回目の実行時以降はこの変換コードの方で動くって事みたいですね。
基本的には VAX VMS --> OpenVMS/APX の VAX Environment Software
Translator (VEST)/Translated Image Environment (TIE) の技術をベー
スにしているのではなかろうか。
で、こっちの VEST に関しても、手元の OpenVMS の膨大な(^^;)ドキュ
メントをざっと見渡しても、どう使うかはあっても、内部的な技術資料
は見つからないっす。
# 結構役たたずな記事か?(^^;
--
成田 隆興 @ エー・アイ・ソフト株式会社ソリューシュン開発部
E-mail tak...@aisoft.co.jp
『十分間で決断し、短い理由を添えよ。』
> http://www.compaq.co.jp/products/fx32/fx32jmec.html これですよね?
ですです. なんか best technology 賞とかもらってるし.
> で、上記の「動作メカニズム」を読むと、最初の実行時は x86 エミュレ
> ータが動いて、それと同時にネイティブコード変換を行っておいて、二
> 回目の実行時以降はこの変換コードの方で動くって事みたいですね。
一旦終了させないといけないようで, 動的というのとはちと違いましたね
(「二回目以降」の意味の取り違いでした).
いや, 知りたかったのはですね. x86 命令って prefix が 0~4 バイト付くこ
とがあるんですよ. それでオペランドが変わったりするんですけど,
| |
+-------+ ← ココと
| prfix |
+-------+ ← ココ
| inst. |
+-------+
| |
上のそれぞれの部分に分岐されたりしたらどうするんだろうと思って.
--
Kazuo Fox Dohzono / doh...@hf.rim.or.jp
<a4t0po$2mi9$1...@news2.rim.or.jp>の記事において
doh...@hf.rim.or.jpさんは書きました。
> いや, 知りたかったのはですね. x86 命令って prefix が 0~4 バイト付くこ
> とがあるんですよ. それでオペランドが変わったりするんですけど,
>
> | |
> +-------+ ← ココと
> | prfix |
> +-------+ ← ココ
> | inst. |
> +-------+
> | |
>
> 上のそれぞれの部分に分岐されたりしたらどうするんだろうと思って.
ほとんどなんの根拠も無いのですが、前の記事で書いた VMS のトランス
レータのドキュメントを読んで想像した結果ですが、おそらくそういう
命令にぶち当たったときは変換出来なくて、実行時に VMS で言えば
Translated Image Environment が VAX コードを解釈実行するように、
元イメージを解釈実行するのではなかろうかと。
先の VEST では、「出来るだけ多くの VAX コードを AXP コードにトラ
ンスレートします。TIE は、Alpha AXP 命令に変換出来なかった VAX コ
ードを解釈します。」とありまして、続いて TIE の解釈するものとして
「VEST が静的に識別出来なかった命令」ってのが挙げられています。
おそらくこれに倣っているのでは無いでしょうか。
ついでに書いておくと、VEST で変換されて出来た AXP の .EXE ファイ
ルは、内部に VAX の .EXE ファイルイメージを保存しているそうで、こ
れなんぞも FX!32 では元イメージがそのまま残っているってふうになる
のでしょうけれど、状況的に似ているし。
Raymnod J.Hookway and Mark.A.Herdeg,
DIGITAL FX!32: Combining Emulation and Binary Translation
Digital Technical Journal Vol.9 No.1, 1997
が,
http://research.compaq.com/wrl/DECarchives/DTJ/DTJP01/DTJP01PF.PDF
にあるようです.
天海
# 別に他の人がレポートしてもいいですよー :-)
In article <a4t4c6$lgi$1...@nwall1.odn.ne.jp>
tak...@aisoft.co.jp (Narita Takaoki) writes:
> 先の VEST では、「出来るだけ多くの VAX コードを AXP コードにトラ
> ンスレートします。TIE は、Alpha AXP 命令に変換出来なかった VAX コ
> ードを解釈します。」とありまして、続いて TIE の解釈するものとして
> 「VEST が静的に識別出来なかった命令」ってのが挙げられています。
それはスタックに命令を書いて実行するような場合じゃないでしょうか (演算
によって分岐先が定まる命令もあるので, どのアドレスも分岐先になり得ると
考えると TIE 頼りになっちゃう). 分岐先アドレスが異なる上にデコードした
結果も異なるので, 別々にデコードした異なるコードの固まりに変換されるん
じゃないかという気がします.
# 自己改変コードは…NT だと元々そういうのはないのかな?
> ついでに書いておくと、VEST で変換されて出来た AXP の .EXE ファイ
> ルは、内部に VAX の .EXE ファイルイメージを保存しているそうで、こ
> れなんぞも FX!32 では元イメージがそのまま残っているってふうになる
> のでしょうけれど、状況的に似ているし。
元イメージがないとプログラム領域からリードされた時困っちゃいます :-)
> 今久野さんに教えてもらった資料を少し読みました. レポートはもちっと待っ
> てください. 今仕事が立て込んでいるので遅くなるかもです.
うう. 忙しいときに限って読みたくなる….
# 適当に抜き出しました. データベースの話とかは省いてあります.
FX!32 (以下 FX) は以下の七つのモジュールからなるそうです.
1. The transparency agent, x86 アプリケーションを透過的に使用可能にす
る (要するにダブルクリックで実行出来るようにするってことかな)
2. The runtime, x86 アプリケーションを実行可能な環境を整える. OS の
API との橋渡し.
3. The emulator, ま, これはいいですね.
4. The translator, プロファイラのデータを元に Alpha のコードセットを生
成する. これとプロファイラが FX のキモ.
5. The database, プロファイラの結果を記録しておく. この記録はトランス
レータで使われる.
6. The server, データベースとトランスレータの管理.
7. The manager, FX の緒設定など.
The Transparency Agent
NT の CreateProcess API をフックして, 指定されたイメージが x86 アプリ
ケーションだったら runtime を起動する.
The Runtime
x86 アプリケーションイメージをメモリへロードし, ランタイム環境を整備し,
エミュレータを起動する. x86 と Alpha とで異なる API の呼び出し規約を調
整するため, x86 からの DLL 呼び出しは runtime のルーチンを呼ぶようにす
る (この仕組みは jacket と呼ばれる). データベースからプロファイルデー
タや変換済のコードを取ってくる (変換済のコードは Alpha ネイティブな
DLL なので直接 Alpha NT の APIが使える).
jacket は不正な x86 コードを含み, これはインタープリタへの signal とし
て使われ, x86 スタックから適切な Alpha のレジスタへ値をコピーして API
を呼ぶ. また, FX で必要な戻り値の書き換えなども行う (Alpha の dll を上
書きしないように GetSystemDirectory をローカルにするとか).
The Emulator
アセンブラで書かれているそうです. x86 の状態は一部が Alpha のレジスタ,
一部がスレッド毎のデータ構造にストア (で, 特定の Alpha のレジスタがそ
こを指している). x86 の整数レジスタは全て Alpha のレジスタにマップされ
ている, と (permanently か…ま, 64bit レジスタですもんね).
エミュレータは x86 命令の最初の 2 バイトで分岐 (64k エントリ). ですが,
Alpha の 64 ビットレジスタを活かすために 8 バイトをフェッチし, 次の命
令のフェッチ・分岐のためにパイプライン的な処理になっている. オペランド
フェッチなどの処理は, first-level キャッシュに収まるようサブルーチン/
コルーチンを使って可能な限り小さくしている.
Condition Code Evaluation
たとえば, 演算のオーバーフロー検出はゼロフラグが参照されない限り行われ
ない, など. どうやるかは後述.
Floating-point Instruction Emulation
x86 アーキテクチャは内部的には 80bit 精度だけど, 64bit 精度でやってま
す (でも充分でしょ? という話). FPU レジスタはメモリ上に仮想スタックを
作って実現.
Generation of Profiles
まず以下の情報が記録される.
* call 命令の分岐先
* 間接分岐の (source, target) の組
* メモリの unaligned アクセス情報
これらはハッシュで管理されます (たとえば, call 命令の target がキーと
か). 処理が進むと複数命令から成るコードの塊がそのエントリポイントで引
けるようになってくる. つまり call 命令をデコードした時に変換済のコード
があるかどうかはこのテーブルを見れば良い, と.
The Translator
えー, トランスレータはさらに八つのモジュール (とフェイズ) から成るそう
です.
1. The Regionizer. プロファイラ情報から領域分割を行う. 領域は連続した
命令の組で, 複数のエントリが有り得る. さらに分岐命令からフロー解析を行
う (間接分岐の場合プロファイラ情報を用いる).
2. Build. 再度 x86 命令をパースして内部形式 (IR: internal
representaion) を作る. IR は基本ブロックのグラフで表現される.
3. The Register Mangler. 前段まではレジスタのオーバーラップ (eax, ax,
ah/al) を無視しているので, 辻褄合わせに必要な情報を挿入.
4. The Condition Code Mangler. 条件分岐に至る前に影響されるフラグ情報
を生成する情報を挿入.
5. Improve. Alpha アーキテクチャ向けの命令セットにする (たとえばエミュ
レートされた push/pop を load/store にとか). 最終的にスタックポインタ
の加減は基本ブロックの出入り口でそれぞれ一つとなるようにする. スタック
や FPU スタックの使用を解析してメモリ入出力を減らし, 定数を畳み込む.
6. The Code Selector. ここまではまだ IR は x86 命令を多数含んでいるの
で, 全て Alpha の命令セットに置き換える (一つの x86 命令→複数の Alpha
の命令群).
7. The Scheduler. 命令の並び替え.
8. The Assembler. お疲れ様.
Use of Profile Data
制御フロー情報を使うのは regionizer だけ. 将来のトランスレータはこの情
報を使って path-directed 最適化を行い, キャッシュミスを減らすようなコー
ドの配置を行うようになるだろう.
コードの再変換はプロファイラデータの増加によって行われる (変換されてい
ない部分の実行が増えたことを意味するので).
Alignment Issues
align されていない領域との read/write は専用の命令 (LDQ_U/STQ_U) で行
われる. コードセレクタはどちらの命令を使うか選択するのにプロファイラの
情報を使う (ので, プロファイラの情報が少ない時は変換後のプログラムで例
外が発生することがある).
Shadow Stack
変換後の call 命令を実行する時, x86 側の戻り番地を x86 のスタックに積
んで call するが, return 時には x86 側のスタックは上の通り, Alpha の戻
るべき番地は Alpha のレジスタに入っている. return 命令で x86 側のスタッ
クからテーブルをひいてもいいが, x86 スタック上の値が不変なら pop して
native-return でいい. 問題は Alpha の戻り番地をどこへ待避するかと, 戻
り番地の変更検出.
で, 変換後のコードでは shadow スタックが使われる. call の度にスタック
フレームが生成され, 戻り番地と x86 のスタックポインタが保存され, 呼ば
れた側は native な戻り番地を保存する. 呼ばれた側は return に先立って現
在の x86 のスタックポインタと戻り番地を比較し, 一致すれば native
return を実行し, どちらか一方が異なればエミュレータを起動する.
同様にしてエミュレータは「いつ native コードに戻れるか」を知ることが出
来る. たとえば変換時にわからなかった分岐によってエミュレータに突入した
場合, native な return が実行可能なタイミングで native return を実行す
ることになる.
<a4t9pf$2r5t$1...@news2.rim.or.jp>の記事において
doh...@hf.rim.or.jpさんは書きました。
> 今久野さんに教えてもらった資料を少し読みました. レポートはもちっと待っ
私もざっと斜め読みしてみました。
> In article <a4t4c6$lgi$1...@nwall1.odn.ne.jp>
> tak...@aisoft.co.jp (Narita Takaoki) writes:
>
> > 先の VEST では、「出来るだけ多くの VAX コードを AXP コードにトラ
> > ンスレートします。TIE は、Alpha AXP 命令に変換出来なかった VAX コ
> > ードを解釈します。」とありまして、続いて TIE の解釈するものとして
> > 「VEST が静的に識別出来なかった命令」ってのが挙げられています。
>
> それはスタックに命令を書いて実行するような場合じゃないでしょうか (演算
> によって分岐先が定まる命令もあるので, どのアドレスも分岐先になり得ると
> 考えると TIE 頼りになっちゃう). 分岐先アドレスが異なる上にデコードした
> 結果も異なるので, 別々にデコードした異なるコードの固まりに変換されるん
> じゃないかという気がします.
どうもそうだったみたい。
ただ件の問題に対する FX!32 の解決法って観点からみると、必ずしも別
々に変換されたコードの塊にはしないで、下の方で書かれる routine は
もちろん、どうも region でさえも、別にしないですますような印象が
あって、別々に準備しないですむように region の取り方とその変換コ
ードへの変換のしかたに工夫って事みたいな気がします。
塊に分解して小さな塊をいっぱい作るのはいっぱい作っていますが。
> # 自己改変コードは…NT だと元々そういうのはないのかな?
そういうのは、あまり聞かないけれど、プロセスオブジェクトのアクセ
ス制御リストが許せば出来ないことはなさそうな気がします。
で、関連しそうなところをざっと眺めた感じ、おおざっぱに
1:エミュレート時に呼ばれた命令のアドレス等をプロファイル
2:プロファイルをもとに translator の regionizer が元コードイ
メージの region を切り出す
3:regionizer は 2 で作った region を組み合わせて、プロファイ
ルからの元コードイメージの制御フローに従い routine を組む
4:各 routine を internal representation (IR) に変換
5:すったもんだあって IR をターゲットコードに変換
ってことのようなので、件の問題は 2 のところで切り出した region を
実際に code selector がコードを変換するところで(5 のところ)解消っ
てことのように見えます。
件の状況はあんまり問題にしていないような。間接ジャンプでさえプロ
ファイルで解決って話のようなので。
これは 1~3 の絡みが凄いんですって事みたいな気がする(かなり brute
な気もするけれど ^^;)。IR をターゲットコードにするところに TWIG
code generator を使ったってのも凄いってところ(今だとそうでもない
のかもしれないけれど)?
1 のプロファイルは
% DTJ vol.9 No.1 1997,
% DIGITAL FX!32: Combining Emulatation and Binary Translation
% http://research.compaq.com/wrl/DECarchives/DTJ/DTJP00
\begin{quotation} % p.7
The profile data includes the following information:
* Addresses that are the targets of call insturctions
* (Source address, target address) pairs for indirect control
transfers
* Addresses of instructions that make unaligned references to
memory
The translator uses this information to generate routines, that
is, units of translation that approximate a source code routine.
\end{quotation}
てことで、かなり強力にやっているですね。
# 苦手な英語の斜め読みなんで全然自信無しなうえに、適当に想像補完
# されているんで、誰か誤り訂正と検証して、ってそれじゃ何の意味も
# 無いか。む~、また恥さらしかな?(^^;;;
でもやっぱり自己改変しちゃうと無理だろうから、Win32 だと無いのか
な?
"Narita Takaoki" <tak...@aisoft.co.jp> wrote in message
news:a4tic5$1ds6$1...@nwall1.odn.ne.jp...
> 成田です。
>
チョキチョキ…
>
> > # 自己改変コードは…NT だと元々そういうのはないのかな?
>
> そういうのは、あまり聞かないけれど、プロセスオブジェクトのアクセ
> ス制御リストが許せば出来ないことはなさそうな気がします。
>
一番普通に出てくる自己改変コードはDynamic Link Tableではないでしょうか。ま
た, IA-32用のJavaのJITなども動きませんと言うわけには行かないでしょうから,
JavaのByte CodeをホットスポットなどのJITがx86バイナリに変換して,それをFX!32
がアルファバイナリに変換して実行するというケースだってありそうです。これなど
は自己改変コードの塊になりそうですが。
いずれにしても,元の命令のページに書き込みが起こるので,MMUの保護(通常,
命令ページは書込み禁止にする)に引っかかってトラップするので検出は容易なはず
です。それで,そこからエミューレートを開始すれば良い筈で,性能はともかく,当
然サポートしていると思います。
私はハード屋で専門外ですが,このごろのコンパイラはプログラムフローの解析は
結構頑張っています。ただ,Just In Time Compilerではコンパイル時間が限られる
ので制約があるようですが。でも,JavaのJITは普通に使われているし,古くはApple
が68KからPowerPCに乗り換えたときもあまり問題にはならなかったし,一般的に言っ
て技術は確立していると言えると思います。
但し,何でもできるというわけでは無く,ハードのサポートが欲しいのは,一つは
エンディアンが異なる場合ですが,x86とAlphaは同じなのでこれは問題になりませ
ん。もう一つはMMUの機能の違いで,これが問題になるとメモリをアクセスする命令
ごとにソフトでチェックを走らせる必要が出たりして性能ががた落ちになります。
FX!32ではIA-32特有のセグメントなんかどう処理しているのでしょうね。
Ando_san
doh...@hf.rim.or.jpさん:
> うう. 忙しいときに限って読みたくなる….
いやいや。おつかれ様です。
ざっと拝見した印象ですが、native側とx86コードの解釈実行とがちゃ
んと行き来できるようにするところが言語屋としてはいちばん面白い。
本当はフロー情報を使ってさらに速くできるともっと迫力ですよね。
> コードの再変換はプロファイラデータの増加によって行われる (変換されてい
> ない部分の実行が増えたことを意味するので).
これが分からないです。どのタイミングでnativeへの変換が起きるん
ですか? なぜ再変換が必要?
他人に読んでもらって楽してすいません。 久野
In article <a4tic5$1ds6$1...@nwall1.odn.ne.jp>
tak...@aisoft.co.jp (Narita Takaoki) writes:
> ただ件の問題に対する FX!32 の解決法って観点からみると、必ずしも別
> 々に変換されたコードの塊にはしないで、下の方で書かれる routine は
> もちろん、どうも region でさえも、別にしないですますような印象が
> あって、別々に準備しないですむように region の取り方とその変換コ
> ードへの変換のしかたに工夫って事みたいな気がします。
うーん. 件の文章を読む限りもっと素直な作りだと思うんですよね. あのコー
ドは
[ prefix - instruction ] - { following instructions }
[ instruction ] - { following instructions }
の二通りの解釈がある (つまり機能としては
[ A ] - { C }
[ B ] - { C }
となるわけですよね). これでプロファイルをつくった場合
[ A ] - { C } が先で, [ B ] に分岐
[ B ] - { C } が先で, [ A ] に分岐
の二通りが考えられるわけですが, どちらの場合も単純に
[ A ] - [ B ] - { C }
な IR が作られちゃうんじゃないかと思います (アドレス順序が A < B < C
で間に分岐命令無いので). もちろん対処可能なんだけどそこまでするかなあ.
> やっぱりDECは面白い。なんでCompaqなんかにと言ったらCompaqに悪いけど…
> (と言いながら書いている)
Alpha チームは Intel に行ったんじゃありませんでしたっけ? (うろ覚え)
> > コードの再変換はプロファイラデータの増加によって行われる (変換されてい
> > ない部分の実行が増えたことを意味するので).
>
> これが分からないです。どのタイミングでnativeへの変換が起きるん
> ですか? なぜ再変換が必要?
native への変換は, 該当アプリケーションの終了後, バックグラウンドで密
かに :-) 行われるようです. 動的ではないのでした.
結構重い処理のようで (まあ最適化フェーズですもんね), 毎回やるんじゃな
くてある程度プロファイル情報が貯まってから (それだけエミュレーションが
実行されてから) その部分を変換するってことでしょう.
> 他人に読んでもらって楽してすいません。
いえいえ, 私も勉強になりますんで.
In article <a4tmu5$1...@utogw.gssm.otsuka.tsukuba.ac.jp>
ku...@gssm.otsuka.tsukuba.ac.jp writes:
> なぜ再変換が必要?
二つは考え付きます.
A. 新たな分岐先を検出した場合 (動的に分岐先が定まる場合ですね). 分岐先
アドレスでデータベースを引いたけど無かった→とりあえずデータベースにそ
のことを書く→後はエミュレータに任せる. で, 再変換で更新された制御フロー
から native なコードを再構築してもらう.
B. ロード/ストアするアドレスを演算で求めたら align されていなかった
(若干遅い native コードが必要だった)→ align されていないアドレスでも
load/store 可能な native コードに置き換える.
> Alpha チームは Intel に行ったんじゃありませんでしたっけ? (うろ覚え)
そうなんですか、いやまあそうじゃなくて、DECはDECとして存在して
次々に新しいことをやって欲しかったなあと。
> native への変換は, 該当アプリケーションの終了後, バックグラウ
> ンドで密かに :-) 行われるようです. 動的ではないのでした.
おお! 理解しました。そこはうまいね。 久野
> A. 新たな分岐先を検出した場合 (動的に分岐先が定まる場合ですね). 分岐先
> アドレスでデータベースを引いたけど無かった→とりあえずデータベースにそ
> のことを書く→後はエミュレータに任せる. で, 再変換で更新された制御フロー
> から native なコードを再構築してもらう.
> B. ロード/ストアするアドレスを演算で求めたら align されていなかった
> (若干遅い native コードが必要だった)→ align されていないアドレスでも
> load/store 可能な native コードに置き換える.
なるほど、繰り返し実行してみると出て来る、エミュレータを呼んで
しまう部分をnativeで大丈夫なように作り直すのですね。
やっぱり、Intelのアーキテクチャ屋さんと、DECのシステムまで含め
たアーキテクチャデザインとは毛色がちょっと違うなあと思います。
まあIntelもIA32が売れすぎたんだろうけど 久野
> IA-32用のJavaのJITなども動きませんと言うわけには行かないでしょうから,
> JavaのByte CodeをホットスポットなどのJITがx86バイナリに変換して,そ
> れをFX!32がアルファバイナリに変換して実行するというケースだってあり
> そうです。これなどは自己改変コードの塊になりそうですが。
Java は詳しくないんですが, これは JIT for Alpha (COCOA) を用意すれば済
むので問題にはならないでしょう.
> いずれにしても,元の命令のページに書き込みが起こるので,MMUの保護
> (通常,命令ページは書込み禁止にする)に引っかかってトラップするので
> 検出は容易なはずです。それで,そこからエミューレートを開始すれば良い
> 筈で,性能はともかく,当然サポートしていると思います。
いや, これを実現しようとすると性能ガタ落ちなのでサポートしていないと思
います (500MHz Alpha で 200MHz Pentium-Pro 相当のエミュレーションとい
うのはかなり高速な部類です). 理由は以下の通り.
まず x86 実行イメージにストア可能だとして, Alpha の変換後のコードでは
対処不可能です (対応するコード群がどれか, なんていうことはわからないし,
そもそも存在しないかもしれない. スケジューラを通った後なので).
変換後のコードへはデータベースをエントリポイントでひくことで分岐するわ
けですが, 仮に「書き換えられたアドレス A がそこから始まる region 内に
存在する」ことがわかれば, 変換後のコードではなくエミュレータを起動する
ことで対処できます. が, 件の文書からはそうは読めない.
また仮にそれが可能であったとしても, 実行時にストア先が定まるようなスト
ア命令を含む region は最初から全てエミュレータで実行する必要があります
変換後のコードで同一 region 内にストアされたら手の打ちようがないので.
rewind するのも考えられますが, そこまではしないでしょう.
> Java は詳しくない
外しているかもしれませんが, 以下のような話ならエミュレータが解釈実行す
るだけだと思います (コード領域外への分岐はデータベースに登録しないこと
で対処できるので. スタック上に命令を積んで…と同じですね).
unsigned
foo (void *p, unsigned a)
{
unsigned (*fp)(unsigned) = p;
return (*fp) (a);
}
unsigned
bar (unsigned n)
{
return n+1;
}
int
main ()
{
char a[] = {
0x8b, 0x44, 0x24, 0x04, /* movl 4(%esp),%eax */
0x40, /* incl %eax */
0xc3 /* ret */
};
char *m = malloc (sizeof a);
memcpy (m, a, sizeof a);
printf ("%u, %u, %u\n"
, foo (bar, 0)
, foo (a, 1)
, foo (m, 2));
return 0;
}
$ ./m
1, 2, 3
<a4tp4f$19k$1...@news2.rim.or.jp>の記事において
doh...@hf.rim.or.jpさんは書きました。
> 実は正解は「そんな状態には対応していない」な気が. まずそういうコードは
> 存在しないだろうし.
んー、x86 にはそんなに詳しくないのでなんとも。まぁ、自己改変を排
斥するとしたら、そういうのもありではあるかと。
> In article <a4tic5$1ds6$1...@nwall1.odn.ne.jp>
> tak...@aisoft.co.jp (Narita Takaoki) writes:
>
> > ただ件の問題に対する FX!32 の解決法って観点からみると、必ずしも別
> > 々に変換されたコードの塊にはしないで、下の方で書かれる routine は
> > もちろん、どうも region でさえも、別にしないですますような印象が
> > あって、別々に準備しないですむように region の取り方とその変換コ
> > ードへの変換のしかたに工夫って事みたいな気がします。
ここ、勘違いしてたっぽい。region は一通りだけど、routine は二つ準
備しそうな気がする。
> うーん. 件の文章を読む限りもっと素直な作りだと思うんですよね. あのコー
> ドは
>
> [ prefix - instruction ] - { following instructions }
> [ instruction ] - { following instructions }
>
> の二通りの解釈がある (つまり機能としては
>
> [ A ] - { C }
> [ B ] - { C }
>
> となるわけですよね).
ここまでは了承です。
> これでプロファイルをつくった場合
>
> [ A ] - { C } が先で, [ B ] に分岐
> [ B ] - { C } が先で, [ A ] に分岐
>
> の二通りが考えられるわけですが, どちらの場合も単純に
>
> [ A ] - [ B ] - { C }
>
> な IR が作られちゃうんじゃないかと思います (アドレス順序が A < B < C
> で間に分岐命令無いので). もちろん対処可能なんだけどそこまでするかなあ.
regionizer はここで、
"[ prefix - instruction ]" と "{ following instructions }"
もしくは
"[ prefix - instruction ] { following instructions }"
に region を取るような気がするのですが。この方が、ずっと素直でし
ょ?prefix 部分があるのか?とか、prefix 部分は prefix なのか?は
profile から判定出来るだろうし、prefix と続く instruction を別の
region にする理由もなさそうな気がするのですが。
ここでは、region の取り方は、本質的には上でも下でも問題点は変わら
ないと思うので、ここでは上で考えるとします。
region "[ prefix - instruction ]" に対し、regionizer が次にするの
は、profile を使って制御フローを辿って、routine を組み上げるわけ
ですが、このとき
[ A ]
[ B ]
の二つの routine を組んじゃう。
# ↑ここが勘違いしてたと思ったところ。
で build が上記 routine に対し、制御フローが
[ A ] -+- { C }
|
[ B ] -+
となるような基本ブロック取りして IR を作る。ここで最適化しちゃえ
ば、
[ A'] - [ B'] - { C }
# [ B'] - [ A'] - { C } でも良いけれど。
という形になるかも(ここは蛇足)。
で最終的に code selector で Alpha 命令群を持ってくれば良いのでは?
結果分岐が入っているかもしれないけれど、何とかなりそうな。
こういうのは無し?
まあ、なんか Dohzono さん説とほとんど変わらない気もしないでもない
し、上で Dohzono さんが「対処可能」ってのは、こういう事かもしれな
いけれど。
# x86 の prefix を本質的に勘違いしている可能性がある。(^^;
> > Alpha チームは Intel に行ったんじゃありませんでしたっけ? (うろ覚え)
>
> そうなんですか
検索は不得手なんですが試みました :-)
http://www.atmarkit.co.jp/fpc/kaisetsu/intel_compaq/intel_compaq.html
| 2001/06/27
...
| さらに、重大なのは「技術移転」についての合意である。Compaqは、
| Alphaプロセッサに関する各種技術ならびにAlphaプロセッサ向け コンパイ
| ラ をIntelに譲渡する、としている。これには、Alphaプロセッサ関連のツー
| ルならびにエンジニアリング・リソースも含まれている。つまり今回の契約
| により、CompaqはAlphaプロセッサから撤退し、Intelがそのリソースを受け
| 継ぐことになる。もともとAlphaプロセッサは、Compaqと合併したDECが持っ
| ていたもの。このとき、DECの半導体工場とStrongARMをIntelが買収した経
| 緯がある。現在でも、Alphaプロセッサは、IntelがDECから買収した工場を
| 使って製造している。数年を経て、DECの半導体事業のすべてがIntelに吸収
| されることになったのは、何とも皮肉なものだ。
|
| 数百人のAlphaプロセッサ関連(マイクロプロセッサならびにコンパイラ)
| の技術者は、すべてIntelに移籍することが提案されている。ただ、現在開
| 発中のAlphaプロセッサについては引き続き開発が行われるため、一部の開
| 発者は継続してCompaqで作業を行い、その後にIntelに合流することになる
| ようだ。
Intel のここ最近の急速な高クロック化の陰には Alpha の技術が…と思って
いたんですけど深読みしすぎ?
> いやまあそうじゃなくて、DECはDECとして存在して次々に新しいことをやっ
> て欲しかったなあと。
んー. でも今何か新しいことをやれるだけの体力のあるところって Intel く
らいじゃないでしょうかね (過去何をやってきたかは別として).
「昔は誰が何をしていても文句言われなかったんだけど, リストラ (解雇じゃ
なくて再構築) でそうもいかなくなったね. 今後ベル研からノーベル賞受賞者
が出ることはまずないだろう」ってベル研の誰かも言ってたし.
# Microsoft にはあまり期待する気になれないし….
> > [ A ] - [ B ] - { C }
> >
> > な IR が作られちゃうんじゃないかと思います (アドレス順序が A < B < C
> > で間に分岐命令無いので). もちろん対処可能なんだけどそこまでするかなあ.
まず regionizer は, この region にはエントリが A, B の二通りある, と認
識しますよね. つまり
[1] [2]
↓ ↓
+---+---+
| - | - | ←まだ空
+---+---+
で [1] が A のエントリ, [2] が B のエントリ (他にも region 解析や
region 同士のフロー解析もやりますが, とりあえずここだけに注目します).
で, 次段の build フェーズではその中に x86 実行イメージから命令を実際に
入れていくわけですが, ここで
+---+-------
| A | B C ...
+---+-------
となっちゃうだろうということです (もっとおかしいことになるかもしれない).
> [ A ]
> [ B ]
>
> の二つの routine を組んじゃう。
うんうん.
> で build が上記 routine に対し、制御フローが
>
> [ A ] -+- { C }
> |
> [ B ] -+
>
> となるような基本ブロック取りして IR を作る。
となるためには A から C への分岐命令がないと駄目なんじゃないでしょうか.
C にエントリはないので.
> で最終的に code selector で Alpha 命令群を持ってくれば良いのでは?
> 結果分岐が入っているかもしれないけれど、何とかなりそうな。
code selector は思い切り最適化フェーズなので最早意味の変わるようなこと
に手を出したりはしないと思います.
> # x86 の prefix を本質的に勘違いしている可能性がある。(^^;
別に prefix でなくとも, 複数バイト命令の途中に分岐した場合でも一緒です
(prefix を例に挙げたのは, その次の命令でどちらも同じ意味に戻ることが保
証されるから). でも普通コンパイラはそういうコードを生成しませんよね.
# 彼らの目的は Alpha NT 上で x86 NT のアプリケーションを動作させること
# であって, 完全な x86 エミュレータを作ることではないというか.
doh...@hf.rim.or.jpさん:
> 検索は不得手なんですが試みました :-)
なるほど。言われてみればどっかで見たかも。とにかくあれだけやっ
たのにAlpha撤退っていうのは悲しいですねえ。せっかくさまざまな命
令セットからAlphaにバイナリ変換して実行できるようにしたのに。こ
れからはインテルの命令セットだけ内部のよくわからんアーキテクチャ
(実はAlphaの後継?)に変換して実行する、になるんでしょうかね。
> Intel のここ最近の急速な高クロック化の陰には Alpha の技術が…
> と思っていたんですけど深読みしすぎ?
なるほど、それはあるかも知れません。
> > いやまあそうじゃなくて、DECはDECとして存在して次々に新しいことをやっ
> > て欲しかったなあと。
> んー. でも今何か新しいことをやれるだけの体力のあるところって
> Intel くらいじゃないでしょうかね (過去何をやってきたかは別とし
> て).
いや、Intelが過去だめだったとは言わないですよ。ただ、文化が違
うんだからそれぞれ競って欲しかった。まあもちろん体力云々はそうだ
と思います。しかしARMもIntelになっちゃうし、Intelのアーキテクチャ
買い漁り恐るべし。はっ! そのうちSPARCなんかも…まさかね。
> 「昔は誰が何をしていても文句言われなかったんだけど, リストラ
> (解雇じゃなくて再構築) でそうもいかなくなったね. 今後ベル研か
> らノーベル賞受賞者が出ることはまずないだろう」ってベル研の誰か
> も言ってたし.
なるほど、ありがちですが悲しいですね。
> # Microsoft にはあまり期待する気になれないし….
そりゃーもう…え? MS製のアーキテクチャ? 絶対嫌そう… 久野
Inside Windows NT でちと復習してみた。
<a4tnfb$7pd$1...@news512.nifty.com>の記事において
ando...@nifty.comさんは書きました。
> "Narita Takaoki" <tak...@aisoft.co.jp> wrote in message
> news:a4tic5$1ds6$1...@nwall1.odn.ne.jp...
> > 成田です。
:
> > > # 自己改変コードは…NT だと元々そういうのはないのかな?
> >
> > そういうのは、あまり聞かないけれど、プロセスオブジェクトのアクセ
> > ス制御リストが許せば出来ないことはなさそうな気がします。
6.2.3 の「メモリの保護」とのところで、「どのスレッドも実行可能イ
メージを読み書き出来ない」と書かれているので、建前上は出来ないっ
てことで良いような気がします。
とはいえ、それなら何故バッファーオーバーランでコードを改変出来る
のか謎ではあるのだけれど。
ということで、どうも本当は出来るし、実際にやっているってことにな
るような。(^^;
# それともいろいろ問題出ているオーバーランを使ってクラックされる
# ところってカーネルモードで動いているところなんだろうか?
> 一番普通に出てくる自己改変コードはDynamic Link Tableではないでしょうか。ま
> た, IA-32用のJavaのJITなども動きませんと言うわけには行かないでしょうから,
> JavaのByte CodeをホットスポットなどのJITがx86バイナリに変換して,それをFX!32
> がアルファバイナリに変換して実行するというケースだってありそうです。これなど
> は自己改変コードの塊になりそうですが。
ただ、上記のようなことは大概は御作法的には「API を通して OS にお
願いする」ってのが筋ってことの様な気もするので、通常は自己改変コ
ードへの対処は考えなくても良いってことになるのかも。
IA-32 の JIT に関しては、門外漢なのでようわからんのですが、JIT が
生成した x86 命令群なども実行イメージとしてロードするのは、結局
API を通してやってもらうとかなのではないでしょうか?
In article <a4vo90$2b0q$1...@nwall1.odn.ne.jp>, Narita Takaoki wrote:
>成田です。
>6.2.3 の「メモリの保護」とのところで、「どのスレッドも実行可能イ
>メージを読み書き出来ない」と書かれているので、建前上は出来ないっ
>てことで良いような気がします。
これはほとんどの OS(メインフレーム,Unix, etc)でそうなってますね.
#近代 OS の必要条件に挙げている OS の教科書もあります.
実行速度上も,これを許すと命令の先取りができなくなるので,パイプライ
ンが使い物にならなくなります.
>とはいえ、それなら何故バッファーオーバーランでコードを改変出来る
>のか謎ではあるのだけれど。
これは書換え可能領域(データあるいはスタック)に命令を書き込んでおい
て,スタック上にある戻り番地をそこに飛ぶように書き換える事でやるのが
定石です.
>ということで、どうも本当は出来るし、実際にやっているってことにな
>るような。(^^;
ということで,本当にできません.
># それともいろいろ問題出ているオーバーランを使ってクラックされる
># ところってカーネルモードで動いているところなんだろうか?
そういう手法もあります(古いウィルスにはそういう物もあったよう
な朧な記憶が...)が,スタックを使うシステムの場合,その必要は無い
わけです.
--
Hideki Kato <mailto:ka...@pop12.odn.ne.jp>
-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
Check out our new Unlimited Server. No Download or Time Limits!
-----== Over 80,000 Newsgroups - 19 Different Servers! ==-----
> とにかくあれだけやったのにAlpha撤退っていうのは悲しいですねえ。
消費電力を見て仰け反ったのは後にも先にもあれだけです (今はもっと凄いの
かもしれないけど '92? で 3.3V 9A ってのは…).
> せっかくさまざまな命令セットからAlphaにバイナリ変換して実行できるよ
> うにしたのに。
でもまあそれで「一応実用に耐え得るだけのパフォーマンスは出せる」ってい
うのは示したんだし, FX!32 もこれだけ手法を明らかにしてるんだから…. あ,
例の paper って '96 だか '97 だかですよね. Intel から DEC へ出資 (とい
うか工場買収とか色々) あった頃で, 埋もれさせるのが忍びなくて出したとか….
> これからはインテルの命令セットだけ内部のよくわからんアーキテクチャ
> (実はAlphaの後継?)に変換して実行する、になるんでしょうかね。
いつか FPU の構成について書きましたけど, IA-32 ってのは Intel 自身も頭
痛いと思うんですよね. いっそのこと native な環境を揃えて FX!32 みたい
なので移行を促せばいいのに.
# それをハードウェアでやっちゃう辺りが別の意味で凄いんだけど. 力業.
まあ FX!32 は「別アーキテクチャなのに (にしては) こんなに速い」という
ことが言えるけど, Intel が出すと文句言われたりするのかな. 新しいステッ
プに対してそういう方法で移行していくというのが常識みたいになるといいで
しょうね. 新規参入の道も拓けてくるし.
> 文化が違うんだからそれぞれ競って欲しかった。
あーこれは私もそう思います.
# 実は GNU に対する competitor が居ないといけないんじゃないかと思う今
# 日この頃.
> しかしARMもIntelになっちゃうし、Intelのアーキテクチャ買い漁り恐るべ
> し。
でも DEC のアレは最初は援助だったんじゃないかと好意的に考えてるんです
けどね.
> > 「昔は誰が何をしていても文句言われなかったんだけど, リストラ
> > (解雇じゃなくて再構築) でそうもいかなくなったね. 今後ベル研か
> > らノーベル賞受賞者が出ることはまずないだろう」ってベル研の誰か
> > も言ってたし.
>
> なるほど、ありがちですが悲しいですね。
ですね. 日本はこういう人の意見を聞いて「金は出すけど口は出さん」でいく…
のか? 行かないだろうなあ. NASA ですら資金削られてるし.
# 人と金と時間. うーん. 誰か数人一生食えるくらいの資金提供してくれたら
# 一つくらい面白いもの作れそうなんだけど. はっ, AUP 違反? (^^;
> > # Microsoft にはあまり期待する気になれないし….
>
> そりゃーもう…え? MS製のアーキテクチャ? 絶対嫌そう…
コンパイラチームには結構優秀な人が居るんじゃないかと思うんですけどねー.
そういう方面からのアプローチがいいと思うんだけど.
# あれもどこかの下請なのかな?
> >6.2.3 の「メモリの保護」とのところで、「どのスレッドも実行可能イ
> >メージを読み書き出来ない」と書かれているので、建前上は出来ないっ
> >てことで良いような気がします。
>
> これはほとんどの OS(メインフレーム,Unix, etc)でそうなってますね.
> #近代 OS の必要条件に挙げている OS の教科書もあります.
NeXTSTEP (68040) でサポートされる実行形式には書き換えを許すものがあっ
たそうです. で,
> 実行速度上も,これを許すと命令の先取りができなくなるので,パイプライ
> ンが使い物にならなくなります.
先輩が色々試した範囲では既に prefetch された命令を書き換えると書き換え
前の命令が実行されていたとか.
# だから完全なエミュレータを作る場合はちゃんとプリフェッチして, 条件分
# 岐をフェッチした後の挙動まで合わせないといけない.
> > IA-32用のJavaのJITなども動きませんと言うわけには行かないでしょうから,
> > JavaのByte CodeをホットスポットなどのJITがx86バイナリに変換して,そ
> > れをFX!32がアルファバイナリに変換して実行するというケースだってあり
> > そうです。これなどは自己改変コードの塊になりそうですが。
>
> Java は詳しくないんですが, これは JIT for Alpha (COCOA) を用意すれば済
> むので問題にはならないでしょう.
FX!32から話はそれますが、例えばTransmeta CrusoeのCode Morphingだと、実
際にこれを(効率良く)処理しなければなりませんよね。JITの中には自己改変
コードを出すものがある(たとえば、最初のコンパイル時は手を抜いておいて、
高い頻度で実行されると最適化コードに置き換える)と聞きますし、それを
Crusoeが実行できないと困るでしょうから。
なので、ひょっとしたら手はあるのかも。
前田敦司
> 6.2.3 の「メモリの保護」とのところで、「どのスレッドも実行可能イ
> メージを読み書き出来ない」と書かれているので、建前上は出来ないっ
> てことで良いような気がします。
>
> とはいえ、それなら何故バッファーオーバーランでコードを改変出来る
> のか謎ではあるのだけれど。
バッファオーバーランでは
「実行可能イメージを読み書き」して「コードを改変」
しているのではなく、書ける場所(スタックとか)に書いたコードを実行させて
いるからでは。
(なお、スタックを実行不可にしても、バッファオーバーランは防げません。)
> IA-32 の JIT に関しては、門外漢なのでようわからんのですが、JIT が
> 生成した x86 命令群なども実行イメージとしてロードするのは、結局
> API を通してやってもらうとかなのではないでしょうか?
たいていのJITは、きちんとOSが認識する実行可能ファイルを出してそれをロー
ドするのではなくて、やはりデータ領域に機械語コードを書き込んでそれを実
行するのだと思います。
前田敦司
> 買い漁り恐るべし。はっ! そのうちSPARCなんかも…まさかね。
StrongARMの買収に関しては、主だったアーキテクトに逃げられて、万々歳と
は行かなかったようです。(そのアーキテクトらは最近AMDへ...)
http://www.watch.impress.co.jp/pc/docs/2002/0208/kaigai01.htm
しかし、MIPSってハイエンドでは聞かなくなったですけど、しぶといですね。
と思ったらハイエンドもまだあきらめてないのかー。
http://www.zdnet.co.jp/news/bursts/0202/19/11.html
前田敦司
> > >6.2.3 の「メモリの保護」とのところで、「どのスレッドも実行可能イ
> > >メージを読み書き出来ない」と書かれているので、建前上は出来ないっ
> > >てことで良いような気がします。
> >
> > これはほとんどの OS(メインフレーム,Unix, etc)でそうなってますね.
> > #近代 OS の必要条件に挙げている OS の教科書もあります.
a.outの頃のLinuxだと -N オプションをつけてコンパイルすると、textに書け
たんじゃなかったでしたっけ。(OMAGIC実行形式になる。)
かつて、2ページより小さいバイナリを作る唯一の手段として使われていたよ
うな。(データとテキストを同じページに置ける。)
> NeXTSTEP (68040) でサポートされる実行形式には書き換えを許すものがあっ
> たそうです. で,
>
> > 実行速度上も,これを許すと命令の先取りができなくなるので,パイプライ
> > ンが使い物にならなくなります.
>
> 先輩が色々試した範囲では既に prefetch された命令を書き換えると書き換え
> 前の命令が実行されていたとか.
ハーバードアーキテクチャだと、キャッシュが命令/データで別だったりしま
すしね。 またSMPだと、他のプロセッサのキャッシュに入ってたりとか...
JITだと、このあたりの配慮は必須だと思います。
前田敦司
> 先輩が色々試した範囲では既に prefetch された命令を書き換えると書き換え
> 前の命令が実行されていたとか.
じゃ Pentium だとどうだ? というんで
unsigned
foo (void *p, unsigned a)
{
unsigned (*fp)(unsigned) = p;
return (*fp) (a);
}
int
main ()
{
unsigned char a[] = {
0x8b, 0x44, 0x24, 0x04, /* mov 4(%esp), %eax */
0xe8, 0x00, 0x00, 0x00, 0x00, /* call .+5 */
0x5a, /* pop %edx */
0x83, 0xc2, 0x07, /* add $0x07, %edx */
0xc6, 0x02, 0x48, /* movb $0x48, (%edx) - "dec %eax" */
0x40, /* inc %eax (or dec %eax) */
0xc3 /* ret */
};
unsigned char *m = malloc (sizeof a);
memcpy (m, a, sizeof a);
printf ("%u, %u\n"
, foo (a, 1)
, foo (m, 2));
printf ("%u, %u\n"
, foo (a, 1)
, foo (m, 2));
return 0;
}
これを実行してみると
無印 Pentium:
$ ./m
0, 1
0, 1
PentiumII:
$ ./m
2, 3
0, 1
という結果に.
The CPUID instruction can be executed at any privilege level to
serialize instruction execution. Serializing instruction execution
guarantees that any modifications to flags, registers, and memory
for previous instructions are completed before the next instruction
is fetched and executed.
なので後者は cpuid を入れるとちゃんとなるかな, と思ったのですが nop だ
けで変更後の動作をしたので諦めました (cpuid は 2 バイト命令).
# なんか一気にロード値が跳ね上がったので、これで御仕舞いかも。
<a4vmkm$10o2$1...@news2.rim.or.jp>の記事において
doh...@hf.rim.or.jpさんは書きました。
> In article <a4vjj4$2el7$1...@nwall2.odn.ne.jp>
> tak...@aisoft.co.jp (Narita Takaoki) writes:
>
> > > [ A ] - [ B ] - { C }
> > >
> > > な IR が作られちゃうんじゃないかと思います (アドレス順序が A < B < C
> > > で間に分岐命令無いので). もちろん対処可能なんだけどそこまでするかなあ.
>
> まず regionizer は, この region にはエントリが A, B の二通りある, と認
> 識しますよね. つまり
>
> [1] [2]
> ↓ ↓
> +---+---+
> | - | - | ←まだ空
> +---+---+
>
> で [1] が A のエントリ, [2] が B のエントリ (他にも region 解析や
> region 同士のフロー解析もやりますが, とりあえずここだけに注目します).
> で, 次段の build フェーズではその中に x86 実行イメージから命令を実際に
> 入れていくわけですが, ここで
いえ、その前に routine を "divide the source image code *into*
routines" なので、単純に routine に分割ってんじゃなくて、ニュアン
ス的には routine に image code を「分配」するのではないかと。
で、"The regionizer builds routines by following the control flow
of the source image." ということで、この分配にフローを使うので、
>
> +---+-------
> | A | B C ...
> +---+-------
>
> となっちゃうだろうということです (もっとおかしいことになるかもしれない).
こうはならないのではないかとみたのですが。
# "Each call target in the profile is used to generate *an*
# entry to rountine." というのもあるです。
> > で build が上記 routine に対し、制御フローが
> >
> > [ A ] -+- { C }
> > |
> > [ B ] -+
> >
> > となるような基本ブロック取りして IR を作る。
>
> となるためには A から C への分岐命令がないと駄目なんじゃないでしょうか.
> C にエントリはないので.
まぁ、確かにここまでのフローを追跡をするとなると、x86 命令の
semantices を見ていかないといけなくなるので大変でしょうけれど、
emulator があるくらいだから、やらないのももったいないような気もし
ます。
# けどやっていない気もすごくするのはするです。(^^;;
# 確かに面倒だもの。
> > で最終的に code selector で Alpha 命令群を持ってくれば良いのでは?
> > 結果分岐が入っているかもしれないけれど、何とかなりそうな。
>
> code selector は思い切り最適化フェーズなので最早意味の変わるようなこと
> に手を出したりはしないと思います.
Build の IR の段階で既に変質しているので、正確には code selector
で何か意味を変えるということではないということで。
Debug API を捨てている点をみると、自己改変コードに関しても捨てて
いる可能性は高いとは思います。
# 願望が見せている幻想って可能性は高いな。
ちなみに、今まで一緒に仕事なかでは、旧日本 DEC はピカ一にプロっぽ
くて、ある意味クライアントだったのに、こちらが仕事をしやすいよう
に最大限便宜を図ってくれるし、役割分担的に DEC 側の仕事はしっかり
十分やってくれたし、大変良い相手でした。
おそらく企業文化がそうなんだろうけれど、そうは滅多に無いのでは。
In article <a4vrv6$13dm$1...@news2.rim.or.jp>, Kazuo Fox Dohzono wrote:
>In article <3c7353dd$1...@binarykiller.newsgroups.com>
> Hideki Kato <ka...@pop12.odn.ne.jp> writes:
>
>> >6.2.3 の「メモリの保護」とのところで、「どのスレッドも実行可能イ
>> >メージを読み書き出来ない」と書かれているので、建前上は出来ないっ
>> >てことで良いような気がします。
>>
>> これはほとんどの OS(メインフレーム,Unix, etc)でそうなってますね.
>> #近代 OS の必要条件に挙げている OS の教科書もあります.
>
>NeXTSTEP (68040) でサポートされる実行形式には書き換えを許すものがあっ
>たそうです. で,
PC OS は...真っ当な OS には入れられませんねぇ...Mac OS でもで
きる(た?)んじゃなかったかな.
>> 実行速度上も,これを許すと命令の先取りができなくなるので,パイプライ
>> ンが使い物にならなくなります.
>
>先輩が色々試した範囲では既に prefetch された命令を書き換えると書き換え
>前の命令が実行されていたとか.
至極当然ですね.まさか prefetch 済みのアドレスの内容が書き換わったか
どうかを常時チェックするわけにもいかないし.
#今ならやろうと思えばできるかな?
ハードには(大抵?)命令キャッシュの無効化命令がありますから,アプリ
側からやろう思えば(& OS が許していれば)できるんですけどね.
#あれ,prefetch した命令まで取り消せたかな?
># だから完全なエミュレータを作る場合はちゃんとプリフェッチして, 条件分
># 岐をフェッチした後の挙動まで合わせないといけない.
.......これはネタですよねぇ...???
<m3d6z0e...@maedapc.cc.tsukuba.ac.jp>の記事において
ma...@cc.tsukuba.ac.jpさんは書きました。
> tak...@aisoft.co.jp (Narita Takaoki) writes:
> > とはいえ、それなら何故バッファーオーバーランでコードを改変出来る
> > のか謎ではあるのだけれど。
>
> バッファオーバーランでは
> 「実行可能イメージを読み書き」して「コードを改変」
> しているのではなく、書ける場所(スタックとか)に書いたコードを実行させて
> いるからでは。
あ、そうか、そうですね。
# 昔学んだのに……鶏頭のバカ……(^^;;;
> > IA-32 の JIT に関しては、門外漢なのでようわからんのですが、JIT が
> > 生成した x86 命令群なども実行イメージとしてロードするのは、結局
> > API を通してやってもらうとかなのではないでしょうか?
>
> たいていのJITは、きちんとOSが認識する実行可能ファイルを出してそれをロー
> ドするのではなくて、やはりデータ領域に機械語コードを書き込んでそれを実
> 行するのだと思います。
そうなんですか。
NT の最初の設計に従うと、メモリにくっつく ACL で「実行可能」とい
う、読み書き不可で実行しか出来ない属性があるようなので、これがち
ゃんと機能していたら、出来なかった手ですね。
もし、この属性が機能していたら、やっぱりメモリマップトファイルな
りなんなりに実行イメージを作ってから、そいつを実行属性付きのメモ
リオブジェクトにロードしてって手順になるんだろうなぁ。
実際は実質読み込み属性のみになっているようなのですが。おかげで、
面倒が減って幸せ?(^^;
そんなことはありません。通常は先取りをやっていて書き換えられたかどうかを検
出して,その場合だけインバリデートすれば良いのです。自己改変コードを許さない
仕様のプロセサならOKですが,私の知る限り大部分のプロセサではそうはなっていま
せん。IBMのメインフレームは明らかに自己改変コードを許しています。
>
> 先輩が色々試した範囲では既に prefetch された命令を書き換えると書き換え
> 前の命令が実行されていたとか.
自己改変するストアがいつメモリに反映されるかはメモリモデルによって違いま
す。IBMメインフレームとかIA-32のようなStrong Orderのプロセサでこれが起こった
とすると明らかにバグです。アルファのようなRelaxed Orderのプロセサでは明示的
にメモリバリア命令を出さないとストアが反映される保障はありません。従って,ス
トアの後にメモリバリアを出してないと書き換え前の命令が実行されてもアーキテク
チャ上OKです。但し,プリフェッチとの関係は微妙で,アーキ仕様を見ないとなんと
もいえません。しかし,書き換えを確実に反映する方法が無いとするとアーキテク
チャ上問題と思います。でないと,書き換えから10命令待てば反映されるのか,はた
また10秒待たないと反映されないのか分かりませんから。
Ando_san
<a505th$egm$1...@nwall2.odn.ne.jp>の記事において
tak...@aisoft.co.jpさんは書きました。
> NT の最初の設計に従うと、メモリにくっつく ACL で「実行可能」とい
> う、読み書き不可で実行しか出来ない属性があるようなので、これがち
> ゃんと機能していたら、出来なかった手ですね。
読み返したらどうしても変なんで。
上の属性は本来意図的にはおそらく実行させたいイメージをロードした
メモリには付いていないと実行を許さないのじゃないかといういうもの
です。
で、読み書きは出来ないので、実行したいイメージがあったら OS に頼
んでイメージはロードしてもらわざる得なくなるということで。
だいたいのプロセサはこれに対応していると思います。やり方として簡単なのは,
同じメモリのデータを命令とデータのキャッシュに同時には入れない(一方にあるも
のを他方がキャッシュしようとすると,前からあるほうをインバリデートする)とい
う方法です。もう少し頑張ると,データキャッシュにストアが発生すると同時に命令
キャッシュのインデックスで一致するものをインバリデートするという方法をとりま
す。
もちろん,あまり頻繁には起こらないということを前提とした最適化です。
> JITだと、このあたりの配慮は必須だと思います。
これをソフトにやれというと,当然ハードの仕事でしょと,総スカンを食います。
>
> 前田敦司
フラッシュ命令を出さなくても,書き換えた命令を実行するまでに時間があれば書
き換え後の命令が実行されるのですが,正確に何時からというのは実行状態に影響さ
れたり,プロセサの実装が変わると違ってきたりしますから,信頼できないコードに
なってしまいます。
Ando_san
"ando_san" <ando...@nifty.com> wrote in message
news:a506ju$cjh$1...@news512.nifty.com...
In article <a506ju$cjh$1...@news512.nifty.com>, ando_san wrote:
>
>"Kazuo Fox Dohzono" <doh...@hf.rim.or.jp> wrote in message
>news:a4vrv6$13dm$1...@news2.rim.or.jp...
>> In article <3c7353dd$1...@binarykiller.newsgroups.com>
>> Hideki Kato <ka...@pop12.odn.ne.jp> writes:
>>
>> > >6.2.3 の「メモリの保護」とのところで、「どのスレッドも実行可能イ
>> > >メージを読み書き出来ない」と書かれているので、建前上は出来ないっ
>> > >てことで良いような気がします。
>> >
>> > これはほとんどの OS(メインフレーム,Unix, etc)でそうなってますね.
>> > #近代 OS の必要条件に挙げている OS の教科書もあります.
>>
>> NeXTSTEP (68040) でサポートされる実行形式には書き換えを許すものがあっ
>> たそうです. で,
>>
>> > 実行速度上も,これを許すと命令の先取りができなくなるので,パイプライ
>> > ンが使い物にならなくなります.
>
> そんなことはありません。通常は先取りをやっていて書き換えられたかどうかを検
>出して,その場合だけインバリデートすれば良いのです。
ちょっと書き方が足りなかったかな? この文章の「これを許すと」っての
は「書き換えた直後からそれが有効になる」という意味を含んでいます.
#すればよい,ってのは当然ですが,現実に,おっしゃるような処理をして
#いるプロセッサをご存知ですか? 私が以前調べた時は見つけられません
#でしたが.
>自己改変コードを許さない
>仕様のプロセサならOKですが,私の知る限り大部分のプロセサではそうはなっていま
>せん。IBMのメインフレームは明らかに自己改変コードを許しています。
許してても,書き換えた瞬間に反映される事を保証しているプロセッサは
(今は)無いんじゃないでしょうか.許すのと,それがいつ反映されるかは
別物だと思います.
>>
>> 先輩が色々試した範囲では既に prefetch された命令を書き換えると書き換え
>> 前の命令が実行されていたとか.
>
> 自己改変するストアがいつメモリに反映されるかはメモリモデルによって違いま
>す。IBMメインフレームとかIA-32のようなStrong Orderのプロセサでこれが起こった
>とすると明らかにバグです。
上に書いたように,そうは思いません.
>アルファのようなRelaxed Orderのプロセサでは明示的
>にメモリバリア命令を出さないとストアが反映される保障はありません。従って,ス
>トアの後にメモリバリアを出してないと書き換え前の命令が実行されてもアーキテク
>チャ上OKです。但し,プリフェッチとの関係は微妙で,アーキ仕様を見ないとなんと
>もいえません。
同一アーキテクチャでもモデルによって違ったりします.
>しかし,書き換えを確実に反映する方法が無いとするとアーキテク
>チャ上問題と思います。でないと,書き換えから10命令待てば反映されるのか,はた
>また10秒待たないと反映されないのか分かりませんから。
キャッシュに入っていたら時間は無関係ですよね.しかも今は実行順序も変
えてるし.さらには投機実行もやろうとしてるし.
スタック領域に実行可能属性がついているのはUnixの歴史的問題点ですが,NTでも実
行可能になっているのですか??? Solarisではスタックを実行可能にしない安全
モードがありますが…
>
> 読み返したらどうしても変なんで。
>
> 上の属性は本来意図的にはおそらく実行させたいイメージをロードした
> メモリには付いていないと実行を許さないのじゃないかといういうもの
> です。
通常は,同じ物理メモリに読み書きの出来る論理空間アドレスを対応させたページ
を作ってローダはこれを使って,実行する方は実行だけで読み書きの出来ない論理空
間のページで実行すると思いますが。
アーキテクチャ・マニュアルの中では self-modifying code はお勧めしない、って
ずっと書いてあるんですが、自分とこでも使ってるし。
> いえ、その前に routine を "divide the source image code *into*
> routines" なので、単純に routine に分割ってんじゃなくて、ニュアン
> ス的には routine に image code を「分配」するのではないかと。
勘違いしていました. x86 命令を region 内に配置していくのは regionizer
です (でないと条件分岐が判定できない).
> で、"The regionizer builds routines by following the control flow
> of the source image." ということで、この分配にフローを使うので、
うーん. 私は先の部分は region として捉えています. routine というのはプ
ロファイル情報で言うところの call から始まる (おそらく ret で閉じる)
一連のコード群で, トランスレータ全体はプロファイラに call エントリのあ
る各 routine 部分を変換していくわけですよね (無い部分は変換されない).
The regionizer uses the profile to determine which parts of the
source image are translated.
この routine を region に分割するのが regionizer だと思っています (The
regionizer represents routines as a collection of regions).
普通 basic block というとラベル[*] と {ラベルまたは分岐命令} との間を
指すわけですが, Unlike basic blocks, regions can have multiple entry
points から
+-----+ ←L1
+-----+ ←L2
+-----+ ←L3
| ... |
+-----+
のような塊が region だと (ラベルは region 末尾からループ命令で参照され
たり, 他の region から参照されたりするが, 静的にアドレスが求まるので
hash table にエントリはない). region 間 (というか basic block 間) の相
互関係は次段の build フェーズにてグラフで表現されます. これが IR.
# [*]: 私は先の記事でこれを region のエントリとしていましたが, hash
# table のエントリと誤解されるかもしれないので言葉を分けます.
This representation was chosen to efficiently describe the division
of the source image into units of translation.
とありますが, 私も昔似たような構造を使ってコンパイラのコードジェネレー
タを移植したことがあります (おお, stack machine → register machine と
いうのも一緒だ).
で,
> # "Each call target in the profile is used to generate *an*
> # entry to rountine." というのもあるです。
これは各 routine についてはそうだということです.
The regionizer builds routines by following the control flow of the
source image というのは, 一つは上のような call による routine の作成,
もう一つは続く間接分岐の説明にある通りだと思います (プロファイラに記録
されている二つ目の情報).
> まぁ、確かにここまでのフローを追跡をするとなると、x86 命令の
> semantices を見ていかないといけなくなるので大変でしょうけれど、
で, それを諦めるとプログラムもかなり簡単になると思います.
<a50dmr$4ja$1...@news511.nifty.com>の記事において
ando...@nifty.comさんは書きました。
>
> "Narita Takaoki" <tak...@aisoft.co.jp> wrote in message
> > tak...@aisoft.co.jpさんは書きました。
> >
> > > NT の最初の設計に従うと、メモリにくっつく ACL で「実行可能」とい
> > > う、読み書き不可で実行しか出来ない属性があるようなので、これがち
> > > ゃんと機能していたら、出来なかった手ですね。
>
> スタック領域に実行可能属性がついているのはUnixの歴史的問題点ですが,NTでも実
> 行可能になっているのですか??? Solarisではスタックを実行可能にしない安全
> モードがありますが…
ですから、「NT にも実行専用保護というのがある」すなわち本来意図と
しては「実行専用保護属性の付いていない仮想ページでの実行は許さな
い」としたかったんだろうけれど、ここの項目のところで「ハードウェ
アがこれに対応している場合」というただし書きつきであり、MIPS
R4000 も Intel 386/486 も実行専用保護をサポートしていない旨の但し
書きもあるので、実質はこの保護は働いていないので、「どこでも実行
可」ということで。
で、こういうハードの場合は、NT においては実行アクセスは読みだし専
用アクセスと同じになるそうです。
ただし、私の読んでいる Inside... は 1992/9 のものなので、むちゃ古
いので、なんか変わっている可能性はあるけれど、ハードは変っとらん
だろうから本質的には改善されてはいないでしょうねぇ。
> ちょっと書き方が足りなかったかな? この文章の「これを許すと」っての
> は「書き換えた直後からそれが有効になる」という意味を含んでいます.
>
> #すればよい,ってのは当然ですが,現実に,おっしゃるような処理をして
> #いるプロセッサをご存知ですか? 私が以前調べた時は見つけられません
> #でしたが.
>
IBMメインフレームとその互換機では直後の命令を書き換えるコードがあって,こ
れを正しく実行しないと困ります。昔の資産が動かないと高いメインフレームを買っ
てくれるお客様はいなくなりますから,これは重要です。
> キャッシュに入っていたら時間は無関係ですよね.しかも今は実行順序も変
> えてるし.さらには投機実行もやろうとしてるし.
実行順序を変更するとか投機実行をやるとかはハードの都合で,これでソフトに迷
惑はかけるわけには行かないというのがハード屋の制約です。勝手に変えてよけれ
ば,ハードを節約できるとか性能を向上できる手はあるのですが,ソフト無ければた
だの箱ですから。
最近のプロセサ(アルファとかPowerPC,Itanium)はRelaxed Memory Order(単一
CPUで見るとLoad/Storeを命令順に実行しているように見えるが,メモリ側で見ると
順序はCPUの都合の良いように勝手に変更する)を採用していますが,これで結構,
Strong Order(メモリ側や他のプロセサでみてもプログラムと同じ順でRead/Writeが
起こる)を前提にマルチプロセサの同期を取っている古いプログラムがこけました。
また,ドライバがアダプタにコマンドを出して,そのレスポンスを読む動作が,
I/Oアダプタではレスポンスのリードがコマンドのライトより前に来てこけたりしま
した。もちろん,マニュアルにはコマンドのライトの後にバリア命令を入れて,レス
ポンスのリードの前にライトが完了するようにコーディングしろとは書いてあるので
すが,ドライバを書くプログラマ全員の意識が瞬時に切り替わるわけでも無いし,前
に書いたドライバをちょっと手直しして使うことも多いでしょうから,なかなか上手
く行きません。
ということで,ハードも結構気を使って作っているのです。
Ando_san
In article <a52ufc$98g$1...@news511.nifty.com>, ando_san wrote:
>
>"Hideki Kato" <ka...@pop12.odn.ne.jp> wrote in message
>news:3c7399c6$1...@binarykiller.newsgroups.com...
>> 加藤@ODNです.
>>
>
>> ちょっと書き方が足りなかったかな? この文章の「これを許すと」っての
>> は「書き換えた直後からそれが有効になる」という意味を含んでいます.
>>
>> #すればよい,ってのは当然ですが,現実に,おっしゃるような処理をして
>> #いるプロセッサをご存知ですか? 私が以前調べた時は見つけられません
>> #でしたが.
>>
>
> IBMメインフレームとその互換機では直後の命令を書き換えるコードがあって,こ
>れを正しく実行しないと困ります。昔の資産が動かないと高いメインフレームを買っ
>てくれるお客様はいなくなりますから,これは重要です。
EXECUTE 命令の事ですか? これは書き換えているわけではなく,単によそ
にある命令に修飾を掛けて実行するだけですよね.
#アーキ屋があれのせいでスピードが出ないってぼやいてました.
>> キャッシュに入っていたら時間は無関係ですよね.しかも今は実行順序も変
>> えてるし.さらには投機実行もやろうとしてるし.
>
> 実行順序を変更するとか投機実行をやるとかはハードの都合で,これでソフトに迷
>惑はかけるわけには行かないというのがハード屋の制約です。勝手に変えてよけれ
>ば,ハードを節約できるとか性能を向上できる手はあるのですが,ソフト無ければた
>だの箱ですから。
その妥協点が,完全なチェックはやらないから,自分で面倒見てねって事だ
と言うのが,このスレッドの主題だったと思うのですが...
#キャッシュの無効化命令が無いプロセッサ(Gμの一つだったと思う)も
#あるんですよ~~.Lisp 屋としては強く反対したんだけど...
> 最近のプロセサ(アルファとかPowerPC,Itanium)はRelaxed Memory Order(単一
>CPUで見るとLoad/Storeを命令順に実行しているように見えるが,メモリ側で見ると
>順序はCPUの都合の良いように勝手に変更する)を採用していますが,これで結構,
>Strong Order(メモリ側や他のプロセサでみてもプログラムと同じ順でRead/Writeが
>起こる)を前提にマルチプロセサの同期を取っている古いプログラムがこけました。
>
> また,ドライバがアダプタにコマンドを出して,そのレスポンスを読む動作が,
>I/Oアダプタではレスポンスのリードがコマンドのライトより前に来てこけたりしま
>した。もちろん,マニュアルにはコマンドのライトの後にバリア命令を入れて,レス
>ポンスのリードの前にライトが完了するようにコーディングしろとは書いてあるので
>すが,ドライバを書くプログラマ全員の意識が瞬時に切り替わるわけでも無いし,前
>に書いたドライバをちょっと手直しして使うことも多いでしょうから,なかなか上手
>く行きません。
と言う事で,実際にどの辺りが妥協点なのか,その理由は,さらにはもっと
いい方法はあるのか/ないのか,という辺りに興味があるのですが...
> ということで,ハードも結構気を使って作っているのです。
う~ん,ここの議論に参加してる人の多くはそんな事は百も承知で議論して
いると思いますです.#少なくとも私はそうです.
この時代のMMUはそうですね。ソフトの辛いところは有る程度古いハードもサポー
トしなければならない点ですね。しかし,それはハードも同じで,有る程度古いソフ
トも動かせないと商品になりません。ということで,結構イナーシャを感じます。
>
> ただし、私の読んでいる Inside... は 1992/9 のものなので、むちゃ古
> いので、なんか変わっている可能性はあるけれど、ハードは変っとらん
> だろうから本質的には改善されてはいないでしょうねぇ。
古いハードは古いソフトと同じようにそのままですが,新しいハードはそれなりに
変わっています。MMUで言えば実行専用のページ属性は普通ですし,I/Oアダプタなど
のために常にStrong Orderで動作するページ属性などが設けられています。ご存知の
ように,ゲームの3D GraphicsやDSP的な用途をサポートするSSE,SSE2のような機能
も一般化しています。
また,Little EndianのDEC,IntelアーキとIBM,SPARC系とInternetプロトコルの
Big Endianをサポートするのが必須の世の中になっているのでBi-Endianサポートが
普通になっています。
昔に比べるとパフォーマンスモニタなども充実してきて,これを使ってソフトの
チューニングもやりやすくなっていると思います。
また,ソフト屋さんには直接関係ないですが,ハードがどんどん複雑化し,同時に
クロックも速くなって外付けのロジックアナライザではデバグ出来なくなっており,
CPUチップ内にロジックアナライザ機能を組み込むようになってきています。
この次の変化は,Power4などのようなマルチコアサポートやIntelの次期Pentium 4
のようなマルチスレッド対応です。また,セキュリティーの観点から暗号化などの需
要が増えてLong Multiply(64bitx64bitの上位64bitを計算する)などのサポートが
検討されています。ということでプロセサを開発する会社は減ってきているのです
が,ハード屋の仕事も無くなりません。
Ando_san
In article <3c74e7f9$1...@binarykiller.newsgroups.com>, Hideki Kato wrote:
>加藤@ODNです.
>> IBMメインフレームとその互換機では直後の命令を書き換えるコードがあって,こ
>>れを正しく実行しないと困ります。昔の資産が動かないと高いメインフレームを買っ
>>てくれるお客様はいなくなりますから,これは重要です。
>
>EXECUTE 命令の事ですか? これは書き換えているわけではなく,単によそ
>にある命令に修飾を掛けて実行するだけですよね.
>#アーキ屋があれのせいでスピードが出ないってぼやいてました.
これは意味を取り違えました (_ _).IBM の OS/360 の話ですね? んで,
その後 EXECUTE 命令が追加されたんでしたか,確か.これは今でも(MVS
以後でも)そのままですか? また,互換機メーカーの方の OS(OSIV/F4,
VOS3)でも同じでしょうか?
また,以前,これに関連した(隠し)スイッチが有ると聞いたような記憶が
あるのですが,どなたかご存知ありませんか?
<a52c00$2c69$1...@news2.rim.or.jp>の記事において
doh...@hf.rim.or.jpさんは書きました。
> In article <a500i1$2re$1...@nwall2.odn.ne.jp>
> tak...@aisoft.co.jp (Narita Takaoki) writes:
:
> > # "Each call target in the profile is used to generate *an*
> > # entry to a rountine." というのもあるです。
>
> これは各 routine についてはそうだということです.
どうもわたしのこの引用英文の読み間違いって事ですね。routine は複
数エントリーポイントを持ち得るわけですね。そうすると、region の集
合体で過不足なく routine は確かに被覆出来ますね。
もっとも、一つのエントリーポイントにつき一つの routine を作るよう
にすれば、prefix が付いていようが簡単に対応出来るようになるはずで
すが。
この場合は、複数エントリーポイントを持つ region を含む routine が
エントリーポイントの数だけ出来ちゃうので、translator はてんてこ舞
いになるかもしれませんが。
で、Dohzono さん説が妥当だとしても、やっぱり問題ないのでは?
何故なら例えば多バイト命令 a b c ってのがあり、ソースイメージコー
ドとして a b c d とあったとして、a と b に余所から移ってくるエン
トリーポイントがあったとしたら、IR にはそのことは織り込まれている
のでは?
ようは、build でエントリーが a の時は命令 a b c で b のときは命令
b c となり、それぞれ続く命令は d であることがわかり、それを Alpha
命令に直せればそうすればそれで御仕舞いだし、そうじゃなくても IR が
それを記述出来れば以降のどこか、例えば code selector とかでも変換
可能なのでは。
# で、私の最初の主張は、どこかでこれに類する操作を行うだけで、対
# 応可能なのでは?ということです。
でたまたまどうせ、build では x86 命令を reparse するんだし、そう
してもこの場合は、そんなにコストが高くならないのではないかと思う
のですが?
なんでコストが高くならないかと思ったかというと、命令列 a b c d に
対し、build で reparse して a b c を見た時点で「多バイト命令だ!」
となっても、エントリーポイントと突き合わせて a にしかそれが無けれ
ば、通常に処理を続けるだけですむ。で b とかにエントリーポイントが
あるってことかほとんどあり得ないならば、さきのようなややこしい処
理は例外処理的な処理であるので、ここで余計に掛かるコストは「多バ
イト命令を見つけたときに、その途中にエントリーポイントが無いかの
判定」だけにほとんどなると。
あ、でもこの判定は馬鹿にならない頻度で起こる?
出来上がったコードの実行速度などは、そういうケースが頻発していな
い限り、そう差は無いのではないかと思うのですが。
で、多バイト命令の途中(もしくは prefix 直後)エントリーに対応をす
るか、しないかは費用対効果でしょうか。
> > > # "Each call target in the profile is used to generate *an*
> > > # entry to a rountine." というのもあるです。
> >
> > これは各 routine についてはそうだということです.
>
> どうもわたしのこの引用英文の読み間違いって事ですね。routine は複
> 数エントリーポイントを持ち得るわけですね。
え? 各 routine に (hash table に記載される) エントリは一つで合っている
と思いますが….
プログラムの call または間接分岐から開始される一連のコード群それぞ
れを一つの routine と見做し, さらにそれぞれを一つ以上の region に分割
して…と変換されるので, 最初の開始点以外にエントリはないはずです.
# なので条件分岐などの部分は前回までの実行時に実行されていなくても変換
# されなければならない (間接分岐はここが無理なわけで).
> もっとも、一つのエントリーポイントにつき一つの routine を作るよう
> にすれば、prefix が付いていようが簡単に対応出来るようになるはずで
> すが。
あ, わかりました. 成田さんは多バイト命令の途中への分岐を call または間
接分岐によるものと見ていられるんじゃないですか. 私が考えているのは「あ
る routine 内にあり, 同一 routine 内から条件分岐などで分岐するもの」で
す (つまり routine でなくて region の入り口の話).
仮に routine の入り口だとすると, 単にそのアドレスを hash table に置い
てそこから変換するだけの話だと思います[*].
> 何故なら例えば多バイト命令 a b c ってのがあり、ソースイメージコー
> ドとして a b c d とあったとして、a と b に余所から移ってくるエン
> トリーポイントがあったとしたら、IR にはそのことは織り込まれている
> のでは?
>
> ようは、build でエントリーが a の時は命令 a b c で b のときは命令
> b c となり、それぞれ続く命令は d であることがわかり、それを Alpha
> 命令に直せればそうすればそれで御仕舞いだし、そうじゃなくても IR が
> それを記述出来れば以降のどこか、例えば code selector とかでも変換
> 可能なのでは。
>
> # で、私の最初の主張は、どこかでこれに類する操作を行うだけで、対
> # 応可能なのでは?ということです。
やるなら regionizer でやらないと (経路によって region が異なる可能性が
ありますから).
> で、多バイト命令の途中(もしくは prefix 直後)エントリーに対応をす
> るか、しないかは費用対効果でしょうか。
私ならそれが原因で動かないアプリケーションが複数あったら対応するかも.
で, 実際にはないと思いますね. 80bit-FPU に依存しているほうがまだありそ
うだし.
[*]: ところで (これもまあ無いだろうとは思うんですが)
+------+ L1
| .. |
+------+ ← call エントリ
| .. |
|jz L1 |
+------+
のようなのはどうです? これと多バイト命令云々は同じ方法で解決出来そうで
はありますけど.
> > 先輩が色々試した範囲では既に prefetch された命令を書き換えると書き換え
> > 前の命令が実行されていたとか.
>
> じゃ Pentium だとどうだ? というんで
> unsigned
> foo (void *p, unsigned a)
(コード省略)
僕の手もとの 600 MHz (超低電圧版) Mobile Pentium III (Coppermine) では、
堂園さんの Pentium III とは違って、無印 Pentium と同じ結果になります。
堂園さんの Pentium III での結果を見る限り、IA-32 プロセッサでも、
パイプラインにすでに入ってしまっている命令は書き換え前のものが実行される、
ということが起こるんですね。へー。
仕様で許されているのか、それとも、erratum なのか?に興味があります。
Kazuyuki Shudo/首藤一幸 私をたばねないで あらせいとうの花のように
sh...@computer.org
> 僕の手もとの 600 MHz (超低電圧版) Mobile Pentium III (Coppermine) では、
> 堂園さんの Pentium III とは違って、無印 Pentium と同じ結果になります。
^^^IIですよね。
私の手元のPentium III (Coppermine) 750MHzおよび933MHzでは、
0, 1
0, 1
となりました。
Mobile Pentium MMX 266MHz では、
2, 3
0, 1
となりました。
無印Pentium ○
Mobile Pentium MMX ×
PII ×
PIII(Coppermine) ○
Mobile PIII(Coppermine) ○
(○ = 書き換えが直ちに反映された)
> 仕様で許されているのか、それとも、erratum なのか?に興味があります。
ちゃんと調べてないけど、「仕様で許されている」に100ウーロン。
だからわざわざ、
Serializing instruction execution
guarantees that any modifications to ... memory
for previous instructions are completed before the next instruction
is fetched and executed.
と書いてあるのでは。
前田敦司
> > 堂園さんの Pentium III とは違って
> ^^^IIですよね。
です.
> 無印Pentium ○
> Mobile Pentium MMX ×
> PII ×
> PIII(Coppermine) ○
> Mobile PIII(Coppermine) ○
>
> (○ = 書き換えが直ちに反映された)
II と対比して無印だと思ったんですが, ノートだとまた違うのかな. cpuid
を使ってみました (プログラムは末尾につけておきます).
無印だと思ったもの:
1 GenuineIntel
Original OEM Prosessor: Family = 5, Model = 2, Stepping ID=12
CXS-CMPXCHG8B Inst.
MCE-Machine Check Exception
MSR-RDMSR and WRMSR Support
TSC-Time Stamp Counter
PSE-Page Size Extensions
DE-Debugging Extensions
VME-Virtual-8086 Mode Enhancement
FPU-FPU on Chip
PentiumII;
1 GenuineIntel
Original OEM Prosessor: Family = 5, Model = 4, Stepping ID=4
MMX(TM) technology
CXS-CMPXCHG8B Inst.
MCE-Machine Check Exception
MSR-RDMSR and WRMSR Support
TSC-Time Stamp Counter
PSE-Page Size Extensions
DE-Debugging Extensions
VME-Virtual-8086 Mode Enhancement
FPU-FPU on Chip
> > 仕様で許されているのか、それとも、erratum なのか?に興味があります。
>
> ちゃんと調べてないけど、「仕様で許されている」に100ウーロン。
>
> だからわざわざ、
> Serializing instruction execution
> guarantees that any modifications to ... memory
> for previous instructions are completed before the next instruction
> is fetched and executed.
> と書いてあるのでは。
特殊レジスタとかピンアサインで config 出来たりして… (インストラクショ
ンリファレンスしかないので実際どうなのかはわかりません).
で, 確かに cpuid はそういう具合いに使えるんですが, eax/ebx/ecx/edx と
いった頻出レジスタを上書きしちゃうので使いづらい命令かもしれません.
typedef struct regs_t_ {
unsigned eax, ebx, ecx, edx;
} regs_t;
void
cpuid (regs_t *rp)
{
__asm__ __volatile__ ("cpuid"
: "=a" (rp->eax)
, "=b" (rp->ebx)
, "=c" (rp->ecx)
, "=d" (rp->edx)
: "0" (rp->eax));
}
int
main ()
{
regs_t r;
r.eax = 0;
cpuid (&r);
printf ("%u %c%c%c%c%c%c%c%c%c%c%c%c\n"
, r.eax
, r.ebx
, r.ebx >> 8
, r.ebx >> 16
, r.ebx >> 24
, r.edx
, r.edx >> 8
, r.edx >> 16
, r.edx >> 24
, r.ecx
, r.ecx >> 8
, r.ecx >> 16
, r.ecx >> 24
);
r.eax = 1;
cpuid (&r);
{
const char *tname[] = {
"Original OEM Prosessor",
"Intel OverDrive(R) Prosessor",
"Dual Prosessor",
"Intel Reserved",
};
printf ("%s: Family = %u, Model = %u, Stepping ID=%u\n"
, tname[(r.eax >> 12) & 3]
, (r.eax >> 8) & 0xf
, (r.eax >> 4) & 0xf
, r.eax & 0xf);
}
{
struct {
unsigned b;
char *s;
} features[] = {
{ 1 << 23, "MMX(TM) technology" },
{ 1 << 15, "CMOV-Cond.Move/Cmp.inst." },
{ 1 << 14, "MCA-Machine Check Arch." },
{ 1 << 13, "PGE-PTE Global Bit" },
{ 1 << 12, "MTRR-Mem.Type Range Reg." },
{ 1 << 9, "APIC-APIC on Chip" },
{ 1 << 8, "CXS-CMPXCHG8B Inst." },
{ 1 << 7, "MCE-Machine Check Exception" },
{ 1 << 6, "PAE-Physical Address Extensions" },
{ 1 << 5, "MSR-RDMSR and WRMSR Support" },
{ 1 << 4, "TSC-Time Stamp Counter" },
{ 1 << 3, "PSE-Page Size Extensions" },
{ 1 << 2, "DE-Debugging Extensions" },
{ 1 << 1, "VME-Virtual-8086 Mode Enhancement" },
{ 1 << 0, "FPU-FPU on Chip" },
};
unsigned i;
for (i = 0; i < sizeof features/sizeof features[0]; i++)
if (r.edx & features[i].b)
printf ("%s\n", features[i].s);
}
return 0;
> > 無印Pentium ○
> > Mobile Pentium MMX ×
> > PII ×
> > PIII(Coppermine) ○
> > Mobile PIII(Coppermine) ○
> >
> > (○ = 書き換えが直ちに反映された)
Family-Model-Stepping を加えると、
無印Pentium 5-2-C ○
Mobile Pentium MMX 5-8-1 ×
PII 5-4-4 ×
PIII(Coppermine)750 6-8-1 ○
PIII(Coppermine)933 6-8-6 ○
Mobile PIII(Coppermine) ?-?-? ○
という感じですね。(Linuxだと、/proc/cpuinfo を見ると分かります。)
...あれ、5-4-4はPIIじゃなくてP55C(Pentium MMX)のようですが...
(PIIならFamilyが6のはず。)
前田敦司
このケースの場合は書き換え直後に実行しているので多分正しいと思いますが,一
般論として×は絶対×ですが,○がどのような条件でも絶対○になるかどうかは容易
には証明できません。書き換えから時間が経つにつれて反映される可能性が高くなり
ますから,ギリギリのあたりに居ると同一のコードでも実行の度に結果が変わったり
して嫌らしい問題です。
>
> > 仕様で許されているのか、それとも、erratum なのか?に興味があります。
>
> ちゃんと調べてないけど、「仕様で許されている」に100ウーロン。
>
> だからわざわざ、
> Serializing instruction execution
> guarantees that any modifications to ... memory
> for previous instructions are completed before the next instruction
> is fetched and executed.
> と書いてあるのでは。
こう書いてあるということは,Serializing Instructionを実行しないと前に命令
の結果がメモリに反映されないことがあっても良い(反映されるケースも当然有る)
ということですから仕様ですね。そこまで注意して読んでませんでした。参考になり
ました。
Ando_san
> ...あれ、5-4-4はPIIじゃなくてP55C(Pentium MMX)のようですが...
そういえば社長がマシン入れ替えてたような….
> > > JavaのByte CodeをホットスポットなどのJITがx86バイナリに変換して,そ
> > > れをFX!32がアルファバイナリに変換して実行するというケースだってあり
> > > そうです。これなどは自己改変コードの塊になりそうですが。
> FX!32から話はそれますが、例えばTransmeta CrusoeのCode Morphingだと、実
> 際にこれを(効率良く)処理しなければなりませんよね。
言わずもがな、ですが、近年では
コード書き換えは実行効率を下げる避けるべき行為 (*) なので、
頻度は低く、例外的だと考えてよい
ので、x86 コードの書き換えがあった場合は単に、
変換後の VLIW コードを invalidate する、くらいで
充分だったりはしないでしょうか。
そんなに、コード書き換えがあった場合の効率なんて考えなくても
問題ないのではないかと推測してます。どうでしょ。
(*) ダイナミックリンカなどの例外を除く。
> JITの中には自己改変
> コードを出すものがある(たとえば、最初のコンパイル時は手を抜いておいて、
> 高い頻度で実行されると最適化コードに置き換える)と聞きますし、
コンパイルのし直しはよくありますが、コード書き換えではなくて、
call 先、ジャンプ先の書き換えのようなものだと思います。
よくある (ほんとか ??) のは
- 最初のコンパイルでは都合のよい仮定を置いておいて、
その仮定が崩れてしまったら、コードを書き換える。
(e.g. IBM TRL の JITC がやってる devirtualization)
- あるコードの初回実行時にのみ、ある処理 (クラス初期化) をするために、
初回の実行後にコードを書き換える。
(e.g. shuJIT がやってる、INT $3 (0xCC) やジャンプ命令の上書き)
shuJIT が、Crusoe (600 MHz TM5600) だと異常に遅いんですよ。
ベンチマーク結果: http://www.shudo.net/jit/perf-20010222/
しかし、どうやらコード書き換えが原因ではなさそうです。
コード書き換えをしないように shuJIT をコンパイルしても、
異常に遅いことに変わりはないので...
いまだに原因不明です。
無印Pentium 5-2-C ○
Mobile Pentium MMX 5-8-1 ×
Pentium MMX P55C 5-4-4 ×
PIII(Coppermine)750 6-8-1 ○
PIII(Coppermine)933 6-8-6 ○
Mobile PIII(Coppermine) ?-?-? ○
Pentium 4(Willamette) 15-0-10 ○ 追加
余談ですが…
CPU family は 4 bit しかないのに、Pentium 4 で最大値の 15 となっています。
x86 のマイクロアーキテクチャは Pentium 4 の NetBurst アーキテクチャで
打ち止めか?と思ったら、友人からこんなことを聞きました。
Pentium 4 → IV → 15 だから「15」なのだと Intel は言っている
すごく言い訳っぽいです。
shu...@muraoka.info.waseda.ac.jpさん:
> - 最初のコンパイルでは都合のよい仮定を置いておいて、
都合のよいコードが好きです。
> その仮定が崩れてしまったら、コードを書き換える。
スカートを踏んでいる。
いまいちなギャグだ。 久野
> Family-Model-Stepping を加えると、
(略)
> という感じですね。(Linuxだと、/proc/cpuinfo を見ると分かります。)
さらに、Crusoe の結果を追加します。
無印Pentium 5-2-C ○
Mobile Pentium MMX 5-8-1 ×
Pentium MMX P55C 5-4-4 ×
PIII(Coppermine)750 6-8-1 ○
PIII(Coppermine)933 6-8-6 ○
Mobile PIII(Coppermine) ?-?-? ○
P4(Willamette)1.7 15-0-10 ○
Crusoe TM5600 600 ○ 追加
身のまわりに他に x86 のコードを実行できるマシンはないだろうか?
と考えたところ、そういえば 733 MHz Itanium がありました。
OS は Linux です:
% uname -a
Linux ほげほげ 2.4.3-12smp #1 SMP Fri Jun 8 13:06:07 EDT 2001 ia64 unknown
なんとまあ、こんな結果になりました。
0, 3
0, 1
0, 3 ということは、元 (unsigned char a[]) のコードへの書き換えは
即、反映されていますが、
コピー先 (unsigned char *m) コードへの書き換えはすぐに反映されていません。
> なんとまあ、こんな結果になりました。
アラインメントに依存するようですね (当然か). MMX では
0: 2 3 ←パターン 1
0: 0 1
1: 2 3
1: 0 1
2: 2 3
2: 0 1
3: 2 3
3: 0 1
4: 2 3
4: 0 1
5: 2 1 ← パターン 2
5: 0 1
6: 2 1
6: 0 1
7: 2 1
7: 0 1
8: 2 3
8: 0 1
9: 2 3
9: 0 1
10: 2 3
10: 0 1
11: 2 3
11: 0 1
12: 2 3
12: 0 1
13: 0 3 ← パターン 3
13: 0 1
14: 0 3
14: 0 1
15: 0 3
15: 0 1
となりましたが, 無印は全て書き換え後の 0 1 でした.
unsigned
foo (void *p, unsigned a)
{
unsigned (*fp)(unsigned) = p;
return (*fp) (a);
}
int
main ()
{
const unsigned char o[] = {
0x8b, 0x44, 0x24, 0x04, /* mov 4(%esp), %eax */
0xe8, 0x00, 0x00, 0x00, 0x00, /* call .+5 */
0x5a, /* pop %edx */
0x83, 0xc2, 0x07, /* add $0x07, %edx */
0xc6, 0x02, 0x48, /* movb $0x48, (%edx) - "dec %eax" */
0x40, /* inc %eax (or dec %eax) */
0xc3 /* ret */
};
unsigned char a[sizeof o + 0x10];
unsigned char *m = malloc (sizeof o + 0x10);
unsigned i;
for (i = 0; i < 0x10; i++)
{
unsigned j;
for (j = 0; j < i; j++)
a[j] = m[j] = 0x90; /* nop */
memcpy (a+i, o, sizeof o);
memcpy (m+i, o, sizeof o);
printf ("%2u: %u %u\n"
, i
, foo (m, 1)
, foo (a, 2));
printf ("%2u: %u %u\n"
, i
, foo (m, 1)
, foo (a, 2));
# 風邪でダウン。やっと復帰。ということで、ちょっとボケぎみですが。
# subject もなんか見返せば全然ふさわしくない気もするけれど、もう
# 続かんだろうから、そのままで。
<a5cpvu$1pfm$1...@news2.rim.or.jp>の記事において
doh...@hf.rim.or.jpさんは書きました。
> In article <a555mk$2mjg$1...@nwall2.odn.ne.jp>
> tak...@aisoft.co.jp (Narita Takaoki) writes:
>
> > > > # "Each call target in the profile is used to generate *an*
> > > > # entry to a rountine." というのもあるです。
> > >
> > > これは各 routine についてはそうだということです.
> >
> > どうもわたしのこの引用英文の読み間違いって事ですね。routine は複
> > 数エントリーポイントを持ち得るわけですね。
>
> え? 各 routine に (hash table に記載される) エントリは一つで合っている
> と思いますが….
"is used to" ですから。とはいえ、大凡エントリは一つなのでしょう。
で、結局
> > 何故なら例えば多バイト命令 a b c ってのがあり、ソースイメージコー
> > ドとして a b c d とあったとして、a と b に余所から移ってくるエン
> > トリーポイントがあったとしたら、IR にはそのことは織り込まれている
> > のでは?
これをやれば、何がどうでも大丈夫そうだから、それで良いじゃん、と
いう気分になりつつある、今日この頃。(^^;
> > で、多バイト命令の途中(もしくは prefix 直後)エントリーに対応をす
> > るか、しないかは費用対効果でしょうか。
>
> 私ならそれが原因で動かないアプリケーションが複数あったら対応するかも.
> で, 実際にはないと思いますね. 80bit-FPU に依存しているほうがまだありそ
> うだし.
結局実際に対応していたか、していなかったかだけの話ではあるのです
が、対応したにせよそれほど莫大な労力が必要とも思えないはなしでは
あるのですが、対応してなくても MS Word for Windows95 とかの MS の
Office for Windows95 系アプリの類いが動けば、恐らく積極的にやる必
要はなかったでしょうね。
さらに、これが原因で動かないアプリケーションが複数あっても、営業
的に割に合うものでなかったりしたら、当然やらないでしょうし。
件の文書の "be used to" 表記の条件以外の状況に対応していないとい
う可能性は極めて高いでしょうから、対応していたか、いないか、どち
らかに金を賭けろといわれたら、私はしていない方に賭けちゃいます。
(^^;;
で、大元に戻ると、対応するなら、別々の(コードの)塊にする必要はな
いですし、しないでやった方が良いのでは。
# region の中に subdivision があるような気持ち悪さは若干残るけれ
# ど。
> [*]: ところで (これもまあ無いだろうとは思うんですが)
>
> +------+ L1
> | .. |
> +------+ ← call エントリ
> | .. |
> |jz L1 |
> +------+
>
> のようなのはどうです? これと多バイト命令云々は同じ方法で解決出来そうで
> はありますけど.
出来るだろうから良いのでは?
このスレッドとは関係ないのですが,Itaniumでx86コードを実行した時の性能って
どんなもんですか? 昔,Intelはその時代の中くらいのx86プロセサくらいと言って
ましたが,1.5~1.8GHzのP4並でしょうか?
Ando_san
Ando_san wrote:
> Itaniumでx86コードを実行した時の性能ってどんなもんですか?
> 昔,Intelはその時代の中くらいのx86プロセサくらいと言ってましたが,
> 1.5~1.8GHzのP4並でしょうか?
いや~、とてもとても、そんな性能は出ないんじゃないかな~
…と…試しました。
ベンチマークプログラムの内容は、完全に整数演算だけです。
多倍長整数演算ライブラリ GMP (http://swox.com/gmp/) を使っています。
実験環境は以下の通りで、gcc のコンパイルオプションは -O2 としました。
OS はすべて Linux 2.4.X です。
Pentium 4
- Pentium 4 / 1.7 GHz
- gcc version 3.1 20020131 (Red Hat Linux Rawhide 3.1-0.20)
- GMP 4.0.1
Pentium III
- Mobile Pentium III (ULV 版) / 600 MHz
- gcc version 3.1 20020205 (Red Hat Linux Rawhide 3.1-0.21)
- GMP 4.0.1
Itanium
- Itanium / 733 MHz
- gcc version 2.96 20000731 (Red Hat Linux 7.1 2.96-85)
- GMP 3.1.1
Itanium で IA-32 コードを試す際は、
Pentium III 上でコンパイルしたバイナリを Itanium マシンにコピーしました。
つまり、gcc 3.1 (CVS 版) でコンパイルされたバイナリが、GMP 3.1.1 と
ダイナミックリンクされて実行されています。
結果はこうです:
Pentium 4 / 1.7 GHz 3.302 秒
Pentium III / 600 MHz 8.837 秒
Itanium / 733 MHz / IA-64 コード 24.244 秒
Itanium / 733 MHz / IA-32 コード 39.395 秒
ひじょ~にラフに言って、
733 MHz Itanium/IA-32 : 600 MHz Pentium III の性能比は 1 : 4.458 ですから、
733 MHz Itanium は 135 MHz Pentium III 相当、でしょうか。
(飽くまで、整数演算中心の特定のプログラムでの結果だということに
ご注意くださいませ。)