内联asm有副作用

时间:2014-01-31 17:05:36

标签: c assembly arm clang inline-assembly

我想使用内联asm for ARMv7 with clang 3.4,以编写访问CPU控制寄存器的低级代码。作为测试,我编写了一个程序,它从寄存器中读取,有条件地摆弄一些位,并写回新值。

但是,当我查看生产的机器代码时,整个小工具已被优化掉了。显然我没有使用正确的asm约束来告诉clang写入寄存器的结果取决于写入的内容。 (我只使用了一个简单的“volatile”修饰符)。

我应该如何编写内联asm代码以便clang生成正确的asm?这是代码test.c

typedef unsigned int uint32_t;

// code that reads and writes the ID_PFR1 register

uint32_t read_ID_PFR1() {
  uint32_t a;
  asm volatile ("mrc     p15, 0, %0, c0, c1, 1" : : "r"(a) : );
  return a;
}

void write_ID_PFR1(uint32_t a) {
  asm volatile ("mcr     p15, 0, %0, c0, c1, 1" :"=r"(a) :  :  );
}

// regular c code that modifies the register

uint32_t foo(uint32_t b) {
  uint32_t a;
  a = read_ID_PFR1();
  write_ID_PFR1(b);
  return a+b;
}

void bit_fiddle() {
  uint32_t a;
  a = read_ID_PFR1();
  if ((a & 0x3) == 1) {
    a |= 1<<2;
  }
  a |= 1<<3;
  write_ID_PFR1(a);
}

我用

编译了它
clang-3.4 -target armv7a-none-eabi test.c -o test -O3

这是生成的机器代码

$ arm-linux-gnueabi-objdump -S test

test:     file format elf32-littlearm


Disassembly of section .text:

00000000 <read_ID_PFR1>:
   0:   ee100f31    mrc 15, 0, r0, cr0, cr1, {1}
   4:   e12fff1e    bx  lr

00000008 <write_ID_PFR1>:
   8:   ee000f31    mcr 15, 0, r0, cr0, cr1, {1}
   c:   e12fff1e    bx  lr

00000010 <foo>:
  10:   ee100f31    mrc 15, 0, r0, cr0, cr1, {1}
  14:   ee000f31    mcr 15, 0, r0, cr0, cr1, {1}
  18:   e12fff1e    bx  lr

0000001c <bit_fiddle>:
  1c:   ee100f31    mrc 15, 0, r0, cr0, cr1, {1}
  20:   ee000f31    mcr 15, 0, r0, cr0, cr1, {1}
  24:   e12fff1e    bx  lr

正如您在<bit_fiddle>中看到的那样,mrcmcr指令之间没有任何内容。另请参阅foo未能在生成的计算机代码中将a+b加在一起。

2 个答案:

答案 0 :(得分:1)

我正在使用&#34; r&#34;和&#34; = r&#34;以错误的方式约束。 write应该有输入约束,read应该有输出约束。

这是做到这一点的方法:

uint32_t read_ID_PFR1() {
  uint32_t a;
  asm volatile ("mrc     p15, 0, %0, c0, c1, 1" : "=r"(a) : : );
  return a;
}

void write_ID_PFR1(uint32_t a) {
  asm volatile ("mcr     p15, 0, %0, c0, c1, 1" : : "r"(a) :  );
}

以下是为bit_fiddle生成的代码:

00000020 <bit_fiddle>:
  20:   ee100f31    mrc 15, 0, r0, cr0, cr1, {1}
  24:   e2001003    and r1, r0, #3
  28:   e3510001    cmp r1, #1
  2c:   03800004    orreq   r0, r0, #4
  30:   e3800008    orr r0, r0, #8
  34:   ee000f31    mcr 15, 0, r0, cr0, cr1, {1}
  38:   e12fff1e    bx  lr

非常好......

答案 1 :(得分:1)

您已接近"=":表示此操作数是只写的:

  

使用约束时,可以更精确地控制效果   约束,GCC为我们提供了约束修饰符。最常用的   

是约束修饰符      

“=”:表示该操作数对该指令是只写的;该   先前的值被丢弃并由输出数据替换。 “&安培;” :手段   这个操作数是一个早期的操作数,之前被修改过   使用输入操作数完成指令。因此,这   操作数可能不在用作输入操作数的寄存器中   作为任何内存地址的一部分。输入操作数可以绑定到   earlyclobber操作数如果它只用作输入之前发生   早期的结果是写的。

输入和输出由列表中以:

分隔的顺序确定
  asm ( assembler template 
       : output operands                  /* optional */
       : input operands                   /* optional */
       : list of clobbered registers      /* optional */
       );
  1. 阅读gcc inline assembly HOWTO
  2. Copy content of C variable into a register (GCC)