|
gcc 4.2.2 и умножение int 16x16 |
|
|
|
May 29 2008, 03:47
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Доброго времени! Подскажите, как заставить компилятор в Winavr генерить правильный код. Нужно Код int16_t Xarg,Yarg,Zarg; ........................ Zarg= (Xarg*Yarg) >>16; Приведение к 32-битам - это безумное количество кода. Надо, чтоб генерился код примерно такой (имена регистров не важны): Код lds r16,Xarg lds r17,Xarg+1 lds r18,Yarg lds r19,Yarg+1 clr r6 //9 // дальше стандартное знаковое умножение muls r17,19 movw r4,r0 mul 16,r18 movw r2,r0 mulsu r19,r16 sbc r5,r6 add r3,r0 adc r4,r1 adc r5,r6 mulsu r17,r18 sbc r5,r6 add r3,r0 adc r4,r1 adc r5,r6 // и выделение старшей части 32-битного результата sts Zarg,r4 sts Zarg+1,r5 // итого 22+9 = 31 такт Бился головой о стену - ниасилил компилер такой красоты. А очень надо. Если кто уже получал такое чистым Си, поделитесь, пожалуйста, опытом.
|
|
|
|
|
May 29 2008, 05:57
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(_Pasha @ May 29 2008, 07:47)  Бился головой о стену - ниасилил компилер такой красоты. А очень надо. Если кто уже получал такое чистым Си, поделитесь, пожалуйста, опытом. Нужно править компилятор. Анатолий.
Сообщение отредактировал aesok - May 29 2008, 06:00
|
|
|
|
|
May 29 2008, 06:47
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(aesok @ May 29 2008, 08:57)  Нужно править компилятор.
Анатолий. А с умножением Код static int16_t TR1=-1039; TR0=0; static uint8_t TR2=30; .........<skipped>........................... for(;;) { TR0 = (TR1*TR2)>>8; ..<skipped>............... } Получаем такую херь: Код 103: TR0 = (TR1*TR2)>>8; +00000833: 91800200 LDS R24,0x0200 Load direct from data space +00000835: 91900201 LDS R25,0x0201 Load direct from data space +00000837: 019C MOVW R18,R24 Copy register pair +00000838: E065 LDI R22,0x05 Load immediate +00000839: 0F22 LSL R18 Logical Shift Left +0000083A: 1F33 ROL R19 Rotate Left Through Carry +0000083B: 956A DEC R22 Decrement +0000083C: F7E1 BRNE PC-0x03 Branch if not equal +0000083D: 0F88 LSL R24 Logical Shift Left +0000083E: 1F99 ROL R25 Rotate Left Through Carry +0000083F: 1B28 SUB R18,R24 Subtract without carry +00000840: 0B39 SBC R19,R25 Subtract with carry +00000841: 2F23 MOV R18,R19 Copy register +00000842: 0F33 LSL R19 Logical Shift Left +00000843: 0B33 SBC R19,R19 Subtract with carry +00000844: 9330031A STS 0x031A,R19 Store direct to data space +00000846: 93200319 STS 0x0319,R18 Store direct to data space Оптимизация -O2 (!) (ошибочка:оптимизация была -Os) , mega640 (это к тому, что mulsu там есть  ) Компилер понял, что TR2 таки константа  , но это 41такт ! В то же время, при тех же условиях Код LDS R24,0x0200// Load direct from data space LDS R25,0x0201// Load direct from data space LDI R18,0x1E// я не говорю о том, что здесь не западло и LDS оставить и не оптимизировать ненужное // было 5 тактов mul r24,r18 movw r2,r0 mulsu r25,r18 ldi r18,0 sbc r1,r18 add r0,r3 adc r1,r18 //14 STS 0x031A,R1 Store direct to data space STS 0x0319,R0 Store direct to data space //18 составит 18 тактов. В общем, проблема серьезная, поскольку такие операции (умножить и поделить на 256) очень часто употребимы, думаю, не только мной. УПС, ошибочка в последнем коде. Исправленному верить.
|
|
|
|
|
May 29 2008, 07:03
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(_Pasha @ May 29 2008, 10:47)  Оптимизация -O2 (!), mega640 (это к тому, что mulsu там есть  ) А почему Вы не используете оптимизацию -Os? Анатолий.
Сообщение отредактировал aesok - May 29 2008, 07:03
|
|
|
|
|
May 29 2008, 08:42
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(_Pasha @ May 29 2008, 07:47)  Бился головой о стену - ниасилил компилер такой красоты. А очень надо. Если кто уже получал такое чистым Си, поделитесь, пожалуйста, опытом. А почему нужно именно на чистом С ? напишите свою функцию на чистом asm и будет Вам полное счастье.  Мне как-то понададобилось умножение 16бит x 16 бит на ATtiny, так там на С без mul получается вобще ужас с приведением к 32бит, ну а функция на асм DWORD mulu16by16(WORD, WORD); получилась более менее по времени.
|
|
|
|
|
May 29 2008, 09:05
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(singlskv @ May 29 2008, 11:42)  А почему нужно именно на чистом С ? напишите свою функцию на чистом asm и будет Вам полное счастье.  Дык я уже так и сделал, в смысле, прерывание написал. Получилось примерно следующее: 3 знаковых умножения 16х16 вида X[i] = Beta*Y[i] 15 умножений int16_t * uint8_t 18 операций 16-бит сложения 30 операций 16-бит сравнения (ограничение) помещается в 553 такта с учетом того, что используются 15 регистров, бесстыдно сохраняемых в стеке + SREG ( это 31+31 = 62 такта на контекст) Сишный вариант давал 1500 - с чем-то тактов. Даже с -О2. Устраивает тактов до 800 Беда, однако...
|
|
|
|
|
Jun 1 2008, 09:27
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Какая же все-таки грабляндия эта арифметика! Привожу правильный и проверенный на всем множестве код для операции (int16_t) = (int16_t)*(uint8_t)/256 в виде AVR gnu-as Код #define arg1L r16 #define arg1H r17 #define scalar r18 #define NULL_REG r6 scale16x8: clr NULL_REG //........................... mul arg1L,scalar mov r2,r1 mulsu arg1H,scalar adc r0,r2 adc r1,NULL_REG
; result in r0:r1 Итого 7 тактов.
|
|
|
|
|
Jun 1 2008, 14:04
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(_Pasha @ Jun 1 2008, 15:27)  Какая же все-таки грабляндия эта арифметика! Привожу правильный и проверенный на всем множестве код для операции Переписал для inline асссемблера: Код static inline uint16_t scale16x8(uint16_t a, uint8_t b) { uint16_t result; uint8_t tmp; /* %A0 - low byte of result, %B0 - hi byte of result, %1 - tmp %A2 - low byte of the first operand, %B2 - hi byte of the first operand, %3 - second operand */
asm ( "clr %1 \n\t" "mul %A2,%3 \n\t" "mov r2,r1 \n\t" "mulsu %B2,%3 \n\t" "adc r0,r2 \n\t" "adc r1,%1 \n\t" "movw %A0, r0 \n\t" "clr __zero_reg__ \n\t" : "=&r" (result), "=&r" (tmp) : "a" (a), "a" (b) );
return result; } Добавил зачистку __zero_reg__, так положено. Только вот что-то не сходится: Код int scale_test(void) { uint16_t a = 0; uint8_t b = 0; while (TRUE) { if (scale16x8(a, b) != ((unsigned long)a*b/256)) { nokia_puts_p("a = "); nokia_put_word(a); nokia_puts_p(", b = "); nokia_put_word(b); return FALSE; } if (a == 0xFFFF && b == 0xFF) return TRUE; if (!++b) a++; } } Ошибка при a=0x8000 и b=1.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jun 1 2008, 14:22
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

|
Цитата(AHTOXA @ Jun 1 2008, 18:04)  Ошибка при a=0x8000 и b=1. У Вас static inline uint16_t scale16x8( uint16_t a , uint8_t b ) а было: (int16_t) = ( int16_t )*(uint8_t)/256
|
|
|
|
|
Jun 1 2008, 15:47
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(singlskv @ Jun 1 2008, 20:22)  У Вас static inline uint16_t scale16x8(uint16_t a , uint8_t b ) а было: (int16_t) = (int16_t )*(uint8_t)/256 Точно! Виноват :-) Исправил: Код static inline int16_t scale16x8(int16_t a, uint8_t b) { int16_t result; uint8_t tmp; /* %A0 - low byte of result, %B0 - hi byte of result, %1 - tmp %A2 - low byte of the first operand, %B2 - hi byte of the first operand, %3 - second operand */
asm ( "clr %1 \n\t" "mul %A2,%3 \n\t" "mov r2,r1 \n\t" "mulsu %B2,%3 \n\t" "adc r0,r2 \n\t" "adc r1,%1 \n\t" "movw %A0, r0 \n\t" "clr __zero_reg__ \n\t" : "=&r" (result), "=&r" (tmp) : "a" (a), "a" (b) );
return result; }
int scale_test(void) { int16_t i = -32768; uint8_t b = 0; int16_t i1, i2; while (TRUE) { i1 = scale16x8(i, b); i2 = (signed long)i*b/256; if (i1 != i2) { nokia_puts_p(" a = "); nokia_put_int(i); nokia_puts_p(", b = "); nokia_put_word(b); return FALSE; } b++; if (!b) { i++; } if ((i == 32767) && (b == 0xFF)) break; } return TRUE; } Но всё равно, выдаёт ошибку при i=-32768 и b=1... Опять я где-то что-то проглядел?
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jun 2 2008, 05:19
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(AHTOXA @ Jun 2 2008, 06:57)  То есть, функция таки работает не "во всём множестве"? :-) А зачем? :-) Или -32768 не бывает?  Таки да. Каюсь, не проверил оные значения ввиду нехватки времени и отсутсвия их практической ценности в целевом проекте. На самом деле с единицей оказалось все правильно. Возьмем -32767=0x8001  Для того, чтобы доказать, что 0x8001 >>8 != 0x80, приведем его в беззнаковый вид. Дополнительный код даст 0x7fff, операция сдвига на 8 бит даст 0x7f. Извлекаем обратно дополнительный код из результата, получаем 0x81. При расширении знака в старший байт имеем 0xFF81, что и наблюдаеццо в симуляторе. Как ни странно. Хотя здравый смысл подсказывает, что signed 0x8001 >>8 = 0xFF80. -32768 - счастливое исключение  Поскольку +32768 в двух байтах не бывает, попытка привести к беззнаковому умножению 16*8 тоже ни к чему не приведет. Но на этом беда не заканчивается. Однобайтовый int8_t тоже ведь имеет встроенный баг 0x80. Видимо, придется обрабатывать как исключение. Но проще съехать с базара и сказать Таки да! Не на всем множестве!
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|