• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 405
  • Last Modified:

what's wrong with this MIPS code?

I've been tracing it with spim for hours and can't find why the error happened..  the error is:


Exception 6 [Bad instruction address] and this always happened after I print a new line.. why is this?

The error is that because when it tries to return the ra, the address given is the address of _s2, I haven't touched any 20($fp) register, so why can it change when i load it?

when I remove this code:

       # MOVE
      la  $t0,_s2
      sw  $t0,-16($fp)
            # PARAM
      lw  $t0,-16($fp)
      sub $sp,$sp,4
      sw $t0, ($sp)
            # PARAM in REG0
      lw  $a0,-16($fp)
            # PARAM in REG1
      lw  $a1,-16($fp)
            # Function call
      jal print_string
      add $sp,$sp,4
      

the error disappears

it seems like that after I called expand 2 times.. then it seems that the third time it overwrites the $ra... can someone help me to fix this?


 
 
	.data
	.globl _s1
	_s1: .asciiz "\n"  
	.globl _s2
	_s2: .asciiz "\n"  
			# TEXT 
	.text
	.globl _expand
	_expand:
			# Function Entry
	sw $ra,-4($sp)
	sw $fp,-8($sp)
	add  $fp, $zero, $sp
	subu $sp, $sp, 8
	 # MOVE 
	lw  $t0,0($fp)
			# RETURN
	add  $v0,$zero,$t0
			# JUMP
	jal LABEL1
			# Function Ends
	LABEL1:
			# Exit Sequence
	lw $ra,-4($fp)
	add  $sp, $zero, $fp
	lw $fp,-8($fp)
	jal $ra
	.globl main
	main:
			# Function Entry
	sw $ra,-20($sp)
	sw $fp,-24($sp)
	add  $fp, $zero, $sp
	subu $sp, $sp, 24
	 # MOVE 
	add $t0,$zero,1
	sw  $t0,-4($fp)
			# PARAM
	lw  $t0,-4($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
			# PARAM in REG0
	lw  $a0,-4($fp)
			# PARAM in REG1
	lw  $a1,-4($fp)
			# Function call
	jal _expand
	add $sp,$sp,4
			# Function call
	jal print_int
	add $sp,$sp,4
	 # MOVE 
	la  $t0,_s1
	sw  $t0,-8($fp)
			# PARAM
	lw  $t0,-8($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
			# PARAM in REG0
	lw  $a0,-8($fp)
			# PARAM in REG1
	lw  $a1,-8($fp)
			# Function call
	jal print_string
	add $sp,$sp,4
	 # MOVE 
	add $t0,$zero,0
	sw  $t0,-12($fp)
			# PARAM
	lw  $t0,-12($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
			# PARAM in REG0
	lw  $a0,-12($fp)
			# PARAM in REG1
	lw  $a1,-12($fp)
			# Function call
	jal _expand
	add $sp,$sp,4
			# Function call
	jal print_int
	add $sp,$sp,4
	
	
	
	 # MOVE 
	la  $t0,_s2
	sw  $t0,-16($fp)
		# PARAM
	lw  $t0,-16($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
		# PARAM in REG0
	lw  $a0,-16($fp)
		# PARAM in REG1
	lw  $a1,-16($fp)
		# Function call
	jal print_string
	add $sp,$sp,4
	
	
	
	
	
	
	
	
			# Function Ends
	LABEL2:
			# Exit Sequence
	lw $ra,-20($fp)
	add  $sp, $zero, $fp
	lw $fp,-24($fp)
	jal $ra
 
print_int:
	li $v0,1
	syscall
	jr $ra
 
print_string:
	li $v0,4
	syscall
	jr $ra

Open in new window

0
kuntilanak
Asked:
kuntilanak
  • 17
  • 12
1 Solution
 
Infinity08Commented:
>>       jal $ra

shouldn't that be :

        jr $ra

when returning from a subroutine ?


jal basically does this :

        $ra = program_counter
        jump to label $ra             (where $ra is not a valid label)

jr basically does this :

        jump to the return address in $ra

which is what you want.


Or in other words, jal is used for calling a subroutine, jr is used for returning from it.
0
 
kuntilanakAuthor Commented:
same thing ,I changed that and it doesn't work... I think the problem is that when first started I looked at MIPS and then $ra was 400018 something like that ,and then at the end when it was supposed to do jal (or you said jr ra) it was 1000010. 1000010 was the address of _s2, and that is executed by this:

 sub $sp,$sp,4
        sw $t0, ($sp)


so when it subtract $sp by 4 and the result is  the address of  -20($fp), which was the address of the $ra.., therefore it overwrites the original $ra. One other weird thing is that, this only happens when the function _expand was called twice and then the next function call do this... I don't know why that could happen...
0
 
Infinity08Commented:
>>       sub $sp,$sp,4
>> <SNIP>
>>                   # Function call
>>       jal _expand
>>       add $sp,$sp,4
>>                   # Function call
>>       jal print_int
>>       add $sp,$sp,4

You subtract 4 from the stack pointer, and then you add 4 to it TWICE. The stack pointer will now no longer point to the correct value.
I assume you forgot to put the argument for print_int on the stack before calling the print_int function ?


You do the same thing further in the code too.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
kuntilanakAuthor Commented:
the function print_int is an extern function... I just hardcoded it in the MIPS code, so which add 4 should I delete it from above?
0
 
Infinity08Commented:
It depends on how print_int takes its parameters. Does it take it from a register ? Or from the stack ? How many and which parameters ?
0
 
kuntilanakAuthor Commented:
print_int always and will always take one parameter which is an integer, that's why I hard coded them in the code directly.. it reads from $a0
0
 
Infinity08Commented:
Ah, just found out that print_int takes its parameter from $a0. So, you don't put anything on the stack, and simply do :

      jal print_int

instead of :

      jal print_int
      add $sp,$sp,4


Note that print_string also takes its parameter from $a0. So, you don't have to put anything on the stack for it either.
So, all this for example :

      la  $t0,_s2
      sw  $t0,-16($fp)
            # PARAM
      lw  $t0,-16($fp)
      sub $sp,$sp,4
      sw $t0, ($sp)
            # PARAM in REG0
      lw  $a0,-16($fp)
            # PARAM in REG1
      lw  $a1,-16($fp)
            # Function call
      jal print_string
      add $sp,$sp,4

can simply become this :

      la  $t0,_s2
      sw  $t0,-16($fp)
            # PARAM in REG0
      lw  $a0,-16($fp)
            # Function call
      jal print_string


You can do similar simplifications elsewhere in the code.
0
 
kuntilanakAuthor Commented:
also I have this code, where it always spins around at the end:

especially at this part:

# PARAM in REG0
      lw  $a0,-12($fp)
                  # PARAM in REG1
      lw  $a1,-12($fp)
                  # Function call
      jal _expand
      add $sp,$sp,4
                  # Function call
      jal _print
      add $sp,$sp,4


This time I am clueless why it's like that

	.text
	.globl _expand
	_expand:
			# Function Entry
	sw $ra,-4($sp)
	sw $fp,-8($sp)
	add  $fp, $zero, $sp
	subu $sp, $sp, 8
	 # MOVE 
	lw  $t0,0($fp)
			# RETURN
	add  $v0,$zero,$t0
			# JUMP
	jal LABEL1
			# Function Ends
	LABEL1:
			# Exit Sequence
	lw $ra,-4($fp)
	add  $sp, $zero, $fp
	lw $fp,-8($fp)
	jal $ra
	.globl _print
	_print:
			# Function Entry
	sw $ra,-4($sp)
	sw $fp,-8($sp)
	add  $fp, $zero, $sp
	subu $sp, $sp, 8
			# Function Ends
	LABEL2:
			# Exit Sequence
	lw $ra,-4($fp)
	add  $sp, $zero, $fp
	lw $fp,-8($fp)
	jal $ra
	.globl main
	main:
			# Function Entry
	sw $ra,-16($sp)
	sw $fp,-20($sp)
	add  $fp, $zero, $sp
	subu $sp, $sp, 20
	 # MOVE 
	add $t0,$zero,5
	sw  $t0,-4($fp)
			# PARAM
	lw  $t0,-4($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
			# PARAM in REG0
	lw  $a0,-4($fp)
			# PARAM in REG1
	lw  $a1,-4($fp)
			# Function call
	jal _expand
	add $sp,$sp,4
			# Function call
	jal _print
	add $sp,$sp,4
	 # MOVE 
	add $t0,$zero,2
	sw  $t0,-8($fp)
			# PARAM
	lw  $t0,-8($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
			# PARAM in REG0
	lw  $a0,-8($fp)
			# PARAM in REG1
	lw  $a1,-8($fp)
			# Function call
	jal _expand
	add $sp,$sp,4
			# Function call
	jal _print
	add $sp,$sp,4
	 # MOVE 
	add $t0,$zero,8
	sw  $t0,-12($fp)
			# PARAM
	lw  $t0,-12($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
			# PARAM in REG0
	lw  $a0,-12($fp)
			# PARAM in REG1
	lw  $a1,-12($fp)
			# Function call
	jal _expand
	add $sp,$sp,4
			# Function call
	jal _print
	add $sp,$sp,4
			# Function Ends
	LABEL3:
			# Exit Sequence
	lw $ra,-16($fp)
	add  $sp, $zero, $fp
	lw $fp,-20($fp)
	jal $ra
 
print_int:
	li $v0,1
	syscall
	jr $ra
 
print_string:
	li $v0,4
	syscall
	jr $ra

Open in new window

0
 
Infinity08Commented:
>> This time I am clueless why it's like that

It's the same problem. Did you understand why it's a problem to modify the stack pointer incorrectly ?
0
 
Infinity08Commented:
I also notice that you did not fix the problems I pointed out.
0
 
kuntilanakAuthor Commented:
okay, this time I am not using the function print_int which is hard coded in the code.. it's called print_ and as you can see it's on the code somewhere..
0
 
Infinity08Commented:
You can use print_int - there was no problem with that. Instead of adding new features, could you start by fixing the problems you experienced (the ones you mentioned in your question) ?

Start by fixing all the issues I pointed out. And if that fixes your problem, then that's it. If not, we'll look further. But, please focus on fixing the problem first.
0
 
kuntilanakAuthor Commented:
I've already fixed the problem before that.. now I have a different code.. which doesn't use print_int.. it just happens co incidently that I have a function called print, which you might think does the same thing.. I am testing a function which takes in a function... but that function is actually declared, print has a stack now.. different from print_int which is just a call to the syscall.. although it seems useless and doing nothing.. I just want to now that I am passing the right thing to the code and the code doesn't fail.. eventually it does
0
 
Infinity08Commented:
If your problem was solved, then you can close this question.
If you have another problem, you can open a new question for that.
0
 
Infinity08Commented:
btw :

>> I've already fixed the problem before that..

No, you haven't. The last code you posted has the exact same problems that you say you already fixed. (jal instead of jr, incorrect modification of the stack pointer, unnecessarily complicated code)
0
 
kuntilanakAuthor Commented:
the jal and jr doesn't matter, it works either way.. I fixed the part where print_int and print_string is putting in stacks, which is not necessary.. and it's done

and you may think it is complicated, this is because it's a code from a compiler I made.. that's why the code is general and there can be some un-necessary code that is un-needed.. all I need to do is have the code working
0
 
kuntilanakAuthor Commented:
and here's my fixed code for my first code: as you can see I've fixed all you said.. (excluding the jal of ra). and it works..

 
 
	.data
	.globl _s1
	_s1: .asciiz "\n"  
	.globl _s2
	_s2: .asciiz "\n"  
	.globl _s3
	_s3: .asciiz "\n"  
			# TEXT 
.text
.globl _expand
_expand:
			# Function Entry
	sw $ra,-4($sp)
	sw $fp,-8($sp)
	add  $fp, $zero, $sp
	subu $sp, $sp, 8
	 # MOVE 
	lw  $t0,0($fp)
			# RETURN
	add  $v0,$zero,$t0
			# JUMP
	jal LABEL1
			# Function Ends
	LABEL1:
			# Exit Sequence
	lw $ra,-4($fp)
	add  $sp, $zero, $fp
	lw $fp,-8($fp)
	jal $ra
	
	
	
.globl main
main:
			# Function Entry
	sw $ra,-32($sp)
	sw $fp,-36($sp)
	add  $fp, $zero, $sp
	subu $sp, $sp, 36
	 # MOVE 
	add $t0,$zero,1
	sw  $t0,-4($fp)
			# PARAM
	lw  $t0,-4($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
			# PARAM in REG0
	lw  $a0,-4($fp)
			# PARAM in REG1
	lw  $a1,-4($fp)
			# Function call
	jal _expand
	add $sp,$sp,4
			# Function call
	jal print_int
	
	 # MOVE 
	la  $t0,_s1
	sw  $t0,-8($fp)
			
			# PARAM in REG0
	lw  $a0,-8($fp)
			# PARAM in REG1
	lw  $a1,-8($fp)
			# Function call
	jal print_string
	
	 # MOVE 
	add $t0,$zero,0
	sw  $t0,-12($fp)
			# PARAM
	lw  $t0,-12($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
			# PARAM in REG0
	lw  $a0,-12($fp)
			# PARAM in REG1
	lw  $a1,-12($fp)
			# Function call
	jal _expand
	add $sp,$sp,4
			# Function call
	jal print_int
	
	 # MOVE 
	la  $t0,_s2
	sw  $t0,-16($fp)	
			# PARAM in REG0
	lw  $a0,-16($fp)
			# PARAM in REG1
	lw  $a1,-16($fp)
			# Function call
	jal print_string
	
	 # MOVE 
	add $t0,$zero,1
	sw  $t0,-24($fp)
			# PARAM
	lw  $t0,-24($fp)
	sub $sp,$sp,4
	sw $t0, ($sp)
			# PARAM in REG0
	lw  $a0,-24($fp)
			# PARAM in REG1
	lw  $a1,-24($fp)
	 # MOVE 
			# UMINUS
	lw  $t0,-20($fp)
	neg $t0,$t0
	sw  $t0,-24($fp)
			# Function call
	jal _expand
	add $sp,$sp,4
			# Function call
	jal print_int
	
	 # MOVE 
	la  $t0,_s3
	sw  $t0,-28($fp)
			
			# PARAM in REG0
	lw  $a0,-28($fp)
			# PARAM in REG1
	lw  $a1,-28($fp)
			# Function call
	jal print_string
	
			# Function Ends
	LABEL2:
			# Exit Sequence
	lw $ra,-32($fp)
	add  $sp, $zero, $fp
	lw $fp,-36($fp)
	jal $ra
 
print_int:
	li $v0,1
	syscall
	jr $ra
 
print_string:
	li $v0,4
	syscall
	jr $ra

Open in new window

0
 
Infinity08Commented:
>> the jal and jr doesn't matter, it works either way..

Even if it works, it's still more proper to use jr for returning from a function, because that's the instruction that was designed for that use.
The jal instruction was not designed to be used for returning from a function, so don't use it for that.


>> I fixed the part where print_int and print_string is putting in stacks, which is not necessary.. and it's done

Well, you didn't fix that in the last code you posted.


>> and you may think it is complicated

It's not only that I think it's unnecessarily complicated - it IS unnecessarily complicated. A lot of the instructions you use have no effect, like :

      sw  $t0,-8($fp)
      lw  $t0,-8($fp)

What's the point of that second instruction ?

Other instructions simply don't serve any purpose. When calling a function for example, you copy the argument in 4 different locations - only one of these is actually used by the function. The others are unnecessary copies, and basically a waste of processor cycles.


>> this is because it's a code from a compiler I made..

Well, then I'd put some work into optimizing the code generated by your compiler :)
0
 
Infinity08Commented:
>> as you can see I've fixed all you said..

Not everything. You're still making unnecessary copies of data, and performing instructions that have no effect (see my previous post for more details). You have fixed that for the print_string calls, but not for the other calls.
0
 
kuntilanakAuthor Commented:
of course I would be optimizing it , but before optimizing it I need to get it working first right? : )

is there a way to solve the problem above, by still keeping the :

 # PARAM
      lw  $t0,-16($fp)
      sub $sp,$sp,4
      sw $t0, ($sp)

when I call print_int or print_string?
0
 
Infinity08Commented:
>> is there a way to solve the problem above, by still keeping the :

You can keep it if you really want to. But it's completely unnecessary. Just remember that for every stack size increase (sub), you need to have a corresponding stack size decrease (add). If you increase the size by a total of 20 inside a function, then you need to have decreased the size by a total of 20 at the time the function ends. The stack size at the end of the function needs to be the same as the stack size at the beginning of the function. As long as you keep that in mind, you shouldn't have any corruptions like the one you experienced.
0
 
Infinity08Commented:
It helps to keep a mental picture of the stack in your head while writing the code. Or write the stack down on a piece of paper, and take note of all the data present on the stack at any time, as well as where the stack and frame pointers point at any time.
0
 
kuntilanakAuthor Commented:
yes, infinity I realized that's the main problem I am having here... so the problem in the second code is also the same? Because I did not add and substract the sp by the same amount after calling it?
0
 
Infinity08Commented:
In the last code you posted, there are indeed still places where changing the stack pointer is not done correctly. So, without looking too much into it, I would suspect it's indeed the same problem here :)
Start by fixing that first, and see if it solves your problem. If not, we can look further.
0
 
kuntilanakAuthor Commented:
yes, I just erased all the 3 add sp, sp, after the call to _print and it works.. I think that's the only problem.. hmmm.. I am just curious when a function is called.. do we need to always subtract the sp by some amount and then add it back again when we return? I was just curious why my _print does not add some amount to the stack pointer, if it does then it should be ok and I won't have to deal with this problem
0
 
Infinity08Commented:
>> I am just curious when a function is called.. do we need to always subtract the sp by some amount and then add it back again when we return?

Only when the function takes parameters on the stack do you have to do that. If not, you don't do it :)
0
 
Infinity08Commented:
Do you know the concept of stack frames ? That's what it's about :

        http://en.wikipedia.org/wiki/Call_stack#Structure

The calling code first reserves enough space on the stack for the function parameters (if any) and copies the parameters into that space.
Then it calls the function which puts the return address (and the old frame pointer) on the stack (right after the parameters).
The function can then optionally reserve further space on the stack for its local data.

The total of all this data on the stack (arguments + return address + data) is called the stack frame.
0
 
kuntilanakAuthor Commented:
Thanks infinity that helps, now I fixed everything and got my compiler to work...
0
 
Infinity08Commented:
Great :)
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 17
  • 12
Tackle projects and never again get stuck behind a technical roadblock.
Join Now