GCC Inline Assembly Multiplication

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

Итак, здесь я хочу умножить два беззнаковых 8-битных целых числа и посмотреть, не выйдет ли результат за край. Обычно я просто загружаю первый операнд в регистр AL, а другой операнд в регистр BL, а затем использую инструкцию mul . Результат сохраняется как 16-битное значение в регистре AX. Затем я копирую значение из регистра AX в свою переменную C b , если оно не переполнится. Если он переполняется, я устанавливаю c на 1.

 uint8_t a = 10;
 uint8_t b = 25;
 uint8_t c = 0; // carry flag

 __asm__
 (
  "clc;"                  // Clear carry flag
  "movb %3, %%al;"        // Load b into %al
  "movb %2, %%bl;"        // Load a into %bl 
  "mul %%bl;"             // Multiply a * b (result is stored in %ax)
  "movw %%ax, %0;"        // Load result into b
  "jnc out;"              // Jump to 'out' if the carry flag is not set
  "movb $1, %1;"          // Set 'c' to 1 to indicate an overflow
  "out:"
  :"=m"(b), "=m"(c)       // Output list
  :"ir"(a), "m"(b)        // Input list
  :"%al", "%bl"           // Clobbered registers (not sure about this)
 );

Кажется, это работает нормально. Если я printf значение 'b' я получаю 250, что правильно. Кроме того, если я изменю начальное значение 'b' на 26, то после умножения c будет установлено в 1, что, конечно, указывает на переполнение из-за (10 * 26> ~ uint8_t (0)). Проблема, которую я вижу, заключается в том, что для переменной C a установлено значение 0 после умножения (или 1 при переполнении). Я не понимаю, почему a вообще может быть изменено тем, что я здесь делаю. Этого даже нет в списке выходных переменных, так почему моя процедура сборки влияет на значение a ?

Кроме того, я не уверен в списке затертых регистров. Этот список должен информировать GCC о любых регистрах, которые использовались во время процедуры сборки, чтобы GCC не пытался использовать их неправильно. Я думаю, мне нужно сообщить GCC, что я использовал регистры AL и BL, но как насчет регистра AX? Он используется неявно для хранения произведения двух 8-битных целых чисел, поэтому нужно ли мне включать его в список затираемых регистров?

5
задан Channel72 7 October 2010 в 17:04
поделиться