Люди уже указывали (неоднократно) на то, что вам следует избегать встроенного asm, если это возможно. Я согласен со всеми из них.
При этом вы также попросили «по крайней мере, объяснить GCC Inline Assembler."Эта часть может быть спорной, но FWIW:
Начиная с шаблоном на ассемблере:.
"cmpl %3, %2\n\tcmovg %4, %0\n\tcmovle %5, %1"
Есть пару вещей происходит здесь, что может выглядеть странно программист VS
cmpl
- По умолчанию в asm используется gc as & t синтаксис вместо intel.Это приводит к тому, что некоторые вещи имеют тонкую разницу. Например, l
здесь указывает, что сравнение должно применяться к longs (где долго в этом контексте означает 4 тес). Одно из других различий заключается в том, что операнды меняются на противоположные. Таким образом, intel может использовать mov eax, 1
, но att будет использовать movl $1, %eax
(знак доллара означает постоянный,% обозначает регистр). Есть сайты, которые рассказывают обо всех различиях здесь.
\n\t
- gcc выводит код на ассемблер. Чтобы поместить каждую из этих трех команд в свои собственные строки, вы добавляете '\ n' для новой строки и '\ t' для того, чтобы вкладка хорошо отображалась.
- % 0-% 5 - Вы должны подумать о шаблоне ассемблера (строка, содержащая ассемблер), скорее, как строка формата, которую вы отправляете printf. Некоторые части печатаются буквально, а другие детали заменяются (думаю:
printf("2 + 2 = %d\n", 2+2);
). Таким же образом gcc заменит части шаблона следующими параметрами. % 0 - первый параметр, а% 5 - шестой (пронумерованный в порядке их появления).
Это приводит нас к остальной части команды. Элементы, которые приходят после первого двоеточия являются выходными параметрами:
: "+r" (min),
"+r" (max)
Знак «+» здесь означает, что переменные чтения и записи. Если они были только выводятся, вы должны использовать '='. Если вы собираетесь читать, но не изменять, вы должны указать его как входной параметр (т. Е. После следующего двоеточия). «R» указывает, что значения должны быть перемещены в регистры перед выполнением asm. Он оставляет решение о , которое регистрируется компилятором. Программисту не нужно знать; он может использовать% 0 для min
и% 1 для max
, и токен будет заменен соответствующим образом.
Что приводит нас к выходным параметрам. Довольно многое, чего вы ожидаете, кроме того, что 'g' является общим типом ограничения. Теоретически это позволяет сохранять значения в регистре, памяти или в непосредственном значении. В основном это просто регистр.
: "r" (key), "g" (arr [middle]),
"g" (middle + 1), "g" (middle));
Есть страницы документов о инлайн ассемблере GCC (см https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html), которые описывают все это в мучительных подробно. Также есть несколько страниц, на которых обсуждаются ограничения, отличные от «r» и «g» (https://gcc.gnu.org/onlinedocs/gcc/Constraints.html).
Как вы можете себе представить, невероятно легко получить это неправильно. И если вы это сделаете, вы, вероятно, не получите хорошую, понятную ошибку компилятора. Вместо этого произойдет то, что он будет работать, тогда что-то еще дюжина строк позже провалится без видимой причины.
Именно поэтому друзья не говорят друзьям, чтобы они использовали inline assembler.
Эй, вы спросили ...
Короткий ответ: Не делайте этого. Сохраните его на C и дайте компилятору позаботиться об этом. – Jester
Всякий раз, когда я вижу, что кто-то падает на inline asm, мой первый, хотя «тот парень делает это неправильно». Конечно, есть * редкие случаи, когда это имеет смысл, но в 99% случаев нужно просто написать читаемые C или C++ и позволить компилятору разобраться с деталями (обычно это будет лучше работать). –
Привет, Йеспер, хороший момент, однако для критического алгоритма производительности есть хороший пример для оптимизации. Если вы прочитали связанную статью выше, двоичный поиск с CMOV превосходит CMOV в 2 раза. Я просмотрел вывод сборки Visual Studio для простого цикла while и не генерирует инструкцию CMOV, а вместо этого генерирует ветвь. Именно эта непредсказуемая ветвь приводит к увеличению коэффициента производительности при использовании CMOV. –