7

小さなアカデミック OS を TriCore から ARM Cortex (Thumb-2 命令セット) に移植しています。スケジューラが機能するためには、スタックやリンク レジスタを変更せずに別の関数に直接 JUMP する必要がある場合があります。

TriCore (またはむしろ tricore-g++) では、このラッパー テンプレート (任意の 3 つの引数関数) が機能します。

template< class A1, class A2, class A3 > 
inline void __attribute__((always_inline)) 
JUMP3( void (*func)( A1, A2, A3), A1 a1, A2 a2, A3 a3 ) {
    typedef void (* __attribute__((interrupt_handler)) Jump3)( A1, A2, A3);
    ( (Jump3)func )( a1, a2, a3 );
}

//example for using the template:
JUMP3( superDispatch, this, me, next );

これにより、 の代わりにアセンブラ命令J(別名 JUMP)が生成さCALLれ、(それ以外の場合は通常の) C++ 関数にジャンプするときに、スタックと CSA は変更されませんsuperDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to)

ここで、ARM Cortex (または、arm-none-linux-gnueabi-g++) で同等の動作が必要です。つまり、 (リンクと交換を伴うBBRANCH) の代わりに (別名 BRANCH) 命令を生成します。BLXしかしinterrupt_handler、arm-g++ には属性がなく、同等の属性が見つかりませんでした。

だから私asm volatileはasmコードに頼って直接書いてみました:

template< class A1, class A2, class A3 > 
inline void __attribute__((always_inline)) 
JUMP3( void (*func)( A1, A2, A3), A1 a1, A2 a2, A3 a3 ) {
    asm volatile (
                  "mov.w r0, %1;"
                  "mov.w r1, %2;"
                  "mov.w r2, %3;"
                  "b %0;"
                            :
                            : "r"(func), "r"(a1), "r"(a2), "r"(a3)
                            : "r0", "r1", "r2"
                  );
}

これまでのところ、少なくとも私の理論では、とても良いです。Thumb-2 では、関数の引数をレジスタ (この場合は r0..r2) で渡す必要があるため、動作するはずです。

しかし、その後リンカーは死ぬ

undefined reference to `r6'

asmステートメントの閉じ括弧に...そして、どうすればよいかわかりません。OK、私は C++ の専門家ではありません。asm 構文はあまり単純ではありません。arm-g++ を修正__attribute__するためのヒントは 1 つの方法であり、asm コードを修正するためのヒントは別の方法です。a1..a3もう1つの方法は、asmステートメントが入力されたときにすでにレジスターにある必要があることをコンパイラーに伝えることr0..r2です(少し調べましたが、ヒントは見つかりませんでした)。

4

2 に答える 2

1

リンク エラーは、分岐命令を使用してポインターにジャンプしようとしたために発生します。これにより、シンボルではないb r6ためリンクに失敗するのようなコードが生成されます。r6分岐命令を に変更するmov pc,%0と、正しいジャンプが得られるはずです。

コメントで述べたように、ARM 割り込みハンドラーはinterrupt属性を使用して宣言されますが、おわかりのように、これは呼び出し方法には影響しません。これは、たまたま TriCore で正しいことを行ったプラットフォーム固有のトリックだったと思います。

volatile命令でregister int reg0 asm("r0") = a1;はなく、GCC の拡張構文を使用して、特定のレジスタで変数を宣言してみることができます。movこれにより、コンパイラがより良いコードを生成できるようになる場合があります。

于 2010-03-29T18:11:09.843 に答える
0

さて、私は今何が間違っていたのかを理解しました。

別の関数に直接ジャンプするという概念全体は、ARM Cortex では意味がありません。TriCore は、別の関数を呼び出すたびにコンテキスト保存領域 (CSA) を使用して CPU コンテキスト全体を保存するためです。CALLこれは、それぞれに合わせて成長し、それぞれに合わせて縮小する 2 番目の独立したスタックと考えてくださいRET。また、各 CSA ブロックのサイズは一定です。

一方、ARM Cortexは単純な標準スタックを使用します(システムスタックとスレッドスタックについては知っていますが、ここでは重要ではありません)-そしてGCCは各機能に必要なものを保存するだけなので、各フレームには異なるサイズ。したがって、ジャンプ先の関数が使用する不揮発性レジスタの保存を開始するとすぐにスタックが破損するため、単に別の関数にジャンプすることは問題外です。

そして、r6 への未定義の参照によるリンカ エラーについては...まあ、命令セットのドキュメントをもっと注意深く読むべきでした。は即値アドレスBの無条件分岐であり、レジスタ内の分岐アドレスを期待する命令です。説明書に「取引所のある分岐」と短く書かれている説明書にだまされました。何も交換したくなかったので、単純なジャンプが欲しかったので、それ以上読みませんでした。BXBX

BというわけでBX、コード内で交換後asm volatile、コードをコンパイルしました。しかし、上で指摘したように、コンセプト全体が期待どおりに機能することはありません。他の誰かがそのコードのユースケースを見つけることができるかもしれません。私は今、古典的な関数呼び出しに頼らなければなりません...

于 2010-03-31T16:35:02.840 に答える