Перейти к содержанию
    

Здравствуйте.

 

Была в проекте написана простая функция преобразования числа uint32 в строку, без оптимизации работает, при включении Optimization/High/Speed и наличии опции Function Inlining вместо всего числа в строку заносит только последнюю цифру. Функцию конечно поправил так чтоб работала и при оптимизации но как то все равно неприятно, где еще ждать косяков.

сам код:

 

//
///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

Было обнаружено на STM32F103, проверено наличие бага и для LPC2378

 

Спасибо.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что Вы хотели донести до общественности? Правильнее выложить asm-листинги и указать различия до и после оптимизации.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что Вы хотели донести до общественности? Правильнее выложить asm-листинги и указать различия до и после оптимизации.

1. Есть баги изменяющие работающий без оптимизации алгоритм

2. выложен проект, кто хочет - скомпилит и посмотрит сам.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1. Есть баги изменяющие работающий без оптимизации алгоритм

2. выложен проект, кто хочет - скомпилит и посмотрит сам.

Т.е. Вы не в состоянии указать почему эти баги появились/исчезли? Если хотите обвинить оптимизацию в иаре, то потрудитесь выложить асм код до/после оптимизации и покажите на "IAR 6.4 Optimization Bug".

но как то все равно неприятно, где еще ждать косяков.

С таким подходом везде... =)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Была в проекте написана простая функция преобразования числа uint32 в строку

Вы меня извините, но это далеко не простая функция, сразу бросаются в глаза алгоритмические косяки:

 

- вычисление степени 10 в цикле (facepalm)

(это можно сделать таблицей, да и вообще без степеней можно обойтись)

 

- вычисление очередной цифры циклом вычитания (facepalm)

(про деление автору ничего неизвестно?)

 

- выход из цикла с лишним сравнением условия (l=1) (facepalm)

(банальный break там просится)

 

ИМХО, тут не в компиляторе "собака порылась".

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вы меня извините, но это далеко не простая функция, сразу бросаются в глаза алгоритмические косяки:

ИМХО, тут не в компиляторе "собака порылась".

Речь не о кривости алгоритма а о разной его работе при отключенной и включенной оптимизации в компиляторе.

про степень согласен, но тогда баг скорее всего бы не вылез :-)

деление - писалось под ARM7TDMI, там нет аппаратного деления, был бы вызов библиотечной функции

break - просто мне он не нравится, так же как и return из середины функции.

 

2Lotr: это просто сообщение о наличии проблемы в IAR 6.4 и проект где она воспроизводится. Я ее обошел, мне не мешает. Если кому то надо то он прочитает и разберется сам.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

это просто сообщение о наличии проблемы в IAR 6.4 и проект где она воспроизводится. Я ее обошел, мне не мешает. Если кому то надо то он прочитает и разберется сам.

Я устал намекать, что скорее всего проблема не в оптимизаторе компилятора. Обходили Вы свои же косяки.

 

PS: Вам нужно самому разобраться и осознать, что 99% ошибок в наши дни - именно авторские, а не компилятора/кристалла.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Речь не о кривости алгоритма а о разной его работе при отключенной и включенной оптимизации в компиляторе.

про степень согласен, но тогда баг скорее всего бы не вылез :-)

Ну я вообще-то осторожно намекал что там, скорее всего, еще косяки есть, а не только "бросившиеся в глаза".

 

деление - писалось под ARM7TDMI, там нет аппаратного деления, был бы вызов библиотечной функции

А почему не устраивает библиотечная функция? Недостаточно быстрая для использования при выводе строки? Ну если уж такие требования к скорости, то надо было бы рассмотреть алгоритмы быстрого деления на константу (через умножение, Кнут, том 2). И сделать две явные версии - для процессоров с аппаратным делением и без (а при использовании библиотеки это автоматом).

 

ИМХО, разумным (чтобы не заводить таблицу из 10000 слов) по скорости для вывода в ASCII числа 0-9999 было бы одно деление на 100 с остатком, и вывод двух чисел 0-99 (частного и остатка) по готовой табличке ('00' .. '99'). В этом одном делении, даже универсальном (не константу, на любое число) программном в библиотечной функции, было бы максимум 8 итераций цикла деления. Сравните со своими средним количеством циклов 20 (циклы вычисления степени не учитываем).

 

break - просто мне он не нравится, так же как и return из середины функции.

OK, имейте лишнюю проверку условия, раз уж нравится (за скорость уже не боремся?). И необходимость полной ревизии цикла, если условие в цикле надо будет изменить. "return" - то несколько из другой оперы.

 

ЗЫ: Я тоже думаю что 99% компилятор тут не при чем. "Листинг в студию, сестра!" ©

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кстати, и мне интересно. Без доказательств всё это выглядит как наброс. Нет, ЯР не безгрешен, но гораздо чаще встречаются косяки программиста.

Список доказательств:

1) исходники (вроде бы есть)

2) правильный (ожидаемый) результат работы программы

3) неправильный результат

4) дизассембленый листинг кода, давшего неправильный результат

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

в архиве проект под IAR, оптимизация включена. Запускаем симулятор. Содержимое buf после выполнения функции {0,0,'7',0,0}

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так вы бы листинг и привели с указанием ошибки!

Честно говоря такую кривую функцию даже компилировать не хочется!

 

Если уж нужна быстрая - так у всех АРМ есть умножение и можно обойтись без циклов, примерно так (для 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';

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может быть, кому-то захочется покопаться:

Исходник (орфография и пунктуация автора сохранены):

#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;
}

Дизассемблер:

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'.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Видно что

buf[idx]++;

компилятор выкинул

 

Кстати если inline отключить, т.е. и Pow и Int2Str будут как функции!

Все равно buf[idx]++; выкидывает!

 

 

На самом деле бага довольно серьезная!!! И может вылезти в проектах :(

Если в цикле используется buf++, а после цикла присваивание последнему элементу массива проявляется!

 

Для иллюстрации 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=0;

}

buf=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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Может я не то что-то делаю, но у меня на 6.10 (другого нет) корректно работает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

так писали про 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=0, не учитывает что в цикле индекс меняется и buf это разные элементы массива.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...