Портировал я свою программу с IARа на GCC.
Оптимизация максимальная по скорости у первого, и Os с ключами -mcall-prologues -fno-inline-small-functions -fno-threadsafe-statics -ffunction-sections --gc-sections --relax у второго.
В результате код IARа - 6 090 bytes of CODE memory + 493 bytes of DATA memory.
Код GCC - Program: 7120 bytes + Data: 363 bytes.
Код иара компактнее свыше чем на килобайт. Правда, некоторые функции "тюнингованы" специальными ключами (типа __z_x и т.д.) для оптимальной генерации.
Добавление некоторых аттрибутов для функций в GCC в аналогичных целях, к сожалению, не дало абсолютно никакого результата...
На первый взгляд, скомпилированный GCC код весьма красив и аккуратен, за исключением излишней любви к LDS и STS, например:
исходный текст:
Код
case USART_SEND_DATA_EX:
length = 4 + 4 + 4 + 20;
*pb++ = length + 1;
*pb++ = command;
*pb++ = temperature.cpu;
*pb++ = temperature.gpu;
*pb++ = temperature.amb;
*pb++ = temperature.hdd;
*pb++ = fan_speed[0];
*pb++ = fan_speed[1];
*pb++ = fan_speed[2];
*pb++ = fan_speed[3];
*pb++ = cpuFan.GetCurrentSpeed();
*pb++ = rearFan.GetCurrentSpeed();
*pb++ = sideFan.GetCurrentSpeed();
*pb++ = frontFan.GetCurrentSpeed();
*pb++ = cpuFan.RPMval[RPM_MIN];
*pb++ = cpuFan.RPMval[RPM_MED];
*pb++ = cpuFan.RPMval[RPM_MAX];
*pb++ = cpuFan.minTEMPval;
*pb++ = cpuFan.maxTEMPval;
*pb++ = rearFan.RPMval[RPM_MIN];
*pb++ = rearFan.RPMval[RPM_MED];
*pb++ = rearFan.RPMval[RPM_MAX];
*pb++ = rearFan.minTEMPval;
*pb++ = rearFan.maxTEMPval;
*pb++ = sideFan.RPMval[RPM_MIN];
*pb++ = sideFan.RPMval[RPM_MED];
*pb++ = sideFan.RPMval[RPM_MAX];
*pb++ = sideFan.minTEMPval;
*pb++ = sideFan.maxTEMPval;
*pb++ = frontFan.RPMval[RPM_MIN];
*pb++ = frontFan.RPMval[RPM_MED];
*pb++ = frontFan.RPMval[RPM_MAX];
*pb++ = frontFan.minTEMPval;
*pb++ = frontFan.maxTEMPval;
break;
результат IARа:
Код
105 case USART_SEND_DATA_EX:
106 length = 4 + 4 + 4 + 20;
\ ??usartSendCommand_4:
\ 000000C4 E280 LDI R24, 32
107 *pb++ = length + 1;
\ 000000C6 E201 LDI R16, 33
\ 000000C8 930D ST X+, R16
108 *pb++ = command;
\ 000000CA 934D ST X+, R20
109 *pb++ = temperature.cpu;
\ 000000CC 9100.... LDS R16, temperature
\ 000000D0 930D ST X+, R16
110 *pb++ = temperature.gpu;
\ 000000D2 9100.... LDS R16, (temperature + 1)
\ 000000D6 930D ST X+, R16
111 *pb++ = temperature.amb;
\ 000000D8 9100.... LDS R16, (temperature + 2)
\ 000000DC 930D ST X+, R16
112 *pb++ = temperature.hdd;
\ 000000DE 9100.... LDS R16, (temperature + 3)
\ 000000E2 930D ST X+, R16
113 *pb++ = fan_speed[0];
\ 000000E4 9100.... LDS R16, fan_speed
\ 000000E8 930D ST X+, R16
114 *pb++ = fan_speed[1];
\ 000000EA 9100.... LDS R16, (fan_speed + 1)
\ 000000EE 930D ST X+, R16
115 *pb++ = fan_speed[2];
\ 000000F0 9100.... LDS R16, (fan_speed + 2)
\ 000000F4 930D ST X+, R16
116 *pb++ = fan_speed[3];
\ 000000F6 9100.... LDS R16, (fan_speed + 3)
\ 000000FA 930D ST X+, R16
117 *pb++ = cpuFan.GetCurrentSpeed();
\ 000000FC .... LDI R16, LOW(cpuFan)
\ 000000FE .... LDI R17, (cpuFan) >> 8
\ 00000100 .... RCALL ??GetCurrentSpeed
\ 00000102 930D ST X+, R16
118 *pb++ = rearFan.GetCurrentSpeed();
\ 00000104 .... LDI R16, LOW(rearFan)
\ 00000106 .... LDI R17, (rearFan) >> 8
\ 00000108 .... RCALL ??GetCurrentSpeed
\ 0000010A 930D ST X+, R16
119 *pb++ = sideFan.GetCurrentSpeed();
\ 0000010C .... LDI R16, LOW(sideFan)
\ 0000010E .... LDI R17, (sideFan) >> 8
\ 00000110 .... RCALL ??GetCurrentSpeed
\ 00000112 930D ST X+, R16
120 *pb++ = frontFan.GetCurrentSpeed();
\ 00000114 .... LDI R16, LOW(frontFan)
\ 00000116 .... LDI R17, (frontFan) >> 8
\ 00000118 .... RCALL ??GetCurrentSpeed
\ 0000011A 930D ST X+, R16
121 *pb++ = cpuFan.RPMval[RPM_MIN];
\ 0000011C 9100.... LDS R16, (cpuFan + 5)
\ 00000120 930D ST X+, R16
122 *pb++ = cpuFan.RPMval[RPM_MED];
\ 00000122 9100.... LDS R16, (cpuFan + 6)
\ 00000126 930D ST X+, R16
123 *pb++ = cpuFan.RPMval[RPM_MAX];
\ 00000128 9100.... LDS R16, (cpuFan + 7)
\ 0000012C 930D ST X+, R16
124 *pb++ = cpuFan.minTEMPval;
\ 0000012E 9100.... LDS R16, (cpuFan + 8)
\ 00000132 930D ST X+, R16
125 *pb++ = cpuFan.maxTEMPval;
\ 00000134 9100.... LDS R16, (cpuFan + 9)
\ 00000138 930D ST X+, R16
126 *pb++ = rearFan.RPMval[RPM_MIN];
\ 0000013A 9100.... LDS R16, (rearFan + 5)
\ 0000013E 930D ST X+, R16
127 *pb++ = rearFan.RPMval[RPM_MED];
\ 00000140 9100.... LDS R16, (rearFan + 6)
\ 00000144 930D ST X+, R16
128 *pb++ = rearFan.RPMval[RPM_MAX];
\ 00000146 9100.... LDS R16, (rearFan + 7)
\ 0000014A 930D ST X+, R16
129 *pb++ = rearFan.minTEMPval;
\ 0000014C 9100.... LDS R16, (rearFan + 8)
\ 00000150 930D ST X+, R16
130 *pb++ = rearFan.maxTEMPval;
\ 00000152 9100.... LDS R16, (rearFan + 9)
\ 00000156 930D ST X+, R16
131 *pb++ = sideFan.RPMval[RPM_MIN];
\ 00000158 9100.... LDS R16, (sideFan + 5)
\ 0000015C 930D ST X+, R16
132 *pb++ = sideFan.RPMval[RPM_MED];
\ 0000015E 9100.... LDS R16, (sideFan + 6)
\ 00000162 930D ST X+, R16
133 *pb++ = sideFan.RPMval[RPM_MAX];
\ 00000164 9100.... LDS R16, (sideFan + 7)
\ 00000168 930D ST X+, R16
134 *pb++ = sideFan.minTEMPval;
\ 0000016A 9100.... LDS R16, (sideFan + 8)
\ 0000016E 930D ST X+, R16
135 *pb++ = sideFan.maxTEMPval;
\ 00000170 9100.... LDS R16, (sideFan + 9)
\ 00000174 930D ST X+, R16
136 *pb++ = frontFan.RPMval[RPM_MIN];
\ 00000176 9100.... LDS R16, (frontFan + 5)
\ 0000017A 930D ST X+, R16
137 *pb++ = frontFan.RPMval[RPM_MED];
\ 0000017C 9100.... LDS R16, (frontFan + 6)
\ 00000180 930D ST X+, R16
138 *pb++ = frontFan.RPMval[RPM_MAX];
\ 00000182 9100.... LDS R16, (frontFan + 7)
\ 00000186 930D ST X+, R16
139 *pb++ = frontFan.minTEMPval;
\ 00000188 9100.... LDS R16, (frontFan + 8)
\ 0000018C 930D ST X+, R16
140 *pb++ = frontFan.maxTEMPval;
\ 0000018E 9100.... LDS R16, (frontFan + 9)
\ 00000192 CF96 RJMP ??usartSendCommand_12
141 break;
вполне прилично.
А вот у GCC:
Код
case USART_SEND_DATA_EX:
length = 4 + 4 + 4 + 20;
*pb++ = length + 1;
153c: 81 e2 ldi r24, 0x21; 33
153e: 80 93 ed 01 sts 0x01ED, r24
*pb++ = command;
1542: 90 93 ee 01 sts 0x01EE, r25
*pb++ = temperature.cpu;
1546: 80 91 34 01 lds r24, 0x0134
154a: 80 93 ef 01 sts 0x01EF, r24
*pb++ = temperature.gpu;
154e: 80 91 35 01 lds r24, 0x0135
1552: 80 93 f0 01 sts 0x01F0, r24
*pb++ = temperature.amb;
1556: 80 91 36 01 lds r24, 0x0136
155a: 80 93 f1 01 sts 0x01F1, r24
*pb++ = temperature.hdd;
155e: 80 91 37 01 lds r24, 0x0137
1562: 80 93 f2 01 sts 0x01F2, r24
*pb++ = fan_speed[0];
1566: 80 91 6f 01 lds r24, 0x016F
156a: 80 93 f3 01 sts 0x01F3, r24
*pb++ = fan_speed[1];
156e: 80 91 70 01 lds r24, 0x0170
1572: 80 93 f4 01 sts 0x01F4, r24
*pb++ = fan_speed[2];
1576: 80 91 71 01 lds r24, 0x0171
157a: 80 93 f5 01 sts 0x01F5, r24
*pb++ = fan_speed[3];
157e: 80 91 72 01 lds r24, 0x0172
1582: 80 93 f6 01 sts 0x01F6, r24
*pb++ = cpuFan.GetCurrentSpeed();
1586: 87 e3 ldi r24, 0x37; 55
1588: 92 e0 ldi r25, 0x02; 2
158a: 4c d7 rcall .+3736 ; 0x2424 <__data_load_end+0x6fe>
158c: 80 93 f7 01 sts 0x01F7, r24
*pb++ = rearFan.GetCurrentSpeed();
1590: 81 e4 ldi r24, 0x41; 65
1592: 92 e0 ldi r25, 0x02; 2
1594: 47 d7 rcall .+3726 ; 0x2424 <__data_load_end+0x6fe>
1596: 80 93 f8 01 sts 0x01F8, r24
*pb++ = sideFan.GetCurrentSpeed();
159a: 8b e4 ldi r24, 0x4B; 75
159c: 92 e0 ldi r25, 0x02; 2
159e: 42 d7 rcall .+3716 ; 0x2424 <__data_load_end+0x6fe>
15a0: 80 93 f9 01 sts 0x01F9, r24
*pb++ = frontFan.GetCurrentSpeed();
15a4: 85 e5 ldi r24, 0x55; 85
15a6: 92 e0 ldi r25, 0x02; 2
15a8: 3d d7 rcall .+3706 ; 0x2424 <__data_load_end+0x6fe>
15aa: 80 93 fa 01 sts 0x01FA, r24
*pb++ = cpuFan.RPMval[RPM_MIN];
15ae: 80 91 3c 02 lds r24, 0x023C
15b2: 80 93 fb 01 sts 0x01FB, r24
*pb++ = cpuFan.RPMval[RPM_MED];
15b6: 80 91 3d 02 lds r24, 0x023D
15ba: 80 93 fc 01 sts 0x01FC, r24
*pb++ = cpuFan.RPMval[RPM_MAX];
15be: 80 91 3e 02 lds r24, 0x023E
15c2: 80 93 fd 01 sts 0x01FD, r24
*pb++ = cpuFan.minTEMPval;
15c6: 80 91 3f 02 lds r24, 0x023F
15ca: 80 93 fe 01 sts 0x01FE, r24
*pb++ = cpuFan.maxTEMPval;
15ce: 80 91 40 02 lds r24, 0x0240
15d2: 80 93 ff 01 sts 0x01FF, r24
*pb++ = rearFan.RPMval[RPM_MIN];
15d6: 80 91 46 02 lds r24, 0x0246
15da: 80 93 00 02 sts 0x0200, r24
*pb++ = rearFan.RPMval[RPM_MED];
15de: 80 91 47 02 lds r24, 0x0247
15e2: 80 93 01 02 sts 0x0201, r24
*pb++ = rearFan.RPMval[RPM_MAX];
15e6: 80 91 48 02 lds r24, 0x0248
15ea: 80 93 02 02 sts 0x0202, r24
*pb++ = rearFan.minTEMPval;
15ee: 80 91 49 02 lds r24, 0x0249
15f2: 80 93 03 02 sts 0x0203, r24
*pb++ = rearFan.maxTEMPval;
15f6: 80 91 4a 02 lds r24, 0x024A
15fa: 80 93 04 02 sts 0x0204, r24
*pb++ = sideFan.RPMval[RPM_MIN];
15fe: 80 91 50 02 lds r24, 0x0250
1602: 80 93 05 02 sts 0x0205, r24
*pb++ = sideFan.RPMval[RPM_MED];
1606: 80 91 51 02 lds r24, 0x0251
160a: 80 93 06 02 sts 0x0206, r24
*pb++ = sideFan.RPMval[RPM_MAX];
160e: 80 91 52 02 lds r24, 0x0252
1612: 80 93 07 02 sts 0x0207, r24
*pb++ = sideFan.minTEMPval;
1616: 80 91 53 02 lds r24, 0x0253
161a: 80 93 08 02 sts 0x0208, r24
*pb++ = sideFan.maxTEMPval;
161e: 80 91 54 02 lds r24, 0x0254
1622: 80 93 09 02 sts 0x0209, r24
*pb++ = frontFan.RPMval[RPM_MIN];
1626: 80 91 5a 02 lds r24, 0x025A
162a: 80 93 0a 02 sts 0x020A, r24
*pb++ = frontFan.RPMval[RPM_MED];
162e: 80 91 5b 02 lds r24, 0x025B
1632: 80 93 0b 02 sts 0x020B, r24
*pb++ = frontFan.RPMval[RPM_MAX];
1636: 80 91 5c 02 lds r24, 0x025C
163a: 80 93 0c 02 sts 0x020C, r24
*pb++ = frontFan.minTEMPval;
163e: 80 91 5d 02 lds r24, 0x025D
1642: 80 93 0d 02 sts 0x020D, r24
*pb++ = frontFan.maxTEMPval;
1646: 80 91 5e 02 lds r24, 0x025E
164a: 80 93 0e 02 sts 0x020E, r24
164e: 10 e2 ldi r17, 0x20; 32
1650: cf e0 ldi r28, 0x0F; 15
1652: d2 e0 ldi r29, 0x02; 2
1654: 08 c0 rjmp .+16 ; 0x1666 <_Z16usartSendCommandhPKvh+0x24e>
break;
жаль, не догадался компилер использовать указатель
А зря. Итог - внушительный, в два раза больший, размер функции.
Также в коде есть одна немаленькая static inline функция, которая используется всего один раз (и то, понятно, инлайнится в тело вызывающей функции), но непонятно, почему остаётся её вторая копия?
За короткое время знакомства с GCC понравилось: удобная реализация атомарности через макросы ATOMIC_BLOCK(), удобная обёртка для обработчиков прерываний вида ISR(vector_name){}.
Что не понравилось: гиморрой с обработкой данных, расположенных во флеш - для их чтения необходимо использовать специальный макрос pgm_read_...(). В ИАРе достаточно объявить указатель типа __flash и можно работать с ним совершенно обычным образом
Также не очень удобно каждый раз пользоваться PSTR() для обозначения in-line строк вида: printText(PSTR("some")).
ИАР тут опять на высоте со своим __flash, так как подобная функция в нём декларируется как printText(char const __flash __flash * pointer), и вызов делается просто: printText("that`s cool")