Jump to content

    

IAR ARM ассемблер

Здравствуйте. В проекте функция реализована как ассемблерная процедура. Состоит из одной инструкции и хотелось бы преобразовать её в макрос, чтобы не тратить такты на вход и выход.

Реализована в *.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-файла?

Share this post


Link to post
Share on other sites

Не занимайтесь ерундой. Умножать с нужной разрядностью - дело компилятора.

 

  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]

Share this post


Link to post
Share on other sites

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 не получится.

 

Share this post


Link to post
Share on other sites

Из любопытства попробовал на 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]

Share this post


Link to post
Share on other sites
#define MULSHIFT32(arg1, arg2)    ((((long long)arg1)*((long long)arg2))>>32)

Если Вам нужны только старшие 32 бита (как следует из этого макроса), то всё просто:

#define MULSHIFT32(x, y) __SMMUL(x, y)

Share this post


Link to post
Share on other sites

Во-первых, очень некрасиво не показывать типы используемых переменных и желаемого результата.

Во-вторых, разберитесь с размерностью вычислений. Написанный вами макрос делает совсем не то что вы хотите.

В-третьих, ваш 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 можете ставить что угодно, только с соблюдением типов.

Share this post


Link to post
Share on other sites
Поверьте, компилятор сделает всё лучше вас, особенно с оптимизацией. Только не мешайте ему.

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

Share this post


Link to post
Share on other sites

Кстати, если в моём эксперименте с GCC сделать так

#define MULSHIFT32(a, b)  (((long long)(a) * (b)) >> 32)

то код генерится вменяемый.

Очевидно, в первоначальном варианте макроса не хватает скобок вокруг аргументов. На первый взгляд, в данном случае неважно, но, возможно, я что-то не заметил.

Share this post


Link to post
Share on other sites

jcxz, всё верно, чужие библиотеки. Портирую mp3-декодер Helix. В очередной раз)

 

Если Вам нужны только старшие 32 бита (как следует из этого макроса), то всё просто:

#define MULSHIFT32(x, y) __SMMUL(x, y)

Спасибо. Даже забыл, что в IAR есть intrinsic-ассемблер, как всё просто оказывается.

Дарю

#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%.

post-40764-1503999354_thumb.png

 

Дизассемблер для каждой из реализаций. Алгоритм раскладывается в одни и те же инструкции, но с использованием разных РОН.

        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

И хотя первоначальный вопрос про синтаксис макросов остался открытым, но задача решена другими средствами. Спасибо за помощь!

Share this post


Link to post
Share on other sites
И хотя первоначальный вопрос про синтаксис макросов остался открытым, но задача решена другими средствами. Спасибо за помощь!

Имеется в виду вот этот вопрос?

Кто подскажет, как этот макрос правильно оформить, чтобы можно было использовать его вне *.s-файла?

Тогда ответ такой: никак. Это макросы ассемблера, фактически текстовая подстановка в пределах ассемблерного файла. Ни разу не видел, чтобы такие вещи действовали в сишных файлах. И уж точно на уровне линкера ничего этого быть не может.

Share this post


Link to post
Share on other sites
jcxz, всё верно, чужие библиотеки. Портирую mp3-декодер Helix. В очередной раз)

Ну вот я так и думал, что Helix ;)

И макрос этот я запостил как раз из своего работающего проекта на Helix :rolleyes:

Что ваяете, если не секрет?

Share this post


Link to post
Share on other sites
Это макросы ассемблера, фактически текстовая подстановка в пределах ассемблерного файла.

Вот оно что. Спасибо. С ARM-ассемблером пару раз в жизни сталкивался, не знал про эти тонкости.

jcxz, в двух словах - интерактивная игрушка для детей, ничего серьёзного. Кодек этот давно ещё портировал на cortex M3 (не без помощи здешних форумчан). Теперь перевожу на cortex M4, ну и решил по-максимуму из него выжать и закрыть тему навсегда.

Share this post


Link to post
Share on other sites
Дарю

#define MULSHIFT32(arg1, arg2)    (((int64_t)(int32_t)(arg1)*(int32_t)(arg2))>>32)

__SMMUL() может быть чуть-чуть лучше если нужно ещё и округление результата: __SMMULR().

Правда без возможности предварительного сдвига результата влево на 1 бит, это округление редко бывает полезным. :(

Не знаю почему в системе команд не предусмотрена опция сдвига влево результата на 1 бит.

Share this post


Link to post
Share on other sites

Если есть возможность не уходить от чистого С/C++, то лучше от него не уходить. Чтобы не возникало потом таких тем как эта. Вот что заставило автора того кода ассемблерную функцию применить? Компилятор не знал команду smmul или программист не смог ему объяснить что он хочет? Я ставлю на второе.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this