強火で進め

このブログではプログラム関連の記事を中心に書いてます。

iPhoneでインラインアセンブラを使う(プログラム編[4] メモリのロード/ストア)

第4回はメモリのロード/ストア(読み込み/書き込み)処理についてです。

サンプルは以下の様になります。

    int list[] = {
        1, 2, 3, 4
    };
    int *pList = list;
    NSLog(@"%d %d %d %d", list[0], list[1], list[2], list[3]);
    __asm__ volatile (
                      // data[0]に対する処理
                      "ldr        r0, [%[list_address]] \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address]] \n\t"
                      // data[1]に対する処理
                      "ldr        r0, [%[list_address], #4] \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address], #4] \n\t"
                      // data[2]に対する処理
                      "ldr        r0, [%[list_address], #8] \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address], #8] \n\t"
                      // data[3]に対する処理
                      "ldr        r0, [%[list_address], #12] \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address], #12] \n\t"
                      : [list_address] "+r" (pList)
                      : 
                      : "r0", "memory"
                      );
    NSLog(@"%d %d %d %d", list[0], list[1], list[2], list[3]);

C言語で定義した変数を名称で指定

今回からC言語で定義した変数をインラインアセンブラ内で使用するときに %0、 %1 などの分かり辛いものでなく名称で指定して使用する方法を導入します。

Extended Asm - Using the GNU Compiler Collection (GCC)
http://gcc.gnu.org/onlinedocs/gcc-4.2.3/gcc/Extended-Asm.html#Extended-Asm

なお、こちらの機能、ドキュメントを参照するとGCCのバージョン3.1から導入されたものの様ですがiPhoneの開発環境では問題なく使用可能です。こちらの方法を使用した方がどこでどのデータを使用しているかが明確であるため、以降はこちらの記述を使用します。

こちら今までのこの様な記述が

    int val = 2;
    NSLog(@"%d", val);
    __asm__ volatile (
                      "mov        %0, #10 \n\t"
                      : "+r" (val)
                      : 
                      );
    NSLog(@"%d", val);

この様に記述できます。

    int val = 2;
    NSLog(@"%d", val);
    __asm__ volatile (
                      "mov        %[c_val], #10 \n\t"
                      : [c_val] "+r" (val)
                      : 
                      );
    NSLog(@"%d", val);

メモリへのアクセス

メインの解説に戻ります。
まずは以下のldr(LDR)命令。

ldr r0, [ %[list_address] ]

こちら「レジスタ r0 へ list_address(=pList) に格納されているアドレスの値を代入」という意味になります。 
%[list_address] は pList が割り当ててあるのでCで定義した list のアドレスが格納されています。

それが [] で囲まれている場合、C言語で言うところ * を先頭に付けた *pList と同じ扱いとなります。

そのため、C言語風に記述するとこうなります。

r0 = *pList

*pList は list[0] と同じアドレスの値となるので r0 にロードされる値は 1 となります。

次に r0 に r0 を加算(r0 = r0 + r0)をします。これにより r0 の値は 2 となります。

add r0, r0, r0

次のstr(STR)命令はldrのときとは反対に「レジスタ r0 の値を list_address で指定されたアドレスに格納」という意味になります。
これにより list[0] の値は 2 に置き換わります。

以降、list[1]、list[2]の値にも同様に処理します。

プリインデクス(pre-index)

しかし、list[1]に対する処理以降では %[list_address] の後に記述が追加されているのが確認できるかと思います。

ldr r0, [%[list_address], #4]

ここでの #4 はアドレスに4バイト追加するという意味になります。
C言語風に記述するとこうなります。

r0 = *(pList+1);

C言語の場合は +1 とした場合、そのポインタが指している型のサイズ(今回の場合、intなので4バイト)分、先のアドレスを指定したことになります。

しかし、アセンブリで記述する場合はその様な処理は行われないため4バイトを指定しないといけません。

以降、4バイトずつ加算された値を指定することになります。

list[2]のとき

ldr r0, [%[list_address], #8]

list[3]のとき

ldr r0, [%[list_address], #12]

ポインタを自動更新

また、別の記述方法として ! を使った以下の様な記述方法があります。

    int list[] = {
        1, 2, 3, 4
    };
    int *pList = list;
    NSLog(@"%d %d %d %d", list[0], list[1], list[2], list[3]);
    __asm__ volatile (
                      // data[0]に対する処理
                      "ldr        r0, [%[list_address]] \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address]] \n\t"
                      // data[1]に対する処理
                      "ldr        r0, [%[list_address], #4]! \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address]] \n\t"
                      // data[2]に対する処理
                      "ldr        r0, [%[list_address], #4]! \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address]]! \n\t"
                      // data[3]に対する処理
                      "ldr        r0, [%[list_address], #4]! \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address]]! \n\t"
                      : [list_address] "+r" (pList)
                      : 
                      : "r0", "memory"
                      );
    NSLog(@"%d %d %d %d", list[0], list[1], list[2], list[3]);

こちらがポインタの値の更新部分になります。

ldr r0, [%[list_address], #4]!

ここでは %[list_address]+4バイト の位置のデータを r0 にロード(データの読み込み)をした後、 %[list_address]に4バイト加算します。

擬似的に記述するとこう。

r0 = [%[list_address], #4]
%[list_address] = %[list_address] + 4

C言語風に記述するとこうなります。

r0 = *(pList+1)
pList = pList + 1

この様な処理を使う事により data[1]〜data[3] のデータ処理についてはループを3回するという記述も可能となり、メンテナンス性の向上とメモリ使用量の削減が可能です。

ポストインデクス(post-indexed)

ポストインデクスというものあります。サンプルは以下となります。

    int list[] = {
        1, 2, 3, 4
    };
    int *pList = list;
    NSLog(@"%d %d %d %d", list[0], list[1], list[2], list[3]);
    __asm__ volatile (
                      // data[0]に対する処理
                      "ldr        r0, [%[list_address]] \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address]], #4 \n\t"
                      // data[1]に対する処理
                      "ldr        r0, [%[list_address]] \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address]], #4 \n\t"
                      // data[2]に対する処理
                      "ldr        r0, [%[list_address]] \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address]], #4 \n\t"
                      // data[3]に対する処理
                      "ldr        r0, [%[list_address]] \n\t"
                      "add        r0, r0, r0 \n\t"
                      "str        r0, [%[list_address]], #4 \n\t"
                      : [list_address] "+r" (pList)
                      : 
                      : "r0", "memory"
                      );
    NSLog(@"%d %d %d %d", list[0], list[1], list[2], list[3]);

ポスト(post-)と有る様にロード処理を行った後にアドレスを加算する動作になります。

擬似的に記述するとこう。

r0 = [%[list_address]]
%[list_address] = %[list_address] + 4

C言語風に記述するとこうなります。

r0 = *pList
pList = pList + 1

この様な処理を使う事により data[0]〜data[3] まですべてをループ内に記述も可能となり、「ポインタを自動更新」の場合よりもよりメンテナンス性の向上とメモリ使用量の削減が可能です。

iPhoneインラインアセンブラを使うのエントリー一覧はこちら