Código fuente
void switch_eg(long x, long n, long* dest) { long val = x; switch(n) { case 0: val *= 13; break; case 2: val += 10; case 3: val += 11; break; case 4: case 6: val += 11; break; default: val = 0; } *dest = val; }
Código de ensamblaje correspondiente
.LFB0: // x in %rdi, n in %rsi, *dest in %rdx cmpq $6, %rsi ja .L8 // if n > 6, switch->default ------------------------- leaq .L4(%rip), %rcx movslq (%rcx,%rsi,4), %rax addq %rcx, %rax jmp *%rax ------------------------- .L4: .long .L3-.L4 // case 0 .long .L8-.L4 // case 1 .long .L5-.L4 // case 2 .long .L6-.L4 // case 3 .long .L7-.L4 // case 4 .long .L8-.L4 // case 5 .long .L7-.L4 // case 6 .L3: leaq (%rdi,%rdi,2), %rax // %rax = 3 * val leaq (%rdi,%rax,4), %rdi // val = 4 * 3 * val + val = 13val jmp .L2 // break .L5: addq $10, %rdi // val += 10 .L6: addq $11, %rdi // val += 11 .L2: movq %rdi, (%rdx) // *dest = val ret // return .L7: addq $11, %rdi // val += 11 jmp .L2 // break .L8: movl $0, %edi // val = 0 jmp .L2 // break
¿Quién puede explicar el significado del siguiente código ensamblador?
leaq .L4(%rip), %rcx movslq (%rcx,%rsi,4), %rax addq %rcx, %rax jmp *%rax
En mi opinión,
leaq .L4(%rip), %rcx
%rcx recibe la primera dirección de la matriz de la tabla de salto
movslq (%rcx, %rsi, 4), %rax
%rax recibe el valor del elemento de matriz específico, es decir, la dirección de la operación correspondiente al caso
Pero no sé qué addq %rcx, %rax
aquí...
Quien me puede ayudar... Gracias!!!
La tabla de salto en realidad no está construida para contener direcciones de salto, sino compensaciones. Ver ( .L3-.L4
).
Cuando se obtiene el desplazamiento de la tabla, debe agregarse con la dirección base de la tabla, ubicada en leaq .L4(%rip), %rcx
.
Este esquema hace que el archivo de objeto sea reubicable; y el uso de la carga de extensión de signo permite usar números enteros de 32 bits para decodificar las compensaciones con el fin de ahorrar espacio.
Tenga en cuenta que las direcciones en la tabla de saltos se almacenan como compensaciones relativas a la dirección de la etiqueta L4
, no como direcciones absolutas (por ejemplo .L3-.L4
y no solo .L3
). Esto permite el uso de un valor más corto de 32 bits en lugar de 64 bits, y hace que el código sea independiente de la posición (en ningún momento tiene que haber direcciones absolutas codificadas en las instrucciones, que de otro modo requerirían correcciones al cargar el módulo).
Para hacer el salto final, el código necesita construir la dirección absoluta correcta de la etiqueta relevante (por ejemplo .L3
). Esto se hace en tres pasos:
.L4
en función de su desplazamiento relativo a RIP y almacene el resultado en RCX: leaq .L4(%rip), %rcx
. someLabel(%rip)
es un modo de direccionamiento especial documentado en la parte inferior de esta página de documentos . Esencialmente, se compilará en un código que tenga un desplazamiento independiente de la posición entre la línea de código actual y la etiqueta dada en la instrucción, y el resultado de la instrucción será una dirección absoluta basada en la dirección relativa dada.movslq (%rcx,%rsi,4), %rax
..L4
(en RCX), de modo que obtengamos, por ejemplo .L4+(.L3-.L4)
= .L3
y almacene el resultado en RAX: addq %rcx, %rax
. Luego podemos saltar a la dirección calculada: jmp *%rax
.