c++ c gcc constraints inline-assembly

c++ - Montaje en línea GCC: restricciones



constraints inline-assembly (1)

Tengo dificultades para comprender el papel que juegan las restricciones en el ensamblado en línea de GCC (x86). He leído el manual , que explica exactamente lo que hace cada restricción. El problema es que aunque entiendo lo que hace cada restricción, tengo muy poca comprensión de por qué usaría una restricción sobre otra, o cuáles podrían ser las implicaciones.

Me doy cuenta de que este es un tema muy amplio, por lo que un pequeño ejemplo debería ayudar a limitar el enfoque. La siguiente es una rutina simple de asm que solo agrega dos números. Si se produce un desbordamiento de enteros, escribe un valor de 1 en una variable C de salida.

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 );

Ahora esto funciona bien, excepto que tuve que jugar arbitrariamente con las restricciones hasta que funcionara correctamente. Originalmente, usé las siguientes restricciones:

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

Tenga en cuenta que en lugar de un "0", uso una restricción "m" para b . Esto tuvo un efecto secundario extraño: si compilaba con indicadores de optimización y llamaba la función dos veces, por alguna razón, el resultado de la operación de adición también se almacenaría en c . Finalmente, leí sobre " restricciones de coincidencia ", lo que le permite especificar que una variable se utilizará como operando de entrada y salida. Cuando cambié "m"(b) a "0"(b) funcionó.

Pero realmente no entiendo por qué usaría una restricción sobre otra. Quiero decir, sí, entiendo que "r" significa que la variable debe estar en un registro y "m" significa que debe estar en la memoria, pero realmente no entiendo cuáles son las implicaciones de elegir una sobre otra, o por qué la adición La operación no funciona correctamente si elijo una cierta combinación de restricciones.

Preguntas: 1) En el código de ejemplo anterior, ¿por qué la restricción "m" en b causa que se escriba? 2) ¿Existe algún tutorial o recurso en línea que explique con más detalle las restricciones?


Aquí hay un ejemplo para ilustrar mejor por qué debería elegir las restricciones cuidadosamente (la misma función que la suya, pero quizás escrita un poco más sucintamente):

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; }

Por lo tanto, las restricciones utilizadas fueron: q , r y g .

  • q significa que solo se pueden seleccionar eax , ecx , edx o ebx . Esto se debe a que las instrucciones del set* deben escribir en un registro direccionable de 8 bits ( al , ah , ...). El uso de b en los medios %b0 , usa la porción más baja de 8 bits ( al , cl , ...).
  • Para la mayoría de las instrucciones de dos operandos, al menos uno de los operandos debe ser un registro. Así que no uses m o g para ambos; usa r para al menos uno de los operandos.
  • Para el operando final, no importa si se trata de un registro o de una memoria, entonces use g (general).

En el ejemplo anterior, elegí usar g (en lugar de r ) para a porque las referencias generalmente se implementan como punteros de memoria, por lo que usar una restricción r hubiera requerido copiar el referente a un registro primero, y luego copiarlo. Usando g , el referente podría ser actualizado directamente.

En cuanto a por qué su versión original sobrescribió su c con el valor de la suma, eso se debe a que especificó =m en la ranura de salida, en lugar de (digamos) +m ; eso significa que el compilador tiene permitido reutilizar la misma ubicación de memoria para entrada y salida.

En su caso, eso significa dos resultados (ya que se utilizó la misma ubicación de memoria para c ):

  • La adición no se desbordó: entonces, c se sobrescribió con el valor de b (el resultado de la adición).
  • La adición se desbordó: entonces, c convirtió en 1 (y b podría volverse 1, dependiendo de cómo se generó el código).