• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

20_LESI

Dúvidas assembly Y86

18 mensagens neste tópico

Provavelmente pouca gente usa disto, mas aqui vai a dúvida:

Tenho de gerar o código Y86 do seguinte programa em C:

typedef int mytype;
mytype array[100];
int sum, i;
void ini (void)
{
   for (i=0 ; i<100 ; i++)
       array[i] = i;
}
main ()
{
   ini ();
   sum = 0;
   for (i=0 ; i<100 ; i++)
       sum += array[i];
}

E eis a resolução do prof, para facilitar um bocado a coisa:

.pos 0
irmovl Pilha, %esp
rrmovl %esp, %ebp
jmp main
.pos 20
array:
.pos 420
sum: .long 0
i: .long 0
ini: # ---------------------
pushl %ebp
rrmovl %esp, %ebp
irmovl 4, %edx
subl %edx, %esp
#1 – push registos
irmovl $0, %edx
rmmovl %edx, -4(%ebp)
for1:
irmovl $100, %ecx
subl %edx, %ecx #100-j
jle fim_for1
rrmovl %edx, %eax
addl %eax, %eax
addl %eax, %eax
rmmovl %edx, array(%eax)
irmovl $1, %eax
addl %eax, %edx
rmmovl %edx, -4(%ebp)
jmp for1
fim_for1:
#2 – pop registos
rrmovl %ebp, %esp
popl %ebp
ret
main: # ---------------------
call ini
irmovl $0, %ebx
rmmovl %ebx, sum(%ebx)
rmmovl %ebx, i(%ebx)
for2:
irmovl $100, %ecx
subl %ebx, %ecx # 100-i
jle fim_for2
rrmovl %ebx, %eax
addl %eax, %eax
addl %eax, %eax
mrmovl array(%eax), %edx
irmovl $0, %esi
mrmovl sum(%esi), %ecx
addl %edx, %ecx
rmmovl %ecx, sum(%esi)
irmovl $1, %eax
addl %eax, %ebx
rmmovl %ebx, i(%esi)
jmp for2
fim_for2:
halt
# --- espaço pilha ---------
.pos 800
Pilha:

Tenho vindo a estudar e a compreender a pouco e pouco como isto funciona, ainda assim, não csg entender o que se está a fazer quando se faz:

rmmovl %edx, -4(%ebp)

que ocorre antes do for1, e dentro do for1

Obrigado pela disponibilidade

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não percebo muito de Assembly, mas penso que tem a ver com a variável i.

Coloca i a 0:

irmovl $0, %edx
rmmovl %edx, -4(%ebp)

Soma 1 ao i:

irmovl $1, %eax
addl %eax, %edx
rmmovl %edx, -4(%ebp)

Esse rmmovl deve estar a passar o valor do i do registo para memória.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Anotei o code com as instr. C

.pos 0
irmovl Pilha, %esp
rrmovl %esp, %ebp
jmp main
.pos 20                        #  inicio do array
array:
.pos 420
sum: .long 0                   #  outras vars. globais
i: .long 0
ini: # ---------------------
pushl %ebp                    ## guardar o base pointer antigo o call frame
rrmovl %esp, %ebp            
irmovl 4, %edx                ## criar um espaço no stack frame
subl %edx, %esp            
#1 – push registos
irmovl $0, %edx               ## i=0   
rmmovl %edx, -4(%ebp)         ## e mantê-lo numa var. local !!BUG, ela é global
for1:
irmovl $100, %ecx             ##
subl %edx, %ecx #100-j        ## %edx<100, o seja o i, já chegou a 100
jle fim_for1                  #  se o 100 for menor ou igual a i, salta para o fim
rrmovl %edx, %eax
addl %eax, %eax               ## eax = 4*i, ou seja, i*sizeof(int)
addl %eax, %eax            
rmmovl %edx, array(%eax)      #  array[i] = i
irmovl $1, %eax            
addl %eax, %edx               #  i++;
rmmovl %edx, -4(%ebp)         #  store do i no espaço da var local
jmp for1                      #  repeat
fim_for1:
#2 – pop registos
rrmovl %ebp, %esp             #  restaurar o estado para sair da func.
popl %ebp
ret
main: # ---------------------
call ini
irmovl $0, %ebx
rmmovl %ebx, sum(%ebx)        # sum=0
rmmovl %ebx, i(%ebx)          # i=0        !! Aqui já se usa o sitio certo do i
for2:
irmovl $100, %ecx            
subl %ebx, %ecx # 100-i       # i<100?
jle fim_for2                  # if not, jump fim_for2
rrmovl %ebx, %eax             # eax=i;
addl %eax, %eax            
addl %eax, %eax               ##eax *= 4
mrmovl array(%eax), %edx      # edx = array[i]     ##
irmovl $0, %esi                                    ##
mrmovl sum(%esi), %ecx        # ecx = sum          ## sum = sum + array[i]
addl %edx, %ecx               # ecx += edx         ##
rmmovl %ecx, sum(%esi)        # sum = edx          ##
irmovl $1, %eax            
addl %eax, %ebx                ## i++
rmmovl %ebx, i(%esi)          # guardar o i
jmp for2                      # repeat
fim_for2:
halt                          # end
# --- espaço pilha ---------
.pos 800
Pilha:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não percebo muito de Assembly, mas penso que tem a ver com a variável i.

Coloca i a 0:

irmovl $0, %edx
rmmovl %edx, -4(%ebp)

Soma 1 ao i:

irmovl $1, %eax
addl %eax, %edx
rmmovl %edx, -4(%ebp)

Esse rmmovl deve estar a passar o valor do i do registo para memória.

Exacto! Mas eu não via utilidade nenhuma nisso! Além de não perceber o porquê de guardar na stack. Mas penso que o post do edsousa me ajudou a perceber uma coisa muito importante: estou a ver a solução de uma outra versão do exercício, em que na funçao ini existe uma variável local 'j' que é usada em detrimento da variável global 'i':

void ini (void)
{
   for (j=0 ; j<100 ; j++)
       array[j] = j;
}

Ainda assim, continuo sem perceber o porquê de ser necessário guardar a variável 'i' em memória, ainda para mais na stack.

rmmovl %edx, -4(%ebp)

Dado que se tira -4 ao endereço contido em %ebp, como o sp neste caso aponta para um endereço imediatamente abaixo de %ebp, estou a meter o valor de %edx lá, certo?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Sendo a variável j global, precisarias de a guardar em memória. Se for local, será uma questão de má optimização de código.

Mas nesse código que mostras, continuas sem ter a variável como local.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Sendo a variável j global, precisarias de a guardar em memória. Se for local, será uma questão de má optimização de código.

Mas nesse código que mostras, continuas sem ter a variável como local.

Enganei-me! Esqueci-me da inicialização... Mas acho que já percebi isto! Obrigado aos 2!!!

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Deve ter e eu também e como tive o dia todo a estudar este exercício, mais propriamente a tentar perceber como é que tudo funciona a nível da stack e tal, tenho aqui uma solução um pouco diferente da apresentada e como o assunto é exactamente o mesmo, aproveito o tópico.

Alguém que perceba do assunto e tenha paciência, agradecia imenso que desse uma revisão no seguinte código totalmente comentado por mim da forma que eu entendi o dito cujo pois não queria chegar ao teste e fazer as coisas mal...

2niw5uc.jpg

E uma nota sobre o código:

4(0xBP): Esta expressão representa o endereço de memória apontado por %ebp mais 4 e não o endereço '0xBP'. ie: %ebp = 0x0020 ou seja 4(0x0020) = 0x0024

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

tens exame na segunda de manhã, não tens?

Tenho :D

Alguém sabe como transcrever isto para Y86?

int main(int argc, char **argv)

{

  int i, j=10;

  i = j*4;

}

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

A minha tentativa:

.pos 0
  irmovl Stack, %esp
  rrmovl %esp, %ebp

i: .long 0
j: .long 0

main:
  irmovl $10, %ebx			// %ebx = 10
  irmovl i, %esi			// %esi = 0xi (endereço de 'i')

  rmmovl %ebx, 4(%esi)			// 4(%esi) = 10 (j = 10)

  addl %ebx, %ebx			// %ebx = %ebx + %ebx <=> %ebx = 10 + 10 = 20
  addl %ebx, %ebx			// %ebx = %ebx + %ebx <=> %ebx = 20 + 20 = 40

  rmmovl %ebx, 0(%esi)			// 0(%esi) = 40 (i = 40 <=> i = j*4)

  halt

.pos 50
Stack: .long 0

Problemas:

  • Não faço ideia qual seria o procedimento para guardar o argc e o argv, principalmente o argv que vai depende do valor de argc.
  • As variáveis i e j estão a ser usadas como globais e não locais, não faço ideia como se faz de outra forma.

Fora isto, acho que fiz bem...  :D

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Problemas:

  • Não faço ideia qual seria o procedimento para guardar o argc e o argv, principalmente o argv que vai depende do valor de argc.
  • As variáveis i e j estão a ser usadas como globais e não locais, não faço ideia como se faz de outra forma.

Fora isto, acho que fiz bem...  :D

As minhas dúvidas são relativas à passagem de parâmetros. O resto sei fazer. Quanto às variáveis locais, tens de as meter na stack e não torná-las globais, se bem que pouco mais além da teoria sobre isso eu sei.... Um outro exemplo que não me bate certo no simulador:

int f(int p)
{
   int j, ret = 1;

   for (j=p ; j>1 ; j--)
      ret *= j;

   return ret;
}

int main()
{
      f(3);
}

Codifiquei isto da seguinte forma em assembly:

  .pos 0
irmovl Stack, %esp
rrmovl %esp, %ebp
jmp main

  .pos 20

f: 
pushl %ebp                   # salva %ebp
rrmovl %ebp, %esp            # inicializa a stack frame da função f

irmovl 1, %eax               # ret = 1
irmovl 8, %ecx               # guarda o espaço a reservar para variáveis locais num registo
subl %ecx, %esp              # reservar espaço para variáveis locais
rmmovl %eax, -4(%ebp)        # guarda o valor de ret na stack
mrmovl 8(%ebp), %ecx         # j = p (vai buscar o parâmetro p)
rmmovl %ecx, -8(%ebp)        # guarda o valor de j na stack

for1:
irmovl 1, %edx
rrmovl %ecx, %eax            # salvaguarda o valor da variável j dentro do ciclo
subl %edx, %ecx              # j=j-1
jle fim_for1
mrmovl -4(%ebp), %edx        # vai à stack buscar o valor da variável ret
addl %ecx, %eax              # ret = j+ret
rmmovl %eax, -4(%ebp)        # guardar o valor da variável ret na stack
jmp for1

fim_for1:
rrmovl %ebp, %esp
popl %ebp
ret                          # o valor de ret já se encontra na variável a, logo podemos retornar

main:
irmovl 3, %ebx
pushl %ebx
call f
halt

  .pos250

Stack:

Parecia-me estar correcto, mas a partir da linha:

	rmmovl %eax, -4(%ebp)        # guarda o valor de ret na stack

Começo a apagar valores que tenho na stack e que vou precisar mais tarde deles... Alguém sabe o que estou a fazer mal?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não vejo onde é que essa linha estaria mal, com as linhas irmovl 8, %ecx e subl %ecx, %esp estás a guardar espaço para o j e o ret, correcto? E de seguida usas o -4 para aceder ao ret e -8 para o j, acho que está bem...

Eu acho é que estas a fazer mal no p porque o p também vai ser uma variável local que recebe o valor quando f é chamado. Logo, acho que te falta reservar algum espaço na stack (dentro do stack frame de f) para o p. O que eu fazia, era remover o pushl %ebx do main e coloca-lo no f logo a seguir a inicialização da stack, acho que faz mais sentido porque o p é local.


Voltando ao problema inicial a ver se percebi o que disseste, para remover as variáveis locais tenho de as meter na stack, será assim:

.pos 0
  irmovl Stack, %esp
  rrmovl %esp, %ebp

main:
  irmovl $8, %ebx               ; %ebx = 8
  subl %ebx, %esp               ; %esp = %esp - %ebx (guarda espaço na stack para o 'i' e 'j')

  irmovl $10, %ebx              ; %ebx = 10
  rmmovl %ebx, -4(%ebp)         ; -4(%ebp) = 10 (j = 10)

  addl %ebx, %ebx               ; %ebx = %ebx + %ebx <=> %ebx = 10 + 10 = 20
  addl %ebx, %ebx               ; %ebx = %ebx + %ebx <=> %ebx = 20 + 20 = 40
  rmmovl %ebx, -8(%ebp)         ; -8(%ebp) = 40 (i = 40 <=> i = j*4)

  halt

.pos 50
Stack: .long 0

Foi isto que disseste?

EDIT: Penso que agora faz mais sentido, estava a esquecer-me de reservar espaço na stack (modificar o %esp).

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não vejo onde é que essa linha estaria mal, com as linhas irmovl 8, %ecx e subl %ecx, %esp estás a guardar espaço para o j e o ret, correcto? E de seguida usas o -4 para aceder ao ret e -8 para o j, acho que está bem...

Já descobri o erro! O que eu estava a fazer mal era somente isto:

rrmovl %ebp, %esp

pois deveria ser:

rrmovl %esp, %ebp


Eu acho é que estas a fazer mal no p porque o p também vai ser uma variável local que recebe o valor quando f é chamado. Logo, acho que te falta reservar algum espaço na stack (dentro do stack frame de f) para o p. O que eu fazia, era remover o pushl %ebx do main e coloca-lo no f logo a seguir a inicialização da stack, acho que faz mais sentido porque o p é local.

Não creio. Existe um espaço na stack frame de uma função para os parâmetros. Repara como ficou o código, já com as alterações finais:

  .pos 0
irmovl Stack, %esp
rrmovl %esp, %ebp
jmp main

f: 
pushl %ebp                   # salva %ebp
rrmovl %esp, %ebp            # inicializa a stack frame da função f

irmovl 1, %eax               # ret = 1
pushl %eax                   # guarda o valor de ret na stack

mrmovl 8(%ebp), %ecx         # j = p (vai buscar o parâmetro p)
pushl %ecx                   # guarda o valor de j na stack

for1:
irmovl 1, %edx
rrmovl %ecx, %eax            # salvaguarda o valor da variável j dentro do ciclo
subl %edx, %ecx              # j=j-1
jle fim_for1
mrmovl -4(%ebp), %edx        # vai à stack buscar o valor da variável ret
addl %edx, %eax              # ret = j+ret
rmmovl %eax, -4(%ebp)        # guardar o valor da variável ret na stack
jmp for1

fim_for1:
rrmovl %ebp, %esp
popl %ebp
ret                          # o valor de ret já se encontra na variável a, logo podemos retornar

main:
irmovl 3, %ebx
pushl %ebx
call f
halt

  .pos250

Stack:

Funciona na perfeição!


Voltando ao problema inicial a ver se percebi o que disseste, para remover as variáveis locais tenho de as meter na stack, será assim:

.pos 0
  irmovl Stack, %esp
  rrmovl %esp, %ebp

main:
  irmovl $8, %ebx               ; %ebx = 8
  subl %ebx, %esp               ; %esp = %esp - %ebx (guarda espaço na stack para o 'i' e 'j')

  irmovl $10, %ebx              ; %ebx = 10
  rmmovl %ebx, -4(%ebp)         ; -4(%esi) = 10 (j = 10)

  addl %ebx, %ebx               ; %ebx = %ebx + %ebx <=> %ebx = 10 + 10 = 20
  addl %ebx, %ebx               ; %ebx = %ebx + %ebx <=> %ebx = 20 + 20 = 40
  rmmovl %ebx, -8(%ebp)         ; 0(%esi) = 40 (i = 40 <=> i = j*4)

  halt

.pos 50
Stack: .long 0

Foi isto que disseste?

EDIT: Penso que agora faz mais sentido, estava a esquecer-me de reservar espaço na stack (modificar o %esp).

Sim. Acho que isso está correcto. Mas fica mais simples com pushl's. Repara nas alterações que fiz ao meu código!


NOTA: No ciclo do programa em C, em vez de um * deveria estar um +

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Tudo bem, funciona, mas a questão não é essa lol... Há muita coisa que pode funcionar e não ser boa prática.

Repara que eu só estou a dizer para fazeres pushl %ebx dentro do f, mais nada. Tu ao chamares f(3) no main, o 3 "pertence" ao main, esse valor faz parte do main (irmovl 3, %ebx) e não do f, tu vais é passar esse valor para o f.

Dentro do f, esse valor pode mudar mas no main tu queres que ele continue a ser 3, logo tu tens de fazer um pushl %ebx dentro do f para salvaguardar o 3 e no fim de tudo fazes um popl %ebx para restaurar esse 3 caso o valor do %ebx tenha sido mudado no f, que por acaso não foi. Mas como o %ebx é callee save, é boa prática fazeres o push e o pop dele. Isso está nos slides do prof, vai lá ver.

No geral, nem se quer tinha lógica fazeres pushl/popl do %ebx, não o estás a mudar na rotina f e quando a função f fizer ret, o programa termina (halt). Mas é boa prática, e essa boa prática faz-se dentro do f, no caso do %ebx. Lê bem os slides na parte de "caller save" e "callee save".


Quando ao outro problema... Com pushs ou com subl vai dar ao mesmo, desde que um gajo reserve espaço lol... Ok, vai dar ao mesmo no sentido de que reservamos espaço, mas óbvio que ao usar o pushl é mais rápido e mais simples (uma instrução ao contrário de duas irmovl/subl).

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Tudo bem, funciona, mas a questão não é essa lol... Há muita coisa que pode funcionar e não ser boa prática.

Repara que eu só estou a dizer para fazeres pushl %ebx dentro do f, mais nada. Tu ao chamares f(3) no main, o 3 "pertence" ao main, esse valor faz parte do main (irmovl 3, %ebx) e não do f, tu vais é passar esse valor para o f.

Dentro do f, esse valor pode mudar mas no main tu queres que ele continue a ser 3, logo tu tens de fazer um pushl %ebx dentro do f para salvaguardar o 3 e no fim de tudo fazes um popl %ebx para restaurar esse 3 caso o valor do %ebx tenha sido mudado no f, que por acaso não foi. Mas como o %ebx é callee save, é boa prática fazeres o push e o pop dele. Isso está nos slides do prof, vai lá ver.

No geral, nem se quer tinha lógica fazeres pushl/popl do %ebx, não o estás a mudar na rotina f e quando a função f fizer ret, o programa termina (halt). Mas é boa prática, e essa boa prática faz-se dentro do f, no caso do %ebx. Lê bem os slides na parte de "caller save" e "callee save".

Já li isso bem, tal como algumas partes do CS:APP! O %ebx é um registo callee save, e no CS:APP diz o seguinte:

Um procedimento P vai chamar um procedimento Q, e vai necessitar de um valor X antes e após a chamada de Q. Pode proceder de duas formas:

A: Guardar o valor de X na sua própria Stack Frame antes de invocar Q. Após Q retornar, poderá aceder ao valor de X através da Stack

B: Guardar o valor de X num registo callee save. Se Q ou qualquer outro procedimento invocado por Q tiver que usar este registo, o valor de X deverá ser guardado na sua Stack Frame e posteriormente restaurado, antes do retorno de Q

Diz também que os processadores costumam optar pela solução B! Eu estou a utilizar a solução B, tu dizes-me para utilizar a solução A. Por aquilo que o jmf diz, realmente deveríamos optar pela solução A, mas farto de pushl's e popl's já eu estou! Vou optar pela solução B, e deixar um comentário (que vou levar na folha) para explicar o que fiz...

É de mim, ou  desde que começou esta conversa, tanto eu como tu já evoluímos bué em Y86?

:ipool:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não lol, eu estou a dizer para tu utilizares a B mesmo... Repara o %ebx é "callee save" que é o que tu estas a usar, logo, segundo que colocaste ai, se Q tiver que usar o %ebx, o valor tem de ser guardado na sua stack frame (na de Q) e posteriormente restaurado antes do retorno de Q, ou seja:

Q:
  pushl %ebp
  rrmovl %esp, %ebp
  pushl %ebx ; guarda o valor de %ebx porque ele é callee save
  irmovl $5, %ebx ; muda o valor de %ebx (só para exemplo)
  popl %ebx ; restaura o valor do stack pointer para o %ebx
  ret

Isto é o que a opção B diz, e isto não é o que tu estás a fazer... Tu estás a fazer A:

main:
  irmovl 3, %ebx
  pushl %ebx
  call f

Ou seja, tu estás a guardar o valor do %ebx na própria stack do main e não no stack frame do f. Isto é a opção A e não a B.

Eu acho que depende um pouco da solução do problema... Por exemplo, se no main usasses o %eax, que é caller save (e não callee) só precisavas de fazer um pushl %eax no main caso o fosses o usar na função f (mesmo que não o usasses na função f, como diz os slides, é boa prática fazê-lo). Se fosse o %ebx que é callee (e não caller), tens de fazer o pushl %ebx dentro da stack frame do f e no fim popl %ebx.

Resumindo:

Se usas registos "caller save" e a função que invoca (main) outra função (f) precisa de manter esses registos, então tem de os guardar previamente na sua stack (main). Se usas registos "callee save", eles não podem ser alterados na função invocada (f), se precisam de ser alterados, têm de ser guardados na própria stack frame (f) e depois restaurados no fim.

Penso que estou correcto e só estou a seguir o raciocínio dos slides...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Estás! Eu é que falei sem olhar bem para o código! Ou seja, isto aqui:

main:
  irmovl 3, %ebx
  pushl %ebx
  call f

Nem seria necessário pois o registo %ebx é callee save. Segundo o que tu dizes, é boa prática guardar o seu valor no início procedimento f, e restaurá-lo no fim. Certo?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Se usares o %ebx, sim (ou outro registo qualquer que também seja callee save). Se usasses um registo caller save, ai sim, já fazias o pushl no main (tal como tens, mas para o respectivo registo) e no procedimento f n precisavas de guardar/restaurar.

Ou seja, depende do registo que usares.

Isto é o que diz os slides, diz que isto é boa prática, porque que realmente funciona assim ou porquê que se deve fazer assim, não faço a mínima  :confused:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora