ARQUITECTURA DE SISTEMAS PARALELOS I. 4º INGENIERÍA INFORMÁTICA. ENSAMBLADOR DEL “DLX” 1. Sea el siguiente fragmento de código: Loop: LW ADDI SW ADDI SUB BNEZ R1, 0(R2) R1, R1, #1 0(R2), R1 R2, R2, #4 R4, R3, R2 R4, loop donde el valor inicial de R3 es R2+400. Explicar qué hace y cual puede ser su traducción en lenguaje C o similar. 2. Dado el fragmento de código: int i; double a[MAX], b[MAX], c[MAX], t; for (i=0, t=0 ; i<MAX ; i++) t += b[i] + c[i]*a[i]; Escribir una posible traducción a ensamblador del DLX. 3. Explicar qué hace el siguiente fragmento de código y cual puede ser su traducción en lenguaje C o similar. Bucle: LW R1, (R8)0 LW R2, (R7)0 SW (R8)0, R2 SW (R7)0, R1 ADDI R7, R7, #4 SUBI R8, R8, #4 BNEZ R8, Bucle ¿Qué ha de retocarse si se ponen las instrucciones de suma y resta al principio del bucle? 4. Para el siguiente código en C, escribir una posible traducción a ensamblador del DLX: double a, z[256]; /*suponer que z[0] está en la dirección 0, para usar un único puntero al array z, y decrementarlo hasta que éste sea 0 */ for (i=127 ; i>=0 ; i--) z[i+128] = a*z[i] + z[i+128]; SOLUCIONES: 1. Se trata de un bucle donde en las últimas tres instrucciones (parte de “control del bucle”) el registro R2 se va incrementando de 4 en 4 y se va comparando con R3, usando la instrucción SUB. En el DLX la instrucción de comparar no existe con el mnemónico CMP o algo parecido, aunque sí existen instrucciones de valorar un registro (set en inglés) cuyo mnemónico es Sxx, donde xx es GT(>), GE(>=), EQ(=), etc. En lugar de SUB podría haberse usado SNE (Set if Not Equal). Tal comparación hace de límite del bucle ya que el valor inicial de R3 es R2+400, y R2 va creciendo hasta haberse incrementado en 400. Como se incrementa de 4 en 4, el bucle tiene 100 iteraciones. Loop: LW ADDI SW R1, 0(R2) R1, R1, #1 R1, 0(R2) ADDI SUB BNEZ R2, R2, #4 R4, R3, R2 R4, loop ; incrementa R2 ; compara R2 y R3. Podría haberse usado SNE En las tres primeras instrucciones del bucle tenemos que se carga el dato de la dirección 0(R2), se incrementa en un registro, y se escribe sobre memoria en la misma posición. Por tanto debe existir un array en memoria que está siendo incrementado. Por tanto el código de alto nivel puede ser algo así como: do { r1 = *(r2); r1 = r1 +1; /* o r1++ */ *(r2) = r1 r2 = r2 +4; /* r2 +=4*/ } while (r2 != r3) Evidentemente este código no es muy de alto nivel, y sería más lógico definir un array en lugar de usar punteros. Entonces sería algo como: for (i=0 ; i<100; i++) a[i]=a[i]+1; donde a[i] es un array de enteros de 32 bits apuntado por r2. 2. Se trata de un bucle y de tres arrays. Vamos a usar el índice de cualquiera de los arrays para decidir el límite del bucle, de forma análoga a problema anterior, por ejemplo el de a[] apuntado por el registro genérico Ra. Como los RISC tienen muchos registros y son ortogonales (es decir, sirven todos para todo), utilizaremos los registros con letras para seguir mejor el código: Rb, Rc apuntan a b[], c[] y Rt hace de acumulador t. Entonces hay que hacer las lecturas de memoria de un elemento de a, b y c, y hacer las operaciones: Bucle: LD Fa, (Ra)0 LD Fb, (Rb)0 LD Fc, (Rc)0 MULD Fa, Fa, Fc ADDD Fa, Fa, Fb ADDD Ft, Ft, Fa Se ha usado el mismo registro Fa para hacer de variable temporal donde ir calculando los valores intermedios, y así ahorrar el uso de otro registro (análogamente se podrían ahorrar más registros si no se hacen tres cargas seguidas sino sólo dos, se calcula el producto, luego se hace otra carga y se calculan las sumas, hacerlo como ejercicio). Por último han de incrementarse los punteros de los tres arrays y evaluar la condición de salida del bucle (con Ra como se dijo), por ejemplo algo como: ADD Ra, Ra, #8 ; incrementa 8 porque es double, doble precisión , 8 bytes ADD Rb, Rb, #8 ADD Rc, Rc, #8 SGE R1, Ra, FinRa ; R1 es temporal para contener la comparación entre Ra y FinRa, que se supone que apunta al final del array, es decir FinRa=RA inicial + 8*MAX BEQZ R1, Bucle 3. Se trata de un bucle y de dos arrays apuntados por R7 y R8. R7 se incrementa pero R8 se decrementa, y además controla la salida del bucle, ya que cuando R8 sea cero el salto no se tomará. Es decir el array R8 empieza en la dirección 0x00000000 del segmento de datos. Por otra parte los elementos apuntados por R7 y R8 se intercambian, como se entiende ya que lo que se lee de (R8)0 se escribe en (R7)0 y viceversa. Todo se explica en la siguiente figura: Se incrementa (R7) Se intercambian (R8) Direcc 0x0 Se decrementa Total que podría ser algo así, si a8 es el array de R8, y a7 es el de R7: for (i=0; i<TAM_a8; i++) { temp = a8[TAM_a8-i-1]; /* el -1 es porque en C se indexa un array desde el 0 al TAM-1 */ a8[TAM_a8-i-1]=a7[i]; a7[i]=temp; } Si se ponen las instrucciones de suma y resta al principio del bucle, éste quedaría así: Bucle: ADDI R7, R7, #4 SUBI R8, R8, #4 LW R1, (R8)4 LW R2, (R7)-4 SW (R8)4, R2 SW (R7)-4, R1 BNEZ R8, Bucle Es decir se han tenido que cambiar los desplazamientos de los direccionamientos, para que el bucle siga siendo correcto; ya que si se incrementa antes de tiempo el registro R7, hay que restar 4 al índice (R7) (recuérdese que admite signo el desplazamiento). Idem pero al revés para R8 4. La solución podría ser algo así: (suponemos que R1 apunta inicialmente a z[127]) Bucle: LD F0, 0(R1) LD F4, 1024(R1) MULTD F0, F0, F2 ADDD F0, F0, F4 SD 1024(R1), F0 SUBI R1, R1, #8 BNEZ R1, Bucle ; cargo z[i] ; cargo z[i+128] ; F2 contiene a ‘a’ ; almaceno en z[i+128] ; condición de salida y decremento el puntero ANEXO 1: JUEGO DE INSTRUCCIONES DEL DLX Instrucciones para la transferencia de datos LB Rd,Adr LBU Rd,Adr LH Rd,Adr LHU Rd,Adr LW Rd,Adr LF Fd,Adr LD Dd,Adr SB Adr,Rs SH Adr,Rs SW Adr,Rs SF Adr,Fs SD Adr,Fs MOVI2FP Fd,Rs MOVI2FP Rd,Fs MOVF Fd,Fs MOVD Dd,Ds MOVI2S SR,Rs MOVS2I Rs,SR Load byte (sign extension) Load byte (unsigned) Load halfword (sign extension) Load halfword (unsigned) Load word Load single-precision Floating point Load double-precision Floating point Store byte Store halfword Store word Store single-precision Floating point Store double-precision Floating point Move 32 bits from integer registers to FP registers Move 32 bits from FP registers to integer registers Copy one Floating point register to another register Copy a double-precision pair to another pair Copy a register to a special register (not implemented!) Copy a special register to a GPR (not implemented!) Instrucciones lógicas y aritméticas para enteros ADD Rd,Ra,Rb ADDI Rd,Ra,Imm ADDU Rd,Ra,Rb ADDUI Rd,Ra,Imm SUB Rd,Ra,Rb SUBI Rd,Ra,Imm SUBU Rd,Ra,Rb SUBUI Rd,Ra,Imm MULT Rd,Ra,Rb MULTU Rd,Ra,Rb DIV Rd,Ra,Rb DIVU Rd,Ra,Rb AND Rd,Ra,Rb ANDI Rd,Ra,Imm OR Rd,Ra,Rb ORI Rd,Ra,Imm XOR Rd,Ra,Rb XORI Rd,Ra,Imm LHI Rd,Imm SLL Rd,Rs,Rc SRL Rd,Rs,Rc SRA Rd,Rs,Rc SLLI Rd,Rs,Imm SRLI Rd,Rs,Imm SRAI Rd,Rs,Imm S__ Rd,Ra,Rb S__I Rd,Ra,Imm S__U Rd,Ra,Rb S__UI Rd,Ra,Imm NOP Add Add immediate (all immediates are 16 bits) Add unsigned Add unsigned immediate Subtract Subtract immediate Subtract unsigned Subtract unsigned immediate Multiply signed Multiply unsigned Divide signed Divide unsigned And And immediate Or Or immediate Xor Xor immediate Load high immediate - loads upper half of register with immediate Shift left logical Shift right logical Shift right arithmetic Shift left logical 'immediate' bits Shift right logical 'immediate' bits Shift right arithmetic 'immediate' bits Set conditional: "__" may be EQ, NE, LT, GT, LE or GE Set conditional immediate: "__" may be EQ, NE, LT, GT, LE or GE Set conditional unsigned: "__" may be EQ, NE, LT, GT, LE or GE Set conditional unsigned immediate: "__" may be EQ, NE, LT, GT, LE or GE No operation Instrucciones de Control BEQZ Rt,Dest BNEZ Rt,Dest BFPT Dest BFPF Dest J Dest JR Rx JAL Dest JALR Rx TRAP Imm RFE Dest Branch if GPR equal to zero; 16-bit offset from PC Branch if GPR not equal to zero; 16-bit offset from PC Test comparison bit in the FP status register (true) and branch; 16-bit offset from PC Test comparison bit in the FP status register (false) and branch; 16-bit offset from PC Jump: 26-bit offset from PC Jump: target in register Jump and link: save PC+4 to R31; target is PC-relative Jump and link: save PC+4 to R31; target is a register Transfer to operating system at a vectored address; see Traps. Return to user code from an execption; restore user mode (not implemented!) Instrucciones en punto flotante ADDD Dd,Da,Db ADDF Fd,Fa,Fb SUBD Dd,Da,Db SUBF Fd,Fa,Fb MULTD Dd,Da,Db MULTF Fd,Fa,Fb DIVD Dd,Da,Db DIVF Fd,Fa,Fb CVTF2D Dd,Fs CVTD2F Fd,Ds CVTF2I Fd,Fs CVTI2F Fd,Fs CVTD2I Fd,Ds CVTI2D Dd,Fs __D Da,Db __F Fa,Fb Add double-precision numbers Add single-precision numbers Subtract double-precision numbers Subtract single-precision numbers. Multiply double-precision Floating point numbers Multiply single-precision Floating point numbers Divide double-precision Floating point numbers Divide single-precision Floating point numbers Converts from type single-precision to type double-precision Converts from type double-precision to type single-precision Converts from type single-precision to type integer Converts from type integer to type single-precision Converts from type double-precision to type integer Converts from type integer to type double-precision Double-precision compares: "__" may be EQ, NE, LT, GT, LE or GE; sets comparison bit in FP status register Single-precision compares: "__" may be EQ, NE, LT, GT, LE or GE; sets comparison bit in FP status register Directivas del simulador WinDLX .align n .ascii "string1","..." .asciiz "string1","..." .byte byte1,byte2,... .data [address] .double number1,... .global label .space size .text [address] .word word1,word2,... Cause the next data/code loaded to be at the next higher address with the lower n bits zeroed (the next closest address greater than or equal to the current address that is a multiple of 2n (e.g. .align 2 means the next word begin). Store the "strings" listed on the line in memory as a list of characters. The strings are not terminated by a 0 byte. Similar to .ascii, except each string is terminated by a 0 byte. Store the bytes listed on the line sequentially in memory. Cause the following code and data to be stored in the data area. If an address was supplied, the data will be loaded starting at that address, otherwise, the last value for the data pointer will be used. If we were just reading data based on the text (code) pointer, store that address so that we can continue from there later (on a .text directive). Store the "numbers" listed on the line sequentially in memory as doubleprecision Floating point numbers. Make the label available for reference by code found in files loaded after this file. Move the current storage pointer forward size bytes (to leave some empty space in memory) Cause the following code and data to be stored in the text (code) area. If an address was supplied, the data will be loaded starting at that address, otherwise, the last value for the text pointer will be used. If we were just reading data based on the data pointer, store that address so that we can continue from there later (on a .data directive). Store the word listed on the line sequentially in memory.