While Loop in Mips (New to Mips) - loops

How would I convert this code into Mips?
int n = 100; int sum = 0; while (n>0) {
sum = sum + n;
n--; }
I have this so far and am not sure what to do to finish this.
.data
n: .word 100
.text
main:
la $t0, n
lw $t1, 0(t0)
li $so, 0
Loop:
bgt $t1, $zero, EXIT
add $t1, $s0, $t1
addi $t1, $t1, -1
j Loop
exit:

Change the line:
add $t1, $s0, $t1
To:
add $s0, $s0, $t1
Also, there is no need for use of the data segment. Just set $t1 using:
li $t1, 100

"Mips" isn't a language. MIPS an Instruction Set Architecture (ISA). Are you trying to figure out how to turn the C code into MIPS assembly code?
This looks like a homework assignment from the Patterson and Hennessy textbook. If so, you should go to your professor's office hours to get help. Almost every university includes in its academic handbook a statement that it's unethical to ask for homework help online.
If your request isn't a homework assignment, then the best way to convert that C code into MIPS assembly code is with a compiler. For simple loops, the compiler will generate more effective code than you can generate by hand. For example, "gcc -march=native -O3" will generate code that optimizes for the exact CPU on which you're compiling, taking into account pipeline depth and cache latencies.
If you absolutely need to see the assembly code, use "gcc -S" to produce an assembly file.

Mips doesn't have loops per-say, instead what your going to do is use a jump statement with conditions and loop with that.

I think bgt $t1, $zero, EXIT is the opposite of what you want. It seems like you want to convert while(n > 100), it would help if you make another method to do the codes inside of the while loop, then bgt $t1, $zero, . Correct me if I'm wrong.

Related

I am trying to convert one small for loop which is written in C language into an MIPS code

I am trying to convert this for loop into MIPS assembly language and i am not sure how exactly to tackle this question. I tried using youtube to understand the concept of it and I am still strugling with it.
here is the C code:
int sum = 0;
for (int i = 1; i <= 10; i ++){
if (i & 1) sum = sum + i * i;
else sum = sum + i;
}
I tried converting but i am just not sure where to start it.
expecting to get MIPS code with explanation in possible so I can learn from it!
I assume that you are doing the bitwise and operator with 1 to indicate if the current 'i' is odd or even. What you can do is the following :
.text
li $t0, 1 #is for counting or i in this case
li $t1, 1 #for if statements condition
li $v0, 0 #output register
forLoop:
bgt $t0, 10, end
andi $t2, $t0, 1 #and opertion
beq $t2, 1, odd #if andi resulted in one do the odd computation if not it is even
add $v0, $v0, $t0
addi $t0, $t0, 1
j forLoop
odd:
mul $t2, $t0, $t0 #t2 is like a temporary register can be overwritten, square of i
add $v0, $v0, $t2
addi $t0, $t0, 1
j forLoop
end:
move $a0, $v0
li $v0, 1
syscall
li $v0, 10
syscall
This implementation is correct however the "right" implementation should use stack pointer and jump and link command for calling the function. This example has the sole purpose for showing the logic behind it.

Converting while loop from C to MIPS

I just started to learn the basics of MIPS. While reading a book about MIPS I thought about the next question:
I have the following code:
while (x) {...}
What's between brackets isn't important (just put dots on the right place on the code). It is know that x contains a Boolean statement. Let reg $t0 contain x. I am trying to convert this C while loop into a MIPS code. How to do so?
You can do it with a branch and a label. For example, the statement
while (n>0) { .... }
can be translated to MIPS using a branch and a label:
.data
n: .word 100
.text
main:
la $t0, n
lw $t1, 0($t0)
li $s0, 0
Loop:
bgt $t1, $zero, EXIT
add $t1, $s0, $t1
addi $t1, $t1, -1
j Loop
EXIT:

Translating C Code to Mips Assembly

Quick question. I'm looking to translate this C code into MIPS assembly language:
f=g-A[B[4]]
I can use any registers to place the variables so I used the following:
$s1=f, $s2=g, $s3=A, $s4=B, $t0=B[4], $t1=A[B[4]]
Here is the code I have tried so far but I am not sure if it is correct or not:
lw $t0, 16($s4)
add $t0, $t0, $s3
lw $t1, 0($t0)
sub $s1, $s2, $t1
Can you guys help me edit this? Thank you in advance.
Looks like you didn't scale the index of A by the size of the A elements. You probably want to multiply $t0 by 4 before adding $s3.
If you have some C code you can compile it to assembly using gcc
gcc-mipsel-linux-gnu -save-temps program.c
cat program.S
this assumes you have a mipsel [cross-]compiler installed, get one from
http://www.emdebian.org/crosstools.html
or
https://wiki.debian.org/BuildingCrossCompilers

Is this MIPS strlen correctly converted from the corresponding C loop?

I have a simple question for a Comp Sci class I'm taking where my task is to convert a function into MIPS assembly language. I believe I have a correct answer but I want to verify it.
This is the C function
int strlen(char *s) {
int len;
len=0;
while(*s != '\0') {
len++;
s++;
}
return len;
}
Thanks!
strlen:
add $v0, $zero, $zero # len = 0
loop: # do{
lbu $t0, 0($a0) # tmp0 = load *s
addi $a0, $a0, 1 # s++
addi $v0, $v0, 1 # len++
bne $t0, $zero, loop # }while(tmp0 != 0)
s_end:
addi $v0, $v0, -1 # undo counting of the terminating 0
j $ra
Yeah, you have a correct asm version, and I like the fact that you do as much work as possible before testing the value of t0 to give as much time as possible for loading from memory.
(Editor's note: the add of -1 after the loop corrects for off by 1 while still allowing an efficient do{}while loop structure. This answer proposes a more literal translation from C into an if() break inside an unconditional loop.)
I think the while loop isn't right in the case of *s == 0.
It should be something like this:
...
lbu $t0, 0($a0)
loop:
beq $t0, $zero, s_end # *
...
b loop
s_end:
...
*You could use a macro instruction (beqz $t0, s_end) instead of beq instruction.
Yes, looks correct to me, and fairly efficient. Implementing a while loop with asm structured like a do{}while() is the standard and best way to loop in asm. Why are loops always compiled into "do...while" style (tail jump)?
A more direct transliteration of the C would check *s before incrementing len.
e.g. by peeling the first iteration and turning it into a load/branch that can skip the whole loop for an empty string. (And reordering the loop body, which would probably put the load close to the branch, worse for performance because of load latency.)
You could optimize away the len-- overshoot-correction after the loop: start with len=-1 instead of 0. Use li $v0, -1 which can still be implemented with a single instruction:
addiu $v0, $zero, -1
A further step of optimization is to only do the pointer increment inside the loop, and find the length at the end with len = end - start.
We can correct for the off-by-one (to not count the terminator) by offsetting the incoming pointer while we're copying it to another reg.
# char *s input in $a0, size_t length returned in $v0
strlen:
addiu $v0, $a0, 1 # char *start_1 = start + 1
loop: # do{
lbu $t0, ($a0) # char tmp0 = load *s
addiu $a0, $a0, 1 # s++
bne $t0, $zero, loop # }while(tmp0 != '\0')
s_end:
subu $v0, $a0, $v0 # size_t len = s - start
jr $ra
I used addiu / subu because I don't want it to fault on signed-overflow of a pointer. Your version should probably use addiu as well so it works for strings up to 4GB, not just 2.
Untested, but we can think through the correctness:
For an empty string input (s points at a 0): when we reach the final subtract, we have v0=s+1 (from before the loop) and a0=s+1 (from the first/only iteration which falls through because it loads $t0 = 0). Subtracting these gives len=0 = strlen("")
For a length=1 string: v0=s+1, but the loop body runs twice so we have a0=s+2. len = (s+2) - (s+1) = 1.
By induction, larger lengths work too.
For MIPS with a branch-delay slot, the addiu and subu can be reordered after bne and jr respectively, filling those branch-delay slots. (But then bne is right after the load so classic MIPS would have to stall, or even fill the load-delay slot with a nop on a MIPS I without interlocks for loads).
Of course if you actually care about real-world strlen performance for small to medium strings (not just tiny), like more than 8 or 16 bytes, use a bithack that checks whole words at once for maybe having a 0 byte.
Why does glibc's strlen need to be so complicated to run quickly?

MIPS assembly, register traversing?

Hello and thanks in advance,
My question is if it is possible to go through registers like having a pointer in one ($t0) and moving the pointer to another one ($t1).
What i actually want to do is in one loop read 8 integers and store them in ($s0-$s7)
You could try changing the bits in the sw opcode to point to increasing registers, but that's a terrible idea. I think your best bet is to just write your unrolled loop yourself:
lw $s0, $t0
addi $t0, $t0, 4
lw $s1, $t0
...
Rearrange things to minimize stalls, but that's about as good as you're going to get.
You want to have a register number be variable? I don't know MIPS inside and out, but I doubt it's possible. The only ISAs I know of that have anything like that are SPARC (register windows, not usable for what you want) and IA64 ("rotating registers", could be used for what you want, but only with floating point).
I'm not aware of an existing MIPS architecture that supports referencing a register by the contents of a register, which would allow the type of thing you suggest, like:
move $t0, $zero
mover $t0, $s0 # $s0 = register($t0) = register(0)
addi $t0, 1
mover $t0, $s1 # $s1 = register($t0) = register(1)
addi $t0, 1
...
Although in any case it's not a good idea in my opinion, for a few reasons. Firstly, you're dealing with a very small number of registers anyway, so there is a small upper bound on the loop in any case, making the direct approach not much less flexible.
More importantly, a loop like that would be horribly inefficient. It would initialise, increment, perform a move and a branch check (at least) for every iteration. Even without taking branch stalls into account this is at least 3x slower than simply:
move $t0, $s0
move $t1, $s1
...
move $t8, $s8

Resources