Мой компилятор MIPS сумасшедший, или я сумасшедший для выбора MIPS?

Я использую процессор MIPS (PIC32) во встроенном проекте, но я начинаю сомневаться в своем выборе. Я понимаю, что процессор RISC, такой как MIPS, будет генерировать больше инструкций, чем можно было бы ожидать, но я не думал, что это будет так. Вот фрагмент из списка разборки:

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

Это похоже на сумасшедшее количество инструкций для простого чтения / записи и проверка переменных по фиксированным адресам. На другой процессор, я могу каждое утверждение с около 1..3 инструкции, не прибегая к рукописному asm. Очевидно, что тактовая частота довольно высока, но это не 10x выше, чем у меня было бы в другом процессоре (например, dsPIC).

У меня оптимизация на максимум. Мой компилятор C ужасен (это gcc 3.4.4)? Или это типично для MIPS?

6 ответов


наконец-то понял ответ. Список разборки полностью вводит в заблуждение. Компилятор выполняет развертывание цикла, и то, что мы видим под каждым оператором C, на самом деле 8x количество инструкций, потому что он разворачивает цикл 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:                 

паника по каждому.


Я думаю, что ваш компилятор ведет себя неправильно... Проверьте, например, это утверждение:

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) загружаются с одним и тем же значением дважды.

Я думаю, что ваш компилятор не делает хорошую работу по оптимизации компилируемого кода.


MIPS-это в основном воплощение всего, что было глупо в дизайне RISC. В наши дни x86 (и x86_64) поглотили почти все стоящие идеи из RISC, и ARM эволюционировал, чтобы быть намного более эффективным, чем традиционный RISC, оставаясь при этом верным концепции RISC о сохранении небольшого, систематического набора инструкций.

чтобы ответить на вопрос, я бы сказал, что вы сумасшедшие для выбора MIPS или, что более важно, для выбора его без первого изучения немного о MIPS ISA и почему это так плохо и сколько неэффективности вам нужно мириться, если вы хотите использовать его. Я бы выбрал ARM для маломощных/встроенных систем в большинстве ситуаций, или еще лучше Intel Atom, если вы можете позволить себе немного больше энергопотребления.

Edit: На самом деле, вторая причина, по которой вы можете быть сумасшедшим... Из комментариев кажется, что вы используете 16-битные целые числа. Вы никогда не должны использовать меньше, чемint типы В C, за исключением массивов или структуры, которая будет выделяться большими числами (либо в массиве, либо каким-либо другим способом, например, связанным списком/деревом/и т. д.). Использование небольших типов никогда не даст никаких преимуществ, кроме экономии места (что не имеет значения, пока у вас нет большого количества значений такого типа) и почти наверняка менее эффективно, чем использование "нормальных" типов. В случае MIPS разница является экстремальной. Перейти int и посмотреть, если ваша проблема исчезнет.


единственное, о чем я могу думать, это, возможно,возможно компилятор может вводить дополнительные бессмысленные инструкции для сопряжения скорости процессора с гораздо более медленной скоростью шины данных. Даже этого объяснения недостаточно, поскольку инструкции store / load аналогично имеют избыточность.

поскольку компилятор подозревается, не забывайте, что фокусировка усилий в компиляторе может ослепить вас к типу туннельного зрения. Возможно, ошибки скрыты в других частях и цепи инструментов тоже.

где вы взяли компилятор? Я нахожу, что некоторые из "простых" источников часто отправляют некоторые довольно ужасные инструменты. Встроенные друзья по разработке обычно компилируют свою собственную цепочку инструментов с иногда много лучшие результаты.


Я попытался скомпилировать следующий код с 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

как вы можете видеть, каждый с карты на две-три инструкции. Использование 16-битных целых чисел делает функцию только одной инструкцией длиннее.


вы включили оптимизацию компилятора? Код Unoptimzied имеет много дублирования в нем.