我想使用内联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>
中看到的那样,mrc
和mcr
指令之间没有任何内容。另请参阅foo
未能在生成的计算机代码中将a+b
加在一起。
答案 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 */
);