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

Баг IAR 7.80.4 (ARM) при работе с float.

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

Я думаю компилятор видит тут беззнаковое сложение. Если вот так?

Ну а при чём тут сложение? Там операция приведения типа должна выполняться до сложения. Вне зависимости что делается после неё.

Цитата

res32 = (int)(eeg[i]*16) + (int)B15;

 

Чтобы добиться такого результата, я перебрал множество вариантов записи выражения. И если первый баг (VCVT.U16.F32 S0,S0,#+3) легко достигается, то для VCVT.U32.F32 S0,S0,#+4  мне пришлось долго искать условия его проявления. Если B15 привести к int или тип целевого адреса заменить на s32 (там u32), то компилит нормально. Или при изменении ещё каких-то условий (в соседних выражениях) - тоже начинает компилить нормально. Но это не выход. Что-ж - после каждой компиляции перепроверять все листинги?

 

Также можете посмотреть исходное моё сообщение: там вообще результат приведения float->int сразу поступает в функцию как аргумент. Аргумент типа int. И тем не менее - баг проявляется. Хотя тот случай мне удалось добиться под IAR_7.80.4, под 8.50.4 - ни в какую, что-то ещё влияет.

У меня вообще складывается ощущение, что замена S32->U32 происходит уже после компиляции, на каком-то этапе анализа результирующего кода (хотя даже с полностью отключенной оптимизацией всё равно это происходит).

 

PS: Посмотрим что шведы завтра скажут.

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


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

Если проследить все неявные преобразования, то получается как-то так 

res32 = (unsigned int)(int)(eeg[i] * 16.0f) + 0x00008000ul;

Как по стандарту положено делать последовательное float -> int -> unsigned int на ночь глядя не берусь сказать.

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


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

Да тут как раз все нормально - 16 к float, умножение, приведение к знаковому целому, репрезентация как беззаковое, сложение с 32768.
Умножение на 8 (или на 16) float-а и дальнейшее приведение к int компилятор сократил до инструкции преобразования float в fixed-point.
И как раз тут ошибка (дальше даже можно не рассматривать) - компилятор использует тип результата как u32, а не s32, не учитывая знак.

P.S. Интересно, что поместит инструкция VCVT.U32.F32 S0, S0, #+3 при, скажем, S0 == -2.5f (любое отрицательное значение) в S0. 0? Другой мусор?

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


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

Забил пример в Compiler Explorer. GCC как  vcvt.s32.f32 в обоих случаях делает.

https://godbolt.org/z/jjcjcK

 

Чутка упростил и осовременил код

https://godbolt.org/z/13YG81

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


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

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

И как раз тут ошибка (дальше даже можно не рассматривать) - компилятор использует тип результата как u32, а не s32, не учитывая знак.

Ошибка не в этом. Ошибка в том что значение получается неправильное. Уже после команды VCVT.U32.F32/VCVT.U16.F32.

Цитата

P.S. Интересно, что поместит инструкция VCVT.U32.F32 S0, S0, #+3 при, скажем, S0 == -2.5f (любое отрицательное значение) в S0. 0? Другой мусор?

Инструкции VCVT.U... любой отрицательный входной аргумент заменяют на 0. Т.е. - после выполняют аналог операции USAT(x, 16) или USAT(x, 32) для целых.

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


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

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

Забил пример в Compiler Explorer. GCC как  vcvt.s32.f32 в обоих случаях делает.

Только в случае с res16 = (int)(x * 8) + B15 получается куча лишних команд. По идее сама команда VCVT.S16.F32 уже должна бОльшую часть всех нужных операций сделать:

и преобразование в fixed-point и отсечение с насыщением для 16 бит.

С помощью инлайн-ассемблера завёрнутого в макрос удалось добиться желаемого:

#define VCVT_U16(y0, x, shift) do              \
  asm(                                         \
    "VCVT.S16.F32 %1, %2, %3\n"                \
    "VMOV %0, %1"                              \
    : "=r"(y0), "=t"(x) : "t"(x), "I"(shift)); \
  while (0)

...
  
  uint i;    
  float a = eegEmu[c];
  VCVT_U16(i, a, 3);
  adcVkt.in0 = i + B15;

результат:

0xED90 0x8A00      VLDR         S16,[R0, #0]       
0xEEBE 0x8A66      VCVT.S16.F32 S16, S16, #0x3 
0xEE18 0x0A10      VMOV         R0, S16                
0xF5A0 0x4000      SUB          R0,R0,#+32768      
0x81A8             STRH         R0,[R5, #+12]

с минимумом команд.

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


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

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

Только в случае с res16 = (int)(x * 8) + B15 получается куча лишних команд. По идее сама команда VCVT.S16.F32 уже должна бОльшую часть всех нужных операций сделать:

Как это? Все операции справа от равно должны в 32-битах выполниться, а лишь затем до 16 обрезаться. Именно так gcc и сделал.

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


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

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

Как это? Все операции справа от равно должны в 32-битах выполниться, а лишь затем до 16 обрезаться. Именно так gcc и сделал.

Неверно написал. Следует читать если применить: res16 = (s16)(x * 8) + B15, то получается куча лишних команд.

Если для такого преобразования использовать VCVT.S16.F32, то (когда целевая переменная - 16-разрядная) такое преобразование более полезно, чем VCVT.S32.F32. Тем что выполняет насыщение по диапазону -2^15...2^15-1. В то время как VCVT.S32.F32 - насыщает по диапазону -2^31...2^31-1 с последующим отсечением.

Так вот - GCC может и правильно делает в приведённых вами примерах, но он выполняет насыщение по -2^31...2^31-1, а затем отсечение мл.16 бит, а не просто насыщение по -2^15...2^15-1.

По идее - насыщение -2^15...2^15-1 в данном случае полезнее. IAR вроде сначала движется по такому пути, но затем путает 'S' на 'U'. :sad:

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


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

Смотри. Прикольно получилось. Заставил IAR просчитать это же выражение на этапе компиляции в классе test. Вывел его параллельно с тем что он в рантайме считает.

constexpr float eeg[] = {
   1.5290918e+01,
  -9.9167579e-03,
  -2.1458661e+01,
  -4.5517792e+01,
  -6.7281206e+01,
  -8.0055255e+01,
  -7.7754796e+01,
  -5.9643962e+01,
  -3.0858393e+01,
   2.3332083e+00,
   3.5382015e+01,
   6.4759012e+01,
   8.6718579e+01,
   9.6603208e+01,
   9.0879472e+01,
   6.9869923e+01,
   3.7196912e+01,
  -1.2272832e+00,
  -3.6623416e+01
};

class TEST
{
  uint32_t eeg_int32[ncell(eeg)]{};
public:  
  constexpr TEST()
  {
    for(uint32_t i=0; i<ncell(eeg); i++)
      eeg_int32[i] = (int32_t)(eeg[i]*16) + 0x00008000ul;
  }
  constexpr uint32_t operator()(uint32_t n) const { return eeg_int32[n]; }
};

inline constexpr TEST test;

int main()
{     
  for(uint32_t i=0; i<ncell(eeg); i++)
  {
    uint32_t tmp = (int32_t)(eeg[i]*16) + 0x00008000ul;  
    DEBUG_Print("%10d %10d\r\n",tmp,test(i));
  }
  
  for(;;);
}

Результат

Runtime   Constexpr
33012      33012
32768      32768
32768      32425
32768      32040
32768      31692
32768      31488
32768      31524
32768      31814
32768      32275
32805      32805
33334      33334
33804      33804
34155      34155
34313      34313
34222      34222
33885      33885
33363      33363
32768      32749
32768      32183

 

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


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

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

Прикольно получилось.

Листингом не поделитесь?)

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


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

Поделюсь. Чуть позже, работа пришла :)

Если отключить FPU, то колонки совпадают.

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


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

//    9 constexpr float eeg[] = {
eeg:
        DATA32
        DC32 0x4174'a79a, 0xbc22'79e6, 0xc1ab'ab56, 0xc236'1238, 0xc286'8ffa
        DC32 0xc2a0'1c4a, 0xc29b'8275, 0xc26e'936b, 0xc1f6'ddfd, 0x4015'5349
        DC32 0x420d'872f, 0x4281'849d, 0x42ad'6fea, 0x42c1'34d8, 0x42b5'c24a
        DC32 0x428b'bd67, 0x4214'c9a3, 0xbf9d'179e, 0xc212'7e61
//   10    1.5290918e+01,
//   11   -9.9167579e-03,
//   12   -2.1458661e+01,
//   13   -4.5517792e+01,
//   14   -6.7281206e+01,
//   15   -8.0055255e+01,
//   16   -7.7754796e+01,
//   17   -5.9643962e+01,
//   18   -3.0858393e+01,
//   19    2.3332083e+00,
//   20    3.5382015e+01,
//   21    6.4759012e+01,
//   22    8.6718579e+01,
//   23    9.6603208e+01,
//   24    9.0879472e+01,
//   25    6.9869923e+01,
//   26    3.7196912e+01,
//   27   -1.2272832e+00,
//   28   -3.6623416e+01
//   29 };

//   44 inline constexpr TEST test;
test:
        DATA32
        DC32 33'012, 32'768, 32'425, 32'040, 31'692, 31'488, 31'524, 31'814
        DC32 32'275, 32'805, 33'334, 33'804, 34'155, 34'313, 34'222, 33'885
        DC32 33'363, 32'749, 32'183

//   46 int main()
//   47 {     
main:
        PUSH     {R3-R7,LR}
//   48   for(uint32_t i=0; i<ncell(eeg); i++)
        MOVS     R4,#+0
        ADR.N    R5,?_0
        LDR.N    R6,??DataTable3
        LDR.N    R7,??DataTable3_1
//   49   {
//   50     uint32_t tmp = (int32_t)(eeg[i]*16) + 0x00008000ul;  
//   51     DEBUG_Print("%10d %10d\r\n",tmp,test(i));
??main_0:
        ADD      R0,R6,R4, LSL #+2
        LDR      R3,[R7, R4, LSL #+2]
        MOV      R1,R5
        ADDS     R4,R4,#+1
        VLDR     S0,[R0, #0]
        MOVS     R0,#+0
        VCVT.U32.F32 S0,S0,#+4
        VMOV     R2,S0
        ADD      R2,R2,#+32768
        BL       SEGGER_RTT_printf
        CMP      R4,#+19
        BCC.N    ??main_0

 

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


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

5 часов назад, VladislavS сказал:

Результат


Runtime   Constexpr
33012      33012
32768      32768
32768      32425
32768      32040
32768      31692
32768      31488
32768      31524
32768      31814
32768      32275
32805      32805
33334      33334
33804      33804
34155      34155
34313      34313
34222      34222
33885      33885
33363      33363
32768      32749
32768      32183

 

Ну да - это он и есть. Этот баг.

5 часов назад, VladislavS сказал:

Если отключить FPU, то колонки совпадают.

Если каким-либо образом заставить IAR ставить правильную VCVT, то (естественно) тоже совпадут.  :unknw:

3 часа назад, VladislavS сказал:

VCVT.U32.F32 S0,S0,#+4

 

Ну вот - у Вас даже VCVT.U32.F32! Мне с трудом удалось добиться стабильной генерации неверной команды для случая 32-битного выходного значения. Для 16-битного выходного - легко.

Это на какой версии IAR?

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


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

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

Это на какой версии IAR?

8.50.9. Оптимизация High->Size.

Для 32 бит вот так стабильно лечится. (int32_t)(eeg*16) + (int32_t)B15;

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


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

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

8.50.9. Оптимизация High->Size.

Для 32 бит вот так стабильно лечится. (int32_t)(eeg*16) + (int32_t)B15;

Но если целевая переменная в памяти - u16, то не поможет:

   res16 = (int)(eeg[i] * 8) + (int)B15;      
0xEB05 0x0086      ADD      R0,R5,R6, LSL #+2 
0xED90 0x0A00      VLDR     S0,[R0, #0]       
0xEEBF 0x0A66      VCVT.U16.F32 S0,S0,#+3     

IAR 8.50.4

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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