1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Intel® HD Graphicsを雑に読む

Posted at

要旨

Intel® HD Graphics用プログラムの開発環境は人間がアセンブリコードを記述するには不向きなようです。読む場合も大雑把に雰囲気をとらえる程度にとどめておく方がよいかもしれません。

準備

無料の開発者ソフトウェアとサービス - Visual StudioからVisual Studio Communityを、
Intel® ToolkitsからIntel® oneAPI Base Toolkitを適当にインストールします。

次に挙げる参考文献をダウンロードします。

GCNを読むで扱った、コンパイルしバイナリ列を保存するプログラムをビルドします。ファイル名はcl2bin.exe辺りでよいでしょう。

次のようなカーネルを書きます。ファイル名はexample.cl辺りでよいでしょう。

kernel void K0()
{
}

kernel void K1(global uchar *d)
{
	d[31] = 42;
}

cl2bin
Platform-Index, Device-Index: Device-Name
0, 0: Intel(R) FPGA Emulation Device
1, 0: Intel(R) Core(TM) i5-10400 CPU @ 2.90GHz
2, 0: gfx900
3, 0: Intel(R) UHD Graphics 630
cl2bin Platform-Index Device-Index Source-File-Path

ファイルに書き出します。

cl2bin 3 0 example.cl > example.bin

ディスアセンブルします。怪しい雰囲気ですが気にしません。./dump以下に*.asmが三つ出来ているはずです。

ocloc disasm -file example.bin
Warning : missing or invalid -device parameter - results may be inaccurate
Warning : Path to dump folder not specificed - using ./dump as default.
Path to patch list not provided - using defaults, skipping patchtokens as undefined.
Trying to disassemble K0.krn
Trying to disassemble K1.krn
Trying to disassemble K2.krn

読む

カーネルK0のディスアセンブル結果は次のようなものでした。

L0:
(W)     mov (8|M0)               r2.0<1>:ud    r0.0<1;1,0>:ud                  
(W)     or (1|M0)                cr0.0<1>:ud   cr0.0<0;1,0>:ud   0x4C0:uw              {Switch}
(W)     mov (8|M0)               r127.0<1>:ud  r2.0<8;8,1>:ud                   {Compacted}
(W)     send (8|M0)              null     r127    0x27            0x02000010           {EOT} // wr:1+0, rd:0; spawner; end of thread
L56:

読んでいきます。
L0:L56:はラベルで、0バイト目と56バイト目を意味しているようです。{Compacted}と付いている命令の長さは8バイトで、付いていない命令は16バイトです。基本的に命令長は16バイトなのですが条件を満たせば8バイトにできます。


(W) mov (8|M0) r2.0<1>:ud r0.0<1;1,0>:ud
レジスタのサイズは32バイト、つまり32bitを8個ぶんです。

uint32_t *d = ((uint32_t *)r2);
uint32_t *s = ((uint32_t *)r0);
d[0] = s[0];
d[1] = s[0];
d[2] = s[0];
d[3] = s[0];
d[4] = s[0];
d[5] = s[0];
d[6] = s[0];
d[7] = s[0];
といった感じです。
(W) predicate(選択的に結果を反映するあれ)とのことですがよくわかりません。
(8|M0) 8要素をSIMDのチャネル0から順に詰めるといった感じです。左側の数字は1, 2, 4, 8, 16, 32のいずれかで、右側の数字は4の非負整数倍です。
ソースオペランドr0.0<1;1,0>:udのフォーマットは regnum.subreg<vertStride;width,horzStride>:type です。udはunsigned dword、(8|M0)なので同じものを8要素となります。
デスティネーションオペランドr2.0<1>:udの山括弧内の1はhorzStrideです。


(W) or (1|M0) cr0.0<1>:ud cr0.0<0;1,0>:ud 0x4C0:uw {Switch}
half float, float, double floatの非正規数を許可します。コントロールレジスタを操作する際は{Switch}指定で強制的にスレッドを切り替えて命令キューをフラッシュします。


(W) mov (8|M0) r127.0<1>:ud r2.0<8;8,1>:ud {Compacted}
r2の値をr127にコピーしています。


(W) send (8|M0) null r127 0x27 0x02000010 {EOT} // wr:1+0, rd:0; spawner; end of thread
スレッドの終了のようです。(8|M0)な点が少々不思議です。
dest: null
src: r127
ex_desc: 0x27 (0b00100111)

  • Target Function ID: 7(SFID_SPAWNER)
  • EOT: 1
  • Extended Message Length: 0
  • Extended Function Control: 0

desc: 0x02000010 (0b00000010000000000000000000010000)

  • Function Control: 0b0000000000000010000
  • Header Present: 0
  • Response Length: 0
  • Message Length: 1

カーネルK1のディスアセンブル結果は次のようなものでした。

L0:
(W)     mov (8|M0)               r2.0<1>:ud    r0.0<1;1,0>:ud                  
(W)     or (1|M0)                cr0.0<1>:ud   cr0.0<0;1,0>:ud   0x4C0:uw              {Switch}
(W)     mov (1|M0)               r4.0<2>:b     42:w                              
(W)     mov (1|M0)               r3.0<1>:ud    0x1F:uw                             
(W)     mov (8|M0)               r127.0<1>:ud  r2.0<8;8,1>:ud                   {Compacted}
(W)     mov (1|M0)               r5.0<1>:ud    r4.0<0;1,0>:ub                  
(W)     sends (1|M0)             null:ud  r3      r5      0x4A            0x02030000           // wr:1+1, rd:0; hdc.dc0; byte scattering write 8b
(W)     send (8|M0)              null     r127    0x27            0x02000010           {EOT} // wr:1+0, rd:0; spawner; end of thread
L120:

(W) sends (1|M0) null:ud r3 r5 0x4A 0x02030000 // wr:1+1, rd:0; hdc.dc0; byte scattering write 8b
グローバルメモリに1バイト書き込むようです。r3はオフセットです。
dest: null:ud
src0: r3
src1: r5
ex_desc: 0x4A (0b01001010)

  • Target Function ID: 10(SFID_DP_DC0)
  • EOT: 0
  • Extended Message Length: 1
  • Extended Function Control: 0

desc: 0x02030000 (0b00000010000000110000000000000000)

  • Function Control: 0b0110000000000000000
  • Header Present: 0
  • Response Length: 0
  • Message Length: 1

さて、ABIがどうなっているのか調べても容易には見つからなかったので雑に探ってみたわけですが...。次のようなカーネルを書いてみます。

kernel void K2(global uint *d)
{
	*d = get_work_dim();
}

kernel void K3(global uint *d)
{
	*d = get_local_size(0);
}

カーネルK2のディスアセンブル結果は次のようなものでした。

L0:
(W)     mov (8|M0)               r2.0<1>:ud    r0.0<1;1,0>:ud                  
(W)     or (1|M0)                cr0.0<1>:ud   cr0.0<0;1,0>:ud   0x4C0:uw              {Switch}
(W)     mov (1|M0)               r4.0<1>:uq    r3.0<0;1,0>:uq                  
(W)     mov (1|M0)               r6.0<1>:d     r3.2<0;1,0>:d                    {Compacted}
(W)     mov (8|M0)               r127.0<1>:ud  r2.0<8;8,1>:ud                   {Compacted}
(W)     sends (1|M0)             null:ud  r4      r6      0x4C            0x040681FF           // wr:2+1, rd:0; hdc.dc1; a64 dword scattering write
(W)     send (8|M0)              null     r127    0x27            0x02000010           {EOT} // wr:1+0, rd:0; spawner; end of thread
L96:

最初の引数のアドレスはr3.0<1>:uq、次元はr3.2<1>:dに入っているようです。引数が増えると次元を入れてある場所が変わるかもしれません。


(W) sends (1|M0) null:ud r4 r6 0x4C 0x040681FF // wr:2+1, rd:0; hdc.dc1; a64 dword scattering write
dest: null:ud
src0: r4
src1: r6
ex_desc: 0x4C (0b01001100)

  • Target Function ID: 12 (SFID_DP_DC1)
  • EOT: 0
  • Extended Message Length: 1
  • Extended Function Control: 0

desc: 0x040681FF (0b00000100000001101000000111111111)

  • Function Control: 0b1101000000111111111
  • Header Present: 0
  • Response Length: 0
  • Message Length: 2

カーネルK1の場合とはTarget Function IDとFunction Controlが異なります。r4はオフセットではなくアドレスだからなのでしょう。


カーネルK3のディスアセンブル結果は次のようなものでした。

L0:
(W)     mov (8|M0)               r2.0<1>:ud    r0.0<1;1,0>:ud                  
(W)     or (1|M0)                cr0.0<1>:ud   cr0.0<0;1,0>:ud   0x4C0:uw              {Switch}
(W)     mov (1|M0)               r4.0<1>:uq    r3.0<0;1,0>:uq                  
(W)     mov (1|M0)               r6.0<1>:d     r3.2<0;1,0>:d                    {Compacted}
(W)     mov (8|M0)               r127.0<1>:ud  r2.0<8;8,1>:ud                   {Compacted}
(W)     sends (1|M0)             null:ud  r4      r6      0x4C            0x040681FF           // wr:2+1, rd:0; hdc.dc1; a64 dword scattering write
(W)     send (8|M0)              null     r127    0x27            0x02000010           {EOT} // wr:1+0, rd:0; spawner; end of thread
L96:

ワークグループのx次元のサイズはr3.2<1>:dに入っているようです。
「ワークグループのx次元のサイズ」と「次元」は同じ場所に書かれているようです。


気を取り直して、次のようなカーネルを書いてディスアセンブルします。

kernel void K4(global uint *d)
{
	d[0] = get_work_dim();
	d[1] = get_local_size(0);
}
L0:
(W)     mov (8|M0)               r2.0<1>:ud    r0.0<1;1,0>:ud                  
(W)     or (1|M0)                cr0.0<1>:ud   cr0.0<0;1,0>:ud   0x4C0:uw              {Switch}
(W)     mov (2|M0)               r3.4<1>:d     r3.2<2;2,1>:d                   
(W)     mov (8|M0)               r7.0<1>:w     0x40:uv                             
(W)     mov (8|M0)               r127.0<1>:ud  r2.0<8;8,1>:ud                   {Compacted}
(W)     mov (8|M0)               r6.0<1>:d     r3.4<0;1,0>:d                    {Compacted}
(W)     add (8|M0)               r4.0<1>:uq    r3.0<0;1,0>:uq    r7.0<8;8,1>:w   
(W)     mov (2|M0)               r6.0<1>:d     r3.4<2;2,1>:d                   
(W)     sends (8|M0)             null:ud  r4      r6      0x4C            0x040681FF           // wr:2+1, rd:0; hdc.dc1; a64 dword scattering write
(W)     send (8|M0)              null     r127    0x27            0x02000010           {EOT} // wr:1+0, rd:0; spawner; end of thread
L144:

次元はr3.2<1>:d、ワークグループのx次元のサイズはr3.3<1>:dに入っているようです。


任意のカーネルを走らせるプログラムをビルドしK2, K3, およびK4をIntel® VTune™ Profilerで観測してみましたが、同様のディスアセンブル結果でした。使用するデバイスの名前はIntel(R) UHD Graphics 630であることをcl::Device::getInfo<CL_DEVICE_NAME>()で取得し確認しています。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?