The signature of the function in a high-level language (C) would be double power(double x, int n){ } Here x is the parameter of type double, which must be passed in $f14, and n is the parameter of type int, which must be passed in $a1, and the procedure should calculate and return the result of the expression in $f0 - $f2. Since it’s a procedure, it must be called from main using jal power and since it’s also a recursive procedure, inside the implementation of power you must have jal power as well, which will turn it into a recursive function.
.data
array1: .double 2.0 2.0 3.0 3.0 4.2 5.0 2.0 0.0 0.0
array2: .word 5 4 2 1 0 2 3 0 10
array_size: .word 9
newLine: .asciiz "\n"
one: .double 1.0
.text
main:
la $s1 array1
la $s2 array2
lw $s3 array_size #s3 = 9
li $s4, 0
loop:
beq $s4 $s3 exit
l.d $f14 0($s1)
lw $a2 0($s2)
jal power
li $v0, 3
mov.d $f12, $f20
syscall
addi $s1 $s1 8
addi $s2 $s2 4
addi $s4, $s4, 1 #index
j loop
power:
andi $sp 0xfffffff8
addi $sp $sp -16
s.d $f20 ($sp)
sw $ra 8($sp)
s.d $f14, 8($sp)
l.d $f20 one
beq $s2 $zero done
addi $a2 $a2 -1
jal power
mul.d $f20 $f14 $f20
done:
lw $ra 8($sp)
l.d $f14, 8($sp)
l.d $f20 ($sp)
addi $sp $sp 16
jr $ra
exit:
li $v0, 10
syscall
The logic seems okay to me, but the output gives me all zeros. I do not know what is wrong :(
Looks like the whole class is here on stack overflow with this assignment. The template and instructions you've been given are arguably lacking and/or incorrect.
For one, a function with signature
double power ( double x, int n );
should pass x
in $f12
and n
in $a2
for 32-bit MIPS an $f12
and $a1
for 64-bit MIPS. Your template code is 32-bit MIPS. Suspect your instructors are taking a page from 64-bit MIPS here, and also avoiding use of $f12
, since that's handy to use with the syscall to print a float/double. Obviously, you should go with what's been given in your template, just realize perhaps that this is not quite the official way.
It is incorrect to align the stack upon function entry without also making special provisions to restore the stack pointer to the caller-provided value. It is mandatory to restore the stack point to the caller-provided value, though in this particular case, won't crash the program because the main
does not use the stack pointer (but this cannot be relied upon in general). Solutions here are to either:
(a) Align the stack pointer once in the startup code (which here is main
) before doing any function calling — this means moving the andi sp 0xfffffff8
into the main
. Also then ensure that all non-leaf functions allocate stack space in multiples of 8 bytes rather than the sometimes seen multiples of 4 bytes.
(b) Or else align the stack pointer upon function entry but make provisions to save and restore the original stack pointer value upon function exit. That means capturing the original stack pointer value before aligning it, then saving that in the stack after aligning and restoring the stack pointer from that saved memory location rather than simply adding a fixed amount to the stack pointer (as that won't necessarily restore the stack pointer to the caller-provided value). Your template fails to restore the stack pointer to that provided by main
, but it since main
doesn't consult the stack, it doesn't crash or get incorrect program output despite this flaw.
The standard MIPS calling convention requires reservation of stack memory locations for the parameters by the caller to be provided to the callee. This is not being done in your template, however, is often not done for these kinds of assignments.
Since the function signature has power
returning a double, you must consistently return a double from all return ...;
statements. Here you're returning an integer 1 in $v0
from the base recursive case as if the signature was int power ( double x, int n)
, though the callers of power expecting a double of value 1.0 to be returned instead (in the case the n is zero). Failing to do this means that $f0
holds the simulator startup value of 0.0 and as a result the multiplications simply fail to go to any non-zero value.
By the standard calling convention, you cannot rely on parameter (here in $f14
though should be $f12
) to survive a function call, even to itself. There are several possible solutions here:
(a) Use a call-preserved register, meaning, using the given $f20
register that ensures survival across a function call. This means moving the parameter from $f14
into $f20
before the recursive call, and then using it from that call-preserved register after the recursive function call. The save & restore of $f20
that is in the template is necessary for this. Since your template already has this boilerplate, might as well go that route.
(b) Or else, use stack memory directly to preserve the incoming parameter value, omitting saving and restoring of $f20
and instead to save the incoming $f14
parameter value in that stack slot at 0($sp)
. After the recursive call, restore the incoming parameter value from the stack slot, to a scratch register, like $f2
, and use it from there for the multiplication. This is actually the most efficient approach with this problem when using the standard calling convention, since there is one less instruction than with approach (a) — no need to move $f14
to $f20
, just store $f14
directly to memory and reload when needed. That makes one save, one restore, whereas with (a) there's one save, one move, and one restore.
(c) Create a non-standard calling convention for this function that preserves the incoming argument $f14
for callers to use after. This is an advanced technique that can work in this case because we can know the implementation of both caller and callee. In this case there is no requirement to (a) align the stack, or (b) save and restore any floating point register (be it $f20
or $f14
). While this will work, you'll likely get points off for this approach, since we should probably be following the standard calling convention and only making deviations with more experience.
As far as the rest goes, you need to be able to handle the case of negative powers, so you'll need more code. Find or make a C version that includes that case so you know what it needs to do, and then code up the same in assembly.