5

我正在研究使用 GCC 编译内联 ARM 程序集的概述中的一个示例。我使用的是 llvm-gcc 4.2.1 而不是 GCC,我正在编译以下 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

这两个 NOP 位于它们各自的 @APP/@NO_APP 对之间。我的期望是,asm()由于 -O3 标志,没有 volatile 关键字的语句将被优化不存在,但显然两个内联汇编语句都存在。

为什么该asm("mov r0, r0")行没有被识别为 NOP 并被删除?

4

2 回答 2

5

正如MysticalMārtiņš Možeiko所描述的,编译器不会优化代码;即,更改说明。编译器优化的是指令的调度时间。当你使用volatile时,编译器不会重新调度。在您的示例中,重新安排将在printf.

编译器可能进行的另一个优化是让C值为您注册。寄存器分配对优化非常重要。这不会优化assembler,但允许编译器对函数内的其他代码做一些明智的事情。

要查看效果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;
}

这两个分支具有相同的代码,除了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”代码相反。由于管道衬里,某些处理器上的第二个分支较慢。编译器更喜欢

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

不要顺序执行。

Ethernut ARM 教程是一个极好的资源。然而,优化是一个有点重载的词。编译器不分析汇编器,只分析参数和代码将在哪里发出

于 2013-02-28T01:55:02.623 回答
1

volatile如果asm语句没有声明输出,则隐含。

于 2017-11-18T10:24:50.393 回答