Tema 3: Traducció de programes

12.1 Operacions lògiques i desplaçaments

Vegeu Secció 3.1 i Secció 3.2.

Problema 12.1 Calculeu el resultat en hexadecimal de les operacions lògiques bit a bit següents amb 16 bits:

  1. 0x7654 | 0x3333
  2. 0x7654 & 0x3333
  3. 0x7654 << 2
  4. 0x7654 >> 3 (desplaçament lògic)

Problema 12.2 Donada la sentència en C:

C
C = A ^ B;

Traduïu-la a assemblador RISC-V sense utilitzar la instrucció xor. Recordeu que l’operació és equivalent a:

C
C = (~A & B) | (A & ~B);

Problema 12.3 Escriviu les seqüències de codi necessàries per deixar a t1 el resultat de les condicions lògiques següents, sense fer servir instruccions de salt. El valor final de t1 ha de ser 0 si la condició no es compleix, o diferent de zero altrament.

  1. L’enter representat per t1 és negatiu.
  2. L’enter representat per t1 és múltiple de 4.
  3. El natural representat per t1 és tal que el seu quadrat no es pot escriure amb 32 bits.
  4. El natural representat per t1 és tal que el seu doble no es pot escriure amb 32 bits.
  5. Els bits 1, 3 i 5 de t1 valen zero.

Problema 12.4 Programeu una seqüència de no més de 6 instruccions que posi a 0 el bit \(i\)-èsim del registre t1, deixant la resta de bits intactes. Suposeu que \(i\) està guardat a t2. Podeu usar altres registres per a resultats temporals.

Problema 12.5 Feu un programa que faci els desplaçaments següents sobre un nombre de 64 bits guardat als registres t2 (32 bits de més pes) i t1 (32 bits de menys pes):

  1. Desplaçament a l’esquerra d’1 posició.
  2. Desplaçament lògic a la dreta d’1 posició.
  3. Desplaçament aritmètic a la dreta d’1 posició.
  4. Desplaçament lògic a l’esquerra de \(n\) posicions (\(n < 32\)), on \(n\) ocupa t3.
  5. Desplaçament lògic a la dreta de \(n\) posicions (\(n < 32\)), on \(n\) ocupa t3.

Problema 12.6 Suposeu que el registre t0 conté dos enters de 16 bits en complement a 2: un als 16 bits de més pes i un altre als 16 bits de menys pes. Escriviu una seqüència d’instruccions que extregui aquests dos nombres, els representi en 32 bits estenent el signe, i els guardi a t1 i t2 respectivament, sense usar cap instrucció d’accés a memòria.

Problema 12.7 Feu un programa que compti el nombre de bits actius que hi ha al registre t1, executant el mínim d’instruccions possible i sense fer cap lectura de memòria. El resultat s’ha de deixar al registre t3.

Problema 12.8 Escriviu un fragment de codi assemblador RISC-V que intercanviï tots els bits parells amb els bits senars del registre t0.

Problema 12.9 Representeu el valor inicial de cada bit de t4 amb una lletra: el bit 31 és b31, el bit 30 és b30, …, el bit 0 és b0. Cada lletra denota un valor binari desconegut (0 o 1).

  1. Escriviu les instruccions necessàries perquè el bit de més pes passi a ser el de menys pes i la resta de bits es desplacin una posició cap a l’esquerra.
  2. Seguint la mateixa notació, escriviu el contingut del registre t4 després d’executar les instruccions del Codi 12.1:
Codi 12.1: Codi de l’apartat b).
RV32I
        li      t1, 16
        mv      t2, t4
bucle:
        srli    t4, t4, 1
        add     t2, t2, t2
        addi    t1, t1, -1
        bne     t1, zero, bucle
        or      t4, t4, t2

12.2 Sentències condicionals i bucles

Vegeu Secció 3.6 i Secció 3.7.

Problema 12.10 Traduïu a assemblador RISC-V el programa en C del Codi 12.2, suposant que a, b i c ja s’han inicialitzat als registres t1, t2 i t3:

Codi 12.2: Sentències if imbricades.
C
void main() {
    int a, b, c, i;  // i s'emmagatzema a t0
    ...
    if (a > 0) {
        i = b;
        if (((i - 4) < a) && (i > 0))
            b = b + c;
        else if (c > 0)
            b = b - c;
    } else {
        a = a + b;
    }
}

Problema 12.11 Traduïu a assemblador RISC-V la sentència if del programa en C del Codi 12.3, que converteix un dígit hexadecimal (representat per un caràcter ASCII guardat a la variable global c) al seu valor binari equivalent i l’escriu a la variable global num (enter de 8 bits). Suposeu que el valor de c és un dels caràcters {'0'..'9', 'A'..'F', 'a'..'f'}.

Codi 12.3: Conversió d’un dígit hexadecimal en ASCII al seu valor.
C
char c, num;
void main() {
    ...
    if ((c >= 'A') && (c <= 'F'))
        num = c - 'A' + 10;
    else if ((c >= 'a') && (c <= 'f'))
        num = c - 'a' + 10;
    else
        num = c - '0';
}

Problema 12.12 Traduïu a assemblador RISC-V la sentència switch del Codi 12.4. La variable op ocupa el registre t0 i les variables a, b, res ocupen t1, t2, t3:

Codi 12.4: Sentència switch amb quatre casos i default.
C
int op, a, b, res;
...
switch (op) {
    case 0:  res = a + b;  break;
    case 1:  res = a - b;  break;
    case 2:  res = a & b;  break;
    case 3:  res = a | b;  break;
    default: res = 0;
}

Proposeu dues implementacions: una amb salts encadenats i una altra amb taula de salts (jump table). Quan és preferible cada enfocament?

Problema 12.13 Donat el programa en C:

C
void main() {
    int a = 23;  // guardat a t0
    int b = 12;  // guardat a t1
    int i;       // guardat a t2
    for (i = 0; i < 10; i++)
        a += b;
}
  1. Traduïu-lo a assemblador RISC-V.
  2. Quin és el nombre total d’instruccions que s’executen?
  3. Suposant un processador a 2 GHz on totes les instruccions tenen CPI = 2, si el nombre d’instruccions executades fos 10, quants segons trigaria a executar-se el programa?
  4. En un processador a 2 GHz, si el nombre d’instruccions executades fos 10, com a l’apartat c), i el programa trigués \(5 \times 10^{-9}\) s, quin seria el CPI del programa?

Problema 12.14 Donades dues variables enteres a i b (amb b ≥ 0), ja inicialitzades als registres t1 i t2:

  1. Programeu en C una seqüència d’instruccions que multipliqui a * b usant un bucle while i operacions de suma. El resultat s’ha de deixar a c, ubicada al registre t3.
  2. Traduïu-la a assemblador RISC-V.
  3. Quin és el nombre màxim d’instruccions RISC-V que poden arribar a executar-se?
  4. En un processador a 2 GHz on totes les instruccions usen 2 cicles de rellotge, quants segons trigaria a executar-se el programa?
  5. Si en comptes de sumes s’usa una única instrucció de multiplicació que utilitza 10 cicles de rellotge, quants segons trigaria a executar-se? Quin seria el CPI del programa?

Problema 12.15 Traduïu a assemblador RISC-V el programa en C del Codi 12.5:

Codi 12.5: Bucle while que escriu el vector D.
C
int D[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
void main() {
    int a = 0;   // guardat a t0
    int b = 12;  // guardat a t1
    while (a < 10) {
        D[a] = b + a;
        a += 1;
    }
}

Problema 12.16 Traduïu a assemblador RISC-V el programa en C del Codi 12.6, suposant que les variables globals ja s’han inicialitzat:

Codi 12.6: Suma i recompte dels elements de V més grans que 5.
C
#define N 30
int V[N], suma, elems;
void main() {
    int i;
    ...
    i = 0;
    elems = 0;
    suma = 0;
    while (i < N) {
        if (V[i] > 5) {
            suma = suma + V[i];
            elems++;
        }
        i++;
    }
}

Problema 12.17 Donada la variable global (on N és una constant):

C
int v[N];

Calculeu el temps d’execució en cicles de rellotge de cada versió del programa que suma els elements de v, suposant que:

  • Els accessos a memòria triguen 4 cicles.
  • Els salts que no salten triguen 1 cicle.
  • Els salts que salten triguen 2 cicles.
  • La resta d’instruccions triguen 1 cicle.
RV32I
        li      t0, 0        # suma
        li      t1, 0        # i
        li      t2, 0        # desplaçament
bucle:
        li      t3, N
        bge     t1, t3, fi
        la      t4, v
        add     t4, t4, t2
        lw      t4, 0(t4)
        add     t0, t0, t4
        addi    t2, t2, 4
        addi    t1, t1, 1
        j       bucle
fi:
RV32I
        li      t0, 0        # suma
        li      t1, 0        # i
bucle:
        li      t3, N
        bge     t1, t3, fi
        la      t3, v
        slli    t2, t1, 2
        add     t2, t3, t2
        lw      t2, 0(t2)
        add     t0, t0, t2
        addi    t1, t1, 1
        j       bucle
fi:
RV32I
        li      t0, 0        # suma
        li      t1, 0        # i
bucle:
        la      t3, v
        slli    t2, t1, 2
        add     t2, t2, t3
        lw      t2, 0(t2)
        add     t0, t0, t2
        addi    t1, t1, 1
        li      t3, N
        blt     t1, t3, bucle
RV32I
        li      t0, 0        # suma
        li      t1, N-1      # i
bucle:
        la      t3, v
        slli    t2, t1, 2
        add     t2, t3, t2
        lw      t2, 0(t2)
        add     t0, t0, t2
        addi    t1, t1, -1
        bge     t1, zero, bucle
RV32I
        li      t0, 0        # suma
        li      t1, N        # i
        la      t2, v
bucle:
        lw      t3, 0(t2)
        add     t0, t0, t3
        addi    t2, t2, 4
        addi    t1, t1, -1
        bne     t1, zero, bucle
  1. Programeu una nova versió de l’apartat e) que en disminueixi el temps d’execució.

Problema 12.18 Donada la declaració en C (on N és una constant):

C
int V[2*N];

Escriviu un fragment de codi assemblador RISC-V que intercanviï cada element d’índex parell amb l’element següent d’índex senar.

Problema 12.19 Donades les declaracions:

RV32I
.data
v1:     .byte   '0', '0', '4', '1', '9'
v2:     .byte   '8', '9', '7', '2', '3'
v3:     .byte   0,   0,   0,   0,   0,  0

Cada vector conté caràcters ASCII que representen dígits numèrics. En conjunt, cada vector representa un natural decimal amb les unitats a l’última posició. Escriviu un programa que sumi els naturals representats per v1 i v2 i deixi el resultat a v3, que té un dígit de més per poder representar qualsevol suma sense desbordament. Feu servir l’algorisme típic de la suma dígit a dígit.

Problema 12.20 El vector d’enters Array està ubicat a l’adreça 0x10010000 de memòria i s’ha declarat així:

RV32I
.data
Array:  .word   2, 4, 6, 1

Feu un programa en assemblador RISC-V que ordeni els elements del vector de menor a major, deixant el valor més petit a la posició de memòria més baixa.

12.3 Subrutines: introducció

Vegeu Secció 3.9.

Problema 12.21 Escriviu en C i en assemblador RISC-V una funció que, donats dos enters de 32 bits passats per valor, retorni el mínim dels dos.

Problema 12.22 Traduïu a assemblador RISC-V la subrutina en C del Codi 12.7:

Codi 12.7: Funció strlen.
C
int strlen(char s[]) {
    int i = 0;
    while (s[i] != '\0')
        i++;
    return i;
}

Problema 12.23 Donat el fragment de programa en C:

C
void examen(char vi[], char vo[]) {
    int i = 0;
    while (vi[i] != '\0') {
        if (vi[i] != ' ')
            vo[i] = vi[i] - 32;
        else
            vo[i] = vi[i];
        i++;
    }
    vo[i] = '\0';
}
void main() {
    examen(vect1, vect2);
}

Sabent que vect1 i vect2 són variables globals:

  1. Traduïu a assemblador RISC-V la funció main.
  2. Traduïu a assemblador RISC-V la subrutina examen.

Problema 12.24 Donades les declaracions en C:

C
int proc(int vector[], int *x1);

char exam(int v[], int a, int b) {
    int i, *p;
    ...
}
  1. Traduïu a assemblador RISC-V el fragment de codi del Codi 12.8, que pertany a exam, suposant que i i p ocupen t0 i t1:
Codi 12.8: Fragment de l’apartat a): una crida a proc.
C
p = &v[a + 5];
i = proc(v, p);
  1. Traduïu a assemblador RISC-V la sentència condicional del Codi 12.9, que pertany a exam, suposant que i ocupa t0:
Codi 12.9: Sentència condicional de l’apartat b).
C
if ((i > 0) && (i < 10))
    i = 10;
else
    i = 0;
  1. Traduïu a assemblador RISC-V el fragment de codi del Codi 12.10, que pertany a exam, suposant que i i p ocupen t0 i t1:
Codi 12.10: Bucle de l’apartat c).
C
p = v + b;
for (i = 0; i < 10; i++) {
    *p = 0;
    p++;
}

Problema 12.25 Donades les funcions en C:

C
void swap(int *a, int *b) {
    int temp;
    temp = *a;
    *a   = *b;
    *b   = temp;
}

void examen(int vector[], char c, int j) {
    swap(&vector[j + 1], &vector[j]);
}
  1. Traduïu a assemblador RISC-V la subrutina swap.
  2. Traduïu a assemblador RISC-V la subrutina examen.

Problema 12.26 Sabent que la funció f té la capçalera en C:

C
int f(int n);

i que la seva traducció a assemblador RISC-V és el Codi 12.11:

Codi 12.11: Traducció de la funció f.
RV32I
f:
        mv      t0, a0     # t0 = n
        li      a0, 0      # a0 = acumulador
buc:
        andi    t1, t0, 1
        add     a0, a0, t1
        srli    t0, t0, 1
        bne     t0, zero, buc
        li      t1, 32
        sub     a0, t1, a0
        ret
  1. Quin valor retorna f per a n = 0x6789ABCD?
  2. Expliqueu amb paraules què calcula la funció f.

Problema 12.27 Traduïu a assemblador RISC-V la funció recursiva del Codi 12.12:

Codi 12.12: Funció recursiva pellnumber.
C
int pellnumber(int n) {
    if (n < 2)
        return n;
    else
        return 2 * pellnumber(n - 1) + pellnumber(n - 2);
}

Problema 12.28 Traduïu a assemblador RISC-V la subrutina en C del Codi 12.13:

Codi 12.13: Subrutina recursiva maxv.
C
int maxv(int vector[], int length) {
    int temp;
    if (length == 1)
        temp = vector[0];
    else {
        temp = maxv(vector, length - 1);
        if (vector[length - 1] > temp)
            temp = vector[length - 1];
    }
    return temp;
}

Problema 12.29 Traduïu a assemblador RISC-V la funció func1:

C
unsigned int func1(unsigned int v, unsigned int *p) {
    if (v == 0)
        *p = 0;
    else
        *p = func1(func1(v - 1, p), p + 1) - 1;
    return *p;
}

Problema 12.30 El repertori de RISC-V inclou la instrucció jalr per cridar subrutines i per a salts indirectes. Suposeu que s’executa el fragment de codi RISC-V del Codi 12.14:

Codi 12.14: Fragment amb la, addi i jalr.
RV32I
        la      ra, etiq
        addi    ra, ra, -4
etiq:   jalr    ra, 0(ra)

Quina és la resposta correcta?

  1. La instrucció jalr s’executa 1 vegada.
  2. La instrucció jalr s’executa 2 vegades.
  3. La instrucció jalr s’executa 3 vegades.
  4. La instrucció jalr s’executa infinites vegades (el programa entra en un bucle infinit).
  5. Sense saber el contingut d’altres posicions de memòria no es pot saber quantes vegades s’executa.

Problema 12.31 Donades les declaracions en C:

C
int s2(int c, long long *d, long long w[]);

int s1(int x, long long v[], int *p) {
    long long *k;
    k = &v[x];
    x = s2(*p, k, v);
    return *p + x;
}

Traduïu a assemblador RISC-V la funció s1.

12.4 Subrutines: context i bloc d’activació

Vegeu Secció 3.9.3 i Secció 3.9.4.

Problema 12.32 Donades les declaracions en C:

C
int subr2(int a, char *b, char c, int d);

int subr1(char v[], int e2, int *var, char p) {
    int valor;
    valor = subr2(*var, &v[e2], p, e2);
    return valor + e2;
}

Traduïu a assemblador RISC-V la funció subr1.

Problema 12.33 Donades les declaracions de variables globals i de la funció A:

C
char  x;
int   z;
char  w[20];

char A(char i, int k, char *v) {
    char r[10];
    char c;
    c    = v[3];
    r[k] = i;
    return c;
}
  1. Traduïu a assemblador RISC-V la sentència:
C
x = A(x, z, w);
  1. Dibuixeu el bloc d’activació d’una crida a la funció A.
  2. Traduïu a assemblador RISC-V la funció A.

Problema 12.34 Traduïu a assemblador RISC-V la funció A:

C
int B(short *x, short y[], int z, char m);

int A(int a[], short b[], char c) {
    int   p1 = 0;
    short p2[5];
    p1         = B(b, p2, p1, 'A');
    p2[p1 + a[2]] = b[p1];
    if (c == 'z')
        p1 = p1 - 3;
    else
        p1 = 2;
    return p1;
}

Problema 12.35 Traduïu a assemblador RISC-V la funció sub (escrivint punts suspensius per a les sentències desconegudes):

C
void sub(int p1[10], int p2, int p3) {
    int x1[10], x2;
    ...
    x1[p2]  = p1[x2] + p3;
    ...
    sub(x1, p2, p1[3]);
    ...
    x2 = p2 + p3;
    ...
}

12.5 Compilació, assemblatge, enllaçament i càrrega

Vegeu Secció 3.10.

Problema 12.36 La pseudoinstrucció la rd, symbol s’expandeix habitualment en dues instruccions reals. Expliqueu quines són i per què són necessàries dues instruccions. Quina és la limitació en el desplaçament que pot representar l’expansió?

Problema 12.37 Un projecte té dos fitxers font:

C
// fitxer a.c
int x = 42;
int f(int n) { return n + x; }
C
// fitxer b.c
extern int x;
int f(int n);
int main() { return f(x); }
  1. Quines fases del flux de generació (compilació, assemblatge, enllaçament) intervenen en construir l’executable?
  2. El fitxer objecte b.o conté una referència externa a x i a f. Qui resol aquestes referències i en quin moment?
  3. Si x es declara static int x = 42 a a.c, canvia alguna cosa? Per què?
  4. La pseudoinstrucció la t0, x es tradueix en auipc + addi (Problema 12.36). Per què auipc, relatiu al PC, i no lui, que carrega una adreça absoluta?