He escrito el siguiente código muy simple con el que estoy experimentando en el explorador del compilador de Godbolt:
#include <cstdint> uint64_t func(uint64_t num, uint64_t den) { return num / den; }GCC produce el siguiente resultado, que esperaría:
func(unsigned long, unsigned long): mov rax, rdi xor edx, edx div rsi retSin embargo, Clang 13.0.0 produce lo siguiente, que implica cambios y un salto incluso:
func(unsigned long, unsigned long): # @func(unsigned long, unsigned long) mov rax, rdi mov rcx, rdi or rcx, rsi shr rcx, 32 je .LBB0_1 xor edx, edx div rsi ret .LBB0_1: xor edx, edx div esi retAl usar uint32_t, la salida de clang es una vez más "simple" y lo que esperaría.
Parece que esto podría ser algún tipo de optimización, ya que clang 10.0.1 produce el mismo resultado que GCC, sin embargo, no puedo entender lo que está sucediendo. ¿Por qué clang produce este montaje más largo?
El ensamblado parece estar comprobando si num o den es mayor que 2**32 desplazándose 32 bits hacia la derecha y luego comprobando si el número resultante es 0. Dependiendo de la decisión, una división de 64 bits ( div rsi ) o 32 -Se realiza la división de bits ( div esi ).
Presumiblemente, este código se genera porque el escritor del compilador cree que las comprobaciones adicionales y la ramificación potencial superan los costos de realizar una división innecesaria de 64 bits.
Si lo entiendo correctamente, solo verifica si alguno de los operandos tiene más de 32 bits y usa diferentes div para "hasta" 32 bits y para uno más grande.