[risc-v]Cをrisc-vアセンブリに変換


Assembly Languageとは?


アセンブリ言語またはアセンブリ言語は、機械言語に対応するコンピュータプログラミングの低レベル言語である.(wiki)
Sam Blairを紹介する前に、開発者がC言語で作成したソースコードが実際にどのように動作しているかを理解しておきましょう.C言語プログラムは以下の順序で作成し、実行する.
  • ソースコードの作成(C言語)
    開発者はC言語文法に基づいてプログラムを作成する.
  • int s = 0; 
    for (i=0; i<10; i++) {
        if (A[i]==0) continue;
        s += A[i];
    }
  • しかし、CPUはこのC言語コードを直接理解できない.したがって,コンパイラというツールを用いてCPUを理解可能な言語にコンパイルする.
  • コンパイル
    コンパイラがコンパイルするCPUのアーキテクチャにマシン言語を生成する.
  • 0x00000000		0x000009B3
    0x00000004		0x00000A33
    0x00000008		0x10000A97
    0x0000000C		0xFF8A8A93
    0x00000010		0x00A00E13
    0x00000014		0x03CA5063
    0x00000018		0x002A1513
    0x0000001C		0x01550533
    0x00000020		0x00052483
    0x00000024		0x00048463
    0x00000028		0x009989B3
    0x0000002C		0x001A0A13
    0x00000030		0xFE0002E3
    CPU運転
  • コンパイルされたマシン言語
    CPUは、生成したマシン言語を順次読み出すことによりプログラムを実行する.
    CPUはregister,ALU,MUXなどからなり,入力されたマシン命令セットが下図のように順次取得,復号,実行されるにつれてプログラムが実行される.△この構造については、次の記事で詳しく説明します.
  • では、ここのコンポーネントは何でしょうか。


    アセンブリ言語は、機械言語に対応する低レベルのプログラミング言語である.
    したがって,コンパイラがコンパイルしたマシン言語はコンポーネントに一つ一つ対応する.開発者がコンポーネントを介してプログラムを記述する場合、コマンドセットレベルでプログラムの動作を完全に制御できます.
    0x00000000		0x000009B3		add x19 x0 x0
    0x00000004		0x00000A33		add x20 x0 x0
    0x00000008		0x10000A97		auipc x21 65536
    0x0000000C		0xFF8A8A93		addi x21 x21 -8
    0x00000010		0x00A00E13		addi x28 x0 10
    0x00000014		0x03CA5063		bge x20 x28 32
    0x00000018		0x002A1513		slli x10 x20 2
    0x0000001C		0x01550533		add x10 x10 x21
    0x00000020		0x00052483		lw x9 0(x10)
    0x00000024		0x00048463		beq x9 x0 8
    0x00000028		0x009989B3		add x19 x19 x9
    0x0000002C		0x001A0A13		addi x20 x20 1
    0x00000030		0xFE0002E3		beq x0 x0 -28

    CをRISC-Vコンポーネントに変換


    上のcコードの動作を説明すると以下のようになります.
  • sという変数を宣言し、値を0に初期化します.
  • int s = 0;
    変数宣言は
  • iで、初期化は0で、iが10以上の場合は重複文を終了し、重複文が終了するとi値は+1になります.
  • for (i=0; i<10; i++) { 
  • アレイAのiの1番目の値が0である場合、継続する.
  •     if (A[i]==0) continue;
  • s値にアレイAのi値を加算します.
  •     s += A[i];
    }
    ここで、s変数がx 19、i、x 20、Aのアドレスがx 21のレジスタにそれぞれ格納されていると仮定すると、次のようにコンポーネントコードを記述できます.
            add x19 x0 x0    // s = 0
            add x20 x0 x0    // i = 0
            addi x28 x0 10   // x28 = 10
    LOOP:   bge x28 x20 EXIT // if 10 >= i; GOTO EXIT
            slli x10 x20 2   // x10 = i * 4
            add x10 x10 x21  // x10 = &A[0] + i*8
            lw x9 0(x10)     // x9 = A[i]
            beq x9 x0 L1     // if A[i] == 0; GOTO L1
            add x19 x19 x9   // s = s + A[i]
    L1  :   addi x20 x20 1   // i = i + 1
            beq x0 x0 LOOP   // GOTO LOOP
    EXIT:
    まずsとiを表すレジスタを0(x 0)に初期化する.bgeは定数を比較するのではなく、レジスタに格納されている値を比較するため、x 28という一時レジスタを作成して数字10を格納する.for (i=0; i<10; i++)は、3行目および3行目によって達成される.
    32ビットriscvを使用する場合、a[i]の値は以下のようにロードされます.
  • iの値*4=>x 10
    32ビットriscvは4バイトが1文字です.したがって,インデックスが表す配列の間隔を求めるには,配列のインデックスに4を乗じなければならない.
  • Aの先頭アドレス+x 10=>x 10
    配列の先頭アドレスに間隔を付けて、実際の値を格納するアドレス値を求めます.
  • x 10に記憶するメモリにアドレス値を介してメモリにアクセスし、x 9レジスタ
  • に値をロードする.
    slli x10 x20 2   // x10 = i * 4
    add x10 x10 x21  // x10 = &A[0] + i*8
    lw x9 0(x10)     // x9 = A[i]

    シミュレーション


    VscodeのRISC−V Venusシミュレータ拡張を用いて記述されたアセンブリコードをシミュレートすることができる.(Venusシミュレータは32ビットriscvをサポートしているのでload wordを使用しています.)

    上のコードを実行します.

    ループを繰り返すと、x 9、x 19レジスタに適切な値が格納され、所望の瞬間にループが終了することが確保される.

    Reference

  • https://ko.wikipedia.org/wiki/%EC%96%B4%EC%85%88%EB%B8%94%EB%A6%AC%EC%96%B4
  • Computer Organization and Design RISC-V edition
  • コンピュータ構造課資料、李英敏、ソウル市立大学