优化该代码时遇到的问题是:
08000328 <mul_test01>:
8000328: f04f 5000 mov.w r0,
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);
}
给出这个
gcc:
08000340 <mul_test04>:
8000340: fb00 f001 mul.w r0, r0, r1
8000344: 4770 bx lr
8000346: bf00 nop
clang:
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
gcc:
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>
如果您不想使用硬编码的答案,那么您需要使用一个不在同一优化过程中的函数。
操纵编译器和优化器需要大量实践,并且这不是一门精确的科学,因为编译器和优化器不断发展(好的或坏的)。通过将小代码片段隔离在一个函数中,会以另一种方式引起问题,更大的函数更有可能需要堆栈帧并将变量从寄存器驱逐到堆栈中,而较小的函数可能不需要这样做,因此优化器可能会改变代码的实现方式。您可以通过一种方式测试代码片段以了解编译器正在执行的操作,然后将其用于更大的函数,并且没有得到想要的结果。如果存在确切的指令或指令序列需要实现...请使用汇编语言实现。如果您的目标是针对特定的指令集/处理器,则避免游戏,避免在更改计算机/编译器等时更改代码,并只针对该目标使用汇编语言。如果需要,可以使用条件编译选项进行ifdef或其他操作以构建不同目标的代码。