División rápida en GCC / ARM

Por lo que sé, la mayoría de los compiladores harán una división rápida multiplicando y luego desplazando los bits a la derecha. Por ejemplo, si marcaeste hilo SO dice que cuando le pida al compilador de Microsoft que haga la división por 10, multiplicará el dividendo por 0x1999999A (que es 2 ^ 32/10) y luego dividirá el resultado por 2 ^ 32 (utilizando 32 turnos a la derecha).

Hasta ahora tan bueno.

Sin embargo, una vez que probé la misma división por 10 en ARM usando GCC, el compilador hizo algo ligeramente diferente. Primero multiplicó el dividendo por 0x66666667 (2 ^ 34/10), luego dividió el resultado por 2 ^ 34. Hasta ahora es lo mismo que Microsoft, excepto que se usa un multiplicador más alto. Después de eso, sin embargo, se restó (dividendo / 2 ^ 31) del resultado.

Mi pregunta: ¿por qué en la versión ARM hay esa resta extra? ¿Me puede dar un ejemplo numérico donde sin esa resta el resultado sea incorrecto?

Si quieres verificar el código generado, está abajo (con mis comentarios):

        ldr     r2, [r7, #4] @--this loads the dividend from memory into r2
        movw    r3, #:lower16:1717986919 @--moves the lower 16 bits of the constant 
        movt    r3, #:upper16:1717986919 @--moves the upper 16 bits of the constant
        smull   r1, r3, r3, r2 @--multiply long, put lower 32 bits in r1, higher 32 in r3
        asr     r1, r3, #2 @--r3>>2, then store in r1 (effectively >>34, since r3 was higher 32 bits of multiplication)
        asr     r3, r2, #31 @--dividend>>31, then store in r3
        rsb     r3, r3, r1 @--r1 - r3, store in r3
        str     r3, [r7, #0] @--this stores the result in memory (from r3) 

Respuestas a la pregunta(2)

Su respuesta a la pregunta