Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: IAR ARM ассемблер
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Sergey_Aleksandrovi4
Здравствуйте. В проекте функция реализована как ассемблерная процедура. Состоит из одной инструкции и хотелось бы преобразовать её в макрос, чтобы не тратить такты на вход и выход.
Реализована в *.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-файла?
VladislavS
Не занимайтесь ерундой. Умножать с нужной разрядностью - дело компилятора.

Код
  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]
Sergey_Aleksandrovi4
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 не получится.
scifi
Из любопытства попробовал на 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]
jcxz
Цитата(Sergey_Aleksandrovi4 @ Aug 28 2017, 17:43) *
Код
#define MULSHIFT32(arg1, arg2)    ((((long long)arg1)*((long long)arg2))>>32)

Если Вам нужны только старшие 32 бита (как следует из этого макроса), то всё просто:
#define MULSHIFT32(x, y) __SMMUL(x, y)
VladislavS
Во-первых, очень некрасиво не показывать типы используемых переменных и желаемого результата.
Во-вторых, разберитесь с размерностью вычислений. Написанный вами макрос делает совсем не то что вы хотите.
В-третьих, ваш 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 можете ставить что угодно, только с соблюдением типов.
jcxz
Цитата(VladislavS @ Aug 28 2017, 19:00) *
Поверьте, компилятор сделает всё лучше вас, особенно с оптимизацией. Только не мешайте ему.

Вангую, что автор не сам ваяет, а пытается запустить/портировать готовое, где уже есть много MULSHIFT32()...
VladislavS
Дарю

Код
#define MULSHIFT32(arg1, arg2)    (((int64_t)(int32_t)(arg1)*(int32_t)(arg2))>>32)
scifi
Кстати, если в моём эксперименте с GCC сделать так
Код
#define MULSHIFT32(a, b)  (((long long)(a) * (b)) >> 32)

то код генерится вменяемый.
Очевидно, в первоначальном варианте макроса не хватает скобок вокруг аргументов. На первый взгляд, в данном случае неважно, но, возможно, я что-то не заметил.
Sergey_Aleksandrovi4
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

И хотя первоначальный вопрос про синтаксис макросов остался открытым, но задача решена другими средствами. Спасибо за помощь!
scifi
Цитата(Sergey_Aleksandrovi4 @ Aug 29 2017, 12:53) *
И хотя первоначальный вопрос про синтаксис макросов остался открытым, но задача решена другими средствами. Спасибо за помощь!

Имеется в виду вот этот вопрос?
Цитата(Sergey_Aleksandrovi4 @ Aug 28 2017, 16:42) *
Кто подскажет, как этот макрос правильно оформить, чтобы можно было использовать его вне *.s-файла?

Тогда ответ такой: никак. Это макросы ассемблера, фактически текстовая подстановка в пределах ассемблерного файла. Ни разу не видел, чтобы такие вещи действовали в сишных файлах. И уж точно на уровне линкера ничего этого быть не может.
jcxz
Цитата(Sergey_Aleksandrovi4 @ Aug 29 2017, 12:53) *
jcxz, всё верно, чужие библиотеки. Портирую mp3-декодер Helix. В очередной раз)

Ну вот я так и думал, что Helix wink.gif
И макрос этот я запостил как раз из своего работающего проекта на Helix rolleyes.gif
Что ваяете, если не секрет?
Sergey_Aleksandrovi4
Цитата(scifi @ Aug 29 2017, 13:29) *
Это макросы ассемблера, фактически текстовая подстановка в пределах ассемблерного файла.

Вот оно что. Спасибо. С ARM-ассемблером пару раз в жизни сталкивался, не знал про эти тонкости.
jcxz, в двух словах - интерактивная игрушка для детей, ничего серьёзного. Кодек этот давно ещё портировал на cortex M3 (не без помощи здешних форумчан). Теперь перевожу на cortex M4, ну и решил по-максимуму из него выжать и закрыть тему навсегда.
jcxz
Цитата(VladislavS @ Aug 28 2017, 19:14) *
Дарю
Код
#define MULSHIFT32(arg1, arg2)    (((int64_t)(int32_t)(arg1)*(int32_t)(arg2))>>32)

__SMMUL() может быть чуть-чуть лучше если нужно ещё и округление результата: __SMMULR().
Правда без возможности предварительного сдвига результата влево на 1 бит, это округление редко бывает полезным. sad.gif
Не знаю почему в системе команд не предусмотрена опция сдвига влево результата на 1 бит.
VladislavS
Если есть возможность не уходить от чистого С/C++, то лучше от него не уходить. Чтобы не возникало потом таких тем как эта. Вот что заставило автора того кода ассемблерную функцию применить? Компилятор не знал команду smmul или программист не смог ему объяснить что он хочет? Я ставлю на второе.
scifi
Цитата(VladislavS @ Aug 30 2017, 14:34) *
Компилятор не знал команду smmul или программист не смог ему объяснить что он хочет? Я ставлю на второе.

"Не смог объяснить компилятору" - тоже неоднозначная вещь. Если семантика языка Си не даёт компилятору применить оптимальные инструкции, тогда согласен, лучше допилить исходный код. А если компилятор туповат, и для одинаковых по семантике исходников может генерить как более, так и менее оптимальный код, то ловить вариант, который ему более по душе, - сомнительное занятие.
VladislavS
Не надо ничего ловить. Надо правильно применять язык. Ну что далеко ходить, пример из этой темы. Надо было перемножить два 32-битных числа, получить 64 битный результат и взять старшие 32 бита. Автор тут же рождает макрос, который перемножает два 64-битных числа. Тут же его последователь на другом компиляторе получает "АдЪ". Хотя, компилятор сделал в точности то что его просили и вовсе он не туповат. Сомневаюсь, что сейчас можно найти туповатый компилятор для ARM. Не так уж много способов записать правильно требуемый алгоритм. Сделайте это, а с остальным компилятор разберётся.

ЗЫ: Я не отрицаю, что есть такие вещи, которые чистым С не описать. Но их ещё поискать надо и чаще всего можно интринсиками обойтись без применения ассемблера в чистом виде.
jcxz
Цитата(VladislavS @ Aug 30 2017, 20:58) *
Сделайте это, а с остальным компилятор разберётся.
ЗЫ: Я не отрицаю, что есть такие вещи, которые чистым С не описать. Но их ещё поискать надо и чаще всего можно интринсиками обойтись без применения ассемблера в чистом виде.

Искать далеко не надо. Попробуйте-ка к примеру определить номер самого старшего "1"-ного бита в переменной на чистом си. Каков будет размер кода? Сумеете объяснить компилятору на ARM, что можно обойтись одной CLZ?
А такая операция, мне например, частенько бывает нужна.

Аналогично - инструкции с "насыщением".
VladislavS
Могли бы показать пример кода, где вы CLZ применяете?
Правда, интересно глянуть.
jcxz
Цитата(VladislavS @ Aug 31 2017, 06:58) *
Могли бы показать пример кода, где вы CLZ применяете?
Правда, интересно глянуть.

А какой смысл? Вы считаете, что сможете вот так прям показать как обойтись без CLZ? biggrin.gif
Видимо разработчики 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) {
...
VladislavS
Цитата(jcxz @ Aug 31 2017, 09:12) *
А какой смысл? Вы считаете, что сможете вот так прям показать как обойтись без CLZ? biggrin.gif

Нет, мне реально интересно где и как вы используете CLZ. Спасибо за примеры.

И всё же, я считаю, что надо по максимуму использовать возможности языка для переносимости. Затем интринсики и в самых тяжёлых случаях асм.

UPD: В случае с CLZ с переносимостью как раз полный порядок. Она есть практически во всех процессорах, за исключением всяких 8-биток.
jcxz
Цитата(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 видимо не догадывался заменить одной командой.
KRS
Так у IAR теперь есть GCC inline assembler
так что можно и inline функции на асме писать.
Obam
Цитата(KRS @ Aug 31 2017, 15:15) *
Так у IAR теперь есть GCC inline assembler
так что можно и inline функции на асме писать.

IAR с GNU Compiler Collection?
KRS
Цитата(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
Obam
Да, но не GCC.
KRS
Цитата(Obam @ Sep 4 2017, 10:36) *
Да, но не GCC.

В доке на IAR теперь указано
"The syntax of an inline assembler statement is (similar to the one used by GNU gcc):"
Но и в самой документации, довольно неплохо описано как им пользоваться.

Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.