Отговори на тема  [ 29 мнения ]  Отиди на страница Предишна  1, 2
бъг в оптимизатора на С30 (С за 30та серия на микрочип) 
Автор Съобщение
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Окт 31, 2004 9:19 pm
Мнения: 4464
Местоположение: Stara Zagora
Мнение 
ReadRSWord(); връща 16 битова стойност. И в твоя случай има от малък към голям


Сря Дек 26, 2007 7:47 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Окт 31, 2004 9:19 pm
Мнения: 4464
Местоположение: Stara Zagora
Мнение 
А за варианта с union-a не съм се сблъскавал с проблеми досега. Но мисля че си прав.
Верния код като за разнообразни платформи предполагам че е
Код:
u64  ReadRSDWord()
{
   union
   {
       u64 lg;
       struct
       {
           u64  low:16;
           u64  p1:16;
           u64  hi:16;
           u64  p2:16;
        };
   };
   
   lg   = 0;
   low =  ReadRSWord();
   hi   =  ReadRSWord();
   return lg;
}


Не се сещам за платформа на която няма да работи. Но може и да бъркам все пак.

Когато имах проблеми с visual studioto 6 с кастването го реших по следния начин. Трябва да се укаже ясно на компилатора да нулира старшите 16 бита.
Код:
unsigned long  ReadRSDWord()
{
   unsigned int dw[2];
   dw[0]=(unsigned int)ReadRSWord() & 0xFFFF;
   dw[1]=(unsigned int)ReadRSWord() & 0xFFFF;
   return *((unsigned long*)dw);   
}

а за поредноста на елементите не би трябвало да се притесняваш при 32 битови елементи и litle endian на процесора според мен. Ако е big endian трябва да размениш поредноста на членовете в които записваш само.
Иначе твоето решение наистина ще работи и при 2та вида еndian. но пак явно трябва да се приложи същия трик при кастването.

А ако наистина трябва кода да работи независимо от платформата това мисля че е варианта.
Код:
unsigned long  ReadRSDWord()
{
   unsigned long dw=0;
   dw |= ReadRSWord(); 
   return dw | (((unsigned long)ReadRSWord() & 0xFFFF) << 32); 
}


Сря Дек 26, 2007 7:50 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Юли 24, 2005 10:28 am
Мнения: 2658
Мнение 
миро, ти преди да пишеш пробва ли си решението? щото дава абсолютно същия код, т.е. грешен :lol: :lol: :lol:
един поглед на асемблера е достатъчен за да се види че компилатора забравя да вкара инструкция, какви масиви, какви пет лева, какво пакетиране. и къде видя кастване от голям към малък тип? функцията връща размер като елемента на масива - 16 битов инт.
за масивите и стандарта може и да си прав, макар че ако ми покажеш компилатор за който разликата между адрес на два елемента от масив не е sizeof(type), то ще съм искрено удивен.
със унион дава правилен код, но ето че на този компилатор синтаксиса на униона се оказа различен - иска задължително инстанция на униона и после обръщение към полетата все едно са полета в структура, въпреки че е безименен.


Сря Дек 26, 2007 8:14 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Окт 31, 2004 9:19 pm
Мнения: 4464
Местоположение: Stara Zagora
Мнение 
По анси стандарта мисля че при C няма безименни структури и union-и. Те са влезли вече при C++. Но повечето съвременни C компилатори ги поддържат.

Аз си имам дефинирани с typedef такива структури и си ги ползвам тях. За случая го направих така за да е ясно написано. Ето това ползвам с IAR и АРМ.
Код:
typedef union
{
     u64  _64;
     struct
     {
          u64 _0:16;
          u64 _1:16;
          u64 _2:16;
          u64 _3:16;
      }a16;
      struct
      {
          u64 _0:32;
          u64 _1:32;
       }a32;
}un64;


Сря Дек 26, 2007 8:26 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Апр 27, 2005 12:48 pm
Мнения: 6094
Мнение 
Taka написан кода наистина дава неверен резултат при -Os,
ОБАЧЕ
dereferencing type-punned pointer will break strict-aliasing rules [ return *((unsigned long*)&dw); ]

и това е така при активиране на -Os оптимизация
-fstrict-aliasing -
MPLAB_C30_Users_Guide_51284f.pdf "DS51284F-page 49"

-----------------------------------------------------------------------

typedef unsigned char BYTE; // 8-bit unsigned
typedef unsigned short int WORD; // 16-bit unsigned
typedef unsigned long DWORD; // 32-bit unsigned
typedef unsigned long long QWORD; // 64-bit unsigned
typedef signed char CHAR; // 8-bit signed
typedef signed short int SHORT; // 16-bit signed
typedef signed long LONG; // 32-bit signed
typedef signed long long LONGLONG; // 64-bit signed

typedef union _DWORD_VAL
{
DWORD Val;
WORD w[2];
BYTE v[4];
struct
{
WORD LW;
WORD HW;
} word;
struct
{
BYTE LB;
BYTE HB;
BYTE UB;
BYTE MB;
} byte;
struct
{
unsigned char b0:1;
unsigned char b1:1;
unsigned char b2:1;
unsigned char b3:1;
unsigned char b4:1;
unsigned char b5:1;
unsigned char b6:1;
unsigned char b7:1;
unsigned char b8:1;
unsigned char b9:1;
unsigned char b10:1;
unsigned char b11:1;
unsigned char b12:1;
unsigned char b13:1;
unsigned char b14:1;
unsigned char b15:1;
unsigned char b16:1;
unsigned char b17:1;
unsigned char b18:1;
unsigned char b19:1;
unsigned char b20:1;
unsigned char b21:1;
unsigned char b22:1;
unsigned char b23:1;
unsigned char b24:1;
unsigned char b25:1;
unsigned char b26:1;
unsigned char b27:1;
unsigned char b28:1;
unsigned char b29:1;
unsigned char b30:1;
unsigned char b31:1;
} bits;
} DWORD_VAL;

unsigned long C, D;
int DM = 0;

int F1() {
int i;
for(i=0;i<5;i++)
DM += i;
return DM;
}

unsigned long ReadRSDWord() {
DWORD_VAL dw;
dw.word.LW=F1();
dw.word.HW=F1();
return dw.Val;
}

int main(int argc, char * argv[]){
unsigned long A, B;
A = ReadRSDWord();
B = ReadRSDWord();
C = ReadRSDWord();
D = ReadRSDWord();
printf("A=0x%0lX\n", A);
printf("B=0x%0lX\n", B);
printf("C=0x%0lX\n", C);
printf("D=0x%0lX\n", D);
Nop();
return 0;
}


RESULT:
A=0x14000A
B=0x28001E
C=0x3C0032
D=0x500046

_________________
main[-1u]={1};


Сря Дек 26, 2007 8:26 pm
Профил ICQ
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Юли 24, 2005 10:28 am
Мнения: 2658
Мнение 
еее, браво TheWizard, това е правилния отговор, полезна информация.
Цитат:
In particular, an object of one type is assumed never to reside at
the same address as an object of a different type, unless the
types are almost the same.

все пак мисля че тъпото нещо поне предупреждение трябва да дава. това все пак е С, не джава, тук игричките с указатели са норма а не изключение. аз дълги години пиша за виндовс, никога не съм имал подобни проблеми, ако не ми беше обърнал внимание на тая опция, пак щях да го напиша някъде. сега ми се върти в главата че още някъде в кода имам подобно нещо, ще го издиря.


Сря Дек 26, 2007 8:50 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог

Регистриран на: Нед Фев 26, 2006 6:52 pm
Мнения: 11266
Местоположение: Добрич
Мнение 
zaphod написа:
миро, ти преди да пишеш пробва ли си решението?

Не разбира се, не си падам по чепове и технните компилатори ;-)

Цитат:
щото дава абсолютно същия код, т.е. грешен :lol: :lol: :lol:

Е тук вече имаш проблем :D

Цитат:
един поглед на асемблера е достатъчен за да се види че компилатора забравя да вкара инструкция

въпросът е дали има право да я забрави или не... В твоя код според мен може да го направи ако е ANSI или C99 или който и да е от С-стандартите.

Цитат:
какви масиви, какви пет лева, какво пакетиране. и къде видя кастване от голям към малък тип? функцията връща размер като елемента на масива - 16 битов инт.

Ти предполагаш че int е 16-битов, че allign е не повече от 2 и т.н. Това може и да е така, но може и да не. Пак ти казвам да не забравяш че работиш на С и ако тия които са писали компилатора не са я карали съвсем през просото са спазили някой от спецификациите за С. Да си призная не познавам абсолютни всички стандарти, но за тия които познавам мога да ти гарантирам че предположението ти е некоректно.
Знам че звучи логично един масив от 16-битови елементи да се пакетира в паметта и втория елемент да е веднага след първия, т.е. на офсет 2. Обаче по стандарт това не е задължително и освен ако си указал изрично пакетирането и подравняването, не може да разчиташ че *((unsigned long*)dw) ще ти даде 32-битовото число което очакваш.
Това ти е единия проблем, вторият проблем е че в тоя typecast надхвърляш размера на обекта. В случая dw ти e указател към първия елемент на масива. Ти използваш този указател за четене, което означава че ползваш стойността на първия елемент на масива. Демек първия елемент на масива не може да се "оптимизира" защото очевидно се ползва. Вторият елемент на масива ти обаче не се използва толкова "очевадно", затова и компилатора е в правото си да го оптимизира. Това е смисълът на оптимизацията - нещата които не се ползват се разкарват. Със същия успех може да декларираш масива с 1000 вместо 2 елемента, но компилатора няма да ти изяди 2К от стека само защото ти си се улял с декларацията.
Сега, ти може да си мислиш че ползваш и втория елемент, защото като тайпкастваш указателя на първия от 16 битов на 32-битов обект ти реално адресираш първия и втория елемент заедно.... Да ама не!
По стандарт нямаш право да тайпкастваш 16 битова променлива в 32 битова. Точка!
Ако все пак го направиш рискуваш да адресираш невалидна памет. А дори и да е валидна, защото ти по някакъв начин си успял да отгатнеш какво стои след обекта който адресираш, то това не се брои за легитимно адресиране. С други думи дори и да познаеш че втория елемент стои непосредствено след първия (което не е задължително), то това не означава че компилатора е длъжен да отгатне какво си отгатнал ти...
За компилатора, вторият елемент на масива се използва само за писане, но не и за четене. И съвсем нормално е при оптимизация да не хаби инструкции за писане на нещо от клас auto което няма да се използва.
Ако няма оптимизация може би ще работи, но въпреки това не ти препоръчвам да тайпкастваш 16 битов обект (какъвто е указателя към първия елемент на масива ти) към 32-битов. Повечето компилатори биха те предупредили че не е редно да преобразуваш указател от един тип с един размер в друг тип с друг размер и ако все пак го направиш това си е на твоя глава.


Сря Дек 26, 2007 9:57 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог

Регистриран на: Нед Фев 26, 2006 6:52 pm
Мнения: 11266
Местоположение: Добрич
Мнение 
miro_atc написа:
Код:
unsigned long  ReadRSDWord()
{
   unsigned long dw;
   ((unsigned int*)&dw)[0]=ReadRSWord();
   ((unsigned int*)&dw)[1]=ReadRSWord();
   return dw;   
}


Верно че и тоя код е бъгав, въпреки че компилаторите които ползвам не бига се оплакали ;-)
Просто има две последователни писания, въпреки че не се пише по цялата променлива са си две писания и компилатора си е в правото да "спести" едното ;-)

Може да се преработи така:

Код:
unsigned long  ReadRSDWord()
{
   unsigned long dw;
   dw=ReadRSWord();
   ((unsigned int*)&dw)[1] |= ReadRSWord();
   return dw;   
}


или

Код:
unsigned long  ReadRSDWord()
{
   unsigned long dw;
   dw=ReadRSWord();
   dw |= ((unsigned long)ReadRSWord()) << (sizeof(int) *8);
   return dw;   
}


или

Код:
unsigned long  ReadRSDWord()
{
   unsigned int low;
   unsigned long dw;
   
   low=ReadRSWord();
   dw= ReadRSWord();
   dw <<= sizeof(int)*8;
   return (dw | low);   
}


Сря Дек 26, 2007 10:41 pm
Профил
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Сря Апр 27, 2005 12:48 pm
Мнения: 6094
Мнение 
в случая с оптимизация -Os http://tntm.eu/wiz/test4.jpg

unsigned long ReadRSDWord() {
union{
unsigned long v;
unsigned int w[2];
}dw;
dw.w[0]=F1();
dw.w[1]=F1();
return dw.v;
}

_________________
main[-1u]={1};


Чет Дек 27, 2007 3:29 pm
Профил ICQ
Ранг: Почетен член
Ранг: Почетен член
Аватар

Регистриран на: Пет Фев 17, 2006 9:17 am
Мнения: 765
Местоположение: Стара Загора
Мнение 
Е, аз поне винаги се стремя да се придържам към най-елементарния начин по който да се направи дадено нещо. Аз бих го написал по следния начин

Код:
unsigned long  ReadRSDWord()
{
  unsigned long j=0;
   j|=ReadRSWord()<<sizeof(int);
   j|=ReadRSWord();
   return j;   
}


Не виждам никакви причини това да е с нещо по-лошо от вариантите с указатели, масиви и юниони и би трябвало да работи абсолютно навсякъде. Дори вероятно бих го направил като макрос.


Сря Яну 09, 2008 11:14 am
Профил ICQ
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Юли 24, 2005 10:28 am
Мнения: 2658
Мнение 
setoy написа:
Е, аз поне винаги се стремя да се придържам към най-елементарния начин по който да се направи дадено нещо. Аз бих го написал по следния начин

Код:
unsigned long  ReadRSDWord()
{
  unsigned long j=0;
   j|=ReadRSWord()<<sizeof(int);
   j|=ReadRSWord();
   return j;   
}


Не виждам никакви причини това да е с нещо по-лошо от вариантите с указатели, масиви и юниони и би трябвало да работи абсолютно навсякъде. Дори вероятно бих го направил като макрос.

не виждаш причина? а това << малка причина ли ти се вижда? да не говорим че няма да бачка тоя код, трябва да е <<sizeof(int)*8, понеже << е побитова операция, не побайтова :)
впрочем така и така се е подновила темата, да споделя че почнах да се съмнявам в обяснението TheWizard че това е от strict-aliasing. обяснението е много логично, обаче във документацията е казано че ниво 2 на оптимизация НЕ включва strict-aliasing, а при ниво две кода е същия. така или иначе за мене това е недопустимо, особено за компилатор на С, особено за ембедед С. дори това да не е бъг, а наистина да е така по замисъл, то за мене е абсолютно недопустимо да се налагат ограничения върху ползването на union.
ето къде черно на бяло е написано:
Цитат:
Allows the compiler to assume the strictest aliasing rules
applicable to the language being compiled. For C, this
activates optimizations based on the type of expressions. In
particular, an object of one type is assumed never to reside at
the same address as an object of a different type, unless the
types are almost the same. For example, an unsigned int
can alias an int, but not a void* or a double. A character
type may alias any other type.
Pay special attention to code like this:
union a_union {
int i;
double d;
};
int f() {
union a_union t;
t.d = 3.0;
return t.i;
}
The practice of reading from a different union member than
the one most recently written to (called “type-punning”) is
common. Even with -fstrict-aliasing, type-punning is
allowed, provided the memory is accessed through the union
type. So, the code above will work as expected. However, this
code might not:
int f() {
a_union t;
int* ip;
t.d = 3.0;
ip = &t.i;
return *ip;
}


както виждате, дори ползването на union е под ударите на тая така наречена "оптимизация". според мене работата е по-скоро бъг, обявен за feature. на тая мисъл ме навеждат две неща - факта че ситуацията за която говорим се проявява при ниво две, а също и факта че въпросната "оптимизация" зачерква свободното ползване на union.


Чет Яну 10, 2008 10:33 am
Профил
Ранг: Почетен член
Ранг: Почетен член
Аватар

Регистриран на: Пет Фев 17, 2006 9:17 am
Мнения: 765
Местоположение: Стара Загора
Мнение 
Цитат:
трябва да е <<(long) sizeof(int)*8


Да, прав си, така трябва да е. Досега не съм срещал компилатор, който да компилира такъв израз по друг начин, освен чрез mov един кое си един къде си. Същото става и с умножение *256 (или колкото там се пада в зависимост от платформата), но с << е по-пригледно.


Чет Яну 10, 2008 11:41 am
Профил ICQ
Ранг: Форумен бог
Ранг: Форумен бог
Аватар

Регистриран на: Нед Юли 24, 2005 10:28 am
Мнения: 2658
Мнение 
setoy написа:
Цитат:
трябва да е <<(long) sizeof(int)*8


Да, прав си, така трябва да е. Досега не съм срещал компилатор, който да компилира такъв израз по друг начин, освен чрез mov един кое си един къде си. Същото става и с умножение *256 (или колкото там се пада в зависимост от платформата), но с << е по-пригледно.

ами честито, вече си виждал, микрочопския точно това прави. ето кода, току що реших да пробвам :)
Код:
1283:                 j|=(long)ReadRSWord()<<sizeof(int)*8;
  0D10  07094E     rcall 0x001fae
  0D12  DE80CF     asr 0x0000,#15,0x0002
  0D14  DD00C0     sl 0x0000,#0,0x0002
  0D16  200000     mov.w #0x0,0x0000
  0D18  740400     ior.w 0x0010,0x0000,0x0010
  0D1A  748481     ior.w 0x0012,0x0002,0x0012
1284:                    j|=ReadRSWord();
  0D1C  070948     rcall 0x001fae
  0D1E  DE80CF     asr 0x0000,#15,0x0002
  0D20  740400     ior.w 0x0010,0x0000,0x0010
  0D22  748481     ior.w 0x0012,0x0002,0x0012
1285:                 return j;

а аз съм срещал компилатор който даже константните изрази смята рънтайм, така че внимателно с боготворенето на компилатора :) винаги трябва да се мята по едно око към кода, за да няма изненади.
впрочем както виждаш, идеята ти да ползваш "най-простия" метод тутакси доведе до ДВА грешки в имплементацията. първата - умножението по 8 да речем че просто си я пропуснал. втората обаче е че трябва да има (long) пред изместването, щото иначе ще се мести както си е инт и нищо няма да стане. за чест на микрочип, компилатора им даде варнинг, иначе и аз не бях съобразил.


Чет Яну 10, 2008 8:41 pm
Профил
Ранг: Почетен член
Ранг: Почетен член
Аватар

Регистриран на: Пет Фев 17, 2006 9:17 am
Мнения: 765
Местоположение: Стара Загора
Мнение 
Тъпо. Никакво боготворене, гадинката винаги трябва да се следи отблизо какво точно прави и след като си придобил някаква представа за жизнените и навици можеш поне да и имаш някакво доверие... донякъде :)
С С30 не съм работил досега, наблюденията ми са базирани на CCS, Keil и други, дори опън сорс-а CDCC се сеща да замени умножение/ротиране с дължина байт с просто местене на байт, дори ротиране на полубайт често го заменят със swap и маскиране за по-кратко.
Не че е закон де, но се учудвам че С30 не го прави. А пробва ли този код с различни настройки на оптимизациите? Не че е важно, но за обща култура поне да се знае :)
Преобразуването на тип и *8 ги бях забравил, сори, после се коригирах.


Пет Яну 11, 2008 5:27 pm
Профил ICQ
Покажи мненията от миналия:  Сортирай по  
Отговори на тема   [ 29 мнения ]  Отиди на страница Предишна  1, 2

Кой е на линия

Потребители разглеждащи този форум: 0 регистрирани и 0 госта


Вие не можете да пускате нови теми
Вие не можете да отговаряте на теми
Вие не можете да променяте собственото си мнение
Вие не можете да изтривате собствените си мнения
Вие не можете да прикачвате файл

Търсене:
Иди на:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.
Хостинг и Домейни