Sie sind auf Seite 1von 23

Grado

Informtica, 2 Curso

Estructura de Computadores

Pra ctica 2.- Programacio n mixta C-asm x86 Linux


1 Resumen de objetivos
Alfinalizarestaprctica,sedeberasercapazde: Usar las herramientas gcc, as y ld para compilar cdigo C, ensamblar cdigo ASM, enlazar ambos tipos de cdigo objeto, estudiar el cdigo ensamblador generado por gcc con y sin optimizaciones,localizarelcdigoASMenlneaintroducidoporelprogramador,yestudiarel correctointerfazdelmismoconelrestodelprogramaC. Reconocerlaestructuradelcdigogeneradoporgccsegnlaconvencindellamadacdecl. Reproducir dicha estructura llamando a funciones C desde programa ASM, y recibiendo llamadasdesdeprogramaCasubrutinasASM. Escribirfragmentossencillosdeensambladorenlnea. UsarlainstruccinCALL(conconvencin cdecl)desdeprogramasASMparahacerllamadasal sistemaoperativo(kernelLinux,seccin2)yalalibreraC(seccin3delmanual). EnumerarlosregistrosyalgunasinstruccionesdelosrepertoriosMMX/SSEdelalneax86. Usarconefectividadundepuradorcomogdb/ddd. Argumentarlautilidaddelosdepuradoresparaahorrartiempodedepuracin. Explicarlaconvencindellamadacdeclparaprocesadoresx86. Recordarypracticarenunaplataformade32bitslasoperacionesdeclculodeparidad,clculo depesoHamming(populationcount),sumalateral(debitsodecomponentesSIMDenteros)y productodematrices.

2 Convencin de llamada cdecl


En la prctica anterior ya vimos la conveniencia de dividir el cdigo de un programa entre varias funciones,parafacilitarsulegibilidadycomprensin,ademsdesureutilizacin.EnlaFigura1sevuelve amostrarlasumadelistadeenterosde32bits,destacandoestostresaspectosqueahoranosinteresan: Ladireccindeiniciodelalistaysutamaoselepasaalafuncinatravsderegistros. ElresultadosedevuelvealprogramaprincipalatravsdelregistroEAX. LasubrutinapreservaelvalordeEDX.

Probablementeelautordeestafuncinconsiderequequienreutilicesusfuncionesdebeaprenderqu registrossedebenusarparapasarlosargumentos,teniendogarantizadoquealavueltadelasubrutina slosehabrmodificadoelvalordelregistroEAX,quecontieneelvalorderetornodelafuncin. Sepuedenusarvariasalternativasparapasarparmetrosafuncionesypararetornarlosresultadosde lafuncinalcdigoquelahallamado.Sellamaconvencindellamada(callingconvention)alconjunto dealternativasescogidas(parapasarparmetrosydevolverresultados).Correspondealaconvencin determinar,porejemplo: Dndeseponenlosparmetros(enregistros,enlapilaoenambos). Elordenenquesepasanlosparmetrosalafuncin. o Siesenregistros,enculsepasaelparmetro1,2,etc. o Siesenpila,losparmetrospuedenintroducirseenelordenenqueaparecenenla declaracindelafuncin(comoenPascal)oalcontrario(comosehaceenC). Laprimeraopcinexigequeellenguajeseafuertementetipificado,yasuna funcinslopodrtenerunnmerofijodeargumentosdetipoconocido. Lasegundaopcinpermiteunnvariabledeargumentosdetiposvariables. Quregistrospreservaelcdigodellamada(invocante)yculeslafuncin(cdigoinvocado). o Losprimeros(callersave,salvainvocante)puedenusarsedirectamenteenlafuncin. o Los segundos (calleesave, salvainvocado) deberan salvarse a pila antes de que la funcinlosmodifique,parapoderrestaurarsuvalorantesderetornaralinvocante.

EstructuradeComputadores 1

Quinliberaelespacioreservadoenlapilaparaelpasodeparmetros:elcdigodellamada (invocante,comoenC)olafuncin(cdigoinvocado,comoenPascal). o Laprimeraopcinpermiteunnmerovariabledeargumentos.Elinvocantesiempre sabecuntoshansidoestavez(encadainvocacinpodraserunnmerodistinto). o La segunda opcin exige que el lenguaje sea fuertemente tipificado, pero ahorra cdigo:lainstruccinparaliberarpilaaparecenunanicavez,enlapropiafuncin.

Laconvencindellamadadependedelaarquitectura,dellenguaje,ydelcompiladorconcreto.Asenun procesadorconpocosregistros,comolosx86de32bits,generalmenteseprefierepasarlosparmetros aunafuncinatravsdelapila,mientrasqueenprocesadoresconmuchosregistrosseprefierepasar losparmetrosatravsderegistros.Enlosprocesadoresx86_64seusanregistrosylapila. Enesteguinnoscentraremosenlaconvencin cdecl,estndarparaarquitecturasx86de32bitsen lenguajeC,ytambinenlenguajeC++parafuncionesglobales.Siprogramamosfuncionesensamblador respetandolaconvencin cdecl,elcdigoobjetogenerado(usando as)podrinteroperarconcdigo objeto generado (mediante gcc) a partir de cdigo fuente C/C++; es decir, podremos construir un programamezclandoficherosobjetocompiladosdesdefuentesC/C++conficherosobjetoensamblados desdefuentesASM.Laconvencindellamadacdecltienelassiguientesespecificaciones:

# suma.s: # # retorna:

Sumar los elementos de una lista llamando a funcin, pasando argumentos mediante registros cdigo retorno 0, comprobar suma en %eax mediante gdb/ddd

# SECCIN DE DATOS (.data, variables globales inicializadas) .section .data lista: .int 1,2,10, 1,2,0b10, 1,2,0x10 longlista: .int (.-lista)/4 resultado: .int -1 # SECCIN DE CDIGO (.text, instrucciones mquina) .section .text _start: .global _start # PROGRAMA PRINCIPAL mov $lista, %ebx mov longlista, %ecx call suma mov %eax, resultado mov mov int # SUBRUTINA: # entrada: # # salida: suma: push %edx mov $0, %eax mov $0, %edx bucle: add inc cmp jne pop ret (%ebx,%edx,4), %eax %edx %edx,%ecx bucle %edx # restaurar %edx # preservar %edx # acumulador # ndice $1, %eax $0, %ebx $0x80 # 1er arg. en EBX: direccin del array lista # 2 arg. en ECX: nmero de elementos a sumar # llamar suma(&lista, longlista);

void _exit(int status);

int suma(int* lista, int longlista); 1) %ebx = direccin inicio array 2) %ecx = nmero de elementos a sumar %eax = resultado de la suma

Figura1:suma.s:pasodeparmetrosporregistros

EstructuradeComputadores 2

Los parmetros se pasan en pila, de derecha a izquierda; es decir, primero se pasa el ltimo parmetro,despuselpenltimoyporfinelprimero. Elespacioreservadoenlapilaparaelpasodeparmetrosloliberaelcdigoquellama.Estas dos primerasalternativas de la convencin permiten al lenguaje C soportar funciones conun nmerovariabledeargumentos. El resultado se devuelve en EAX usualmente (ver Tabla 1). Por supuesto, tambin se pueden pasar punteros o referencias a una funcin C/C++ para que sta modifique el valor referenciado. Los registros EAX, ECX, EDX son salvainvocante (callersave): la funcin los puede usar directamente, sin tener que preservarlos (sin tener que guardarlos en la pila ni recuperarlos antes de retornar). Es responsabilidad delinvocante(caller, el cdigo que llama a la funcin) guardarlosenlapilasidesearecuperarsuvalortraselretornodelafuncin. Los registros EBX, ESI y EDI son salvainvocado (calleesave): la funcin debe preservarlos (guardarlosenpila)yrestaurarlosantesderetornar,sinecesitaramodificarsucontenido. LosregistrosESPyEBPnodebenmanipularse:laconvencincdecl asumequefucionancomo punterodepilaymarcodepila. Tipodevariable [unsigned]longlongint(64bit) [unsigned]long [unsigned]int [unsigned]short [unsigned]char punteros float/double Registro EDX:EAX EAX EAX AX AL EAX ST(0) topedepilax87

Tabla1:Devolucinderesultadosdeunafuncinbajocdecl32bits

Hayciertasvariacionesrespectoalainterpretacindelaconvencindellamada cdecl entresistemas operativosycompiladoresdeC/C++,aunquenolashayenlocomentadohastaahora(msinformacin en[3]).

Ejercicio 1: suma_01_S_cdecl
Modificarelficherosuma.smostradoanteriormente(Figura1)paravolverloconformealaconvencin cdecl. Ensamblar, enlazar, depurar, y comprobar que sigue calculando el resultado correcto. En la Figura2semuestranlaslneasquedebenmodificarse.

# suma.s del Guin 1 # 1.- aadindole convencin cdecl # as --32 -g suma_01_S_cdecl.s -o suma_01_S_cdecl.o # ld -m elf_i386 suma_01_S_cdecl.o -o suma_01_S_cdecl ... _start: .global _start # PROGRAMA PRINCIPAL pushl longlista pushl $lista call suma add $8, %esp mov %eax, resultado # # # # 2 arg: nmero de elementos a sumar 1er arg: direccin del array lista llamar suma(&lista, longlista); quitar args

... # SUBRUTINA: int suma(int* lista, int longlista); suma: push %ebp # Ajuste marco pila mov %esp, %ebp # antes, en el original, conservar todo push %ebx # push %edx mov 8(%ebp), %ebx # ahora %ebx es callee-save en cdecl mov 12(%ebp), %ecx # %ecx,%edx no (caller-save) ... pop %ebx pop %ebp ret # Recuperar callee-save # Deshacer marco pila

Figura2:suma_01_S_cdecl.s:pasodeparmetrosporpila(convencincdecl)
EstructuradeComputadores

Observarque,enelprogramaprincipal,antesdellamaralafuncinseintroducenlosargumentosen pilaenordeninverso,ytraselretornoselimpialapila.Enlafuncin,seempiezaajustandoelmarcode pila,sepreservanregistrossalvainvocado(sihacefalta),yseaccedealosargumentostomandocomo baseelmarcodepilaEBP.Alretornar,serestauranlosregistrospreservadosyelmarcodepilaanterior. Todafuncincomienzaajustandoelmarcodepilaaltopeactualdepila(salvandopreviamenteelmarco anterior),demaneraquedurantelaejecucindelafuncin,(%ebp)eselantiguomarco,4(%ebp)esla direccin de retorno, y a partir de 8(%ebp) estn los argumentos de la funcin. Las dos primeras instruccionesdecualquierfuncin cdecl sonlasmostradasenlaFigura2.Elquegeneraelcdigo(ya seaprogramadorhumanooel gcc)sabequregistrosmodificalafuncin,asquesialgunodeelloses salvainvocado debe preservar su valor (en pila) para poder restaurarlo a la vuelta. En nuestro caso necesitamos4registros,asquealmenosunoibaasersalvainvocado.Todafuncinterminadejandola pilacomoestaba:restaurandolosregistrossalvainvocado,elmarcodepilaanterior,yladireccinde retorno,estoes,retornandoalinvocante. marcodepilagenrico Argumentos Dir. retorno Antiguo %ebp regs.salvados + vars. locales Argumentos Dir. retorno %esp %ebp

detalleaniveldebytes NULL argv[0]=pnam argc=1 Argumento #2 Argumento #1 Dir. retorno Antiguo %ebp Antiguo %ebx %ebp %esp
%espinicial

Figura3:marcodepilagenrico,ymarcocorrespondientealejemplo

Ejecutarpasoapasocon ddd elprogramasuma_01_S_cdecldelaFigura2yresponderalassiguientes preguntasdeautocomprobacin:


1 Sesindedepuracinsuma_01_S_cdecl Se puede realizar un volcado de la pila, usando Data>Memory>Examine 8 hex words(4B) $esp, en donde se ha escogido 8 por tener margen de sobra (en lnea de comandos gdb sera x/8xw $esp). El programa principal no tiene marcodepila(EBP=0),peroelS.O.ledejaalgoanotadoenpila.Siel1indicaraintargc=1yelsegundoargumentofuera un array de char argv[] cmo se volcara su valor (Examine 1 <qu> bytes <argv[0]>)? Qu podra ser ese 1 argumento? Pista:Sisedesea,sepuedeindagarmsajustandolosargumentosendddconsetargs<arg1><arg2> El volcado de pila es til para ir viendo la pila durante la ejecucin del programa, conforme va cambiando ESP. Tras llamar a suma, se puede realizar un volcado de memoria para comprobar que el argumento #2 es nuestra lista de 9 enteros,usandoData>Memory>Examine<cuntos>hexwords(4B)<qu>.Dedndesacamos<cuntos>y<qu>? Porqulafuncinsumapreservaahora%ebxynohacelomismocon%edx? Qumodosdedireccionamientousalainstruccinadd(%ebx,%edx,4),%eax? Cmosellamacadacomponentedel primermodo?Elltimocomponentesedenominaescala.Qusucederasiloeliminsemos? Es posible eliminar el factor de escala y conseguir que el programa siga funcionando correctamente sin aadir instruccionesadicionales,sinosimplementemodificandolasquehay.Cmo?(pista:dec%ecx) Tambinesposibleconseguirlomismodejandonicamenteunpuntero,add(%edx),%eax.Cmo? Lainstruccinjneenelprogramaoriginalsepodra cambiarporalgunodeentre otrostressaltoscondicionales(unode ellosessencillamenteotromnemotcnicoparaelmismocdigodeoperacin)yelprogramaseguirafuncionandoigual. Culessonesos3mnemotcnicos?Qutendraquesucederparaquesenotarandiferenciasconeloriginal? SegnlaFigura3,sihubisemosnecesitadoaadirunavariablelocal.int (enterode4B)alafuncinsuma,hubiramos restado4aESPcundo?(entreculesdosinstrucciones).Alasalida,deberamossumarle4aESPcundo? Sihubisemosreservadositiopara3variableslocales.int(enterosde4B),qudosinstruccionescambiaranrespectoa lapreguntaanterior,yenqucambiaran?Cmosedireccionaralasegundavariablelocalrespectoalmarcodepila? Porejemplo,cmoseralainstruccinensambladorparaponeresavariablea0? VolviendoalaFigura3,cuandounafuncinnotieneregistrossalvados,sinoslovariableslocales,esposibleeliminarlas de otra forma alternativa, ms directa que sumar el tamao a ESP. Cul? Pista: las siguientes instrucciones seran recuperarelantiguoEBPyretornar,asquequotracosasepodrahacerparaquePOPEBPfuncionarabien?

3 4 5 6 7

8 9

10

Tabla2:preguntasdeautocomprobacin(suma_01_S_cdecl)

Preguntaralosestudiantessihanvistointmain(intargc,char*argv[]).Sino,eliminaresapregunta.

EstructuradeComputadores 4

Ejercicio 2: suma_02_S_libC
Laventajadeusarlaconvencin cdecl esquepodemosinteroperarconotrasfuncionesconformesa cdecl, como son obviamente todas las funciones de la librera C. En la seccin 2 del manual se documentanloswrappersallamadasalsistema(p.ej.: man 2 exit),yenlaseccin3lasfuncionesde librera(p.ej.: man 3 printf).Modificarelprogramaanterior,aadindoleunallamadaa printf() para sacar por pantalla el resultado en decimal y hexadecimal, y sustituyendo la llamada directa al kernelLinuxporelcorrespondientewrapperlibC.Ensamblar,enlazar,depurar,ycomprobarquesigue calculandoelresultadocorrecto.EnlaFigura4semuestranlaslneasquedebenmodificarse,yaparece comocomentarioelcomandoutilizadoparaenlazarconlalibreraC.

# suma.s del Guin 1 # 1.- aadindole convencin cdecl # 2.- aadindole printf() y cambiando syscall por exit() # as --32 -g suma_02_S_libC.s -o suma_02_S_libC.o # ld -m elf_i386 suma_02_S_libC.o -o suma_02_S_libC \ # -lc -dynamic-linker /lib/ld-linux.so.2 .section .data lista: longlista: resultado: formato:

.int 1,2,10, 1,2,0b10, 1,2,0x10 .int (.-lista)/4 .int -1 .ascii "resultado = %d = %0x hex\n" # formato para printf() libC # PROGRAMA PRINCIPAL

.section .text _start: .global _start pushl pushl call add mov push push push call add

longlista $lista suma $8, %esp # quitar args %eax, resultado # resultado=suma(&lista, longlista) %eax %eax $formato printf $12, %esp # versin libC de syscall __NR_write # ventaja: printf() con formato "%d" / "%x" # traduce resultado a ASCII decimal/hex # == printf(formato,resultado,resultado) # versin libC de syscall __NR_exit # mov $1, %eax # mov $0, %ebx (no ret) # int $0x80 == _exit(0)

# ...

pushl $0 call exit add $4, %esp

Figura4:suma_02_S_libC.s:llamandoalibCdesdeASM

Observarque,paracadallamadaafuncin,elprogramaprincipalintroducelosargumentosenpilaen ordeninverso,ytraselretornoselimpialapila.EnlaFigura5seilustralasituacindelapilaantesde llamaracadafuncin.Notarquenohabroportunidaddelimpiarlosargumentosdeexit(). argsparasuma() NULL 0xffffd606 1 longlista=9


$lista=0x080492bc

ini %esp

argsparaprintf() NULL 0xffffd606 1 resultado=0x25 resultado=0x25


$formato=0x080492e8

ini

argsparaexit() NULL 0xffffd606 1 status=0

ini %esp

%esp

Figura5:pasodeargumentoscorrespondientesalejemplolibC

ComoseusandosfuncionesdelalibreraC,esnecesarioenlazarcondichalibrera(switch-lc).Sinose hace,lasfunciones printf()/exit()nosepuedenresolver,esdecir,elensambladornosabeaqu direccin de subrutina saltar. Adems, una instalacin normal de gcc espera que las aplicaciones se compilen para usar la librera C dinmica (libc.so, por shared object), por lo cual necesitaremos especificarelenlazadordinmicoausar(elde32bits,ennuestrocaso).

EstructuradeComputadores 5

Ejecutardesdelneadecomandoselprogramasuma_02_S_libCdelaFigura4,aprovechandoqueahora imprimeelresultadoporpantalla.Depurarlotambinpasoapasoconddd,reproduciendoconvolcados de pantalla el esquema mostrado en la Figura 5. Responder a las siguientes preguntas de autocomprobacin:
1 2 Sesindedepuracinsuma_02_S_libC Qu error se obtiene si no se aade lc al comando de enlazar? Qu tipo de error es? (en tiempo de ensamblado, enlazado,ejecucin) Querrorse obtienesinoseaadelaespecificacindelenlazadordinmicoalcomandodeenlazar?Ysiseindica como enlazador un fichero inexistente, p.ej. <>.so.3 en lugar de <>.so.2? Qu tipo de error es? (en tiempo de ensamblado,enlazado,ejecucin) ProporcionarInstruccionespasoapasoparaobtenerunvolcadocomoelprimerodelaFigura5 (argumentosdesuma) enmodogrficoddd(Examine<cant><fmt><tam>desde<dir>)yenmodocomandogdb(x/<fmt><addr>) Enesemomento,antesdellamarasuma,podramosmodificarmemoriaconelcomandogdbset*(int*)$esp=suma. Qu efecto tendra eso sobre nuestro programa? Para precisar la respuesta, tambin podemos ejecutar el comando gdbset*(int*)($esp+4)=2.Quresultadoseobtiene?Deberanobtenertodosloscompaerosesemismoresultado? De qu depende que suceda eso? Dicho de otro modo qu se est sumando, al hacer esas alteraciones? Pista: objdumpdsuma_02_S_libCyAccesories>CalculatorHex Repetir3paraelsegundovolcadodelaFigura5. Enesemomento,antesdellamaraprintf,podemosmodificarelpunterodepilacon elcomandogdbset$esp=$esp4,y modificar el tope con set * (int*) $esp=&formato. Qu resultado se obtiene? Deberan obtener todos ese mismo resultado?Dequdependequesucedaeso?Dichodeotromodoquseimprime,alhaceresasalteraciones? Repetir3paraeltercervolcadodelaFigura5. Enesemomento,antesdellamaraexit,podemosmodificarelpunterodepilaconelcomandogdbset$esp=$esp+4. Qu pasa entonces? En qu afecta eso a nuestro programa? Deberan obtener todos ese resultado? De qu dependeelresultado? Repetir8poniendo4enlugarde+4

3 4

5 6

7 8

Tabla3:preguntasdeautocomprobacin(suma_02_S_libC)

Ejercicio 3: suma_03_SC
Laventajadeusarlaconvencin cdecl esquepodemosinteroperarconotrasfuncionesconformesa cdecl. Hemos probado con funciones de la librera C, y ahora experimentaremos con nuestra propia funcin suma(), pasndola a lenguaje C. Modificar el programa anterior, eliminando el cdigo ensamblador de suma() y creando un nuevo mdulo en lenguaje C que realice la misma funcin. Ensamblar,enlazar,depurar,ycomprobarquesiguecalculandoelresultadocorrecto.EnlaFigura6se muestran las lneas que deben modificarse, y los comandos utilizados para compilar, ensamblar y enlazarlosdosmdulos.

# MODULO suma_03_SC_s.s # suma.s del Guin 1 # 1.- aadindole convencin cdecl # 2.- aadindole printf() y cambiando syscall por exit() # 3.- extrayendo suma a mdulo C para linkar # gcc -m32 -O1 -g -c suma_03_SC_c.c # as --32 -g suma_03_SC_s.s -o suma_03_SC_s.o # ld -m elf_i386 suma_03_SC_c.o suma_03_SC_s.o -o suma_03_SC \ # -lc -dynamic-linker /lib/ld-linux.so.2 ... formato: .ascii "resultado = %d = %0x hex\n\0" # formato para printf() libC (asciiz) ... # MODULO suma_03_SC_c.c int suma(int* array, int len) { int i, res=0; for (i=0; i<len; i++) res += array[i]; return res; }

Figura6:Aplicacinsuma_03_SC:llamandoamduloCdesdemduloASM

Sehaescogidoelnombre _SC_paraindicarquesellamadesdeASMaC,ycadamdulorepiteensu nombrelaextensin(_s.s, _c.c)paraquenocoincidanlosnombresdelosficherosobjeto.Recordar quegcc creutilizaelnombredelfuente,mientrasqueconashayqueindicarelnombredelobjeto.

EstructuradeComputadores 6

Ejecutar desde lnea de comandos el programa resultante, comprobando que imprime el mismo resultadoporpantalla.Depurarlotambinpasoapasoconddd,comprobandoquealpasaralenguajeC losargumentosformalesadquierenelvalordelosparmetrosactualespasadosenpila.Probablemente necesitemoslistarlasubrutina_startdesdelneadecomandosgdb,yaquealenlazarhemospuestoel objeto C como primer mdulo, y por tanto se ser el que muestre ddd al inicio. Responder a las siguientespreguntasdeautocomprobacin:
1 Sesindedepuracinsuma_03_SC Qu comando gdb se puede usar para ver el punto de entrada? Si no conocemos ese comando qu problemas tendramos para depurar un programa como ste? Recordar la importancia de dominar no slo el modo grfico ddd, sinotambinlalneadecomandosgdb. QudiferenciahayentreloscomandosNext yStep?(Pista:textodeayuda)Yentreesoscomandosysuversin<>i? Aprovechando que por primera vez incorporamos lenguaje C, poner un breakpoint en call suma y probar las cuatro variantes,comentandoadndellevaexactamentecadaunadeellas,yporqu.(Pista:MachineCodeWindow) Editarelformatoparaqueseaidnticoaldesuma_02(acabadoen\n),reconstruirelprogramayejecutarlo.Explicar conprecisinporquseobtieneGCC:(Ubuntu4.4 .34ubuntu5)4.4.3.(Pista:objdumps) Obtenerelcdigoensambladorgeneradoparasuma(noconddd>MachineCodeWindow,sinocongcc)ycompararlo connuestrasuma_01.Qudiferenciashay?Sugerencia:quitarinformacindedepuracinparasimplificarellistado. Probar las opciones ddd> Data> Display Local Variables/Display Arguments. Qu significa value optimized out? Pista: ir avanzando en suma con Stepi hasta que desaparezca el mensaje optimized out. Cundo desaparece? Es posibleponerunbreakpointsobrelaMachineCodeWindow,notieneporquserenlaSourceWindow. Tambin se puede compilar el mdulo C sin optimizacin. No hace falta reensamblar el mdulo ASM, basta con re enlazarelejecutable.Comprobarsisiguesaliendoelmensaje.Qudireccionestienenlasvariableslocalesiyrestras dichocambio?Realizarundibujodelmarcodepiladesumasinoptimizacin.

3 4 5

Tabla4:preguntasdeautocomprobacin(suma_03_SC)

Ejercicio 4: suma_04_SC
Antes de llevrnoslo todo a lenguaje C, vamos a probar a dejar nicamente los datos y el punto de entradaenensamblador.Nuestranicainstruccinvaaserunsalto(nollamada)alasubrutina suma,y sta acceder a los datos globalmente, imprimir el resultado y terminar el programa. No retornaremosdesuma,niusaremosinstruccionesensambladorparapasarleparmetros.Loscambios necesarios se ilustran en la Figura 7. No hay cambios en las instrucciones para compilar, ensamblar y enlazarlosdosmdulos.Hacerlo,ycomprobarquesesiguecalculandoelresultadocorrecto.

# MODULO suma_04_SC_s.s # suma.s del Guin 1 # 1.- aadindole convencin cdecl # 2.- aadindole printf() y cambiando syscall por exit() # 3.- extrayendo suma a mdulo C para linkar # 4.- dejando slo los datos, que el resto lo haga suma() en mdulo C ... .global lista, longlista, resultado, formato .section .text _start: .global _start jmp suma

# MODULO suma_04_SC_c.c #include <stdio.h> // para printf() #include <stdlib.h> // para exit() extern int lista[]; extern int longlista, resultado; extern char formato[]; void suma() { int i, res=0; for (i=0; i<longlista; i++) res += lista[i]; resultado = res; printf(formato,res,res); exit(0); }

Figura7:Aplicacinsuma_04_SC:dejandoslodatosypuntodeentradaenmduloASM
EstructuradeComputadores

NotarqueenelmduloCseaadenlos includes necesarios,cambialasignaturadelafuncin suma (nitomaargumentosniproduceresultado),yseusanlosnombresdelasvariablesglobales,nodelos parmetros.Tambinseimprimeelresultadoysefinalizaelprograma. EnelmduloASMsedeclaranglobaleslossmbolosexportados.EnelmduloCsedeclaranexternos.A


gcclebastaconsabereltipodeesasvariables,paragenerarlasinstruccionesqueaccedenaellas(salvo

ladireccin,quesedejaacero,sinrellenar).Entiempodeenlaceseresuelvenestossmbolos:porel nombreselocalizaladefinicinenlastablasdesmbolosysedescubreladireccinqueocupan. Ejecutar desde lnea de comandos el programa resultante, comprobando que imprime el mismo resultadoporpantalla.Depurarlotambinpasoapasocon ddd,comprobandoqueelsaltoalenguajeC no toca la pila, y que una vez en C las variables globales son exactamente las definidas en ASM. Probablementenecesitemoslistarlasubrutina _startdesdelneadecomandos gdb,debidoalorden deenlaceescogido.Responderalassiguientespreguntasdeautocomprobacin:
1 Sesindedepuracinsuma_04_SC Obtenerelcdigoensambladorgeneradoparasuma (noconddd>MachineCodeWindow,sinocongcc)ycompararlo conelanteriorsuma_03.Qudiferenciashay?Sugerencia:quitarinformacindedepuracinparasimplificarellistado. Otrasugerencia:comparartambinconelsuma.soriginaldelaFigura1. Una de esas diferencias nos hace pensar que nuestra versin ensamblador de suma no implementa exactamente un buclefor,ypodraproducirunresultadoincorrectoparaciertotamaodelalistaCul?Porqu? Otrasdiferenciasestnenelmanejodepila.Explicardichasdiferencias. Loscuriosospuedenbuscar__printf_chkflag conGoogle. Ejecutar nm sobre ambos objetos C/ASM y sobre el ejecutable, indicando qu significa cada letra y fijndose en los valores (direcciones) asociados con cada smbolo. Notar que las direcciones en el ejecutable son definitivas. Qu smboloscarecendedireccin?Cmoesposibleque_startylistatenganlamismadireccinenlosobjetos?Porqu notienenlamismadireccinenelejecutable? Cmoesposiblequeanquedensmbolossindefinir(U)enelejecutable? Ejecutarnmsobrelosobjetosyejecutabledelejemploanteriorsuma_03,yexplicarlasdiferenciasconsuma_04:Por qu el mdulo C tiene ahora smbolos indefinidos? Cul podra ser el motivo de que los smbolos anteriormente (d) seanahora(D)?(Pista:compararlosfuentes)Porquvaraelnombredelsmboloprintf? En relacin con 5, por qu cambian los nombres de los smbolos printf y exit del objeto al ejecutable, tanto en suma_03comoensuma_04? Cmosepodracomprobarlaafirmacindequelossmbolosnoresueltosserellenanacero?(seafirmadosprrafos porencimadeestatabla)Pista:objdump.Localizarlos6smbolosindefinidosycomprobarsiserellenanacero.Hay algunoqueserelleneavalordistintodecero?Compararelobjetoconelejecutable.

2 3 4

5 6

7 8

Tabla5:preguntasdeautocomprobacin(suma_04_SC)

Ejercicio 5: suma_05_C
PasartodoelcdigoalenguajeC.Comprobarquesesiguecalculandoelresultadocorrecto.

# MODULO suma_05_C.c #include <stdio.h> #include <stdlib.h>

// para printf() // para exit()

int lista[]={1,2,10, 1,2,0b10, 1,2,0x10}; int longlista= sizeof(lista)/sizeof(int); int resultado=-1; int suma(int* array, int len) { int i, res=0; for (i=0; i<len; i++) res += array[i]; return res; } int main() { resultado = suma(lista, longlista); printf("resultado = %d = %0x hex\n", resultado,resultado); exit(0); }

Figura8:suma_05_C:cdigoCpuro
EstructuradeComputadores

Notarquesedeshaceelcambiodesignaturadelafuncinsuma,lasvariablesglobalessedefinenenC,y el programa principal llama a nuestra funcin y a las de librera. El punto de entrada es ahora main. Notarlasintaxisparadeclarareinicializararrays,sisedesconoca.Eloperadorsizeofresultatilpara reproducirlosclculosquehacamosenelfuenteASM. Ejecutar desde lnea de comandos el programa resultante, comprobando que imprime el mismo resultadoporpantalla.Depurarloconddd.Responderalassiguientespreguntasdeautocomprobacin:
1 Sesindedepuracinsuma_05_C Volver a probar las diferencias entre Next/Step y sus variantes <>i aprovechando la llamada a suma. Poniendo un breakpointenlaprimeralneademain,quefectotienecadaunodeloscomandos?Seguramenteconvienevisualizar Machine Code Window, Display>Locals/Args, y un volcado de pila, y pulsar varias veces cada comando a partir del breakpoint. Pulsando una vez Next tras el breakpoint, identificar en el volcado de pila los argumentos (su valor se indica en el volcadoData>DisplayArgs),ypartiendodeah,indentificarloscomponentesdelmarcodepila.Puedeserinteresante utilizarStatus>Backtrace(odisasmain,enlneadecomandos)paracomprobarladireccinderetorno. Comprobar la respuesta anterior reiniciando la ejecucin y avanzando con Stepi. Esto nos permite comprobar dos valoresdelmarcodepilaqueantesslopodamossuponer,basndonoseneldesensamblado.Cules? Comohemoscomprobado, mainsquetienemarco depila(_start notena,recordarEBP=0). Recordando laprimera pregunta de comprobacin del guin, cmo se volcaran los argumentos de main? Pista: esta vez, el segundo argumento es char*argv[],yconvendrausar Examine<n><qu>bytes*<argv>. Recordarquesepueden ajustarlos argumentosconsetargs. CongccS(yquitandodepuracin) podemosconsultarelcdigoensambladorgeneradoporgccparaesteprograma. Nosdeberasonartodo,salvoalgunosdetalles:Seaplicaunamscaraalpunterodepila.Cul,yquefectoproduce? (Pista:alineamiento).Nosotrosusamos.intparadeclararenterosyarrays.Quusagcc?Nosotrosusamoselcontador deposicionesyaritmticadeetiquetasparacalcularlalongituddelarray.Quusagcc?Nosotroshemosusadopush paraintroducirargumentosenpila,aunqueentransparenciasenclasehemosvistootrosmtodos.Culusagcc?

3 4

Tabla6:preguntasdeautocomprobacin(suma_05_C)

Ejercicio 6: suma_06_CS
Volver a pasar la funcin suma a un mdulo ensamblador separado. En la Figura 9 se ilustra cmo quedara el mdulo C, y se recuerdan las instrucciones para compilar, ensamblar y enlazar (varias alternativasposibles).Comprobarquesesiguecalculandoelresultadocorrecto.

# MODULO suma_06_CS_c.c #include <stdio.h> // para printf() #include <stdlib.h> // para exit() extern int suma(int* array, int len); int lista[]={1,2,10, 1,2,0b10, 1,2,0x10}; int longlista= sizeof(lista)/sizeof(int); int resultado=-1; int main() { resultado = suma(lista, longlista); printf("resultado = %d = %0x hex\n", resultado,resultado); exit(0); } # MODULO suma_06_SC_s.s # ... # 5.- entero en C # 6.- volviendo a sacar la suma a ensamblador # gcc -m32 -O1 -g suma_06_CS_c.c suma_06_CS_s.s -o suma_06_CS # # gcc -m32 -O1 -g -c suma_06_CS_c.c # as --32 -g suma_06_CS_s.s -o suma_06_CS_s.o # gcc -m32 suma_06_CS_c.o suma_06_CS_s.o -o suma_06_CS # # LDIR=`gcc -print-file-name=` # ld -m elf_i386 suma_06_CS_c.o suma_06_CS_s.o -o suma_06_CS \ # -dynamic-linker /lib/ld-linux.so.2 \ # /usr/lib32/crt1.o /usr/lib32/crti.o /usr/lib32/crtn.o \ # $LDIR/32/crtbegin.o $LDIR/32/crtend.o -lc ...

Figura9:suma_06_CS:programaCllamandoafuncinasmcdecl
EstructuradeComputadores

Notarqueseindicaque suma es extern,comoanteslofueron lista y longlista.Estanfrecuente que las funciones estn en otro mdulo, que no hace falta indicar extern al compilador, basta con mostrarleelprototipo.Inclusosinoseindicaraprototipo,elcompiladorasumiraquelafuncines int func()(quedevolverint),producindoseunavisosiresultaratenerargumentosodevolverotracosa. Los prototipos de una librera suelen recolectarse en un fichero <librera>.h, para su inclusin en programasqueutilicenlalibrera. Notarqueespreferiblecompilar,ensamblaryenlazarlaaplicacincongcc,yaqueelpuntodeentrada es main (y vamos a usar la librera C). Anteriormente hemos preferido usar as porque el punto de entrada era _start, teniendo que enlazar explcitamente con la librera C y el enlazador dinmico cuandohemos usado funciones libC.El compilador gcc aade esas opciones(yotras para soporteen tiempodeejecucin),admiteficherosfuenteCyASMenunasolalneadecomandos,ypuedecompilar, ensamblaryenlazarenunsolocomando,porlocualespreferibleenelcasoactual.Sloparademostrar quepuedenseguirusndose as y ld,seofrecenlasinstruccionesalternativas.Notarqueenestecaso, hacefaltatambinindicarexplcitamenteelsoporteentiempodeejecucin(Cruntime). Ejecutar desde lnea de comandos el programa resultante, comprobando que imprime el mismo resultadoporpantalla.Depurarlocon ddd,comprobandoqueelmarcodepilageneradopara suma es idnticoacuandolafuncinestabaprogramadaenlenguajeC.

3 Ensamblador enlnea (inline assembly) con asm()


Hayocasionesespecialesenqueresultaraconvenienteintroducirunaspocasinstruccionesdelenguaje ensambladorentre(enlneacon)elcdigoC,pormotivosmuyconcretos: Utilizar alguna instruccin de lenguaje mquina que el compilador no conozca, o no utilice nunca,onouseenelcasoconcretoquenosinteresa(rdtsc,xchg,etc) Aprovecharalgunacaracterstica(registro,etc)delaarquitecturaqueelcompiladornoutilice (timestampcounter,performancecounters,etc). Conseguiralgunaoptimizacinquenoseaposiblemedianteswitchesuotrascaractersticasdel compilador (builtins, etc), del lenguaje (keywords como register, etc), o mediante libreras optimizadas.

En general es difcil, a menudo muy difcil, y siempre muy tedioso, intentar ganar a gcc o cualquier compiladoroptimizadorenloqueserefiereamovimientosdedatos,buclesyestructurasdecontrol, queusualmenteesungranporcentajedeltextodecualquierprograma.Esmsconvenienteestudiarel manualyusarlosswitchescorrespondientes(p.ej.:-mtune=core2,-msse4.2),paraqueelcompilador genereinstruccionesespecficasdeesaarquitectura(sidecidequesonventajosas),yreordeneyalinee instruccionesydatosteniendoencuentadetallesdelamicroarquitecturaignoradosuobviadosporla mayora de los programadores (y an prestndoles atencin, se necesitaran manuales y simuladores paraaprovecharlosenigualgradoquegcc). Para posibilitar esa insercin de unas pocas instrucciones ensamblador enlnea con el cdigo C, gcc tambindispone(igualqueotroscompiladores)deunasentenciaasm(),conlasiguientesintaxis: Bsica: asm(<sentencia ensamblador>) Extendida:asm(<sentencia asm>:<salidas>:<entradas>:<sobrescritos>)

Aunque en principio el mecanismo est pensado para una nica instruccin ensamblador, se puede aprovechar la concatenacin de strings (dos strings seguidos en un fuente C se concatenan automticamente) y los caracteres \n\t como terminacin, para insertar varias lneas que el ensambladorinterpreteposteriormentecomoinstruccionesdistintasdecdigofuenteASM. SielcdigoinlineestotalmenteindependientedelcdigoC,enelsentidodenonecesitarcoordinacin con objetos controlados por el compilador (variables, registros de la CPU, etc), puede usar la sintaxis bsica(sinrestricciones).Perohabitualmente,desearemosqueelcdigoinlinesecoordineconelcdigo C, porque queramos modificar el valor de alguna variable (restricciones de <salida>), o consultarlo (<entrada>),osimplementeparanointerferirconlasoptimizacionesencurso(<sobrescritos>).

EstructuradeComputadores 10

Ejercicio 7: suma_07_Casm
Una ventaja de usar ensamblador inline es que podemos incorporar lo que de otra forma se hubiera convertidoenunpequeomduloASMenelpropiocdigoC,facilitandoelestudiodelaaplicaciny evitando la necesidad de ensamblar y enlazar separadamente, o al menos reduciendo el nmero de ficherosfuenteimplicados.Modificarelejemploanterior,volviendoaincorporarelcdigoensamblador desumacomoensambladorenlnea.Compilar,ejecutar,ycomprobarquesiguecalculandoelresultado correcto.EnlaFigura10semuestraelfuenteresultante.

#include <stdio.h> #include <stdlib.h>

// para printf() // para exit()

int lista[]={1,2,10, 1,2,0b10, 1,2,0x10}; int longlista= sizeof(lista)/sizeof(int); int resultado=-1; int { // // // // suma(int* array, int len) int i, res=0; for (i=0; i<len; i++) res += array[i]; return res; asm("push %ebx "mov 8(%ebp),%ebx mov 12(%ebp), %ecx

\n\t" // clobber \n" " \n" " \n" " mov $0, %eax \n" " mov $0, %edx \n" "bucle: \n" " add (%ebx,%edx,4), %eax\n" " inc %edx \n" " cmp %edx,%ecx \n" " jne bucle \n" " \n" " pop %ebx \n" // : // output // : // input // : "cc", // clobber // "eax","ebx","ecx","edx" ); } int main() { resultado = suma(lista, longlista); printf("resultado = %d = %0x hex\n", resultado,resultado); exit(0); }

Figura10:suma_07_Casm:incorporandomduloASMcomoinlineasm

Sehaescogidoelnombre _Casm_paraindicarqueseusaasminline.Notarque,comoconocemosla convencin cdecl, podemos obtener los valores de los argumentos array y len sin necesidad de coordinarnos con gcc mediante restricciones de entrada, y producir el valor de retorno (en EAX) sin indicar restricciones de salida. Ni siquiera necesitamos avisar a gcc de los registros que alteramos (restriccionesdesobreescritos,oclobberconstraints).Elregistroccsonlosflagsdeestado(condition codes).Avecespuedesernecesarioindicaragccquenuestrocdigoinlinemodificalosflags. Responderalassiguientespreguntasdeautocomprobacin:
1 2 3 Preguntasdeautocomprobacin:suma_07_Casm Compararelcdigoensambladorgeneradoporgcc paraelejemploanterioryparaste.Hayalgunadiferencia? No necesitamos declarar como sobrescrito ninguno de los registros usados, aunque por distintos motivos. Cuntos motivosdistintoshay,yaquregistrosseaplicacadauno?Notarquelasentenciaasmimplementalafuncincompleta. Pormotivosestticos,avecesseterminanlaslneasASMcon\nyotrascon\n\t.Porqu enestecasoapenasse hausado\t?Explicarquedicinestticarealizalasentenciaasm()sobrelalneaASMinsertada.Pista:hacerpruebas conmsymenoslneas,conysin\n\t,yconsultarelcdigoensambladorgeneradoporgcc. Esaedicinestticadelataquelasentenciaasm()estpensadainicialmenteparaunanicalneaASM.Porqu?

Tabla7:preguntasdeautocomprobacin(suma_07_Casm)

EstructuradeComputadores 11

Restricciones de salida, entrada, y sobrescritos


Como ya se coment, es difcil ganar a gcc en movimiento de datos o control de flujo, y tedioso el simple hecho de intentarlo. El ensamblador enlnea es ms efectivo para las situaciones en que conocemos alguna funcionalidad o mejora que gcc ha pasado por alto. Usualmente se tratara de insertarunanicainstruccinensamblador(opocas),peroquenecesitamoscoordinarcongccporque: Modificanalgunavariable(restriccionesdesalida) Necesitanelvalordealgunavariable,constante,direccin(restriccionesdeentrada) ModificanestadodelaCPUquepuedaestarusandogcc(registrossobrescritos)

Esacoordinacinseexpresamediantelasdenominadasrestricciones(constraints),conestasintaxis: Salidas: Entradas: Sobrescritos: [<ASMname>] =<constraint> (<Cname>) [<ASMname>] <constraint> (<Cexpr>) <reg>|cc|memory

El nombre o expresin C se asociar con algn recurso ensamblador (registro de la CPU, registro del coprocesador, valor inmediato, direccin de memoria) que cumpla la restriccin indicada. El nombre ensambladores opcional. Si se indica en la restriccin,podremos hacer referencia a dicho recurso en nuestrocdigoinlinecomo%[<ASMname>].Sino,elrecursosereferenciarcomo%0,%1,%2enel ordenenqueaparezcaenlalistaderestricciones. Notarquelasrestriccionesdesalidadebenllevarelmodificador=(otambin+,verTabla8).Enel apartadodesobrescritossedebenindicarlosrecursosquemodificanuestrocdigoinline,afindeque gcc no optimice errneamente el acceso a los mismos, ignorando que han sido alterados en nuestra sentencia asm. En general, es buena idea comprobar el cdigo ensamblador generado alrededor de nuestra sentencia asm, para anticipar (si lo vemos antes) o corregir (si no lo hemos visto antes) un posible error de coordinacin con gcc, debido a haber especificado unas restricciones incorrectas. Convienerecordarqueelmecanismoasmfuepensadoinicialmenteparaunanicainstruccinmquina, y as veremos que a veces una restriccin =r (salida registro) reutiliza el mismo registro que una entradar.Amenudo,usandorestriccin+rdesapareceelproblema(porqu?). Elmanualdegcc[7]ysuInlineassemblyHOWTO[8]sonlosdocumentosdereferenciaparalasdistintas restriccionesdisponibles,tantoengeneralparatodoslosprocesadoressoportados,comoenparticular paralosprocesadoresdelasfamiliasx86yx8664.Existentambinnumerosostutorialesydocumentos web (ver por ejemplo la Linux Assembly HOWTO [9] y los tutoriales SourceForge [10]) sobre esta temtica.Paranuestrosobjetivos,seguramentenosbasteconocerlasrestriccionesmsbsicas: Restriccin Registro a EAX b EBX c ECX d EDX S ESI D EDI A EDX:EAX f ST(i)registrop.flotante t ST(0) topedepilax87 u ST(1) siguientealtope Modificadores = Salida(writeonly) + EntradaSalida Restriccin m q r g I i G <n> Operando operandodememoria registroseax,ebx,ecx,edx registroseax,ebx,ecx,edx,esi,edi registro(q)omemoria(m) valorinmediato0..31(desplrotacin) valorinmediatoentero valorinmediatopuntoflotante en restriccin de entrada, un nmero indica que el operando tambin es de salida,lasalidanmero%<n>

=&

Earlyclobber (salida sobrescrita antes de leertodaslasentradas)

Tabla8:Restricciones(constraints)ymodificadoresmsutilizados

Lamayoradelosfragmentosinlinepuedenresolverseconlasrestriccionesquehemosretintado.

EstructuradeComputadores 12

Ejercicio 8: suma_08_Casm
Modificar el ejemplo anterior, reduciendo el cdigo ensamblador enlnea al cuerpo del bucle for. Compilar,ejecutar,ycomprobarquesiguecalculandoelresultadocorrecto.EnlaFigura11semuestra elfragmentorelevante.

int suma(int* array, int len) { int i, res=0; for (i=0; i<len; i++) // res += array[i]; asm("add (%[a],%[i],4),%[r] \n" : [r] "+r" (res) // output-input : [i] "r" (i), // input [a] "r" (array) // : "cc" // clobber ); return res; }

Figura11:suma_08_Casm:inlineasmconrestricciones

Notar que para redactar este cdigo inline no es necesario conocer la convencin cdecl, y podemos obtener referencias a array, i y res coordinndonos con gcc mediante restricciones de salida y entrada.Responderalassiguientespreguntasdeautocomprobacin:
1 2 3 4 5 Preguntasdeautocomprobacin:suma_08_Casm Compararelcdigoensambladorgeneradoporgcc paraelejemploanterioryparaste.Hayalgunadiferencia? Compararelcdigogeneradocomentandoydescomentandoccde lalistaclobber.Hayalgunadiferencia? Nonecesitamosdeclararningnotrosobrescrito,aunqueporunmotivo distintoqueenelejemploanterior.Porqu? Siresesvariabledesalida,porquselehaindicadorestriccin+r,enlugarde=r? Volveraexplicarporquenestecasoseprefiereacabarlalneacon\nenlugarde\n\t

Tabla9:preguntasdeautocomprobacin(suma_08_Casm)

Ejercicio 9: suma_09_Casm
Como el cdigo generado es el mismo, no se espera que haya ninguna diferencia en cuanto a prestaciones entre los ltimos tres ejemplos. Para comprobarlo, crear un programa que incorpore las tres alternativas de suma, y que ejecute cada una cronometrando su tiempo de ejecucin, usando la funcindelibreraC gettimeofday.Compilar,ejecutar,comprobarquelastresversionesproducenel mismo resultado, y calcular el tiempo de ejecucin promedio (de cada versin) sobre 10 ejecuciones consecutivas.EnlaFigura12semuestraelprogramaresultante.

#include <stdio.h> // para printf() #include <stdlib.h> // para exit() #include <sys/time.h> // para gettimeofday(), struct timeval #define SIZE (1<<16) int lista[SIZE]; int resultado=0; // tamao suficiente para tiempo apreciable

int suma1(int* array, int len) { int i, res=0; for (i=0; i<len; i++) res += array[i]; return res; } int suma2(int* array, int len) { int i, res=0; for (i=0; i<len; i++) // res += array[i]; asm("add (%[a],%[i],4),%[r] \n" : [r] "+r" (res) // output-input : [i] "r" (i), // input [a] "r" (array) // : "cc" // clobber ); return res; }

EstructuradeComputadores 13

int suma3(int* array, int len) { asm("mov 8(%%ebp), %%ebx \n" // array " mov 12(%%ebp), %%ecx \n" // len " \n" " mov $0, %%eax \n" // retval " mov $0, %%edx \n" // index "bucle: \n" " add (%%ebx,%%edx,4), %eax\n" " inc %%edx \n" " cmp %%edx,%%ecx \n" " jne bucle \n" : // output : // input : "ebx" // clobber ); } void crono(int (*func)(), char* msg){ struct timeval tv1,tv2; // gettimeofday() secs-usecs long tv_usecs; // y sus cuentas gettimeofday(&tv1,NULL); resultado = func(lista, SIZE); gettimeofday(&tv2,NULL); tv_usecs=(tv2.tv_sec -tv1.tv_sec )*1E6+ (tv2.tv_usec-tv1.tv_usec); printf("resultado = %d\t", resultado); printf("%s:%9ld us\n", msg, tv_usecs); } int main() { int i; for (i=0; i<SIZE; i++) lista[i]=i; crono(suma1, "suma1 crono(suma2, "suma2 crono(suma3, "suma3 printf("N*(N+1)/2 = exit(0); }

// inicializar array // se queda en cache

(en lenguaje C )"); (1 instruccin asm)"); (bloque asm entero)"); %d\n", (SIZE-1)*(SIZE/2)); /*OF*/

Figura12:suma_09_Casm:esqueletodeprogramaparacomparartiemposdeejecucin

Notarquesehadefinidountamaodearraylosuficientementegrandecomoparaqueeltiempode ejecucinseaapreciable.Dehecho,elmotivoparanoponeruntamaomayorhasidolaincomodidad paracalcularelresultadocorrectomediantelafrmulacorrespondiente.Encualquiercaso,inclusopara tamaosmuchomenoressevenacumpliendoqueeltiempodeejecucincrecaidnticamenteconel tamaodelarray(tamaodoble empodoble),locualindica,paraunalgoritmodecomplejidadlineal comoste,queeltiempocronometradonoestdominadoporotrosfactoresajenos,sinoporelpropio procesorealizado(sumarlosNelementos,enestecaso). Notarqueeltamaodelarraynosuponeperjuicioparaelcronometrajedeningunaversin.Ennuestro caso es lo suficientemente pequeo como para caber en cache L2 y estar disponible para las tres ejecuciones,unavezinicializadoelarray.Sifuerademasiadograndetampocoimportara,porquealno caber,igualnocabealinicializar,quenocabealcronometrarlaversin1,quenocabealcronometrar ningunaotra.Enestecaso,elordendeejecucindelasversionesnoafectaasucronometraje.Tampoco afectaculsealaprimeraqueseejecute,trasinicializarelarray.Engeneral,esenoeselcaso,ysedebe meditar cuidadosamente cmo realizar el cronometraje de forma justa y equitativa para todas las versiones. Notarquesehaintroducidounaligeravarianteenlaversin3,yquecuandohaylistadesobrescritos, losregistrossereferenciancomo%%<reg>.Cuandonohaysobrescritos,bastacon%<reg>. Este mismo programa nos puede servir de esqueleto para el resto de trabajos de optimizacin y medicindetiempos(cronometraje)contempladosenestaprctica.

EstructuradeComputadores 14

Responderalassiguientespreguntasdeautocomprobacin:
1 2 3 4 5 6 Preguntasdeautocomprobacin:suma_09_Casm Repasarelcdigoensambladorgeneradoporgcc paralastresversiones.Hayalgunadiferencia? Enlaversin3sehaaadidounclobberqueantesnoestaba(verFigura10).Acasonosirveparanadaeseclobber? Nohaydiferenciasenelcdigoensambladorgenerado? Enlaversin3sehanescritolosregistroscondossmbolos%,enlugardeuno,comoanteriormente(verFigura10). Qupasasiseescribencomoantes?Porqunopasabaesoantes? Cuntoselementostieneelarray?Cuntamemoriaocupa?Cuntovalelasuma,qufrmulaseusaparacalcular unasumacomoesa?Cmosellamanesetipodesumas? ElcdigoCimprimeunmensajediciendoN*(N+1)/2=,peroluegocalcula(SIZE1)*(SIZE/2).Culeslafrmulacorrecta? Esalneavienecomentadacon/*OF*/.Qupuedesignificaresecomentario? Qusepuededeciracercadelaforma deescribiresafrmula?Siesporincomodidadparacalcularlafrmula,qusepodrahaberhechoparaevitarde golpecualquierincomodidad?Cmoseescribiraentonces,mscmodamente,lafrmula,ytodalainstruccinprintf? Enlafuncincronocmoseleeeltipodelprimerargumento?Quformatoprintf seusaparaimprimirelsegundo? Porqusepasaporreferenciaelprimerargumentodegettimeofday?PorquseponeaNULLelsegundo?Porqu semultiplicapor1E6unadelasrestas,ylaotrano?Porquelprimerformatoprintfacabacon\t,enlugardecon\n? Qusignificaelformato%9ldusadoenelsegundoprintf,porqunoseusa%9d,osencillamente%d? Hayalgunaesperanzadeganaragcchaciendoeltipodecosasquevenimoshaciendoconsuma?(preguntaretrica)

Tabla10:preguntasdeautocomprobacin(suma_09_Casm)

4 Trabajo a realizar
Nos interesa experimentar con ejemplos que permitan obtener ventaja sobre gcc, y que al mismo tiempo sean lo suficientemente sencillos como para estudiarlos y programarlos en pocas sesiones de prcticas.Oanmejor,quenorequieranestudioadicional.Estascondicioneslascumplenporejemplo: elclculodeparidad,queyasepostulaenellibrodeteoracomobuencandidatoparaello;eldelpeso Hamming o population count, visto tambin en clase de teora, para el cual existe una instruccin SSE4.2 cuyo uso gcc no podr deducir a partir de nuestro cdigo C; y la multiplicacin de matrices, algoritmo que no requiere explicacin adicional, y para el cual daremos pistas sobre las instrucciones SSSE3ySSE4aprovechables,alobjetodeguiar,orientaryacelerarlalecturadelmanualdelrepertorio deinstrucciones.Setrataportantodeprogramarvariasversionesdecadaunadeesasfunciones: Sumarlasparidadesdetodosloselementosdeunarray SumarlospesosHamming(bitsactivados)detodosloselementosdeunarray Calcularelproductodedosmatricescuadradasdenmerosenteros

conysinensambladorenlnea,ycomprobandosiemprelacorreccindelresultadocalculado,paralo cual har falta una frmula aplicable a los datos de entrada utilizados. Para el producto de matrices aceptaremoscomovlidocomprobarnicamentelasesquinasdelamatrizresultado,comparandocon algnpaquetesoftwaredeAlgebraLineal.Sedebencronometrardeformajustayequitativatodaslas versiones. Una vez desarrollado el programa, repetiremos 10 veces la ejecucin para promediar los tiemposdeejecucindecadaversin,yrepetiremoselestudioparadistintosnivelesdeoptimizacin. Lostiempospromediadossepuedenpresentarenunagrficadepaqueteofimtico(CalcoExcel). SepropondrcomenzarconunprogramanormalenC(laversinmsinmediataposible),ycontinuar con mejoras que no requieran ASM, si las hubiera. Cuando no se pueda mejorar ms en lenguaje C, pasaraASMenlnea.EnellaboratoriodisponemosdeprocesadoresconSSE3(noSSE4),mientrasque muchosestudiantesdisponendeporttilesconSSE4.Sepedirportantorealizaralgunaversinquese pueda usar en el laboratorio, y se sugerir explorar alguna otra que aproveche las capacidades superioresdelosporttiles,inclusoaunquenosepuedaejecutarsobrelosequiposdeprcticas. Segn la temporizacin de cada curso, se procurarn realizar guiadamente (como Seminario Prctico) los Ejercicios 16 aproximadamente (incluso 79 si sobrara tiempo). Aunque no diera tiempo a tanto, responderalaspreguntasdeautocomprobacin,comprenderlosprogramasmostradosyejercitarseen el uso de las herramientas son competencias que cada uno debe conseguir personalmente. Para aprender el funcionamiento de nuevas instrucciones (sean o no del repertorio SSE) basta con leer el manual y probarlas en la propia sentencia ASM inline (y depurarlas con ddd, si no produjeran el resultadoesperado).

EstructuradeComputadores 15

Alobjetodefacilitareldesarrolloprogresivodelaprctica,sesugiererealizarenordenlassiguientes tareas:

4.1 Contestar las preguntas de autocomprobacin (suma_01suma_09)


Elobjetivoescomprendercondetalleelprocesodeensamblado,compilacinyenlacedelosprogramas mixtos CASM, utilizar con soltura las herramientas implicadas (incluyendo objdump y nm), entender cundoyporquhacefaltaenlazarlalibreraC,elruntimeCyelenlazadordinmico,entendercundo convieneusaras/ldycundogcc,comprenderlaconvencincdecl,sercapazdeleer(comprender)y redactarcdigoASMenconvencin cdeclyenlnea(inlineASM),yadquirirhabilidadenelmanejode lasherramientasusadas(compilador,ensamblador,enlazadorydepurador).

4.2 Calcular la suma de paridades de una lista de enteros sin signo


Utilizarelprograma suma_09delaFigura12comoesqueletoparacronometrardiversasversionesde una funcin que sume las paridades de los elementos de una lista de N nmeros, calculadas como el XOR (lateral) de los bits de cada elemento. Notar que la suma puede llegar a ser N, si todos tienen paridad impar (y producen XOR lateral 1). Concluir que basta calcular la suma en un entero, para cualquiervalorprcticodeN. Para poder comparar tiempos ser necesario que todos usemos los mismos datos de entrada, que arbitrariamente fijaremos como: SIZE=220 elementos, inicializados en secuencia desde 0 hasta SIZE1. Calcularqusumadeparidadestieneeseejemplo(enfuncindeSIZE). RealizarunaprimeraversinCquerecorraconunbucle forelarray,yconotrobucle foranidadolos bitsdelelemento,extrayndolosconunamscaraAND(0x1)ysucesivosdesplazamientosaladerecha (delelementodelarray),yacumulndolosconXOR(^=)enunavariableparidad.Unavezcalculadala paridad,seacumulara(consumanormal,+=)enotravariablecontador,enelbucleexterno. Paralasegundaversin,yahemosvistoenclase(ejemplo popcount[1])queelbucle for noesbuena eleccinenestoscasos,siendoventajosoel while,quepuededejardeiterarencuantoseacabenlos bitsdelelemento.Compararamosconlostiemposdelaversin1paraconstatarqueavecessepueden obtenerbuenasgananciaspensandobienlascosasenC,sinnecesidaddeusarASM. Realizar una tercera versin sustituyendo todo el bucle interno while por un tramo de unas 911 instruccionesensamblador,basadasenaprovecharTESTySETcc (ver[1]).ConsultarelmanualdeTEST paracomprobarquefectotienesobrePF(yZF),yelmanualdeSETccparaelegirculmnemotcnico nos interesa usar para extraer la paridad del ltimo byte (LSB) de un elemento del array. Comprobar tambin en el manual que SETcc no afecta a ZF, de manera que el ltimo TEST sirve tambin para comprobarsiquedanbitsenelelementodelarray,encuyocasotendramosquedesplazarunbyteala derechayrepetirlaoperacin,acumulando(XOR)laparidadnuevaconlaanterior.Alsalirdelbloque asm,sesumaralaparidadfinalalavariablecontador,enlenguajeC(+=).Compararamosconelcrono (tiempo de ejecucin promedio) de la versin 2, para ver la ganancia por usar ASM, cuando existen instruccionesocaractersticasquegccnosabeaprovechar. La cuarta versin consiste en implementar toda la funcin con un bloque asm (68 lneas ASM adicionales)porseguircomprobandosideverdadestandifcilganara gcc enbucles.Compararamos conelcronodelaversin3paravercmodebuenossomossacndoleventajaagccenbucles. Como tambin nos interesa saber cmo mejora gcc segn el nivel de optimizacin (-O0, -O1, -O2), repetiremoslas10medicionesparaalmenosesos3niveles.Setrataportantoderecompilar3veces, repetir 10 mediciones, y organizar los resultados en una grfica de paquete ofimtico (Calc o Excel), mostrando los promedios de cada versin (14) para cada nivel de optimizacin (02), tal vez con un grficodebarrasconabscisasbidimensionalesversinoptimizacin(Excellodenominacolumnas3D). Enprincipio,noseesperaquevarenlospromediosdelaversin4(ypocolosdela3)conelnivelde optimizacin,dadoquedejamospocoonadadecdigoCparaquegccseesmere.Squeseesperaque con nivel O0 la versin 3 sea ms lenta, porque no se optimiza el acceso a variables en memoria (consultar el ensamblador generado).Anas, en distintos modelos deordenadorpodremos observar diferentes(avecessorprendentes)prestacionesrelativas.

EstructuradeComputadores 16

Recordarquesiempresedebecomprobarqueelresultadoescorrecto.Unaoptimizacinqueproduce un resultado distinto slo tiene tres explicaciones: o est mal el programa optimizado, o est mal el original,oestnmalambos.
1 Cuestionessobreparity.c Paralaversin3seguramentesedeseeusarcomorestricciones: [res]"=r"(res):[e]"r"(elem):edx,siendoresla variable paridad calculada en ASM, elem el elemento accedido en esa iteracin del bucle, y EDX el registro clobber escogidocomovariablelocal(sepuedenusarporejemploDHparaextraerlasparidades,yDLparaacumularlas).Qu riesgosecorresinoseespecificaEDXenlalistaclobber?Comprobarlocomparandoelensambladorgeneradoconysin clobber,yejecutandoesteltimoprograma. Siguiendoconlaversin3:Porqurestringirres aunregistro,enlugardedejarloenmemoria?Sepodrarazonarque, sialfinyalcabosloseaccedeunavezynovarias,noseobtieneventajaportraerloaunregistro.Dependiendodela forma en que se declare res en lenguaje C (y las instrucciones que se escojan para modificar su valor), puede ser obligatorio o conveniente (o a lo mejor indiferente) que la restriccin sea r. Cul es el caso? Comprobarlo recompilandoelprogramay/ocomparandolostiemposdeejecuciny/oestudiandoelensambladorgeneradoporgcc. Paralaversin4seguramentedesearemos mencionar tres registros enlalistadesobrescritos.Cules?Porqu?Es realmente necesario usar lista clobber? Funciona el programa si se quita entera, o si se quita alguno de los tres registros?Porqu? Paralaversin4,puedeserconvenientesumardesdeelltimoelementohastaelprimero,porqueasseahorrauna instruccin.Cul?Porqu?Qucondicindeparadasepondraentonces(qusaltocondicional)?

Tabla11:preguntasdeautocomprobacin(parity.c)

4.3 Calcular la suma de bits de una lista de enteros sin signo


Utilizarelprograma suma_09delaFigura12comoesqueletoparacronometrardiversasversionesde unafuncinquesumelosbits(pesoHamming,popcount)deloselementosdeunalistadeNnmeros. Notar que la suma puede llegar a ser 32*N (en modo 32bits, donde un entero ocupa 4B), si todos valieran2321(yporconsiguientetuvieranactivadostodoslosbits).Concluirquebastacalcularlasuma enunentero,paracualquiervalorprcticodeN.CmodegrandepuedeserNendichopeorcaso? Para poder comparar tiempos ser necesario que todos usemos los mismos datos de entrada, que arbitrariamente fijaremos como: SIZE=220 elementos, inicializados en secuencia desde 0 hasta SIZE1. Para comprobar la correccin del resultado se podra por ejemplo crear una pequea funcin nbits popcnt(nbits)quecalcularalasumadebitsdetodalasecuencia0..2 1.Qurazonamientose puedeaplicarparacalcularelresultadodedichasuma?Porquhacemosunafuncin,enlugardeuna frmulacerrada,comoenelcasoanterior? Realizar una primera y segunda versiones como en el ejemplo anterior, recorriendo el array con un bucle for, y los bits con bucle for (1 versin) o while (2 versin), aplicando mscara 0x1 y desplazamiento a la derecha, para ir extrayendo y acumulando los bits (ver [1]). La diferencia es que antes acumulbamos bits con XOR (^=) para calcular la paridad, y ahora todo se acumula con sumas (+=),pudindoseusarunanicavariablecontador.Compararamoslostiemposparaconstatarquea vecessepuedenobtenerbuenasgananciaspensandobienlascosasenC,sinnecesidaddeusarASM. Realizarunaterceraversinsustituyendotodoelbucleinterno while poruntramodeunas57lneas ensamblador, basadas en la instruccin ADC (o JC/JNC) que ya utilizamos en la prctica anterior. El razonamientoconsisteenquetenemosquedesplazardetodasformas,ysielbitdesplazadoacabaenel acarreo (consultar el manual de SHR), de ah mismo lo podemos sumar, y nos ahorramos aplicar la mscara. En principio, debera suponer alguna mejora sobre las versiones 12. El resultado podra sorprendernos,segnelmodelodeordenadorusado. Implementar como cuarta versin la solucin que aparece en el libro de clase [1], problema 3.49, resueltoenlapgina364(lenguajeC).Vieneresueltopara64bits,bastaraconadaptarloa32bits.Esta cuartaversinnosdemostrara,casodesermejor,lodifcilqueesganaraunprogramaCbienpensado. Paraunaquintaversin,sepuedebuscarconGooglequotrosmtodoshanusadoalgunosentusiastas para calcular el popcount, e implementar alguno de ellos (ver [5], instruccin SSSE3 PSHUFB). Compararamos con el crono de las versiones 23 para ver cunto se gana por pasar del repertorio normalaSSSE3.Lasiguientepginasededicaaexplicarelmtodo[5].

EstructuradeComputadores 17

ParacomprenderestemtodoSSSE3convieneconsultareldibujoqueacompaaalapginademanual dePSHUFB,laoperacindebarajemscortadelrepertorioSSSE3.LosregistrosXMM(XMM0XMM7) sonde128bits,yestnpensadosparaalmacenarenparalelovarioselementos,porejemplo4enteros de32bits(4intsx25bits/int=27bits=128bits),8shorts,o16chars(24x23).Laoperacindebaraje permitebarajaresoselementos(comosifuerancartasdeunabaraja),indicandoencadaposicindel registro destino qu elemento de la fuente deseamos que acabe all (en esa posicin del destino). Es fundamental advertir que no se indica a qu posicin va cada elemento (podramos equivocarnos y dejarhuecos),sinoquelementoterminaencadaposicin.Porfijarconceptos,lainstruccin pshufb %xmm1, %xmm2barajalos16bytesdeXMM1,colocandoelbytei(i=0..15)entodoslosbytesdeXMM2 dondepongai.Estonospermiterepetirelementosyqueotrossequedenfueradelresultado,locual puedeparecerantiintuitivoypocorelacionadoconbarajasdecartas.Notartambinquelosndicesde baraje (XMM2) son sobrescritos. Conviene releer este prrafo junto con el dibujo del manual hasta comprenderlaoperacindebaraje. Laideaparaacelerarelclculodelpopcountconsisteenprecalcularcuntosbitstieneactivadoscada nmero(hastaunlmitedado,porejemplode8bits:0tiene0bits,1y2tienen1bit,3tiene2bitshasta 255,quetiene8bitsactivados),yusarelpropionmerocomondiceenunatabla(msomenosgrande segn el lmite impuesto) en donde se almacenan esos resultados precalculados. El popcount de un elem=array[i](supongamos elem=255)esentonces Tabla[elem](=8).Aestetipodetabla,donde el dato disponible indexa el resultado deseado, se les suele llamar Tabla de Consulta (LookUp Table, LUT). Por ejemplo, una paleta de colores indexados es una LUT, porque el cdigo del color se usar comondice. Siguiendoconelejemplo Tabla[array[i]],setardamenosenaccederalelemento255delatabla (obteniendoresultado=8bits)quehacer8desplazamientos,mscarasyacumulaciones.Elinconveniente es que una tabla tan grande no cabe en un registro XMM. Pero si la limitamos a elementos de 4bits (medio byte, un nibble), s que podemos almacenarla en un registro XMM, en donde caben 16B. De hechonossobramsdelamitaddecadabyte,porquelaLUTslonecesita16elementosde3bits:16 porque calcularemos popcount de 4bits, y 3 porque el mximo son 4bits activados (0b100). Pero en SSSE3 no existe operacin de baraje con 32 nibbles, y aunque la hubiera, no nos interesa calcular popcountde5en5bits.Laoperacindebarajemscortaoperasobre16B,ynosotrosaprovecharemos slolamitaddecadabyte. Sepuederecorrerportantoelarrayde4en4elementos,cargando4enterosenunregistroXMMde 128bits(16B),repartiendosusnibblesentredosregistrosXMM(paraquetodoslosndicessalganentre 0..15), barajando con la tabla precalculada (LUT) para obtener cuntos bits hay activados en cada nibble,ysumandotodosesosnmeros.Seguramentedesearemosdeclararlasiguientetablaymscara:

int popcount5(unsigned* array, int len) { int i, res, count=0; static const int SSE_mask[] __attribute__ ((aligned (16))) = {0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f}; static const int SSE_LUTb[] __attribute__ ((aligned (16))) = {0x02010100, 0x03020201, 0x03020201, 0x04030302}; // 3 2 1 0 7 6 5 4 1110 9 8 15141312 for (i=0; i<len; i+=4) { asm("movdqa %[x], %%xmm0 \n\t" ... "movd %%xmm0, %[res] \n\t" : [res]"=m" (res) : [x] "m" (array[i]), [m] "m" (SSE_mask[0]), [l] "m" (SSE_LUTb[0]) ); count += res; } return count; }

Figura13:popcount5:esqueletodelafuncinparaclculoSSSE3delpesoHamming
EstructuradeComputadores

18

Notar que se han declarado static (para evitar su eliminacin con optimizacin) y alineadas a 16 (requisito para usar MOVDQA, consultar manual de Intel). Notar que todas las restricciones se han indicadoenmemoria,encargndonosnosotrosdelmovimientoexplcitoaregistros,paraqueeltramo ASM sea virtualmente idntico al de la web [5]. Notar por ltimo que el array se recorre de 4 en 4 elementos,yquedichorecorridoylaacumulacinsonlasnicastareasqueserealizanenC. Una hipottica sexta versin consistira en sustituir todo el bucle interno while por una nica instruccin SSE4. Para localizarla rpidamente se podra consultar en la Wikipedia [4] la lista de instrucciones SSE4, y restringir la lectura de los manuales de Intel a las instrucciones de dicha lista, o buscarenlospropiosmanualesdeIntelculessonlasinstruccionesSSE4.Compararamosconelcrono de la versin 5 para ver cunto se gana por pasar del repertorio SSSE3 a SSE4 (y a lo mejor nos volveramosasorprender).Atendiendoaqueestaversinnosepodraejecutarenellaboratorio,oen unporttilquenotengaSSE4,sedejatanslocomosugerencia. Denuevorepetiramos10medicionescon3nivelesdeoptimizacin(-O0, -O1, -O2),organizandolos resultadosenunagrficadepaqueteofimtico(CalcoExcel),mostrandolospromediosdecadaversin (15)paracadaniveldeoptimizacin(02).Enprincipio,lospromediosdelasversionesensambladorno deberanvariarmuchoconelniveldeoptimizacin,sloconO1sernalgopeoressitienensuficiente accesoavariablesenC. Recordarquesiempresedebecomprobarqueelresultadoescorrecto.Unaoptimizacinqueproduce un resultado distinto slo tiene tres explicaciones: o est mal el programa optimizado, o est mal el original,oestnmalambos.
1 Cuestionessobrepopcount.c Darunarespuestaprecisaalaprimerapregunta(primerprrafo):Enelpeorcaso,cuandotodosloselementostienen todoslosbitsactivadoscmodegrandepuedeserNsinquehayaoverflow,siacumulamoslasumadebitsenunint? Ysiseacumularaenununsigned? Programarlafuncinpopcnt(nbits)sugeridaenelsegundoprrafo.Cmoseharazonadoeseclculo?Porquhemos preferidonocalcularunafrmulaaritmticacerrada? Porqunecesitaremosdeclararlalistadeenteroscomo unsigned? Quproblemahabrasisedeclararacomoint? Notaramosennuestroprogramaladiferencia?Encasonegativoqutendraquesucederparanotarladiferencia? RealizarundibujodecmofuncionaunaiteracindelalgoritmoSSSE3,convaloresdeelementoquecausenqueseuse todalatablaLUT,preferiblementenoenorden(porqueentoncesnoquedaraclaralaoperacindebaraje).

2 3 4

Tabla12:preguntasdeautocomprobacin(popcount.c)

4.4 Calcular el producto de matrices cuadradas de nmeros enteros


Utilizarelprograma suma_09delaFigura12comoesqueletoparacronometrardiversasversionesde una funcin que calcule el producto de dos matrices cuadradas de NxN enteros. Notar que un nico productodeenterosyapuedeproduciroverflow,asquelasumadevariosparaobtenerunelemento delresultado,conmayormotivo. Para poder comparar tiempos ser necesario que todos usemos los mismos datos de entrada, que arbitrariamentefijaremoscomo:NxN=28x28elementos,inicializadosensecuenciadesde0hastaMODK 1=1001=99enordenmayordefila(rowmajororder).Porejemplo,conMODK=100,seinicializarala primerafiladeambosarraysa[0..99,0..99,0..55],empezandolasegundafilaconelvalor56.Sepuede introducireseejemploenalgnsoftwaredeAlgebraLineal(tipoMATLAB/Octave)paradescubrirquelas esquinas de la matriz resultado C=A*B son: 556780, 591580, 593980, 613180. Usaremos esos datos comocomprobacin(mnima). Realizarunaprimeraversinqueuselostrestpicosbucle forparacalcularelresultado.Pistaparala segundaversin:yahemosvistoenclase deteora(accesoamatrices)elusodepunterosfrenteala indexacin.Compararamosconelcronodelaversin1paraconstatarqueavecessepuedenobtener buenasgananciaspensandobienlascosasenC,sinnecesidaddeusarASM. Unaterceraversin,inspiradaenlasegunda,seracopiarlacolumnaqueseestaccediendoaunvector temporal,paraaccederloconstride1(saltodeunoenuno),enlugardeN(saltandopunterosdeunafila alasiguiente).Yaquesevaacopiarlacolumna,convienehacerseguidostodoslosclculosrelacionados

EstructuradeComputadores 19

conella,demaneraquehabrquereordenarlosbucles.Esomejoralalocalidadespacialdelafuncin(y latasadeaciertosdecache).Compararamosconelcronodelaversin2paraconstatarqueaveces puedemerecerlapenarepensardosvecesbienlascosasenC,antesqueusarASM. Lacuartaversinqueproponemospararealizarenellaboratorioconsisteensustituirelbucleforms internoporunbloqueASMdeunas2425lneasaprovechandolainstruccinSSE2PMULUDQ(queslo hacelamitaddetrabajoquelainstruccinSSE4PMULLD)yotrasdelrepertorioSSSE3.Compararamos con el crono de la versin 3 para ver cunto se gana por pasar de repertorio normal a SSSE3. Dependiendodelmodelodeprocesador,podemosobtenerresultadosquenossorprendan. ParaacelerareldesarrollodeestaversinSSSE3,enlaFigura14seofreceunaposibleimplementacin, comentando que pueden ser necesarias algunas de las siguientes instrucciones: movd/movdqa/ movdqu, pxor/paddd/phaddd, punpckldq/punpckhdq/pmuludq/psignd, y otras del repertorio normalIA32como add/dec/jnzetc,segnseimplementenlosbucles.Podransalirunas25lneasde cdigoinline,delascualessesugierenunas19enlaFigura14.Lasinstrucciones phaddd/psigndson delrepertorioSSSE3,elrestosonSSE2.Convieneestudiarlascorrespondientespginasdemanualde Intel.

void matmult4(matriz A, matriz B, matriz C) { int i,j,k; int *Ap,*Bp; int res; static int colB[SIZE] __attribute__ ((aligned (16))); static const int SSE_mask[] __attribute__ ((aligned (16))) = {1,0,1,0}; for (j=0; j<SIZE; j++) { for (i=0; i<SIZE; i++) colB[i] = B[i][j]; for (i=0; i<SIZE; i++) { Ap = A[i]; Bp = colB; k = SIZE>>2; asm("pxor %%xmm0, %%xmm0 "pxor %%xmm1, %%xmm1 "movdqa %[msk], %%xmm6 "bucle4: "movdqa (%[Ap]), %%xmm2 "movdqa %%xmm2, %%xmm3 ... "punpckldq %%xmm1, %%xmm2 "punpckhdq %%xmm1, %%xmm3 ... "pmuludq %%xmm2, %%xmm4 ... "psignd %%xmm6, %%xmm4 ... "phaddd ... "paddd ...

\n\t" \n\t" \n" \n\t" \n\t" \n\t" \n\t" \n\t" \n\t" \n\t"

// acum = 0, 0, 0, 0 //interlve 0, 0, 0, 0 // mask = 0, 1, 0, 1 // 2 copias A3.A2.A1.A0

// interlve 00.A1.00.A0 // interlve 00.A3.00.A2 // res64bit A1*B1.A0*B0 = C1hl.C0hl // res32bit 00.C1.00.C0 8->4res C3.C2.C1.C0

\n\t" // \n\t" "\n\t" "... ...,%[Ap] \n\t" // "... ...,%[Bp] \n\t" // "... %[k] \n\t" "jnz bucle4 \n\t" "\n\t" "phaddd ... \n\t" // "phaddd ... \n\t" // "movd %%xmm0, %[res] \n\t" // : [Ap] "+r" (Ap), // inouts [Bp] "+r" (Bp), [k] "+r" (k), [res] "=m" (res) // output :[msk] "m" (SSE_mask[0]) // input ); C[i][j] = res;

Aritmtica directa, no de punteros

4res->2res(2res altos=0) 2res->1res sacar ese resultado

} } }

Figura14:matmult4:esqueletodelafuncinparaclculoSSSE3delproductodematrices
EstructuradeComputadores

20

Lainstruccin pmuludq %xmm1, %xmm2consideraquelosregistrosXMMde128bitsalmacenanslo2 enterosde32bits(no4)enlasposiciones0y2(dejandohuecaslasposiciones1y3),realizaenparalelo losdosproductos32x3264bits,ysobreescribeelresultadoenelregistrodestinocomo2cudruples palabrasde64bits(consultarlapginademanualdeIntel).Desearemosutilizarlainstruccin psignd con una mscara, para eliminar la parte alta del producto 32x3264bits, y phaddd para sumar horizontalmentelosdosproductoscalculadosencadaregistroXMM. Notarcmoenlasrestriccionessugeridassedejanenmemorialosdatosquesloseaccedenunavez, mientrasquelosqueparticipanenelbucleserestringenaregistros. Naturalmente, se puede probar cualquier otra idea que intente acercarse ms a las prestaciones de PMULLD (que realiza 4 productos 32x3232bits en paralelo). Lo mximo a que podra aspirar PMULUDQseraalamitaddevelocidad,silasinstruccionesadicionalesquehayqueaadirpararetocar elresultadonogastaranningntiempodeCPU. Se podra realizar una quinta versin sustituyendo el bucle for ms interno por cdigo SSE4 aprovechandolainstruccinPMULLD,quepuederealizar4productosdeenterosalavezenunregistro XMMde128bits(4x32bits).Compararamosconelcronodelaversin3paraverlagananciaporpasar delrepertorionormalalSSE4.Perodenuevo,enellaboratorio(yenmuchosporttiles)nodisponemos deSSE4,asqueslosecomentacomosugerencia. Para guiar la bsqueda de las instrucciones requeridas para esta versin SSE4, comentaremos que pueden ser necesarias algunas de las siguientes instrucciones: movd/movdqa/movdqu, pxor/paddd/ phaddd, pmulld,yotrasdelrepertorionormalIA32como add/dec/jnzetc,segnseimplementen losbucles.Podransalirunas1213lneasdecdigoinline. Seguramenteinteresarpasarlospunterosconrestriccinderegistro,parapodercargarymultiplicar losregistrosXMMconlosvaloresapuntadosporellos.Con movdqu nodebehaberproblema,perosise usa movdqa seguramente har falta consultar en el manual de gcc cmo especificar el atributo de variable aligned (y leer con atencin la instruccin movdqa en el manual de Intel). En el mencionado ejemplo SSSE3 para popcount ([5]) hay enlaces a cdigo mixto Casm inline usando esa instruccin y atributos. Denuevorepetiramos10medicionescon3nivelesdeoptimizacin(-O0, -O1, -O2),organizandolos resultadosenunagrficadepaqueteofimtico(CalcoExcel),mostrandolospromediosdecadaversin (14)paracadaniveldeoptimizacin(02).Enprincipio,lospromediosdelasversionesensambladorno deberanvariarmuchoconelniveldeoptimizacin,sloconO1sernalgopeoressitienensuficiente accesoavariablesenC. Recordarquesiempresedebecomprobarqueelresultadoescorrecto.Unaoptimizacinqueproduce un resultado distinto slo tiene tres explicaciones: o est mal el programa optimizado, o est mal el original,oestnmalambos.Lacomprobacinqueusamosenesteejemploesbastanteendeble,pero anas,mejoresoquenocomprobarnada.
1 Cuestiones(msbienpistasadicionales)sobrematmult.c PorqusabemosquelosdatosacargarenunregistroXMMdebenestaralineados?Aqutamao?Dndelopone? Qu pone exactamente? En qu seccin del manual gcc se explica cmo alinear variables? Hacer un dibujo de la operacin realizada por las instrucciones SSE2 de movimiento MOVD/MOVDQA/MOVDQU. Concluir su conveniencia para cargar en un registro XMM 4 enteros de 4B (que podrn ser luego operados en paralelo con posteriores instrucciones SSE), y para almacenar en memoria la parte menos significativa de un registro XMM (posiblemente el resultadofinaldelafuncin). Hacer un dibujo de la operacin realizada por las instrucciones SSE2 aritmticolgicas PXOR y PADDD. Concluir su convenienciaparainicializaryactualizarunregistroXMMquesedesearausarcomoacumulador. Hacer dibujos de las operaciones realizadas por las instrucciones SSE PMULLD (SSE4.1) y PMULUDQ (SSE2). Explicar grficamente la necesidad de eliminar la parte superior de los resultados de sta ltima, al objeto de que ambas calculen la misma operacin. Concluir que, si ambas instrucciones tardan el mismo nmero de ciclos, la versin 4 tardarcomomnimoeldobledetiempoquela5,aigualdaddelrestodeinstrucciones. ExplicargrficamentetambinlaconvenienciadePHADDDyMOVDparaconcentrarelresultadoenunnicoenterode 4B.Pista:hacefaltaotroregistroadicionalpuestoa0.JuntoconunaspocasinstruccionesIA32(incrementarpunteros, decrementarcontador),ladescripcingrficadelaversin5puedeconsiderarsecompletaconestas4preguntas.

2 3

EstructuradeComputadores 21

ExplicargrficamentelautilidaddelainstruccinSSSE3PSIGNDparaeliminarlosresultadossuperioresdePMULUDQ, segn se coment en la pregunta 3. Calcular la mscara necesaria para conseguirlo. Recordar que en memoria los registros XMM tambin se almacenan en orden littleendian, as que tener especial cuidado al indicar cmo se declararalamscara(consusvaloresinicializados)enlenguajeC.Recordartambinelatributodealineamiento,para podercargarlaenunregistroXMM.Probablementedeseemosusarenestecasounarestriccindeentradadememoria, alestilode:[msk]"m"(SSE_mask[0]),suponiendoquesedeseenesosnombresparaelarrayCyelrecursoASM. Explicargrficamentelautilidaddelainstrucciones SSSE2PUNPCKLDQ yHDQ,enconjuncinconalgnregistroXMM puesto a 0, para reorganizar los 4 enteros de 4B cargados como se dijo en la pregunta 1, de forma que se les pueda aplicar PMULUDQ para nuestro propsito. Posteriormente se les aplicara PSIGND como se vi en la pregunta 5. Posteriormente,PHADDDcomosevienlapregunta4.Laiteracindelbucleenensambladorsepuedeconcluircon unainstruccinPADDD,comosevienlapregunta2. LasentenciaASMparalaversinPMULUDQ sepuedeconcluircomoseindicaenlapregunta4 paraPMULLD.Juntocon unas pocas instrucciones IA32 (incrementar punteros, decrementar contador), la descripcin grfica de la versin 4 puedeconsiderarsecompletaconestaspreguntasadicionales.

Tabla13:preguntasdeautocomprobacin(matmult.c)

5 Entrega del trabajo desarrollado


Los distintos profesores de teora y prcticas acordarn las normas de entrega para cada grupo, incluyendoqusehadeentregar,cmo,dndeycundo.Porejemplo,puedequeenungruposedeba entregarenundocumentoPDFlasrespuestasalaspreguntasdeautocomprobacin,loslistadosdelos programas realizados (parity.c, popcount.c, matmult.c), y las grficas de las mediciones de tiempo, subindolo al SWAD hasta 3 das despus de la ltima sesin de prcticas dedicada a esta prctica,conpenalizacincrecienteporentregatardahasta1semanaposterior. Puede que en otro grupo se pueda trabajar y entregar por parejas, pero que el profesor de prcticas visite cada puesto al final de cada sesin comprobando si ambos estudiantes saben responder a las preguntas,programarenensambladorenlneayutilizarlasherramientas,permitiendoquesesubaal SWADeltrabajoencasoafirmativo. Los profesores de teora y prcticas de cada grupo acordarn cmo entregar ese grupo el trabajo desarrollado.

6 Bibliografa
[1] Apuntesypresentacionesdeclase,yparticularmente ProgramacinMquinaII:AritmticayControl seccinBucles,p.38ysiguientes seccinCdigosdecondicin,instruccionestest/setcc,p.2225 LibroCS:APP,Problema3.49,p.364 [2] ManualesdeIntelsobreIA32eIntel64,enconcretoelvolumen2:InstructionSetReference http://www.intel.com/content/dam/doc/manual/64ia32architecturessoftwaredevelopervol 2a2binstructionsetazmanual.pdf [3] Wikipedia,convencionesdellamada http://en.wikipedia.org/wiki/Calling_convention X86callingconventions http://en.wikipedia.org/wiki/X86_calling_conventions WikiBook http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Conventions [4] Wikipedia,extensionesx86MMX/SSE:http://en.wikipedia.org/wiki/X86#Extensions MMX http://en.wikipedia.org/wiki/X86#MMX SSE,SSE2,SSE3,SSSE3,SSE4 http://en.wikipedia.org/wiki/X86#SSE [5] CdigoSSSE3parafastpopcount http://0x80.pl/articles/ssepopcount.html [6] GASmanual http://sourceware.org/binutils/docs/as/index.html 9.13:80386depend.features http://sourceware.org/binutils/docs/as/i386_002dDependent.html [7] GCCmanualv.4.4(ladellaboratorio) http://gcc.gnu.org/onlinedocs/gcc4.4.6/gcc/ 5:ExtensionstoCLanguage http://gcc.gnu.org/onlinedocs/gcc4.4.6/gcc/index.html#toc_CExtensions 5.33:Variableattributes http://gcc.gnu.org/onlinedocs/gcc4.4.6/gcc/VariableAttributes.html 5.37:AssemblerwithCoperands http://gcc.gnu.org/onlinedocs/gcc4.4.6/gcc/ExtendedAsm.html 5.38:Constraints http://gcc.gnu.org/onlinedocs/gcc4.4.6/gcc/Constraints.html#Constraints [8] GCCInlineAssemblyHOWTO http://www.ibiblio.org/gferg/ldp/GCCInlineAssemblyHOWTO.html

EstructuradeComputadores 22

6:Moreaboutconstraints http://www.ibiblio.org/gferg/ldp/GCCInlineAssemblyHOWTO.html#s6 [9] LinuxAssemblyHOWTO http://tldp.org/HOWTO/AssemblyHOWTO/index.html 3.1:GCCinlineassembly http://tldp.org/HOWTO/AssemblyHOWTO/gcc.html BrennansGuidetoinlineasmhttp://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html 5.1:Linuxcallingconventions http://tldp.org/HOWTO/AssemblyHOWTO/linux.html [10] Sourceforgetutorials http://asm.sourceforge.net/resources.html#tutorials UsingasminLinux http://asm.sourceforge.net/articles/linasm.html#InlineASM Inlineasmx86IBM http://www.ibm.com/developerworks/linux/library/lia OtraBrennansSETI@Home http://setiathome.ssl.berkeley.edu/~korpela/djgpp_asm.html Miyagisintrotexto http://asm.sourceforge.net/articles/rmiyagiinlineasm.txt

EstructuradeComputadores 23

Das könnte Ihnen auch gefallen