GNURX用のCCRXmachine.hとCCRXmachine.cというソースがe2 studioフォルダにありました(内容は概ね名前から予想される通りのものでした)

こんにちは。NoMaYです。

e2 studio v6.3.0がリリースされていたので、インストールして幾つかプロジェクトを作成して、いつものようにe2 studioのインストールフォルダを眺めていたら、CCRXmachine.hとCCRXmachine.cというファイルがあることに気付きました。中を見てみると、概ねファイル名から予想される通りのソースファイルでした。(今までのe2 studioのインストールフォルダを見直してみたところ、以前からあったことが分かりましたが、今まで気付きませんでした。) ただ、一部コメントアウトされているものがあったり、以前に別スレッド『GUNRX用プロジェクトのスマートコンフィグレータのBSPを見ていて気付いた変な移植コード』で話題にしたことと同じ元のコードの意図を理解していない書き換えがあったり、ちょっと惜しいような気もしました。

e2 studioインストールフォルダ\internal\projectgen\rx\Generate\CCRXConversion\inc\CCRXmachine.h



e2 studioインストールフォルダ\internal\projectgen\rx\Generate\CCRXConversion\inc\CCRXmachine.c



Parents
  • こんにちは。NoMaYです。

    GNURX(というかGCC)のインラインアセンブラは難しいですね。(それでも、ようやく、きっとこれで合っているに違いない、という感覚が湧き上がってくることがあるレベルになって来ましたが、、、) 以前に投稿したGNURXでの今回のCCRXmachine2.hのxchg()関数のインラインアセンブラ記述の件で、何か腑に落ちない感覚があったのですが、しつこく調べていたところ、stackoverflowというサイトに投稿されていた以下のスレッドを見て、やっと直接の原因を理解出来ました(と思う)。そして、GCC本家のドキュメントを見直してみると、確かにそう書いてありました。つまり、GCCは、より良いコードを生成する為、アセンブラ記述部では変数への書き込みがあった時点で変数からの読み出しは全て完了しているものと仮定してコードを生成する、そういう実装になっている、ということでした。(でも、その仮定が正しくない場合もあるので、その動作を変更するアセンブラ記述も出来るようになっていました。)

    以前の投稿で腑に落ちなかった記述

    static __inline__ void xchg(signed long *data1, signed long *data2) __attribute__((always_inline));
    static __inline__ void xchg(signed long *data1, signed long *data2)
    {
    /* CC-RX V2.03
            MOV.L [R1], R14
            XCHG [R2].L, R14
            MOV.L R14, [R1]
            RTS
    */
        signed long temp; /* = *data1; */
        __asm__ volatile
        (
            /* AssemblerTemplate */
            "MOV.L [%[R1]], %[R14]\n\t"
            "XCHG [%[R2]].L, %[R14]\n\t"
            "MOV.L %[R14], [%[R1]]"
            : /* OutputOperands */
                [R14] "=r" (temp)
            : /* InputOperands */
                [R1] "r" (data1),
                [R2] "r" (data2)

        );
        /* *data1 = temp; */
        return;
    /* GNURX 2018q1
      92 0010 EC 12                         MOV.L [r1], r2 ← r2を壊してしまった
      93 0012 06 A0 10 22                   XCHG [r2].L, r2
      94 0016 E3 12                         MOV.L r2, [r1]
      97 0018 02                            rts
    */
    }

    そして以下のことが思い浮かんだが、、、

    ● インラインアセンブラ記述部のInputOperandとしてポインタの値だけというのは変だよね。少なくとも以下の何れか1つは一緒に指定されていないと変だよね。
    (1) OutputOperandにポインタの値を加工した値の出力
    (2) InputOperandにポインタの値(もしくは加工した値)によるメモリのリード
    (3) OutputOperandにポインタの値(もしくは加工した値)によるメモリのライト(もしくはリードモデファイライト)
    ●この何れも指定されていないのであれば、ポインタの値は実質使われていない、ということだから壊しても構わないよね。

    やっと直接の原因を理解出来た(と思う、、、)

    stackoverflow.com/questions/15819794/when-to-use-earlyclobber-constraint-in-extended-gcc-inline-assembly


    GCC本家のドキュメントをにも確かにそう書いてありました

    gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#index-asm-output-operands


    gcc.gnu.org/onlinedocs/gcc/Modifiers.html#index-earlyclobber-operand


    ですので、今回のCCRXmachine2.hの手元の作業中のソースでは、xchg()関数は以下の記述に至っています。(なお、以下のOutputOperandsとInputOperandsの制約はAssemblerTemplateの3行に渡っての制約ですので、xchg命令単体としての制約とは異なります。)

    #ifndef xchg
    #define xchg(data1, data2) ccrx_machine_h_xchg(data1, data2)
    #endif
    static __inline__ void ccrx_machine_h_xchg(signed long *data1, signed long *data2) __attribute__((always_inline));
    static __inline__ void ccrx_machine_h_xchg(signed long *data1, signed long *data2)
    {
    /* CC-RX V2.03
            MOV.L [R1], R14
            XCHG [R2].L, R14
            MOV.L R14, [R1]
            RTS
    */
        signed long temp;
        __asm__ volatile
        (
            /* AssemblerTemplate */
            "MOV.L [%[R1]], %[R14]"     "\n\t"
            "XCHG [%[R2]].L, %[R14]"    "\n\t"
            "MOV.L %[R14], [%[R1]]"     "\n\t"
            : /* OutputOperands */
                [R14] "=&r" (temp),
                /***/ "+m" (*data1),
                /***/ "+m" (*data2)
            : /* InputOperands */
                [R1] "r" (data1),
                [R2] "r" (data2)

        );
        return;
    /* GNURX 2018q1
      92 0010 EC 15                         MOV.L [r1], r5
      93 0012 06 A0 10 25                   XCHG [r2].L, r5
      94 0016 E3 15                         MOV.L r5, [r1]
      97 0018 02                            rts
    */
    }

    ちなみに、"+m" (*data1)や"+m" (*data2)の制約が無いと、私の前の投稿での__builtin_rx_rmpa()関数で滅茶苦茶なコードが生成された件と同様のトラブルが、複雑なソースにxchg()関数がインライン展開された場合に発生する可能性があると思われます。(同様のトラブルの別の例を挙げれば、変数(or 内蔵周辺)にvolatile宣言を付け忘れた時に、コンパイラの最適化レベルによっては、volatile宣言を忘れた変数(or 内蔵周辺)に対してのリードやライトが省略されていたり思わぬ位置に移動していたりする、ということが挙げられると思います。)

Reply
  • こんにちは。NoMaYです。

    GNURX(というかGCC)のインラインアセンブラは難しいですね。(それでも、ようやく、きっとこれで合っているに違いない、という感覚が湧き上がってくることがあるレベルになって来ましたが、、、) 以前に投稿したGNURXでの今回のCCRXmachine2.hのxchg()関数のインラインアセンブラ記述の件で、何か腑に落ちない感覚があったのですが、しつこく調べていたところ、stackoverflowというサイトに投稿されていた以下のスレッドを見て、やっと直接の原因を理解出来ました(と思う)。そして、GCC本家のドキュメントを見直してみると、確かにそう書いてありました。つまり、GCCは、より良いコードを生成する為、アセンブラ記述部では変数への書き込みがあった時点で変数からの読み出しは全て完了しているものと仮定してコードを生成する、そういう実装になっている、ということでした。(でも、その仮定が正しくない場合もあるので、その動作を変更するアセンブラ記述も出来るようになっていました。)

    以前の投稿で腑に落ちなかった記述

    static __inline__ void xchg(signed long *data1, signed long *data2) __attribute__((always_inline));
    static __inline__ void xchg(signed long *data1, signed long *data2)
    {
    /* CC-RX V2.03
            MOV.L [R1], R14
            XCHG [R2].L, R14
            MOV.L R14, [R1]
            RTS
    */
        signed long temp; /* = *data1; */
        __asm__ volatile
        (
            /* AssemblerTemplate */
            "MOV.L [%[R1]], %[R14]\n\t"
            "XCHG [%[R2]].L, %[R14]\n\t"
            "MOV.L %[R14], [%[R1]]"
            : /* OutputOperands */
                [R14] "=r" (temp)
            : /* InputOperands */
                [R1] "r" (data1),
                [R2] "r" (data2)

        );
        /* *data1 = temp; */
        return;
    /* GNURX 2018q1
      92 0010 EC 12                         MOV.L [r1], r2 ← r2を壊してしまった
      93 0012 06 A0 10 22                   XCHG [r2].L, r2
      94 0016 E3 12                         MOV.L r2, [r1]
      97 0018 02                            rts
    */
    }

    そして以下のことが思い浮かんだが、、、

    ● インラインアセンブラ記述部のInputOperandとしてポインタの値だけというのは変だよね。少なくとも以下の何れか1つは一緒に指定されていないと変だよね。
    (1) OutputOperandにポインタの値を加工した値の出力
    (2) InputOperandにポインタの値(もしくは加工した値)によるメモリのリード
    (3) OutputOperandにポインタの値(もしくは加工した値)によるメモリのライト(もしくはリードモデファイライト)
    ●この何れも指定されていないのであれば、ポインタの値は実質使われていない、ということだから壊しても構わないよね。

    やっと直接の原因を理解出来た(と思う、、、)

    stackoverflow.com/questions/15819794/when-to-use-earlyclobber-constraint-in-extended-gcc-inline-assembly


    GCC本家のドキュメントをにも確かにそう書いてありました

    gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#index-asm-output-operands


    gcc.gnu.org/onlinedocs/gcc/Modifiers.html#index-earlyclobber-operand


    ですので、今回のCCRXmachine2.hの手元の作業中のソースでは、xchg()関数は以下の記述に至っています。(なお、以下のOutputOperandsとInputOperandsの制約はAssemblerTemplateの3行に渡っての制約ですので、xchg命令単体としての制約とは異なります。)

    #ifndef xchg
    #define xchg(data1, data2) ccrx_machine_h_xchg(data1, data2)
    #endif
    static __inline__ void ccrx_machine_h_xchg(signed long *data1, signed long *data2) __attribute__((always_inline));
    static __inline__ void ccrx_machine_h_xchg(signed long *data1, signed long *data2)
    {
    /* CC-RX V2.03
            MOV.L [R1], R14
            XCHG [R2].L, R14
            MOV.L R14, [R1]
            RTS
    */
        signed long temp;
        __asm__ volatile
        (
            /* AssemblerTemplate */
            "MOV.L [%[R1]], %[R14]"     "\n\t"
            "XCHG [%[R2]].L, %[R14]"    "\n\t"
            "MOV.L %[R14], [%[R1]]"     "\n\t"
            : /* OutputOperands */
                [R14] "=&r" (temp),
                /***/ "+m" (*data1),
                /***/ "+m" (*data2)
            : /* InputOperands */
                [R1] "r" (data1),
                [R2] "r" (data2)

        );
        return;
    /* GNURX 2018q1
      92 0010 EC 15                         MOV.L [r1], r5
      93 0012 06 A0 10 25                   XCHG [r2].L, r5
      94 0016 E3 15                         MOV.L r5, [r1]
      97 0018 02                            rts
    */
    }

    ちなみに、"+m" (*data1)や"+m" (*data2)の制約が無いと、私の前の投稿での__builtin_rx_rmpa()関数で滅茶苦茶なコードが生成された件と同様のトラブルが、複雑なソースにxchg()関数がインライン展開された場合に発生する可能性があると思われます。(同様のトラブルの別の例を挙げれば、変数(or 内蔵周辺)にvolatile宣言を付け忘れた時に、コンパイラの最適化レベルによっては、volatile宣言を忘れた変数(or 内蔵周辺)に対してのリードやライトが省略されていたり思わぬ位置に移動していたりする、ということが挙げられると思います。)

Children
  • NoMaYさん、fujitaさん

    シェルティです。こんにちは。

    種々ご協力いただきありがとうございます。
    問題点の整理と対応状況をまとめました。

    ① GCC -O2指定時のxchg前後のmov命令順が不正
       →ルネサスから報告済。Workaroundがないかさらに問い合わせています。
        fujitaさんにも報告いただいたようで深謝いたします。
       何か見解がでてきたら、私からこのスレッドに報告します。
        また、ルネサスの本サイトに対する公式見解の出し方が不明瞭で申し訳なく思います。
        この点も可能な限り改善していきたいと私は考えております。

    ② 排他制御可能なコードが出力されない 
       →どのような対策が一番良いか検討を重ねる必要性を感じます。
        私が開発を進めているAmazon FreeRTOS関連予算の中でGCC対応を検討する項目がありますので
        そのなかでGCC用のBSPがどうあるべきか(GCCに対してフィードバックが要るのか)を
        検証します。当面はNoMaYさんにご提示いただいたコードを使わせていただき
        Amazon FreeRTOSのコードの一部とさせていただくつもりですが、
        引き続き相談させていただき、最適解を導いていきたいと思います。
         #私自身はアセンブラに弱く(というより管理ばかりでソフト全般弱くなってしまいました)
          専門家に助力を依頼しているところです。

    ③ e2 studioのCC-RX→GCCのソースコンバータ(command.h等)のラッパー関数xchg()は組込み関数を呼ぶように修正中です。

    以上です