Zek*_*eke 5 assembly gcc arm llvm
我正在编写使用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;
}
Run Code Online (Sandbox Code Playgroud)
使用以下命令:
llvm-gcc -emit-llvm -c -o compiled.bc input.c
llc -O3 -march=arm -o output.s compiled.bc
Run Code Online (Sandbox Code Playgroud)
我的输出.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
Run Code Online (Sandbox Code Playgroud)
两个NOP位于各自的@ APP/@ NO_APP对之间.我的期望是,asm()由于-O3标志,没有volatile关键字的语句将被优化,但显然两个内联汇编语句都存在.
为什么asm("mov r0, r0")线不会被识别并作为NOP删除?
正如Mystical和MārtiņšMožeiko所描述的那样,编译器并没有优化代码; 即,更改说明.编译器优化的是指令的调度时间.使用时volatile,编译器不会重新安排.在您的示例中,重新安排将在之前或之后移动printf.
编译器可能进行的另一个优化是获取C值以便为您注册.寄存器分配对于优化非常重要.这不会优化汇编程序,但允许编译器使用函数中的其他代码执行合理的操作.
要查看效果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;
}
Run Code Online (Sandbox Code Playgroud)
这两个分支的代码相同,除了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
Run Code Online (Sandbox Code Playgroud)
注意:汇编程序分支与"C"代码相反.由于管道衬里,第二分支在某些处理器上较慢.编译器更喜欢这样
add r1, r1, r1, lsl #2
add r0, r0, r1
Run Code Online (Sandbox Code Playgroud)
不要按顺序执行.
该Ethernut ARM教程是一个很好的资源.但是,优化是一个过载的单词.编译器不分析汇编,只有参数和其中的代码将被发射.