jcxz 186 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба Похоже - нарыл очередной баг IAR. Хотя вроде не такой опасный, как предыдущий. Может даже и совсем не опасный, и даже не баг, а просто неоптимальность компиляции... Код: typedef unsigned char u8; static u8 volatile moFault int i0; ... i0 += 25 << moFault; Даёт: ;i0 += 25 << moFault; 0x2219 MOVS R2,#+25 0xF995 0x101E LDRSB R1,[R5, #+30] 0x408A LSLS R2,R2,R1 0x1810 ADDS R0,R2,R0 Хотя, по идее, должна быть LDRB, а не LDRSB. Если написать например: i0 += 25u << moFault; то уже всё ок - LDRB detected. Понятно, что 25 - знаковое, но это вроде никак не должно влиять на команду чтения moFault? Или я чего-то не понимаю? PS: "IAR ANSI C/C++ Compiler V8.50.4.261/W32 for ARM". Но актуально и для более ранних версий. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Edit2007 3 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба Вероятно, таким образом компилятор приводит значение moFault к типу int, а в опциях проекта по умолчанию это знаковый тип. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба Вроде законно все, разве нет? 25 это int, в операции сдвига moFault имеет меньший тип, значит он неявно приводится к int. Соответственно, компилятор вправе загрузить moFault как знаковый сразу, без промежуточных команд. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 186 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба 36 минут назад, Edit2007 сказал: Вероятно, таким образом компилятор приводит значение moFault к типу int, а в опциях проекта по умолчанию это знаковый тип. Приведение к типу int: int i = moFault; Вот здесь приводится к типу int. И делается посредством LDRB. Что является правильным, так как по правилам си: Если для приведения типа нужно сделать несколько действий (например: изменение знаковости, изменение размера и т.п.) то первыми делаются те преобразования, которые выполняют наибольшее приближение, и далее - по убывающей. Т.е. - для приведения "unsigned char" -> "signed int", 1-м шагом должно выполниться приведение "unsigned char" -> "unsigned int", а вторым шагом "unsigned int" -> "signed int". А значит - должна быть LDRB. Что и наблюдается в других случаях. А данный баг проявляется только для счётчика сдвигов в операции сдвига. 30 минут назад, Arlleex сказал: 25 это int, в операции сдвига moFault имеет меньший тип, значит он неявно приводится к int. Соответственно, компилятор вправе загрузить moFault как знаковый сразу, без промежуточных команд. Нет. Попробуйте сделать: int i = moFault; Будет LDRB. Почему - писал выше. Да и легко можно убедиться глянув любой листинг. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
SII 0 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба 1 hour ago, jcxz said: Похоже - нарыл очередной баг IAR. Хотя вроде не такой опасный, как предыдущий. Может даже и совсем не опасный, и даже не баг, а просто неоптимальность компиляции... Код: typedef unsigned char u8; static u8 volatile moFault int i0; ... i0 += 25 << moFault; Даёт: ;i0 += 25 << moFault; 0x2219 MOVS R2,#+25 0xF995 0x101E LDRSB R1,[R5, #+30] 0x408A LSLS R2,R2,R1 0x1810 ADDS R0,R2,R0 Хотя, по идее, должна быть LDRB, а не LDRSB. Если написать например: i0 += 25u << moFault; то уже всё ок - LDRB detected. Понятно, что 25 - знаковое, но это вроде никак не должно влиять на команду чтения moFault? Или я чего-то не понимаю? PS: "IAR ANSI C/C++ Compiler V8.50.4.261/W32 for ARM". Но актуально и для более ранних версий. Если память не изменяет, всё выражение будет считаться знаковым, если в нём есть знаковые операнды -- а 25 у Вас было знаковое, как Вы сами и написали (примерно то же самое с вычислениями с плавающей запятой: если есть операнд double, то и все вычисления будут в double, а по умолчанию константы трактуются именно как double. если принудительно f не написать). Соответственно, компилятор moFault тоже преобразовал в знаковое -- и загрузил из памяти уже как знаковое, а не беззнаковое. Но для точности надо б стандарт читать -- а это лениво :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба Стандарт пишет, что продвижение типов при сдвиге делается для каждого операнда. Результирующий тип операции сдвига определяется типом левого операнда. В выражении int i = moFault; moFault вычисляется первым, и его тип - u8. Поэтому загружается он инструкцией LDRB. Дальше - присвоение. Тип результата - знаковое целое. u8 всем своим диапазоном помещается в положительную область значений знакового int, поэтому дополнительных продвижений не делается - записывается как есть. А вот при сдвигах (как я писал выше про стандарт): 25 это int, продвижение не выполняется, при этом int будет типом всего выражения 25 << moFault. moFault тоже не имеет смысла продвигать по типу вверх, т.к. архитектура не подразумевает типов данных с шириной > 64 бит. Значит, вполне закономерно загрузить moFault как угодно, в целом. Вариации LDRx не должны иметь побочных эффектов, поэтому листинг можно считать лишь не оптимальным по скорости и занимаемому объему. ИМХО. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 186 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба 27 минут назад, SII сказал: Если память не изменяет, всё выражение будет считаться знаковым, если в нём есть знаковые операнды Нет, это не так: ; i0 += 25 << (moFault >> 1); /// 0x2019 MOVS R0,#+25 0xF894 0x2020 LDRB R2,[R4, #+32] 0x0852 LSRS R2,R2,#+1 0x4090 LSLS R0,R0,R2 0x19C0 ADDS R0,R0,R7 В выражении могут быть и знаковые и беззнаковые операции вне зависимости от того, что в какой-то его части есть знаковые. Да - операция между знаковым типом и беззнаковым в результате даст знаковый тип, но в выражении до этого может быть выполнено несколько беззнаквых операций, если все их аргументы - беззнаковые. И только на какой-то N-ой операции, повстречав первый знаковый аргумент, операции станут знаковыми. Что и иллюстрирует пример выше. 27 минут назад, SII сказал: Соответственно, компилятор moFault тоже преобразовал в знаковое -- и загрузил из памяти уже как знаковое, а не беззнаковое. Но для точности надо б стандарт читать -- а это лениво :) Порядок многоступенчатого приведения типа с расширением и изменением знаковости - я уже выше описывал. Нельзя там грузить из памяти беззнаковую 8-разрядную переменную через LDRSB. Ни в каком случае нельзя. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба 17 минут назад, jcxz сказал: Нельзя там грузить из памяти беззнаковую 8-разрядную переменную через LDRSB. Ни в каком случае нельзя. Стандарт гласит, что отрицательный правый операнд в операции левого сдвига - это undefined behavior. Равно как и если значение этого правого операнда >= ширине типа левого операнда - тот же undefined behavior. Соответственно, компилятор вправе предполагать, что moFault априори не содержит значений > 31. А значит вправе грузить moFault любой инструкцией - знаковой или нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 186 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба 14 минут назад, Arlleex сказал: А значит вправе грузить moFault любой инструкцией - знаковой или нет. Это я понимаю. И команда сдвига Cortex-M со счётчиком в регистре использует только младшие 8 бит значения регистра. Понятно, что оно и так и так будет работать. Только размер LDRSB может быть больше чем LDRB. Но по тому как это выглядит, у меня складывается впечатление, что это всё же баг компилятора, который не заметили при тестировании, ведь результат всё равно правильный. Ибо даже: ; i0 += 25 >> (uint)moHost; /// 0x2019 MOVS R0,#+25 0xF994 0x201D LDRSB R2,[R4, #+29] 0x4603 MOV R3,R0 0x4113 ASRS R3,R3,R2 0x19DA ADDS R2,R3,R7 всё равно LDRSB хотя даже есть явное приведение типа. И в то же время: i0 += 25u >> (uint)moHost; /// 0x2019 MOVS R0,#+25 0x7F62 LDRB R2,[R4, #+29] 0x4603 MOV R3,R0 0x40D3 LSRS R3,R3,R2 0x19DA ADDS R2,R3,R7 чудеса! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба Скорее, логика была в том, что раз Стандарту не противоречит (п. 6.5.7 Bitwise shift operators), то не баг То, что в обоих примерах загрузка moHost разная - скорее, результат несколько иных оценок выражений при их декодировании. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 29 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба AR C/C++ Compiler for ARM 8.50.9.278 (8.50.9.278), Cortex-M0, оптимизация High Speed. //static uint8_t volatile moFault; moFault: DS8 1 DS8 3 //int i0; i0: DS8 4 //int main() //{ main: SUB SP,SP,#+4 // i0 += 25 << moFault; LDR R0,??DataTable0 LDR R1,[R0, #+4] MOVS R2,#+25 LDRB R0,[R0, #+0] LSLS R2,R2,R0 ADDS R0,R1,R2 // volatile int x = i0; STR R0,[SP, #+0] //for(;;); ??main_0: B ??main_0 //} ??DataTable0: DC32 moFault Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 186 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба 40 минут назад, VladislavS сказал: AR C/C++ Compiler for ARM 8.50.9.278 (8.50.9.278), Cortex-M0, оптимизация High Speed. Ну-да, ну-да - у GCC как всегда всё чистенько. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 29 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба Причём тут GCC? Первая "I" не скопипастилась. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Darth Vader 0 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба А что говорит стандарт о типе левого операнда в побитовых операциях сдвига? Мне казалось, что он должен быть беззнаковым char/short/long/long long/int (из популярной литературы по языку, не из стандарта) и компилятор делает неявное преобразование его в беззнаковое. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 131 22 января, 2021 Опубликовано 22 января, 2021 · Жалоба 17 минут назад, Darth Vader сказал: А что говорит стандарт о типе левого операнда в побитовых операциях сдвига? Говорит, что надо почитать сперва тему, в которой я, между прочим, уже приводил выдержки из Святого Писания... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться