0

ブランチを持つ asm インラインで c の外部関数を呼び出そうとしています。arm m0 命令セットにコンパイルしていますが、不適切な式が返されます。

コードは次のとおりです。

__asm volatile (
                "   cmp     r3,#0                   \n"                     
                "   b %[my_function]                \n" //Call function
                "   bx r14                          \n"
                : // no output
                : [my_function] "i" (my_function) // input
                : "r0" // clobber
            );

戻り値は次のとおりです。

/tmp/ccICkDIE.s: Assembler messages:
/tmp/ccICkDIE.s:152: Error: bad expression -- `b #my_function'

私たちは何をする必要がありますか?

4

2 に答える 2

0

あなたはBL指示が欲しいです。これが「分岐とリンク」です。ジャンプしてリターンアドレスを に格納しr14ます。

しかし、まだ問題があります...そうすると、必要なBLものが破壊r14されます。次の作業を行った後でも、まだやるべきことがあります。

stmfd   sp!,{v1-v6,lr}              // preserve caller registers
bl      %[my_function]              // call function
ldmfd   sp!,{v1-v6,pc} @std         // restore caller registers and return

さらに調査が必要です。コンパイルされた関数を逆アセンブルし、インライン asm の周りにどのようなラッパーがあるかを確認し、それに応じて調整することをお勧めします。それはあなたのstmfd/ldmfdためにするかもしれません。クロバーとしてマークr14してみてください。

だけで良いかもしれませんBL。リストアなしではBX、無限ループまたは予測不能な結果が生じる可能性があります。私はそれをオフにします

于 2015-11-04T23:22:45.303 に答える
0

以下を作成した後、 ethernut のチュートリアルを思い出しました。彼はほとんど同じ答えを持っています。

asm volatile(
    "mov lr, %1\n\t"
    "bx %0\n\t"
    : : "r" (main), "r" (JMPADDR));

OPはこのチュートリアルを読むのに適しています。「m0」とは対照的に、従来のARM用ですが。


制約を使用し'r'てアドレスをレジスタに配置し、それに分岐することができます。

は、オンライン コンパイラ Godbolt にあります。

extern int my_function(void);

void f(void)
{
__asm volatile (
                "   cmp     r3,#0                   \n"                     
                "   b %[my_function]                \n" //Call function
                "   bx r14                          \n"
                : // no output
                : [my_function] "r" (my_function) // input
                : "r0" // clobber
            );

 }

出力により、

f():
    ldr r3, .L2
       cmp     r3,#0                   
   b r3                
   bx r14                          

    bx  lr
.L2:
    .word   my_function()

出力にいくつかの問題が見られます。r14 はlrであり、b r3は制御を直接転送し、 の呼び出し元に戻りfます。cmp r3, #0完全に不要なようです(質問のコンテキストが限られている場合)。

上記のサンプルは質問に答えており、末尾呼び出しマクロやその他の用途に使用できますが、明らかにいくつかの作業が必要です。次のような関数ポインタ

int (*g)(void) = my_function;

また、GCC 拡張アセンブラーへのパラメーター'my_function'としても機能します。

もう 1 つの方法は、'C' マクロ文字列連結を使用することです。これが開始サンプルです。

#define xstr(s) str(s)
#define str(s) #s
#define TAIL_CALL(func) __asm volatile(" b  " str(func) "\n")

ほとんどのコード サイズ (ジャンプ距離) では、ブランチは解決できます (4MB?)。関数ポインター方式を使用する場合、問題はありません。

于 2015-11-05T14:08:11.470 に答える