Académique Documents
Professionnel Documents
Culture Documents
Procesador MIPS
Consideraciones:
Tamao de palabra: 32 bits (4 bytes).
Tamao de media palabra: 16 bits (2 bytes).
Registros:
$t0
$t1
$zero: Registro especial cuyo valor es siempre 0.
$at: Registro especial para operaciones inmediatas.
Instrucciones:
Operaciones matemticas:
Sumar registros: add destino, origen1, origen2
add $t0, $t1, $t2 #Suma $t1 y $t2 y lo guarda en $t0
Suma inmediata con signo: addi destino, origen, aadido
addi $t0, $zero, 1 #Aade 1 al valor de $t0
Suma inmediata sin signo: addiu destino, origen, aadido
addiu $t0, $zero, 1 #Aade 1 al valor de $t0
Restar registros: sub destino, origen1, origen2
sub $t1,$1,$3 #Guarda en $t1 ($1 - $3)
Comparaciones:
Slt: slt destino,origen1,origen2
slt $at, $t4, $s2 #Si t4 < s2, at = 1,en caso contratio, at = 0
Igual: beq a, b, salto
Desigual: bne a, b, salto
bne $at, $zero, loop #Salta a loop si at != 0
Menor: blt a, b, salto (*)
slt $at, a, b
bne $at, $zero, salto
Mayor: bgt a, b, salto (*)
slt $at, b, a
bne $at, $zero, salto
Menor o igual: ble a, b, salto (*)
slt $at, b, a
beq $at, $zero, salto
Mayor o igual: bge a, b, salto (*)
slt $at, a, b
beq $at, $zero, salto
Menor o igual que 0: blez a, salto
blez $t1, loop #Salta si el contenido del registro es menor o igual
que 0
Desplazamientos:
Desplazamiento lgico a la izq: sll destino,origen,n de 0
sll $t3, $s1, 3 #Coge los bits desde el 28 hasta el 0 de s1 y les
aade 3 ceros por la derecha
Desplazamiento lgico a la der: srl destino, origen, n de 0
srl $t3, $s1, 3 #Coge los bits desde el 31 hasta el 3 de s1 y les
aade 3 ceros por la izquierda
Instrucciones de carga de datos:
Carga de una palabra (Load Word): lw destino, desplazamiento(origen)
lw $t1, 40($t3)
Carga media palabra (Half Word): lh destino, desplazamiento(origen)
lh $t1, 40($t3)
Carga media palabra SIN signo: lhu destino, desplazamiento(origen)
lhu $t1, 40($t3)
Carga de un byte: lb destino, desplazamiento(origen)
lb $t1, 40($t3)
Carga de un byte SIN signo: lbu destino, desplazamiento(origen)
lbu $t1, 40($t3)
Carga inmediata parte superior: lui destino, semipalabra?
lui $s2, 0x0987
Carga inmediato de 16 bits con signo: l i,destino, n (*)
li $t1,5000 #al n en hexadecimal le aade 16 0 por la izq y lo
guarda en t1
li $t1, -5000 #al n en hexadecimal le aade 16 1 por la izq y lo
guarda en t1
Carga inmediato de 32 bits: li,destino,n ( *)
li $t1, 0xa0b0c0 #carga el n entero en $t1
Banderas(registro EFLAGS):
OF (bit 11): Se pone a 1 si hay overflow en operaciones con signo y
complemento a 2..
SF (bit 7): Signo del resultado de la ltima operacin con signo (1: Negativo).
ZF (bit 6): Se pondr a 1 si la ltima operacin aritmtica es cero.
CF (bit 0): Toma el valor 1 si hay acarreo. Contiene el valor del bit que sale en
los desplazamientos(En operaciones sin signo).
Para usarlo, ponemos jnX si queremos comprobar si es 0 o jX para ver si es 1
Instrucciones aritmticas:
Suma sin acarreo: ADD destino/origen, origen
Suma con acarreo: ADC destino/origen, origen
Instrucciones lgicas:
Operacin AND bit a bit: AND destino/origen, origen
Operacin OR bit a bit: OR destino/origen, origen
Operacin XOR bit a bit: XOR destino/origen, origen
Operacin NOT bit a bit: NOT destino/origen
NOTA: Todas las instrucciones del tipo XXX destino/origen, origen significan que intervienen
ambos registros en la operacin pero que se guarda en el primero de ellos.
Instrucciones de desplazamiento:
Desplazamiento ARITMTICO a la izquierda: SAL registro, bits
Desplazamiento ARITMTICO a la derecha: SAR registro, bits
Otras instrucciones:
?: LEA ?
5. OpenMp
Paralelizar un bloque:
#pragma omp parallel private(lista) shared(lista) firstprivate(lista) lastprivate(lista)
shared(list): Variable compartida.
private(list): Variable privada.
firstprivate(list): Para que una variable privada se inicialice con el valor que
tena antes de entrar en el bloque.Si no se indica esto, el valor inicial de la
variable es indeterminado (no inicializada).
lastprivate(list): Para que una variable conserve su valor una vez finalizado el
bloque,si no se indica,su valor final es indeterminado.
Paralelizacin de bucles:
#pragma omp for (Esta declaracin estar dentro del bloque paralelo previamente
declarado mediante #pragma omp parallel)
9. Open MPI
char p[MPI_MAX_PROCESSOR_NAME];
int tam;
MPI_Get_processor_name(p, &tam);
Cuando el proceso B ejecuta un recive siempre se bloquea hasta que le mensaje ya est en el
buffer.
Ejercicios prctica 1:
1. Dado el siguiente fragmento de cdigo en lenguaje C:
r = 0;
a = 1;
b = 2;
if (a <= b)
r = 1;
Cdigo en ensamblador:
main:
addiu $t0, $zero, 0 #$t0 = r
addiu $t1, $zero, 1 #$t1 = a
addiu $t2, $zero, 2 #$t2 = b
Paso a paso:
1. sub $t1, $zero, $t1
Resta a $t1 cero menos $t1, es decir, pone $t1 a 0.
$t1 = 0x00000000
2. addi $t2, $zero, -1
Suma (con signo) a $t2 el valor de menos uno, por tanto el valor de $t2 ser -1
$t2 = 0xFFFFFFFF
3. xor $t1, $t1, $t2
Guarda el resultado de la operacin exclusive-or de $t1 y $t2 en $t1
$t1 = 0xFFFFFFFF
4. addi $t1, $t1, 1
Aade (con signo) uno al valor de $t1
$t1 = 0x00000000
.data
datos: .space 32
.text
la $t2, datos
lui $t1,0x8899
sw $t1, 16($t2)
lb $t1, 19($t2)
0 x 01 00 00 00
1er 2do 3er 4to
byte byte byte byte
Tenemos que tener en cuenta que 0x00 es un byte y que por tando 0x00000000 son 4 bytes
que es el tamao de palabra (8*4 = 32 bits). Cada posicin de memoria almacena UN byte.
Paso a paso:
1. la $t2, datos
$t2 = 0x10010000
2. lui $t1,0x8899
Rellena con ceros por la derecha y lo guarda en $t1
$t1 = 0x88990000
3. sw $t1, 16($t2)
Guarda el contenido de $t1 en la direccin de memoria que contiene $t2 con 16
bits de desplazamiento.
Primero sumamos 16 (10 en hexadecimal) a la direccin de memoria que
contiene $t2, es decir, a 0x10010000 le sumamos 10 = 0x10010010.
(0x10010010) = 0x88990000
4. lb $t1, 19($t2)
Carga un byte en un registro, en este caso el que est en la posicin
0x10010000 ms 19 (que es 13 en hexadecimal), es decir, en la direccin 0x10010013
$t1 = 0xFFFFFF88
Con pseudoinstrucciones:
addiu $t1, $zero, 0x12345 # t1 = valor cambiante, a sustituir
bge $t1, 0x12345, etiq #salta a continua si t1 >= 0x12345
syscall
etiq: addiu $t0, $zero, 1 #t0 valdr uno si ha saltado a etiq y 0 si no lo ha hecho
syscall
Sin pseudoinstrucciones:
#addiu $t1, $zero, 0x12345 #t1 = valor cambiante, a sustituir
lui $t1, 0x0001
ori $t1, $t1, 0x2345
#bge $t1, 0x12345, etiq #salta a continua si t1 >= 0x12345
lui $at, 0x001
ori $at, $at, 0x2345
slt $at, $t1, $at #
beq $at, $zero, etiq #
syscall
etiq: addiu $t0, $zero, 1 #t0 valdr uno si ha saltado a etiq y 0 si no lo ha hecho
syscall
5. Escriba una secuencia de instrucciones que convierta una cadena de caracteres en
mayscula a minscula. La cadena comienza en la direccin a la que apunta la etiqueta
cadena y el valor 0 indica fin de cadena. Los caracteres de la 'a' a la 'z', tanto en
minscula como en mayscula, tienen cdigos ASCII consecutivos (A es el 65 y a el
97). Para convertir un carcter en mayscula 'Q' a minscula hay quecalcular, por tanto,
'Q'-65+97.
.data
cadena: .ascii "ABCDEFG0"
.text
la $t0, cadena #Nos traemos la direccin de memoria en la que se almacena la cadena a $t0
bucle: lb $t1, 0($t0)
beq $t1, 48, fin #Si $t1 = 48, que es el valor ASCII de 0, entonces se sale del bucle
addiu $t1, $t1, 32 #Le sumamos 32 a la palabra para pasarla a minscula
sb $t1, 0($t0)
addiu $t0, $t0, 1
j bucle #Salta al bucle de nuevo
fin: syscall
6. Escriba un programa en ensamblador del MIPS equivalente a la siguiente porcin de
cdigo en lenguaje de alto nivel (lenguaje C):
i = 15;
if (i < 10)
r = 0;
else if (i < 20)
r = 1;
else
r = 2;
7. Las estructuras iterativas de los lenguajes de alto nivel se traducen usando una
instruccin de salto condicional (que puede ir precedida de una instruccin slt) para
implementar la condicin del bucle, adems de una instruccin de salto incondicional
para volver al comienzo del bucle y ejecutar la siguiente iteracin.
Como ejemplo, vamos a traducir la siguiente porcin de cdigo en C:
x = 4;
res = 0;
for ( i = 0; i < x; ++i)
res = res + 3;
Ejecute el programa en el simulador MARS y compruebe si tras la ejecucin el valor del
registro
equivalente a la variable res tiene el valor adecuado.
9. Consideremos el siguiente programa en lenguaje de alto nivel (lenguaje C), que suma
dos vectores o arrays guardando el resultado en un tercer vector y adems acumula la
suma de todos los elementos en una variable:
int n = 3, suma = 0;
int a[3] = {1, 3, 5};
int b[3] = {2, 4, 6};
int r[3];
main() {
int i;
for (i = 0; i < n; ++i) {
r[i] = a[i] + b[i];
suma += r[i];
}
}
.data
arrayA: .space 32
arrayB: .space 32
arrayR: .space 32
.text
#Inicializar variables
addiu $t0, $zero, 3 #n=3
addiu $t1, $zero, 0 #suma=0
addiu $t2, $zero, 0 #i=0
#Ahora vamos a utilizar $t3 como registro auxiliar
#Inicializar array A
la $t7, arrayA #Nos traemos la direccin de memoria reservada al array A
addiu $t3, $zero, 1 #Lo que vamos a guardar
sb $t3, 0($t7) #Lo guardamos en el espacio de memoria de array A
addiu $t7, $t7, 1 #Incrementamos la direccin de memoria de A en 1
addiu $t3, $zero, 3 #Lo que vamos a guardar
sb $t3, 0($t7) #Lo guardamos en el espacio de memoria de array A
addiu $t7, $t7, 1 #Incrementamos la direccin de memoria de A en 1
addiu $t3, $zero, 5 #Lo que vamos a guardar
sb $t3, 0($t7) #Lo guardamos en el espacio de memoria de array A
#Inicializar array B
la $t8, arrayB #Nos traemos la direccin de memoria reservada al array B
addiu $t3, $zero, 2 #Lo que vamos a guardar
sb $t3, 0($t8) #Lo guardamos en el espacio de memoria de array B
addiu $t8, $t8, 1 #Incrementamos la direccin de memoria de B en 1
addiu $t3, $zero, 4 #Lo que vamos a guardar
sb $t3, 0($t8) #Lo guardamos en el espacio de memoria de array B
addiu $t8, $t8, 1 #Incrementamos la direccin de memoria de B en 1
addiu $t3, $zero, 6 #Lo que vamos a guardar
sb $t3, 0($t8) #Lo guardamos en el espacio de memoria de array B
fin: syscall
Cuestiones prctica 2:
1. Indique que contiene el registro EAX tras ejecutar cada uno de los siguientes trozos de
cdigo:
a. mov eax, 11AA22BBh ;eax = 0x11AA22BB
mov [1000h], eax ;eax = 0x11AA22BB
mov dword ptr [1004h], 22BB33CCh ;eax = 0x11AA22BB
mov eax, [1002h] ;eax = 0x33CC11AA
2. Tenemos dos valores enteros sin signo de 64 bits en los registros EBX:EAX y
EDX:ECX. Escriba un trozo de cdigo que calcule la suma de dichos valores y la
almacene en EBX:EAX.
add eax, ecx ;Suma la parte baja de ambos nmeros y guarda el resultado en EAX.
adc ebx, edx ;Suma la parte alta de ambos nmeros teniendo en cuenta el acarreo (al
realizarse la operacin anterior el bit de acarreo, CF, se habr activado si se produjo el mismo).
3. Dadas dos variables x e y de 32 bits que se encuentran en las direcciones de memoria
EBP-4 y EBP-8, respectivamente; escriba, para cada una de las siguientes condiciones,
un trozo de cdigo que salte a la etiqueta FIN si se cumple la condicin:
a) el bit 7 de x vale 0
.386
.model flat
option casemap:none
.data
.code
start:
mov ebx, 1
mov eax, 0FFh
test eax, 80h ;El valor hexadecimal 80 tiene todos los bits a 0 salvo el sptimo
jz fin
mov ebx, 0
;EBX indica si ha saltado (1) o si no lo ha hecho (0)
fin:
end start
end
b) el bit 31 de y vale 1
c) x > y (suponiendo que x e y son enteros con signo)
El vector vectres al finalizar la ejecucin del programa contendr en todos sus elementos (10
en total) el valor 11 (0x0b en hexadecimal):
vectres = 0b, 0b, 0b, 0b, 0b, 0b, 0b, 0b, 0b, 0b
En otras palabras, el algoritmo lo que hace es sumar los valores de vect1 con sus pares de
vect2 y guardarlo en vectres. Por tanto, vectres se podr inicilizar con cualquier valor (ya que,
obviamente, no se accede nunca a sus datos, slo se nombra en el cdigo para guardar datos).
2. Escriba un programa en ensamblador que calcule el sumatorio de los elementos de un
vector.
Los elementos del vector son enteros sin signo de 32 bits y el sumatorio debe
almacenarse en un variable de 64 bits por si el resultado ocupa ms de 32 bits (para ello
debe tener en cuenta el bit de carry de las sumas parciales). Reutilice el cdigo del
ejercicio anterior con la siguiente definicin de datos:
.386
.model flat
option casemap:none
.data
vect1 DD 1,2,3,4,5,6,7,8,9,10
suma DQ ?
.386
.model flat
option casemap:none
.data
vect1 DD 1,2,3,4,5,6,7,8,9,10
suma DQ ?
.code
start:
mov eax, 0 ;Variable resultado
mov ecx, 10 ;Contador
mov edx, 0 ;Desplazamiento (de 4 en 4)
inibucle:
add eax, [edx + vect1] ;Almacenamos el valor
add edx, 4 ;Aadimos 4 para avanzar a la siguiente posicin que contenga elementos del array
loop inibucle
;mov suma, eax
end start
end
3. Escriba un programa en ensamblador que, dada una cadena de caracteres en
minscula obtenga, otra cadena similar pero con las letras en maysculas. Para ello
tenga en cuenta que:
Cada carcter ocupa un byte
El valor de la letra a es 97 y de la letra A es 65
Utilice la definicin de datos siguiente:
.386
.model flat
option casemap:none
.data
cadena db "holamundo",0
cadena2 db 20 dup (?)
3.1 Escriba una primera versin suponiendo que la cadena de entrada slo tiene letras
minsculas.
.386
.model flat
option casemap:none
.data
cadena db "holamundo",0
cadena2 db 20 dup (?)
.code
start:
mov edx, 0 ;Desplazamiento (de 1 en 1)
inibucle:
mov al, [edx + cadena]
cmp al, 0
jz fin
sub al, 32
mov [edx + cadena], al
add edx, 1 ;Aadimos 1 para avanzar a la siguiente posicin que contenga elementos del array
jmp inibucle
fin:
end start
end
3.2 Escriba una segunda versin ms completa teniendo en cuenta que la cadena de
entrada puede estar formada por cualquier carcter y que slo debe modificar los
caracteres que se correspondan con letras minsculas. Para probar la solucin utilice
una cadena diferente a la del ejemplo.
.386
.model flat
option casemap:none
.data
cadena db "HolaMundo",0
cadena2 db 20 dup (?)
.code
start:
mov edx, 0 ;Desplazamiento (de 1 en 1)
inibucle:
mov al, [edx + cadena]
;Comprobamos si el carcter es cero (y finalizamos en dicho caso)
cmp al, 0
jz fin
;Comprobamos si restndole 97 ("a") el resultado es negativo (-1 mnimo, en dicho caso es
mayscula)
mov bl, al
sub bl, 97
js esMayuscula
;Sigue aqu si el carcter es minsculas
esMinuscula: sub al, 32
mov [edx + cadena], al
add edx, 1 ;Aadimos 1 para avanzar a la siguiente posicin que contenga elementos del array
jmp inibucle
;Salta aqu el carcter es maysculas
esMayuscula: add al, 32
mov [edx + cadena], al
add edx, 1 ;Aadimos 1 para avanzar a la siguiente posicin que contenga elementos del array
jmp inibucle
fin:
end start
end
4. Escriba un programa que calcule el factorial de una variable n almacenada en
memoria. La variable es un entero sin signo de 32 bits. Reutilice el cdigo del ejercicio 2
con la siguiente definicin de datos:
.386
.model flat
option casemap:none
.data
n DD 4
fact DD ?
.386
.model flat
option casemap:none
.data
n DD 4
fact DD ?
.code
start:
mov eax, 1 ;Variable resultado
mov ecx, n ;El nmero de vueltas del bucle viene dado por el mismo "n"
mov edx, 0 ;Desplazamiento (de 4 en 4)
inibucle:
mul ecx
mov [fact], eax
loop inibucle
end start
end
Cuestiones prctica 3:
1. Traduzca a instrucciones del IA32 los siguientes fragmentos de cdigo MIPS:
a) Salta si $t3<= $t4:
sub $t1, $t3, $t4 #Resta $t3 menos $t4 y lo guarda en $t1
blez $t1, sigue #Si $t1 es menor o igual que 0 entonces salta a sigue
e) Traduzca a IA32 el cdigo C siguiente (use como referencia el cdigo MIPS que se
observa ms abajo):
x=4
res = 0
i=1
while (i <= x ) {
res = res + 2
i++
}
mov eax, 4
mov ebx, 0
mov ecx, 1
bucle: cmp ecx, eax
jbe fin
add ebx, 2
add ecx, 1
jmp bucle
fin:
(REVISAR)
Ejercicios prctica 3:
Ejercicios prctica 4:
Ejercicio 1. Clculo de prestaciones.
Ejecutamos MARS y cargamos el cdigo de ejemplo, lo grabamos y lo ensamblamos (F3)
para pasar a la ventana de ejecucin.
Cdigo de ejemplo:
addi $t3,$zero,0x10010000 #$t3 = 0x10010000 (3 intrucciones)
lw $t4, 0($t3) #$t4 = [10010000], es decir, $t4 = 0 (1 instruccin)
i3: bltz $t4, i10 #Si $t4 < 0 salta a i10, por tanto no salta (1 instruccin)
add $t1,$zero,$t4 #$t1 = $t4, es decir, $t1 = 0 (1 instruccin)
lw $t2,4($t3) #$t2 = [0x10010004], es decir, $t2 = 0 (1 instruccin)
mul $s6,$t2,$t1 #$s6 = $t2 * $t1, que ser igual a $s6 = $t2 * $t4, es decir, $s6 = 0 (1 instruccin)
xor $t4,$s6,$t1 #$t4 = xor($s6, $t1), es decir, $t4 = 0 (1 instruccin)
and $t1,$s6,$t4 #$t1 = and($s6, $t4), es decir, $t1 = 0 (1 instruccin)
sw $t1, 4($t3) #$t1 = [10010004], es decir, $t1 = 0 (1 instruccin)
i10: sw $t4, 0($t3) #$t4 = [10010000], es decir, $t4 = 0 (1 instruccin)
fin: li $v0, 10 #Llamada al sistema para finalizar
syscall
Primera forma:
CPI = 1
Frecuencia de ciclo = 1 MHz = 10^6 Hz
Segunda forma:
CPI = 1
Frecuencia de ciclo = 1 MHz = 10^6 Hz Tiempo de ciclo = 10^-6 s
Ahora en MARS, desde el men Tools cargamos la utilidad Instruction Counter y cuando
aparezca pulsamos sobre el botn Connect to MIPS para que comience a contabilizar.
Ahora ejecutemos el programa directamente sin usar el paso a paso, Run Go o F5.
e) Indicar el nmero de instrucciones ejecutadas de forma general y por cada tipo de
instruccin.
El nmero de instrucciones ejecutadas de forma general : 14
R-Type: 6 [42%]
I-Type: 8 [57%]
J-Type: 0 [0%]
f) Suponiendo que las instrucciones de tipo R consumieran 3 ciclos de reloj, las de tipo I
1 ciclo y las de tipo J 3 ciclos, calcular (segn las frmulas de la Figura 2) el nmero de
ciclos del programa, el tiempo real de CPU y el CPI medio de este ejemplo.
.000026 s
tCPU real = nCiclos * tiempoCiclo (apartados anteriores) = 26 ciclos * 10^-6 s = 0
g) Repetir los apartados e y f, pero ahora usando los valores arrojados por la
herramienta Instruction Statistics y suponiendo que los valores de ciclos por tipo de
instruccin son: ALU = 1, Jump = 3, Branch = 5, Memory = 2, Other = 3.
Total de instrucciones= 14
ALU: 7 [50%]
Jump: 0 [0%]
Branch: 1 [7%]
Memory: 4 [29%]
Other: 2 [14%]
.000026 s
tCPU real = nCiclos * tiempoCiclo = 26 ciclos * 10^-6 s = 0
Total: 279
ALU: 147 [53%]
Jump: 0 [0%]
Branch: 30 [10%]
Memory: 26[9%]
Others: 76 [27%]
Total: 182
ALU: 80 [44%]
Jump: 0 [0%]
Branch: 25 [14%]
Memory: 26 [14%]
Other: 51 [28%]
Ejercicios prctica 5
Pi no paralelo:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int i;
double x, pi, sum = 0.0;
long numSteps = atol(argv[1]);
double step = 1.0 / (double)numSteps;
for (i=0; i<numSteps; ++i) {
x = (i+0.5)*step;
sum += 4.0/(1.0+x*x);
}
pi = step * sum;
printf("Valor de pi: %f\n", pi);
return 0;
}
Pi paralelo:
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int i;
double x,y;
double pi,area;
long numSteps = atol(argv[1]);
srand(seed);
for (i=0; i<numSteps; ++i) {
x= drand48();
y= drand48();
}
pi=4.0*area/numSteps;
printf("Valor de pi: %f\n", pi);
return 0;
}
--------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
double sem;
struct erand48_data randBuffer;
int i;
double x, y, area,pi;
long numSteps = atol(argv[1]);
#pragma omp parallel private(randBuffer)
{
srand48_r(omp_get_thread_num(), &randBuffer);
#pragma omp parallel for private(x,y) reduction(+:pi)
for (i=0; i<numSteps; ++i) {
erand48_r(&randBuffer, &x);
erand48_r(&randBuffer, &y);
}
pi=4.0*area/numSteps;
printf("Valor de pi: %f\n", pi);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int main(int argc, char *argv[]) {
double pi = 0;
int dardosEnCirculo = 0;
double x;
double y;
int i;
int totalDardos = atoi(argv[1]);
for(i = 0; i < totalDardos; i++) {
x = drand48();
y = drand48();
if((x*x) + (y*y) <= 1) {
dardosEnCirculo++;
}
}
pi = 4.0 * dardosEnCirculo/totalDardos;
printf("%f\n", pi);
return pi;
}