|
Виж темите без отговор | Виж активните теми
Дата и час: Вто Юли 28, 2026 6:47 am
бъг в оптимизатора на С30 (С за 30та серия на микрочип)
| Автор |
Съобщение |
|
Nikola Kirov
Ранг: Форумен бог
Регистриран на: Нед Окт 31, 2004 9:19 pm Мнения: 4464 Местоположение: Stara Zagora
|
ReadRSWord(); връща 16 битова стойност. И в твоя случай има от малък към голям
|
| Сря Дек 26, 2007 7:47 pm |
|
 |
|
Nikola Kirov
Ранг: Форумен бог
Регистриран на: Нед Окт 31, 2004 9:19 pm Мнения: 4464 Местоположение: Stara Zagora
|
А за варианта с union-a не съм се сблъскавал с проблеми досега. Но мисля че си прав.
Верния код като за разнообразни платформи предполагам че е
Не се сещам за платформа на която няма да работи. Но може и да бъркам все пак. Когато имах проблеми с visual studioto 6 с кастването го реших по следния начин. Трябва да се укаже ясно на компилатора да нулира старшите 16 бита. а за поредноста на елементите не би трябвало да се притесняваш при 32 битови елементи и litle endian на процесора според мен. Ако е big endian трябва да размениш поредноста на членовете в които записваш само. Иначе твоето решение наистина ще работи и при 2та вида еndian. но пак явно трябва да се приложи същия трик при кастването. А ако наистина трябва кода да работи независимо от платформата това мисля че е варианта.
|
| Сря Дек 26, 2007 7:50 pm |
|
 |
|
zaphod
Ранг: Форумен бог
Регистриран на: Нед Юли 24, 2005 10:28 am Мнения: 2658
|
миро, ти преди да пишеш пробва ли си решението? щото дава абсолютно същия код, т.е. грешен
един поглед на асемблера е достатъчен за да се види че компилатора забравя да вкара инструкция, какви масиви, какви пет лева, какво пакетиране. и къде видя кастване от голям към малък тип? функцията връща размер като елемента на масива - 16 битов инт.
за масивите и стандарта може и да си прав, макар че ако ми покажеш компилатор за който разликата между адрес на два елемента от масив не е sizeof(type), то ще съм искрено удивен.
със унион дава правилен код, но ето че на този компилатор синтаксиса на униона се оказа различен - иска задължително инстанция на униона и после обръщение към полетата все едно са полета в структура, въпреки че е безименен.
|
| Сря Дек 26, 2007 8:14 pm |
|
 |
|
Nikola Kirov
Ранг: Форумен бог
Регистриран на: Нед Окт 31, 2004 9:19 pm Мнения: 4464 Местоположение: Stara Zagora
|
По анси стандарта мисля че при C няма безименни структури и union-и. Те са влезли вече при C++. Но повечето съвременни C компилатори ги поддържат.
Аз си имам дефинирани с typedef такива структури и си ги ползвам тях. За случая го направих така за да е ясно написано. Ето това ползвам с IAR и АРМ.
|
| Сря Дек 26, 2007 8:26 pm |
|
 |
|
TheWizard
Ранг: Форумен бог
Регистриран на: Сря Апр 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 |
|
 |
|
zaphod
Ранг: Форумен бог
Регистриран на: Нед Юли 24, 2005 10:28 am Мнения: 2658
|
еее, браво TheWizard, това е правилния отговор, полезна информация.
все пак мисля че тъпото нещо поне предупреждение трябва да дава. това все пак е С, не джава, тук игричките с указатели са норма а не изключение. аз дълги години пиша за виндовс, никога не съм имал подобни проблеми, ако не ми беше обърнал внимание на тая опция, пак щях да го напиша някъде. сега ми се върти в главата че още някъде в кода имам подобно нещо, ще го издиря.
|
| Сря Дек 26, 2007 8:50 pm |
|
 |
|
miro_atc
Ранг: Форумен бог
Регистриран на: Нед Фев 26, 2006 6:52 pm Мнения: 11266 Местоположение: Добрич
|
Не разбира се, не си падам по чепове и технните компилатори  Е тук вече имаш проблем въпросът е дали има право да я забрави или не... В твоя код според мен може да го направи ако е ANSI или C99 или който и да е от С-стандартите.
Ти предполагаш че 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 |
|
 |
|
miro_atc
Ранг: Форумен бог
Регистриран на: Нед Фев 26, 2006 6:52 pm Мнения: 11266 Местоположение: Добрич
|
Верно че и тоя код е бъгав, въпреки че компилаторите които ползвам не бига се оплакали  Просто има две последователни писания, въпреки че не се пише по цялата променлива са си две писания и компилатора си е в правото да "спести" едното  Може да се преработи така: или или
|
| Сря Дек 26, 2007 10:41 pm |
|
 |
|
TheWizard
Ранг: Форумен бог
Регистриран на: Сря Апр 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 |
|
 |
|
setoy
Ранг: Почетен член
Регистриран на: Пет Фев 17, 2006 9:17 am Мнения: 765 Местоположение: Стара Загора
|
Е, аз поне винаги се стремя да се придържам към най-елементарния начин по който да се направи дадено нещо. Аз бих го написал по следния начин
Не виждам никакви причини това да е с нещо по-лошо от вариантите с указатели, масиви и юниони и би трябвало да работи абсолютно навсякъде. Дори вероятно бих го направил като макрос.
|
| Сря Яну 09, 2008 11:14 am |
|
 |
|
zaphod
Ранг: Форумен бог
Регистриран на: Нед Юли 24, 2005 10:28 am Мнения: 2658
|
не виждаш причина? а това << малка причина ли ти се вижда? да не говорим че няма да бачка тоя код, трябва да е <<sizeof(int)*8, понеже << е побитова операция, не побайтова  впрочем така и така се е подновила темата, да споделя че почнах да се съмнявам в обяснението TheWizard че това е от strict-aliasing. обяснението е много логично, обаче във документацията е казано че ниво 2 на оптимизация НЕ включва strict-aliasing, а при ниво две кода е същия. така или иначе за мене това е недопустимо, особено за компилатор на С, особено за ембедед С. дори това да не е бъг, а наистина да е така по замисъл, то за мене е абсолютно недопустимо да се налагат ограничения върху ползването на union. ето къде черно на бяло е написано:
както виждате, дори ползването на union е под ударите на тая така наречена "оптимизация". според мене работата е по-скоро бъг, обявен за feature. на тая мисъл ме навеждат две неща - факта че ситуацията за която говорим се проявява при ниво две, а също и факта че въпросната "оптимизация" зачерква свободното ползване на union.
|
| Чет Яну 10, 2008 10:33 am |
|
 |
|
setoy
Ранг: Почетен член
Регистриран на: Пет Фев 17, 2006 9:17 am Мнения: 765 Местоположение: Стара Загора
|
Да, прав си, така трябва да е. Досега не съм срещал компилатор, който да компилира такъв израз по друг начин, освен чрез mov един кое си един къде си. Същото става и с умножение *256 (или колкото там се пада в зависимост от платформата), но с << е по-пригледно.
|
| Чет Яну 10, 2008 11:41 am |
|
 |
|
zaphod
Ранг: Форумен бог
Регистриран на: Нед Юли 24, 2005 10:28 am Мнения: 2658
|
ами честито, вече си виждал, микрочопския точно това прави. ето кода, току що реших да пробвам
 |  |  |  | Код: 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 |
|
 |
|
setoy
Ранг: Почетен член
Регистриран на: Пет Фев 17, 2006 9:17 am Мнения: 765 Местоположение: Стара Загора
|
Тъпо. Никакво боготворене, гадинката винаги трябва да се следи отблизо какво точно прави и след като си придобил някаква представа за жизнените и навици можеш поне да и имаш някакво доверие... донякъде
С С30 не съм работил досега, наблюденията ми са базирани на CCS, Keil и други, дори опън сорс-а CDCC се сеща да замени умножение/ротиране с дължина байт с просто местене на байт, дори ротиране на полубайт често го заменят със swap и маскиране за по-кратко.
Не че е закон де, но се учудвам че С30 не го прави. А пробва ли този код с различни настройки на оптимизациите? Не че е важно, но за обща култура поне да се знае
Преобразуването на тип и *8 ги бях забравил, сори, после се коригирах.
|
| Пет Яну 11, 2008 5:27 pm |
|
|
Кой е на линия |
Потребители разглеждащи този форум: 0 регистрирани и 3 госта |
|
Вие не можете да пускате нови теми Вие не можете да отговаряте на теми Вие не можете да променяте собственото си мнение Вие не можете да изтривате собствените си мнения Вие не можете да прикачвате файл
|
|