Встроенный ассемблер 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;
}
может показаться похожим на первый взгляд, и вы наивно предполагаете, что они делают то же самое; но они очень далеки от этого!
Есть несколько проблем с первой версией этого кода.
- Например, если вы укажете его как отдельные операторы
asm()
, компилятор может свободно вставлять произвольный код между ними. Это, в частности, означает, что инструкции sub
, даже если они сами не изменяют коды условий, могут привести к тому, что компилятор выберет для вставки то, что сделал.
- Во-вторых, опять же из-за разделения инструкций при указании отдельных операторов
asm()
, нет гарантии, что генератор кода выберет один и тот же регистр для установки myval
в обоих случаях, спецификация asm("r2")
в объявлении переменной несмотря на это.
- В-третьих, сделанное в первом предположении, что
r0
содержит аргумент функции, неверно; компилятор к тому времени, когда он доберется до блока сборки, мог бы выбрать перемещение этого аргумента в любое другое место. Хуже того, с тех пор у вас снова есть оператор split, и не дается никаких гарантий относительно того, что происходит между two asm()
. Даже если вы укажете __asm__ __volatile__(...);
, компилятор обрабатывает два таких блоков как независимые объекты.
- В-четвертых, вы не говорите компилятору, что вы дурачите / присваиваете
myval
. Возможно, он решил временно переместить его в другое место, потому что вы ударили «r2» и, вернувшись, решите восстановить его из ... (???).
Просто для удовольствия, вот вывод первой функции для следующих четырех случаев:
- по умолчанию -
gcc -c tst.c
- оптимизировано -
gcc -O8 -c tst.c
- с использованием некоторых необычных опций -
gcc -c -finstrument-functions tst.c
- оптимизация плюс -
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()
(сделайте многострочное выражение).