Этикетки в GCC встроенной сборки
В ходе моих экспериментов со встроенной сборкой GCC я столкнулся с новой проблемой, связанной с метками и встроенным кодом.
Рассмотрим следующий простой прыжок:
__asm__
(
"jmp out;"
"out:;"
:
:
);
Это ничего не делает, кроме прыжка к out
этикетка. Как и этот код прекрасно компилируется. Но если вы поместите его в функцию, а затем скомпилируете с флагами оптимизации, компилятор скажет: "Ошибка: символ" out "уже определен".
Кажется, что происходит то, что компилятор повторяет этот ассемблерный код каждый раз, когда он указывает на функцию. Это вызывает метку out
дублироваться, что приводит к множественному out
этикетки.
Итак, как мне обойти это? Неужели нельзя использовать метки во встроенной сборке? В этом руководстве по встроенной сборке GCC упоминается, что:
Таким образом, вы можете поместить свою сборку в макросы CPP и встроенные функции C, так что любой может использовать ее как любую функцию / макрос C. Встроенные функции очень похожи на макросы, но иногда их удобнее использовать. Помните, что во всех этих случаях код будет продублирован, поэтому в этом ассемблерном коде должны быть определены только локальные метки (стиля 1: стиль).
Я пытался найти больше информации об этих "локальных метках", но не могу найти ничего, касающегося встроенной сборки. Похоже, что в учебнике говорится, что локальная метка - это число, за которым следует двоеточие (например, 1:
), поэтому я попытался использовать такую метку. Интересно, что код скомпилирован, но во время выполнения он просто вызвал ошибку сегментации. Хм...
Так какие-нибудь предложения, намеки, ответы...?
2 ответа
Объявление локальной метки - это действительно число, за которым следует двоеточие. Но ссылка на локальный ярлык требует суффикса f
или же b
в зависимости от того, хотите ли вы смотреть вперед или назад - то есть 1f
относится к следующему 1:
метка в прямом направлении.
Так объявив ярлык как 1:
верно; но чтобы сослаться на это, нужно сказать jmp 1f
(потому что в этом случае вы прыгаете вперед).
Ну, этот вопрос не молодеет, но есть два других интересных решения.
1) В этом примере используется%=. %= в шаблоне на ассемблере заменяется числом, которое "уникально для каждого insn во всей компиляции. Это полезно для создания локальных меток, которые упоминаются более одного раза в данном insn". Обратите внимание, что для использования% = у вас (по-видимому) должен быть хотя бы один вход (хотя вам, вероятно, не обязательно его использовать).
int a = 3;
asm (
"test %0\n\t"
"jnz to_here%=\n\t"
"jz to_there%=\n\t"
"to_here%=:\n\t"
"to_there%=:"
::"r" (a));
Это выводит:
test %eax
jnz to_here14
jz to_there14
to_here14:
to_there14:
В качестве альтернативы, вы можете использовать asm goto (я думаю, что он добавлен в v4.5). Это фактически позволяет вам переходить к меткам c, а не просто к asm-меткам:
asm goto ("jmp %l0\n"
: /* no output */
: /* no input */
: /* no clobber */
: gofurther);
printf("Didn't jump\n");
// c label:
gofurther:
printf("Jumped\n");