优化该代码时遇到的问题是:
08000328 <mul_test01>:
8000328: f04f 5000 mov.w r0, #536870912 ; 0x20000000
800032c: 4770 bx lr
800032e: bf00 nop
您的代码在运行时不做任何事情,因此优化器可以计算最终答案。
这:
.thumb_func
.globl mul_test02
mul_test02:
smull r2,r3,r0,r1
mov r0,r3
bx lr
用这个调用:
c = mul_test02(0x40000000,0x40000000);
给出 0x10000000
UMULL 给出相同的结果,因为您使用的是正数,操作数和结果都是正数,因此它不会进入有符号/无符号差异。
嗯,好吧,你让我上了这个。我会把你的代码读成告诉编译器将乘法提升到 64 位。smull 是两个 32 位操作数,给出 64 位结果,这不是您的代码所要求的......但是 gcc 和 clang 都使用了 smull,即使我将它作为未调用函数保留,所以它不知道编译时操作数没有超过 32 的有效数字,它们仍然使用 smull。
也许这种转变是原因。
是的,就是这样。。
int mul_test04 ( int a, int b )
{
int c;
c = ((long long)a*b) >> 31;
return(c);
}
给
gcc 和 clang(以及 clang 回收 r0 和 r1 而不是使用 r2 和 r3)
08000340 <mul_test04>:
8000340: fb81 2300 smull r2, r3, r1, r0
8000344: 0fd0 lsrs r0, r2, #31
8000346: ea40 0043 orr.w r0, r0, r3, lsl #1
800034a: 4770 bx lr
但是这个
int mul_test04 ( int a, int b )
{
int c;
c = ((long long)a*b);
return(c);
}
给了这个
海湾合作委员会:
08000340 <mul_test04>:
8000340: fb00 f001 mul.w r0, r0, r1
8000344: 4770 bx lr
8000346: bf00 nop
铛:
0800048c <mul_test04>:
800048c: 4348 muls r0, r1
800048e: 4770 bx lr
因此,通过位移,编译器意识到您只对结果的上部感兴趣,因此他们可以丢弃操作数的上部,这意味着可以使用 smull。
现在,如果你这样做:
int mul_test04 ( int a, int b )
{
int c;
c = ((long long)a*b) >> 32;
return(c);
}
两个编译器都变得更加智能,尤其是 clang:
0800048c <mul_test04>:
800048c: fb81 1000 smull r1, r0, r1, r0
8000490: 4770 bx lr
海湾合作委员会:
08000340 <mul_test04>:
8000340: fb81 0100 smull r0, r1, r1, r0
8000344: 4608 mov r0, r1
8000346: 4770 bx lr
我可以看到 0x40000000 被视为一个浮点数,您可以在其中跟踪小数位,并且该位置是一个固定位置。0x20000000 作为答案是有意义的。我还不能确定 31 位移位是普遍适用还是仅适用于这种情况。
用于上述的完整示例在这里
https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/sample01
我确实在 stm32f4 上运行了它以验证它的工作原理和结果。
编辑:
如果将参数传递给函数而不是在函数中硬编码它们:
int myfun ( int a, int b )
{
return(a+b);
}
编译器被迫生成运行时代码,而不是在编译时优化答案。
现在,如果您从另一个带有硬编码数字的函数调用该函数:
...
c=myfun(0x1234,0x5678);
...
在这个调用函数中,编译器可以选择计算答案并在编译时将其放置在那里。如果 myfun() 函数是全局的(未声明为静态的),编译器不知道稍后要链接的其他代码是否会使用它,因此即使在此文件中的调用点附近,它也会优化答案,它仍然必须生成实际函数并将其保留在对象中以供其他文件中的其他代码调用,因此您仍然可以检查编译器/优化器对该 C 代码的作用。除非您使用 llvm 例如在哪里可以优化整个项目(跨文件),否则调用此函数的外部代码将使用实际函数,而不是编译时计算的答案。
gcc 和 clang 都做了我所描述的,将函数的运行时代码作为全局函数,但在文件中它在编译时计算答案并将硬编码的答案放在代码中,而不是调用函数:
int mul_test04 ( int a, int b )
{
int c;
c = ((long long)a*b) >> 31;
return(c);
}
在同一文件的另一个函数中:
hexstring(mul_test04(0x40000000,0x40000000),1);
函数本身在代码中实现:
0800048c <mul_test04>:
800048c: fb81 1000 smull r1, r0, r1, r0
8000490: 0fc9 lsrs r1, r1, #31
8000492: ea41 0040 orr.w r0, r1, r0, lsl #1
8000496: 4770 bx lr
但是在所谓的地方,他们已经硬编码了答案,因为他们拥有这样做所需的所有信息:
8000520: f04f 5000 mov.w r0, #536870912 ; 0x20000000
8000524: 2101 movs r1, #1
8000526: f7ff fe73 bl 8000210 <hexstring>
如果您不想要硬编码的答案,则需要使用不在同一优化过程中的函数。
操作编译器和优化器归结为大量实践,这不是一门精确的科学,因为编译器和优化器在不断发展(无论好坏)。
通过在函数中隔离一小段代码,您会以另一种方式引起问题,较大的函数更有可能需要堆栈帧并将变量从寄存器逐出到堆栈中,较小的函数可能不需要这样做,并且优化器可能会因此改变代码的实现方式。您以一种方式测试代码片段以查看编译器正在做什么,然后在更大的函数中使用它并且没有得到您想要的结果。如果有您想要实现的确切指令或指令序列......在汇编程序中实现它们。如果您针对特定指令集/处理器中的特定指令集,请避免游戏,避免在更改计算机/编译器/等时更改代码,并为该目标使用汇编程序。