|
|
  |
IAR 6.4 Optimization Bug, изменение работы алгоритма при включении оптимизации |
|
|
|
Oct 3 2012, 05:50
|
Местный
  
Группа: Свой
Сообщений: 207
Регистрация: 26-01-06
Из: СПб
Пользователь №: 13 659

|
Здравствуйте. Была в проекте написана простая функция преобразования числа uint32 в строку, без оптимизации работает, при включении Optimization/High/Speed и наличии опции Function Inlining вместо всего числа в строку заносит только последнюю цифру. Функцию конечно поправил так чтоб работала и при оптимизации но как то все равно неприятно, где еще ждать косяков. сам код: CODE // ///8************************************************* // перевод числа i в строку символов в buf длиной = maxlen uint32_t Int2Str(uint32_t i,uint8_t *buf,uint32_t maxlen){ uint32_t j; uint32_t k,l; uint32_t idx;
idx=0; // используется только для преобразования даты if((i<10000)&&(maxlen)&&(maxlen<=4)){ for(j=(maxlen-1);j;j--){ buf[idx]='0'; k=Pow10(j); for(l=9;l;l--){ if(i>=k){ i-=k; buf[idx]++; }else l=1; // выход из цикла } idx++; } buf[idx]='0'+i; idx++; } // buf[idx]=0; return idx; }
///8************************************************* uint32_t Pow10(uint32_t p){// возвращает 10^p uint32_t ret=1; if(p>8)ret=0; else{ for(;p;p--)ret*=10; } return ret; }
Отдельный проект с этими функциями в IAR:
iarbug.rar ( 15.32 килобайт )
Кол-во скачиваний: 76Было обнаружено на STM32F103, проверено наличие бага и для LPC2378 Спасибо.
|
|
|
|
|
Oct 3 2012, 06:17
|
Местный
  
Группа: Свой
Сообщений: 207
Регистрация: 26-01-06
Из: СПб
Пользователь №: 13 659

|
Цитата(Lotor @ Oct 3 2012, 10:01)  Что Вы хотели донести до общественности? Правильнее выложить asm-листинги и указать различия до и после оптимизации. 1. Есть баги изменяющие работающий без оптимизации алгоритм 2. выложен проект, кто хочет - скомпилит и посмотрит сам.
|
|
|
|
|
Oct 3 2012, 06:50
|
Местный
  
Группа: Свой
Сообщений: 476
Регистрация: 3-07-07
Из: Санкт-Петербург
Пользователь №: 28 866

|
Цитата(Sagittarius @ Oct 3 2012, 10:17)  1. Есть баги изменяющие работающий без оптимизации алгоритм 2. выложен проект, кто хочет - скомпилит и посмотрит сам. Т.е. Вы не в состоянии указать почему эти баги появились/исчезли? Если хотите обвинить оптимизацию в иаре, то потрудитесь выложить асм код до/после оптимизации и покажите на "IAR 6.4 Optimization Bug". Цитата(Sagittarius @ Oct 3 2012, 09:50)  но как то все равно неприятно, где еще ждать косяков. С таким подходом везде... =)
--------------------
Ковырял чукча отверткой в ухе, звук в телевизоре и пропал.
|
|
|
|
|
Oct 3 2012, 06:55
|

embarrassed systems engineer
    
Группа: Свой
Сообщений: 1 083
Регистрация: 24-10-05
Из: Осокорки
Пользователь №: 10 038

|
Цитата(Sagittarius @ Oct 3 2012, 08:50)  Была в проекте написана простая функция преобразования числа uint32 в строку Вы меня извините, но это далеко не простая функция, сразу бросаются в глаза алгоритмические косяки: - вычисление степени 10 в цикле (facepalm) (это можно сделать таблицей, да и вообще без степеней можно обойтись) - вычисление очередной цифры циклом вычитания (facepalm) (про деление автору ничего неизвестно?) - выход из цикла с лишним сравнением условия (l=1) (facepalm) (банальный break там просится) ИМХО, тут не в компиляторе "собака порылась".
|
|
|
|
|
Oct 3 2012, 07:43
|
Местный
  
Группа: Свой
Сообщений: 207
Регистрация: 26-01-06
Из: СПб
Пользователь №: 13 659

|
Цитата(VslavX @ Oct 3 2012, 10:55)  Вы меня извините, но это далеко не простая функция, сразу бросаются в глаза алгоритмические косяки: ИМХО, тут не в компиляторе "собака порылась". Речь не о кривости алгоритма а о разной его работе при отключенной и включенной оптимизации в компиляторе. про степень согласен, но тогда баг скорее всего бы не вылез :-) деление - писалось под ARM7TDMI, там нет аппаратного деления, был бы вызов библиотечной функции break - просто мне он не нравится, так же как и return из середины функции. 2Lotr: это просто сообщение о наличии проблемы в IAR 6.4 и проект где она воспроизводится. Я ее обошел, мне не мешает. Если кому то надо то он прочитает и разберется сам.
|
|
|
|
|
Oct 3 2012, 08:31
|
Местный
  
Группа: Свой
Сообщений: 476
Регистрация: 3-07-07
Из: Санкт-Петербург
Пользователь №: 28 866

|
Цитата(Sagittarius @ Oct 3 2012, 11:43)  это просто сообщение о наличии проблемы в IAR 6.4 и проект где она воспроизводится. Я ее обошел, мне не мешает. Если кому то надо то он прочитает и разберется сам. Я устал намекать, что скорее всего проблема не в оптимизаторе компилятора. Обходили Вы свои же косяки. PS: Вам нужно самому разобраться и осознать, что 99% ошибок в наши дни - именно авторские, а не компилятора/кристалла.
--------------------
Ковырял чукча отверткой в ухе, звук в телевизоре и пропал.
|
|
|
|
|
Oct 3 2012, 09:01
|

embarrassed systems engineer
    
Группа: Свой
Сообщений: 1 083
Регистрация: 24-10-05
Из: Осокорки
Пользователь №: 10 038

|
Цитата(Sagittarius @ Oct 3 2012, 10:43)  Речь не о кривости алгоритма а о разной его работе при отключенной и включенной оптимизации в компиляторе. про степень согласен, но тогда баг скорее всего бы не вылез :-) Ну я вообще-то осторожно намекал что там, скорее всего, еще косяки есть, а не только "бросившиеся в глаза". Цитата(Sagittarius @ Oct 3 2012, 10:43)  деление - писалось под ARM7TDMI, там нет аппаратного деления, был бы вызов библиотечной функции А почему не устраивает библиотечная функция? Недостаточно быстрая для использования при выводе строки? Ну если уж такие требования к скорости, то надо было бы рассмотреть алгоритмы быстрого деления на константу (через умножение, Кнут, том 2). И сделать две явные версии - для процессоров с аппаратным делением и без (а при использовании библиотеки это автоматом). ИМХО, разумным (чтобы не заводить таблицу из 10000 слов) по скорости для вывода в ASCII числа 0-9999 было бы одно деление на 100 с остатком, и вывод двух чисел 0-99 (частного и остатка) по готовой табличке ('00' .. '99'). В этом одном делении, даже универсальном (не константу, на любое число) программном в библиотечной функции, было бы максимум 8 итераций цикла деления. Сравните со своими средним количеством циклов 20 (циклы вычисления степени не учитываем). Цитата(Sagittarius @ Oct 3 2012, 10:43)  break - просто мне он не нравится, так же как и return из середины функции. OK, имейте лишнюю проверку условия, раз уж нравится (за скорость уже не боремся?). И необходимость полной ревизии цикла, если условие в цикле надо будет изменить. "return" - то несколько из другой оперы. ЗЫ: Я тоже думаю что 99% компилятор тут не при чем. "Листинг в студию, сестра!" ©
|
|
|
|
|
Oct 3 2012, 11:21
|
Местный
  
Группа: Свой
Сообщений: 207
Регистрация: 26-01-06
Из: СПб
Пользователь №: 13 659

|
в архиве проект под IAR, оптимизация включена. Запускаем симулятор. Содержимое buf после выполнения функции {0,0,'7',0,0} Отключаем оптимизацию, запускаем. Содержимое буфера {'7','7','7',0,0}. По поводу косячности функции - была проверена записью в файл и последующем чтении из файла в диапазоне входных значений 0-9999, результат корректен Задачи оптимизации данной функции не ставилось.
|
|
|
|
|
Oct 3 2012, 11:44
|

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

|
Так вы бы листинг и привели с указанием ошибки! Честно говоря такую кривую функцию даже компилировать не хочется! Если уж нужна быстрая - так у всех АРМ есть умножение и можно обойтись без циклов, примерно так (для 4 разрядных чисел) Код d=(v*8389)>>23; v=v-d*1000; buf[0]=d+'0'; d=(v*41)>>12; v=v-d*100; buf[1]=d+'0'; d=(v*103)>>10; buf[2]=d+'0'; v=v-d*10; buf[3]=d+'0';
|
|
|
|
|
Oct 3 2012, 11:47
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Может быть, кому-то захочется покопаться: Исходник (орфография и пунктуация автора сохранены): CODE #include <ctype.h> #include <stdint.h> #include <intrinsics.h>
uint32_t Int2Str(uint32_t i,uint8_t *buf,uint32_t maxlen); uint32_t Pow10(uint32_t p);
volatile uint8_t buf[5];
void main(void){ // Int2Str(777,buf,3); while(1){ Int2Str(777,(uint8_t*)buf,3); buf[0]++; buf[2]++; buf[4]++; } }
///8************************************************* // перевод числа i в строку символов в buf длиной = maxlen uint32_t Int2Str(uint32_t i,uint8_t *buf,uint32_t maxlen){ uint32_t j; uint32_t k,l; uint32_t idx;
idx=0; // используется только для преобразования даты if((i<10000)&&(maxlen)&&(maxlen<=4)){ for(j=(maxlen-1);j;j--){ buf[idx]='0'; k=Pow10(j); for(l=9;l;l--){ if(i>=k){ i-=k; buf[idx]++; }else l=1; // выход из цикла } idx++; } buf[idx]='0'+i; idx++; } // buf[idx]=0; return idx; }
/* // корректно оптимизируемый код uint32_t Int2Str(uint32_t i,uint8_t *buf,uint32_t maxlen){ uint32_t j; uint32_t k,l; uint32_t idx;
idx=0; // используется только для преобразования даты if((i<10000)&&(maxlen)&&(maxlen<=4)){ for(j=(maxlen-1);j;j--){ *buf='0'; k=Pow10(j); for(l=9;l;l--){ if(i>=k){ i-=k; (*buf)++; }else l=1; // выход из цикла } buf++; } *buf='0'+i; idx++; } // buf[idx]=0; return idx; }
*/
///8************************************************* uint32_t Pow10(uint32_t p){// возвращает 10^p uint32_t ret=1; if(p>8)ret=0; else{ for(;p;p--)ret*=10; } return ret; }
Дизассемблер: CODE void main(void){ main: 0x2f4: 0x480f LDR.N R0, ??DataTable1 ; buf 0x2f6: 0xe00f B.N ??main_0 ; 0x318 }else l=1; // auoia ec oeeea ??main_1: 0x2f8: 0x2601 MOVS R6, #1 for(l=9;l;l--){ ??main_2: 0x2fa: 0x1e76 SUBS R6, R6, #1 0x2fc: 0xd116 BNE.N ??main_3 ; 0x32c for(j=(maxlen-1);j;j--){ 0x2fe: 0x1e64 SUBS R4, R4, #1 for(j=(maxlen-1);j;j--){ 0x300: 0xd10c BNE.N ??main_4 ; 0x31c buf[idx]='0'+i; 0x302: 0x3330 ADDS R3, R3, #48 ; 0x30 0x304: 0x7083 STRB R3, [R0, #0x2] return idx; 0x306: 0x7803 LDRB R3, [R0] 0x308: 0x1c5b ADDS R3, R3, #1 0x30a: 0x7003 STRB R3, [R0] 0x30c: 0x7883 LDRB R3, [R0, #0x2] 0x30e: 0x1c5b ADDS R3, R3, #1 0x310: 0x7083 STRB R3, [R0, #0x2] 0x312: 0x7903 LDRB R3, [R0, #0x4] 0x314: 0x1c5b ADDS R3, R3, #1 0x316: 0x7103 STRB R3, [R0, #0x4] ??main_0: 0x318: 0x4b07 LDR.N R3, ??DataTable1_1 ; 0x309 (777) for(j=(maxlen-1);j;j--){ 0x31a: 0x2402 MOVS R4, #2 uint32_t ret=1; ??main_4: 0x31c: 0x2501 MOVS R5, #1 if(p>8)ret=0; 0x31e: 0x0026 MOVS R6, R4 for(;p;p--)ret*=10; ??main_5: 0x320: 0x00af LSLS R7, R5, #2 0x322: 0x197d ADDS R5, R7, R5 0x324: 0x006d LSLS R5, R5, #1 for(;p;p--)ret*=10; 0x326: 0x1e76 SUBS R6, R6, #1 for(;p;p--)ret*=10; 0x328: 0xd1fa BNE.N ??main_5 ; 0x320 for(l=9;l;l--){ 0x32a: 0x2609 MOVS R6, #9 if(i>=k){ ??main_3: 0x32c: 0x42ab CMP R3, R5 0x32e: 0xd3e3 BCC.N ??main_1 ; 0x2f8 i-=k; 0x330: 0x1b5b SUBS R3, R3, R5 buf[idx]++; 0x332: 0xe7e2 B.N ??main_2 ; 0x2fa ??DataTable1: 0x334: 0x40000120 DC32 buf ??DataTable1_1: 0x338: 0x00000309 DC32 777 ; ' ...' exit: 0x33c: 0xb580 PUSH {R7, LR} 0x33e: 0xf000 0xf825 BL ?Veneer (4) for _exit ; 0x38c 0x342: 0xbc09 POP {R0, R3} 0x344: 0x4718 BX R3
Да, код заполняет в буфере только позицию buf[2] = '7'.
|
|
|
|
|
Oct 3 2012, 12:30
|

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

|
Видно что buf[idx]++; компилятор выкинул Кстати если inline отключить, т.е. и Pow и Int2Str будут как функции! Все равно buf[idx]++; выкидывает! На самом деле бага довольно серьезная!!! И может вылезти в проектах Если в цикле используется buf[i]++, а после цикла присваивание последнему элементу массива проявляется! Для иллюстрации 2 простых функции Код void test_bug(uint32_t v, uint8_t *buf) { unsigned i; for(i=0;i<3;i++){ buf[i]='0'; if (v & 1<<i) buf[i]++; } buf[i]=v & 0x8; }
void test_bug2(uint32_t v, uint8_t *buf) { unsigned i; for(i=0;i<3;i++){ buf[i]='0'; if (v & 1<<i) buf[i]++; } } А вот листинг Код 19 void test_bug(uint32_t v, uint8_t *buf) 20 { 21 unsigned i; 22 for(i=0;i<3;i++){ 23 buf[i]='0'; 24 if (v & 1<<i) 25 buf[i]++; 26 } 27 buf[i]=v & 0x8; \ test_bug: \ 00000000 0x2208 MOVS R2,#+8 \ 00000002 0x4010 ANDS R0,R0,R2 \ 00000004 0x70C8 STRB R0,[R1, #+3] 28 } \ 00000006 0x4770 BX LR ;; return 29
\ In section .text, align 2, keep-with-next 30 void test_bug2(uint32_t v, uint8_t *buf) 31 { \ test_bug2: \ 00000000 0xB418 PUSH {R3,R4} 32 unsigned i; 33 for(i=0;i<3;i++){ \ 00000002 0x2230 MOVS R2,#+48 34 buf[i]='0'; \ 00000004 0x2330 MOVS R3,#+48 35 if (v & 1<<i) \ 00000006 0x07C4 LSLS R4,R0,#+31 \ 00000008 0xD500 BPL ??test_bug2_0 36 buf[i]++; \ 0000000A 0x2331 MOVS R3,#+49 \ ??test_bug2_0: \ 0000000C 0x700B STRB R3,[R1, #+0] \ 0000000E 0x2330 MOVS R3,#+48 \ 00000010 0x0784 LSLS R4,R0,#+30 \ 00000012 0xD500 BPL ??test_bug2_1 \ 00000014 0x2331 MOVS R3,#+49 \ ??test_bug2_1: \ 00000016 0x704B STRB R3,[R1, #+1] \ 00000018 0x0740 LSLS R0,R0,#+29 \ 0000001A 0xD500 BPL ??test_bug2_2 \ 0000001C 0x2231 MOVS R2,#+49 \ ??test_bug2_2: \ 0000001E 0x708A STRB R2,[R1, #+2] 37 } 38 } \ 00000020 0xBC11 POP {R0,R4} \ 00000022 0x4770 BX LR ;; return
\ In section .text, align 4, keep-with-next \ ??DataTable0: \ 00000000 0x........ DC32 buf Видно что в первом варианте код весь убран! Вы будете смеяться!!!все еще проще  void test_bug3(uint8_t *buf) { unsigned i; for(i=0;i<3;i++){ buf[i]=0; } buf[i]=0; } Код In section .text, align 2, keep-with-next 40 void test_bug3(uint8_t *buf) 41 { 42 unsigned i; 43 for(i=0;i<3;i++){ 44 buf[i]=0; 45 } 46 buf[i]=0; \ test_bug3: \ 00000000 0x2100 MOVS R1,#+0 \ 00000002 0x70C1 STRB R1,[R0, #+3] 47 } \ 00000004 0x4770 BX LR ;; return
|
|
|
|
|
Oct 3 2012, 12:52
|

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

|
так писали про 6.4 у меня IAR ANSI C/C++ Compiler V6.40.1.53790/W32 for ARM в итоге самый простой код приводящий к ошибке Код void test_bug3(uint8_t *buf) { unsigned i; for(i=0;i<100;i++){ buf[i]=0; } buf[i]=0; } в цикле константа может быть любая! (пробовал 3,4,7,100) видимо ЯР видя за циклом buf[i]=0, не учитывает что в цикле индекс меняется и buf[i] это разные элементы массива.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|