Ćwiczenia 9-10: protokół wywołania funkcji (2 tygodnie)

Zadanie 1

Jezyk - C + przekazywanie parametrów przez referencję

Dany program

int f(int w, int& x);
void printInt(n);
 
int g(int y, int& z)
{
  int a=z, b;
  z = f(a+2,y);
  printInt(z);
  b = y;
  return b;
}

Zaplanuj rekord aktywacji dla funkcji g i przetłumacz ją

  • z argumentami na stosie (x86 - architektura 32-bitowa)
  • z argumentami w rejestrach (x86-64 - architektura 64-bitowa)

Zadanie 2 (egzamin 2008)

Język: oddzielnie kompilowane, rekurencyjne funkcje bez struktury blokowej, zmienne lokalne i wynik funkcji typu integer, dwa rodzaje parametrow: przez wartość (integer) oraz referencję (również do zmiennej typu integer). Składnia zbliżona do C++ (parametry przekazywane przez referencję są oznaczane jako typu int&).

Komputer: cztery rejestry uniwersalne R0,...,R3 i instrukcje
Ri:=Ri op Rj (op to +-*/)
Ri:=Rj
Ri:=n
INC Ri,n
LOAD Ri,n
LOAD Ri, [Rj]
STORE Ri,[Rj]
GOTO n
GOTO [Ri]
CALL Ri (adres skoku w rejestrze, ślad tamże)
plus potrzebne skoki warunkowe.
Notacja [Ri] oznacza komórkę pamięci o adresie w Ri

Zaprojektuj protokół wywołania i powrotu z funkcji i przetłumacz

extern int g(int& a);
int f(int a, int& b) {
  int c;
  c =  21 + g(a) + g(b);
  return c;
}

rozwiązanie powinno obejmować opis rekordu aktywacji funkcji f i istotny fragment rekordu aktywacji funkcji g.

Zadanie 3 (z egzaminu)

Rozważmy język D♭ ze strukturą blokową, dopuszczający zagnieżdżanie funkcji oraz przekazywanie ich jako parametrów (poza tym podobny do C), oraz fragment programu w nim:

int h(int a, int b, function int k(int)) {
  return k(a)  + k(b) ;
}
 
int g(int a) {
 int b;
 int f(int c) { return (b=a+c); }
 f(a);
 return h(a,b,f);
}

a. Zaprojektuj protokół wywołania i powrotu z funkcji dla tego języka.
b. Opisz rekordy aktywacji dla funkcji f,g,h
c. Wygeneruj kod dla podanego fragmentu programu.

Można przyjąć, że w języku D♭ nie występują wskaźniki do funkcji (ani w ogóle konstrukcje nie występujące w przytoczonym fragmencie programu).

Zadanie 4 (egzamin 2009)

Rozważmy język ze strukturą blokową, dopuszczający zagnieżdzanie procedur oraz trzy rodzaje parametrów:

* wejściowe (przekazywane przez wartość), oznaczane na liście parametrów formalnych słowem kluczowym in
* wyjściowe - kopiowane po powrocie z funkcji na zmienną będącą parametrem faktycznym, oznaczane out
* wejściowo-wyjściowe - przekazujące wartość do funkcji przy wywołaniu i z funkcji przy powrocie (inout)

Dalej, rozważmy fragment programu w tym języku:

program Foo;
procedure P(in int a,out int b);
  var int c;
  procedure Q(inout int b, out int c);
  begin 
    c := b / a;
    b := b - c
  end; (* Q *)
  procedure  R(inout int d) begin Q(d,c) end (* R *);
begin (* P *)
    c := a + 1;
    R(c)
    Q(c,b);
end; (* P *)

a. Zaprojektuj protokół wywołania i powrotu z procedur dla tego języka.
b. Opisz rekordy aktywacji dla procedur P,Q,R
c. Wygeneruj kod na maszynę stosową dla podanego fragmentu programu.

Zadanie 5

Jezyk: oddzielnie kompilowane, rekurencyjne funkcje bez struktury
blokowej, zmienne lokalne i wynik funkcji typu integer, dwa rodzaje
parametrow: przez wartosc (integer) oraz etykieta, skoki tylko nielokalne,
do etykiet przekazanych jako parametr.
Komputer: cztery rejestry uniwersalne R0,...,R3 i instrukcje
Ri:=Ri op Rj (op to +-*/)
Ri:=Rj
Ri:=n
INC Ri,n
LOAD Ri,arg
STORE Ri,arg
GOTO arg
CALL Ri (adres skoku w rejestrze, slad tez tam)
plus potrzebne skoki warunkowe.

Zaprojektowac protokol wywolania funkcji dla tego jezyka i przetlumaczyc:

function F(a,b,c);
var d,e,f,g;
begin
  g:=10;
  if a>0 then c:=a+b;
  repeat
    f:=a+b; e:=b; d:=a;
    f:=FUNKCJA((d+e)*f,(b+d)-(c*g),ETYKIETA);
    if f>0 then e:=(a+b)*(c*d) else e:=c*d;
ETYKIETA:
    e:=e-(c*d);
    a:=a+1
  until a=b;
  return e;
end;

(Oczywiscie interesuja nas tylko ciekawsze fragmenty)

Zadanie 6

W jezyku L-- istnieja rekurencyjne funkcje (z parametrami przekazywanymi
przez wartosc). Funkcje moga byc zagniezdzane, ale wewnatrz funkcji
widoczne sa jedynie zmienne i funkcje zadeklarowane w niej samej lub w
funkcji bezposrednio ja otaczajacej. Wszystkie zmienne lokalne, parametry
i funkcje sa typu integer. Przyklad programu:

function p();
  var p1; function q(a); begin ...end;
  function F(a,b); var c; function R(); begin ...end;
  begin {F}
    c:=1; if a<b then c:=F(Q(F(a+p1,b-1)),R()); return 5;
  end;
begin ... end;

Komputer ma rejestr adresowy A oraz wbudowany stos (o ograniczonej,
niewielkiej pojemnosci) sluzacy do obliczen arytmetycznych. Istnieja
miedzy innymi instrukcje:

PUSH A
PUSH n
PUSH (A+n)
PUSH * adres na czubku stosu zastepuje wartoscia spod tego adresu
POP A
POP (A+n)
POP * na czubku wartosc i adres, wykonujemy przypisanie
ADD, SUB ...
GOTO n
GOTO * adres skoku na czubku stosu
IFZERO n
IFNOTNEG n
CALL n slad zostaje na czubku stosu

Ponadto istniej funkcja ALLOC przydzielajaca obszar pamieci o rozmiarze na czubku stosu i zastepujaca rozmiar adresem obszaru. Jest tez procedura FREE zwalniajaca obszar o adresie na czubku stosu.

Zaprojektowac rekord aktywacji i protokol wywolania funkcji. (Rekordy aktywacji tworzymy i usuwamy przez ALLOC i FREE).

Napisac tlumaczenie funkcji F z przykladu powyzej.

Zadanie 7 (z kolokwium)

Dany jest język programowania, w którym:

  • program składa się z funkcji, które można zagnieżdżać i wywoływać rekurencyjnie
  • zasady widoczności zmiennych i parametrów w funkcjach zagnieżdżonych są takie, jak w Pascalu
  • wszystkie dane (zmienne, parametry, wynik funkcji) są typu integer
  • parametry funkcji mogą być przekazywane przez wartość lub przez zmienną (przekazywanie przez zmienną
    zaznaczone jest przez &)
  • liczba argumentów funkcji może być zmienna (ostatnim argumentem jest wówczas ...), ale wywoływana funkcja
    zna liczbę argumentów, z którymi została wywołana (jako zmienną _argc); do argumentów bez nazwy odwołuje
    się poprzez tablicę _argv; argumenty bez nazwy są zawsze przekazywane przez wartość
  • argumenty operacji arytmetycznych oraz parametry funkcji obliczane są od lewej do prawej

Maszyna docelowa ma pamięć składającą się ze słów, długość adresu jest równa długości słowa. Maszyna ma dwa
rejestry ogólnego przeznaczenia, R0 i R1, wskaźnik wierzchołka stosu SP i rejestr BP. Stos rośnie w kierunku
malejących adresów. Adresy można podawać bezpośrednio bądź w sposób indeksowany (np. [adres+10]).
Dostępne są następujące instrukcje:

MOV Rx, Ry – kopiuje zawartość Rx do Ry,
MOV adres, Ry – kopiuje zawartość pamięci spod adresu adres do Ry,
MOV Rx, adres – kopiuje zawartość rejestru Ry do pamięci pod adres adres,
ADD Rx, Ry, SUB Rx, Ry, MUL Rx, Ry – operacje arytmetyczne, ich wynik jest umieszczany w Rx,
PUSH num – odkłada na stos liczbę num,
PUSH Rx – odkłada na stos zawartość rejestru Rx,
POP Rx – zdejmuje ze stosu liczbę i umieszcza ją w Rx,
CALL adres – odkłada na stos adres kolejnej instrukcji i skacze pod adres adres,
RET – zdejmuje ze stosu liczbę, którą traktuje jako adres pod który skacze,
oraz inne, nieistotne dla treści zadania.

Zaprojektuj protokół wywołania i powrotu z funkcji tak, by możliwe było zaimplementowanie powyżej
przedstawionych cech języka programowania. Opisz jak wygląda prolog i epilog funkcji oraz jej wywołanie i do czego
są używane poszczególne rejestry.

Przedstaw na rysunku zawartość stosu (zaznaczając strzałkami SL-e i DL-e oraz pomijając zmienne tymczasowe) przed
wykonaniem linijki zaznaczonej przez (*) z poniższego programu, zakładając, że pierwszy wykonywany był wiersz
oznaczony (**):

int f1(int a, int b, int c) {
int f2(int &a) {
int f3(int b, ...) {
int c = f4(a)+_argv[0]+b;
return c;
};
a = 17;
return f3(c, 2)+b;
}
int f4(int c) {
(*)
return a+c;
}
return f2(b);
}
...
(**) f1(1,2,3);
...

Adresy funkcji przedstaw jako #nazwa, a adresy zmiennych przez ich nazwę w nawiasach kwadratowych.

Podaj wartość zwracaną przez wywołanie f1.

Zadanie 8

Przesledzic wykonanie programu (wczesniej trzeba rozplanowac rekord
aktywacji i omowic sposob przekazywania parametrow):

program A;
var aa:integer;
  function B(function bf:integer; var bv:integer):integer;
    function C(cc:integer):integer;
    begin
      c:=aa+bv+cc;
      aa:=E(cc)
    end;
  begin
    bv:=bv+1;
    if bv=2
      then B:=B(C,bv)
      else B:=(aa+bv)*D(bf)
  end;
  function D(function dd:integer):integer;
  begin
    D:=(2*aa)-dd(aa)
  end;
  function E(ee:integer):integer;
  begin
    E:=ee+2
  end;
begin
  aa:=1;
  writeln(B(D,aa))
end.