#include <iostream>
using namespace std;
//Reference: https://www.geeksforgeeks.org/print-sums-subsets-given-set/
//==============testcase1==============
int arr[] = { 5, 4, 3 };
int n = 3;
//output: 12 9 8 5 7 4 3 0
//=====================================
//==============testcase2==============
//int arr[] = {2, 3};
//int n = 2;
//output: 5 2 3 0
//=====================================
// Prints sums of all subsets of arr[l..r]
void subsetSums(int arr[], int l, int r, int sum)
{
// Print current subset
if (l > r) {
cout << sum << " ";
return;
}
// Subset including arr[l]
subsetSums(arr, l + 1, r, sum + arr[l]);
// Subset excluding arr[l]
subsetSums(arr, l + 1, r, sum);
}
// Driver code
int main()
{
cout << "output: ";
subsetSums(arr, 0, n - 1,0);
return 0;
}
我想转换成R64I汇编代码。这就是我所做的。
# Reference: https://www.geeksforgeeks.org/print-sums-subsets-given-set/
.data
.align 4
# =========testcase1===========
arr: .word 5, 4, 3
n: .word 3
str: .string "output: "
space: .string " "
# output: 12 9 8 5 7 4 3 0
# ==============================
# =========testcase2===========
#arr: .word 2, 3
#n: .word 2
#str: .string "output: "
#space: .string " "
# output: 5 2 3 0
# ==============================
.text
.global _start
# Start your coding below, don't change anything upper except testing different testcase
_start:
#la t0, aa
#auipc t0, 0x1
#nop
#nop
#addi t0, t0, 148
#nop
#nop
li a7, 4
la a0, str
ecall
#load arr, 0, n, 0
la a0, arr
la a1, 0
la a2, n
la a3, 0
jal subsetSums
j end # Jump to end of program
subsetSums:
mv t0, a0
mv t1, a1
mv t2, a3
# if l > r print sum
blt a2, a1, sum
add t3, a1, 0
slli t3, t3, 2
addi t3, a0, t3
lw t4, 0(t3)
add a3, t2, t3
addi a1, t1, 1
jal subsetSums
addi a1, t1, 1
add a3, t2, 0
jal subsetSums
sum:
mv t0, a3
# Print sum
li a7, 1
addi a0, t0, 0
ecall
#Print
li a7, 4
la a0, space
ecall
lw ra, 0(sp) # Reload return address from stack
addi sp, sp, 4 # Restore stack pointer
jr x1
end:nop
我想做两个测试。第一个来自arr:.word 5,4,3并得到输出:12 9 8 5 7 4 3 0.但是,我不知道如何打印字符串和数字。我已经学习Hello World两年了。我以前从来没有学过汇编代码,也不知道我在做什么。编辑2:谢谢你的回复。我已经修复了如何输出字符串和数字,但我得到了一个错误,在这段代码
subsetSums:
mv t0, a0
mv t1, a1
mv t2, a3
# if l > r print sum
blt a2, a1, sum
#BUG
add t3, a1, 0
slli t3, t3, 2
addi t3, a0, t3
lw t4, 0(t3)
add a3, t2, t3
addi a1, t1, 1
jal subsetSums
addi a1, t1, 1
add a3, t2, 0
jal subsetSums
#BUG
似乎我不能把正确的参数放在我的函数中。至于为什么我在end:中使用nop,我必须这样做才能跟随我的教授。
1条答案
按热度按时间pkwftd7m1#
把递归看作是某种唯一的问题,这是一个red herring。
虽然它需要知识,但简单而正确的方法是遵循调用约定。只要环境有一个运行时调用堆栈(所有现代CPU环境都有),那么递归就可以简单地看作函数调用的子集--一个没有特殊要求的子集!
调用约定说明了一个函数应该如何调用另一个函数,并且这两个函数可以在单独的编译单元中(这意味着它们不能看到彼此的内部代码序列和它们的寄存器选择),并且只要函数签名不改变(变量和类型的参数列表,以及返回值和类型),被调用者可以在不需要改变其调用者的情况下改变/版本。
重申一下,递归没有向标准调用约定添加额外的规范或要求。
(It然而,一个简单的直接递归函数与它自己在同一个编译单元中,如果我们知道某些函数的内部寄存器使用情况,我们可以应用违反标准调用约定的自定义调用优化(假设该函数也不参与标准调用,例如来自另一个编译单元的调用)。但是,如果你的目的/目标是学习正确的函数调用方式,例如:按照标准约定,这样的优化是比您的目标更高级的主题。)
更详细地说,调用约定说明了如何:
从这些规范和对函数中使用的变量的分析中,我们可以确定哪些值需要保留以使函数自身受益,可能包括返回地址,参数变量和局部变量(包括临时变量)。
这样,你就可以有一个递归的fib,如下所示:
并且,按照调用约定,其汇编/机器代码将与以下非递归函数相同(用递归
fib
替换非递归fib2
)。因此,建议是简单地遵循调用约定,不要担心递归。