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

Keil uVision. Баги, приколы, подводные камни

Приветствую!

Данная тема будет посвящена различным багам, найденным в среде Keil uVision.

Поскольку компиляторы нынче дюже умные, считаю абсолютно не зазорным размещать здесь любые найденные "косяки".

Даже если "косяком" окажется вполне закономерное поведение компилятора.

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

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

Если проблема заключается не в компиляторе, а в исходном коде автора, автор редактирует свой пост, перекрашивая триггер в зеленый цвет.

Так другие участники смогут быстро пролистать тему и сразу глазами отыскать все пока что найденные баги.

Я, пожалуй, буду первым.

 

1. При оптимизации уровня 'Balanced' генерируется некорректный код

Итак, имеем Keil uVision 5.28, компилятор ARM CLang V6.12.

Проблема возникает только при оптимизации уровня 'Balanced'.

Есть следующий код

static void Write(SPI_TypeDef *spi, u16 addr, u32 data, u8 size)
{
  if(size > 0 && size < 5)
  {
    u8 buf[8] =
    {
      0x40,
      addr >> 11,
      addr >>  3,
      addr <<  5,
      data >> (size - 1) * 8,
      data >> (size - 2) * 8,
      data >> (size - 3) * 8,
      data
    }, *p = buf, i = 4 + size;
    while(i--)
    {
      spi->DR = *p++;
      while(~spi->SR & SPI_SR_RXNE);
      (void)spi->DR;
    }
  }
}

 

Эта функция записывает во внешнюю микросхему по SPI некоторые данные.

Вначале идет код записи 0x40, дальше три байта адреса, а дальше байт, полуслово или слово, в зависимости от size. Аргумент size может быть равен 1, 2 или 4.

Для этой функции имею следующий листинг

Скрытый текст

0x08001244 F1A30C01  SUB      r12,r3,#0x01
0x08001248 FA5FFC8C  UXTB     r12,r12
0x0800124C F1BC0F03  CMP      r12,#0x03
0x08001250 D840      BHI      0x080012D4
0x08001252 B580      PUSH     {r7,lr}
0x08001254 B082      SUB      sp,sp,#0x08
0x08001256 F04F0C40  MOV      r12,#0x40
    75:       addr <<  5, 
    76:       data >> (size - 1) * 8, 
    77:       data >> (size - 2) * 8, 
0x0800125A EA4F1E41  LSL      lr,r1,#5
    71:     { 
    72:       0x40, 
    73:       addr >> 11, 
0x0800125E F88DC000  STRB     r12,[sp,#0x00]
0x08001262 F88DE003  STRB     lr,[sp,#0x03]
    74:       addr >>  3, 
    75:       addr <<  5, 
    76:       data >> (size - 1) * 8, 
    77:       data >> (size - 2) * 8, 
0x08001266 EA4F0ED1  LSR      lr,r1,#3
    73:       addr >> 11, 
    74:       addr >>  3, 
    75:       addr <<  5, 
    76:       data >> (size - 1) * 8, 
    77:       data >> (size - 2) * 8, 
0x0800126A 0AC9      LSRS     r1,r1,#11
    71:     { 
0x0800126C F88DE002  STRB     lr,[sp,#0x02]
0x08001270 F88D1001  STRB     r1,[sp,#0x01]
0x08001274 F06F0117  MVN      r1,#0x17
0x08001278 F88D2007  STRB     r2,[sp,#0x07]
    78:       data >> (size - 3) * 8, 
    79:       data 
    80:     }, *p = buf, i = 4 + size; 
0x0800127C EB0101C3  ADD      r1,r1,r3,LSL #3
    78:       data >> (size - 3) * 8, 
0x08001280 FA22F101  LSR      r1,r2,r1
    71:     { 
    72:       0x40, 
    73:       addr >> 11, 
    74:       addr >>  3, 
    75:       addr <<  5, 
    76:       data >> (size - 1) * 8, 
0x08001284 F88D1006  STRB     r1,[sp,#0x06]
0x08001288 F06F010F  MVN      r1,#0x0F
    77:       data >> (size - 2) * 8, 
    78:       data >> (size - 3) * 8, 
    79:       data 
    80:     }, *p = buf, i = 4 + size; 
0x0800128C EB0101C3  ADD      r1,r1,r3,LSL #3
    77:       data >> (size - 2) * 8, 
0x08001290 FA22F101  LSR      r1,r2,r1
    71:     { 
0x08001294 F88D1005  STRB     r1,[sp,#0x05]
0x08001298 F06F0107  MVN      r1,#0x07
    76:       data >> (size - 1) * 8, 
    77:       data >> (size - 2) * 8, 
    78:       data >> (size - 3) * 8, 
    79:       data 
    80:     }, *p = buf, i = 4 + size; 
0x0800129C EB0101C3  ADD      r1,r1,r3,LSL #3
    76:       data >> (size - 1) * 8, 
0x080012A0 FA22F101  LSR      r1,r2,r1
0x080012A4 466A      MOV      r2,sp
    71:     { 
0x080012A6 F88D1004  STRB     r1,[sp,#0x04]
    81:     while(i--) 
    82:     { 
0x080012AA 1CD9      ADDS     r1,r3,#3
0x080012AC B002      ADD      sp,sp,#0x08
0x080012AE E8BD4080  POP      {r7,lr}
0x080012B2 BF00      NOP      
    83:       spi->DR = *p++; 
0x080012B4 FA5FF38C  UXTB     r3,r12
    83:       spi->DR = *p++; 
0x080012B8 8183      STRH     r3,[r0,#0x0C]

...

 

 

Самое интересное. Сначала в стеке выделяется кусочек на 8 байтов (под массив buf)

...
0x08001254 B082      SUB      sp,sp,#0x08
...

Потом он инициализируется. Пофиг, что как-то монструозно - потом, может, переделаю. Не суть.

Теперь, перед циклом while(i--), адрес верхушки стека запоминается в R2 и (мой мозг)... SP откатывается обратно!

...
0x080012A4 466A      MOV      r2,sp
...
0x080012AC B002      ADD      sp,sp,#0x08
0x080012AE E8BD4080  POP      {r7,lr}
...

 

И дальше по листингу я вижу, как R2 используется в качестве указателя на buf.

Но прилетает прерывание и, естественно, весь buf коту под хвост.

Баг или я чего-то непонимаю? Как компилятор может допустить такое?

Это же настолько тупо, насколько возвращать адрес локального массива из какой-нибудь функции.

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


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

А попробуйте убрать для пробы отрицательные сдвиги. Я имею в виду вот это:

      data >> (size - 2) * 8,
      data >> (size - 3) * 8,

Вдруг компилятор видит здесь UB, и считает себя вправе генерить всякую дичь?

 

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


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

Убрал отрицательные сдвиги и да, баг пропал (смотрю листинг).

На самом деле там даже не очевидные правки "оживляют" код. Типа volatile u8 i = 4 + size.

Я считал, что UB здесь будет относительно результирующего содержимого buf.

Ну, типа в нужных мне элементах данные будут рассчитаны верно, в остальных мусор (что мне тоже подходило бы).

Но это не так. Видимо, на то он и UB, что последствия совершенно непредсказуемы:smile:

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


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

Заменил проблемный участок кода на следующий

const u8 sht[] = {0, 8, 16, 24};
      u8 buf[] =
{
  0x40,
  addr >> 11,
  addr >>  3,
  addr <<  5,
  data >> sht[size - 1 & 0x3],
  data >> sht[size - 2 & 0x3],
  data >> sht[size - 3 & 0x3],
  data
}, *p = buf, i = 4 + size;

и он тоже не работает.

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

Планировал, что в случае, когда, например, size - N < 0, маска 0x3 не даст получить некорректный индекс таблицы сдвига sht.

ЧЯДНТ?:unknw:

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


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

13 hours ago, Arlleex said:

0x080012B2 BF00 NOP

Не подскажете, зачем компилятор добавляет инструкцию NOP? Что она делает?

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


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

2 минуты назад, haker_fox сказал:

Не подскажете, зачем компилятор добавляет инструкцию NOP? Что она делает?

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

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


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

1 час назад, haker_fox сказал:

Не подскажете, зачем компилятор добавляет инструкцию NOP? Что она делает?

Скорее всего - для ускорения. Чтобы выровнять на 32 находящийся после неё целевой адрес перехода и таким образом - увеличить скорость выборки инструкций после перехода на этот адрес.

 

PS: Мне вот другое интересно - нигде (у IAR) не видел такого построения кода, когда внутри функции имеются инструкции, перемещающие SP. У IAR манипуляции с SP всегда только в начале и конце функции.

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


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

Решил глянуть релиз CLang v6.13.

Среди списка исправленных багов нашел следующее

Цитата

[SDCOMP-53434]
In certain circumstances, when compiling at any optimization level except -O0, the compiler could generate incorrect code for a function F that contains conditional accesses to the stack. Such incorrect code prematurely released stack memory that must remain reserved throughout the execution of F. This has been fixed.

Бинго! Обновляю компилятор.

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


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

В Keil 5.30 компилятор V6.14. А уже 5.31 версия вышла.

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


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

9 минут назад, AHTOXA сказал:

Значит всё-таки баг.

Ага.

9 минут назад, ViKo сказал:

В Keil 5.30 компилятор V6.14. А уже 5.31 версия вышла.

А что там прям существенного поменялось в самой среде?

На официальном сайте актуальная версия CLang v6.14.1.

Но пока что 32-битный вариант не получается скачать - пишет, мол, нет на сервере этого архива.

Ну, в крайнем случае, скачаю не v6.14.1, а v6.14.

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


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

Keil 5.36 не даёт задать в опциях назначения частоту Xtal. Внутри поля пишет <undefined>. По этой частоте симулятор время вычислял. Что-то изменилось в идеологии Keil?

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


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

16 минут назад, ViKo сказал:

Keil 5.36 не даёт задать в опциях назначения частоту Xtal. Внутри поля пишет <undefined>.

Тут смотрели? Вроде есть пара моментов, как исправить.

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


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

Добавил в ini файл для симулятора
XTAL = 120000000;

Значение переменной читается правильное. Но симулятор ходит с шагом 10 нс/такт, то есть по 100 МГц частоте. Какой бы XTAL ни задал.

В другом проекте тактовая частота получается 16 МГц. Как-то Кейл научился определять.

Переменная clock равна тактовой частоте. Но как она определяется, не понимаю.

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


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

Работаю в уже стареньком Keil 4.54. Камень STM32F429.

Обнаружил, что если в настройках проекта стоит 'USE FPU', то при попытке считать/записать в

переменную типа 'float' через звездочку из невыровненного указателя, то проц виснет.

При отключенном FPU все нормально для любого адреса.

    volatile int block[16];
    char *ptr = (char*)block;
    k_omega = *(float*)(&ptr[2]); // так повиснет, если включено FPU
    k_omega = *(float*)(&ptr[4]); // а так сработает

    //k_omega - глобальная float-переменная

Это проявляется, разумеется, только с реально существующими переменными,

которые не удалены оптимизацией.

Досадно, почти 10 лет в нем пишу, а наткнулся впервые.

Интересно, а в более новых версиях Keil также?

Изменено пользователем Sidoroff

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


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

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

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

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

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

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

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

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

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

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