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

Следующий баг IAR (сдвиги на переменную величину).

Похоже - нарыл очередной баг 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.  :unknw:

Если написать например: i0 += 25u << moFault;

то уже всё ок - LDRB detected.

Понятно, что 25 - знаковое, но это вроде никак не должно влиять на команду чтения moFault?

Или я чего-то не понимаю?

 

PS: "IAR ANSI C/C++ Compiler V8.50.4.261/W32 for ARM". Но актуально и для более ранних версий.

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


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

Вероятно, таким образом компилятор приводит значение moFault к типу  int, а в опциях проекта по умолчанию это знаковый тип.

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


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

Вроде законно все, разве нет?
25 это int, в операции сдвига moFault имеет меньший тип, значит он неявно приводится к int.
Соответственно, компилятор вправе загрузить moFault как знаковый сразу, без промежуточных команд.

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


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

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. Почему - писал выше. Да и легко можно убедиться глянув любой листинг.

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


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

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.  :unknw:

Если написать например: 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 тоже преобразовал в знаковое -- и загрузил из памяти уже как знаковое, а не беззнаковое. Но для точности надо б стандарт читать -- а это лениво :)

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


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

Стандарт пишет, что продвижение типов при сдвиге делается для каждого операнда.
Результирующий тип операции сдвига определяется типом левого операнда.

В выражении

int i = moFault;

moFault вычисляется первым, и его тип - u8. Поэтому загружается он инструкцией LDRB. Дальше - присвоение. Тип результата - знаковое целое. u8 всем своим диапазоном помещается в положительную область значений знакового int, поэтому дополнительных продвижений не делается - записывается как есть.

А вот при сдвигах (как я писал выше про стандарт): 25 это int, продвижение не выполняется, при этом int будет типом всего выражения 25 << moFault. moFault тоже не имеет смысла продвигать по типу вверх, т.к. архитектура не подразумевает типов данных с шириной > 64 бит. Значит, вполне закономерно загрузить moFault как угодно, в целом. Вариации LDRx не должны иметь побочных эффектов, поэтому листинг можно считать лишь не оптимальным по скорости и занимаемому объему.

ИМХО.

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


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

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. Ни в каком случае нельзя.

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


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

17 минут назад, jcxz сказал:

Нельзя там грузить из памяти беззнаковую 8-разрядную переменную через LDRSB. Ни в каком случае нельзя.

Стандарт гласит, что отрицательный правый операнд в операции левого сдвига - это undefined behavior.
Равно как и если значение этого правого операнда >= ширине типа левого операнда - тот же undefined behavior.
Соответственно, компилятор вправе предполагать, что moFault априори не содержит значений > 31.
А значит вправе грузить moFault любой инструкцией - знаковой или нет.

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


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

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           

чудеса!  :crazy:

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


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

Скорее, логика была в том, что раз Стандарту не противоречит (п. 6.5.7 Bitwise shift operators), то не баг:wink:

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

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


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

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

 

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


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

40 минут назад, VladislavS сказал:

AR C/C++ Compiler for ARM 8.50.9.278 (8.50.9.278), Cortex-M0, оптимизация High Speed.

Ну-да, ну-да - у GCC как всегда всё чистенько. :wink:

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


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

А что говорит стандарт о типе левого операнда в побитовых операциях сдвига? Мне казалось, что он должен быть беззнаковым char/short/long/long long/int (из популярной литературы по языку, не из стандарта) и компилятор делает неявное преобразование его в беззнаковое.

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


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

17 минут назад, Darth Vader сказал:

А что говорит стандарт о типе левого операнда в побитовых операциях сдвига?

Говорит, что надо почитать сперва тему, в которой я, между прочим, уже приводил выдержки из Святого Писания...:spiteful:

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


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

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

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

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

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

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

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

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

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

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