Lets say i have the following command in MIPS:
sw $t0, 0($sp) # $sp=-4
Does that mean that the register $t0 is saved from 0 to -3 byte or from -4 to -7 byte ?
Stack growing downward in memory to make space for new data to be saved. The stack pointer $sp point to the top of stack.
The stack pointer $sp starts at a high memory address and decrements to expand as needed. Figure (b) shows the stack expanding
to allow two more data words of temporary storage. To do so, $sp decrements by 8 to become 0x7FFFFFF4. Two additional data words,
0xAABBCCDD and 0x11223344, are temporarily stored on the stack.
So in your case if I understand your question well the sw is word- addressable and the word will be stored on that location in memory which $sp point to. In case you store the next word it must have an offset of 4.
[Harris&Harris]
UPDATE
Take this example when you use lb
Say you have this word 0x23456789
when using lb $s0,1($0) After the load byte instruction, lb $s0, 1($0),
$s0 would contain 0x00000045 on a big-endian system and 0x00000067 on a
little-endian system.[Harris&Harris]
Related
Supposing that f, g, h, i are stored in $s0~$s4 respectively and the base addresses of arrays A and B are in $S6 and $S7.
sll $t0, $s0, 2
add $t0, $s6, $t0
sll $tl, $sl, 2
add $tl, $s7, $tl
lw $s0, 0($t0)
addi $t2 , $t0, 4
lw $t0, 0($t2)
add $t0, $t0, $s0
SW $t0, 0($tl)
I'm not familiar with MIPS so I Wonder how to translate MIPS into C and how to minimize these MIPS instructions?
how to translate MIPS into C
You recognize the patterns, here for array indexing / array element access.
On a byte addressable machine (all modern hardware), a 4-byte integer occupies 4 bytes in memory, and each of those bytes has a unique memory address. Because of the way the hardware works, we only use one of those 4 addresses to refer to the whole 4-byte integer, namely we use the lowest address among the 4. The hardware can load a 4-byte integer from memory given that one address (the lowest).
Since each 4-byte integer in memory occupies 4 addresses, in an array of 4-byte integers, the memory address of the first element and the memory address of the second element are 4 addresses apart even though are sequential index positions (i.e. they are only 1 index position apart).
The formula for indexing a 4-byte integer array, then is to convert the index into a byte offset, then add the byte offset to the base address of the array. The first part of that: converting an index to a byte offset, is sometimes referred to as "scaling". Scaling is conceptually done by multiplication, so in A[i], i needs to be scaled by the size of the array elements of A. If 4-byte integers that means scaling (multiplying) the index by 4. A quick way of doing that is shifting by 2 bit positions, which has the same effect as multiplying by 4.
The C language automatically scales when doing array references, whereas assembly language requires explicit scaling. C can do this because it knows the type of the array, whereas assembly language does not.
In C we can do expressions like A[i]. The C language allows us to break that down somewhat into *(A+i), which separates the pointer arithmetic addition A+i from the dereferencing of that sum, dereferencing with the unary indirection operator, *. As previously mentioned, C automatically scales, so A+i becomes the equivalent of A+i*4, in which we can substitute shifting for multiplication: A+(i<<2).
Next, we need to know if the dereference is for read or for write. When A[i] is accessed for its value, we will see it on what we call the "right hand side" of an assignment operator, as in ... = A[i]. When A[i] is access to update/store a value, we will see it on what we call the left hand side of an assignment operator, as in A[i] = ....
So, the sequence for doing A[i] for read (right hand side) in C is the following in assembly:
sll $temp1, $i, 2
addu $temp2, $A, $temp1
lw $temp3, 0($temp1)
Where $tempN is some register (usually a designated temporary) chosen to hold an intermediate value. Since multiple instructions are needed to accomplish anything, sequences of instructions are interconnected with registers that hold the intermediate states. And also, in assembly we name registers, not variables, so in my above $i and $A should be a registers names representing those variables rather than variable names directly used.
The pattern for write/store array access is similar but ends with a sw instruction instead, to store some value into memory at the index position.
These instruction sequence are interconnected by the use of these registers, and the sequences can be interrupted or interspersed with other instructions — what we have to follow then is the above pattern by paying attention to to the register usages that interconnect them rather than the specific sequences.
In your sample code:
sll $t0, $s0, 2 # sourcing an index in $s0, scaling it into temp $t0
add $t0, $s6, $t0 # adding a base array in $s6, putting back into $t0
sll $tl, $sl, 2
add $tl, $s7, $tl
lw $s0, 0($t0) # accessing the value of $s6[$s0*4], aka A[f]
addi $t2 , $t0, 4
lw $t0, 0($t2)
add $t0, $t0, $s0
SW $t0, 0($tl)
We can see the pattern for a read access to an index in $s0, and an array in $s6, these, we are told, map to f and A, so those three instructions comprise A[f] to read a value from A at index f.
The rest are done similarly. Your job is to use this knowledge to find the other array indexing patterns in the above sequence. Find out how the results of the array indexing operations are used and you'll have the complete C code.
NOTE that the sample you've been given incorrectly uses add and addi when pointer arithmetic should use addu and addiu — we don't want signed integer overflow checking on pointer arithmetic, as pointers are unsigned.
One of the add instructions is not for pointer arithmetic, but should probably still have used addu if this is intended to be replicated in C, because the C language does not have a built in operator to trap on overflow.
I am new to MIPS and I have a given MIPS recursive code which I have to apply it to a specific number (ex. number 3)
Bellow is the given MIPS code and I am supposed to write if I were to pass ex. number 3 in the function what would be the changes in the registries and cache for the recursion for said number 3?
I have tried to do it step by step on a piece of paper but got nowhere.
fact:
addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
slti $t0,$a0,1
beq $t0,$zero,L1
addi $v0,$zero,1
addi $sp,$sp,8
jr $ra
Ll:addi $a0,$a0,-1
jal fact
lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
mul $v0,$a0,$v0
jr $ra
Always test your code with the smallest possible input first. If you try with 0, and single step some 8 instructions worth, you can observe that your code doesn't return to its caller with the proper stack pointer value — an important part of functions is to preserve the registers that must be preserved for the caller, and that includes the stack pointer. Usually, the way to restore the stack pointer is to deallocate any allocated space, and as long as this is balanced, the preservation rule for $sp is honored.
As a result of an improper stack pointer, the caller is messed up. Where this bites you, then is when the caller tries to return to its caller. Likely this is not a problem for main b/c on MARS, for example, we usually exit main via the exit syscall. However, if you do fact(2), you'll see that the first return it executes works ok, but after that the caller (fact itself, recursive caller) doesn't work — it is unable to return to its caller, because it cannot properly restore its previously preserved $ra, because the stack pointer has been unbalanced by the terminal case.
We need to balance prologue with epilogue: not just statically but dynamically.
Your code is allocating stack space in all cases, though in the terminal case, your code is omitting the deallocation of the stack, so it becomes unbalanced.
Either add stack adjustment (deallocation) to the terminal case, or, test for the terminal case before even allocating stack space (so you don't have to either allocate or deallocate in that terminal case).
I'm trying to implement a linked list in MIPS and I also need to allocate memory for it. In every example I see, they assume that the first element in the list is on certain register, but they don't actually explain how to do it realistically.
I tried this, but it says "store address not aligned on word boundary 0x10040319"
# Allocate memory with syscall 9
li $v0, 9
addi $a0, $zero, 8 # Reserve 8 bytes, 4 for int data, 4 for pointer to next
syscall
# Make $t0 point to the beginning of the reserved memory?
add $t0, $v0, $zero
# Create linked list node
addi $t1, $zero, 10 # $t1 has the int data
sw $t1, 0($t0) # $t1 is now node->data
sw $zero, 4($t0) # node->next is NULL
So I have two questions here, but they are related to each other. One is how to properly allocate memory with syscall 9, and the other one is how to reference it so it can be used in a linked list (I actually need to implement a sorting algorithm using linked lists, because I need to be able to sort any number of elements (not a fixed number) and this is step -1 haha). Thanks.
Oh god, apparently it was the simulator they uploaded for the assignment.
I downloaded the most recent version of Mars MIPS from the official website and it worked. Well, this is awkward.
li $t0 , 0xABCD9876
sw $t0 , 100($0)
lb $s5 , 101($0)
New to MIPS; So my understanding is,
li loads the value 0xABCD9876 into register $t0
This value is then stored into memory at address ($0+100)
lb then copies the byte at address ($0+101) into register $s5
But there's nothing in register ($0+101) is there? 0xABCD9876 was stored in ($0+100), not ($0+$101). Lost at this point.
Memory is byte-addressed. Hence, ($0+100) points at a single byte. When it is used with a sw or lw instruction, you are actually accessing not only ($0+100), but also ($0+101), ($0+102), and ($0+103) (in other words, you’re accessing four bytes (one word) beginning at that address). By storing a word and then accessing a particular byte of it, you can determine which order the word’s bytes were stored in memory, and hence determine the processor’s endianness.
How does 'alignment of memory operands' help MIPS to be pipelined?
The book says:
Fourth, as discussed in Chapter 2, operands must be aligned in memory. Hence,
we need not worry about a single data transfer instruction requiring two data
memory accesses; the requested data can be transferred between processor and
memory in a single pipeline stage.
I think I understand that one data transfer instruction does not require two or more data memory aaccesses.
However, I am not sure what does it have to do with the alignment of memory operands.
Thanks, in advance!
The lw instruction requires that the memory address be word aligned.
Therefore, to access an unaligned word, one would need to access the two word boundaries that the required word intersects and mask out the necessary bytes.
For example, suppose you desire to load a word stored at address 0x2. 0x2 is not word aligned, so you would need to load the half word stored at 0x2 and the half-word stored at 0x4.
To do so, one might write:
lh $t0 2($zero)
lh $t1 4($zero)
sll $t1 $t1 16
or $t2 $t0 $t1
This only gets more complicated if you want to load for example a word stored at address 0x3:
# load first byte
lb $t0 3($zero)
# load second word, mask out first 3 bytes
lw $t1 4($zero)
lui $t2 0x0000FFFF
ori $t2 $t2 0xFFFFFFFF
or $t1 $t1 $t2
# combine
sll $t1 $t1 8
or $t2 $t0 $t1
So, it can be seen that the requirement for word alignment doesn't help MIPS to be pipelined, but rather that access to unaligned words requires excess memory accesses — this is a limitation of the ISA.