|
|
  |
ARM7 сравнение компиляторов, провел небольшое исследование |
|
|
|
Oct 29 2009, 14:12
|
Частый гость
 
Группа: Свой
Сообщений: 163
Регистрация: 24-08-05
Пользователь №: 7 937

|
Цитата(Rst7 @ Oct 29 2009, 16:49)  Листинг в студию. http://tnkernel.com/downloads/tnkernel-2-5...xM3-LPC1766.zipThis ZIP archive contains: - A latest TNKernel Cortex-M3 port version (2.5.3) - An examples(source code) for the NXP© LPC1766 MCU - A projects for the Rowley CrossWorks Studio 1.7, Keil RVC v.3.xx, IAR ARM v.5.xx, GCC 4.3.2 (Codesourcery 2008q3-66)
|
|
|
|
|
Oct 29 2009, 14:24
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
На примере простого кода, работы с битовыми полями, я его уже давно как то выкладывал, для версий 4.42, 5.11 и RVCT (уже не помню какого) Код struct bf_s { unsigned f1:6; unsigned f2:2; unsigned f3:20; unsigned f4:4; } bf;
void short_clear(void) { bf.f2 = 0; }
void short_set(void) { bf.f2 = 3; }
void short_const(void) { bf.f2 = 1; }
void short_var(unsigned val) { bf.f2 = val; }
void long_clear(void) { bf.f3 = 0; }
void long_set(void) { bf.f3 = (1<<20)-1; }
void long_const(void) { bf.f3 = 1; }
void long_var(unsigned val) { bf.f3 = val; }
void long_var_if(unsigned val,unsigned cond) { if (cond) bf.f3 = val; else bf.f4=val; }
unsigned a; unsigned b; unsigned c; void var_if(unsigned val) { if (val) a = c; else b = c; c = val; } Сейчас скомпилил последним иаром V5.40.2.51604/W32 Улучшений серьезных нет  BFI и условное выполнение до сих пор IAR делать не умеет! Для примера листинг одной функции Код RVCT -O3 -Otime --cpu Cortex-M3 long_var_if PROC LDR r3,|L1.176| CMP r1,#0 LDR r2,[r3,#0] ITE EQ BFIEQ r2,r0,#28,#4 BFINE r2,r0,#8,#20 STR r2,[r3,#0] ; bf BX lr ENDP
IAR --cpu Cortex-M3 -Ohs long_var_if: LDR.N R2,??DataTable13 ;; bf LDR R3,[R2, #+0] CBZ R1,??long_var_if_0 LDR.N R1,??DataTable14 ;; 0xf00000ff ANDS R1,R1,R3 LDR.N R3,??DataTable15 ;; 0xfffff00 AND R0,R3,R0, LSL #+8 ORRS R0,R0,R1 B.N ??long_var_if_1 ??long_var_if_0: LSLS R1,R3,#+4 LSRS R1,R1,#+4 ORR R0,R1,R0, LSL #+28 ??long_var_if_1: STR R0,[R2, #+0]
BX LR
|
|
|
|
|
Oct 29 2009, 16:18
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(KRS @ Oct 29 2009, 17:24)  На примере простого кода, работы с битовыми полями, я его уже давно как то выкладывал, для версий 4.42, 5.11 и RVCT (уже не помню какого) Еще раз посомтрел листинги, и заодно GNUC попробовал. GNUC - BFI использует! А вот условное выполнение нет  . А вот новый IAR оказывается умеет иногда использовать условное выполнение и почему не всегда - загадка, вот листинги последней функции Код GNUC gcc version 4.3.3 (Sourcery G++ Lite 2009q1-161) -O3 var_if: @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 @ link register save eliminated. push {r4} mov r4, r0 cbnz r0, .L29 movw r1, #:lower16:c movt r1, #:upper16:c movw r2, #:lower16:b ldr r3, [r1, #0] movt r2, #:upper16:b str r3, [r2, #0] .L27: str r4, [r1, #0] pop {r4} bx lr .L29: movw r1, #:lower16:c movt r1, #:upper16:c movw r2, #:lower16:a ldr r3, [r1, #0] movt r2, #:upper16:a str r3, [r2, #0] b .L27
IAR
var_if: LDR.N R1,??var_if_0 ;; a LDR R2,[R1, #+8] CMP R0,#+0 ITE NE STRNE R2,[R1, #+0] STREQ R2,[R1, #+4] STR R0,[R1, #+8]
BX LR ;; return
RVCT var_if PROC LDR r2,|L1.176| CMP r0,#0 LDR r1,[r2,#0xc] ITE EQ STREQ r1,[r2,#8] ; b STRNE r1,[r2,#4] ; a STR r0,[r2,#0xc] ; c BX lr ENDP как видно еще GNUC по другому подходит к загрузке адреса в регистр! Но IMHO это неоптимально, вместо одной 16 битной инструкции и 32 бит данных используется две 32 битных команды.
|
|
|
|
|
Nov 1 2009, 11:18
|

бессмертным стать можно тремя способами
    
Группа: Свой
Сообщений: 1 405
Регистрация: 9-05-06
Из: Москва
Пользователь №: 16 912

|
Генерит GCC условные команды IT я взял асм своего проека где Stm32 UsbLib использую - там вот обнаружился такой кусок к примеру Цитата void ClearDTOG_RX(uint8_t bEpNum) { 800160e: b2c0 uxtb r0, r0 _ClearDTOG_RX(bEpNum); 8001610: f853 2020 ldr.w r2, [r3, r0, lsl #2] 8001614: f412 4f80 tst.w r2, #16384 ; 0x4000 8001618: bf1f itttt ne 800161a: f853 1020 ldrne.w r1, [r3, r0, lsl #2] 800161e: f648 728f movwne r2, #36751 ; 0x8f8f 8001622: 400a andne r2, r1 8001624: f442 4280 orrne.w r2, r2, #16384 ; 0x4000 8001628: bf18 it ne 800162a: f843 2020 strne.w r2, [r3, r0, lsl #2] } 800162e: 4770 bx lr 8001630: 40005c00 .word 0x40005c00 далее я взял примерно такойже кусок кода как выше указывался: Код struct bf_s { unsigned f1:20; char f2:8; char f3:8; unsigned f4:20; } bf;
void long_var_if(char val,char cond) { if (cond) { bf.f3 = val; bf.f1 = val; bf.f2 = val; } else bf.f4=val; }
char var_if(char a , char b ) { char x = a; if (a) { x = b; } return x; } arm-kgp-elf-gcc -mthumb -march=armv7-m -mcpu=cortex-m3 -S -O3 test2.c результат: Код .syntax unified .thumb .file "test2.c" .text .align 2 .global long_var_if .thumb .thumb_func .type long_var_if, %function long_var_if: @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 @ link register save eliminated. tst r1, #255 itete ne ldrne r3, .L4 ldreq r3, .L4 ldrne r2, [r3, #0] ldreq r2, [r3, #4] uxtb r0, r0 itett ne bfine r2, r0, #0, #20 bfieq r2, r0, #8, #20 strne r2, [r3, #0] strbne r0, [r3, #4] ite ne strbne r0, [r3, #3] streq r2, [r3, #4] bx lr .L5: .align 2 .L4: .word bf .size long_var_if, .-long_var_if .align 2 .global var_if .thumb .thumb_func .type var_if, %function var_if: @ args = 0, pretend = 0, frame = 0 @ frame_needed = 0, uses_anonymous_args = 0 @ link register save eliminated. uxtb r1, r1 uxtb r0, r0 cmp r0, #0 ite ne movne r0, r1 moveq r0, #0 bx lr .size var_if, .-var_if .comm bf,8,4 .ident "GCC: (GNU) 4.5.0 20091029 (experimental)" из сего видно что GCC прекрасно генерит BFI и IT команды. Другое дело что не ВСЕГДА КОГДА НАМ ЭТО ОЧЕВИДНО! полазив по исходникам компилера я увидел что допустим IT или переходы, например BNE являются для него алтернативами в зависимоти от ситуации принимается решение генерить то или иное. Если учесть что IT способно обойти и загрузить конвеер Cortex'а не более чем на 4 инструкции то становится понятным почему короткий if компиллер генерит через IT а более длинный с его точки зрения (n>4) генерится переход. Есть желание думать что скорость нада проверять а не наличие тех или иных команд. собсно примерно так.
|
|
|
|
|
Nov 1 2009, 23:01
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(klen @ Nov 1 2009, 14:18)  из сего видно что GCC прекрасно генерит BFI и IT команды. Другое дело что не ВСЕГДА КОГДА НАМ ЭТО ОЧЕВИДНО! полазив по исходникам компилера я увидел что допустим IT или переходы, например BNE ... да это все известно и из описания архитектуры, для этого лезть в исходники компилера не надо! в моем варианте функции var_if - GNUC явно полез оптимизировать нетуда! в итоге функция неимоверно больше по размеру, использует стек! и явно медленне работает! Про BFI - я сразу говорил что гнусь это умеет! Вот что что а а все подобные иснтуркции гнусь всегда задействует. у IAR с BFI проблема пока. По поводу Вашей функции var_if - вообще смешно!!! RVCT - ее скомпилировал так Код var_if PROC CMP r0,#0 IT NE MOVNE r0,r1 BX lr ENDP по поводу UTX - конечно можно спорить, но это явно лишнее, раз указано что на входе char, дополнительно чистить его не надо, это должна делать вызывающая сторона! арифмитических опреаций с переменными здесь не проводится так что переполнения в старшие биты быть не может! листинг первой функции, выкладывать не буду, но он тоже оптимальнее и код красивее.
|
|
|
|
|
Nov 2 2009, 01:18
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Полуоффтоп: Не, ну таки странности у gcc проскакивают ещё те. Чистая культура кода: Код uint8_t pos(uint8_t num) // num = 0..8 { uint8_t pos = 0x40; while(num >= 3) { pos += 0x40; num -= 3; } pos += num * 7; return pos; } avr-gcc 4.3.2 (WinAVR-20090313) умудрился это дело скомпилировать так, как если бы было написано Код uint8_t pos(uint8_t num) { return (num/3+1)*0x40 + (num % 3)*7; } Он, конечно, правильно понял, что происходит, но назачем в этой короткой функции два вызова __udivmodqi4 ??? Более ранние скомпилировали что написано. Что интересно, эта 20090313 на объёме в около 5.5кБ несмотря на финт с делением таки дала код на сотню байт короче, чем 20071221. И 4.4.0 (Klen-20090207 и 20090323) тоже обошлись без /%, в этой конкретной функции ещё и аккуратнее регистры разложили и общий код ещё почти на сотню байт уменьшили. "но осадок остался"
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Jul 6 2012, 05:57
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046

|
Ни один компилятор не смог нормально соптимизировать простые очевидные вещи.. Код class tormoz{ public: void remove(); private: tormoz *next; tormoz *prev; };
void tormoz::remove(){ next->prev=prev; prev->next=next; } rvct -O3 -OtimeКод 00000134 <_ZN6tormoz6removeEv>: 134: e9d0 1200 ldrd r1, r2, [r0] 138: 604a str r2, [r1, #4] 13a: e9d0 1000 ldrd r1, r0, [r0] ;<<<---- Зачем?? 13e: 6001 str r1, [r0, #0] 140: 4770 bx lr gcc-4.7.1 -O3 -OtimeКод 00000000 <_ZN6tormoz6removeEv>: 0: 6803 ldr r3, [r0, #0] 2: 6842 ldr r2, [r0, #4] 4: 605a str r2, [r3, #4] 6: 6842 ldr r2, [r0, #4] ;<<<---- Зачем?? 8: 6013 str r3, [r2, #0] a: 4770 bx lr Вот так оптимизирует нормально на обеих компиляторах, но это ж не те уже времена, когда использовали ключевое слово register и вучную оптимизировали порядок загрузки.. Код void tormoz::remove(){ tormoz *n=next,*p=prev; n->prev=p; p->next=n; }
0: e890 000c ldmia.w r0, {r2, r3} 4: 6053 str r3, [r2, #4] 6: 601a str r2, [r3, #0] 8: 4770 bx lr
|
|
|
|
|
Jul 6 2012, 06:41
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(brag @ Jul 6 2012, 09:57)  Ни один компилятор не смог нормально соптимизировать простые очевидные вещи.. Очевидно же: После выполнения Код next->prev=next; Следующаяя строка кода может работать с совсем ДРУГОЙ памятью, нежели ДО выполнения предыдущей строчки. Оптимизатор просто не имеет права ничего удалять. Код prev->next=prev; В данном случае всё указано однозначно. Код void tormoz::remove(){ tormoz *n=next,*p=prev; n->prev=p; p->next=n; }
|
|
|
|
|
Jul 6 2012, 07:18
|
Профессионал
    
Группа: Свой
Сообщений: 1 047
Регистрация: 2-12-06
Из: Kyiv, Ukraine
Пользователь №: 23 046

|
Цитата Следующаяя строка кода может работать с совсем ДРУГОЙ памятью, нежели ДО выполнения предыдущей строчки. Оптимизатор просто не имеет права ничего удалять. Интересно с какой? Если будет next==prev всеравно поля next->prev,prev->prev и next->next,prev->next будут иметь разные адреса. Если next==prev==this тогда this->prev=this; this->next=this; Чет никак не могу придумать, как можно добится разного поведения от этих двух разных реализаций... апд. сории,в коде перепутал местами, но сути это не меняет,компилится так же.должно быть так Код next->prev=prev; prev->next=next;
11a: e9d0 1200 ldrd r1, r2, [r0] 11e: 604a str r2, [r1, #4] 120: e9d0 1000 ldrd r1, r0, [r0] 124: 6001 str r1, [r0, #0]
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|