jcxz 245 21 декабря, 2020 Опубликовано 21 декабря, 2020 · Жалоба 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: Посмотрим что шведы завтра скажут. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 21 декабря, 2020 Опубликовано 21 декабря, 2020 · Жалоба Если проследить все неявные преобразования, то получается как-то так res32 = (unsigned int)(int)(eeg[i] * 16.0f) + 0x00008000ul; Как по стандарту положено делать последовательное float -> int -> unsigned int на ночь глядя не берусь сказать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 21 декабря, 2020 Опубликовано 21 декабря, 2020 · Жалоба Да тут как раз все нормально - 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? Другой мусор? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 21 декабря, 2020 Опубликовано 21 декабря, 2020 · Жалоба Забил пример в Compiler Explorer. GCC как vcvt.s32.f32 в обоих случаях делает. https://godbolt.org/z/jjcjcK Чутка упростил и осовременил код https://godbolt.org/z/13YG81 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 21 декабря, 2020 Опубликовано 21 декабря, 2020 · Жалоба 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) для целых. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 21 декабря, 2020 Опубликовано 21 декабря, 2020 · Жалоба 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] с минимумом команд. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 21 декабря, 2020 Опубликовано 21 декабря, 2020 · Жалоба 26 минут назад, jcxz сказал: Только в случае с res16 = (int)(x * 8) + B15 получается куча лишних команд. По идее сама команда VCVT.S16.F32 уже должна бОльшую часть всех нужных операций сделать: Как это? Все операции справа от равно должны в 32-битах выполниться, а лишь затем до 16 обрезаться. Именно так gcc и сделал. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 21 декабря, 2020 Опубликовано 21 декабря, 2020 · Жалоба 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'. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 22 декабря, 2020 Опубликовано 22 декабря, 2020 · Жалоба Смотри. Прикольно получилось. Заставил 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrBearManul 0 22 декабря, 2020 Опубликовано 22 декабря, 2020 · Жалоба 3 минуты назад, VladislavS сказал: Прикольно получилось. Листингом не поделитесь?) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 22 декабря, 2020 Опубликовано 22 декабря, 2020 · Жалоба Поделюсь. Чуть позже, работа пришла :) Если отключить FPU, то колонки совпадают. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 22 декабря, 2020 Опубликовано 22 декабря, 2020 · Жалоба // 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 22 декабря, 2020 Опубликовано 22 декабря, 2020 · Жалоба 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, то (естественно) тоже совпадут. 3 часа назад, VladislavS сказал: VCVT.U32.F32 S0,S0,#+4 Ну вот - у Вас даже VCVT.U32.F32! Мне с трудом удалось добиться стабильной генерации неверной команды для случая 32-битного выходного значения. Для 16-битного выходного - легко. Это на какой версии IAR? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 22 декабря, 2020 Опубликовано 22 декабря, 2020 · Жалоба 32 минуты назад, jcxz сказал: Это на какой версии IAR? 8.50.9. Оптимизация High->Size. Для 32 бит вот так стабильно лечится. (int32_t)(eeg*16) + (int32_t)B15; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 22 декабря, 2020 Опубликовано 22 декабря, 2020 · Жалоба 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться