Вы не можете безопасно использовать глобальные переменные в Basi c Asm заявления либо ; случается, что работает с отключенной оптимизацией, но это небезопасно, и вы злоупотребляете синтаксисом.
Существует очень мало причин для когда-либо использования Basi c Asm. Даже для контроля состояния машины, такого как asm("cli")
, для отключения прерываний, вы часто хотели бы, чтобы "memory"
клоббер упорядочивал его относительно. загружает / сохраняет в глобалы. На самом деле, страница G CC https://gcc.gnu.org/wiki/ConvertBasicAsmToExtended рекомендует никогда не использовать Basi c Asm , поскольку она отличается в разных компиляторах, и G CC может измениться на ее обработку как забивая все вместо ничего (из-за существующего глючного кода, который делает неправильные предположения). Это сделает оператор Basi c Asm, который использует push
/ pop
, еще более неэффективным, если компилятор также генерирует хранилища и перезагружает его.
По сути, это единственный вариант использования для Basi c Asm пишет тело функции __attribute__((naked))
, в которой ввод / вывод данных / взаимодействие с другим кодом следует соглашению о вызовах ABI, а не любому пользовательскому соглашению, которое описывают ограничения / сгустки для действительно встроенного блока кода.
Конструкция встроенного ассемблера GNU C заключается в том, что это текст, который вы вводите в обычный вывод ассемблера компилятора (который затем подается на ассемблер, as
). Расширенная asm делает строку шаблоном, в который она может подставить операнды. И ограничения описывают, как asm вписывается в поток данных программы logi c, а также регистрирует ее клобберы.
Вместо синтаксического анализа строки существует синтаксис, который необходимо использовать для описания именно то, что он делает. Разбор шаблона для имен var мог бы решить только часть проблемы языкового дизайна, которую должны решить операнды, и усложнил бы код компилятора. (Нужно было бы знать больше о каждой инструкции, чтобы знать, была ли разрешена память, регистр или немедленный запрос, и тому подобное. Обычно его файлам описания машины нужно только знать, как go перейти от логической операции к asm, а не в другом направлении.)
Ваш asm-блок Basi c поврежден, поскольку вы изменяете переменные C, не сообщая об этом компилятору . Это может сломаться с включенной оптимизацией (возможно, только с более сложным окружающим кодом, но работа не совсем то же самое, что на самом деле безопасно. Вот почему просто тестирование встроенного асм-кода GNU C даже близко не достаточно для того, чтобы он будущее против новых компиляторов и изменений в окружающем коде). Не существует неявного "memory"
клоббера. (Basi c asm такой же, как Extended asm, за исключением того, что не выполняется подстановка %
в строковом литерале. Поэтому вам не нужно %%
, чтобы получить литерал %
в выводе asm. Это неявно изменчиво, как Расширенный asm без выходов.)
Также обратите внимание, что если вы нацелены на i386 MacOS, вам потребуется _result
в asm. result
работает только потому, что имя символа asm точно совпадает с именем переменной C. Использование расширенных ограничений asm сделает его переносимым между GNU / Linux (без подчеркивания в начале) по сравнению с другими платформами, которые используют ведущие _
.
Ваш расширенный asm не работает, потому что вы изменяете вход ("c"
) (без указания компилятору, что регистр также является выходом, например, операндом вывода, использующим тот же регистр). Это также неэффективно: если mov
является первой или последней инструкцией вашего шаблона, вы почти всегда делаете это неправильно и должны были использовать лучшие ограничения.
Вместо этого вы можете сделать:
asm ("imull %%edx, %%ecx\n\t"
: "=c"(result)
: "d"(data1), "c"(data2));
Или, лучше, используйте операнды "+r"(data2)
и "r"(data1)
, чтобы дать компилятору свободный выбор при размещении регистров, вместо того, чтобы потенциально заставить компилятор выдавать ненужные mov
инструкции. (См. Ответ @ Eri c с использованием именованных операндов и "=r"
и соответствующего ограничения "0"
; это эквивалентно "+r"
, но позволяет использовать разные имена C для ввода и вывода. )
Посмотрите на вывод asm компилятора, чтобы увидеть, как происходит генерация кода вокруг вашего оператора asm, если вы хотите убедиться, что он эффективен.
Поскольку локальные переменные не имеют символа / метки в тексте asm (вместо этого они живут в регистрах или с некоторым смещением от указателя стека или фрейма, то есть автоматического c хранилища), использование символа не может работать имена для них в asm.
Даже для глобальных переменных вы хотите , чтобы компилятор мог максимально оптимизировать работу со встроенным ассемблером, поэтому вы хотите дать компилятору возможность использования копии глобальной переменной, которая уже есть в регистре, вместо получения значения в памяти в syn c с хранилищем, просто чтобы ваш ассемблер мог перезагрузить его.
Попытка компилятора проанализировать ваш asm и выяснить, какие C локальные имена переменных являются входными и выходными. (Но это было бы осложнением.)
Но если вы хотите, чтобы это было эффективно, вам нужно выяснить, когда x
в asm может быть регистром, подобным EAX, вместо того, чтобы делать что-то braindead как всегда хранить x
в памяти перед оператором asm, а затем заменять x
на 8(%rsp)
или что-то еще. Если вы хотите дать оператору asm контроль над тем, где могут находиться входные данные, вам понадобятся ограничения в некоторой форме. Выполнение этого для каждого операнда имеет смысл, и означает, что обработка inline-asm не Необходимо знать, что bts
может принимать непосредственный или регистрируемый источник, но не память, для и других машинно-специфических деталей c, подобных этому. (Помните, G CC - переносимый компилятор; запоминание огромного количества информации о машине в парсер inline-asm было бы плохо.)
(MSV C заставляет все C переменные в _asm{}
блокирует память. Невозможно эффективно использовать обернуть одну инструкцию, потому что ввод должен отскочить через память, даже если вы обернули ее в функцию, чтобы вы могли использовать официально поддерживаемый хак оставить значение в EAX и опустить конец непустой функции. В чем разница между 'asm', '__asm' и '__asm __'? И на практике MSV C s реализация была, по-видимому, довольно хрупкой и трудной в обслуживании, настолько, что они удалили ее для x86-64, и она была задокументирована как не поддерживаемая в функции с аргументами регистров даже в 32-битном режиме! Это не ошибка синтаксического дизайна, хотя, только фактическая реализация.)
Clang поддерживает -fasm-blocks
для синтаксиса в стиле _asm { ... }
MSV C, где он анализирует asm и вы используете C var name. Вероятно, он вводит входы и выходы в память, но я не проверял.
Также обратите внимание, что встроенный синтаксис asm с ограничениями G CC разработан вокруг той же системы ограничений, что G CC - внутренние файлы описания машин, используемые для описания ISA для компилятора . (Файлы .md
в источнике G CC, которые сообщают компилятору об инструкции по добавлению чисел, которые принимают входные данные в регистры "r"
и содержат текстовую строку для мнемони c. Обратите внимание на "r"
и "m"
в некоторых примерах в https://gcc.gnu.org/onlinedocs/gccint/RTL-Template.html).
Проектная модель asm
в GNU C заключается в том, что это черный ящик для оптимизатора; Вы должны полностью описать эффекты кода (для оптимизатора), используя ограничения. Если вы закроете регистр, вы должны сообщить компилятору. Если у вас есть входной операнд, который вы хотите уничтожить, вам нужно использовать фиктивный выходной операнд с соответствующим ограничением или операнд "+r"
для обновления значения соответствующей переменной C.
Если вы читаете или записать память, указанную регистром ввода, вы должны сообщить компилятору. Как я могу указать, что можно использовать память, * указанную * встроенным аргументом ASM?
Если вы используете стек, вы должны сообщить компилятору (но вы не можете, поэтому вместо этого вы должны избегать наступления на красную зону: / Использование базового регистра указателя в C ++ inline asm ) См. Также тег встраиваемой сборки вики
G CC. Конструкция позволяет компилятору вводить данные в регистр и использовать то же самое. зарегистрироваться для другого выхода. (Используйте ограничение Early-Clobber, если оно не в порядке; синтаксис G CC разработан для эффективного переноса одной инструкции, которая читает все ее входные данные перед записью любого из ее выходных данных .)
Если бы G CC мог вывести все эти вещи только из C имен var, появляющихся в источнике asm, я не думаю, что уровень контроля был бы возможен . (По крайней мере, неправдоподобно.) И повсюду, вероятно, будут неожиданные эффекты, не говоря уже о пропущенных оптимизациях. Вы всегда используете встроенный asm только тогда, когда хотите получить максимальный контроль над вещами, поэтому последнее, что вам нужно, это компилятор, использующий множество сложных непрозрачных логик c, чтобы выяснить, что делать.
( Встроенный asm достаточно сложен в своем текущем дизайне и мало используется по сравнению с обычным C, поэтому проект, который требует очень сложной поддержки компилятора, вероятно, приведет к большому количеству ошибок компилятора.)
GNU C встроенный ассемблер не предназначен для низкоэффективной работы с минимальными усилиями. Если вы хотите легко, просто напишите на чистом C или используйте встроенные функции и позвольте компилятору выполнить свою работу. (И файл сообщает об ошибке при пропущенной оптимизации, если он делает неоптимальный код.)