# Quiz2 of Computer Architecture (2020 Fall) :::info :information_source: General Information * You are allowed to read [lecture materials](http://wiki.csie.ncku.edu.tw/arch/schedule). * That is, an open book exam. * You shall not disclose your answer during the quiz. * Each answer has 5 points. * :timer_clock: 09:10 ~ 10:00AM on Oct 6, 2020 ::: ## Question `A` For each RISC-V instruction sequence below, provide the ==hex== values of the specified registers after each sequence has been executed. Assume that all registers are initialized to 0 prior to each instruction sequence. Each instruction sequence begins with the line (`. = 0x0`) which indicates that the first instruction of each sequence is at address 0. Assume that each sequence execution ends when it reaches the `UNIMPL` instruction. ```cpp . = 0x0 jal x5, L1 # Value left in x5? __A01__ jal x6, end # Value left in x6? __A02__ L1: j L2 jal x6, end L2: jr x5 # Address of label L2? __A03__ end: UNIMPL ``` > * A01 = ? > * A02 = ? > * A03 = ? --- ## Question `B` :::info Pseudo-instructions – shorthand syntax for common assembly idioms – `mv rd, rs` = `addi rd, rs, 0` – `li rd, 13` = `addi rd, x0, 13` ::: RISC-V sequence B refers to certain locations in memory. Provide the ==hex== values of the specified registers after each sequence has been executed. Assume that the first 5 memory locations starting from address 0x600 have been initialized with the following 5 words. Assume that each sequence execution ends when it reaches the `UNIMPL` instruction. ```cpp . = 0x600 # First 5 words at address 0x600 .word 0x60046004 .word 0x87654321 .word 0x12345678 .word 0x00000001 ``` Sequence `B` ```cpp . = 0x0 li x7, 0x600 mv x8, x7 # Value left in x7? __B01__ loop: addi x8, x8, 4 # Value left in x8? __B02__ lw x9, 0(x8) sw x9, -4(x8) blez x9, loop # Value left in x9? __B03__ lw x7, 0(x7) end: UNIMPL ``` > * B01 = ? > * B02 = ? > * B03 = ? --- :::info Function Calls The following pseudo instructions are available to call subroutines far from the current position: * `call <symbol>`: call away subroutine - ra is implicitly used to save the return address. * `call <rd>, <symbol>` : call away subroutine - similar to `call <symbol>`, but `<rd>` is used to save the return address instead. * `tail <symbol>` : tail call away subroutine - `t1` is implicitly used as a scratch register. * `jump <symbol>, <rt>` : jump to away routine - similar to `tail <symbol>`, but `<rt>` is used as the scratch register instead. ::: ## Question `C` Below is the C code for a recursive implementation of binary search, which finds the index at which an element should be inserted into a sorted array to ensure that the array is still sorted after the insertion. The roughly translated RISC-V assembly follows the C code. ```cpp /* A recursive binary search function. * It returns location of x in given array arr[l..r] is present, * otherwise -1 */ int binary_search(int arr[], int l, int r, int x) { if (r >= l) { int mid = l + (r - l) / 2; /* If the element is present at the middle itself */ if (arr[mid] == x) return mid; /* If element is smaller than mid, then it can only be present * in left subarray */ if (arr[mid] > x) return binary_search(arr, l, mid - 1, x); /* Else the element can only be present in right subarray */ return binarySearch(arr, mid + 1, r, x); } /* We reach here when element is not present in array */ return -1; } ``` - [ ] Translated RISC-V Assembly (`C01` is unknown) ```cpp binary_search: addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) mv s0, a2 beq a1, a2, done add t0, a1, a2 srli t0, t0, 1 slli t1, t0, 2 add t1, a0, t1 lw t1, 0(t1) if_: bge __C01__ mv a2, t0 j recurse else_: addi t0, t0, 1 mv a1, t0 recurse: call binary_search mv s0, a0 done: mv a0, s0 lw s0, 0(sp) lw ra, 4(sp) L1: addi sp, sp, 8 ret ``` 1. What should be in the blank on the line labeled if to make the assembly implementation match the C code? > C01 = ? 2. How many words will be written to the stack before the program makes each recursive call to the function binary_search? That is, number of words pushed onto stack before each recursive call? > C02 = ? 3. The program’s initial call to function binary_search occurs outside of the function definition via the instruction `call binary_search`. The program is interrupted during a recursive call to `binary_search`, just prior to the execution of `addi sp, sp, 8` at label L1. The diagram on the right shows the contents of a region of memory. All addresses and data values are shown in hex. The current value in the SP register is `0xEB0` and points to the location shown in the diagram CONTENT. What are the ==hex== values in the following registers right when the execution of binary_search is interrupted? Current value of `s0`? > C03 = ? Current value of `ra`? > C04 = ? | Memory address | Data content | | -------------- | ------------ | | 0xEA4 | 0x0 | | 0xEA8 | 0x5 | | 0xEAC | 0xC4 | | SP→ 0xEB0 | 0x6 | | 0xEB4 | 0xC4 | | 0xEB8 | 0x6 | | 0xEBC | 0xC4 | | 0xEC0 | 0xA | | 0xEC4 | 0xC4 | | 0xEC8 | 0x3E | | 0xECC | 0xCA4 | | 0xED0 | 0xCED | > diagram: CONTENT. `sp` points to address `0xEB0` 4. What is the hex address of the `ret` instruction? > C05 = ? --- ## Question `D` The following functions are missing code to save/restore registers to/from the stack. Identify the registers that need to be saved to the stack at any point in the function. You do not need to provide the missing code. If the RISC-V calling convention does not require any registers to be saved to / restored from the stack, write `none`. - [ ] Case `1` ```cpp # register save/restore code missing func: beqz a0, done mv s0, a0 addi a0, a0, -1 jal func # use return value of func: add a0, s0, a0 done: ret ``` registers needing saving (or "none")? > D01 = ? - [ ] Case `2` ```cpp # register save/restore code missing func: li t0, 0x5F3759DF xor t0, a0, t0 loop: beqz t0, end srli t0, t0, 1 andi t0, t0, 0x1 beqz t0, lsb_zero addi s0, s0, 1 j loop lsb_zero: addi s1, s1, 1 j loop end: ret ``` registers needing saving (or "none")? > D02 = ? --- ## Question `E` Below is the C code and translated RISC-V assembly. ```cpp int func(int m, int n) { if (m == 0) return n + 1; if (n == 0) /* m_nonzero */ return func(m - 1, 1); /* both_nonzero */ return func(m - 1, func(m, n - 1)); } ``` - [ ] Translated RISC-V Assembly (`X` is unknown.) ```cpp func: bnez a0, m_nonzero addi a0, a1, 1 ret m_nonzero: addi sp, sp, -X sw ra, 0(sp) bnez a1, both_nonzero addi a0, a0, -1 li a1, 1 call func lw ra, 0(sp) addi sp, sp, X ret both_nonzero: sw a0, 4(sp) # inner call addi a1, a1, -1 call func mv a1, a0 lw a0, 4(sp) # outer call addi a0, a0, -1 call func lw ra, 0(sp) addi sp, sp, X ret ``` What is the minimum value of `X`, the number of bytes needed on the stack? > E01 = ? --- ## Question `F` You are given the following C code along with an incomplete translation of this code into RISC-V assembly. Assume that execution ends when it reaches the `UNIMPL` instruction. ```cpp int a[4] = {0x1, 0x2, 0x4, 0x8}; int b = 0x5; int c = 0; for (int i = 3; i >= 0; i--) { c = a[i] & b; if (c) break; } ``` - [ ] Translation into RISC-V assembly (`F01` is unknown) ```cpp . = 0x0 li a0, 0x800 // starting address of array li a1, 0x5 li a2, 0x3 loop: mv t0, a2 __F01__ add t0, a0, t0 # Value left in t0? __F02__ lw t1, 0(t0) and t1, t1, a1 bnez t1, end; # Value left in t1? __F03__ addi a2, a2, -1 bgez a2, loop # Value left in a2? __F04__ end: UNIMPL . = 0x800 # contents of array a, where a[0] is at 0x800 .word 0x1 .word 0x2 .word 0x4 .word 0x8 ``` > F01 = ? Provide the ==hex== values left in the registers below following the execution of the corrected code. * Value left in t0? > F02 = ? * Value left in t1? > F03 = ? * Value left in a2? > F04 = ? --- ## Question `G` Consider the C code below and its translation to RISC-V assembly code, shown in the later. ```cpp int f(int a, int b) { int c = b – a; if (c & G01 == 0) /* c is a multiple of 4 */ return 1; int d = f(a – 1, b + 2); return 3 * (d + a); } ``` - [ ] RISC-V Assembly (`G01` is unknown.) ```cpp f: sub a2, a1, a0 andi a2, a2, __G01__ bnez a2, ELSE li a0, 1 jr ra ELSE: addi sp, sp, -8 sw a0, 0(sp) sw ra, 4(sp) addi a0, a0, -1 addi a1, a1, 2 jal ra, f A4: lw a1, 0(sp) lw ra, 4(sp) L1: add a0, a0, a1 slli a1, a0, 1 add a0, a0, a1 addi sp, sp, 8 jr ra ``` 1. What value should the `G01` term in the C code and the assembly be replaced with to make the if statement correctly check if the variable `c` is a multiple of 4? > G01 = ? 2. How many words will be written to the stack before the program makes each recursive call to the function f? That is, number of words pushed onto stack before each recursive call? > G02 = ?