LEGv8
LEGv8 es un conjunto de instrucciones. Es una versión simplificada y un subconjunto de ARMv8.
Convención de registros
X0 - X7
: Argumentos de entrada/salida de funciones.X8
: Resultado indirecto de localización de registro.X9 - X15
: Registros temporales.X16 - X17
: Sobreescrito por el sistema como scratch register.X18
: Registro de uso especial del OS o temporalX19 - X27
: Registros de uso general.X28
: Puntero a stack.X29
: Puntero a frame.X30
: Registro de enlace (link register).XZR
: Valor constante0
.
Instrucciones
ADD
: Suma de registrosADDI
: Suma con valor constanteSUB
: Resta de registrosSUBI
: Resta con valor constanteAND
: Operación AND entre registrosANDI
: Operación AND con valor constanteORR
: Operación OR entre registrosEOR
: Operación XOR entre registrosORRI
: Operación OR con valor constanteLSL
: Recibe una constante#c
y recibe un registro para multiplicarlo por2^c
.
Desplaza a la izquierda el registroX1
#c
veces y lo almacena enX2
.LSR
: Recibe una constantec
, desplaza a la derecha el registroX1
#c
veces y lo almacena enX2
.LDUR
: Copia al registroX1
el contenido en la posición de memoria direccionada porX2
sumado a la constante#c
(memoria doubleword)STUR
: Copia el contenido del registroX1
en la posición de memoria direccionada porX2
sumado a la constante#c
(memoria doubleword)LDURSW
: Lo mismo queLDUR
(memoria word)STURSW
: Lo mismo queSTUR
(memoria word)LDURB
: Lo mismo queLDUR
(memoria byte)STURB
: Lo mismo queSTUR
(memoria byte)MOVZ
: Mueve un valor inmediato a un registro (cero extendido)MOVK
: Mueve un valor inmediato a un registro y lo combina con el contenido del registro.
Operaciones
Operaciones inmediatas (valores constantes)
// Ejemplo de código C
f = g + 5;
Sean g => X21
y f => X19
, la instrucción se traduce a:
ADDI X19, X21, #5
Operaciones en registros
Las operaciones en registros se hacen de la siguiente forma
// Ejemplo de código C
f = (g + h) - (i + j);
Solo se puede operar sobre dos registros a la vez, por lo que se requieren valores temporales:
ADD X9, X21, X22 // g + h
ADD X10, X22, X23 // i + j
SUB X19, X9, X10 // f = (g + h) - (i + j)
Operaciones en memoria
Operar en datos almacenados en memoria es más lento que operar en registros, ya que requieren más instrucciones:
// Ejemplo de código C
a[12] = h + a[8];
Sean h => X21
y a => X22
, la instrucción se traduce a:
LDUR X9, [X22, #64]
ADD X9, X21, X9
STUR X9, [X22, #96]
Cada elemento de memoria es de \(8\) bits, para obtener los \(64\) bits necesarios de cada elemento del arreglo a
hay que usar un offset de \(n * 8\) bits.
Ejercicios resueltos - Operaciones
Escribir las secuencias de assembler LEGv8 en base al código C:
1.
// A)
f = g + h + i + j;
// B)
f = g + (h + 5);
// C)
f = (g + h) + (i + j);
Para este ejercicio, se asignan a las variables f, g, h, i, j
los registros X0, X1, X2, X3, X4
, respectivamente:
// A)
ADD X0, X1, X2
ADD X0, X0, X3
ADD X0, X0, X4
// B)
ADD X0, X0, X1
ADDI X0, X2, #5
// C)
ADD X9, X1, X2
ADD X10, X3, X4
ADD X0, X9, X10
2.
// A)
f = -g - f;
// B)
f = g + (-f - 5);
Para este ejercicio, se asignan a las variables f, g
los registros X0, X1
, respectivamente:
// A)
ADD X0, X1, X0
SUB X0, XZR, X0
// B)
ADDI X0, X0, #5
SUB X0, X1, X0
3.
// A)
f = -g - a[4];
// B)
b[8] = a[i - j];
Para este ejercicio, se asignan a las variables f, g, i, j
los registros X0, X1, X2, X3
, respectivamente.
La dirección base de los arrays a, b
se almacenan en los registros X6, X7
// A)
LDUR X0, [X6, #32] // f = a[4]
ADD X0, X0, X1 // f = (a[4]) + g
SUB X0, XZR, X0 // f = 0 - (a[4] + g)
// B)
SUB X9, X2, X3 // X9 = i - j
LSL X9, X9, #3 // X9 = (i - j) * 8
ADD X10, X6, X9 // X10 = &a + [(i - j) * 8]
LDUR X11, [X10, #0] // X11 = a[i - j]
STUR X11, [X7, #64] // b[8] = a[i - j]
4.
Usar MOVZ, MOVK
para cargar los registros:
{X0 = 0x1234000000000000}
{X1 = 0xBBB0000000000AAA}
{X2 = 0xA0A0B1B10000C2C2}
{X3 = 0x0123456789ABCDEF}
MOVZ X0, 0x1234, LSL 48 // 0x1234000000000000
MOVZ X1, 0xBBB, LSL 52 // 0xBBB0000000000000
MOVK X1, 0xAAA, LSL 00 // 0xBBB0000000000AAA
MOVZ X2, 0xA0A0, LSL 48 // 0xA0A0000000000000
MOVK X2, 0xB1B1, LSL 32 // 0xA0A00000B1B10000
MOVK X2, 0xC2C2, LSL 00 // 0xA0A0B1B10000C2C2
MOVZ X3, 0x0123, LSL 48 // 0x0123000000000000
MOVK X3, 0x4567, LSL 32 // 0x0123456700000000
MOVK X3, 0x89AB, LSL 16 // 0x0123456789AB0000
MOVK X3, 0xCDEF, LSL 00 // 0x0123456789ABCDEF
Condicionales y saltos
Mediante el uso de
CBZ
yCBNZ
se puede hacer un salto condicional.
CBZ
recibe un registro y una rama, y si el registro es0
salta a la rama.
CBNZ
recibe un registro y una rama, y si el registro es distinto de0
salta a la rama.
B
es un salto a una rama.
Ejemplo: bloque if
en C:
if (i == j) {
f = g + h;
} else {
f = g - h;
}
Asignamos a las variables f, g, h, i, j
los registros X0, X1, X2, X3, X4
, respectivamente:
SUB X9, X3, X4 // i - j
CBNZ X9, Else // i != j ⇒ salta a Else
ADD X0, X1, X2 // f = g + h
B Exit // salta a Exit
Else: SUB X9, X1, X2 // f = g - h
Exit: ...
Ejemplo: bloque while
en C:
while (save[i] == k) {
i += 1;
}
Asignamos a las variables i, k
los registros X0, X1
, respectivamente.
La dirección base del array save
se almacena en el registro X2
:
LSL X9, X0, #3 // i * 8
ADD X9, X9, X2 // &save[i]
Loop: LDUR X10, [X9] // save[i]
CBNZ X10, End // save[i] != k ⇒ salta a End
ADDI X0, X0, #1 // i += 1
B Loop
End: ...
Operaciones condicionales
Es posible modificar los valores de los flags
N, Z, C, V
mediante el uso de una instrucción con sufijoS
(ADDS, ADDIS, SUBS, SUBIS, ANDS, ANDIS...
)
Con la comparación, se puede hacer un salto condicional conB.EQ
,B.NE
,B.GE
,B.LT
,B.GT
,B.LE...
Ejemplo: bloque if
en C:
if (a > b) {
a += 1;
}
Asignamos a las variables a, b
los registros X0, X1
, respectivamente:
SUBS X9, X0, X1 // a - b
B.GE Else // a <= b ⇒ salta a Else
ADDI X0, X0, #1 // a += 1
Else: ...
También se puede usar CMP, CMPI
para comparar sin almacenar el resultado en un registro:
CMP X0, X1 // comparar a, b
B.GE Else // a <= b ⇒ salta a Else
ADDI X0, X0, #1 // a += 1
Else: ...
Formato y ensamblado de instrucciones
Todas las instrucciones tienen un formato de \(32\) bits, donde la distribución de los bits depende del tipo de instrucción.
Instrucciones de tipo R
Las instrucciones de tipo R son aquellas que operan y reciben registros como argumentos.
- opcode: la instrucción a ejecutar (por ejemplo,
ADD
,SUB
,AND
,ORR
, etc.) - Rm: segundo registro operando.
- shamt: desplazamiento (shift amount).
- Rn: primer registro operando.
- Rd: registro destino (donde se almacena el resultado de la operación).
Opcode | Rm | Shamt | Rn | Rd |
---|---|---|---|---|
\(11\) bits | \(5\) bits | \(6\) bits | \(5\) bits | \(5\) bits |
Instrucciones de tipo D
Las instrucciones de tipo D son aquellas que transfieren datos entre memoria y registros.
Opcode | address | op2 | Rn | Rt |
---|---|---|---|---|
\(11\) bits | \(9\) bits | \(2\) bits | \(5\) bits | \(5\) bits |
address
: offset de la dirección de memoria.
El valor de address
debe ser el valor de la constante en base \(2\),
agregando los bits necesarios para llegar a \(9\) bits.
op2
: expande el opcode (siempre debe estar seteado a00
).
Instrucciones de tipo I
Las instrucciones de tipo I son aquellas que operan con un valor inmediato.
Opcode | immediate | Rn | Rd |
---|---|---|---|
\(10\) bits | \(12\) bits | \(5\) bits | \(5\) bits |
immediate
: valor inmediato (constante). (de \(0\) a $2^12)
Instrucciones de tipo IM
Las instrucciones de tipo IM son aquellas que mueven contenidos entre registros.
Opcode | LSL | MOV immediate | Rd |
---|---|---|---|
\(9\) bits | \(2\) bits | \(16\) bits | \(5\) bits |
LSL
: desplazamiento a la izquierda (shift left). \(00 = 0, 01 = 16, 10 = 32, 11 = 48\)MOV immediate
: valor inmediato (constante). (de \(0\) a \(2^{16}\))
Instrucciones de tipo B
Las instrucción de tipo B (solo
B
) realiza saltos directos a otras instrucciones.
Opcode | BR address |
---|---|
\(6\) bits | \(26\) bits |
El valor de BR address
es un binario cuyo valor es la cantidad de saltos realizados para llegar a la rama seleccionada (colocando los bits necesarios para llegar a \(26\))
Si se realizan tres saltos hacia adelante, su valor sería 00000000000000000000000011 = 3
Si se realizan tres saltos hacia atrás, su valor sería 11111111111111111111110011 = -3
Instrucciones de tipo CB
Las instrucciones de tipo CB son aquellas que realizan saltos condicionales a otras instrucciones.
Opcode | COND BR address | Rt |
---|---|---|
\(8\) bits | \(19\) bits | \(5\) bits |
El criterio para poner el valor de COND BR address
es el mismo que BR address
, ajustando a \(19\) bits.