El cambio de código de bifurcación (x) es muy común en nuestro código ordinario, y también es una operación que consume bastante tiempo. Si se optimiza, la eficiencia del código puede mejorarse enormemente.
1. Para códigos de ramificación de tipo 0 <= x <N
En este caso, N no puede ser demasiado grande. Para el siguiente código C:
int ref_switch(int x)
{
switch (x) {
case 0: return method_0();
case 1: return method_1();
case 2: return method_2();
case 3: return method_3();
case 4: return method_4();
case 5: return method_5();
case 6: return method_6();
case 7: return method_7();
default: return method_d();
}
}
Podemos utilizar el valor del registro de PC como referencia y el valor de x como índice para lograrlo. El código de ensamblaje optimizado es el siguiente:
; int switch_relative(int x)
switch_relative
MP x, #8
ADDLT pc, pc, x, LSL#2
B method_d
B method_0
B method_1
B method_2
B method_3
B method_4
B method_5
B method_6
B method_7
2. x es un valor ordinario
Si encuentra que x no sigue la forma de 0 <= x <N, o N es muy grande, el método anterior obviamente no es aplicable. En este caso, podemos usar la función hash para mapear, es decir, y = f (x), que se puede convertir en la forma de 0 <= y <N, con y = f (x) en lugar de x como condición para el juicio de rama , Para que podamos usar el método anterior.
Por ejemplo, suponga que cuando x = 2 ^ k, se llama a la función method_k, es decir, el valor de x es 1, 2, 4, 8, 16, 32, 64, 128 y otros valores llaman a la función predeterminada method_d. Necesitamos encontrar una función hash compuesta de varias potencias de 2 menos una multiplicación (este método es más eficiente en ARM, y se puede lograr un desplazamiento directo). A través de los experimentos, se encuentra que los dígitos 9-11 de los números obtenidos por los 8 valores anteriores x * 15 * 31 son diferentes, podemos usar esta característica para lograr saltos de rama a través de operaciones de bits.
El siguiente es el código de ensamblaje optimizado:
x RN0
hash RN 1
; int switch_hash(int x)
switch_hash
RSB hash, x, x, LSL#4 ; hash=x*15
RSB hash, hash, hash, LSL#5 ; hash=x*15*31
AND hash, hash, #7 << 9 ; mask out the hash value
ADD pc, pc, hash, LSR#6
NOP
TEQ x, #0x01
BEQ method_0
TEQ x, #0x02
BEQ method_1
TEQ x, #0x40
BEQ method_6
TEQ x, #0x04
BEQ method_2
TEQ x, #0x80
BEQ method_7
TEQ x, #0x20
BEQ method_5
TEQ x, #0x10
BEQ method_4
TEQ x, #0x08
BEQ method_3
B method_d
El método anterior es solo un caso especial que citamos. En el caso de que x sea una potencia distinta de 2, aún podemos usar un método similar para lograrlo. Aquí solo se proporciona una idea.
3. Acceso a datos no alineados
Se debe evitar el acceso a datos alineados sin dirección tanto como sea posible, de lo contrario es perjudicial para la portabilidad y la eficiencia.
- El método de acceso más simple es leer y escribir en unidades de un byte o media palabra. Este método es más recomendable, pero la eficiencia es relativamente baja.
p RN0 x RN1
t0 RN 2
t1 RN 3
t2 RN 12
; int load_32_little(char *p)
load_32_little
LDRB x, [p]
LDRB t0, [p, #1]
LDRB t1, [p, #2]
LDRB t2, [p, #3]
ORR x, x, t0, LSL#8
ORR x, x, t1, LSL#16
ORR r0, x, t2, LSL#24
MOV pc, lr
; int load_32_big(char *p)
load_32_big
LDRB x, [p]
LDRB t0, [p, #1]
LDRB t1, [p, #2]
LDRB t2, [p, #3]
ORR x, t0, x, LSL#8
ORR x, t1, x, LSL#8
ORR r0, t2, x, LSL#8
MOV pc, lr
; void store_32_little(char *p, int x)
store_32_little
STRB x, [p]
MOV t0, x, LSR#8
STRB t0, [p, #1]
MOV t0, x, LSR#16
STRB t0, [p, #2]
MOV t0, x, LSR#24
STRB t0, [p, #3]
MOV pc, lr
; void store_32_big(char *p, int x)
store_32_big
MOV t0, x, LSR#24
STRB t0, [p]
MOV t0, x, LSR#16
STRB t0, [p, #1]
MOV t0, x, LSR#8
STRB t0, [p, #2]
STRB x, [p, #3]
MOV pc,lr