【发布时间】:2021-09-16 16:40:36
【问题描述】:
给定一个非常基本的 C 链表:
struct node {
int value;
struct node *next;
};
我想用 RISC-V 汇编语言编写函数“map”。 map 函数递归地应用一个函数来更改列表中每个节点的值。例如:将链表中的所有数据平方。 C中的函数供参考:
void map(struct node *head, int (*f)(int))
{
if (!head) { return; }
head->value = f(head->value);
map(head->next,f);
}
测试输出如下:打印链表中的数据,使用名为“square”的函数指针调用map,它将每个值平方,然后再次调用map到名为“decrement”的函数指针,它将减少每个值1.
9 8 7 6 5 4 3 2 1 0
81 64 49 36 25 16 9 4 1 0
80 63 48 35 24 15 8 3 0 -1
这是我在 Venus 中运行的 RISC-V 代码,但是当我尝试调用传递给 map 的函数时,我遇到了第 76 行的问题。
.globl map
.text
main:
jal ra, create_default_list
add s0, a0, x0 # a0 (and now s0) is the head of node list
# Print the list
add a0, s0, x0
jal ra, print_list
# Print a newline
jal ra, print_newline
# === Calling `map(head, &square)` ===
# Load function arguments
add a0, s0, x0 # Loads the address of the first node into a0
# Load the address of the "square" function into a1 (hint: check out "la" on the green sheet)
### YOUR CODE HERE ###
la a1, square
# Issue the call to map
jal ra, map
# Print the squared list
add a0, s0, x0
jal ra, print_list
jal ra, print_newline
# === Calling `map(head, &decrement)` ===
# Because our `map` function modifies the list in-place, the decrement takes place after
# the square does
# Load function arguments
add a0, s0, x0 # Loads the address of the first node into a0
# Load the address of the "decrement" function into a1 (should be very similar to before)
### YOUR CODE HERE ###
# Issue the call to map
jal ra, map
# Print decremented list
add a0, s0, x0
jal ra, print_list
jal ra, print_newline
addi a0, x0, 10
ecall # Terminate the program
map:
# Prologue: Make space on the stack and back-up registers
### YOUR CODE HERE ###
addi sp, sp, -8
sw ra, 4(sp)
sw a0, 0(sp)
beq a0, x0, done # If we were given a null pointer (address 0), we're done.
add s0, a0, x0 # Save address of this node in s0
add s1, a1, x0 # Save address of function in s1
# Remember that each node is 8 bytes long: 4 for the value followed by 4 for the pointer to next.
# What does this tell you about how you access the value and how you access the pointer to next?
# Load the value of the current node into a0
# THINK: Why a0?
### YOUR CODE HERE ###
lw a0, 0(s0)
# Call the function in question on that value. DO NOT use a label (be prepared to answer why).
# Hint: Where do we keep track of the function to call? Recall the parameters of "map".
### YOUR CODE HERE ###
jalr ra, 0(a1)
# Store the returned value back into the node
# Where can you assume the returned value is?
### YOUR CODE HERE ###
sw a0 0(s0)
# Load the address of the next node into a0
# The address of the next node is an attribute of the current node.
# Think about how structs are organized in memory.
### YOUR CODE HERE ###
lw t0 4(s0)
mv a0 t0
# Put the address of the function back into a1 to prepare for the recursion
# THINK: why a1? What about a0?
### YOUR CODE HERE ###
mv a1 s1
# Recurse
### YOUR CODE HERE ###
jal ra, map
lw ra, 4(sp)
lw s0, 0(sp)
addi sp, sp, 8
jr ra
done:
# Epilogue: Restore register values and free space from the stack
### YOUR CODE HERE ###
addi sp, sp, -8
jr ra # Return to caller
# === Definition of the "square" function ===
square:
mul a0, a0, a0
jr ra
# === Definition of the "decrement" function ===
decrement:
addi a0, a0, -1
jr ra
# === Helper functions ===
# You don't need to understand these, but reading them may be useful
create_default_list:
addi sp, sp, -12
sw ra, 0(sp)
sw s0, 4(sp)
sw s1, 8(sp)
li s0, 0 # Pointer to the last node we handled
li s1, 0 # Number of nodes handled
loop: # do...
li a0, 8
jal ra, malloc # Allocate memory for the next node
sw s1, 0(a0) # node->value = i
sw s0, 4(a0) # node->next = last
add s0, a0, x0 # last = node
addi s1, s1, 1 # i++
addi t0, x0, 10
bne s1, t0, loop # ... while i!= 10
lw ra, 0(sp)
lw s0, 4(sp)
lw s1, 8(sp)
addi sp, sp, 12
jr ra
print_list:
bne a0, x0, print_me_and_recurse
jr ra # Nothing to print
print_me_and_recurse:
add t0, a0, x0 # t0 gets current node address
lw a1, 0(t0) # a1 gets value in current node
addi a0, x0, 1 # Prepare for print integer ecall
ecall
addi a1, x0, ' ' # a0 gets address of string containing space
addi a0, x0, 11 # Prepare for print char syscall
ecall
lw a0, 4(t0) # a0 gets address of next node
jal x0, print_list # Recurse. The value of ra hasn't been changed.
print_newline:
addi a1, x0, '\n' # Load in ascii code for newline
addi a0, x0, 11
ecall
jr ra
malloc:
addi a1, a0, 0
addi a0, x0, 9
ecall
jr ra
【问题讨论】:
-
RISC-V 汇编函数有是递归的吗?它是尾递归,更容易将其实现为跳转,而不是调用/调用。然后,您只需要 2 个保留调用的寄存器并在某处存储您的返回地址,而不需要随列表长度缩放的堆栈空间量!
-
@PeterCordes 它必须遵循 cmets 制定的蓝图,所以是的,它必须是递归的。
标签: c recursion assembly linked-list riscv