使用ARM Cortex-M4和gcc编译器的定点数学

vc6uscn9  于 2023-08-06  发布在  其他
关注(0)|答案(2)|浏览(148)

我正在使用Freescale Kinetis K60和CodeWarrior IDE(我相信它使用GCC作为编译器)。
我想将两个32位数相乘(结果是一个64位数),只保留高32位。
我认为ARM Cortex-M4的正确汇编指令是SMMUL指令。我更喜欢从C代码而不是汇编中访问此指令。我该怎么做?
我想代码应该是这样的:

int a,b,c;

a = 1073741824;   // 0x40000000 = 0.5 as a D0 fixed point number
b = 1073741824;   // 0x40000000 = 0.5 as a D0 fixed point number

c = ((long long)a*b) >> 31;  // 31 because there are two sign bits after the multiplication
                             // so I can throw away the most significant bit

字符串
当我在CodeWarrior中尝试此操作时,我得到了c的正确结果(536870912 = 0.25作为D 0 FP数)。我在任何地方都没有看到SMMUL指令,乘法是3个指令(UMULL,MLA和MLA -我不明白为什么它使用无符号乘法,但这是另一个问题)。我还尝试了右移32,因为这可能对SMMUL指令更有意义,但这并没有做任何不同的事情。

jyztefdp

jyztefdp1#

优化代码的问题是:

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);


给出0x 10000000
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(well clang循环r 0和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


我可以看到0x 40000000被认为是一个浮点数,你可以跟踪小数位,这个位置是一个固定的位置。0x 20000000是有意义的答案。我还不能决定31位移位是普遍适用还是只适用于这一种情况。
一个完整的例子用于上述是在这里
https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/sample01
而且我确实在stm32 f4上运行了它来验证它的工作和结果。
编辑:
如果将参数传递到函数中,而不是在函数中硬编码它们:

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或其他使用条件编译选项来构建不同的目标,而不需要汇编器。

fzsnzjdm

fzsnzjdm2#

GCC支持实际的定点类型:https://gcc.gnu.org/onlinedocs/gcc/Fixed-Point.html
我不知道它会使用什么指令,但它可能会让你的生活更容易。

相关问题