Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I'm trying to learn assembly on my own, but I'm confused on how to write a recursive function that calls itself more than once in the return statement.
This is the function in C:
int main()
{
int a;
a = rec3(5);
printf("%d", a);
return 0;
}
int recursion(int x) {
if (x > 0) {
return x + recursion(x-1) + recursion(x-2);
}
else {
return 0;
}
}
This is what I've gotten upto so far:
.text
main:
li $v0, 5 #Read in an int
syscall
move $a0, $v0 #Move the int to argument
jal Rec #Call Recursion function
move $a0, $v0 #Print the value
li $v0, 1
syscall
li $v0, 10
syscall
Rec: subu $sp, $sp, 8
sw $ra, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
Done: lw $ra, 4($sp)
lw $s0, ($sp)
addu $sp, $sp, 8
jr $ra
I haven't written the recursive part of the function because I'm so lost on how to do it. Can anyone write out the recursive part so I have an understanding of how to solve it? I also want to clarify, this isn't a school project or anything like that. I am just trying to understand how to do recursion in MIPS Assembly so I made my own function.
You know you can transform it into this, right?
int tmp1 = recursion(x-1);
int tmp2 = recursion(x-2);
return x + tmp1 + tmp2;
If it was literally having multiple recursive calls "in the return statement" that was confusing for you, does that help?
The first temporary you invent has to be saved somewhere (e.g. a call-preserved register) across the 2nd function call as part of evaluating that expression. Just like any time you need some data to survive across a function call.
The way a compiler would do it is to save/restore a couple call-preserved registers like $s0 and $s1 at the start/end of the function, and use them within the function for x and that temporary.
Or optimize that and only save x + recursion(x-1) in a single register, so you only need that and the return value after the 2nd function call returns.
Of course an optimizing compiler would turn some of this recursion into a loop, and not actually generate assembly that recursed as much. By hand you could even simplify it down to a modified Fibonacci loop with O(n) runtime instead of O(Fib(n)), just keeping the last two sequence values in registers. That's how to implement this function efficiently, but wouldn't teach you about recursion. Unfortunately this function is an example of a case where recursion is inconvenient and the worst way to implement this calculation.
(I mostly mention this because you asked how I would write this code in asm. I'd write asm that had the same observable results as the C, applying the as-if rule like the C standard allows compilers to do. Being recursive doesn't count as an observable result in ISO C. Obviously that's not what you should actually do for an assignment or to learn about recursion.)
Related
I am trying to write a MIPS program that takes in user input (integer n) and then prints all the descending numbers until 1 and then all the ascending numbers up to n.
Basically if I input 3, the output would be : 3 2 1 1 2 3
In C#, the code would be:
using System;
public static class LearnRecursion
{
public static void Main()
{
int n;
Console.Write("Enter an integer: ");
n = Convert.ToInt32(Console.ReadLine());
RDemo(n);
}
public static void RDemo(int n)
{
if (n < 1)
{
return;
}
else
{
Console.Write("{0} ", n);
RDemo(n - 1);
Console.Write("{0} ", n);
return;
}
}
}
I have tried implementing the MIPS program like this:
.data
### Declare appropriate strings and the space character for I/O prompts ###
input: .asciiz "Enter an integer : "
space: .asciiz " "
newline: .asciiz "\n"
.text
main:
### call procedure for printing the user prompt ###
li $v0, 4
la $a0, input
syscall
#read input from user
li $v0, 5
syscall
move $s0, $v0 #store the user input into saved register
move $a0, $s0 #move saved user input integer as argument for RDemo
jal RDemo
j exit
# recursive RDemo method
# excpects integer argument (from user input) in $a0
#returns when n<1
RDemo:
#make space for 4 registers on the stack
addi $sp, $sp, -12
sw $ra, 0($sp) #return adress
sw $s0, 4($sp) #saved register (original n)
sw $a0, 8($sp) #argument (user parsed n)
#base case: n < 1 return
blez $a0, RDemoReturn
#print n
li $v0, 1
move $a0, $a0
syscall
la $a0, space
li $v0, 4
syscall
#call RDemo with n-1
addi $a0, $a0, -1
jal RDemo
li $v0, 1
move $a0, $s0 #$s0 or $a0 ?
syscall
la $a0, space
li $v0, 4
syscall
RDemoReturn:
lw $ra, 0($sp)
lw $s0, 4($sp)
lw $a0, 8($sp)
addi $sp, $sp, 12
jr $ra
exit:
li $v0, 10
syscall
It ends up printing an endless loop, with only the original integer n, and then a bunch of numbers that look like adresses i.e 24567 etc.
Does anyone know what is wrong with my program?
That code is making several calling convention related mistakes. In particular, it goes to who owns what register and when.
During single step debugging, you'll notice these mistakes as bad values in registers, but perhaps don't understand what the right way should be.
The function prologue saves away $s0, $a0 and $ra. One of $s0 and $a0 is unnecessary, but neither is being used properly.
public static void RDemo(int n)
{
...
syscall to print n;
syscall to print space
RDemo(n - 1);
syscall to print n;
syscall to print space
}
Let's analyze n, which comes to the top of RDemo in $a0 as per the calling convention. That register is ok to use for the first syscall to print number n, however, as #Jim points out above, printing the space repurposes $a0 as pointer to text to print, and in doing so wipes out $a0, and so the value of n is ~lost, at least from $a0.
It is necessary for n to survive the printing of the space — and let's note that the function prologue did store n (from $a0) into the stack, which will have survived the syscall to print the space. So the simplest thing is to reload it from that stack location, after the printing of the space, and before computing n-1 for the recursive call.
lw $a0, 8($sp)
You'll find that the recursive call also clobbers $a0, by passing n-1 in $a0, so you'll need this lw before using n again after the recursive call. (It might be tempting to add 1 to $a0 to restore its original value, but that would require the callee to preserve its argument register, and that isn't part of the calling convention, i.e. it is a violation of the calling convention.)
In this scenario, $s0, is wholly unnecessary and goes unused, so no need to save and restore it in prologue/epilogue.
Also, while it is necessary to save $a0 in prologue, it is unnecessary to restore it in epilogue: that, $a0, is RDemo's parameter, which, by definition of the calling convention, belongs to this invocation/activation of RDemo and not its caller — restoring upon exit is a benefit to the caller, but not in the case of parameters as these are given to the callee to do with as they please, and callers (should) have no expectation of receiving back the value they provided — this meets the definition the scratch register set, aka call clobbered (and sometimes poorly labeled as caller saves).
As an alternative scenario, we can use the call-preserved register, e.g. $s0 to hold n live across syscalls and other calls. In this case, n found in $a0 upon function entry, should be copied into $s0, as part of function prologue (but after saving $s0 to the stack so $s0's original value can be restored upon exit).
From there on, whenever the code needs n, it can be found in $s0 (because you put it there). So, if/when you want it back in $a0, for example, then use move $a0, $s0.
The prologue in this scenario should save the incoming $s0, and restore it in epilogue, but there's no need to save (or restore) $a0.
I'd like to point out that everything of the above discussion applies whether a call is recursive or simply calling an alternate routine, say RDemo calls RDemo2, which calls RDemo3... Recursion may be hard to follow, but we use the same rules (the rules of the calling convention) just as with any function calling, in other words, recursion adds no additional rules.
Of course, when writing small toy programs we can always invent our own calling convention. Compilers will sometimes do this when they know internal details of callers and callees. However, if you're goal is to learn about (1) a standard calling convention, (2) the difference between call preserved and call clobbered registers, then follow the logic and analysis above.
The analysis we need to properly allocate variables and temporaries to the right kind of registers (call-clobbered vs. call-preserved) is a form of live-variable analysis.
In particular, we're looking to see if a variable is live across a function call. More technically, this analysis looks to match definitions of variables (assignments to that variable) with uses of that variable (consulting the value of the variable) and if some usage is after a call when a reaching definition is before a call, then that variable is live across a (function) call.
When a variable is not live across a function call then we can freely use the scratch/call-clobbered registers, and they are preferred for such variables since they have less prologue and epilogue overhead than call-preserved registers.
However, if a variable is live across a call, then it has special requirements, namely that its value needs to be in function-call-preserved storage, which is either in a call-preserved register, or local stack memory. Making a good choice between a call-preserved register and local stack memory has to do with the actual usage of that variable.
A call-preserved register adds overhead both prologue and epilogue since its original value must be returned to the caller; however, using local stack space has, at minimum the overhead of initialization.
Often when a variable is used within looping statements that involve function calls, a call-preserved register is better choice than stack, and vice versa: when the variable is not used within looping statements local stack memory is better.
Complete analysis depends though circumstances that do vary based on the actual code involved and of a particular dynamic workload, so we can count instruction and stalls (static and/or dynamic approximation) to compare the two approaches for a given variable. Compilers do this type of analysis to make their storage choices.
What adding two registers together in square brackets mean?
I have a question about these lines of code:
"mov al, [ebx+edx];"
"mov [ecx+edx],al;"
I know that mov instruction should move values from source to destination. But I don't really know what [ebx+edx] and [ecx+edx] does.
Is it simply adding two registers and then saving values in the memory?
This will add the values of the two registers and subsequently use them as a memory address reference to either retrieve the value at that register:
MOV EDX, [EBX+EAX]
or store a value to that location:
MOV [EBX+EDX], ECX
I0: slli $s2, $s1, 4
I1: beq $s1, $zero, top
I2: addi $s3, $s2, 6
I3: mult $t2, $s3, $s1
I4: addi $s4, $s2, 8
I5: sw $t2, 0($s4)
Consider a pipeline without any hazard handling. The pipeline is the typical 5- stage IF, ID, EX, MEM, WB MIPS design. For the above code, complete the pipeline for the code. Insert the characters IF(instruction fetch), ID(instruction decode), EX(execution), M(memory), WB(write back) for each instruction in the boxes. Do you guys think my chart is correct?
Thanks!http://imgur.com/PbJ2egd
First let's plot out which instructions rely on which outputs of preceding instructions:
I0: Relies on nothing here
I1: Relies on nothing here, but is a branch
I2: Relies on I0 ($s2)
I3: Relies on I2 ($s3)
I4: Relies on I0 ($s2)
I5: Relies on I4 ($s4)
So when an instruction relies on another, like I5 on I4, its EX block cannot run until the instruction it is relying on finishes its WB block. In the case of I5, we can see this clearly, since the EX block only starts once I4's WB block is done.
Also note that branches prevent the next instruction from starting at all until its EX block has finishes.
With these two rules we can go instruction by instruction and plot it out:
I0: Relies on no outputs.
I1: Relies on no outputs, but note that it is a branch. The next instruction cannot start till EX finishes.
I2: Relies on I0's output, so wait for I0's WB, but also for I1's EX because I1 was a branch. I1's EX is the worse case, so wait till then. This stalls 2 blocks.
I3: Relies on I2's output, so wait for I2's WB. This stalls 2 blocks. (We've now stalled 4 total)
I4: Relies on I0's output, so wait for I0's WB. This stalls 0 blocks, because I0 long since has completed. (We've now stalled 4 total)
I5: Relies on I4's output, so wait for I4's WB. This stalls 2 blocks. (We've now stalled 6 total)
So in the end we stall 3 times, each for two blocks. This equals the 6 "x"s your teacher drew.
This is the correct answer that she gave us from yesterday's review session. Can you point me in the right direction on how to answer this question? How do you know when to delay, put X's, etc? I am sorry I can't give exact details of the answer, but just know this is the correct answer.
I have an assigment in which I have to insert two digit integers into the stack. Search a number in the stack and return in which position this number is, print all the numbers in the stack and delete a number from the stack.
Right now I'm trying to print all the numbers in the stack by going through the stack using the base pointer, but my code doesn't work.
mov di,offset bp
mov ax, [di] ;trying to move de value stored in di direction in stack to Ax
mov digito,ah
mov digito2,al
mov dl,digito
mov ah,02
int 21h
mov dl,digito2
mov ah,02
int 21h
mov ah,01
int 21h
So in this code I'm trying to print the two number digit by getting the bp into di (so later I can decrement it to go through all the stack), and the passing the number stored in that direction in Ax. I'm a newby in assembly so I don't know what I'm doing.
Thank you in advance for your time. (And sorry for my english)
Sorry for the delayed reply. First, bp doesn't really have an "offset", so you could remove that. Second, bp won't automatically point into the stack unless you have made it so (mov bp, sp).
You don't mention an OS, but int 21h identifies it as DOS... which is real mode, segmented memory model. mov ax, [di] defaults to mov ax, ds:[di]. If you've assembled this into a ".com" file, cs, ds, es, and ss are all the same. If you've assembled it into an ".exe" file, this is not so! You may want to write it as mov ax, ss:[di] to be sure. In contrast, mov ax, [bp] defaults to mov ax, ss:[bp], so you may want to use bp instead of di here. I suspect that's how you're "supposed" to do it. If you've got a ".com" file, you can forget about this part (in 32-bit code you can forget about it too, but that doesn't apply to you).
Then... your attempt to print a number isn't really going to work properly. Look for "How do I print a number?" examples for more information on that - too much to get into here...
This is too hard an assignment for a beginner, IMO (but "the instructor is always right" :) ).
Can languages that support tail recursion apply the same technique to non-recursive function calls?
For example, if the last thing that function foo does is return the value of calling bar, might the language discard foo's stack frame? Are any languages known to actually do this?
Erlang does:
Tail recursion as seen here is not making the memory grow because when
the virtual machine sees a function calling itself in a tail position
(the last expression to be evaluated in a function), it eliminates the
current stack frame. This is called tail-call optimisation (TCO) and
it is a special case of a more general optimisation named Last Call
Optimisation (LCO).
LCO is done whenever the last expression to be evaluated in a function
body is another function call. When that happens, as with TCO, the
Erlang VM avoids storing the stack frame. As such tail recursion is
also possible between multiple functions. As an example, the chain of
functions a() -> b(). b() -> c(). c() -> a(). will effectively create
an infinite loop that won't go out of memory as LCO avoids overflowing
the stack. This principle, combined with our use of accumulators is
what makes tail recursion useful.
Yes it can. For example, consider the following C code:
int f();
int g() { return f(); }
When I compile this using gcc 4.6.3 with -O3 on x86-64, I get the following assembly for g():
g:
xorl %eax, %eax
jmp f ; <==== unconditional jump to f