Instrucciones con Inmediatos Enormes

A la hora de hacer cálculos con inmediatos nos topamos con una limitación. Mientras que x86 tiene instrucciones de tamaño variable, que permiten introducir datos extremadamente largos, las micro-operaciones estilo RISC a las que son transformados tienen un tamaño fijo. En estas instrucciones más simples los inmediatos son almacenados en la sección que normalmente guardaría el puntero a un registro, que contiene muchos menos bits.
Así por ejemplo, este programa tiene un problema, y es que estamos tratando de dar a una variable un valor que ni siquiera cabe dentro del espacio asignado en el procesador.

1
2
3
int main() {
    return 0xCAFEBABE;
}

Si compilamos a una arquitectura CISC podemos ver que en principio no hay problema, sus instrucciones soportan estos datos.

gcc -O2 -c test_max.c -o test_max_x86.o
1
2
3
0000000000000000 <main>:
   0:   b8 be ba fe ca          mov    $0xcafebabe,%eax
   5:   c3                      ret

Pero recordemos que internamente estas instrucciones complejas se desmenuzan en otras estilo RISC. Podemos ver las instrucciones reales si compilamos para arm.

aarch64-linux-gnu-gcc -O2 -c test_max.c -o test_max_arm.o
1
2
3
4
0000000000000000 <main>:
   0:   529757c0    mov w0, #0xbabe
   4:   72b95fc0    movk    w0, #0xcafe, lsl #16
   8:   d65f03c0    ret

ARM, con su tamaño limitado para inmediatos, tiene que escribir primero la primera mitad del número y luego la segunda. x86 por su parte usa la instrucción completa, que después será dividida en varias a la hora de ser ejecutada.