gcc встроенные входные и выходные параметры asm для массивов? - PullRequest
1 голос
/ 20 октября 2019

Насколько я понимаю, при написании встроенного asm в стиле gcc, вы должны быть очень специфичными и точными во всех входных и выходных параметрах (и клобберах), чтобы компилятор знал в точности как назначить регистры для вашего кода и что он может предположить относительно значений этих регистров и любой памяти, которую код asm может считывать и / или изменять. Компилятор использует эту информацию для максимально возможной оптимизации окружающего кода (и даже полностью удаляет встроенный asm, если решает, что он ни на что не влияет). Неспособность быть достаточно конкретным по этому поводу может привести к неправильному поведению, потому что компилятор делает предположения на основе вашей неверной спецификации.

Мне немного неясно, как именно я должен указать, что мой асм читает и пишет, когда онприходит к массивам. Если я не сообщу компилятору, что я читаю и / или пишу весь массив, он может сделать неверные предположения и оптимизировать код таким образом, что это приведет к некорректному поведению.

Предположим, чтоУ меня есть два unsigned int массива размером N, скажем, array1 и array2, и мой ассемблерный код читает оба массива и записывает новые данные в array1. Это правильный способ сообщить компилятору об этом?

asm("some asm here using %[array1] and %[array2]"
    : "+m"(*(unsigned(*)[N])array1)
    : [array1]"r"(array1), [array2]"r"(array2),
      "m"(*(unsigned(*)[N])array1),
      "m"(*(unsigned(*)[N])array2)
    : /* possible clobbers, like "cc" */);

Это, по крайней мере, заставляет мой текущий код работать, но я не на 100% уверен, что именно так и должно быть. (Назначает ли компилятор регистры входным и выходным параметрам только в том случае, если эти параметры действительно используются в строке кода asm? Другими словами, те дополнительные входы и выходы, которые существуют исключительно для того, чтобы сообщить компилятору, что мы читаем и записываем их полностью, не будутзаставить компилятор без необходимости выделять регистры или что-то для них?) * В собственной документации

gcc упоминается синтаксис для выходного массива, но, похоже, ничего не говорится о входных массивах, поэтому я просто делаюдикое предположение здесь.

1 Ответ

1 голос
/ 20 октября 2019

Да, выглядит мне правильно , за исключением того, что "+m" делает ввод "m" для того же массива избыточным. Используйте "+m" для массива только для чтения и записи и "m" для массива только для чтения. Но с тем же преобразованием в массив, что и вы.

Отдельные входные и "=m" операнды могут теоретически сказать компилятору, что он может использовать ваш asm в качестве функции копирования и обработки (так чтоне делайте этого, если это не правда, что вы используете разные указатели для чтения ввода и записи вывода). Хотя в отличие от скаляра, я не думаю, что GCC изобрел бы новую копию массива. Но "+m" означает изменение на месте, чтобы компилятор не имел этой опции.


См. Как я могу указать, что память * указана * встроенным ASMаргумент может быть использован? (этот вопрос является почти дубликатом этого). Он показывает пример массива ввода с использованием произвольной длины *(const char (*)[]) input. На практике [N], кажется, игнорируется (рассматривается как неограниченный), если это не константа времени компиляции, подразумевая, что весь объект может быть доступен. например, gcc не оптимизирует и не переупорядочивает хранилище до arr[N+1] вокруг оператора asm, используя (int (*)[N]), если только N не является константой времени компиляции.

Также обратите внимание, что если ваш ввод действительно C массив , а не просто указатель, вам не нужно никакого приведения. int arr[1024] как "m"(arr) вход означает означает весь массив и не затухает до указателя в памяти.


(Компилятор назначает регистрывводить и выводить параметры только в том случае, если эти параметры действительно используются в строке кода asm?

Нет, распределение регистров для операндов не зависит от того, заполнены ли они в шаблоне или нет.

GCC не нужно различать вход "a", который можно использовать как %%rax (вместо %0), против входа "r", где шаблон должен использовать %0 или%[name] потому что он не знает, что может выбрать компилятор.


В собственной документации gcc упоминается синтаксис для выходного массива, но, похоже, ничего не говорится о вводемассивы, так что я просто делаю дикое предположение здесь.

Это идентично, и да, это необходимо.

Без надлежащих фиктивных входов для покрытия ваших массивов (или "memory"clobber), устранение или изменение порядка храненияруды с вашим asm утверждением возможно. (Например, foo[2] = 1; asm(); foo[2] = 3; может переместить первый магазин позже, или 2-й магазин раньше, и сделать только один.

...