domingo, noviembre 27, 2005

 

Convenciones de llamada y parametros variantes

Aunque debo admitir, que no investigué mucho, las principales diferencias entre MIPS e INTEL, al menos las que puedo señalar empiricamente, es, por supuesto, que INTEL pasa parámetros via stack, mientras que MIPS dispone de cuatro registros especiales para ello. Aunque Tambien es cierto que en MIPS podría hacerse todo el paso de parametros por medio del stack. No se para que, pero alguien podría estar usando esos cuatro registros para otra cosa.

Fuera de eso, lo más que sé al respecto, es que, MIPS es arquitectura RISC, mientras INTEL es CISC. Eso significa que MIPS posee un juego de Instrucciones reducido, mientras que Intel, tiene uno extendido. En lo que a mi respecta, eso significa que Intel tiene instrucciones equivalente a la secuencia de Varias instrucciones, lo cual por ejemplo, permite que lo que MIPS hace en dos o más instrucciones, INTEL lo puede hacer en una sola instruccion atómica.

En Realidad, el tema RISC vrs CISC es sumamente extenso, dado que se trata de filosofías de diseño, y como tales, tienen ventajas y desventajas...

En cuanto a la segunda pregunta, pues, si, en efecto hice el programa, pero solo fue "un vil copy-paste" de un ejemplo que encontre en MSDN.

De modo que decidí hacer algo parecido, pero escarbando un poquito, usando direcciones de memoria para ver algunas cosas...

Lo primero que hice, es ver la direccion de memoria de los parámetros, para constatar sus posiciones relativas en la menmoria. Pude confirmar, que C, introduce primero el último parámetro, dejando el primero más cerca del tope del stack. En el ejemplo que proporciono se puede ver como second está en la direccion de memoria siguiente a first.

Eso me indujo a mi siguiente experimento. Dado el procedimiento cuyos parámetros son todos de tipo int, es posible declarar un int*, y asignarle la direccion del primer parámetro. El resultado, si se tiene:
int * ptr = &primerParam
Entonces se puede decir:
ptr[0] = ///
ptr[1] = ///
/// = ptr[n],
donde ptr[n] es el (n+1)-ésimo parámetro.

En mi experimento, pude accesar todos los parámetros, indistintamente si éstos eran fijos o variantes.

ABRE-PARENTESIS

No hay manera de saber cuantos parámetros se han enviado, al menos desde el lado del callee, si no se utiliza algun terminador, bandera, valor especial, ... etc. ni siquiera mediante las macros va_start, va_arg, o va_end (que no se que hace por cierto), y esto, aun en las versiones ansi y unix.

Solamente el caller "sabe exactamente" cuantos argumentos utilizó en la llamada.

Aqui entra en juego el asunto de las CALLING_CONVENTIONS.
En las lecturas que hice, encontré lo siguiente:

hay basicamente tres: __stdcall, __cdecl, y __fastcall.

En realidad no encontré una diferencia entre las dos primeras, excepto que en __cdecl, el caller es el responsable de sacar los parametros del stack una vez que la llamada retorna; no así en __stdcall, donde es el callee el encargado.

En cuanto al orden de los parámetros en la pila, experimenté con __stdcall y con __cdecl, en ambas obtuve las mismas direcciones relativas entre parámetros, i.e. no vi ningun cambio, el primer argumento está siempre mas cerca del tope de la pila que los demás.

De hecho, eso también lo leí por ahi en mis averiguatos.

En lo que más relevancia encontre que la diferenciacion era importante, es en el tema ya de enlace de símbolos (linking), donde tienen mucho que estudiar los que desarrollan DLL, (Dynamic Link Libraries). Aqui lo importante es que, si hay una disparidad entre convenciones de llamada, (que un lado utilice __stdcall y el otro __cdecl) la pila se ve destruida, pues bien, o nadie hace "pop", o ambos lo harán, ( y la máquina hace "puff" y uno queda con "cara de what?").

En un comantario sin importancia, hasta ahora sé porque hay que ponerle __stdcall a las funciones que uno usa en una DLL, jeje, por cierto, eso de #define, es mala manía, porque en las DLL uno usa DLLIMPORT, pero al final, #define DLLIMPORT __stdcall. (Ni los mareros usan tanto alias...)

La consistencia entre convenciones de llamada es también crucial en los casos en que una aplicación se desarrolla desde varios lenguajes.

en cuanto a __fastcall, basicamente utiliza registros para pasar parámetros, pero como no todos los registros estarán disponibles, a fin de cuentas el compilador terminará decidiendo cuales poner y cuales no...

El otro punto a que encontré, concerniente a convenciones, tiene tambien que ver con linking, esta vez con los nombres decorados o "mangling", que es cuando los editores de enlace (equivalente de linkers) cambian los nombres de las funciones, "decorandolos" con simbologías que indican tipo de convencion de llamada, tipo y/o numero de parametros, etc. El "name mangling" depende del diseño del linker.

CIERRA-PARENTESIS

Tambien quise ver las variables locales, y constaté que se menten en la pila en el orden en que se declaran, y despues que los argumentos, es decir asi:

(direcciones bajas/tope)
...
variables locales
8 Bytes que no se que son
arg1
arg2
...
argn
...
(direcciones altas/fondo de pila)


Codigo Usado en el experimento final:

____________________________________________

#include
#include
#include

int recorre4(int first, int second, int third...){
printf("&first =%i\n&second=%i\n&third =%i\n",&first, &second, &third);
int a,b,c,d;
a = 1;
b = 2;
c = 3;
d = 4;
printf("\n&a=%i\n&b=%i\n&c=%i\n&d=%i\n",&a,&b,&c,&d);
int* ptr; ptr = &first;
printf("&p=%i\n",&ptr);
int x,y,z;
printf("\n&x=%i\n&y=%i\n&z=%i\n",&x,&y,&z);
printf("ptr:[%i][%i][%i][%i][%i][%i]\n",ptr[0],ptr[1],ptr[2],ptr[3],ptr[4],ptr[5]);
printf("ptr[7] = = = :%i\n", ptr[7]);
printf("ptr[6] = = = :%i\n", ptr[6]);
printf("arriba^5 de a:%i\n", *(&a+5));
printf("arriba^4 de a:%i\n", *(&a+4));
printf("arriba^3 de a:%i\n", *(&a+3));
printf("arriba^2 de a:%i\n", *(&a+2));
printf("arriba de a:%i\n", *(&a+1));
printf("arriba de b:%i\n", *(&b+1));
printf("arriba de c:%i\n", *(&c+1));
printf("exactamente c:%i\n", *(&c));
printf("debajo de c:%i\n", *(&c-1));
printf("debajo^2 de c:%i\n", *(&c-2));
printf("debajo^3 de c:%i\n", *(&c-3));
//return 224;
}

int __stdcall normal(int m, int n, int o){
int s, t, u;
printf("&m := %i\n", &m);
printf("&n := %i\n", &n);
printf("&o := %i\n", &o);
printf("&s := %i\n", &s);
printf("&t := %i\n", &t);
printf("&u := %i\n", &u);
}

int main(){
int r1 = recorre4(2,5,6,7,8,0);
printf(" retorna %i\n", r1);
int r2 = recorre4(2,5,6,7,8,0);
printf(" retorna %i\n", r2);
printf("\n");
system("pause");
normal(1,2,3);
system("pause");
return 0;
}

____________________________________________

SALIDA
____________________________________________

&first =37814056
&second=37814060
&third =37814064

&a=37814044
&b=37814040
&c=37814036
&d=37814032
&p=37814028

&x=37814024
&y=37814020
&z=37814016
ptr:[2][5][6][7][8][0]
ptr[7] = = = :4200443
ptr[6] = = = :37814112
arriba^5 de a:6
arriba^4 de a:5
arriba^3 de a:2
arriba^2 de a:4200463
arriba de a:37814112
arriba de b:1
arriba de c:2
exactamente c:3
debajo de c:4
debajo^2 de c:37814056
debajo^3 de c:4200624
retorna 22
&first =37814056
&second=37814060
&third =37814064

&a=37814044
&b=37814040
&c=37814036
&d=37814032
&p=37814028

&x=37814024
&y=37814020
&z=37814016
ptr:[2][5][6][7][8][0]
ptr[7] = = = :4200443
ptr[6] = = = :37814112
arriba^5 de a:6
arriba^4 de a:5
arriba^3 de a:2
arriba^2 de a:4200511
arriba de a:37814112
arriba de b:1
arriba de c:2
exactamente c:3
debajo de c:4
debajo^2 de c:37814056
debajo^3 de c:2147332096
retorna 25

Press any key to continue . . .
&m := 37814072
&n := 37814076
&o := 37814080
&s := 37814060
&t := 37814056
&u := 37814052
Press any key to continue . . .


____________________________________________

lunes, noviembre 14, 2005

 

Uso de atributos heredados en CUP(parte 2)

Confirmada mi sospecha, usar el código raro... funciona.
Esta vez utilicé una gramatica similar a la declaración de tipos en C/C++;
la cual es:

dec -> dec tipo lista ';'
dec -> tipo lista ';'
tipo -> entero
tipo -> real
lista -> lista ',' ID
lista -> ID

Las reglas semanticas embebidas en la gramatica, originalmente quedan:

dec -> dec tipo {lista.t = tipo.t} lista ';'
dec -> tipo {lista.t = tipo.t} lista ';'
tipo -> entero {tipo.t = entero}
tipo -> real {tipo.t = real}
lista -> {lista1.t = lista.t} lista ',' ID {declare(ID.lexema, lista.t);}
lista -> ID{declare(ID.lexema, lista.t);}

por la regla de copia, se sustituyen las referencias al atr.heredado por referencias al

atributo sintetizado del símbolo de orígen, por lo tanto, las producciones de lista

quedan:

lista -> lista ',' ID {declare(ID.lexema, lista.t);} // usando atr[top-3]
lista -> ID{declare(ID.lexema, lista.t);} // usando atr[top-1]

aca pueden ver el código de cup.
----------------------------------------------------------------
package EjemploH1;

import java.io.*;
import java_cup.runtime.*;

parser code {:

public static void main(String args[]) throws Exception {
try{
//FileReader fis = new FileReader("dec.txt"); // se construye el File reader
parser paspar = new parser(new Yylex(System.in));
paspar.parse();
System.out.println("Proceso terminado");
System.exit(0);
}catch(FileNotFoundException e){
System.out.println("Imposible abrir el erchivo especificado");
System.exit(1);
}
}
:}

action code {:

private void settype(String id, String tipo){
System.out.println("declare(" + id + ", tipo " + tipo + ");");
}

:}

terminal ENTERO, REAL, COMA, PUNTOYCOMA;
terminal String ID;

non terminal cSimbolo dec, tipo, lista;

dec ::= dec tipo lista PUNTOYCOMA;

dec ::= tipo lista PUNTOYCOMA;

tipo ::= ENTERO {:RESULT = new cSimbolo("entero"); :};

tipo ::= REAL {:RESULT = new cSimbolo("real"); :};

lista ::= lista COMA ID:i{:settype(i,((cSimbolo)((java_cup.runtime.Symbol)

CUP$parser$stack.elementAt(CUP$parser$top-3)).value).tipo);:};

lista ::= ID:i {:settype(i,((cSimbolo)((java_cup.runtime.Symbol)

CUP$parser$stack.elementAt(CUP$parser$top-1)).value).tipo);:};


----------------------------------------------------------------

Aqui un ejemplo de corrida:

----------------------------------------------------------------
entero lunes, martes, miercoles; real madrid, fantasia, cuento;
declare(lunes, tipo entero);
declare(martes, tipo entero);
declare(miercoles, tipo entero);
declare(madrid, tipo real);
declare(fantasia, tipo real);
declare(cuento, tipo real);
^Z
Proceso terminado
Press any key to continue...
----------------------------------------------------------------

y por si las dudas, el código de jFlex:
----------------------------------------------------------------
package EjemploH1;

import java_cup.runtime.Symbol;

/**
* Class YyLex, creada mediante jFlex a partir de minimal. lex.
* @author Gerson Lara
* @URL http://www2.cs.tum.edu/projects/cup/minimal.tar.gz
*/
%%
%cup
%ignorecase
%%
";" { return new Symbol(sym.PUNTOYCOMA); }
"entero" { return new Symbol(sym.ENTERO); }
"real" { return new Symbol(sym.REAL); }
"," { return new Symbol(sym.COMA); }
[A-Za-z]+ { return new Symbol(sym.ID, yytext()); }
[ \t\r\n\f] { /* ignore white space. */ }
. { System.err.println("Illegal character: "+yytext()); }

----------------------------------------------------------------

domingo, noviembre 13, 2005

 

Cambios en la carrera.

Recientemente {asist ( í | imos ) } a una reunión para informarnos a cerca de los cambios que se avecinan en el plan de estudios de la Carrera de Sistemas en Unitec.

Lo que queiro mencionar aquí es mi satisfacción por el hecho de que ahora se va a considerar la falla (ya mencionada antes) de la gente de sistemas para trabajar en grupos multidisciplinarios.

Me permito recordar que si bien es cierto, la carrera es eminenetemente técnica, eventualmete nos corresponderá fungir como administradores de algun departamento o empresa, deseablemente relativa al tema computacional. Y esto lo digo no con ánimos de insistir, sino de apoyar esa decisión en particular de la jefatura de la carrera.

En mi posición particular, el software administrativo, no me agrada (aunque quizá sea el que más venda), prefiero verme como parte de un equipo de desarrollo o investigación sobre nuevas tecnologías, o apoyar proyectos de investigación científica o algo así. Aquí inserto mi frase de "La computación por sí sola no sirve para nada". Digo esto porque (no lo tomen a mal pero,) un autómata ahi solito, sólo sirve para quebrarse la cabeza. Ahora, asocie ese autómata con um problema real, y al resolver el autómata, debería tener la solución del problema original. Suena obvio, y creo que lo es, sin embargo, al trasladar esto un poco más a flote, La gente de Sistemas tiende a pensar que con saber de Sistemas y computadores, bits y bytes, y si se queire cables y frecuencias basta. Pero y a la hora de desarrollar un proyecto que integre personas y tecnología? quién sabe de personas? De alumnos un docente, de pacientes el doctor, de empleados el de Recursos Humanos que con suerte es Psicólogo... La computación sirve cuando se le combina adecuadamente con otra ciencia, arte, deporte, empleo etc... El caso es que, con suerte( no se si buena o mala) me corresponderá algún día, liderar o administrar un grupo como esos, donde cada quien piensa que su propio gremio es lo máximo. ¿Cómo ponerlos de acuerdo?

Por eso creo que le viene bien a Sistemas de Unitec, la carrera "donde se enseña cómo hacer y no cómo usar..." , que le incluyan un par de clases y talleres de liderazgo, para aprender a quitarnos eso de "Porque si, porque yo se de computadoras y (vos | usted | etc ) no!" (Que aunque no lo decimos, lo pensamos)

Una de las últimas clases que incluye el plan 2002 para sistemas, donde se puede trabajar en grupos multidisciplinarios, es Generación de Empresas I, clase que en lo personal, opino que es un desastre; vayan a ver a los de Sistemas, Todos Juntos!!!! Parece que le tienen miedo a los de Publicidad y por eso no se juntan con ellos, como si les fueran a robar los poderes divinos o algo.

Finalmente, comento que alguien me dijo, que en su caso, cuando estudió, era requisito, que en cada período, debía inscribir una clase al menos, no relacionada con el área, es decir, Finanzas, Biología, Arte, ... Unitec podría hacer eso, aunque copiar comportamientos nunca es bueno, eso si los iba a enojar, en lugar de eso, ahi colocaron unos talleres no más, asi que ¡Enhorabuena por los cambios!!!

 

Uso de Atributos heredados en CUP (parte 1)

Lo primero que se me ocurrió fue usar una porción de codigo copiada del código generado. había visto antes eso de top-x, y cuando se hizo la pregunta, yo creí conocer la respuesta.
La verdad solamente había vista la punta de in iceberg.
Al intentar colocar acciones enmbebidas en la gramática se genera una cantidad alta de conflictos shift/reduce; el insertar los marcadores manualmente, procuce (al menos en mis pruebas) exactamente los mismos conflictos.
El mayor problema con esos conflictos, es que , aunque CUP tiene una políitica establecida para resolverlos, también aborta la generación de código, por lo cual, nunca pude probar si mi especulación era correcta.

El código que pensaba emplear eran las respectivas versiones de algo como esto:
cSimbolo E = (cSimbolo)((java_cup.runtime.Symbol) CUP$parser$stack.elementAt(CUP$parser$top-2)).value;
Pero el problema no fue escribir eso, sino hacer que CUP generase el código.

De modo que sigo con la interrogante a cuestas.

Por cierto, aun me queda otra duda:

Si en A -> XYZ, o mejor en S -> XYZ, donde S es el START SYMBOL o uno similar, ¿Cómo puede utilizarse un atributo de S en los hijos de S? ¿No ha reducido ni se ha metido en la pila, entonces de donde toma su valor? y ya que estamos, fuera de ese caso, ¿Para cualquier necesidad de un valor de atributo de Y por parte de un hijo de Y, debe sustituirse por el correspondiente atributo del símbolo del cual hereda el atributo? o hay casos donde no es posible ?(Sólo en L-atribuidas)

domingo, noviembre 06, 2005

 

Tarea

Gramatica para ejerc 6.3
P -> D;E

D -> D;D
D -> id:T { declare(id.nombre, T.tipo) }

T -> char { T.tipo = char }
T -> integer { T.tipo = integer }
T -> list of T1 { T.tipo = tipo_lista(T1.tipo) }

E -> literal { E.tipo = char }
E -> num { E.tipo = integer }
E -> id { E.tipo = tipode(id.nombre) }
E -> ( L ) { E.tipo = L.tipo }

L -> E,L1 { L.tipo = si( E.tipo==(L1.tipo).tipo, tipo_lista(E.tipo, L1.tipo), error_tipo) }
L -> E { L.tipo = E.tipo }

Notas: asuma que: declare() graba a la TDS, tipode() busca en la TDS y retorna el tipo y si() es una funcion que toma un valor booleano y dos expresiones, si el valor es true, retorna el valor de la primera expresion, sino el de la segunda.

EJERCICIO 6.5a (interprete lo que está entre llaves como las "decoraciones del arbol". El árbol está dibujado como en el "treeview" de windows.)
P
+-D
| +-D {declara(id.lexema, T.tipo)}
| | +-id {id.lexema = "c"}
| | | +-c
| | +-:
| | +-T {T.tipo = char}
| | +-char
| +-;
| +-D {declara(id.lexema, T.tipo)}
| +-id {id.lexema = "i"}
| | +-i
| +-:
| +-T {T.tipo = integer}
| +-integer
+-;
+-E {E1.tipo==char && E2.tipo==integer entornces error_tipo}
+-E1 {E1.tipo = tipode(id.lexema)//char}
| +-id {id.lexema="c"}
| +-c
+-mod
+-E2 {E3.tipo==integer && E4.tipo==integer entonces integer}
+-E3 {E3.tipo=tipode(id.lexema)//integer}
| +-id {id.lexema="i"}
| +-i
+-mod
+-E4 {E.tipo = integer}
+-num
+-3

TDS:[id,tipo]
[c,char]
[i,ineger]

EJERCICIO 6.5b
P
+-D
| +-D { declare(id.lexema, T1.tipo) }
| | +-id {id.lexema="p"}
| | | +-p
| | +-:
| | +-T1 {T1.tipo = pointer(integer)}
| | +-^
| | +-integer
| +-;
| +-D {declare(id.lexema,T3.tipo)}
| +-id {id.lexema = "a"}
| | +-a
| +-:
| +-T3 {T3.tipo = array(num.val, T2.tipo)}
| +-array
| +-[
| +-num {num.val = 10}
| | +-10
| +-]
| +-of
| +-T2 {T2.tipo = integer}
| +-integer
+-;
+-E {E.tipo = E3.tipo == integer && E1.tipo == array(s,t) entonces t //integer}
+-E1 {E1.tipo = tipode(id.lexema)//array(10,integer)}
| +-id {id.lexema = "a"}
| +-a
+-[
+-E3 {E3.tipo = E2.tipo==pointer(t1) entonces t1 //integer}
| +-E2 {E2.tipo = tipode(id.lexema)//pointer(integer)}
| | +-id {id.lexema = "p"}
| | +-p
| +-^
+-]

TDS:[id,tipo]
[p:pointer(integer)]
[a:array(10,integer)]

EJERCICIO 6.5C
Asumiendo que existe
E -> E(E)
P -> D;S
S -> id := E
S -> if E then S1
S -> while E do S1
S -> S1;S2

P
+-D
| +-D {declare (id.lexema,T3.tipo)}
| | +-id {id.lexema="f"}
| | +-:
| | +-T3 {T3.tipo = (T1.tipo->T2.tipo)//(integer->boolean)
| | +-T1 {T1.tipo = integer}
| | | +-integer
| | +-'->'
| | +-T2 {T2.tipo = boolean}
| | +-boolean
| +-;
| +-D
| +-D {declare(id.lexema,T4.tipo)}
| | +-id {id.lexema = "i"}
| | +-:
| | +-T4 {T4.tipo = integer}
| | +-integer
| +-;
| +-D
| +-D
| | +-id {id.lexema="j"}
| | +-T5 {T5.tipo=ingeter}
| | +-integer
| +-;
| +-D {declare(id.lexema,T6.tipo)}
| +-id {id.lexema="k"}
| +-:
| +-T6 {T6.tipo=integer}
| +-integer
+-;
+-S6 {S6.tipo = E3.tipo==boolean entonces S5.tipo //vacio}
+-while
+-E3 {E3.tipo = E2.tipo==integer && E1.tipo=(integer->boolean) entonces boolean}
| +-E1 {E1.tipo = tipode(id.lexema)//(integer->boolean)}
| | +-id {id.lexema="f"}
| +-(
| +-E2 {E2.tipo=tipode(id.lexema)//integer}
| | +-id {id.lexema="i"}
| +-)
+-do
+-S5 {S5.tipo = S1.tipo == vacio && S4.tipo == vacio entonces vacio}
+-S1 {S1.tipo = tipode(id.lexema)== E4.tipo entonces vacio;}
| +-id {id.lexema="k"}
| +-:=
| +-E4 {E4.tipo=tipode(id.lexema)//integer}
| +-id {id.lexema="i"}
+-;
+-S4 {S4.tipo = S2.tipo == vacio && S3.tipo == vacio entonces vacio}
+-S2 {S2.tipo = tipode(id.lexema)== E7.tipo entonces vacio}
| +-id {id.lexema="i"}
| +-:=
| +-E7 {E7 = E5.tipo ==integer && E6.tipo ==integer entonces integer}
| +-E5 {E5.tipo = tipode(id.lexema)//integer}
| | +-id {id.lexema="j"}
| +-mod
| +-E6 {E6.tipo = tipode(id.lexema)//integer}
| +-id {id.lexema="i"}
+-;
+-S3 {S3.tipo = tipode(id.lexema)==E8.tipo entonces vacio}
+-id {id.lexema="j"}
+-:=
+-E8 {E8.tipo=tipode(id.lexema)//integer}
+-id {id.lexema="k"}

TDS:[id,tipo]
[f,integer->boolean]
[i,integer]
[j,integer]
[k,integer]

This page is powered by Blogger. Isn't yours?