话不多说,接着干!!!
案例二;
实现一个函数进行前n项和计算。
#include <stdio.h>
#include <stdlib.h>
int sum_(const int n){
int sum=0;
for(int i=0;i<n;i++){
sum+=i;
}
return sum;
}
int main(){
const int N=10;
int sum=sum_(N);
printf("sum=%d\n",sum);
return 0;
}
使用编译命令生成汇编代码
-O0
gcc -S -O0 sum.c
.arch armv8-a
.file "sum.c"
.text
.align 2
.global sum_
.type sum_, %function
sum_:
sub sp, sp, #32 //开辟32个字节来保存局部变量
str w0, [sp, 12] //将寄存器w0中的变量(函数传入参数)存储在栈偏移12字节的位置,也就是传入参数n
str wzr, [sp, 24] //将零寄存器中的值(0)存储在栈偏移为24的位置,其实存储的就是c代码中的sum变量的值
str wzr, [sp, 28] //将零寄存器中的值(0)存储在栈偏移28的的位置,存储的是c代码中i的值
b .L2 //无条件跳转到.L2
.L3:
ldr w1, [sp, 24]
ldr w0, [sp, 28] //这两句是将占中的两个局部变量值sum和i加载到寄存器中
add w0, w1, w0 //计算sum+=i
str w0, [sp, 24] //将计算结果保存
ldr w0, [sp, 28]
add w0, w0, 1
str w0, [sp, 28] //这三句是将i+1,然后保存
//.L3的代码是进行sum+=i
.L2:
ldr w1, [sp, 28]
ldr w0, [sp, 12] //加载寄存器中两个局部变量的值i和n
cmp w1, w0 //判断比较
blt .L3 //blt表示跳转到.L3并返回,然后继续循环
ldr w0, [sp, 24] //循环结束后,从栈中加载计算结果到w0
add sp, sp, 32 //然后释放掉当前函数局部变量所占用的占空间
ret
.size sum_, .-sum_
.section .rodata
.align 3
.LC0:
.string "sum=%d\n"
.text
.align 2
.global main
.type main, %function
main:
stp x29, x30, [sp, -32]!
add x29, sp, 0
mov w0, 10
str w0, [x29, 24]
ldr w0, [x29, 24]
bl sum_
str w0, [x29, 28]
adrp x0, .LC0
add x0, x0, :lo12:.LC0
ldr w1, [x29, 28]
bl printf
mov w0, 0
ldp x29, x30, [sp], 32
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",@progbits
-O3
gcc -S -O3 sum.c
sum_:
mov w2, w0
cmp w0, 0
ble .L7
sub w0, w0, #1
cmp w0, 4
bls .L8
adrp x1, .LC0
lsr w0, w2, 2
movi v0.4s, 0
movi v2.4s, 0x4
ldr q1, [x1, #:lo12:.LC0]
mov w1, 0
.p2align 3
.L4:
add v0.4s, v0.4s, v1.4s
add w1, w1, 1
add v1.4s, v1.4s, v2.4s
cmp w1, w0
bcc .L4
addv s0, v0.4s
and w1, w2, -4
cmp w2, w1
umov w0, v0.s[0]
beq .L1
.L3:
add w3, w1, 1
add w0, w0, w1
cmp w2, w3
ble .L1
add w4, w1, 2
add w0, w0, w3
cmp w4, w2
bge .L1
add w3, w1, 3
add w0, w0, w4
cmp w2, w3
ble .L1
add w0, w0, w3
add w1, w1, 4
add w3, w0, w1
cmp w2, w1
csel w0, w3, w0, gt
.L1:
ret
.p2align 2
.L7:
mov w0, 0
ret
.p2align 2
.L8:
mov w1, 0
mov w0, 0
b .L3
.size sum_, .-sum_
.section .rodata.cst16,"aM",@progbits,16
.align 4
老实说,开了O3优化后的汇编好多都看不明白呜呜呜。
但有一个地方可以看出来,因为学了一点点neon intrinsic编程哈哈哈哈哈哈。
add v0.4s, v0.4s, v1.4s
add w1, w1, 1
add v1.4s, v1.4s, v2.4s
这里很肯定的说使用了neon寄存器。一个neon寄存器是128位,处理的数据时int类型,所以一个neon寄存器可以同时处理4个int型数据。所以很明显,开了O3级别优化,编译器自动的使用了neon实现SIMD并行处理数据了。
另外,我以为对for循环进行4个一组的循环展开可能会使编译器自动使用neon进行优化,但不开O3的时候编译器并没有自动优化。而是他也直接展开(裂开。。)
.L3:
ldr w1, [sp, 24]
ldr w0, [sp, 28]
add w0, w1, w0
str w0, [sp, 24]
ldr w0, [sp, 28]
add w0, w0, 1
ldr w1, [sp, 24]
add w0, w1, w0
str w0, [sp, 24]
ldr w0, [sp, 28]
add w0, w0, 2
ldr w1, [sp, 24]
add w0, w1, w0
str w0, [sp, 24]
ldr w0, [sp, 28]
add w0, w0, 3
ldr w1, [sp, 24]
add w0, w1, w0
str w0, [sp, 24]
ldr w0, [sp, 28]
add w0, w0, 4
str w0, [sp, 28]
感觉学习的不多,希望有对这方面了解的大佬和小伙伴可以评论交流啊,互相学习鸭!