5

GCC を使用してインライン ARM アセンブリをコンパイルするこの概要の例に取り組んでいます。GCC ではなく、llvm-gcc 4.2.1 を使用しており、次の C コードをコンパイルしています。

#include <stdio.h>
int main(void) {
    printf("Volatile NOP\n");
    asm volatile("mov r0, r0");
    printf("Non-volatile NOP\n");
    asm("mov r0, r0");
    return 0;
}

次のコマンドを使用します。

llvm-gcc -emit-llvm -c -o compiled.bc input.c
llc -O3 -march=arm -o output.s compiled.bc

私の output.s ARM ASM ファイルは次のようになります。

    .syntax unified
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .file   "compiled.bc"
    .text
    .globl  main
    .align  2
    .type   main,%function
main:                                   @ @main
@ BB#0:                                 @ %entry
    str lr, [sp, #-4]!
    sub sp, sp, #16
    str r0, [sp, #12]
    ldr r0, .LCPI0_0
    str r1, [sp, #8]
    bl  puts
    @APP
    mov r0, r0
    @NO_APP
    ldr r0, .LCPI0_1
    bl  puts
    @APP
    mov r0, r0
    @NO_APP
    mov r0, #0
    str r0, [sp, #4]
    str r0, [sp]
    ldr r0, [sp, #4]
    add sp, sp, #16
    ldr lr, [sp], #4
    bx  lr
@ BB#1:
    .align  2
.LCPI0_0:
    .long   .L.str

    .align  2
.LCPI0_1:
    .long   .L.str1

.Ltmp0:
    .size   main, .Ltmp0-main

    .type   .L.str,%object          @ @.str
    .section    .rodata.str1.1,"aMS",%progbits,1
.L.str:
    .asciz   "Volatile NOP"
    .size   .L.str, 13

    .type   .L.str1,%object         @ @.str1
    .section    .rodata.str1.16,"aMS",%progbits,1
    .align  4
.L.str1:
    .asciz   "Non-volatile NOP"
    .size   .L.str1, 17

2 つの NOP は、それぞれの @APP/@NO_APP ペアの間にあります。私の予想ではasm()、volatile キーワードのないステートメントは、-O3 フラグのために最適化されなくなりますが、明らかに両方のインライン アセンブリ ステートメントが存続します。

asm("mov r0, r0")行が認識されず、NOP として削除されないのはなぜですか?

4

2 に答える 2

5

MysticalとMārtiņšMožeikoが説明ているように、コンパイラはコードを最適化しません。つまり、指示を変更します。コンパイラが最適化するのは、命令がスケジュールされたときです。を使用するvolatileと、コンパイラは再スケジュールしません。あなたの例では、再スケジュールはの前または後に移動しprintfます。

コンパイラーが行う可能性のある他の最適化は、 C値を取得して登録することです。レジスタ割り当ては、最適化にとって非常に重要です。これはアセンブラを最適化しませんが、コンパイラが関数内の他のコードで賢明なことを実行できるようにします。

の効果を確認するためにvolatile、ここにいくつかのサンプルコードがあります。

int example(int test, int add)
{
  int v1=5, v2=0;
  int i=0;
  if(test) {
    asm volatile("add %0, %1, #7" : "=r" (v2) : "r" (v2));
    i+= add * v1;
    i+= v2;
  } else {
    asm ("add %0, %1, #7" : "=r" (v2) : "r" (v2));
    i+= add * v1;
    i+= v2;
  }
  return i;
}

2つのブランチは、を除いて同じコードを持っていvolatileます。 gcc4.7.2は、ARM926に対して次のコードを生成します。

example:
   cmp  r0, #0
   bne  1f           /* branch if test set? */
   add  r1, r1, r1, lsl #2
   add  r0, r0, #7   /* add seven delayed */
   add  r0, r0, r1
   bx   lr
1: mov  r0, #0       /* test set */
   add  r0, r0, #7   /* add seven immediate */
   add  r1, r1, r1, lsl #2
   add  r0, r0, r1
   bx   lr

注:アセンブラーの分岐は「C」コードに逆になります。一部のプロセッサでは、パイプのライニングが原因で2番目の分岐が遅くなります。コンパイラはそれを好みます

   add  r1, r1, r1, lsl #2
   add  r0, r0, r1

順番に実行しないでください。

EthernutARMチュートリアルは優れたリソースです。ただし、optimizeは少しオーバーロードされた単語です。コンパイラーはアセンブラーを分析せず、引数とコードが発行される場所のみを分析します

于 2013-02-28T01:55:02.623 に答える