【问题标题】:inline asm with sideeffects有副作用的内联汇编
【发布时间】:2014-01-31 17:05:36
【问题描述】:

我想使用带有 clang 3.4 的 ARMv7 的内联汇编,以便编写访问 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

正如您在 &lt;bit_fiddle&gt; 中看到的,mrcmcr 指令之间没有任何内容。还可以查看foo 如何在生成的机器代码中无法将a+b 加在一起。

【问题讨论】:

  • 尝试用clang-3.4 -target armv7a-none-eabi test.c -o test -O0编译
  • 我只是混淆了“r”和“=r”约束吗?应该反过来吗?

标签: c assembly arm clang inline-assembly


【解决方案1】:

你很接近。 "=" 表示操作数是只写的:

在使用约束时,为了更精确地控制 约束,GCC 为我们提供了约束修饰符。主要使用 约束修饰符是

"=" :表示该操作数对该指令是只写的;这 先前的值被丢弃并替换为输出数据。 “&“ : 方法 这个操作数是一个 earlyclobber 操作数,之前修改过 该指令使用输入操作数完成。因此,这 操作数不得位于用作输入操作数的寄存器中或 作为任何内存地址的一部分。输入操作数可以绑定到 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)

【讨论】:

    【解决方案2】:

    我以错误的方式使用了“r”和“=r”约束。 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
    

    挺好看的……

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-20
      • 1970-01-01
      • 1970-01-01
      • 2018-07-28
      • 2013-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多