2

ARM Cortex-M3用のIARコンパイラは、インラインアセンブリを提供します。特定の関数のアドレスをスタック上の場所に格納するにはどうすればよいですか?

Cコードはこれを望みます

void tick();

void bar()
{
    int x;
    // modify a value on stack
    (&x)[4] = &tick;
}

これは一般的に機能しますが、リリースビルドのコンパイラによって最適化されます。インラインアセンブリでコーディングしようとしました

void bar()
{
    int x;
    asm("ldr r0,%0" : : "i" (&tick));
    asm("str r0,[sp+#0x10];
}

ldr命令はIARコンパイラによって受け入れられません。問題は、この命令にはレジスタとオフセットを備えたアドレッシングモードが必要なことです。関数の実際のアドレスは関数のtick後ろに格納され、ldr命令は実際のアドレスを保持するメモリ位置へのオフセットのみを保持します。分解は次のようになります。

    ldr r0,??tick_address
    str r0,[sp+#0x10]
    bx lr ; return
??tick_address dd tick

tickスタック操作に使用するために、レジスタのアドレスをすぐに取得するにはどうすればよいですか?

4

2 に答える 2

2

GNU GCCインラインアセンブリは、次のような疑似空のステートメントを介して単なる割り当てを行うことができます。asm()

asm("" : "=r"(foo) : "0"(tick));

これはコンパイラに次のように伝えます。

  1. 変数fooは、インラインアセンブリブロックの後にレジスタから取得されます
  2. 変数tickは、同じレジスタ(引数0)で渡されます。

使用するレジスタの実際の選択は、完全にコンパイラに任されています。

ここでの秘訣は、出力入力の制約です。(唯一の)入力を出力にエイリアスするだけで、コンパイラーが独自に適切なレジスターを選択し、それぞれの変数をロード/ストアするために必要な命令を生成します。 「実際の」インラインアセンブリコードの前/後。あなたもすることができます:

asm("" : "=r"(foo1), "=r"(foo2) : "0"(tick1) , "1"(tick2));

1つのインラインアセンブリステートメントで2つの「割り当て」を実行します。

このコンパイラによって生成された「入力を設定し、出力を取得する」コード生成は、実際のインラインアセンブリが空の場合でも発生します(ここのように)。

PC別の例:現在のプログラムカウンター(レジスター)を読み取りたいとします。これは、ARMで2つの異なるインラインアセンブリステートメントを介して実行できます。

asm("" : "=pc"(foo));
asm("mov %0, PC" : "=r"(foo));

これは100%同一ではありません。2番目のケースでは、コンパイラーは、の後に表示fooしたいレジスターが何であれ、そこでそれを見つけることを知っています。前者の場合、コンパイラはステートメントのに使用することを認識しているため、から取得する必要があります。2つの違いは、次の場合に発生します。asmfoo PC

uintptr_t *val;
uintptr_t foo;

asm("" : "=pc"(foo));
*val = foo;

この場合、コンパイラは、asmの後にあるstr [R...], PCことがわかっているため、これを単一に変換できることを識別できる可能性があります。あなたはこれを経由して書いたのですかfoopc

asm("mov %0, PC" : "=r"(foo));
*val = foo;

R0コンパイラーは( / R1for foo/を選択すると仮定してval)作成することを余儀なくされます:

MOV R0, PC
STR [R1], R0

この動作のドキュメントは、主にGCCマニュアルの「拡張ASM」セクションにあります。考案されたcombine手順の例を参照してください。

于 2013-03-27T17:14:24.657 に答える
0

xコードには変数の割り当てがないため、その値は未定義でありfoo、変更するために未定義の値に設定する必要はありませんfoo

値は、コンパイラが実装に使用すると想定するメモリ位置ではなく、変数に割り当てる必要があります。

于 2013-03-26T20:16:09.507 に答える