Arlleex 178 24 августа, 2020 Опубликовано 24 августа, 2020 · Жалоба Приветствую! Данная тема будет посвящена различным багам, найденным в среде 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 коту под хвост. Баг или я чего-то непонимаю? Как компилятор может допустить такое? Это же настолько тупо, насколько возвращать адрес локального массива из какой-нибудь функции. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 24 августа, 2020 Опубликовано 24 августа, 2020 · Жалоба А попробуйте убрать для пробы отрицательные сдвиги. Я имею в виду вот это: data >> (size - 2) * 8, data >> (size - 3) * 8, Вдруг компилятор видит здесь UB, и считает себя вправе генерить всякую дичь? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 24 августа, 2020 Опубликовано 24 августа, 2020 · Жалоба Убрал отрицательные сдвиги и да, баг пропал (смотрю листинг). На самом деле там даже не очевидные правки "оживляют" код. Типа volatile u8 i = 4 + size. Я считал, что UB здесь будет относительно результирующего содержимого buf. Ну, типа в нужных мне элементах данные будут рассчитаны верно, в остальных мусор (что мне тоже подходило бы). Но это не так. Видимо, на то он и UB, что последствия совершенно непредсказуемы Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 25 августа, 2020 Опубликовано 25 августа, 2020 · Жалоба Заменил проблемный участок кода на следующий 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. ЧЯДНТ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 25 августа, 2020 Опубликовано 25 августа, 2020 · Жалоба 13 hours ago, Arlleex said: 0x080012B2 BF00 NOP Не подскажете, зачем компилятор добавляет инструкцию NOP? Что она делает? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 25 августа, 2020 Опубликовано 25 августа, 2020 · Жалоба 2 минуты назад, haker_fox сказал: Не подскажете, зачем компилятор добавляет инструкцию NOP? Что она делает? Честно говоря, не знаю. Но могу предполагать, что компилятору захотелось выравнять адрес следующей инструкции на границу 32-битного слова. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 25 августа, 2020 Опубликовано 25 августа, 2020 · Жалоба 1 час назад, haker_fox сказал: Не подскажете, зачем компилятор добавляет инструкцию NOP? Что она делает? Скорее всего - для ускорения. Чтобы выровнять на 32 находящийся после неё целевой адрес перехода и таким образом - увеличить скорость выборки инструкций после перехода на этот адрес. PS: Мне вот другое интересно - нигде (у IAR) не видел такого построения кода, когда внутри функции имеются инструкции, перемещающие SP. У IAR манипуляции с SP всегда только в начале и конце функции. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 25 августа, 2020 Опубликовано 25 августа, 2020 · Жалоба Решил глянуть релиз 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. Бинго! Обновляю компилятор. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 25 августа, 2020 Опубликовано 25 августа, 2020 · Жалоба Значит всё-таки баг. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 25 августа, 2020 Опубликовано 25 августа, 2020 · Жалоба В Keil 5.30 компилятор V6.14. А уже 5.31 версия вышла. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 25 августа, 2020 Опубликовано 25 августа, 2020 · Жалоба 9 минут назад, AHTOXA сказал: Значит всё-таки баг. Ага. 9 минут назад, ViKo сказал: В Keil 5.30 компилятор V6.14. А уже 5.31 версия вышла. А что там прям существенного поменялось в самой среде? На официальном сайте актуальная версия CLang v6.14.1. Но пока что 32-битный вариант не получается скачать - пишет, мол, нет на сервере этого архива. Ну, в крайнем случае, скачаю не v6.14.1, а v6.14. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 18 ноября, 2021 Опубликовано 18 ноября, 2021 · Жалоба Keil 5.36 не даёт задать в опциях назначения частоту Xtal. Внутри поля пишет <undefined>. По этой частоте симулятор время вычислял. Что-то изменилось в идеологии Keil? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 18 ноября, 2021 Опубликовано 18 ноября, 2021 · Жалоба 16 минут назад, ViKo сказал: Keil 5.36 не даёт задать в опциях назначения частоту Xtal. Внутри поля пишет <undefined>. Тут смотрели? Вроде есть пара моментов, как исправить. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ViKo 1 18 ноября, 2021 Опубликовано 18 ноября, 2021 · Жалоба Добавил в ini файл для симулятора XTAL = 120000000; Значение переменной читается правильное. Но симулятор ходит с шагом 10 нс/такт, то есть по 100 МГц частоте. Какой бы XTAL ни задал. В другом проекте тактовая частота получается 16 МГц. Как-то Кейл научился определять. Переменная clock равна тактовой частоте. Но как она определяется, не понимаю. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sidoroff 0 4 января, 2022 Опубликовано 4 января, 2022 (изменено) · Жалоба Работаю в уже стареньком 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 также? Изменено 4 января, 2022 пользователем Sidoroff Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться