Jump to content
    

Keil C51 внезапно начал генерить странный код.

Случайно обнаружил что гарантированно рабочая программа вдруг начала зависать.

Полез разбираться - выяснилось что компилятор вдруг начал генерить неправильный код при вычислении оператора % (остаток от деления).

Достал из бэкапа исходники прошивки конца прошлого года, скомпилял - код неправильный. Достал бинарник сделанный полгода назад из этих-же исходников этим-же кейлом, дизассемблял - код правильный.

Пример:
 

data unsigned long XXX;
data unsigned char YYY;
void main (void)
{
XXX = 608UL;
YYY = XXX % 60UL;
}

Компиляем, получаем такое:

C:0x2BDA    7B3C     MOV      R3,#0x3C
C:0x2BDC    7A00     MOV      R2,#0x00
C:0x2BDE    7900     MOV      R1,#0x00
C:0x2BE0    7800     MOV      R0,#0x00
C:0x2BE2    AF1B     MOV      R7,0x1B
C:0x2BE4    AE1A     MOV      R6,0x1A
C:0x2BE6    AD19     MOV      R5,0x19
C:0x2BE8    AC18     MOV      R4,XXX(0x18)
C:0x2BEA    121552   LCALL    C?ULDIV(C:1552)	; Вот тут в R4567 мы имеем результат деления, а в R0123 остаток
  												; Дальше начинается чушь
C:0x2BED    CC       XCH      A,R4
C:0x2BEE    CD       XCH      A,R5
C:0x2BEF    CE       XCH      A,R6
C:0x2BF0    CF       XCH      A,R7
C:0x2BF1    8F1C     MOV      YYY(0x1C),R7

А полгода назад тот-же самый компилятор из того-же самого проекта делал такое:

    15: YYY = XXX % 60UL;
C:0x504B             MOV     R3, #0x3C
C:0x504D             MOV     R2, #0x00
C:0x504F             MOV     R1, #0x00
C:0x5051             MOV     R0, #0x00
C:0x5053             MOV     R7, 0x1B
C:0x5055             MOV     R6, 0x1A
C:0x5057             MOV     R5, 0x19
C:0x5059             MOV     R4, XXX(0x18)
C:0x2BEA             LCALL    C?ULDIV(C:1552)
C:0x505E             XCH      A, R4
C:0x505F             MOV      A, R0
C:0x5060             XCH      A, R4
C:0x5061             XCH      A, R5
C:0x5062             MOV      A, R1
C:0x5063             XCH      A, R5
C:0x5064             XCH      A, R6
C:0x5065             MOV      A, R2
C:0x5066             XCH      A, R6
C:0x5067             XCH      A, R7
C:0x5068             MOV      A, R3
C:0x5069             XCH      A, R7
C:0x506A             MOV      YYY(0x1C), R7

Опитимизация: 0 (т.е. без оптимизации)

При оптимизации 3 и выше - код генерится правильный.

Кейл снес полностью, поставил с нуля - не помогает.

И вот мне интересно - это я чего-то не понимаю, или это санкционный подарочек от кейла?

UPD. Кейл - последний. 9.60. Он у них уже два года как не обновлялся.

Edited by Intel4004

Share this post


Link to post
Share on other sites

9 hours ago, Intel4004 said:

. . . Кейл снес полностью, поставил с нуля - не помогает. . . . 

А почему на листинге выше физ-адреса разные. Размеры bin-образа прошивки для "правильного-старого" и "неправильного-нового"  как отличаются ? Или только этим участком кода, что приведен выше ?  

Такая хрень могла быть вызвана тем, что в функцию передавалась явная, заранее просчитанная препроцессором значение-константа (ну как умножать на ноль, только для остатка) и даже без оптимизации компилятор "сэкономил" на этом "нуле".

Отсадите вызов из проекта. Сравните генерируемый компилятором код в урезанном и полном проектах. (bin)

"Закладка" . . . Когда "собирали" версию компилятора это было еще неактуально. Хотя триггер по дате вполне может быть.

 

Share this post


Link to post
Share on other sites

10 часов назад, Intel4004 сказал:

 

data unsigned long XXX;
data unsigned char YYY;
void main (void)
{
XXX = 608UL;
YYY = XXX % 60UL;
}

Компиляем, получаем такое:

C:0x2BDA    7B3C     MOV      R3,#0x3C
C:0x2BDC    7A00     MOV      R2,#0x00
C:0x2BDE    7900     MOV      R1,#0x00
C:0x2BE0    7800     MOV      R0,#0x00
C:0x2BE2    AF1B     MOV      R7,0x1B
C:0x2BE4    AE1A     MOV      R6,0x1A
C:0x2BE6    AD19     MOV      R5,0x19
C:0x2BE8    AC18     MOV      R4,XXX(0x18)
C:0x2BEA    121552   LCALL    C?ULDIV(C:1552)	; Вот тут в R4567 мы имеем результат деления, а в R0123 остаток
  												; Дальше начинается чушь
C:0x2BED    CC       XCH      A,R4
C:0x2BEE    CD       XCH      A,R5
C:0x2BEF    CE       XCH      A,R6
C:0x2BF0    CF       XCH      A,R7
C:0x2BF1    8F1C     MOV      YYY(0x1C),R7

В чём именно "чушь"? И почему думаете что "неправильный"?

Не вижу проблемы в результате компиляции. У вас результат (YYY) нигде не используется. Поэтому компилятор вправе полностью выкинуть код записи в него.

А команда "MOV YYY(0x1C),R7" может вообще никак не относиться к этому делению.

 

10 часов назад, Intel4004 сказал:

А полгода назад тот-же самый компилятор из того-же самого проекта делал такое:

    15: YYY = XXX % 60UL;
C:0x504B             MOV     R3, #0x3C
C:0x504D             MOV     R2, #0x00
C:0x504F             MOV     R1, #0x00
C:0x5051             MOV     R0, #0x00
C:0x5053             MOV     R7, 0x1B
C:0x5055             MOV     R6, 0x1A
C:0x5057             MOV     R5, 0x19
C:0x5059             MOV     R4, XXX(0x18)
C:0x2BEA             LCALL    C?ULDIV(C:1552)
C:0x505E             XCH      A, R4
C:0x505F             MOV      A, R0
C:0x5060             XCH      A, R4
C:0x5061             XCH      A, R5
C:0x5062             MOV      A, R1
C:0x5063             XCH      A, R5
C:0x5064             XCH      A, R6
C:0x5065             MOV      A, R2
C:0x5066             XCH      A, R6
C:0x5067             XCH      A, R7
C:0x5068             MOV      A, R3
C:0x5069             XCH      A, R7
C:0x506A             MOV      YYY(0x1C), R7

А это Вы выдрали из рабочего проекта, в котором YYY видимо используется после.

10 часов назад, Intel4004 сказал:

И вот мне интересно - это я чего-то не понимаю, или это санкционный подарочек от кейла?

Во-первых: тестовый пример нужно писать корректно:

или обеспечивать использование YYY после деления;

или использовать volatile для результата (а может ещё какие-то префиксы; в IAR я бы в этом случае написал бы: __root unsigned char volatile YYY).

Во-вторых: надо хотя бы внятно описать - что именно изменилось у вас за время от корректного кода до некорректного? Насколько можно понять - версия компилятора - не изменилась? Исходники - тоже? Тогда что изменилось??? Чудес не бывает.

11 часов назад, Intel4004 сказал:

При оптимизации 3 и выше - код генерится правильный.

А что такое "правильный" и почему думаете что он правильный?

2 часа назад, k155la3 сказал:

А почему на листинге выше физ-адреса разные. Размеры bin-образа прошивки для "правильного-старого" и "неправильного-нового"  как отличаются ?

1й результат ТС получил из исходника, который выше; а 2й - выдрал из рабочего проекта, в котором, вангую, больше чем 2 строки кода.

Share this post


Link to post
Share on other sites

55 minutes ago, jcxz said:

1й результат ТС получил из исходника, который выше; а 2й - выдрал из рабочего проекта, в котором, вангую, больше чем 2 строки кода.

могу еще добавить, возможно имеют значения типы "входных" значений для операции % в старо-рабочем и ново-тестовом проектах, возможно их неявное "приведение" к типу.

 

Share this post


Link to post
Share on other sites

Поясняю: достаем проект из бекапа, компиляем - видим первый пример. Дизассембляем бинарник, скомпиленый из того-же самого проекта тем же самым компилятором - видим второй пример.

На адреса и прочие несоответствия внимание обращать не надо.

Надо создать проект, включить оптимизацию "0 - Constant Folding" и включить галочку "don't use absolute register accesses".

Добавить в проект

data unsigned long XXX;
data unsigned char YYY;
int main (void)
{
XXX = 608UL;
YYY = XXX % 60UL;
return YYY;
}

Скомпилять. Посмотреть. Увидеть такое:

C:0x00ED    12003F   LCALL    C?ULDIV(C:003F)
C:0x00F0    CC       XCH      A,R4
C:0x00F1    CD       XCH      A,R5
C:0x00F2    CE       XCH      A,R6
C:0x00F3    CF       XCH      A,R7
C:0x00F4    8F0C     MOV      YYY(0x0C),R7

Включить оптимизацию 3 и выше. Скомпилять. Посмотреть. Увидеть такое:

C:0x00E9    12003F   LCALL    C?ULDIV(C:003F)
C:0x00EC    CC       XCH      A,R4
C:0x00ED    E8       MOV      A,R0
C:0x00EE    CC       XCH      A,R4
C:0x00EF    CD       XCH      A,R5
C:0x00F0    E9       MOV      A,R1
C:0x00F1    CD       XCH      A,R5
C:0x00F2    CE       XCH      A,R6
C:0x00F3    EA       MOV      A,R2
C:0x00F4    CE       XCH      A,R6
C:0x00F5    CF       XCH      A,R7
C:0x00F6    EB       MOV      A,R3
C:0x00F7    CF       XCH      A,R7
C:0x00F8    8F0C     MOV      YYY(0x0C),R7

Или снять галочку. Скомпилять. Посмотреть. Увидеть такое:

C:0x00ED    12003F   LCALL    C?ULDIV(C:003F)
C:0x00F0    AC00     MOV      R4,0x00
C:0x00F2    AD01     MOV      R5,0x01
C:0x00F4    AE02     MOV      R6,0x02
C:0x00F6    AF03     MOV      R7,0x03
C:0x00F8    8F0C     MOV      YYY(0x0C),R7

И написать мне такой-же у вас результат компиляции или нет.

Share this post


Link to post
Share on other sites

57 minutes ago, Intel4004 said:

Поясняю: . . .

Версия компилятора чуть меньше.

В настройках проекта куча других ньюансов, начиная с типа процессора, модели памяти и "всего-всея прочея".

 

main_O0.lst         main_O3.lst

Share this post


Link to post
Share on other sites

Quote

COMPILER INVOKED BY: ... OPTIMIZE(3,SPEED) ...

> включить оптимизацию "0 - Constant Folding"

 

Quote

В настройках проекта куча других ньюансов, начиная с типа процессора, модели памяти и "всего-всея прочея".

Процессор не влияет. Я пробовал на разных адуках и ат89*.

А все прочее: отличий от дефолтных настроек два - оптимизация 0 и запрет на доступ к регистрам через память.

Edited by Intel4004

Share this post


Link to post
Share on other sites

7 minutes ago, Intel4004 said:

> включить оптимизацию "0 - Constant Folding"

там 2 листинга, первый -O0 второй -O3

 

Spoiler

image.png.df0197dc3b4f2d48abf7bbb1e93e8fdf.png

 

Share this post


Link to post
Share on other sites

Да, извиняюсь, проглядел.

А  вас все выглядит еще интереснее и информативнее...

C51 COMPILER V9.59.0.0, COMPILATION OF MODULE MMMM
COMPILER INVOKED BY: C51.EXE mmmm.c OPTIMIZE(0,SPEED) BROWSE NOAREGS DEBUG OBJECTEXTEND CODE

001C 120000      E     LCALL   ?C?ULDIV
001F FC                MOV     R4,A
0020 FD                MOV     R5,A
0021 FE                MOV     R6,A
0022 FF                MOV     R7,A
0023 8F00        R     MOV     YYY,R7

У C?ULDIV на выходе результат деления в регистрах R4-R7 и остаток от деления в R0-R3. Соответственно у нас в YYY должен попасть R3. А попадает хрен знает что.

Точнее тут видно, что компилятор собирался сначала перекинуть R0-R3 в R4-R7, а потом переложить из R7 в YYY, но часть команд почему-то пропала.

MOV A,R0 <- это пропало

MOV R4,A

...

MOV A,R3 <- это пропало

MOV R7,A

 

При оптимизации 3 все еще интереснее:

C51 COMPILER V9.59.0.0, COMPILATION OF MODULE MMMM
COMPILER INVOKED BY: C51.EXE mmmm.c OPTIMIZE(3,SPEED) BROWSE NOAREGS DEBUG OBJECTEXTEND CODE

0018 120000      E     LCALL   ?C?ULDIV
001B E8                MOV     A,R0
001C E9                MOV     A,R1
001D EA                MOV     A,R2
001E EB                MOV     A,R3
001F F500        R     MOV     YYY,A

MOV A,R0

MOV R4,A <- а тут пропало это

...

MOV A,R3

MOV R7,A <- и это

Но в результатте в YYY случайно попадает R3, который и ожидался

-

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

Не нравится мне это. Сильно не нравится. Такое ощущение, что сработала закладка в компиляторе и верить ему больше нельзя...

Edited by Intel4004

Share this post


Link to post
Share on other sites

>> Соответственно у нас в YYY должен попасть R3. А попадает хрен знает что.

Ну, изучайте "соглашение о связях (вызовах)". Хотя это "кухня" компилятора. Который для того и сделан, чтобы мы этим не заморачивались.

"Прогоните" код на эмуляторе, только не на константах а на переборе делимого и делителя. Думаю, компилятор "знает что делает". 

ps  

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

 

 

 

 

Share this post


Link to post
Share on other sites

9.60 делает код который на эмуляторе гонять не надо

Тут четко видно, что в результате в YYY попадает не R3, а R6. И Так-же четко видно, что пропущены несколько команд.

C:0x00ED    12003F   LCALL    C?ULDIV(C:003F)
C:0x00F0    CC       XCH      A,R4
C:0x00F1    CD       XCH      A,R5
C:0x00F2    CE       XCH      A,R6
C:0x00F3    CF       XCH      A,R7
C:0x00F4    8F0C     MOV      YYY(0x0C),R7

XCH      A,R4
MOV      A,R0 <- это пропущено
XCH      A,R4 <- это пропущено
...
XCH      A,R7
MOV      A,R3 <- это пропущено
XCH      A,R7 <- это пропущено

Я же сравниваю с результатом компиляции того-же самого проекта с теми-же самыми настройками, тем-же самым компилятором, но полгода назад. Разница видна невооруженным взглядом.

Неужели вы думаете, что я из чистого интереса полез изучать код? Нет. Я внес в рабочую прошивку незначительные и не на что не влияющие изменения и она вдруг начала зависать и ребутаться по вачдогу. Полез разбираться и внезапно обнаружил, что результат оператора % теперь не соответствует действительности. А полгода назад соответствовал...

 

Edited by Intel4004

Share this post


Link to post
Share on other sites

О! Нашел в бэкапах не зачищенный от результатов компиляции проект. С листингами.
Скомпилял.
Вот сейчас результат налицо.

Сейчас строчка SEC = time%60; компиляется так (выдержка из листинга):

COMPILER INVOKED BY: C51.EXE unixtime.c OMF2 OPTIMIZE(2,SPEED) BROWSE ORDER NOAREGS DEBUG CODE LISTINCLUDE SYMBOLS

  32   1      SEC = time%60;
                                           ; SOURCE LINE # 32
0024 7B3C              MOV     R3,#03CH
0026 7A00              MOV     R2,#00H
0028 7900              MOV     R1,#00H
002A 7800              MOV     R0,#00H
002C AF00        R     MOV     R7,time+03H
002E AE00        R     MOV     R6,time+02H
0030 AD00        R     MOV     R5,time+01H
0032 AC00        R     MOV     R4,time
0034 120000      E     LCALL   ?C?ULDIV
0037 CC                XCH     A,R4
0038 CD                XCH     A,R5
0039 CE                XCH     A,R6
003A CF                XCH     A,R7
003B 8FA3              MOV     SEC,R7

А год назад тот-же самый проект, тем-же самым компилятором компилялся так:

COMPILER INVOKED BY: C51.EXE unixtime.c OMF2 OPTIMIZE(2,SPEED) BROWSE ORDER NOAREGS DEBUG CODE LISTINCLUDE SYMBOLS

  32   1      SEC = time%60;
                                           ; SOURCE LINE # 32
0024 7B3C              MOV     R3,#03CH
0026 7A00              MOV     R2,#00H
0028 7900              MOV     R1,#00H
002A 7800              MOV     R0,#00H
002C AF00        R     MOV     R7,time+03H
002E AE00        R     MOV     R6,time+02H
0030 AD00        R     MOV     R5,time+01H
0032 AC00        R     MOV     R4,time
0034 120000      E     LCALL   ?C?ULDIV
0037 CC                XCH     A,R4
0038 E8                MOV     A,R0 <- это исчезло
0039 CC                XCH     A,R4 <- это исчезло
003A CD                XCH     A,R5
003B E9                MOV     A,R1 <- это исчезло
003C CD                XCH     A,R5 <- это исчезло
003D CE                XCH     A,R6
003E EA                MOV     A,R2 <- это исчезло
003F CE                XCH     A,R6 <- это исчезло
0040 CF                XCH     A,R7
0041 EB                MOV     A,R3 <- это исчезло
0042 CF                XCH     A,R7 <- это исчезло
0043 8FA3              MOV     SEC,R7

Мля. Мне это не нравится. Совсем не нравится.

Edited by Intel4004

Share this post


Link to post
Share on other sites

35 minutes ago, Intel4004 said:

1 . . . Неужели вы думаете, что я из чистого интереса полез изучать код? Нет.

2. Я внес в рабочую прошивку незначительные и не на что не влияющие изменения и она вдруг . . . 

1. Ну, всяко бывает. Я имел ввиду, что подозревать в "ошибках" компилятор "такое-себе" занятие. Как показывает практика (моя) Он (компилятор) прав на 99.9(9).

2. Ну, изменение одного бита в мегабайтной прошивке тоже можно считать "незначительным".

Значение остатка от деления может быть (делитель - 1). И как это соотносится с операндами unsigned char = UL % UL.

Share this post


Link to post
Share on other sites

 

Quote

Ну, всяко бывает. Я имел ввиду, что подозревать в "ошибках" компилятор "такое-себе" занятие. Как показывает практика (моя) Он (компилятор) прав на 99.9(9).

А я не в ошибках подрозреваю. Я сейчас практически уверен, что по каким-то условиям сработала закладка и компилятор начал преднамеренно портить код. Типа такие тихие санкции.

Quote

2. Ну, изменение одного бита в мегабайтной прошивке тоже можно считать "незначительным".

Посмотрите сообщение выше. Вообще никаких изменений. Ни бита. Достали из бэкапа проект и тут-же скомпиляли. И сравнили старый (годовалый) листинг с новым.

Если бы этот эффект наблюдался только у меня - я бы только порадовался, что это у меня руки не из того места и это можно легко вылечить. Но ведь в ваших листингах видно то-же самое. Причем на компиляторе предыдущей версии.

Share this post


Link to post
Share on other sites

Ну, теоретически - можетбыть. Софт сунулся в сеть, получил инф "стоп-лафа" и все лечение пропало.

Может быть просто несовместимость с конкретной ОС. Я компилировал на Win7/32. "Исправный-старый" код-листинг на какой ОС компилировался ?

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...