Задача оптимизации встроенного ассемблера ARM gcc - PullRequest
1 голос
/ 29 июня 2011

Почему моя подпрограмма встроенного ассемблера не работает, когда у меня есть флаг оптимизации -O3, но он работает с другими флагами оптимизации (-O0, -O1, -O2, -Os)?

Я дажедобавил изменчивость ко всем моим инструкциям на ассемблере, которые, я думал, скажут компилятору ничего не трогать и не переупорядочивать?

С наилучшими пожеланиями

Мистер Гигу

Ответы [ 2 ]

6 голосов
/ 30 июня 2011

Встроенный ассемблер GCC очень чувствителен к правильной спецификации.

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

Следующие два:

    int myasmfunc(int arg)    /* definitely buggy ... */
    {
        register int myval asm("r2") = arg;

        asm ("add r1, r0, #22\n" ::: "r1");
        asm ("adds r0, r1, r0\n" ::: "r0", "cc");
        asm ("subeq r2, #123\n" ::: "r2");
        asm ("subne r2, #213\n" ::: "r2");
        return myval;
    }

и

    int myasmfunc(int arg)
    {
        int myval = arg, plus = arg;

        asm ("add %0, #22\n\t" : "+r"(plus));
        asm ("adds %1, %2\n\t"
             "subeq %0, #123\n\t"
             "subne %0, #213\n\t" : "+r"(myval), "+r"(plus) : "r"(arg) : "cc");
        return myval;
    }

может показаться похожим на первый взгляд, и вы наивно предполагаете, что они делают то же самое; но они очень далеки от этого!

Есть несколько проблем с первой версией этого кода.

  1. Например, если вы укажете его как отдельные операторы asm(), компилятор может свободно вставлять произвольный код между ними. Это, в частности, означает, что инструкции sub, даже если они сами не изменяют коды условий, могут привести к тому, что компилятор выберет для вставки то, что сделал.
  2. Во-вторых, опять же из-за разделения инструкций при указании отдельных операторов asm(), нет гарантии, что генератор кода выберет один и тот же регистр для установки myval в обоих случаях, спецификация asm("r2") в объявлении переменной несмотря на это.
  3. В-третьих, сделанное в первом предположении, что r0 содержит аргумент функции, неверно; компилятор к тому времени, когда он доберется до блока сборки, мог бы выбрать перемещение этого аргумента в любое другое место. Хуже того, с тех пор у вас снова есть оператор split, и не дается никаких гарантий относительно того, что происходит между two asm(). Даже если вы укажете __asm__ __volatile__(...);, компилятор обрабатывает два таких блоков как независимые объекты.
  4. В-четвертых, вы не говорите компилятору, что вы дурачите / присваиваете myval. Возможно, он решил временно переместить его в другое место, потому что вы ударили «r2» и, вернувшись, решите восстановить его из ... (???).

Просто для удовольствия, вот вывод первой функции для следующих четырех случаев:

  1. по умолчанию - gcc -c tst.c
  2. оптимизировано - gcc -O8 -c tst.c
  3. с использованием некоторых необычных опций - gcc -c -finstrument-functions tst.c
  4. оптимизация плюс - gcc -c -O8 -finstrument-functions tst.c
Disassembly of section .text:

00000000 :
   0:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
   4:   e28db000    add fp, sp, #0  ; 0x0
   8:   e24dd00c    sub sp, sp, #12 ; 0xc
   c:   e50b0008    str r0, [fp, #-8]
  10:   e51b2008    ldr r2, [fp, #-8]
  14:   e2811016    add r1, r1, #22 ; 0x16
  18:   e0910000    adds    r0, r1, r0
  1c:   0242207b    subeq   r2, r2, #123    ; 0x7b
  20:   124220d5    subne   r2, r2, #213    ; 0xd5
  24:   e1a03002    mov r3, r2
  28:   e1a00003    mov r0, r3
  2c:   e28bd000    add sp, fp, #0  ; 0x0
  30:   e8bd0800    pop {fp}
  34:   e12fff1e    bx  lr


Disassembly of section .text:

00000000 :
   0:   e1a03000    mov r3, r0
   4:   e2811016    add r1, r1, #22 ; 0x16
   8:   e0910000    adds    r0, r1, r0
   c:   0242207b    subeq   r2, r2, #123    ; 0x7b
  10:   124220d5    subne   r2, r2, #213    ; 0xd5
  14:   e1a00003    mov r0, r3
  18:   e12fff1e    bx  lr


Disassembly of section .text:

00000000 :
   0:   e92d4830    push    {r4, r5, fp, lr}
   4:   e28db00c    add fp, sp, #12 ; 0xc
   8:   e24dd008    sub sp, sp, #8  ; 0x8
   c:   e1a0500e    mov r5, lr
  10:   e50b0010    str r0, [fp, #-16]
  14:   e59f0038    ldr r0, [pc, #56]   ; 54 
  18:   e1a01005    mov r1, r5
  1c:   ebfffffe    bl  0 
  20:   e51b2010    ldr r2, [fp, #-16]
  24:   e2811016    add r1, r1, #22 ; 0x16
  28:   e0910000    adds    r0, r1, r0
  2c:   0242207b    subeq   r2, r2, #123    ; 0x7b
  30:   124220d5    subne   r2, r2, #213    ; 0xd5
  34:   e1a04002    mov r4, r2
  38:   e59f0014    ldr r0, [pc, #20]   ; 54 
  3c:   e1a01005    mov r1, r5
  40:   ebfffffe    bl  0 
  44:   e1a03004    mov r3, r4
  48:   e1a00003    mov r0, r3
  4c:   e24bd00c    sub sp, fp, #12 ; 0xc
  50:   e8bd8830    pop {r4, r5, fp, pc}
  54:   00000000    .word   0x00000000


Disassembly of section .text:

00000000 :
   0:   e92d4070    push    {r4, r5, r6, lr}
   4:   e1a0100e    mov r1, lr
   8:   e1a05000    mov r5, r0
   c:   e59f0028    ldr r0, [pc, #40]   ; 3c 
  10:   e1a0400e    mov r4, lr
  14:   ebfffffe    bl  0 
  18:   e2811016    add r1, r1, #22 ; 0x16
  1c:   e0910000    adds    r0, r1, r0
  20:   0242207b    subeq   r2, r2, #123    ; 0x7b
  24:   124220d5    subne   r2, r2, #213    ; 0xd5
  28:   e59f000c    ldr r0, [pc, #12]   ; 3c 
  2c:   e1a01004    mov r1, r4
  30:   ebfffffe    bl  0 
  34:   e1a00005    mov r0, r5
  38:   e8bd8070    pop {r4, r5, r6, pc}
  3c:   00000000    .word   0x00000000

Как видите, ни то, ни другое не делает то, что вы надеетесь увидеть; вторая версия кода, однако, на gcc -c -O8 ... заканчивается как:

Disassembly of section .text:

00000000 :
   0:   e1a03000    mov r3, r0
   4:   e2833016    add r3, r3, #22 ; 0x16
   8:   e0933000    adds    r3, r3, r0
   c:   0240007b    subeq   r0, r0, #123    ; 0x7b
  10:   124000d5    subne   r0, r0, #213    ; 0xd5
  14:   e12fff1e    bx  lr

и это довольно близко к тому, что вы указали в своей сборке и чего вы ожидаете.

Мораль: будьте явными и точными со своими ограничениями, вашими назначениями операндов и сохраняйте взаимозависимые линии сборки в в одном и том же блоке asm() (сделайте многострочное выражение).

1 голос
/ 29 июня 2011

Это действительно должен быть комментарий, но я по какой-то причине не могу его опубликовать: (

Оптимизация компилятора не должна мешать вашей сборке. Так что, как сказал Игорь, каким образомэто «не работает»? Может быть, ваш ASM разветвляется на функцию, которая была оптимизирована компилятором и дает другой результат, от которого может зависеть ваш код сборки?

некоторый исходный файл или дополнительная информация о компилятореможет быть полезно

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...