新版FM音源プログラム (15)

今回は各種のチップ/コンパイラの組み合わせのオブジェクトに対する逆アセンブル・リストを掲載します。
もとの C ソース・ファイルでの記述を少し変えると、コンパイル後のオブジェクトはかなり大きく変化するので、あまり細部に渡る追及はしないでおきます。
μVision V5.21a (armcc コンパイラ) でコンパイルした calc_slot() 関数を逆アセンブルしたものを下に示します。
Cortex-M4 コアの STM32F446、STM32F407、STM32F303 に共通です。

# uVision V5.21a (armcc v5.06 update 3)
# options: -O3 -cpu Cortex-M4.fp

08000ed4 <calc_slot>:
 8000ed4:   e92d 4030       stmdb   sp!, {r4, r5, lr}
 8000ed8:   e9d0 2300       ldrd    r2, r3, [r0]
 8000edc:   441a            add     r2, r3
 8000ede:   6042            str     r2, [r0, #4]
 8000ee0:   6883            ldr     r3, [r0, #8]
 8000ee2:   68c4            ldr     r4, [r0, #12]
 8000ee4:   441a            add     r2, r3
 8000ee6:   8a03            ldrh    r3, [r0, #16]
 8000ee8:   1e89            subs    r1, r1, #2
 8000eea:   ea03 2312       and.w   r3, r3, r2, lsr #8
 8000eee:   5ee3            ldrsh   r3, [r4, r3]
 8000ef0:   6942            ldr     r2, [r0, #20]
 8000ef2:   435a            muls    r2, r3
 8000ef4:   13d3            asrs    r3, r2, #15
 8000ef6:   f9b0 2012       ldrsh.w r2, [r0, #18]
 8000efa:   8243            strh    r3, [r0, #18]
 8000efc:   8b04            ldrh    r4, [r0, #24]
 8000efe:   441a            add     r2, r3
 8000f00:   4354            muls    r4, r2
 8000f02:   6084            str     r4, [r0, #8]
 8000f04:   8e84            ldrh    r4, [r0, #52]   ; 0x34
 8000f06:   f100 021c       add.w   r2, r0, #28
 8000f0a:   435c            muls    r4, r3
 8000f0c:   e9d2 3500       ldrd    r3, r5, [r2]
 8000f10:   442b            add     r3, r5
 8000f12:   6203            str     r3, [r0, #32]
 8000f14:   4423            add     r3, r4
 8000f16:   8d84            ldrh    r4, [r0, #44]   ; 0x2c
 8000f18:   6a85            ldr     r5, [r0, #40]   ; 0x28
 8000f1a:   ea04 2413       and.w   r4, r4, r3, lsr #8
 8000f1e:   6b03            ldr     r3, [r0, #48]   ; 0x30
 8000f20:   5f2c            ldrsh   r4, [r5, r4]
 8000f22:   4363            muls    r3, r4
 8000f24:   13db            asrs    r3, r3, #15
 8000f26:   85c3            strh    r3, [r0, #46]   ; 0x2e
 8000f28:   3038            adds    r0, #56 ; 0x38
 8000f2a:   2900            cmp     r1, #0
 8000f2c:   dcd4            bgt.n   8000ed8 <calc_slot+0x4>
 8000f2e:   bd30            pop     {r4, r5, pc}

gcc ツールチェインの「objdump」を使っているため、armcc の .o オブジェクト・ファイルには対応しておらず、リンクずみの .axf ファイルを入力としています。
アーキテクチャは 32/16 ビット命令の thumb2 インストラクション・セットを持つ ARMv7-M ですが、多くが 16 ビット thumb 命令へコンパイルされていて、ARMv6-M にはない 32 ビット thumb2 命令は少数となっています。
Atollic TrueSTUDIO for STM32 v9.0.0 (gcc 6.3.1) によるコンパイル結果を逆アセンブルしたものを下に示します。

# Atollic TrueSTUDIO for STM32 v9.0.0 (gcc 6.3.1)
# options: -Os -mcpu=cortex-m4

00000000 <calc_slot>:
   0:   b530            push    {r4, r5, lr}
   2:   3038            adds    r0, #56 ; 0x38
   4:   f850 2c34       ldr.w   r2, [r0, #-52]
   8:   f850 3c38       ldr.w   r3, [r0, #-56]
   c:   f850 5c18       ldr.w   r5, [r0, #-24]
  10:   4413            add     r3, r2
  12:   f850 2c30       ldr.w   r2, [r0, #-48]
  16:   f840 3c34       str.w   r3, [r0, #-52]
  1a:   4413            add     r3, r2
  1c:   f830 2c28       ldrh.w  r2, [r0, #-40]
  20:   ea02 2313       and.w   r3, r2, r3, lsr #8
  24:   f850 2c2c       ldr.w   r2, [r0, #-44]
  28:   5ed2            ldrsh   r2, [r2, r3]
  2a:   f850 3c24       ldr.w   r3, [r0, #-36]
  2e:   4353            muls    r3, r2
  30:   f930 2c26       ldrsh.w r2, [r0, #-38]
  34:   13db            asrs    r3, r3, #15
  36:   189c            adds    r4, r3, r2
  38:   f830 2c20       ldrh.w  r2, [r0, #-32]
  3c:   f820 3c26       strh.w  r3, [r0, #-38]
  40:   4362            muls    r2, r4
  42:   f840 2c30       str.w   r2, [r0, #-48]
  46:   f850 2c1c       ldr.w   r2, [r0, #-28]
  4a:   f830 4c04       ldrh.w  r4, [r0, #-4]
  4e:   442a            add     r2, r5
  50:   f840 2c18       str.w   r2, [r0, #-24]
  54:   fb03 2204       mla     r2, r3, r4, r2
  58:   f830 3c0c       ldrh.w  r3, [r0, #-12]
  5c:   ea03 2312       and.w   r3, r3, r2, lsr #8
  60:   f850 2c10       ldr.w   r2, [r0, #-16]
  64:   5ed2            ldrsh   r2, [r2, r3]
  66:   f850 3c08       ldr.w   r3, [r0, #-8]
  6a:   3902            subs    r1, #2
  6c:   4353            muls    r3, r2
  6e:   13db            asrs    r3, r3, #15
  70:   2900            cmp     r1, #0
  72:   f820 3c0a       strh.w  r3, [r0, #-10]
  76:   f100 0038       add.w   r0, r0, #56     ; 0x38
  7a:   dcc3            bgt.n   4 <calc_slot+0x4>
  7c:   bd30            pop     {r4, r5, pc}

32 ビット thumb2 命令が多用されていますが、そのほとんどがマイナス・オフセットを持つ ldr/ldrh/ldrsh/str/strh 命令で、シフト付きオペランドなどの 32 ビット thumb2 固有の命令は少数となっています。
リンク前のオブジェクト・ファイル「calc_slot.o」を逆アセンブルしています。
μVision V5.21a (armcc v5.06 update 3) で LPC1114 (Cortex-M0, ARMv6-M) 用にコンパイルした結果の逆アセンブル・リストを下に示します。

# uVision v5.21a (armcc v5.06 update 3)
# options: -O3 -cpu Cortex-M0

0000049c <calc_slot>:
     49c:   b530            push    {r4, r5, lr}
     49e:   c80c            ldmia   r0!, {r2, r3}
     4a0:   3808            subs    r0, #8
     4a2:   189a            adds    r2, r3, r2
     4a4:   6042            str     r2, [r0, #4]
     4a6:   6883            ldr     r3, [r0, #8]
     4a8:   68c4            ldr     r4, [r0, #12]
     4aa:   189a            adds    r2, r3, r2
     4ac:   8a03            ldrh    r3, [r0, #16]
     4ae:   0a12            lsrs    r2, r2, #8
     4b0:   4013            ands    r3, r2
     4b2:   5ee3            ldrsh   r3, [r4, r3]
     4b4:   6942            ldr     r2, [r0, #20]
     4b6:   1e89            subs    r1, r1, #2
     4b8:   435a            muls    r2, r3
     4ba:   13d3            asrs    r3, r2, #15
     4bc:   2212            movs    r2, #18
     4be:   5e82            ldrsh   r2, [r0, r2]
     4c0:   8243            strh    r3, [r0, #18]
     4c2:   8b04            ldrh    r4, [r0, #24]
     4c4:   18d2            adds    r2, r2, r3
     4c6:   4354            muls    r4, r2
     4c8:   6084            str     r4, [r0, #8]
     4ca:   8e84            ldrh    r4, [r0, #52]   ; 0x34
     4cc:   4602            mov     r2, r0
     4ce:   321c            adds    r2, #28
     4d0:   435c            muls    r4, r3
     4d2:   ca28            ldmia   r2!, {r3, r5}
     4d4:   3a08            subs    r2, #8
     4d6:   18eb            adds    r3, r5, r3
     4d8:   6053            str     r3, [r2, #4]
     4da:   191b            adds    r3, r3, r4
     4dc:   8a14            ldrh    r4, [r2, #16]
     4de:   0a1b            lsrs    r3, r3, #8
     4e0:   401c            ands    r4, r3
     4e2:   68d5            ldr     r5, [r2, #12]
     4e4:   6953            ldr     r3, [r2, #20]
     4e6:   5f2c            ldrsh   r4, [r5, r4]
     4e8:   3038            adds    r0, #56 ; 0x38
     4ea:   4363            muls    r3, r4
     4ec:   13db            asrs    r3, r3, #15
     4ee:   8253            strh    r3, [r2, #18]
     4f0:   2900            cmp     r1, #0
     4f2:   dcd4            bgt.n   49e <calc_slot+0x2>
     4f4:   bd30            pop     {r4, r5, pc}

TrueSTUDIO for STM32 v9.0.0 は Atollic が STMicro に買収されてからリリースされたバージョンで、制限なし版が無償で提供されていますが、当然 STM32 シリーズのサポートのみとなり、他社のチップには対応していません。
そんなわけで、LPC1114 版は μVision によるコンパイル結果のみを示しています。
細かいことは言わないと言っておきながら、細かい部分に触れますが、冒頭部分、

     49e:   c80c            ldmia   r0!, {r2, r3}
     4a0:   3808            subs    r0, #8

は、連続されて格納されている ph_inc、ph_acc を r2、r3 にロードするコードですが、16 ビット thumb 命令の範囲では、LDM (LoaD Multiple) 命令は「IA」(Increment After) 付き、および「!」(ライトバック・サフィックス) 付きに限られ、命令の実行後に op_prop[] ポインタの r0 が 8 進んでしまうのを、次の subs r0,#8 で戻しています。
もとの C ソースで、サイン波テーブルのアクセスに、

phh >>=  (PH_ACC_FRAC_BITS-1);
out = *(int16_t *)((uint8_t *)o_p->stab_p + (o_p->ind_mask & phh));

のように、元々 (int16_t *) である o_p->stab_p を (uint8_t *) でキャストし、バイト・アドレッシングでポインタのオフセットを計算してから、それを再び (int16_t *) にキャストし直すという、一見変な処理をしています。
これは、Cortex-M0 (ARMv6-M) 用にコンパイルした場合に余計なシフト命令が挿入されないようにするためです。
Cortex-M3/M4 (ARMv7-M) では、thumb2 命令の中に「シフト付きオペランド」を持つ命令があるので、r4 にサイン波テーブルアドレス、r3 に「ハーフワード・インデクス」としてのオフセットが格納されている場合、

  ldrsh  r2, [r4, r3, lsl #1]

のように、1 命令で「ハーフワード・インデクス」から「バイト・オフセット」に変換しながらアクセスすることができます。
それに対して「シフト付きオペランド」を持たない Cortex-M0 (ARMv6-M) では、

  lsls   r3, r3, #1
  ldrsh  r2, [r4, r3]

のようにシフト命令が強制的に挿入されて「ハーフワード・インデクス」から「バイト・オフセット」に変換されます。
フェーズ・アキュムレータの値からサイン波テーブルのオフセットを求めるのに、シフトおよびマスクが欠かせないので、それらの処理をあらかじめバイト・オフセットの領域で計算しておけば、「余分」なシフト命令が不要になります。