RL78のローカル変数のメモリアドレスについて

こんにちは。SUZUKIです。

 

RL78/G14用のプログラムをCS+で開発中に良く分からない現象に遭遇しました。

プログラムは

void test(char a){

char b = 0;

b = a+10;

}

という簡単なものです。

この時に変数a,bをウォッチするとアドレスがそれぞれ[L:REG][H:REG]となっており、変数bの値は0のままでした。

動作する条件としては下記3つありました。

1.変数bの宣言をグローバルで行った場合

2.別の変数(使用しない)を関数内で宣言した場合

3.変数bの型をint型にした場合

これらの時は変数a,b共にアドレスが割り当てられ正常に動作しました。

これはどのような事が起きているのでしょうか。

宜しくお願い致します。

  • わわいです
    もひとつもひとつですが、、
    最適化ありの設定でコンパイルすると、Cのソース行と機械語の対応が1対1ではなくなってきたりします。
    前に言ったように省略されるコードもあり、複数のコードが単一ないしは不可分の一連のコードに展開されたり、はたまた命令の実行順序が入れ替えられたりします。
    そのため、最適化有りのコードをソースデバッグすると、ステップの順序があちこち飛んだり、到達しないコードが出てきたりします。また、変数モニタも最適化のため値の代入がなされなかったり、代入のタイミングがずれたりなんかもします。

    ということで、そういう場合には、コンパイラの最適化を、「なし」あるいは「デバッグ用」にしてコンパイルすると、きちんと追いかけられるようになります
  • SUZUKIさん、こんにちは。NoMaYです。再び自己フォローです。

    初心者向けフォーラムで、うかつなことを言ってしまったかも知れないと気になったので、補足しておこうと思います。今回、volatile云々の話をしたのは、コンパイラのオプションで最適化ありにしてコンパイル(*1)したプログラムを、やむにやまれずデバッグ(*2)しなければならなくなった状況で無理矢理に変数をウォッチ出来るようにするためのテクニックです。C言語の文法上そうしなければならない、ということは全くありません。

    *1: CS+で(一般的に他のデバッガでも)ソースレベルデバッグする時には最適化無しにする(fujita nozomuさん、わわいさん、アドバイス)のが原則です。

    *2: とは言っても、最適化無しにするとバグが発生しなくなる、こともありますので、その時は今回のウォッチのような現象も含むCS+の(一般的に他のデバッガでもの)謎な振る舞い(わわいさんアドバイス)に泣きたくなるような思いをしながら、最適化ありにしたままデバッグすることもあります。

    *2': ですが、正直に言うと、私は過去の投稿で、単に文章/説明を書くのが面倒だったから、という理由で手抜きをする為にvolatileを付けたことがあります、、、

    それと、以前にIKUZOさんという人がCC-RXの最適化で困惑していた時の投稿を読み直していて、今回の件で思い当てることがありましたので、少し調べて後で投稿します。(本日中)

    もうひとつ、ジェネレーションギャップ(?)を感じましたが、コンパイラのバージョンを尋ねた時にCS+のビルドプラグインのバージョンが返事として返って来たのですが、本来は何を(CS+のどこに表示されるバージョンを)返答すべきかなのか、少し確認して後で投稿します。(本日中)

    ちなみに、ひょっとして、個人ユーザさん(趣味としての電子工作)でしょか? (最近は、会社の先輩/上司から、もしくは新入社員教育で、教えて貰えないのかなと気になったものですから。)

  • SUZUKIさん、こんにちは。NoMaYです。

    まず、コンパイラのバージョンの確認方法ですが、以下の画面コピーのように、CS+のメニュー[ヘルプ]→[バージョン情報]もしくはメニュー[ヘルプ]→[詳細バージョン情報]、の何れかを選択します。




    ここまでは御存知かと思いますが、この時、プロジェクトを開いた状態であれば、以下の部分にコンパイラのバージョンが表示されます。




    注意が必要なのは、この時、プロジェクトを開いていない状態だと、その部分にはコンパイラのバージョンは表示されない、という点です。




    ちなみに、上の画面コピーはCS+ for CA,CXですが、CS+ for CCでも以下の画面コピーのように事情は同じです。

    プロジェクトを開いた状態であれば、以下の部分にコンパイラのバージョンが表示されます。




    プロジェクトを開いていない状態だと、その部分にはコンパイラのバージョンは表示されないです。




    後ほど、もう1つ投稿します。

  • SUZUKIさん、こんにちは。NoMaYです。すみません。夜遅くなるどころか日付まで変わってしまいました。

    CA78K0RとCC-RLでコンパイラが生成するコードを調べてみました。試したプロジェクトは以下のzipファイルに固めてあります。ただ、書いてみて思ったのですが、もっと分かり易く書き直さないと駄目な気がします。もう少し、書き方を考えてみようと思います。

    Issue_20171214.zip

    具体的には以下のソースコードに対して2×3=6つの条件でコンパイルして、コンパイルリストを出力させ、変数bへの代入がどうなっているかアセンブラコードを調べました。

    void test(char a){
        /* volatile */ char b = 0;
        b = a+10;
        R_DAC0_Set_ConversionValue(b);
    }

    (1) CA78K0R V1.72
    (2) CC-RL V1.02 (当方特有の事情で最新版では無いです)

    (A) 最適化なし
    (B) プロジェクト作成時のデフォルトの最適化
    (C) プロジェクト作成時のデフォルトの最適化で変数bをvolatile宣言

    今回の件は(1-B)に該当するのですが、わわいさんがアドバイスしている通り、最適化のため変数への値の代入がなされなかった、ということです。ただ、ちょっと紛らわしいことに、もう少しCA78K0Rが賢ければ多分なされなかったと思われる「0を代入する」ことは行われており、それに対して「a+10を代入する」ことだけが行われなくなっていたので、ということでした。(ちなみに、CC-RLでは「0を代入する」ことも行われなくなっていました。)

    (1) CS+ for CA,CX V4.00.01 + CA78K0R V1.72




    (1-A) 最適化なし ⇒ 0(00H)や変数a+10(0AH)を変数bへ代入している

      760   760                      ; line    83 : void test(char a){
      761   761 00012                _test:
      763   763 00012  C7                    push    hl                                              ;[INF] 1, 1
      764   764 00013  C1                    push    ax (補)引数aをスタックに移す                    ;[INF] 1, 1
      765   765 00014  C1                    push    ax (補)変数bの領域をスタック上に確保する        ;[INF] 1, 1
      766   766 00015  FBF8FF                movw    hl,sp                                           ;[INF] 3, 1
      768   768                      ; line    84 :     /* volatile */ char b = 0;
      770   770 00018  CC0100                mov     [hl+1],#00H     ; b,0                           ;[INF] 3, 1
      771   771                      ; line    85 :     b = a+10;
      773   773 0001B  8C02                  mov     a,[hl+2]        ; a                             ;[INF] 2, 1
      774   774 0001D  0C0A                  add     a,#0AH  ; 10                                    ;[INF] 2, 1
      775   775 0001F  9C01                  mov     [hl+1],a        ; b                             ;[INF] 2, 1
      776   776                      ; line    86 :     R_DAC0_Set_ConversionValue(b);
      778   778 00021  318E                  shrw    ax,8 (補)AレジスタをAXレジスタ(引数)に移す   ;[INF] 2, 1
      779   779 00023 RFC000000              call    !!_R_DAC0_Set_ConversionValue                   ;[INF] 4, 3
      780   780                      ; line    87 : }
      783   783 00027  1004                  addw    sp,#04H                                         ;[INF] 2, 1
      784   784 00029  C6                    pop     hl                                              ;[INF] 1, 1
      785   785 0002A  D7                    ret                                                     ;[INF] 1, 6

    (1-B) プロジェクト作成時のデフォルトの最適化 ⇒ 0(00H)を変数bへ代入しているが、変数a+10(0AH)を変数bへ代入していない

      759   759                      ; line    83 : void test(char a){
      760   760 00012                _test:
      762   762 00012  C7                    push    hl                                              ;[INF] 1, 1
      763   763 00013  16                    movw    hl,ax                                           ;[INF] 1, 1
      765   765                      ; line    84 :     /* volatile */ char b = 0;
      767   767 00014  5700                  mov     h,#00H  ; 0                                     ;[INF] 2, 1
      768   768                      ; line    85 :     b = a+10;
      770   770 00016  66                    mov     a,l                                             ;[INF] 1, 1
      771   771 00017  0C0A                  add     a,#0AH  ; 10                                    ;[INF] 2, 1
      772   772                      ; line    86 :     R_DAC0_Set_ConversionValue(b);
      774   774 00019  318E                  shrw    ax,8 (補)AレジスタをAXレジスタ(引数)に移す     ;[INF] 2, 1
      775   775 0001B RFC000000              call    !!_R_DAC0_Set_ConversionValue                   ;[INF] 4, 3
      776   776                      ; line    87 : }
      779   779 0001F  C6                    pop     hl                                              ;[INF] 1, 1
      780   780 00020  D7                    ret                                                     ;[INF] 1, 6

    (1-C) プロジェクト作成時のデフォルトの最適化で変数bをvolatile宣言 ⇒ 0(00H)や変数a+10(0AH)を変数bへ代入している

      759   759                      ; line    83 : void test(char a){
      760   760 00012                _test:
      762   762 00012  C7                    push    hl                                              ;[INF] 1, 1
      763   763 00013  C1                    push    ax (補)引数aをスタックに移す                   ;[INF] 1, 1
      764   764 00014  C1                    push    ax (補)変数bの領域をスタック上に確保する        ;[INF] 1, 1
      765   765 00015  FBF8FF                movw    hl,sp                                           ;[INF] 3, 1
      767   767                      ; line    84 :     volatile char b = 0;
      769   769 00018  CC0100                mov     [hl+1],#00H     ; b,0                           ;[INF] 3, 1
      770   770                      ; line    85 :     b = a+10;
      772   772 0001B  8C02                  mov     a,[hl+2]        ; a                             ;[INF] 2, 1
      773   773 0001D  0C0A                  add     a,#0AH  ; 10                                    ;[INF] 2, 1
      774   774 0001F  9C01                  mov     [hl+1],a        ; b                             ;[INF] 2, 1
      775   775                      ; line    86 :     R_DAC0_Set_ConversionValue(b);
      777   777 00021  8C01                  mov     a,[hl+1]        ; b                             ;[INF] 2, 1
      778   778 00023  318E                  shrw    ax,8 (補)AレジスタをAXレジスタ(引数)に移す     ;[INF] 2, 1
      779   779 00025 RFC000000              call    !!_R_DAC0_Set_ConversionValue                   ;[INF] 4, 3
      780   780                      ; line    87 : }
      783   783 00029  1004                  addw    sp,#04H                                         ;[INF] 2, 1
      784   784 0002B  C6                    pop     hl                                              ;[INF] 1, 1
      785   785 0002C  D7                    ret                                                     ;[INF] 1, 6

    (2) CS+ for CC V6.00.00 + CC-RL V1.02




    (2-A) 最適化なし ⇒ 0(0x00)や変数a+10(0x0000A)を変数bへ代入している

    00000009                      121  _test:
    00000009                      122       .STACK _test = 6
    00000009                      127       ;***       84 : void test(char a){
    00000009 C7                   129       push hl (補)引数aと変数bの領域をスタック上に確保する
    0000000A 9801                 130       mov [sp+0x01], a (補)引数aをスタックに移す
    0000000C                      131       ;***       85 :    /* volatile */ char b = 0;
    0000000C C80000               133       mov [sp+0x00], #0x00
    0000000F                      135       ;***       86 :     b = a+10;
    0000000F 8801                 137       mov a, [sp+0x01]
    00000011 318E                 138       shrw ax, 8+0x00000 (補)AレジスタをAXレジスタ(作業用)に移す
    00000013 040A00               139       addw ax, #0x000A
    00000016 60                   140       mov a, x
    00000017 9800                 141       mov [sp+0x00], a
    00000019                      143       ;***       87 :     R_DAC0_Set_ConversionValue(b);
    00000019 8800                 145       mov a, [sp+0x00]
    0000001B FC000000             146       call !!_R_DAC0_Set_ConversionValue
    0000001F C6                   147       pop hl
    00000020 D7                   148       ret
    00000021                      149       ;***       88 : }

    (2-B) プロジェクト作成時のデフォルトの最適化 ⇒ 0(0x00)も変数a+10(0x0A)も変数bへ代入していない

    00000009                      113  _test:
    00000009                      114       .STACK _test = 4
    00000009                      119       ;***       84 : void test(char a){
    00000009 0C0A                 121       add a, #0x0A
    0000000B                      122       ;***       85 :    /* volatile */ char b = 0;
    0000000B                      123       ;***       86 :     b = a+10;
    0000000B                      124       ;***       87 :     R_DAC0_Set_ConversionValue(b);
    0000000B EC000000             126       br !!_R_DAC0_Set_ConversionValue
    0000000F                      127       ;***       88 : }

    (2-C) プロジェクト作成時のデフォルトの最適化で変数bをvolatile宣言 ⇒ 0(0x00)や変数a+10(0AH)を変数bへ代入している

    00000009                      113  _test:
    00000009                      114       .STACK _test = 6
    00000009                      119       ;***       84 : void test(char a){
    00000009 C7                   121       push hl (補)変数bの領域をスタック上に確保する
    0000000A                      122       ;***       85 :    volatile char b = 0;
    0000000A C80000               124       mov [sp+0x00], #0x00
    0000000D                      125       ;***       86 :     b = a+10;
    0000000D 0C0A                 127       add a, #0x0A
    0000000F 9800                 128       mov [sp+0x00], a
    00000011                      129       ;***       87 :     R_DAC0_Set_ConversionValue(b);
    00000011 8800                 131       mov a, [sp+0x00]
    00000013 FC000000             132       call !!_R_DAC0_Set_ConversionValue
    00000017 C6                   133       pop hl
    00000018 D7                   134       ret
    00000019                      135       ;***       88 : }

     

  • SUZUKIさん、こんにちは。NoMaYです。

    今回の件、アセンブラコードが全く分からなくても、わわいさんがアドバイスしているような、最適化のため変数への値の代入がなされなかった、ということの雰囲気が分かるような説明を考えてみました。今回の件はCC78K0Rだったのですが、インパクトが強そうな気がしましたので、説明としてはCC-RLを使うことにしました。

    まず、SUZUKIさんが書かれたコードはもともと以下のものでした。

    void test(char a){
        char b = 0;
        b = a+10;
        R_DAC0_Set_ConversionValue(b);
    }

    でも、よくよく考えると、b = 0 としたすぐ後に b = a+10 としていますので、プログラムの機能としては b = 0 は無くても良く、以下のコードで置き換えることが出来ます。

    void test(char a){
        char b;
        b = a+10;
        R_DAC0_Set_ConversionValue(b);
    }

    そうして、コードの置き換えを考え始めると、わざわざ a+10 を b に代入しなくても、以下のコードで置き換えることが出来ることに気付きます。

    void test(char a){
        R_DAC0_Set_ConversionValue(a+10);
    }

    ところで、敢えて置き換える価値はありませんが、下のように置き換えてもプログラムの機能としてはもともとのコードと等価です。ただ、この置き換えから、今回の件でウォッチで見た時に b = a+10 が実行されていないように見えた原因が何となく分かるのではないかと、思います。(人間が見れば b = 0 がプログラムの機能として意味が無いことは直ぐに分かりますが、コンパイラとしては少なくとも「a+10 を b に代入した後にその b を読み出して引数にセットする」という冗長な処理が無くなったので価値はある、だから置き換える、と判断することもあるということです。)

    void test(char a){
        char b = 0;
        R_DAC0_Set_ConversionValue(a+10);
    }

    ちなみに、CC-RLのデフォルトの最適化では、上の4つのコードは全て以下の同じアセンブラコードにコンパイルされました。(アセンブラコードの意味が分からなくても同じコードが生成されていることが伝われば良いかな、と考えました。) ソースは違うが、プログラムの機能としては同じなので、同じアセンブラコードになる、という訳です。

    00000009                      120  _test:
    00000009                      114       .STACK _test = 4
    00000009                      126       ;***       84 : void test(char a){
    00000009 0C0A                 128       add a, #0x0A
    0000000B                      129       ;***       85 :    char b = 0;
    0000000B                      130       ;***       86 :     b = a+10;
    0000000B                      131       ;***       87 :     R_DAC0_Set_ConversionValue(b);
    0000000B EC000000             133       br !!_R_DAC0_Set_ConversionValue
    0000000F                      134       ;***       88 : }
    00000009                      113  _test:
    00000009                      114       .STACK _test = 4
    00000009                      119       ;***       84 : void test(char a){
    00000009 0C0A                 121       add a, #0x0A
    0000000B                      122       ;***       85 :     char b;
    0000000B                      123       ;***       86 :     b = a+10;
    0000000B                      124       ;***       87 :     R_DAC0_Set_ConversionValue(b);
    0000000B EC000000             126       br !!_R_DAC0_Set_ConversionValue
    0000000F                      127       ;***       88 : }
    00000009                      113  _test:
    00000009                      114       .STACK _test = 4
    00000009                      119       ;***       84 : void test(char a){
    00000009 0C0A                 121       add a, #0x0A
    0000000B                      122       ;***       85 :     R_DAC0_Set_ConversionValue(a+10);
    0000000B EC000000             124       br !!_R_DAC0_Set_ConversionValue
    0000000F                      125       ;***       86 : }
    00000009                      113  _test:
    00000009                      114       .STACK _test = 4
    00000009                      119       ;***       84 : void test(char a){
    00000009 0C0A                 121       add a, #0x0A
    0000000B                      122       ;***       85 :     char b = 0;
    0000000B                      123       ;***       86 :     R_DAC0_Set_ConversionValue(a+10);
    0000000B EC000000             125       br !!_R_DAC0_Set_ConversionValue
    0000000F                      126       ;***       87 : }

    あと、プログラムの機能という点では、上のCC-RLのデフォルトの最適化ありでコンパイルしたアセンブラコードと、CC-RLで最適化無しでコンパイルした以下のアセンブラコードは、なんと等価ということになるのです。(アセンブラコードの意味が分からなくても量も内容も全然違うコードが生成されていることが伝われば良いかな、と考えました。)

    00000009                      121  _test:
    00000009                      122       .STACK _test = 6
    00000009                      127       ;***       84 : void test(char a){
    00000009 C7                   129       push hl
    0000000A 9801                 130       mov [sp+0x01], a
    0000000C                      131       ;***       85 :    char b = 0;
    0000000C C80000               133       mov [sp+0x00], #0x00
    0000000F                      135       ;***       86 :     b = a+10;
    0000000F 8801                 137       mov a, [sp+0x01]
    00000011 318E                 138       shrw ax, 8+0x00000
    00000013 040A00               139       addw ax, #0x000A
    00000016 60                   140       mov a, x
    00000017 9800                 141       mov [sp+0x00], a
    00000019                      143       ;***       87 :     R_DAC0_Set_ConversionValue(b);
    00000019 8800                 145       mov a, [sp+0x00]
    0000001B FC000000             146       call !!_R_DAC0_Set_ConversionValue
    0000001F C6                   147       pop hl
    00000020 D7                   148       ret
    00000021                      149       ;***       88 : }

    [余談]

    以前にウェブで調べていて気付いたのですが、CC-RLは情報処理学会で論文が発表されていて、その論文は優秀論文の1つとして表彰されたらしいです。

    ipsj.ixsq.nii.ac.jp/ej/?action=pages_view_main&active_action=repository_view_main_item_detail&item_id=163721&item_no=1&page_id=13&block_id=8
    RL78マイコン向けCコンパイラCC-RLにおける機種依存最適化の設計
    The Design of Target Dependent Optimizations in CC-RL, an Optimizing C Compiler for the RL78 Microcontroller
    (情報処理学会の有料コンテンツです。2018年06月06日から無料でダウンロード可能になるみたいです。)

    www.ipsj.or.jp/award/yamashita2016.html
    2016年度(平成28年度)山下記念研究賞 受賞者/論文タイトル/発表研究会略称