avr-gcc чудит с генерацией кода

Wiring, Processing, библиотеки

avr-gcc чудит с генерацией кода

Сообщение burjui » 06 июн 2011, 13:11

Давеча написал вот такую функцию:
Код: Выделить всё
uint8_t reverse8bits(uint8_t bits)
{
  bits = (bits & 0x55) << 1 | (bits & 0xAA) >> 1; // меняем местами биты попарно
  bits = (bits & 0x33) << 2 | (bits & 0xCC) >> 2; // следом пары битов
  bits = (bits & 0x0F) << 4 | (bits & 0xF0) >> 4; // и чертвёрки битов

  return bits;
}

Вроде, всё просто. А компилятор решил, что не очень, и нагенерил вот такой кусок говнокода:
Код: Выделить всё
ldi     r25,lo8(0)
mov     r18,r24
mov     r19,r25
andi    r18,lo8(170)
andi    r19,hi8(170)
asr     r19
ror     r18
andi    r24,lo8(85)
andi    r25,hi8(85)
lsl     r24
rol     r25
or      r18,r24
ldi     r19,lo8(0)
mov     r24,r18
mov     r25,r19
andi    r24,lo8(204)
andi    r25,hi8(204)
asr     r25
ror     r24
asr     r25
ror     r24
andi    r18,lo8(51)
andi    r19,hi8(51)
lsl     r18
rol     r19
lsl     r18
rol     r19
or      r24,r18
mov     r18,r24
swap    r18
andi    r18,lo8(15)
swap    r24
andi    r24,lo8(-16)
or      r24,r18
ret

Ужас, 35 команд. Даже несмотря на флаг -Os (оптимизировать + уменьшить размер кода) GCC с чего-то решил поработать с 16-битными значениями, хотя в коде явно указаны только 8-битные.
Переписал функцию вот так:
Код: Выделить всё
uint8_t reverse8bits(uint8_t bits)
{
  asm volatile("mov     %0, %1"   : "=d"(tmp) : "r"(bits));
  asm volatile("andi    %0, 0x55" : "=d"(bits) : "0"(bits));
  asm volatile("lsl     %0"       : "=d"(bits) : "0"(bits));
  asm volatile("andi    %0, 0xAA" : "=d"(tmp) : "0"(tmp));
  asm volatile("lsr     %0"       : "=d"(tmp) : "0"(tmp));
  asm volatile("or      %0, %1"   : "=d"(bits): "r"(tmp), "0"(bits));

  asm volatile("mov     %0, %1"   : "=d"(tmp) : "r"(bits));
  asm volatile("andi    %0, 0x33" : "=d"(bits) : "0"(bits));
  asm volatile("lsl     %0"       : "=d"(bits) : "0"(bits));
  asm volatile("lsl     %0"       : "=d"(bits) : "0"(bits));
  asm volatile("andi    %0, 0xCC" : "=d"(tmp) : "0"(tmp));
  asm volatile("lsr     %0"       : "=d"(tmp) : "0"(tmp));
  asm volatile("lsr     %0"       : "=d"(tmp) : "0"(tmp));
  asm volatile("or      %0, %1"   : "=d"(bits) : "r"(tmp), "0"(bits));

  asm volatile("swap    %0"       : "=d"(bits) : "0"(bits));

  return bits;
}

Соответственно, после компиляции получается вот что:
Код: Выделить всё
mov     r25, r24
andi    r24, 0x55
add     r24, r24
andi    r25, 0xAA
lsr     r25
or      r24, r25
mov     r25, r24
andi    r24, 0x33
add     r24, r24
add     r24, r24
andi    r25, 0xCC
lsr     r25
lsr     r25
or      r24, r25
swap    r24
ret

Всего 16 команд, смысл кода тот же. Теперь понятно, почему Arduino'вские библиотеки столько флэша жрут - с такой-то "оптимизацией". Один мой скетч, использующий несколько библиотек, 8 кБ flash-памяти жрёт - больше половины доступной :\

Будьте бдительны, господа.
burjui
 
Сообщения: 91
Зарегистрирован: 06 май 2011, 21:47
Откуда: Калининград
programming: Scheme,D,C,C++,Python

Re: avr-gcc чудит с генерацией кода

Сообщение pfalcon » 02 апр 2012, 17:44

Даже несмотря на флаг -Os (оптимизировать + уменьшить размер кода) GCC с чего-то решил поработать с 16-битными значениями, хотя в коде явно указаны только 8-битные.


Надеюсь, именно это вы и имеете ввиду - почему не соптимизировал сдвиг влево на байтовых значениях с последующим truncate'ом до байта. Почему же он вообще так делает вполне известно всем, хорошо знающим C - вся арифметика выполняется в int'ах: http://en.wikipedia.org/wiki/Type_conve ... _promotion . Так что никто не виноват, что 8-битные процессоры такие восьмибитные. Если вы не пытались явно приводить результат сдвигов к uint8_t, то нечего на компилятор пенять, он делает то, что ему сказали вы и что предписано стандартом ;-).

Почему не оптимизирует? Да опасное это дело. Вот в этом примере вроде понятно, что можно. А в этом: ((byte<<1)>>1)? Вы ж не ждете наверное результата эквивалентного byte & 0x7f? Нет, вроде понятно, что взаимообратные операции, любой мало-мальски constant propogating компилятор сведет к byte. А если:

a = b = 1;
((byte<<a)>>b)

? Вот-вот, очень трудно найти грань, где оптимизировать легко, а где сложно. Нужно знать свойства операций (а главное их комбинаций) на всяких кольцах и конечных группах. Вы знаете? Отож, и я. А если делать "по интуиции", что "2+2=4", то такого можно нагородить, что будете не неоптимальности дивиться, а после дней поисков глюков, возмущаться, как можно было генерить код с такими ошибками.

Не захотели GCCятнику туда лезть - ну и бог с ним. Тот разумный порог, которого стоит требовать от компилятора - это чтобы он, если результат простой операции приводится к байту, то выполнял 8-битное действие (ну и плюс чтобы понимал, что чисто логические операции тоже битов результату не добавят).
pfalcon
 
Сообщения: 1
Зарегистрирован: 02 апр 2012, 16:29


Вернуться в Программирование

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

© 2009-2019 |  О проекте  |  Политика Конфиденциальности  |