Я WinAVR использую года 2 и за это время сложилось достаточно положительное впечатление, но не без причуд. Похоже на уровне оптимизации WinAVR "не знает" что регистры 8 разрядные (разработчики портировали с другой архитектуры без существенной переработки, это моё личное мнение)
Например возьмём убогий пример, и откоипилируем с опцией -O2, компилятор последний офиц WinAVR (а по сути здесь и не важно)
Код
uint8_t get_small_val()
{
return 0x8;
}
uint32_t test(uint32_t val)
{
uint8_t tmp = get_small_val();
val += (uint16_t)tmp << 8;
return val;
}
то получим
Код
00000284 <get_small_val>:
284: 88 e0 ldi r24, 0x08; 8
286: 90 e0 ldi r25, 0x00; 0
288: 08 95 ret
0000028a <test>:
28a: ef 92 push r14
28c: ff 92 push r15
28e: 0f 93 push r16
290: 1f 93 push r17
292: 7b 01 movw r14, r22
294: 8c 01 movw r16, r24
296: f6 df rcall .-20 ; 0x284 <get_small_val>
298: 99 27 eor r25, r25
29a: 98 2f mov r25, r24
29c: 88 27 eor r24, r24
29e: aa 27 eor r26, r26
2a0: bb 27 eor r27, r27
2a2: 8e 0d add r24, r14
2a4: 9f 1d adc r25, r15
2a6: a0 1f adc r26, r16
2a8: b1 1f adc r27, r17
2aa: bc 01 movw r22, r24
2ac: cd 01 movw r24, r26
2ae: 1f 91 pop r17
2b0: 0f 91 pop r16
2b2: ff 90 pop r15
2b4: ef 90 pop r14
2b6: 08 95 ret
Во-первых функции которые возвращают uint8_t расширяются до int (строчка 286), причём как я смотрел совершенно не зависит от опций оптимизаций, конечно есть волшеьная опция -mint8 которая заставляет int принимать 8 бит, но при этом разработчики не гарантируют что все проекты откомпилируются с данной опцией.
Во-вторых по согласованию распределения регистров с переменными, есть 3 типа, в стандартном, бедет всегда такая картина при передаче и возврашение 32 битного числа (292-294, 2aa-2ac). Причем если активно использовать 32 битные типы (да понимаю AVR не затачивался под 32) то таких констукций будет много. Причем нам явно младший байт после сдвига tmp прибавлять не надо, это очевидно. Но GCC этого "не прнимает". Причем если не написать (uint16_t) перед опеорацией сдвига, то компилятор расширит uint8_t до int, а int знаковое, и после сдвига явно сбросит 7 бит, чтобы расширение произошло "корректно".
Есть ещё моного тонкостей на которые я наткнулся...
Но вот если в проекте на так много 32битных вычисление то GCC может очень изащренно оптимизировать код, бывало когда я смотрел дамп аж сам биву давался как он налавкачел.. =)
Ещё один огромнейший и неоспоримый плюс GCC это ассемблерные вставки. В них можно написать что-то типа макроса и компилятор будет сам выбирать какие регистры ему в конкретном случае лучше использовать (применительно к inline конструкциям)
Код
__asm__ __volatile__ (
"ldd %0, Z+14" "\r\n"
"andi %0, 0x8C" "\r\n"
"lsr %0" "\r\n"
"mov __tmp_reg__, %0" "\r\n"
"lsr %0" "\r\n"
"swap __tmp_reg__" "\r\n"
"or %0, __tmp_reg__" "\r\n"
: "=r" (resp)
: "z" (data)
);
Вместо %0, в котором размещается resp, компилятор сам подставит необхолимые регистр, далее указываем компилятору чтобы в Z было data. Если бы в констукции использовался явно какой-то регистр (например после mul) то его нужно было бы включить в список экранируемых регистров ( : "r1" , после : "z" (data) ). При это генерируемы код, если правильно составить макрос булет 100% безопасным.
Тема написания вставок вообще очень интересная, думаю общий обзор дал..
Я использую WinAVR на всех проектах с AVR, а где код сильно тормозил, использовую вставочку =)