A
Given the head of a linked list and a value x
, partition it such that all nodes less than x
come before nodes greater than or equal to x
.
You should preserve the original relative order of the nodes in each of the two partitions.
Example 1:
head = [1,4,3,2,5,2]
, x = 3
[1,2,2,4,3,5]
Example 2:
Input: head = [2,1]
, x = 2
Output: [1,2]
C definition for the above singly-linked list:
typedef struct _node {
int val;
struct _node *next;
} node_t;
The function prototype:
node_t *partition(node_t *head, int x);
The corresponding RISC-V implementation is listed as following:
.text
.align 2
.globl partition
partition:
addi a5, sp, 12
sw a5, 24(sp)
addi a5, sp, 20
sw a0, 12(sp)
sw zero, 20(sp)
sw a5, 28(sp)
L1:
bne a0, zero, L4
lw a5, 24(sp)
lw a4, 20(sp)
sw a4, 0(a5)
lw a5, 28(sp)
lw a0, 12(sp)
sw zero, 0(a5)
addi sp, sp, 32
jr ra
L4:
lw a5, 0(a0)
bge a5, a1, L2
lw a4, 24(sp)
addi a5, sp, 24
L3:
sw a0, 0(a4)
addi a4, a0, 4
sw a4, 0(a5)
lw a0, 4(a0)
j L1
L2:
lw a4, 28(sp)
addi a5, sp, 28
j L3
Let's reverse-engineer to get the RISC-V assembly back to the below C code:
node_t *partition(node_t *head, int x)
{
node_t *L = NULL, **p1 = &head, **p2 = &L;
for (node_t *node = head; node; node = node->next) {
node_t ***indir = node->val >= x ?
(A01 /* fill your code */) : (A02 /* fill your code */);
**indir = node;
*indir = A03 /* fill your code */;
}
*p1 = L, *p2 = NULL;
return head;
}
Obviously, the above C code listing was incomplete, you shall provide the functioned implementations. A01
, A02
, and A03
are C expressions. You must obey the following rules when filling them:
node_t *L
to node_t* L
. Be aware of the spaces! Details determine success or failure.
- A01 = ?
- A02 = ?
- A03 = ?
B
Consider the following Python class:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def transform(self, f):
return Vector(f(self.x), f(self.y))
This code has to be converted to C code below.
#include <stdlib.h>
typedef struct vector { int x, y; } vector_t;
vector_t *transform(vector_t *self, int (*f)(int))
{
vector_t *nv = malloc(sizeof(vector_t));
nv->x = f(self->x), nv->y = f(self->y);
return nv;
}
Translate the transform
function to RISC-V. The function takes inputs self
in a0
and f
in a1
, and returns output in a0
. You may assume that vector_t
is as defined in the C code. You may also assume that you have access to malloc
, and that malloc
and f each receive their argument in a0
, and return their result in a0
. Your solution MUST fit within the lines provided.
transform:
addi sp, sp, B01
B02
B03
B04
B05
mv s0, a0
mv s1, a1
li a0, 8
jal ra, malloc
mv s2, a0
lw a0, 0(s0)
B06
sw a0, 0(s2)
lw a0, 4(s0)
B07
sw a0, 4(s2)
mv a0, s2
B08
B09
B10
B11
addi sp, sp, B12
ret
Both B06
and B07
MUST use jalr
instruction. You MUST use ABI name for accessing registers defined in RISC-V Calling Convention. i.e, use ra
instead of x1
while it appears in jalr
instruction.
- B01 = ?
- B02 = ?
- B03 = ?
- B04 = ?
- B05 = ?
- B06 = ?
- B07 = ?
- B08 = ?
- B09 = ?
- B10 = ?
- B11 = ?
- B12 = ?
C
The stack can hold local variables in addition to the ra
register and the s
registers. The following labels that were externally specified are available to you:
password
: a pointer to a statically-stored string "secretpass"get_chars
: A function defined as follows:
a0
is a pointer to a buffera0
with the read data, null-terminating the string. Your code may assume that the input is at most 19
characters, not including the null-terminator.The function verify_password
is defined as follows:
a0
returns 1
if the input from stdin is exactly "secretpass", and 0
otherwise.Complete the function verify_password
. Each line contains exactly one instruction or pseudoinstruction.
verify_password:
addi sp, sp, -24 # Make space for a 20-byte buffer
C01 # Your code here
mv a0, sp
jal ra, get_chars
la t0, password
mv t1, sp
Loop:
lb t2, 0(t0)
lb t3, 0(t1)
C02 # Your code here
C03 # Your code here
addi t0, t0, C04 # Your code here
addi t1, t1, C05 # Your code here
j Loop
Pass:
li a0, C06 # Your code here
j End
Fail:
li a0, C07 # Your code here
End:
C08 # Your code here
C09 # Your code here
jr ra
You MUST use ABI name for accessing registers defined in RISC-V Calling Convention. i.e, use ra
instead of x1
while it appears in jalr
instruction.
- C01 = ?
- C02 = ?
- C03 = ?
- C04 = ?
- C05 = ?
- C06 = ?
- C07 = ?
- C08 = ?
- C09 = ?
D
In order to enhance their models in machine learning, some data scientists add random noise to a training dataset. Here, we will take a dataset and turn it into RISC-V data that can be used.
The function jitter
is defined as follows:
a0
holds a pointer to an integer array.a1
holds a buffer large enough for n integers (which does not overlap with the array in a0
).a2
holds n
, the length of the arrays.a0
holds a pointer to the buffer originally in a1. The buffer is filled with the result of calling noisen
on each element in the a0
array.The function noisen
is defined as follows:
a0
holds an integer.a0
returns the integer with noise added.The implementation of jitter
is provided below, following calling convention:
jitter:
# BEGIN PROLOGUE
addi sp, sp, -20
# We need to make space for 5 registers on the stack,
# and each register is 4 bytes long.
# (multiple lines omitted)
# END PROLOGUE
mv s0, a0
mv s1, a1 # Hold beginning of output arr
mv s2, a1
mv s3, a2 # Hold counter
loop:
beq s3, x0, end
lw a0, 0(s0)
jal ra, noisen
sw a0, 0(s1)
addi s0, s0, 4
addi s1, s1, 4
addi s3, s3, -1
j loop
end:
mv a0, s2
# BEGIN EPILOGUE
# (multiple lines omitted)
addi sp, sp, 20
# END EPILOGUE
ret
Part 1: List all registers that we need to save on the stack in order to follow calling convention. __ D01 __
- D01 = ?
Part 2: Now, we want to implement noisen
to add some random offset to an integer a0
. Unfortunately, we only have access to the randomBool
function, which takes in no inputs and randomly returns either 1
or 0
in a0
. If we implemented noisen
to return a0 + randomBool()
, the integer would always get shifted in the positive direction. Instead, we suggest implementing noisen
so that noisen alternates between returning a0 + randomBool()
and returning a0 - randomBool()
.
Fill in the blanks to complete the above suggested implementation of noisen
. Assume that you can read from and write to any memory addresses, in any section of memory.
noisen:
addi sp, sp, -8 # Prologue
sw ra, 0(sp)
sw s0, 4(sp)
mv s0, a0
jal ra, randomBool
add s0, s0, a0
D02 # Your code here
xori t0, t0, 64
D03 # Your code here
mv a0, s0 # Epilogue
lw ra, 0(sp)
lw s0, 4(sp)
addi sp, sp, 8
ret
- D02 = ?
- D03 = ?
E
The following operations should be implemented in RISC-V 32-bit integer assembly. Any extensions (such as mul
and the floating point or vector extensions) are NOT permitted. Per blank, just one line of RISC-V code may be entered.
float_lt
a0
and a1
are positive non-NaN IEEE-754 32-bit floating point numbers.a0
returns 1
if a0 < a1
and 0
otherwise.a0
and a1
were 1.5
and 1.75
, the expected output would be 1
. If a0
and a1
were 1.5
and 1.5
respectively, the expected output would be 0
.float_lt:
E01 # Your code here
ret
- E01 = ?
skipline
addi t0, x0, 5
jal skipline
addi t0, t0, 300
addi t0, t0, 10
Then at the conclusion of this code, t0
would be equal to 15
.
skipline:
E02 # Your code here
ret
- E02 = ?