有副作用的内联汇编

lar*_*rsr 3 c assembly arm clang inline-assembly

我想对带有 clang 3.4 的 ARMv7 使用内联 asm,以便编写访问 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);
}
Run Code Online (Sandbox Code Playgroud)

我编译它

clang-3.4 -target armv7a-none-eabi test.c -o test -O3
Run Code Online (Sandbox Code Playgroud)

这是生成的机器代码

$ 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
Run Code Online (Sandbox Code Playgroud)

正如您在 中看到的<bit_fiddle>mrcmcr说明之间没有任何内容。并查看如何在生成的机器代码中foo添加失败a+b

lar*_*rsr 5

我以错误的方式使用“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) :  );
}
Run Code Online (Sandbox Code Playgroud)

这是为 生成的代码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
Run Code Online (Sandbox Code Playgroud)

很不错...


0x9*_*x90 5

你很近。这"="意味着操作数是只写的:

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

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

输入和输出由列表中的顺序决定 :

  asm ( assembler template 
       : output operands                  /* optional */
       : input operands                   /* optional */
       : list of clobbered registers      /* optional */
       );
Run Code Online (Sandbox Code Playgroud)
  1. 阅读gcc 内联汇编 HOWTO
  2. 将 C 变量的内容复制到寄存器 (GCC)