Встроенная сборка GCC: ограничения - PullRequest
16 голосов
/ 10 октября 2010

У меня возникают трудности с пониманием роли, которую играют ограничения в встроенной сборке GCC (x86). Я прочитал руководство , которое точно объясняет, что делает каждое ограничение. Проблема в том, что, хотя я понимаю, что делает каждое ограничение, я очень мало понимаю, почему вы будете использовать одно ограничение над другим, или каковы могут быть последствия.

Я понимаю, что это очень широкая тема, поэтому небольшой пример должен помочь сузить фокус. Ниже приведена простая процедура asm, которая просто добавляет два числа. Если происходит целочисленное переполнение, он записывает значение 1 в выходную переменную C.

 int32_t a = 10, b = 5;
 int32_t c = 0; // overflow flag

 __asm__
 (
  "addl %2,%3;"        // Do a + b (the result goes into b)
  "jno 0f;"            // Jump ahead if an overflow occurred
  "movl $1, %1;"       // Copy 1 into c
  "0:"                 // We're done.

  :"=r"(b), "=m"(c)    // Output list
  :"r"(a), "0"(b)     // Input list
 );

Теперь это работает нормально, за исключением того, что мне пришлось произвольно возиться с ограничениями, пока я не заставил его работать правильно. Первоначально я использовал следующие ограничения:

  :"=r"(b), "=m"(c)    // Output list
  :"r"(a), "m"(b)     // Input list

Обратите внимание, что вместо "0" я использую ограничение "m" для b. Это имело странный побочный эффект, когда если бы я компилировал с флагами оптимизации и дважды вызывал функцию, по какой-то причине результат операции сложения также сохранялся бы в c. В конце концов я прочитал о « соответствующих ограничениях », которые позволяют вам указать, что переменная должна использоваться как входной и выходной операнд. Когда я сменил "m"(b) на "0"(b), это сработало.

Но я не очень понимаю, почему вы используете одно ограничение над другим. Я имею в виду, да, я понимаю, что «r» означает, что переменная должна быть в регистре, а «m» означает, что она должна быть в памяти - но я не действительно понимаю, что означает выбор одного над другим или почему операция сложения не работает правильно, если я выбрал определенную комбинацию ограничений.

Вопросы: 1) Почему в приведенном выше примере кода ограничение "m" для b привело к записи в c? 2) Есть ли учебник или онлайн-ресурс, в котором более подробно рассматриваются ограничения?

1 Ответ

15 голосов
/ 10 октября 2010

Вот пример, чтобы лучше проиллюстрировать, почему вы должны тщательно выбирать ограничения (та же функция, что и у вас, но, возможно, написана чуть более кратко):

bool add_and_check_overflow(int32_t& a, int32_t b)
{
    bool result;
    __asm__("addl %2, %1; seto %b0"
            : "=q" (result), "+g" (a)
            : "r" (b));
    return result;
}

Итак, использовались следующие ограничения: q,r и g.

  • q означает, что можно выбрать только eax, ecx, edx или ebx.Это связано с тем, что инструкции set* должны записываться в 8-битный адресуемый регистр (al, ah, ...).Использование b в средстве %b0, использование самой младшей 8-битной части (al, cl, ...).
  • Для большинства инструкций с двумя операндами по крайней мере одиниз операндов должен быть регистр.Так что не используйте m или g для обоих;используйте r как минимум для одного из операндов.
  • Для последнего операнда не имеет значения, регистр это или память, поэтому используйте g (общий).

В приведенном выше примере я решил использовать g (вместо r) для a, поскольку ссылки обычно реализуются как указатели памяти, поэтому использование ограничения r потребовало бы копирования референта в регистр.сначала, а потом копировать обратно.Используя g, референт может быть обновлен напрямую.


Почему исходная версия перезаписала ваш c значением дополнения, потому что вы указали =m в слоте вывода, скореечем (скажем) +m;это означает, что компилятору разрешено повторно использовать одну и ту же ячейку памяти для ввода и вывода.

В вашем случае это означает два результата (поскольку одна и та же ячейка памяти использовалась для b и c):

  • Добавление не переполнилось: тогда c было перезаписано значением b (результат добавления).
  • Добавление переполнилось: затем,c стало 1 (и b также может стать 1, в зависимости от того, как был сгенерирован код).
...