38500行

  • 依存グラフを構築するconstruct_dependence_graph()と各命令に優先度を設定するset_priorities()を書き終わった。
  • 依存関係にある命令間のレイテンシを返す、アーキテクチャ依存のlist_sched_get_latency()を書いた。
  • 例外依存とメモリ依存まわりを書き直してバグを潰した。

ロードストアの並べ替えに関して、現状ではvolatileでないストア─ロード間の並べ替えだけ許可しており、ロード─ストア間やストア─ストア間の順序は保存される。ロード命令をどれだけ上方に引き上げられるかが問題なので、とりあえずはこれで十分だろう。なにせUltraSPARC IIIではロードのレイテンシは3*1もある。Non-faultingロード*2を使いたくなってくる。関係ないが、UltraSPARC IIIのマニュアルを読んでいると「SunのSPARCが速くなる日は遠いな」という感じだ。富士通のほうはよく知らない。

エイリアス解析に関しては、命令スケジューリングフェーズ内では独自の解析を行わず、SSA変換する際に行うエイリアス解析の結果をそのまま流用する。独自の解析を行えばロード─ストア間やストア─ストア間の並び替えもできるようになるのだが、手間のわりに報われなさそうだ。解析の結果は仮想的な「メモリレジスタ」の形で表され、ストアはメモリレジスタへの書き込み、ロードはメモリレジスタからの読み込みを中間言語のレベルで明示的に行う。解析は何の工夫も無い最も単純なもので、あるクラスのあるフィールドを一つのメモリレジスタ、ある型の配列も全て一つのメモリレジスタで表す。

模式的に表すと、

a.field0 := r0
r1 := b.field0
r2 := a.field1

中間言語のレベルでは

m0 := store .field0 a r0
r1 := load .field0 b m0
r2 := load .field1 a m1

となる。この場合、aとbが同じオブジェクトを指しているか否かの解析をしていないので、2番目のロードは1番目のストアより上には行けない。このconservativeなメモリ依存関係はメモリレジスタm0で表されている。一方、3番目のロードは1番目のストアより上に行ける。この事実はメモリレジスタm0とm1が異なることで表されている。

*1:Unsignedのロード、つまり符合拡張しないロードのレイテンシは2。……符合拡張する必要がないことがわかっている場面ではunsignedロードを使うようにすれば全体的に速くなったりするのだろうか。アドホック過ぎて研究ネタにはならなさそうだが。

*2:不正アクセス例外を起こさないロード命令。レジスタへのプリフェッチのようなものだ。