templatetypedef已经总结了.为他的回应添加一些支持.采取这些示例功能:
unsigned int fun1 ( unsigned int *x )
{
unsigned int ra,rb;
rb=0;
for(ra=0;ra<1000;ra++) rb+=*x++;
return(rb);
}
unsigned int fun2 ( unsigned int *x )
{
unsigned int ra,rb;
rb=0;
for(ra=0;ra<1000;ra++) rb+=x[ra];
return(rb);
}
现在gcc产生了这个:
00000000 fun1:
0: e52d4004 push {r4} ; (str r4,[sp,#-4]!)
4: e1a03000 mov r3,r0
8: e2804efa add r4,r0,#4000 ; 0xfa0
c: e3a00000 mov r0,#0
10: e1a02003 mov r2,r3
14: e492c004 ldr ip,[r2],#4
18: e5931004 ldr r1,[r3,#4]
1c: e2823004 add r3,r2,#4
20: e080000c add r0,ip
24: e1530004 cmp r3,r4
28: e0800001 add r0,r1
2c: 1afffff7 bne 10
30: e49d4004 pop {r4} ; (ldr r4,[sp],#4)
34: e12fff1e bx lr
00000038 fun2:
38: e3a03000 mov r3,#0
3c: e1a02003 mov r2,r3
40: e790c003 ldr ip,[r0,r3]
44: e2833004 add r3,r3,#4
48: e7901003 ldr r1,r3]
4c: e2833004 add r3,#4
50: e082200c add r2,ip
54: e3530efa cmp r3,#4000 ; 0xfa0
58: e0822001 add r2,r1
5c: 1afffff7 bne 40
60: e1a00002 mov r0,r2
64: e12fff1e bx lr
代码是不同的,但我很惊讶于错过的优化机会.
Clang / llvm产生:
00000000 fun1:
0: e3a01000 mov r1,#0
4: e3a02ffa mov r2,#1000 ; 0x3e8
8: e1a03001 mov r3,r1
c: e2522001 subs r2,#1
10: e490c004 ldr ip,[r0],#4
14: e08c3003 add r3,ip,r3
18: e2c11000 sbc r1,r1,#0
1c: e182c001 orr ip,r1
20: e35c0000 cmp ip,#0
24: 1afffff8 bne c
28: e1a00003 mov r0,r3
2c: e12fff1e bx lr
00000030 fun2:
30: e3a01000 mov r1,#0
34: e3a02ffa mov r2,#1000 ; 0x3e8
38: e1a03001 mov r3,r1
3c: e2522001 subs r2,#1
40: e490c004 ldr ip,#4
44: e08c3003 add r3,r3
48: e2c11000 sbc r1,#0
4c: e182c001 orr ip,r1
50: e35c0000 cmp ip,#0
54: 1afffff8 bne 3c
58: e1a00003 mov r0,r3
5c: e12fff1e bx lr
您可能会注意到编译器产生完全相同的代码,指针或偏移量.通过改变编译器,我比改变指针对数组索引更好.我认为llvm可以做得更好一些,我将需要更多的了解这些,以了解我的代码做了什么导致这一点.
编辑:
我希望使编译器至少使用有利于指针的ldr rd,[rs],#4指令,并希望编译器会看到它可能会破坏数组地址,从而将其视为指针而不是偏移量变成一个数组(并使用上面的指令,这基本上是clang / llvm做的).或者如果它使用数组的东西,它将使用ldr rd,[rm,rn]指令.基本上希望编译器之一能够产生以下解决方案之一:
funa:
mov r1,#0
mov r2,#1000
funa_loop:
ldr r3,#4
add r1,r3
subs r2,#1
bne funa_loop
mov r0,r1
bx lr
funb:
mov r1,#0
funb_loop:
ldr r3,r2]
add r1,r3
add r2,#4
cmp r2,#0x4000
bne funb_loop
mov r0,r1
bx lr
func:
mov r1,#4000
subs r2,#4
func_loop:
beq func_done
ldr r3,#4
b func_loop
func_done:
mov r0,r1
bx lr
没有相当的到达那里,但相当接近.这是一个有趣的运动.注意以上是所有的ARM汇编器.
一般来说,(不是我的具体的C代码示例而不一定是ARM),一些流行的架构,您将从一个基于寄存器的地址(ldr r0,[r1])和一个寄存器索引/偏移量的负载(ldr r0,[r1,r2]),其中地址是两个寄存器的总和.一个寄存器理想地是阵列的基址,第二个是索引/偏移量.寄存器的前一个负载自身指向指针,后者指向数组.如果您的C程序不会更改或移动指针或索引,那么在这两种情况下,这意味着一个静态地址被计算,那么使用正常的负载,数组和指针都应该产生相同的指令.对于更有意思的改变指针/索引的情况.
Pointer
ldr r0,[r1]
...
add r1,some number
Array index
ldr r0,r2]
...
add r2,some number
(根据需要用商店替换负载和添加子)
一些架构没有三个注册寄存器索引指令,所以你必须做一些事情
array index:
mov r2,r1
...
ldr r0,[r2]
...
add r2,some number
或者根据编译器可能会变得非常糟糕,esp如果编译用于调试或没有优化,并假设您没有三个寄存器添加
array index:
mov r2,#0
...
mov r3,r1
add r3,r2
ldr r4,[r3]
...
add r2,some number
所以这两种方法很可能是相等的.如在ARM上看到的,它可以将两个(即时)指针指针组合在一起,使其更快一点.阵列索引解决方案会烧写更多的寄存器,并且取决于架构的可用寄存器数量,这些寄存器可能会将寄存器交换到堆栈的时间越来越快(比使用指针)更慢.如果您不介意摧毁基地址,底线是指针解决方案可能会从性能角度给您一个优势.它与您的代码和编译器有很大关系.对我来说,它的可读性发挥作用,我觉得数组更容易阅读和跟随,第二个我需要保留该指针释放一个malloc或再次通过该内存等.如果是这样我可能会使用一个数组与一个索引,如果是一次性通过,我不关心摧毁基地址,我将使用一个指针.如上所述,使用编译器生成的代码,如果性能至关重要,那么手工编译汇编器中的解决方案(根据建议的方法,让编译器先尝试一下).