CS+のコード生成ツールが生成するポインタ変数でvolatile修飾子の記述位置がおかしいものがあるように思います

藤田様の別スレッドに便乗なのですが、CS+のRL78のコード生成ツールが生成するuart通信機能のポインタ変数でvolatile修飾子の記述位置がおかしいものがあるように思います。具体的な変数名で言うと、gp_uart0_tx_addressとgp_uart0_rx_addressです。(CS+ for CC V5.00.00のコード生成機能で確認しました。)

コード生成ツールが生成した変数宣言は以下の通りです。

volatile uint8_t * gp_uart0_tx_address;         /* uart0 send buffer address */

volatile uint8_t * gp_uart0_rx_address;         /* uart0 receive buffer address */

ところが、以下の画面コピーの赤枠の箇所の通り、割り込みルーチン内で変更されるのはgp_uart0_tx_addressやgp_uart0_rx_addressの変数自身ですので以下の変数宣言が正しい気がします。

uint8_t * volatile gp_uart0_tx_address;         /* uart0 send buffer address */

uint8_t * volatile gp_uart0_rx_address;         /* uart0 receive buffer address */

試しに型修飾子の作用の仕方がvolatileと同様なconst(型修飾子の作用自体(機能)としては対義語的になる)で試してみると、変数自身に作用したのは以下の画面コピーのように橙枠の方でしたので、volatileも変数自身に作用させる場合には同様である筈です。

  • でも、わわいさん、RL78やRXの話にいつもいつもSHの話を持ち出されてますよね?
    (しかも、頻繁に他の人にRL78やRXでは話が違うと誤りを指摘されていて。)
    (さらに、誤りを指摘されても、意固地なまでに反論されることは有っても、素直に
    間違いを詫びる返事をされたことが無いという。)
    さすがに、あなたがそれを言いますか?という思いです、、、

    わわいさんwrote: said:
    わわいです
    いや、元質問のトピックにわざわざGCCもってこられましても。。
  • >NULL は何のオブジェクトも指しませんが、逆に、何かのオブジェクトを指すポインタは NULL でないことは確定的です。前段の hogehoge の指す先をアクセスした以降では hogehoge の値は NULL ではないということで、無限ループとなります。

      NULL だったら関数を抜けると言う判断をどこかでしているのなら分かります。
     しかし、そんな事はしていないよね。
      NULL かどうか判断して、「NULLでした」と表示するプログラムは作れないと言うこと?
     NULLをどう使うかはプログラマーしだいだと思います。printfだと0x00の前まで送ると言う仕様だから、それに従うしか無い。
     しかし自分で作った関数で、文字列の先頭アドレスと転送バイト数を指定すれば0x00の所で止める必要も無いわけです。
     言いたいことは、「NULLだったらエラーにしなくてはならず、NULLを使ってはいけません」と言う規則がある訳でも無いだろうと言う事です。

     こういう所では、よくバトルになったりするけれど、攻撃している訳じゃないですよ。
     コンパイルの動作がおかしいんじゃないかと言う話です。
  • わわいです
    おまえがゆーな!というのはしごくごもっとも。自覚しておりますです
    まー、このトピックに関しては、ルネサスコンパイラはいろいろユルユルだもんで、それだからこその居心地の良さ、ってのがあったりするもんで、あんまし細かいこと言い出すと使いづらくなっちゃうよー、、、という思いもあったりします
    だもんで、GCC使い出すと、なんでこんな四角四面なんだ!!とぼやきたくなるってもんです。

    #でもオレが他CPUの話し出すときはそう言ってだしてるとおもうんですが。。<と言い訳してみるw

    まあ、最適化、とかの周辺になると、設計思想、や実装依存が色濃く出てくるところなんで、業界標準(??)たるGCCを持ってきてあーだこーだ、比較してなんやらかんやらってのは知識の共有、積み上げにも役に立つので、どしどしやってくださいませ。
  • > いや、元質問のトピックにわざわざGCCもってこられましても。。

    では CC-RL V1.04.00 を使用して最適化の指定を「実効速度優先(-Ospeed)」にしてコンパイルした結果をどうぞ。

    extern volatile int* hogehoge;
    
    void hoge(void)
    {
        while (*hogehoge) {
            ;
        }
    }
    
    void hogera(void)
    {
        while (hogehoge) {
            ;
        }
    }
    
    extern int* volatile piyopiyo;
    
    void piyo(void)
    {
        while (*piyopiyo) {
            ;
        }
    }
    
    void piyora(void)
    {
        while (piyopiyo) {
            ;
        }
    }
    
    _hoge:
    	movw bc, !LOWW(_hogehoge)
    .BB@LABEL@2_1:	; bb1
    	movw ax, 0x0000[bc]
    	or a, x
    	bnz $.BB@LABEL@2_1
    	ret
    
    _hogera:
    	movw ax, !LOWW(_hogehoge)
    	or a, x
    	sknz
    	ret
    .BB@LABEL@3_2:	; bb1
    	br $.BB@LABEL@3_2
    
    _piyo:
    .BB@LABEL@4_1:	; bb1
    	movw bc, !LOWW(_piyopiyo)
    	movw ax, 0x0000[bc]
    	or a, x
    	bnz $.BB@LABEL@4_1
    	ret
    
    _piyora:
    .BB@LABEL@5_1:	; bb1
    	movw ax, !LOWW(_piyopiyo)
    	or a, x
    	bnz $.BB@LABEL@5_1
    	ret
    
  • わわいです
    fujita nozomu さん
    すみません、ごめんなさい。
    本来は私がやらないといけないのに、お手数をおかけいたしました。
    いやー、RLのコンパイラ入れてやっとかんといかんなーと思いつつ、つい面倒がってしまいまして。。
    SHならあるんで、その結果でも貼ろうかとも思いましたが、NoMayさんのツッコミ受けたあとですし。。
    #このトピック言い訳ばっかしw
  • fujita nozomu さん、他のコンパイラーの結果を有難うございます。
     結果を以下にまとめてみました。
     fujita さんの薄青の画面をエディタにコピーすると、スペースも入り難無く見られます。
     しかしそれを加工して投稿すると、スペースなどが消えて見難くなります。
     Reply を押すまでは、正常に表示しているのに。
     薄青の画面はどうやって作るのですか?

    extern volatile int* hogehoge;

    void hoge(void) {while (*hogehoge) ;}
    void hogera(void) {while (hogehoge) ;}


    extern int* volatile piyopiyo;

    void piyo(void) {while (*piyopiyo) ;}
    void piyora(void) {while (piyopiyo) ;}

    ----------------------------------------------

    「extern volatile int* hogehoge;」の場合

    _hoge:
    movw bc, !LOWW(_hogehoge)  この命令は1回のみ実行
    .BB@LABEL@2_1: ; bb1
    movw ax, 0x0000[bc]
    or a, x
    bnz $.BB@LABEL@2_1
    ret
     
    _hogera: 一度しかチェックしない。ゼロ以外なら、無限ループから抜けない。
    movw ax, !LOWW(_hogehoge)
    or a, x
    sknz       この命令は「ゼロで無ければ .BB@LABEL@3_2: に飛ぶ?」
    ret
    .BB@LABEL@3_2: ; bb1
    br $.BB@LABEL@3_2

    ---------------------------------------------

    「extern int* volatile piyopiyo;」の場合

    _piyo:
    .BB@LABEL@4_1: ; bb1
    movw bc, !LOWW(_piyopiyo) この命令は毎回実行
    movw ax, 0x0000[bc]
    or a, x
    bnz $.BB@LABEL@4_1
    ret

    _piyora:        何度もチェックしている
    .BB@LABEL@5_1: ; bb1
    movw ax, !LOWW(_piyopiyo)
    or a, x
    bnz $.BB@LABEL@5_1
    ret
     
     
     確かに違いが有りますね。インターネットで次の記事を見つけました。
     
    sunafukin2go.hatenablog.com/.../014148
    こういった場合にvolatile unsigned char *piAddr;をして、この最適化を抑制します。
    なお補足しておきますが、unsigned char * volatile piAddr; とするとアドレスが最適化されないという意味になります。
    基本的にはアドレスの指す先が最適化されないというのが一般的ではないかと思います。
     
     
     volatile の位置の違いによる差がよく分からない。
     「extern int* volatile piyopiyo;」の場合は、piyo と piyora の両方に利いて、「extern volatile int* hogehoge;」の場合は hoge と hogera の両方に利かなかったと言う事にならないかな。
     コンパイラを作る人の解釈の違い?
  • リカルドさん、こんにちは。藤田様、フォロー有難う御座います。

    リカルドさまwrote: said:
    >1 void hoge(void)
    2 {
    3 extern volatile int* hogehoge;
    4 while (*hogehoge) ;
    7 while (hogehoge) ;
    10 }

    hoge:
    movq .refptr.hogehoge(%rip), %rax
    movq (%rax), %rdx
    .L2: while (*hogehoge)に相当
    movl (%rdx), %eax
    testl %eax, %eax
    jne .L2
    .L3:              while (hogehoge)に相当
    jmp .L3       hogehoge がゼロでない事を何処で判断したの?
    .text
    リカルドさまwrote: said:
    >NULL は何のオブジェクトも指しませんが、逆に、何かのオブジェクトを指すポインタは NULL でないことは確定的です。前段の hogehoge の指す先をアクセスした以降では hogehoge の値は NULL ではないということで、無限ループとなります。

     NULL だったら関数を抜けると言う判断をどこかでしているのなら分かります。
     しかし、そんな事はしていないよね。
     NULL かどうか判断して、「NULLでした」と表示するプログラムは作れないと言うこと?
     ...略...
     コンパイルの動作がおかしいんじゃないかと言う話です。

    それは、このx86 Windows(あるいはLinux)用のGCCの最適化機能に関して私がリンクを書いたKMC(京都マイクロコンピュータ社)のスタッフさんが書かれたブログの以下の話に結びつくものではないかと思います。

    私は、これは GCC の問題というよりは、ポインタを NULL チェック無しでいきなり参照している Linux Kernel のコードの問題だと思います。しかし、Kernel などのセキュリティが重要なソフトウェアをビルドする際には、fno-delete-null-pointer-checks を付けておいた方が良さそうです。

    # あるいは、NULL チェックが行われていないポインタ参照全てにデフォルトのチェックコードを挿入するようなオプションがあれば良いのかもしれません。

    また、この最適化に関しては以下のリンク先のように、リカルドさんと同じく、いろいろ思うところのある人もいると思います。

    Qiita投稿(この人の投稿の真ん中あたり(余談の項より上で))
    qiita.com/AoiMoe/items/2554f78dc9c197d22109

  • > 薄青の画面はどうやって作るのですか?

    投稿の際に'Use rich formatting' を選び、「Tools」→「<> Source code」を選択して HTML 編集を行い、コードは <pre> ~ </pre> で囲んでいます。

  • >  NULL だったら関数を抜けると言う判断をどこかでしているのなら分かります。

    > しかし、そんな事はしていないよね。

    明示的に NULL チェックを行い、ポインタの値が NULL である可能性があることをコンパイラに伝えれば NULL だった場合は安全に関数を抜けるコードを吐きますよ。

    $ cat -n hoge.c
         1  void hoge(void)
         2  {
         3      extern volatile int* hogehoge;
         4      if (hogehoge) {
         5          while (*hogehoge) {
         6              ;
         7          }
         8      }
         9      while (hogehoge) {
        10          ;
        11      }
        12  }
    
    $ gcc -Wall -W -S -O2 hoge.c -o -
            .text
    hoge:
            movq    .refptr.hogehoge(%rip), %rax
            movq    (%rax), %rdx
            testq   %rdx, %rdx
            je      .L1
    .L5:
            movl    (%rdx), %eax
            testl   %eax, %eax
            jne     .L5
    .L4:
            jmp     .L4
    .L1:
            rep ret
    
    $
    
  • 解説をすると、

    extern volatile int* hogehoge;
    
    • hogehoge は「揮発性(勝手に内容が変わるかもしれない)な int のオブジェクト」を指すポインタ変数 

     

    void hoge(void)
    {
        while (*hogehoge) {
            ;
        }
    }
    
    • C 言語では特に指定がない限りは「関数内でオブジェクトの値は勝手に変わることはない」ことを前提とするので hogehoge の値は勝手には変わらない
    • hogehgoe の指す先のオブジェクト(int の値)は宣言により勝手に変わるかもしれない

     

    _hoge:
            movw bc, !LOWW(_hogehoge)       ; _hogehoge の値値を BC に読み出し
    .BB@LABEL@2_1:        ; bb1
            movw ax, 0x0000[bc]             ; BC の指すアドレスから 2バイトを AX に読み出し
            or a, x                         ; A に X を OR し、AX が 0 かを判定
            bnz $.BB@LABEL@2_1              ; 0 でなければ .BB@LABEL@2_1 に分岐
            ret                             ; 復帰
    
    • hogehoge の値が関数内で勝手に変わらないことを利用し hogehoge の値が格納されている _hogehoge を読み込むのはループ外で 1回のみとしている
    • hogehoge の指す先の内容は勝手に変わるかもしれないのでループ中に毎回読み出される

     

    void hogera(void)
    {
        while (hogehoge) {
            ;
        }
    }
    
    • hogehoge の値は関数内で勝手に変わることはない

     

    _hogera:
            movw ax, !LOWW(_hogehoge)       ; _hogehoge の値値を AX に読み出し
            or a, x                         ; A に X を OR し、AX が 0 かを判定
            sknz                            ; 0 でなければ次の命令を実行しない
            ret                             ; (AX が 0であれば)復帰
    .BB@LABEL@3_2:        ; bb1
            br $.BB@LABEL@3_2               ; .BB@LABEL@3_2 に分岐(無限ループ)
    
    • hogehoge の値が関数内で勝手に変わらないことを利用し hogehoge の値が格納されている _hogehoge を読み込むのはループ外で 1回のみとしている
    • hogehoge の値が 0(NULL) であれば速やかにこの関数から復帰する
    • hogehoge の値が 0(NULL) でない場合無限ループとなる

     

    extern int* volatile piyopiyo;
    
    • piyopiyo は int のオブジェクトを指すポインタ変数。揮発性なので piyopiyo の値は勝手に変わるかもしれない

     

    void piyo(void)
    {
        while (*piyopiyo) {
            ;
        }
    }
    
    • piyopiyo の値は宣言で揮発性となっているので関数内で勝手に変わるかもしれない

     

    _piyo:
    .BB@LABEL@4_1:        ; bb1
            movw bc, !LOWW(_piyopiyo)       ; _hogehoge の値値を BC に読み出し
            movw ax, 0x0000[bc]             ; BC の指すアドレスから 2バイトを AX に読み出し
            or a, x                         ; A に X を OR し、AX が 0 かを判定
            bnz $.BB@LABEL@4_1              ; 0 でなければ .BB@LABEL@2_1 に分岐
            ret                             ; 復帰
    
    • piyopiyo の値は勝手に変わるかもしれないので piyopiyo の値が格納されている _piyopiyo はループ中に毎回読み出される

     

    void piyora(void)
    {
        while (piyopiyo) {
            ;
        }
    }
    
    • piyopiyo の値は勝手には変わるかもしれない

     

    _piyora:
    .BB@LABEL@5_1:        ; bb1
            movw ax, !LOWW(_piyopiyo)       ; _hogehoge の値値を AX に読み出し
            or a, x                         ; A に X を OR し、AX が 0 かを判定
            bnz $.BB@LABEL@5_1              ; 0 でなければ .BB@LABEL@5_1 に分岐
            ret                             ; 復帰
    
    • piyopiyo の値は勝手に変わるかもしれないので piyopiyo の値が格納されている _piyopiyo はループ中毎回読み出される