Bueno estuve mirando un poco el codigo y por lo que parece las optimizaciones hacen que utilize menos instrucciones para llegar al punto deseado. Registros propios de la CPU, Lo feo es que por ahi usa demasiadas instrucciones para cosas demasiados simples.
Para que me entiendan un poco
Una CPU MIPS32 posee 32 registros generales + 3 especiales, el tema es que de los registros generales tienen sus "funciones" en realidad es una convencion, en ASM puedo usar los que quiera y como quiera. Pero siguiendo la linea de C, deberia respetarlos
Los Registros son:
R0...R31 --- Registros generales, Particularidades
R0 es siempre 0, util para desechar resultados o poner a 0 algo.
R2/R3 para valores de retorno tambien llamados V0/V1
R4-R7 Para valores de argumento, primeros 4 sino lo demas al stack, tambien llamado A0 - A3
R8-R15 /R24/R25 Registros "temporales" . Tambien llamados T0-T9
R16-R23 "Saved Registers" S0-S7
R26/R27 "Kernel Reserved Registers" K0/K1
R28 "Global Pointer" Usado para direccionar variables globales estaticas o GP
R29 Stack Pointer o SP
R30 Frame Pointer o FP
R31 Return Address o RA
Registros especiales
HI, LO --- Registros especiales, para resultados de multiplicacion/division , no confundir con %hi del codigo de juaperser
PC --- Program Counter
Ahora voy a analizar el codigo de cada uno que me pasaron:
addiu $sp,$sp,-8 -------- SP = SP - 8 , Modifica el stack pointer para hacer lugar y guardar los Frame Pointer, 8 bytes
.LCFI0:
sw $fp,4($sp) -------- Store Word Frame pointer -> SP , FP -> (SP + 4) , Guarda el Frame Pointer es los primeros 4 bytes que ya habiamos reservados
.LCFI1:
move $fp,$sp -------- FP -> SP , Guarda nuevamente el Frame Pointer en los otros 4 bytes y completamos los 8 reservados, ni idea por que 2 veces.
.LCFI2:
.loc 1 43 0
lui $2,%hi(ANSELB) ---- R2 = HI<<16 , guarda los 2 bytes de la direccion superior de ANSELB en R2
sw $0,%lo(ANSELB)($2) [LO + R2] = R0 , Pone 0 en ANSELB, Direccion de arriba(2 bytes) + direccion de abajo(2 bytes)
.L2:
.loc 1 47 0
lui $2,%hi(LATB) R2 = &LATB AND 0xFFFF.0000 , es decir la parte superior de la direccion se guarda en R2 quedando 0xaaaa.0000
lw $2,%lo(LATB)($2) R2 = [R2 + LO] , conteniedo en R2 + offset apuntando a LATB y termina almacenando el Word(32bits) en R2
ext $2,$2,0,1 Extract bit field, Extrae los bits desde el bit 0, con una longitud de 1 y almacena eso nuevamente en R2, quedando solo el bit LATB0
andi $2,$2,0x00ff R2 = R2 & 0xFF
nor $2,$0,$2 R2 = ~ (R0|R2) , R2 ( que contenia LATB0 ) se realiza una OR y luego se nega con R0 ( que siempre es 0), Lo mismo que negar el bit LATB0 y todos los demas tambien
andi $2,$2,0x00ff R2 = R2 & 0xFF Nuevamente lo ajusta a 8 bits (pero de la NOR quedaron muchos 1 ademas del bit de interes)
andi $2,$2,0x1 R2 = R2 & 0x1 Finalmente solo deja LATB0
andi $4,$2,0x00ff R4 = R2 & 0xFF Vuelve a limitarlo a 8 bits y guarda en R4
lui $3,%hi(LATB) R3 = Upper 2 byte address LATB << 16
lw $2,%lo(LATB)($3) R2 = [LO + R3] Carga nuevamente lo que esta en la direccion de LATB
ins $2,$4,0,1 R2 = (R2 & 0xFFFF.FFFE )+ LATB0 (desde R4), la instruccion copia la seccion desde la posicion 0, longitud 1 de R4 a R2 dejando R2 con sus demas bits intactos
sw $2,%lo(LATB)($3) [LO + R3] = R2 Finalmente guardo mi LATB modificado
.loc 1 48 0
j .L2 Salto a L2, por el pipeline si hay otra instruccion puede que se ejecute a pesra que se produjo el salto, asi que se pone un NOP
nop
Se puede observar que hace uso de los registros 2 , 3 y 4, cada instruccion esta explicada.
El frame pointer es para ordenar las subrutinas en las pilas, con cada llamado a subrutina se guarda el puntero de Frame, este Frame contiene algunos registros+espacio para las variables nuevas, etc, luego cuando se vuelve , se vuelve al anterior FP
En fin, ese programa hace desastres, es impresionante la cantidad de AND que hace y un NOR, cuando podria hacer un XOR. Otra cosa lee 2 veces el la memoria para obtener ese valor.
Algo parecido es lo que paso con el codigo de migsantiago sin optimizacion:
9d003e64: 3c02bf86 lui v0,0xbf86 ; Utiliza un registro nomas para almacenar la direccion de memoria
9d003e68: 8c420120 lw v0,288(v0)
9d003e6c: 7c4200c0 ext v0,v0,0x3,0x1
9d003e70: 304200ff andi v0,v0,0xff ; Multiples AND sin sentido
9d003e74: 24420001 addiu v0,v0,1
9d003e78: 30420001 andi v0,v0,0x1
9d003e7c: 304400ff andi a0,v0,0xff
9d003e80: 3c03bf86 lui v1,0xbf86 ; Lo cual lo hace cargarlo de nuevo
9d003e84: 8c620120 lw v0,288(v1)
9d003e88: 7c8218c4 ins v0,a0,0x3,0x1
9d003e8c: ac620120 sw v0,288(v1)
9d003e90: 0b400f99 j 9d003e64
9d003e94: 00000000 nop
Es identico al anterior (por eso no lo explico), nomas que aqui se puede ver realmente la direccion de los registros. si observan el %hi(LATx) y %lo(LATx) se transforma en 0xBF86.0120
Esto por que aceptan solo 16bits esa instruccion
Por ultimo con optimizacion 1:
9d002428: 3c02bf86 lui v0,0xbf86 V0 = 0xbf86.0000
9d00242c: 8c440120 lw a0,288(v0) A0 = [V0 + 288] = [0xBF86.0120]
9d002430: 7c8400c0 ext a0,a0,0x3,0x1 Extrae el bit 4 de A0 y solo eso guarda en el bit0 en A0. (A0 & 0x00 + A0.bit0) = A0.bit4
9d002434: 38840001 xori a0,a0,0x1 A0 = A0 XOR 0x1 , Negar bit0
9d002438: 8c430120 lw v1,288(v0) V1 = [V0 + 288] = [0xBF86.0120] , Vuelvo a cargar el valor del registro
9d00243c: 7c8318c4 ins v1,a0,0x3,0x1 Inserto el bot. V1.bit4 = A0.bit0
9d002440: ac430120 sw v1,288(v0) [V0 + 288] = [0xBF86.0120] = V1 Guardo mi registro
9d002444: 0b40090b j 9d00242c Salto
9d002448: 00000000 nop
Muy parecido al anterior pero aun asi vuelve a hacer cosas sin sentido, como extraer el bit, insertarlo, etc. Ademas de cargar 2 veces lo que esta en memoria.
Si tuviera que hacer algo similar bastaria con 6 intrucciones.
loop:
lui v0,0xBF86 ; Direccion
lw a0,288(v0) ; Cargo valor de registro en A0
xori a0,a0,0x1 ; A0 = A0 XOR 0x1
sw a0,288(v0) ; Guardo A0 en el registro que sea
j loop
nop
Y el loop se puede hacer mas corto si el lui y el lw fuera de este loop. Algo asi:
start
lui v0,0xBF86 ; Direccion
lw a0,288(v0) ; Cargo valor de registro en A0
loop
xori a0,a0,0x1 ; A0 = A0 XOR 0x1
sw a0,288(v0) ; Guardo A0 en el registro que sea
j loop
nop