|
IAR ARM ассемблер, Преобразовать процедуру в макрос |
|
|
|
Aug 28 2017, 13:42
|
Частый гость
 
Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764

|
Здравствуйте. В проекте функция реализована как ассемблерная процедура. Состоит из одной инструкции и хотелось бы преобразовать её в макрос, чтобы не тратить такты на вход и выход. Реализована в *.s файле в виде Код SECTION .text:CODE:NOROOT(2) PUBLIC MULSHIFT32 THUMB MULSHIFT32 smull r2, r0, r1, r0 BX lr Далее используется в *.c файлах как обычная функция b2 = MULSHIFT32(*cptr++, a1 - a2) << (s1); Не зная тонкостей синтаксиса ассемблера IAR попробовал "в лоб" реализовать таким образом Код MULSHIFT32 MACRO smull r2, r0, r1, r0 ENDM Линкер ругается на неизвестное имя MULSHIFT32. Ключевые слова PUBLIC и EXTERN вызывают ошибку. Кто подскажет, как этот макрос правильно оформить, чтобы можно было использовать его вне *.s-файла?
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 26)
|
Aug 28 2017, 14:01
|
Местный
  
Группа: Свой
Сообщений: 475
Регистрация: 14-04-05
Из: Москва
Пользователь №: 4 140

|
Не занимайтесь ерундой. Умножать с нужной разрядностью - дело компилятора. Код int32_t x=1L,y=2L; int64_t z; z = (int64_t)x*y; Дадут вам Код // 106 int32_t x=1L,y=2L; MOVS R0,#+1 STR R0,[SP, #+4] MOVS R0,#+2 STR R0,[SP, #+0] // 107 int64_t z; // 108 z = (int64_t)x*y; LDR R0,[SP, #+4] LDR R1,[SP, #+0] SMULL R0,R1,R1,R0 STRD R0,R1,[SP, #+8]
|
|
|
|
|
Aug 28 2017, 14:43
|
Частый гость
 
Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764

|
VladislavS, спасибо за идею. Это не совсем то, что нужно, но получилось слегка ускорить алгоритм. Создал обычный макрос Код #define MULSHIFT32(arg1, arg2) ((((long long)arg1)*((long long)arg2))>>32) Всё-равно получается избыточно, если верить листингу. Было до Код b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; 0x59ac0: 0xf8d4 0xa010 LDR.W R10, [R4, #0x10] b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; 0x59ac4: 0x1846 ADDS R6, R0, R1 b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; 0x59ac6: 0x1a09 SUBS R1, R1, R0 0x59ac8: 0xf855 0x0b04 LDR.W R0, [R5], #0x4 0x59acc: 0xf7fd 0xf9ea BL MULSHIFT32 ; 0x56ea4 MULSHIFT32: 0x56ea4: 0xfb81 0x2000 SMULL R2, R0, R1, R0 0x56ea8: 0x4770 BX LR 0x59ad0: 0x4680 MOV R8, R0 0x59ad2: 0xea4f 0x0848 LSL.W R8, R8, #1 Стало после Код b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; 0x58dee: 0xf851 0x6b04 LDR.W R6, [R1], #0x4 0x58df2: 0x17f7 ASRS R7, R6, #31 0x58df4: 0x17e5 ASRS R5, R4, #31 0x58df6: 0x17d3 ASRS R3, R2, #31 0x58df8: 0x1aa2 SUBS R2, R4, R2 0x58dfa: 0xeb65 0x0303 SBC.W R3, R5, R3 0x58dfe: 0x4634 MOV R4, R6 0x58e00: 0x463d MOV R5, R7 0x58e02: 0xfba2 0x6704 UMULL R6, R7, R2, R4 0x58e06: 0xfb02 0x7705 MLA R7, R2, R5, R7 0x58e0a: 0xfb03 0x7704 MLA R7, R3, R4, R7 0x58e0e: 0x46b8 MOV R8, R7 0x58e10: 0xea4f 0x0848 LSL.W R8, R8, #1 В моём случае объявить, как в Вашем примере 64-битную переменную и положить в неё результат 32х32 не получится.
|
|
|
|
|
Aug 28 2017, 15:18
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Из любопытства попробовал на GCC вот такое: Код #define MULSHIFT32(arg1, arg2) ((((long long)arg1)*((long long)arg2))>>32) int volatile a0, a7, b7, ar[2]; void f(void) { int* cptr = ar; b7 = MULSHIFT32(*cptr++, a0) << 1; } Получилось вот что: Код 0x0800146E 4D72 LDR r5,[pc,#456]; @0x08001638 0x08001470 686B LDR r3,[r5,#0x04] 0x08001472 68E8 LDR r0,[r5,#0x0C] 0x08001474 FB830100 SMULL r0,r1,r3,r0 0x08001478 184A ADDS r2,r1,r1 0x0800147A 612A STR r2,[r5,#0x10] А если написать вот так Код b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; То получается АдЪ: Код 0x0800146E 4D77 LDR r5,[pc,#476]; @0x0800164C 0x08001470 6868 LDR r0,[r5,#0x04] 0x08001472 68AE LDR r6,[r5,#0x08] 0x08001474 68EC LDR r4,[r5,#0x0C] 0x08001476 17C1 ASRS r1,r0,#31 0x08001478 1B80 SUBS r0,r0,r6 0x0800147A EB6171E6 SBC r1,r1,r6,ASR #31 0x0800147E FB04F201 MUL r2,r4,r1 0x08001482 17E3 ASRS r3,r4,#31 0x08001484 FB002303 MLA r3,r0,r3,r2 0x08001488 FBA40100 UMULL r0,r1,r4,r0 0x0800148C 185C ADDS r4,r3,r1 0x0800148E 1922 ADDS r2,r4,r4 0x08001490 616A STR r2,[r5,#0x14]
|
|
|
|
|
Aug 28 2017, 16:00
|
Местный
  
Группа: Свой
Сообщений: 475
Регистрация: 14-04-05
Из: Москва
Пользователь №: 4 140

|
Во-первых, очень некрасиво не показывать типы используемых переменных и желаемого результата. Во-вторых, разберитесь с размерностью вычислений. Написанный вами макрос делает совсем не то что вы хотите. В-третьих, ваш mulshift32, судя по всему, это банальное Код int32_t x=1L,y=2L; int32_t z; z = ((int64_t)x*y)>>32; Поверьте, компилятор сделает всё лучше вас, особенно с оптимизацией. Только не мешайте ему. Код // int32_t x=1L,y=2L; MOVS R0,#+1 MOVS R1,#+2 // int32_t z; // z = ((int64_t)x*y)>>32; SMULL R0,R1,R1,R0 В-четвёртых, вместо x и y можете ставить что угодно, только с соблюдением типов.
|
|
|
|
|
Aug 28 2017, 16:14
|
Местный
  
Группа: Свой
Сообщений: 475
Регистрация: 14-04-05
Из: Москва
Пользователь №: 4 140

|
Дарю Код #define MULSHIFT32(arg1, arg2) (((int64_t)(int32_t)(arg1)*(int32_t)(arg2))>>32)
|
|
|
|
|
Aug 29 2017, 09:41
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Кстати, если в моём эксперименте с GCC сделать так Код #define MULSHIFT32(a, b) (((long long)(a) * (b)) >> 32) то код генерится вменяемый. Очевидно, в первоначальном варианте макроса не хватает скобок вокруг аргументов. На первый взгляд, в данном случае неважно, но, возможно, я что-то не заметил.
|
|
|
|
|
Aug 29 2017, 09:53
|
Частый гость
 
Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764

|
jcxz, всё верно, чужие библиотеки. Портирую mp3-декодер Helix. В очередной раз) Цитата(jcxz @ Aug 28 2017, 18:49)  Если Вам нужны только старшие 32 бита (как следует из этого макроса), то всё просто: #define MULSHIFT32(x, y) __SMMUL(x, y) Спасибо. Даже забыл, что в IAR есть intrinsic-ассемблер, как всё просто оказывается. Цитата(VladislavS @ Aug 28 2017, 19:14)  Дарю Код #define MULSHIFT32(arg1, arg2) (((int64_t)(int32_t)(arg1)*(int32_t)(arg2))>>32) Спасибо за подарок. Прогнал оба варианта на разных уровнях оптимизации. По оси ординат разница выполнения алгоритма в мс. Time( __SMMUL(x, y)) - Time((((int64_t)(int32_t)(arg1)*(int32_t)(arg2))>>32)). Без оптимизации паритет, на умеренной оптимизации использование интринсик-функции __SMMUL(x, y) оказывается более быстрым, в случае максимальной оптимизации по скорости выигрывает решение от VladislavS. Разница не существенная, в районе 1%.
Дизассемблер для каждой из реализаций. Алгоритм раскладывается в одни и те же инструкции, но с использованием разных РОН. Код b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; 0x59830: 0x1979 ADDS R1, R7, R5 b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; 0x59832: 0x1bed SUBS R5, R5, R7 0x59834: 0xf852 0x7b04 LDR.W R7, [R2], #0x4 0x59838: 0xfb57 0xfc05 SMMUL R12, R7, R5 0x5983c: 0xea4f 0x0c4c LSL.W R12, R12, #1 Код b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; 0x59906: 0x1978 ADDS R0, R7, R5 b0 = a0 + a7; b7 = MULSHIFT32(*cptr++, a0 - a7) << 1; 0x59908: 0xf853 0xcb04 LDR.W R12, [R3], #0x4 0x5990c: 0x1bed SUBS R5, R5, R7 0x5990e: 0xfb5c 0xfc05 SMMUL R12, R12, R5 0x59912: 0xea4f 0x0c4c LSL.W R12, R12, #1 И хотя первоначальный вопрос про синтаксис макросов остался открытым, но задача решена другими средствами. Спасибо за помощь!
|
|
|
|
|
Aug 29 2017, 10:29
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(Sergey_Aleksandrovi4 @ Aug 29 2017, 12:53)  И хотя первоначальный вопрос про синтаксис макросов остался открытым, но задача решена другими средствами. Спасибо за помощь! Имеется в виду вот этот вопрос? Цитата(Sergey_Aleksandrovi4 @ Aug 28 2017, 16:42)  Кто подскажет, как этот макрос правильно оформить, чтобы можно было использовать его вне *.s-файла? Тогда ответ такой: никак. Это макросы ассемблера, фактически текстовая подстановка в пределах ассемблерного файла. Ни разу не видел, чтобы такие вещи действовали в сишных файлах. И уж точно на уровне линкера ничего этого быть не может.
|
|
|
|
|
Aug 29 2017, 13:04
|
Частый гость
 
Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764

|
Цитата(scifi @ Aug 29 2017, 13:29)  Это макросы ассемблера, фактически текстовая подстановка в пределах ассемблерного файла. Вот оно что. Спасибо. С ARM-ассемблером пару раз в жизни сталкивался, не знал про эти тонкости. jcxz, в двух словах - интерактивная игрушка для детей, ничего серьёзного. Кодек этот давно ещё портировал на cortex M3 (не без помощи здешних форумчан). Теперь перевожу на cortex M4, ну и решил по-максимуму из него выжать и закрыть тему навсегда.
|
|
|
|
|
Aug 30 2017, 08:26
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(VladislavS @ Aug 28 2017, 19:14)  Дарю Код #define MULSHIFT32(arg1, arg2) (((int64_t)(int32_t)(arg1)*(int32_t)(arg2))>>32) __SMMUL() может быть чуть-чуть лучше если нужно ещё и округление результата: __SMMULR(). Правда без возможности предварительного сдвига результата влево на 1 бит, это округление редко бывает полезным.  Не знаю почему в системе команд не предусмотрена опция сдвига влево результата на 1 бит.
|
|
|
|
|
Aug 31 2017, 06:12
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(VladislavS @ Aug 31 2017, 06:58)  Могли бы показать пример кода, где вы CLZ применяете? Правда, интересно глянуть. А какой смысл? Вы считаете, что сможете вот так прям показать как обойтись без CLZ? Видимо разработчики ARM-архитектуры всё-таки считают, что CLZ нужна раз ввели её. Что-ж... Например в текущем проекте: 1. Во всех местах, где нужно преобразование IP-адреса и маски подсети из формата типа 192.168.1.x/255.255.255.0 в упакованный формат 192.168.1.x/24. Код if (!(j = ReverseByteOrder(JReadIP(member)))) break; j1 = CntLeadingZeros(ReverseBitOrder(j)); if (j1 < 2 || ~j >> j1) break; cfgMain.enet.mask = j1; 2. В местах, где нужно преобразовать битовую карту в список значений: Код do { addr = 31 - CntLeadingZeros(addrMap); WriteNum(&addr, JSON_T_NUM8U); } while (addrMap -= 1 << addr); 3. В распаковщике сжатого битового потока: Код u16fast TinfData::ReadBits(u8fast n) { u32 i, i0, i1 = tag; if (!(i0 = i1 >> n)) { i = 31 - CntLeadingZeros(i1); u32p8 const *p, *p1 = (u32p8 const *)srcCur; if ((u32)(p = p1 + 1) > (u32)srcEnd) TinfFault(ERR_OVERFLOW_SRC); srcCur = (u8 const *)p; i1 += (i0 = *p1) - 1 << i; i0 = (i0 >> 1) + B31; i0 >>= n - i - 1; } tag = i0; return i1 - (i0 << n); } 4. В API разных служб, которые принимают и фиксируют клиентские запросы (в битовой карте) и впоследствии их обрабатывают по мере освобождения службы и в порядке приоритета. Итого - в десятке мест. в хидерах: #define CntLeadingZeros() __CLZ() #define ReverseBitOrder() __RBIT() В другом проекте есть например использование CLZ при распарсивании UTF-8 кодировки принимаемой с терминала: Код while ((int)(c = TermGetChar(tout)) >= 0) { do { if (PARSE_UTF8_DEF && (c & B7)) { //преобразование UTF-8 в Win1251 u32 cc = (u32)c << 25; if (j = CntLeadingZeros(~cc)) { if (j > 5 || state != S_NONE) return HEsc_ERROR; cnt = j; j += 26; arg = c << j >> j; state = S_UTF8; } else { if (state != S_UTF8) return HEsc_ERROR; arg = arg << 6 | cc >> 25; if (--cnt) continue; c = (cc = arg - 0x410) + 0xC0; if (cc > (uint)'я' - (uint)'А') { c = 'Ё'; if (cc += 0x410u - 0x401u) { c = 'ё'; if (cc += 0x401u - 0x451u) return HEsc_SKIP; } } state = S_INIT; return (HEsc)c; } } else if ((c & 0x7F) == 0x1B) { ...
|
|
|
|
|
Aug 31 2017, 07:53
|
Местный
  
Группа: Свой
Сообщений: 475
Регистрация: 14-04-05
Из: Москва
Пользователь №: 4 140

|
Цитата(jcxz @ Aug 31 2017, 09:12)  А какой смысл? Вы считаете, что сможете вот так прям показать как обойтись без CLZ?  Нет, мне реально интересно где и как вы используете CLZ. Спасибо за примеры. И всё же, я считаю, что надо по максимуму использовать возможности языка для переносимости. Затем интринсики и в самых тяжёлых случаях асм. UPD: В случае с CLZ с переносимостью как раз полный порядок. Она есть практически во всех процессорах, за исключением всяких 8-биток.
|
|
|
|
|
Aug 31 2017, 08:35
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(VladislavS @ Aug 31 2017, 10:53)  UPD: В случае с CLZ с переносимостью как раз полный порядок. Она есть практически во всех процессорах, за исключением всяких 8-биток. Но стандартными средствами языка си её как видимо не получить на современном уровне развития компиляторов. Не настолько они ещё интеллектуальны, чтобы цикл, реализующий функционал CLZ, компилятор заменял на CLZ (хотя - может какие-то уже умеют? 8-) Вот именно поэтому у меня и есть макрос CntLeadingZeros(), разворачивающийся на ARM в CLZ, на других CPU - в их соответствующие инструкции. Или в циклы, там где нет подобной инструкции. А ещё недавно и конструкцию (long)((long long)(long)(x) * (long long)(long)(y) >> 32) IAR видимо не догадывался заменить одной командой.
|
|
|
|
|
Aug 31 2017, 14:41
|

Знающий
   
Группа: Участник
Сообщений: 756
Регистрация: 14-11-14
Пользователь №: 83 663

|
Цитата(KRS @ Aug 31 2017, 15:15)  Так у IAR теперь есть GCC inline assembler так что можно и inline функции на асме писать. IAR с GNU Compiler Collection?
--------------------
Пролетарий умственного труда.
|
|
|
|
|
Sep 3 2017, 17:13
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(Obam @ Aug 31 2017, 17:41)  IAR с GNU Compiler Collection? Вы документацию смотрели на IAR? там теперь можно Код int Add(int term1, int term2) { int sum; asm("add %0,%1,%2" : "=r"(sum) : "r" (term1), "r" (term2)); return sum; } и такую функцию можно сделать inline
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|