Clang (LLVM) встроенная сборка - множественные ограничения с бесполезными разливами/перезагрузками

clang/gcc. Некоторые встроенные операнды могут быть удовлетворены несколькими ограничениями, например, "rm", когда операнд может быть удовлетворен регистром или ячейкой памяти. Например, 64 x 64 = 128 бит умножаются:

__asm__ ("mulq %q3" : "=a" (rl), "=d" (rh) : "%0" (x), "rm" (y) : "cc")

Сгенерированный код, как представляется, выбирает ограничение памяти для аргумента 3, что было бы неплохо, если бы мы регистрировали голод, чтобы избежать разлива. Очевидно, что на x86-64 меньше давления на регистре, чем на IA32. Однако сгенерированный фрагмент сборки (clang):

    movq    %rcx, -8(%rbp)
    ## InlineAsm Start
    mulq -8(%rbp)
    ## InlineAsm End

Выбор ограничения памяти явно бессмысленен! Однако изменение ограничения на: "r" (y) (форсирование регистра) получим:

    ## InlineAsm Start
    mulq %rcx
    ## InlineAsm End

как ожидалось. Эти результаты приведены для clang/LLVM 3.2 (текущий выпуск Xcode). Первый вопрос: Почему clang выбирает менее эффективное ограничение в этом случае?

Во-вторых, существует менее широко используемое, разделенное запятыми, множественное альтернативное ограничение:
"r,m" (y), который должен оценивать затраты по каждой альтернативе и выбирать тот, который приводит к меньшему копированию. Это, похоже, работает, но clang просто выбирает первый - о чем свидетельствует: "m,r" (y)


Я мог бы просто отказаться от альтернативных ограничений "m", но это не отражает диапазон возможных законных операндов. Это подводит меня ко второму вопросу: Устранены ли эти проблемы или, по крайней мере, признаны в 3.3?. Я пробовал просматривать архивы LLVM dev, но я бы предпочел получить некоторые ответы, прежде чем излишне ограничивать ограничения дальше, или присоединение к обсуждениям проектов и т.д.

Ответ 1

У меня был ответ на cfe-dev (список разработчиков front clang) от одного из разработчиков:

LLVM в настоящее время всегда разливает ограничения "rm", чтобы упростить обработка встроенного asm в бэкэнд (вы можете спросить на llvmdev, если хотите Детали). Я не знаю никаких планов исправить это в ближайшем будущем.

Итак, это явно "известная" проблема. Одна из целей clang - правильно обработать синтаксис gcc inline assembly, среди других расширений, который он делает в этом случае - просто не очень эффективно. Короче говоря, это не ошибка, сама по себе.


Так как это не ошибка, я продолжу синтаксис ограничения "r,m". Я полагаю, что это лучший компромисс. gcc выберет наилучший - предположительно, регистр, где это возможно - и clang заставит использовать регистр, игнорируя дополнительные параметры после запятой. Если ничего другого, он по-прежнему сохраняет смысловое намерение оператора сборки, то есть описывает возможные ограничения, даже если они игнорируются.


Последнее примечание (20130715): Этот конкретный пример не будет компилироваться с использованием ограничения "r,m" в одной позиции - нам нужно будет предоставить альтернативное соответствие ограничений для каждого, например,

: "=a,a" (rl), "=d,d" (rh) : "%0,0" (x), "r,m" (y)

Это требуется для нескольких альтернативных ограничений с помощью GCC. Но мы попадаем на территорию, где, как известно, GCC обнаруживает ошибки в прошлом - независимо от того, действительно ли это или нет: 4.8.1, я не знаю. Clang работает без альтернатив в других ограничениях, что несовместимо с синтаксисом GCC и поэтому должно считаться ошибкой.

Если производительность критическая, используйте "r", иначе придерживайтесь "rm", и, возможно, clang обратится к ней в будущем, даже если она приносит пользу GCC.