Divisão rápida no GCC / ARM

Até onde sei, a maioria dos compiladores faz uma divisão rápida multiplicando e, em seguida, mudando para a direita. Por exemplo, se você verificareste segmento SO Ele diz que quando você pede ao compilador da Microsoft para fazer a divisão por 10, ele irá multiplicar o dividendo por 0x1999999A (que é 2 ^ 32/10) e então dividir o resultado por 2 ^ 32 (usando 32 turnos à direita).

Por enquanto, tudo bem.

Uma vez que testei a mesma divisão por 10 no ARM usando o GCC, o compilador fez algo ligeiramente diferente. Primeiro multiplicou o dividendo por 0x66666667 (2 ^ 34/10), depois dividiu o resultado por 2 ^ 34. Até agora é o mesmo que a Microsoft, exceto usando um multiplicador maior. Depois disso, no entanto, subtraiu (dividendo / 2 ^ 31) do resultado.

Minha pergunta: por que na versão ARM tem essa subtração extra? Você pode me dar um exemplo numérico onde, sem essa subtração, o resultado será errado?

Se você quiser verificar o código gerado, está abaixo (com meus comentários):

        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) 

questionAnswers(2)

yourAnswerToTheQuestion