我的MIPS编译器是疯了,还是我疯狂选择MIPS?

时间:2011-10-06 13:56:31

标签: c gcc compiler-construction mips pic

我在嵌入式项目中使用MIPS CPU(PIC32),但我开始质疑我的选择。 我知道像MIPS这样的RISC CPU会生成比预期更多的指令,但我不认为它会是这样的。以下是反汇编列表中的代码段:

225:                         LATDSET = 0x0040;
    sw          s1,24808(s2)
    sw          s4,24808(s2)
    sw          s4,24808(s2)
    sw          s1,24808(s2)
    sw          s4,24808(s3)
    sw          s4,24808(s3)
    sw          s1,24808(s3)

226:                         {

227:                             porte = PORTE;
    lw          t1,24848(s4)
    andi        v0,t1,0xffff
    lw          v1,24848(s6)
    andi        ra,v1,0xffff
    lw          v1,24848(s6)
    andi        ra,v1,0xffff
    lw          v0,24848(s6)
    andi        t2,v0,0xffff
    lw          a2,24848(s5)
    andi        v1,a2,0xffff
    lw          t2,24848(s5)
    andi        v1,t2,0xffff
    lw          v0,24848(s5)
    andi        t2,v0,0xffff

228:                             if (porte & 0x0004)
    andi        t2,v0,0x4
    andi        s8,ra,0x4
    andi        s8,ra,0x4
    andi        ra,t2,0x4
    andi        a1,v1,0x4
    andi        a2,v1,0x4
    andi        a2,t2,0x4

229:                                 pst_bytes_somi[0] |= sliding_bit;
    or          t3,t4,s0
    xori        a3,t2,0x0
    movz        t3,s0,a3
    addu        s0,t3,zero
    or          t3,t4,s1
    xori        a3,s8,0x0
    movz        t3,s1,a3
    addu        s1,t3,zero
    or          t3,t4,s1
    xori        a3,s8,0x0
    movz        t3,s1,a3
    addu        s1,t3,zero
    or          v1,t4,s0
    xori        a3,ra,0x0
    movz        v1,s0,a3
    addu        s0,v1,zero
    or          a0,t4,s2
    xori        a3,a1,0x0
    movz        a0,s2,a3
    addu        s2,a0,zero
    or          t3,t4,s2
    xori        a3,a2,0x0
    movz        t3,s2,a3
    addu        s2,t3,zero
    or          v1,t4,s0
    xori        a3,a2,0x0
    movz        v1,s0,a3

这似乎是一些疯狂的指令,用于在固定地址进行简单的读/写和测试变量。在不同的CPU上,我可能会将每个C语句降低到大约1..3条指令,而不需要使用手写的asm。显然,时钟频率相当高,但它不比我在不同CPU(例如dsPIC)中的时钟频率高10倍。

我将优化设置为最大值。我的C编译器很糟糕(这是gcc 3.4.4)?或者这是典型的MIPS?

6 个答案:

答案 0 :(得分:6)

终于找到了答案。反汇编列表完全是误导性的。编译器正在进行循环展开,我们在每个C语句下看到的实际上是指令数量的8倍,因为它正在展开循环8x。说明不在连续的地址!在编译器选项中关闭循环展开会产生以下结果:

225:                         LATDSET = 0x0040;
    sw          s3,24808(s2)
226:                         {
227:                             porte = PORTE;
    lw          t1,24848(s5)
    andi        v0,t1,0xffff
228:                             if (porte & 0x0004)
    andi        t2,v0,0x4
229:                                 pst_bytes_somi[0] |= sliding_bit;
    or          t3,t4,s0
    xori        a3,t2,0x0
    movz        t3,s0,a3
    addu        s0,t3,zero
230:                 

对每个人都感到恐慌。

答案 1 :(得分:3)

我认为你的编译器行为不端...... 检查一下这个陈述:

228:                             if (porte & 0x0004)
    andi        t2,v0,0x4  (1)
    andi        s8,ra,0x4  (2)
    andi        s8,ra,0x4  (3)
    andi        ra,t2,0x4  (4)
    andi        a1,v1,0x4  (5)
    andi        a2,v1,0x4  (6)
    andi        a2,t2,0x4  (7)

很明显,有些指令基本上什么都不做。指令(3)没有像s8中的存储那样新的指令(2)计算出相同的结果。 指令(6)也没有效果,因为它被下一条指令(7)覆盖, 我相信任何进行静态分析阶段的编译器至少会删除指令(3)和(6)。

类似的分析将适用于您的代码的其他部分。例如,在第一个语句中,您可以看到一些寄存器(v0和v0)加载了两次相同的值。

我认为你的编译器在优化编译代码方面做得不好。

答案 2 :(得分:2)

MIPS基本上是RISC设计愚蠢的一切的体现。目前,x86(和x86_64)已经从RISC中吸收了几乎所有有价值的想法,并且ARM已经发展到比传统RISC更高效,同时仍然忠于保持小型系统指令集的RISC概念。 p>

要回答这个问题,我会说你疯狂选择MIPS,或者更重要的是,选择MIPS而不先学习一下MIPS ISA以及为什么它如此糟糕以及你需要多少效率低下如果你想使用它。在大多数情况下,我会选择ARM用于低功耗/嵌入式系统,如果能够承受更多的功耗,我会选择更好的Intel Atom。

编辑:实际上,你可能会疯狂的第二个原因......从评论来看,你似乎正在使用16位整数。你不应该在C中使用小于int的类型,除非在数组中或在大量分配的结构中(以数组或其他方式,如链表/树/等)。 。使用小类型将永远不会带来任何好处,除了节省空间(这与你拥有大量这种类型的值之前无关),并且几乎肯定比使用“普通”类型效率低。在MIPS的情况下,差异是极端的。切换到int,看看问题是否消失。

答案 3 :(得分:0)

我唯一可以想到的可能是,也许,编译器可能会注入额外的无意义指令,以便以更慢的数据总线速度来提高CPU的速度。即使这种解释也不够,因为存储/加载指令同样具有冗余。

由于编译器是可疑的,不要忘记将注意力集中到编译器上可能会使您失去一种隧道视觉。也许错误在工具链的其他部分也是潜在的。

你从哪里获得编译器?我发现一些“简单”的资源经常会发布一些非常糟糕的工具。我的嵌入式开发朋友通常会编译他们自己的工具链,有时很多更好的结果。

答案 4 :(得分:0)

我尝试使用CodeSourcery MIPS GCC 4.4-303和-O4编译以下代码。我用uint32_t和uint16_t尝试了它:

#include <stdint.h>
void foo(uint32_t PORTE, uint32_t pst_bytes_somi[], uint32_t sliding_bit) {
    uint32_t LATDSET = 0x0040;
    {
        uint32_t porte = PORTE;
        if (porte & 0x0004)
            pst_bytes_somi[0] |= sliding_bit;
        if (porte & LATDSET)
            pst_bytes_somi[1] |= sliding_bit;
    }
}

这是使用uint32_t整数的反汇编:

        uint32_t porte = PORTE;
        if (porte & 0x0004)
   0:   30820004    andi    v0,a0,0x4
   4:   10400004    beqz    v0,18 <foo+0x18>
   8:   00000000    nop
./foo32.c:7
            pst_bytes_somi[0] |= sliding_bit;
   c:   8ca20000    lw  v0,0(a1)
  10:   00461025    or  v0,v0,a2
  14:   aca20000    sw  v0,0(a1)
./foo32.c:8
        if (porte & LATDSET)
  18:   30840040    andi    a0,a0,0x40
  1c:   10800004    beqz    a0,30 <foo+0x30>
  20:   00000000    nop
./foo32.c:9
            pst_bytes_somi[1] |= sliding_bit;
  24:   8ca20004    lw  v0,4(a1)
  28:   00463025    or  a2,v0,a2
  2c:   aca60004    sw  a2,4(a1)
  30:   03e00008    jr  ra
  34:   00000000    nop

这是使用uint16_t整数的反汇编:

        if (porte & 0x0004)
   4:   30820004    andi    v0,a0,0x4
   8:   10400004    beqz    v0,1c <foo+0x1c>
   c:   30c6ffff    andi    a2,a2,0xffff
./foo16.c:7
            pst_bytes_somi[0] |= sliding_bit;
  10:   94a20000    lhu v0,0(a1)
  14:   00c21025    or  v0,a2,v0
  18:   a4a20000    sh  v0,0(a1)
./foo16.c:8
        if (porte & LATDSET)
  1c:   30840040    andi    a0,a0,0x40
  20:   10800004    beqz    a0,34 <foo+0x34>
  24:   00000000    nop
./foo16.c:9
            pst_bytes_somi[1] |= sliding_bit;
  28:   94a20002    lhu v0,2(a1)
  2c:   00c23025    or  a2,a2,v0
  30:   a4a60002    sh  a2,2(a1)
  34:   03e00008    jr  ra
  38:   00000000    nop

正如您所看到的,每个C语句都映射为两到三条指令。使用16位整数使该函数只有一个指令更长。

答案 5 :(得分:-1)

您是否启用了编译器优化?未经优化的代码有很多冗余。