My program has a recursive funciton that takes an integer as input and prints n, n-1...1, 0, 0, 1 ...n
Here is the recursive function:
RDemo:
addi $sp, $sp -4
sw $ra, 0($sp)
#print $a0
li $v0, 1
syscall
addi $a0, $a0, -1
bgezal $a0, RDemo
addi $a0, $a0, 1
li $v0, 1
syscall
lw $ra, 0($sp)
addi $sp, $sp, 4
jr $ra
the problem is that I need it to stop at 1, not 0.
For example, with an input of 3 it should output ( 3, 2, 1, 1, 2, 3) instead of (3, 2, 1, 0, 0, 1, 2, 3)
I am using the bgezal instruction ( branch if greater than or equal to 0 and link) because it is closest to what I need ( I need branch if grater than or equal to 1 and link).
As far as I can tell, there are only two MIPS branch and link instructions. begzal and bltzal (branch if less than 0 and link)
Is there a way I can accomplish what I want to without creating any other subroutines?
You can always use a normal blez
over an unconditional bal
(relative) or jal
(section-absolute). Or offset your counter by one (into another register) in order to get something to branch on. Or in this case optimize your code into 2 loops instead of recursion, using no stack space instead of O(n) space.
Unfortunately slt
doesn't work directly with bgezal
/ bltzal
. The both only check the sign bit, but slt
generates a 0 or 1, both of which are non-negative.
RDemo:
addi $sp, $sp -4
sw $ra, 0($sp)
#print $a0
li $v0, 1
syscall
addi $t0, $a0, -2 # tmp = n-2
addi $a0, $a0, -1 # n--
bgezal $t0, RDemo # if (n-1 >= 0) RDemo(n);
# returns with $a0 unchanged, custom calling convention
addi $a0, $a0, 1
li $v0, 1
syscall
lw $ra, 0($sp)
addi $sp, $sp, 4
jr $ra
tmp = n-2
on the original n
, instead of tmp = new_n - 1
, has better instruction-level parallelism. A CPU able to run multiple instructions per cycle could run those both in parallel, since there's no RAW dependency between them.
In this case, where integer overflow is probably not a concern for n-1
, we can get away with just one extra instruction vs. your original code and still use a conditional-b-and-link, instead of branching around an unconditional call. If a0 = INT_MIN
was a possibility, a0-1 >= 0
would be different from a0 >= 1
. Since we used addi
instead of the normal addiu
, this will actually trap on signed-overflow and only print -2147483647
twice if called with that arg, not print 2^31 times (or actually stop on stack overflow from recursion.)
BTW, your "recursion" is using a custom calling convention where the call "returns" n
in $a0
vs. the standard MIPS calling convention where return values are in $v0
, and $a
arg-passing registers are call-clobbered. https://godbolt.org/z/hrx4rdqaM shows how GCC uses stack space to save the incoming arg across a call if you don't write it with a return value.
This is kind of half way in between iterative and recursive, keeping registers across a call. But you can describe it to a C compiler with a0 = RDemo(a0)
since there's only one arg.
If you were programming for MIPS32r6 (MIPS manual from dec 2016), BGTZALC rt, offset
would be available, a full set of branch-and-link with any of the usual comparison predicates. The "C" is for "compact" - no branch delay slot.
Real MIPS does have a branch delay slot, but your code looks like it's for a simplified MIPS without one, otherwise the addi $a0, $a0, 1
would run before the callee. And MIPS32r6 removed the old bgezal
except for the bgezal $zero
case, unconditional bal
.
MARS may simulate MIPS32, but not MIPS32r6 which reorganized some opcodes. (MIPS32r6 is of limited interest these days except for the architecture design choices; the company developing that owns the ISA has switched over to RISC-V.)