HSPをJIT実行

ようやくraytraceが1.7倍速くらいで動くようになった。

http://peppermint.jp/products/hsp/
http://github.com/zakki/openhsp/tree/jit


まんまhsp3cnvと同じだとインタプリタ実行と等速か若干遅いくらいなので、次のあたりを実装した。

  • 実行時にトレースしつつ型をチェックする
  • 型が一定ならスタック操作を経由せずに四則演算やロード・ストアを直接計算する
  • ローカル変数として使われている変数を検出してメモリアクセスをサボる


パフォーマンス的に効きそうな項目も結構残ってる。
完全に変数の型を解析してかつグローバルに最適化かけられれば10倍速くらいで動いてもいいはずなんだよなー

  • hsp3cnv由来で関数の引数が値渡しで済むはずの箇所も参照渡ししている
  • (関数で無い)コマンドはそのまま
  • 自動型変換が必要な箇所は以前hspvarにまかせている
  • ベーシックブロック単位でしか処理していない

あと、機能的にユーザー定義関数やモジュールが使えないのが痛い。

HSPの1行が

 d = 1.0 / os.hit

次のようなLLVM-IRになって

  %ptr = load i8** getelementptr inbounds (%struct.PVal* @Var__HspVar43, i32 0, i32 4) ; <i8*> [#uses=1]
  %0 = bitcast i8* %ptr to i32*                   ; <i32*> [#uses=1]
  %1 = load i32* %0                               ; <i32> [#uses=1]
  %ptr1 = load i8** getelementptr inbounds (%struct.PVal* @Var__HspVar13, i32 0, i32 4) ; <i8*> [#uses=1]
  %2 = bitcast i8* %ptr1 to double*               ; <double*> [#uses=1]
  call void @HspVarCoreReset(%struct.PVal* @Var__HspVar13)
  call void @HspVarCoreArray2(%struct.PVal* @Var__HspVar13, i32 %1)
  %offset = load i32* getelementptr inbounds (%struct.PVal* @Var__HspVar13, i32 0, i32 8) ; <i32> [#uses=1]
  %3 = getelementptr double* %2, i32 %offset      ; <double*> [#uses=1]
  %4 = load double* %3                            ; <double> [#uses=1]
  %5 = fdiv double 1.000000e+000, %4              ; <double> [#uses=1]
  %ptr2 = load i8** getelementptr inbounds (%struct.PVal* @Var__HspVar71, i32 0, i32 4) ; <i8*> [#uses=1]
  %6 = bitcast i8* %ptr2 to double*               ; <double*> [#uses=1]
  store double %5, double* %6

最終的にx86命令に落ちると

	movl	_Var__HspVar43+28, %eax
	movl	(%eax), %esi
	movl	_Var__HspVar13+28, %edi
	movl	$_Var__HspVar13, (%esp)
	call	_HspVarCoreReset
	movl	%esi, 4(%esp)
	movl	$_Var__HspVar13, (%esp)
	call	_HspVarCoreArray2
	movsd	LCPI89_0, %xmm0
	movl	_Var__HspVar13+40, %eax
	divsd	(%edi,%eax,8), %xmm0
	movl	_Var__HspVar71+28, %eax
	movsd	%xmm0, (%eax)

Cに比べるとだいぶ無駄がある。ループのフロー見て配列の自動拡張必要ないことを特定できれば真ん中の当たりは消せるな。